Merge pull request #64 from status-im/commands-button
Commands button. Phone number validation. Drawer and Profile views.
Former-commit-id: 3d8adf840e
After Width: | Height: | Size: 347 B |
After Width: | Height: | Size: 366 B |
After Width: | Height: | Size: 162 B |
After Width: | Height: | Size: 274 B |
After Width: | Height: | Size: 242 B |
After Width: | Height: | Size: 129 B |
After Width: | Height: | Size: 444 B |
After Width: | Height: | Size: 500 B |
After Width: | Height: | Size: 219 B |
After Width: | Height: | Size: 624 B |
After Width: | Height: | Size: 775 B |
After Width: | Height: | Size: 291 B |
After Width: | Height: | Size: 892 B |
After Width: | Height: | Size: 960 B |
After Width: | Height: | Size: 413 B |
|
@ -15,6 +15,7 @@
|
|||
[syng-im.components.chats.new-group :refer [new-group]]
|
||||
[syng-im.components.chat.new-participants :refer [new-participants]]
|
||||
[syng-im.components.chat.remove-participants :refer [remove-participants]]
|
||||
[syng-im.components.profile :refer [profile]]
|
||||
[syng-im.utils.logging :as log]
|
||||
[syng-im.utils.utils :refer [toast]]
|
||||
[syng-im.navigation :as nav]
|
||||
|
@ -29,7 +30,7 @@
|
|||
;; this listener and handle application's closing
|
||||
;; in handlers
|
||||
(let [stack (subscribe [:navigation-stack])]
|
||||
(when (< 1 (count stack))
|
||||
(when (< 1 (count @stack))
|
||||
(dispatch [:navigate-back])
|
||||
true)))]
|
||||
(add-event-listener "hardwareBackPress" new-listener)))
|
||||
|
@ -46,7 +47,8 @@
|
|||
:chat-list [chats-list]
|
||||
:new-group [new-group]
|
||||
:contact-list [contact-list]
|
||||
:chat [chat]))))
|
||||
:chat [chat]
|
||||
:profile [profile]))))
|
||||
|
||||
(defn init []
|
||||
(dispatch-sync [:initialize-db])
|
||||
|
|
|
@ -96,9 +96,9 @@
|
|||
subtitle])]]])
|
||||
|
||||
(defn actions-list-view []
|
||||
(let [{:keys [group-chat active]}
|
||||
(subscribe [:chat-properties [:group-chat :name :contacts :active]])]
|
||||
(when-let [actions (when (and @group-chat @active)
|
||||
(let [{:keys [group-chat chat-id]}
|
||||
(subscribe [:chat-properties [:group-chat :chat-id]])]
|
||||
(when-let [actions (if @group-chat
|
||||
[{:title "Add Contact to chat"
|
||||
:icon :menu_group
|
||||
:icon-style {:width 25
|
||||
|
@ -123,7 +123,12 @@
|
|||
:icon :settings
|
||||
:icon-style {:width 20
|
||||
:height 13}
|
||||
:handler (fn [])}])]
|
||||
:handler (fn [])}]
|
||||
[{:title "Profile"
|
||||
:icon :menu_group
|
||||
:icon-style {:width 25
|
||||
:height 19}
|
||||
:handler #(dispatch [:show-profile @chat-id])}])]
|
||||
[view st/actions-wrapper
|
||||
[view st/actions-separator]
|
||||
[view st/actions-view
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
(ns syng-im.components.chat.content-suggestions
|
||||
(:require-macros
|
||||
[natal-shell.core :refer [with-error-view]])
|
||||
(:require [clojure.string :as cstr]
|
||||
[reagent.core :as r]
|
||||
[re-frame.core :refer [subscribe dispatch dispatch-sync]]
|
||||
[syng-im.components.react :refer [view
|
||||
icon
|
||||
text
|
||||
touchable-highlight
|
||||
list-view
|
||||
list-item]]
|
||||
[syng-im.components.chat.content-suggestions-styles :as st]
|
||||
[syng-im.utils.listview :refer [to-datasource]]
|
||||
[syng-im.utils.utils :refer [log toast http-post]]
|
||||
[syng-im.utils.logging :as log]))
|
||||
|
||||
(defn set-command-content [content]
|
||||
(dispatch [:set-chat-command-content content]))
|
||||
|
||||
(defn suggestion-list-item [suggestion]
|
||||
[touchable-highlight {:onPress (fn []
|
||||
(set-command-content (:value suggestion)))
|
||||
:underlay-color :transparent}
|
||||
[view st/suggestion-container
|
||||
[view st/suggestion-sub-container
|
||||
[text {:style st/value-text}
|
||||
(:value suggestion)]
|
||||
[text {:style st/description-text}
|
||||
(:description suggestion)]]]])
|
||||
|
||||
(defn render-row [row section-id row-id]
|
||||
(list-item [suggestion-list-item (js->clj row :keywordize-keys true)]))
|
||||
|
||||
(defn content-suggestions-view []
|
||||
(let [suggestions-atom (subscribe [:get-content-suggestions])]
|
||||
(fn []
|
||||
(let [suggestions @suggestions-atom]
|
||||
(when (seq suggestions)
|
||||
[view nil
|
||||
[touchable-highlight {:style st/drag-down-touchable
|
||||
:onPress (fn []
|
||||
;; TODO hide suggestions?
|
||||
)
|
||||
:underlay-color :transparent}
|
||||
[view nil
|
||||
[icon :drag_down st/drag-down-icon]]]
|
||||
[view (st/suggestions-container (count suggestions))
|
||||
[list-view {:dataSource (to-datasource suggestions)
|
||||
:renderRow render-row}]]])))))
|
|
@ -0,0 +1,54 @@
|
|||
(ns syng-im.components.chat.content-suggestions-styles
|
||||
(:require [syng-im.components.styles :refer [font
|
||||
color-light-blue-transparent
|
||||
color-white
|
||||
color-black
|
||||
color-blue
|
||||
color-blue-transparent
|
||||
selected-message-color
|
||||
online-color
|
||||
separator-color
|
||||
text1-color
|
||||
text2-color
|
||||
text3-color]]))
|
||||
|
||||
(def suggestion-height 56)
|
||||
|
||||
(def suggestion-container
|
||||
{:flexDirection :column
|
||||
:paddingLeft 16
|
||||
:backgroundColor color-white})
|
||||
|
||||
(def suggestion-sub-container
|
||||
{:height suggestion-height
|
||||
:borderBottomWidth 1
|
||||
:borderBottomColor separator-color})
|
||||
|
||||
(def value-text
|
||||
{:marginTop 9
|
||||
:fontSize 14
|
||||
:fontFamily font
|
||||
:color text1-color})
|
||||
|
||||
(def description-text
|
||||
{:marginTop 1.5
|
||||
:fontSize 14
|
||||
:fontFamily font
|
||||
:color text2-color})
|
||||
|
||||
(defn suggestions-container [suggestions-count]
|
||||
{:flexDirection :row
|
||||
:marginVertical 1
|
||||
:marginHorizontal 0
|
||||
:height (min 150 (* suggestion-height suggestions-count))
|
||||
:backgroundColor color-white
|
||||
:borderRadius 5})
|
||||
|
||||
(def drag-down-touchable
|
||||
{:height 22
|
||||
:alignItems :center
|
||||
:justifyContent :center})
|
||||
|
||||
(def drag-down-icon
|
||||
{:width 16
|
||||
:height 16})
|
|
@ -7,13 +7,8 @@
|
|||
chat-background
|
||||
color-black]]))
|
||||
|
||||
(def money-input
|
||||
{:flex 1
|
||||
:marginLeft 8
|
||||
:lineHeight 42
|
||||
:fontSize 32
|
||||
:fontFamily font
|
||||
:color :black})
|
||||
(def command-input-and-suggestions-container
|
||||
{:flexDirection :column})
|
||||
|
||||
(def command-input-container
|
||||
{:flexDirection :row
|
||||
|
|
|
@ -6,5 +6,4 @@
|
|||
|
||||
(defn money-input-view [command]
|
||||
[simple-command-input-view command
|
||||
{:keyboardType :numeric
|
||||
:style st/money-input}])
|
||||
{:keyboardType :numeric}])
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
(ns syng-im.components.chat.input.phone
|
||||
(:require
|
||||
[syng-im.components.chat.input.simple-command
|
||||
:refer [simple-command-input-view]]))
|
||||
:refer [simple-command-input-view]]
|
||||
[syng-im.utils.phone-number :refer [valid-mobile-number?]]))
|
||||
|
||||
(defn phone-input-view [command]
|
||||
[simple-command-input-view command {:keyboardType :phone-pad}])
|
||||
[simple-command-input-view command {:keyboardType :phone-pad}
|
||||
:validator valid-mobile-number?])
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
(ns syng-im.components.chat.input.simple-command
|
||||
(:require [re-frame.core :refer [subscribe dispatch dispatch-sync]]
|
||||
[syng-im.components.react :refer [view
|
||||
image
|
||||
icon
|
||||
text
|
||||
text-input
|
||||
touchable-highlight]]
|
||||
[syng-im.resources :as res]
|
||||
[syng-im.components.chat.content-suggestions :refer [content-suggestions-view]]
|
||||
[syng-im.components.chat.input.input-styles :as st]))
|
||||
|
||||
(defn cancel-command-input []
|
||||
|
@ -19,23 +19,31 @@
|
|||
(dispatch [:stage-command])
|
||||
(cancel-command-input))
|
||||
|
||||
(defn simple-command-input-view [command input-options]
|
||||
(defn valid? [message validator]
|
||||
(if validator
|
||||
(validator message)
|
||||
(pos? (count message))))
|
||||
|
||||
(defn simple-command-input-view [command input-options & {:keys [validator]}]
|
||||
(let [message-atom (subscribe [:get-chat-command-content])]
|
||||
(fn [command input-options]
|
||||
(fn [command input-options & {:keys [validator]}]
|
||||
(let [message @message-atom]
|
||||
[view st/command-input-and-suggestions-container
|
||||
[content-suggestions-view]
|
||||
[view st/command-input-container
|
||||
[view (st/command-text-container command)
|
||||
[text {:style st/command-text} (:text command)]]
|
||||
[text-input (merge {:style st/command-input
|
||||
:autoFocus true
|
||||
:onChangeText set-input-message
|
||||
:onSubmitEditing send-command}
|
||||
:onSubmitEditing (fn []
|
||||
(when (valid? message validator)
|
||||
(send-command)))}
|
||||
input-options)
|
||||
message]
|
||||
(if (pos? (count message))
|
||||
(if (valid? message validator)
|
||||
[touchable-highlight {:on-press send-command}
|
||||
[view st/send-container [icon :send st/send-icon]]]
|
||||
[touchable-highlight {:on-press cancel-command-input}
|
||||
[view st/cancel-container
|
||||
[image {:source res/icon-close-gray
|
||||
:style st/cancel-icon}]]])]))))
|
||||
[icon :close-gray st/cancel-icon]]])]]))))
|
||||
|
|
|
@ -18,24 +18,39 @@
|
|||
(dispatch [:send-group-chat-msg chat-id input-message])
|
||||
(dispatch [:send-chat-msg]))))
|
||||
|
||||
(defn message-valid? [staged-commands message]
|
||||
(or (and (pos? (count message))
|
||||
(not= "!" message))
|
||||
(pos? (count staged-commands))))
|
||||
|
||||
(defn try-send [chat staged-commands message]
|
||||
(when (message-valid? staged-commands message)
|
||||
(send chat message)))
|
||||
|
||||
(defn plain-message-input-view []
|
||||
(let [chat (subscribe [:get-current-chat])
|
||||
input-message-atom (subscribe [:get-chat-input-text])
|
||||
staged-commands-atom (subscribe [:get-chat-staged-commands])]
|
||||
staged-commands-atom (subscribe [:get-chat-staged-commands])
|
||||
typing-command? (subscribe [:typing-command?])]
|
||||
(fn []
|
||||
(let [input-message @input-message-atom]
|
||||
[view st/input-container
|
||||
[suggestions-view]
|
||||
[view st/input-view
|
||||
[icon :list st/list-icon]
|
||||
[touchable-highlight {:on-press #(dispatch [:switch-command-suggestions])
|
||||
:style st/switch-commands-touchable}
|
||||
[view nil
|
||||
(if @typing-command?
|
||||
[icon :close-gray st/close-icon]
|
||||
[icon :list st/list-icon])]]
|
||||
[text-input {:style st/message-input
|
||||
:autoFocus (pos? (count @staged-commands-atom))
|
||||
:onChangeText set-input-message
|
||||
:onSubmitEditing #(send @chat input-message)}
|
||||
:onSubmitEditing #(try-send @chat @staged-commands-atom
|
||||
input-message)}
|
||||
input-message]
|
||||
[icon :smile st/smile-icon]
|
||||
(when (or (pos? (count input-message))
|
||||
(pos? (count @staged-commands-atom)))
|
||||
(when (message-valid? @staged-commands-atom input-message)
|
||||
[touchable-highlight {:on-press #(send @chat input-message)}
|
||||
[view st/send-container
|
||||
[icon :send st/send-icon]]])]]))))
|
||||
|
|
|
@ -12,17 +12,22 @@
|
|||
:height 56
|
||||
:backgroundColor color-white})
|
||||
|
||||
(def switch-commands-touchable
|
||||
{:width 56
|
||||
:height 56
|
||||
:alignItems :center
|
||||
:justifyContent :center})
|
||||
|
||||
(def list-icon
|
||||
{:marginTop 22
|
||||
:marginRight 6
|
||||
:marginBottom 6
|
||||
:marginLeft 21
|
||||
:width 13
|
||||
{:width 13
|
||||
:height 12})
|
||||
|
||||
(def close-icon
|
||||
{:width 12
|
||||
:height 12})
|
||||
|
||||
(def message-input
|
||||
{:flex 1
|
||||
:marginLeft 16
|
||||
:marginTop -2
|
||||
:padding 0
|
||||
:fontSize 14
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
(:require [re-frame.core :refer [subscribe dispatch]]
|
||||
[syng-im.components.react :refer [view
|
||||
text
|
||||
icon
|
||||
touchable-highlight
|
||||
list-view
|
||||
list-item]]
|
||||
|
@ -16,12 +17,15 @@
|
|||
(defn suggestion-list-item [suggestion]
|
||||
[touchable-highlight
|
||||
{:onPress #(set-command-input (keyword (:command suggestion)))}
|
||||
[view st/suggestion-item-container
|
||||
[view st/suggestion-container
|
||||
[view st/suggestion-sub-container
|
||||
[view (st/suggestion-background suggestion)
|
||||
[text {:style st/suggestion-text}
|
||||
(:text suggestion)]]
|
||||
[text {:style st/suggestion-description}
|
||||
(:description suggestion)]]])
|
||||
[text {:style st/value-text}
|
||||
(:text suggestion)]
|
||||
[text {:style st/description-text}
|
||||
(:description suggestion)]]]])
|
||||
|
||||
(defn render-row [row _ _]
|
||||
(list-item [suggestion-list-item (js->clj row :keywordize-keys true)]))
|
||||
|
@ -31,8 +35,14 @@
|
|||
(fn []
|
||||
(let [suggestions @suggestions-atom]
|
||||
(when (seq suggestions)
|
||||
[view (st/suggestions-container suggestions)
|
||||
[view nil
|
||||
[touchable-highlight {:style st/drag-down-touchable
|
||||
:onPress (fn []
|
||||
;; TODO hide suggestions?
|
||||
)}
|
||||
[view nil
|
||||
[icon :drag_down st/drag-down-icon]]]
|
||||
[view (st/suggestions-container (count suggestions))
|
||||
[list-view {:dataSource (to-datasource suggestions)
|
||||
:enableEmptySections true
|
||||
:renderRow render-row
|
||||
:style {}}]])))))
|
||||
:renderRow render-row}]]])))))
|
||||
|
|
|
@ -1,44 +1,69 @@
|
|||
(ns syng-im.components.chat.suggestions-styles
|
||||
(:require [syng-im.components.styles :refer [font color-white]]))
|
||||
(:require [syng-im.components.styles :refer [font
|
||||
color-light-blue-transparent
|
||||
color-white
|
||||
color-black
|
||||
color-blue
|
||||
color-blue-transparent
|
||||
selected-message-color
|
||||
online-color
|
||||
separator-color
|
||||
text1-color
|
||||
text2-color
|
||||
text3-color]]))
|
||||
|
||||
(def suggestion-item-container
|
||||
{:flexDirection :row
|
||||
:marginVertical 1
|
||||
:marginHorizontal 0
|
||||
:height 40
|
||||
(def suggestion-height 88)
|
||||
|
||||
(def suggestion-container
|
||||
{:flexDirection :column
|
||||
:paddingLeft 16
|
||||
:backgroundColor color-white})
|
||||
|
||||
(def suggestion-sub-container
|
||||
{:height suggestion-height
|
||||
:borderBottomWidth 1
|
||||
:borderBottomColor separator-color})
|
||||
|
||||
(defn suggestion-background
|
||||
[{:keys [color]}]
|
||||
{:flexDirection :column
|
||||
:position :absolute
|
||||
:top 10
|
||||
:left 60
|
||||
{:alignSelf :flex-start
|
||||
:marginTop 10
|
||||
:height 24
|
||||
:backgroundColor color
|
||||
:borderRadius 10})
|
||||
:borderRadius 50})
|
||||
|
||||
(def suggestion-text
|
||||
{:marginTop -2
|
||||
:marginHorizontal 10
|
||||
:fontSize 14
|
||||
{:marginTop 2.5
|
||||
:marginHorizontal 12
|
||||
:fontSize 12
|
||||
:fontFamily font
|
||||
:color color-white})
|
||||
|
||||
(def suggestion-description
|
||||
{:flex 1
|
||||
:position :absolute
|
||||
:top 7
|
||||
:left 190
|
||||
:lineHeight 18
|
||||
(def value-text
|
||||
{:marginTop 6
|
||||
:fontSize 14
|
||||
:fontFamily font
|
||||
:color :black})
|
||||
:color text1-color})
|
||||
|
||||
(defn suggestions-container
|
||||
[suggestions]
|
||||
(def description-text
|
||||
{:marginTop 2
|
||||
:fontSize 12
|
||||
:fontFamily font
|
||||
:color text2-color})
|
||||
|
||||
(defn suggestions-container [suggestions-count]
|
||||
{:flexDirection :row
|
||||
:marginVertical 1
|
||||
:marginHorizontal 0
|
||||
:height (min 105 (* 42 (count suggestions)))
|
||||
:height (min 168 (* suggestion-height suggestions-count))
|
||||
:backgroundColor color-white
|
||||
:borderRadius 5})
|
||||
|
||||
(def drag-down-touchable
|
||||
{:height 22
|
||||
:alignItems :center
|
||||
:justifyContent :center})
|
||||
|
||||
(def drag-down-icon
|
||||
{:width 16
|
||||
:height 16})
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
[syng-im.utils.listview :refer [to-realm-datasource]]
|
||||
[reagent.core :as r]
|
||||
[syng-im.components.chats.chat-list-item :refer [chat-list-item]]
|
||||
[syng-im.components.drawer :refer [drawer-view open-drawer]]
|
||||
[syng-im.components.action-button :refer [action-button
|
||||
action-button-item]]
|
||||
[syng-im.components.styles :refer [font
|
||||
|
@ -29,7 +30,7 @@
|
|||
[toolbar {:nav-action {:image {:source {:uri "icon_hamburger"}
|
||||
:style {:width 16
|
||||
:height 12}}
|
||||
:handler (fn [])}
|
||||
:handler open-drawer}
|
||||
:title "Chats"
|
||||
:action {:image {:source {:uri "icon_search"}
|
||||
:style {:width 17
|
||||
|
@ -42,6 +43,7 @@
|
|||
(let [chats @chats
|
||||
_ (log/debug "chats=" chats)
|
||||
datasource (to-realm-datasource chats)]
|
||||
[drawer-view {:navigator navigator}
|
||||
[view {:style {:flex 1
|
||||
:backgroundColor "white"}}
|
||||
[chats-list-toolbar]
|
||||
|
@ -62,8 +64,8 @@
|
|||
[action-button-item {:title "New Group Chat"
|
||||
:buttonColor "#1abc9c"
|
||||
:onPress (fn []
|
||||
(dispatch [:show-group-new navigator]))}
|
||||
(dispatch [:show-group-new]))}
|
||||
[icon {:name "person-stalker"
|
||||
:style {:fontSize 20
|
||||
:height 22
|
||||
:color "white"}}]]]]))))
|
||||
:color "white"}}]]]]]))))
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
(ns syng-im.components.contact-list.contact
|
||||
(:require [syng-im.components.react :refer [view text image touchable-highlight]]
|
||||
(:require [re-frame.core :refer [subscribe dispatch dispatch-sync]]
|
||||
[syng-im.components.react :refer [view text image touchable-highlight]]
|
||||
[syng-im.resources :as res]
|
||||
[syng-im.navigation :as nav]
|
||||
[syng-im.components.contact-list.contact-inner :refer [contact-inner-view]]))
|
||||
|
||||
(defn show-chat [navigator whisper-identity]
|
||||
(nav/nav-push navigator {:view-id :chat}))
|
||||
(dispatch [:show-chat whisper-identity navigator :push]))
|
||||
|
||||
(defn contact-view [{:keys [navigator contact]}]
|
||||
(let [{:keys [whisper-identity]} contact]
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
(fn []
|
||||
(let [contacts-ds (get-data-source @contacts)]
|
||||
[view {:style {:flex 1
|
||||
:backgroundColor "white"}}
|
||||
:backgroundColor color-white}}
|
||||
[contact-list-toolbar navigator]
|
||||
(when contacts-ds
|
||||
[list-view {:dataSource contacts-ds
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
(ns syng-im.components.drawer
|
||||
(:require [clojure.string :as s]
|
||||
[re-frame.core :refer [subscribe dispatch dispatch-sync]]
|
||||
[reagent.core :as r]
|
||||
[syng-im.components.react :refer [android?
|
||||
view
|
||||
text
|
||||
image
|
||||
navigator
|
||||
toolbar-android
|
||||
drawer-layout-android
|
||||
touchable-opacity]]
|
||||
[syng-im.resources :as res]
|
||||
[syng-im.components.drawer-styles :as st]))
|
||||
|
||||
(defonce drawer-atom (atom))
|
||||
|
||||
(defn open-drawer []
|
||||
(.openDrawer @drawer-atom))
|
||||
|
||||
(defn close-drawer []
|
||||
(.closeDrawer @drawer-atom))
|
||||
|
||||
(defn user-photo [{:keys [photo-path]}]
|
||||
[image {:source (if (s/blank? photo-path)
|
||||
res/user-no-photo
|
||||
{:uri photo-path})
|
||||
:style st/user-photo}])
|
||||
|
||||
(defn menu-item [{:keys [name handler]}]
|
||||
[touchable-opacity {:style st/menu-item-touchable
|
||||
:onPress (fn []
|
||||
(close-drawer)
|
||||
(handler))}
|
||||
[text {:style st/menu-item-text}
|
||||
name]])
|
||||
|
||||
(defn drawer-menu [navigator]
|
||||
[view st/drawer-menu
|
||||
[view st/user-photo-container
|
||||
[user-photo {}]]
|
||||
[view st/name-container
|
||||
[text {:style st/name-text}
|
||||
"Status"]]
|
||||
[view st/menu-items-container
|
||||
[menu-item {:name "Profile"
|
||||
:handler (fn []
|
||||
(dispatch [:show-profile navigator]))}]
|
||||
[menu-item {:name "Settings"
|
||||
:handler (fn []
|
||||
;; TODO not implemented
|
||||
)}]
|
||||
[menu-item {:name "Discovery"
|
||||
:handler (fn []
|
||||
(dispatch [:navigate-to :discovery]))}]
|
||||
[menu-item {:name "Contacts"
|
||||
:handler (fn []
|
||||
(dispatch [:show-contacts navigator]))}]
|
||||
[menu-item {:name "Invite friends"
|
||||
:handler (fn []
|
||||
;; TODO not implemented
|
||||
)}]
|
||||
[menu-item {:name "FAQ"
|
||||
:handler (fn [])}]]
|
||||
[view st/switch-users-container
|
||||
[touchable-opacity {:onPress (fn []
|
||||
(close-drawer)
|
||||
;; TODO not implemented
|
||||
)}
|
||||
[text {:style st/switch-users-text}
|
||||
"Switch users"]]]])
|
||||
|
||||
(defn drawer-view [{:keys [navigator]} items]
|
||||
[drawer-layout-android {:drawerWidth 300
|
||||
:drawerPosition js/React.DrawerLayoutAndroid.positions.Left
|
||||
:render-navigation-view #(r/as-element [drawer-menu navigator])
|
||||
:ref (fn [drawer]
|
||||
(reset! drawer-atom drawer))}
|
||||
items])
|
|
@ -0,0 +1,64 @@
|
|||
(ns syng-im.components.drawer-styles
|
||||
(:require [syng-im.components.styles :refer [font
|
||||
color-light-blue-transparent
|
||||
color-white
|
||||
color-black
|
||||
color-blue
|
||||
color-blue-transparent
|
||||
selected-message-color
|
||||
online-color
|
||||
separator-color
|
||||
text1-color
|
||||
text2-color
|
||||
text3-color]]))
|
||||
|
||||
(def user-photo
|
||||
{:borderRadius 50
|
||||
:width 64
|
||||
:height 64})
|
||||
|
||||
(def menu-item-touchable
|
||||
{:height 48
|
||||
:paddingLeft 16
|
||||
:paddingTop 14})
|
||||
|
||||
(def menu-item-text
|
||||
{:fontSize 14
|
||||
:fontFamily font
|
||||
:lineHeight 21
|
||||
:color text1-color})
|
||||
|
||||
(def drawer-menu
|
||||
{:flex 1
|
||||
:backgroundColor color-white
|
||||
:flexDirection :column})
|
||||
|
||||
(def user-photo-container
|
||||
{:marginTop 40
|
||||
:alignItems :center
|
||||
:justifyContent :center})
|
||||
|
||||
(def name-container
|
||||
{:marginTop 20
|
||||
:alignItems :center})
|
||||
|
||||
(def name-text
|
||||
{:marginTop -2.5
|
||||
:color text1-color
|
||||
:fontSize 16})
|
||||
|
||||
(def menu-items-container
|
||||
{:flex 1
|
||||
:marginTop 80
|
||||
:alignItems :stretch
|
||||
:flexDirection :column})
|
||||
|
||||
(def switch-users-container
|
||||
{:paddingVertical 36
|
||||
:alignItems :center})
|
||||
|
||||
(def switch-users-text
|
||||
{:fontSize 14
|
||||
:fontFamily font
|
||||
:lineHeight 21
|
||||
:color text3-color})
|
|
@ -0,0 +1,76 @@
|
|||
(ns syng-im.components.profile
|
||||
(:require [clojure.string :as s]
|
||||
[re-frame.core :refer [subscribe dispatch dispatch-sync]]
|
||||
[syng-im.components.react :refer [android?
|
||||
view
|
||||
text
|
||||
text-input
|
||||
image
|
||||
icon
|
||||
scroll-view
|
||||
touchable-highlight
|
||||
touchable-opacity]]
|
||||
[syng-im.resources :as res]
|
||||
[syng-im.components.profile-styles :as st]))
|
||||
|
||||
(defn user-photo [{:keys [photo-path]}]
|
||||
[image {:source (if (s/blank? photo-path)
|
||||
res/user-no-photo
|
||||
{:uri photo-path})
|
||||
:style st/user-photo}])
|
||||
|
||||
(defn user-online [{:keys [online]}]
|
||||
(when online
|
||||
[view st/user-online-container
|
||||
[view st/user-online-dot-left]
|
||||
[view st/user-online-dot-right]]))
|
||||
|
||||
(defn profile-property-view [{:keys [name value]}]
|
||||
[view st/profile-property-view-container
|
||||
[view st/profile-property-view-sub-container
|
||||
[text {:style st/profile-property-view-label}
|
||||
name]
|
||||
[text {:style st/profile-property-view-value}
|
||||
value]]])
|
||||
|
||||
(defn message-user [identity]
|
||||
(when identity
|
||||
(dispatch [:show-chat identity nil :push])))
|
||||
|
||||
(defn profile []
|
||||
(let [contact (subscribe [:contact])]
|
||||
(fn []
|
||||
[scroll-view {:style st/profile}
|
||||
[touchable-highlight {:style st/profile-back-button-touchable
|
||||
:on-press #(dispatch [:navigate-back])}
|
||||
[view st/profile-back-button-container
|
||||
[icon :back st/profile-back-button-icon]]]
|
||||
[view st/status-block
|
||||
[view st/user-photo-container
|
||||
[user-photo {}]
|
||||
[user-online {:online true}]]
|
||||
[text {:style st/user-name}
|
||||
(:name @contact)]
|
||||
[text {:style st/status}
|
||||
"!not implemented"]
|
||||
[view st/btns-container
|
||||
[touchable-highlight {:onPress #(message-user (:whisper-identity @contact))}
|
||||
[view st/message-btn
|
||||
[text {:style st/message-btn-text}
|
||||
"Message"]]]
|
||||
[touchable-highlight {:onPress (fn []
|
||||
;; TODO not implemented
|
||||
)}
|
||||
[view st/more-btn
|
||||
[icon :more_vertical_blue st/more-btn-image]]]]]
|
||||
[view st/profile-properties-container
|
||||
[profile-property-view {:name "Username"
|
||||
:value (:name @contact)}]
|
||||
[profile-property-view {:name "Phone number"
|
||||
:value (:phone-number @contact)}]
|
||||
[profile-property-view {:name "Email"
|
||||
:value "!not implemented"}]
|
||||
[view st/report-user-container
|
||||
[touchable-opacity {}
|
||||
[text {:style st/report-user-text}
|
||||
"REPORT USER"]]]]])))
|
|
@ -0,0 +1,154 @@
|
|||
(ns syng-im.components.profile-styles
|
||||
(:require [syng-im.components.styles :refer [font
|
||||
color-light-blue-transparent
|
||||
color-white
|
||||
color-black
|
||||
color-blue
|
||||
color-blue-transparent
|
||||
selected-message-color
|
||||
online-color
|
||||
separator-color
|
||||
text1-color
|
||||
text2-color]]))
|
||||
|
||||
(def user-photo
|
||||
{:borderRadius 50
|
||||
:width 64
|
||||
:height 64})
|
||||
|
||||
(def user-online-container
|
||||
{:position :absolute
|
||||
:top 44
|
||||
:left 44
|
||||
:width 24
|
||||
:height 24
|
||||
:borderRadius 50
|
||||
:backgroundColor online-color
|
||||
:borderWidth 2
|
||||
:borderColor color-white})
|
||||
|
||||
(def user-online-dot
|
||||
{:position :absolute
|
||||
:top 8
|
||||
:left 5
|
||||
:width 4
|
||||
:height 4
|
||||
:borderRadius 50
|
||||
:backgroundColor color-white})
|
||||
|
||||
(def user-online-dot-left
|
||||
(assoc user-online-dot :left 5))
|
||||
|
||||
(def user-online-dot-right
|
||||
(assoc user-online-dot :left 11))
|
||||
|
||||
(def profile-property-view-container
|
||||
{:height 85
|
||||
:paddingHorizontal 16})
|
||||
|
||||
(def profile-property-view-sub-container
|
||||
{:borderBottomWidth 1
|
||||
:borderBottomColor separator-color})
|
||||
|
||||
(def profile-property-view-label
|
||||
{:marginTop 16
|
||||
:fontSize 14
|
||||
:fontFamily font
|
||||
:color text2-color})
|
||||
|
||||
(def profile-property-view-value
|
||||
{:marginTop 11
|
||||
:height 40
|
||||
:fontSize 16
|
||||
:fontFamily font
|
||||
:color text1-color})
|
||||
|
||||
(def profile
|
||||
{:flex 1
|
||||
:backgroundColor color-white
|
||||
:flexDirection :column})
|
||||
|
||||
(def profile-back-button-touchable
|
||||
{:position :absolute})
|
||||
|
||||
(def profile-back-button-container
|
||||
{:width 56
|
||||
:height 56})
|
||||
|
||||
(def profile-back-button-icon
|
||||
{:marginTop 21
|
||||
:marginLeft 23
|
||||
:width 8
|
||||
:height 14})
|
||||
|
||||
(def status-block
|
||||
{:alignSelf :center
|
||||
:alignItems :center
|
||||
:width 249})
|
||||
|
||||
(def user-photo-container
|
||||
{:marginTop 26})
|
||||
|
||||
(def user-name
|
||||
{:marginTop 20
|
||||
:fontSize 18
|
||||
:fontFamily font
|
||||
:color text1-color})
|
||||
|
||||
(def status
|
||||
{:marginTop 10
|
||||
:fontFamily font
|
||||
:fontSize 14
|
||||
:lineHeight 20
|
||||
:textAlign :center
|
||||
:color text2-color})
|
||||
|
||||
(def btns-container
|
||||
{:marginTop 18
|
||||
:flexDirection :row})
|
||||
|
||||
(def message-btn
|
||||
{:height 40
|
||||
:justifyContent :center
|
||||
:backgroundColor color-blue
|
||||
:paddingLeft 25
|
||||
:paddingRight 25
|
||||
:borderRadius 50})
|
||||
|
||||
(def message-btn-text
|
||||
{:marginTop -2.5
|
||||
:fontSize 14
|
||||
:fontFamily font
|
||||
:color color-white})
|
||||
|
||||
(def more-btn
|
||||
{:marginLeft 10
|
||||
:width 40
|
||||
:height 40
|
||||
:alignItems :center
|
||||
:justifyContent :center
|
||||
:backgroundColor color-blue-transparent
|
||||
:padding 8
|
||||
:borderRadius 50})
|
||||
|
||||
(def more-btn-image
|
||||
{:width 4
|
||||
:height 16})
|
||||
|
||||
(def profile-properties-container
|
||||
{:marginTop 20
|
||||
:alignItems :stretch
|
||||
:flexDirection :column})
|
||||
|
||||
(def report-user-container
|
||||
{:marginTop 50
|
||||
:marginBottom 43
|
||||
:alignItems :center})
|
||||
|
||||
(def report-user-text
|
||||
{:fontSize 14
|
||||
:fontFamily font
|
||||
:lineHeight 21
|
||||
:color text2-color
|
||||
;; IOS:
|
||||
:letterSpacing 0.5})
|
|
@ -26,6 +26,8 @@
|
|||
:placeholder "Type"}
|
||||
props)
|
||||
text])
|
||||
(def drawer-layout-android (r/adapt-react-class (.-DrawerLayoutAndroid js/React)))
|
||||
(def touchable-opacity (r/adapt-react-class (.-TouchableOpacity js/React)))
|
||||
|
||||
|
||||
(defn icon [n style]
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
(def title-font "sans-serif-medium")
|
||||
|
||||
(def color-blue "#7099e6")
|
||||
(def color-blue-transparent "#7099e632")
|
||||
(def color-black "#000000de")
|
||||
(def color-purple "#a187d5")
|
||||
(def color-gray "#838c93de")
|
||||
|
@ -16,8 +17,9 @@
|
|||
|
||||
(def text1-color color-black)
|
||||
(def text2-color color-gray)
|
||||
(def text3-color color-blue)
|
||||
(def online-color color-blue)
|
||||
(def new-messages-count-color "#7099e632")
|
||||
(def new-messages-count-color color-blue-transparent)
|
||||
(def chat-background color-light-gray)
|
||||
(def selected-message-color "#E4E9ED")
|
||||
(def separator-color "#0000001f")
|
||||
|
|
|
@ -15,8 +15,7 @@
|
|||
toolbar-background1]]
|
||||
[syng-im.components.realm :refer [list-view]]
|
||||
[syng-im.utils.listview :refer [to-realm-datasource]]
|
||||
[reagent.core :as r]
|
||||
[syng-im.navigation :refer [nav-pop]]))
|
||||
[reagent.core :as r]))
|
||||
|
||||
(defn toolbar [{:keys [navigator title nav-action action background-color content style]}]
|
||||
(let [style (merge {:flexDirection "row"
|
||||
|
@ -31,7 +30,7 @@
|
|||
:alignItems "center"
|
||||
:justifyContent "center"}
|
||||
[image (:image nav-action)]]]
|
||||
[touchable-highlight {:on-press #(nav-pop navigator)}
|
||||
[touchable-highlight {:on-press #(dispatch [:navigate-back])}
|
||||
[view {:width 56
|
||||
:height 56}
|
||||
[image {:source {:uri "icon_back"}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
;; schema of app-db
|
||||
(def schema {:greeting s/Str})
|
||||
|
||||
(def default-view :discovery)
|
||||
(def default-view :chat-list)
|
||||
|
||||
;; initial state of app-db
|
||||
(def app-db {:greeting "Hello Clojure in iOS and Android!"
|
||||
|
@ -27,6 +27,7 @@
|
|||
|
||||
(def protocol-initialized-path [:protocol-initialized])
|
||||
(def identity-password-path [:identity-password])
|
||||
(def contact-identity-path [:contact-identity])
|
||||
(def current-chat-id-path [:current-chat-id])
|
||||
(def updated-chats-signal-path [:chats-updated-signal])
|
||||
(defn updated-chat-signal-path [chat-id]
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
(:require
|
||||
[re-frame.core :refer [register-handler after dispatch debug enrich]]
|
||||
[schema.core :as s :include-macros true]
|
||||
[syng-im.persistence.simple-kv-store :as kv]
|
||||
[syng-im.protocol.state.storage :as storage]
|
||||
[syng-im.db :as db :refer [app-db schema]]
|
||||
[syng-im.protocol.api :refer [init-protocol]]
|
||||
[syng-im.protocol.protocol-handler :refer [make-handler]]
|
||||
|
@ -27,7 +29,8 @@
|
|||
get-command-handler
|
||||
load-commands
|
||||
apply-staged-commands
|
||||
check-suggestion]]
|
||||
check-suggestion
|
||||
switch-command-suggestions]]
|
||||
[syng-im.handlers.sign-up :as sign-up-service]
|
||||
[syng-im.components.discovery.handlers :as discovery]
|
||||
[syng-im.models.chats :refer [chat-exists?
|
||||
|
@ -76,7 +79,9 @@
|
|||
;; -- Common --------------------------------------------------------------
|
||||
|
||||
(register-handler :initialize-db
|
||||
(fn [_ _] app-db))
|
||||
(fn [_ _]
|
||||
(assoc app-db
|
||||
:signed-up (storage/get kv/kv-store :signed-up))))
|
||||
|
||||
(register-handler :set-loading
|
||||
(fn [db [_ value]]
|
||||
|
@ -445,13 +450,23 @@
|
|||
(fn [db [_ value]]
|
||||
(contacts/load-syng-contacts db)))
|
||||
|
||||
(register-handler :show-profile
|
||||
(fn [db [action identity]]
|
||||
(log/debug action)
|
||||
(let [db (contacts/set-contact-identity db identity)]
|
||||
(dispatch [:navigate-to :profile])
|
||||
db)))
|
||||
|
||||
;; -- Chats --------------------------------------------------------------
|
||||
|
||||
(register-handler :show-chat
|
||||
(fn [db [action chat-id navigator nav-type]]
|
||||
(log/debug action "chat-id" chat-id)
|
||||
(let [db (set-current-chat-id db chat-id)]
|
||||
(dispatch [:navigate-to navigator {:view-id :chat} nav-type])
|
||||
(let [db (-> db
|
||||
(create-chat chat-id [chat-id] false)
|
||||
(set-current-chat-id chat-id))]
|
||||
;; (dispatch [:navigate-to navigator {:view-id :chat} nav-type])
|
||||
(dispatch [:navigate-to :chat])
|
||||
db)))
|
||||
|
||||
(register-handler :init-console-chat
|
||||
|
@ -474,6 +489,10 @@
|
|||
(register-handler :set-chat-input-text
|
||||
((enrich update-command) update-text))
|
||||
|
||||
(register-handler :switch-command-suggestions
|
||||
(fn [db [_]]
|
||||
(switch-command-suggestions db)))
|
||||
|
||||
(register-handler :set-chat-command
|
||||
(fn [db [_ command-key]]
|
||||
;; todo what is going on there?!
|
||||
|
@ -552,9 +571,9 @@
|
|||
db)))
|
||||
|
||||
(register-handler :show-group-new
|
||||
(fn [db [action navigator]]
|
||||
(fn [db [action]]
|
||||
(log/debug action)
|
||||
(nav-push navigator {:view-id :new-group})
|
||||
(dispatch [:navigate-to :new-group])
|
||||
(clear-new-group db)))
|
||||
|
||||
(register-handler :select-for-new-group
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
(ns syng-im.handlers.content-suggestions
|
||||
(:require [re-frame.core :refer [subscribe dispatch dispatch-sync]]
|
||||
[syng-im.db :as db]
|
||||
[syng-im.utils.logging :as log]
|
||||
[clojure.string :as s]))
|
||||
|
||||
(def suggestions
|
||||
{:phone [{:value "89171111111"
|
||||
:description "Number format 1"}
|
||||
{:value "+79171111111"
|
||||
:description "Number format 2"}
|
||||
{:value "9171111111"
|
||||
:description "Number format 3"}]})
|
||||
|
||||
(defn get-content-suggestions [db command text]
|
||||
(or (when command
|
||||
(when-let [command-suggestions ((:command command) suggestions)]
|
||||
(filterv (fn [s]
|
||||
(and (.startsWith (:value s) (or text ""))
|
||||
(not= (:value s) text)))
|
||||
command-suggestions)))
|
||||
[]))
|
|
@ -1,7 +1,9 @@
|
|||
(ns syng-im.handlers.suggestions
|
||||
(:require [re-frame.core :refer [subscribe dispatch dispatch-sync]]
|
||||
[syng-im.db :as db]
|
||||
[syng-im.models.chat :refer [current-chat-id]]
|
||||
[syng-im.models.chat :refer [current-chat-id
|
||||
set-chat-input-text
|
||||
get-chat-input-text]]
|
||||
[syng-im.models.commands :refer [commands
|
||||
suggestions
|
||||
get-commands
|
||||
|
@ -62,3 +64,10 @@
|
|||
[suggestion] (filter #(= suggestion-text' (:text %))
|
||||
(get-commands db))]
|
||||
suggestion)))
|
||||
|
||||
(defn typing-command? [db]
|
||||
(let [text (get-chat-input-text db)]
|
||||
(suggestion? text)))
|
||||
|
||||
(defn switch-command-suggestions [db]
|
||||
(set-chat-input-text db (if (typing-command? db) nil "!")))
|
||||
|
|
|
@ -43,6 +43,9 @@
|
|||
(defn set-chat-input-text [db text]
|
||||
(assoc-in db (db/chat-input-text-path (current-chat-id db)) text))
|
||||
|
||||
(defn get-chat-input-text [db]
|
||||
(get-in db (db/chat-input-text-path (current-chat-id db))))
|
||||
|
||||
(comment
|
||||
|
||||
(swap! re-frame.db/app-db (fn [db]
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
(ns syng-im.models.contacts
|
||||
(:require [cljs.core.async :as async :refer [chan put! <! >!]]
|
||||
[re-frame.core :refer [subscribe dispatch dispatch-sync]]
|
||||
[syng-im.db :as db]
|
||||
[syng-im.utils.utils :refer [log toast]]
|
||||
[syng-im.persistence.realm :as realm]
|
||||
[syng-im.persistence.realm :as r]
|
||||
|
@ -106,7 +107,20 @@
|
|||
(r/sorted :name :asc))))
|
||||
|
||||
(defn contact-by-identity [identity]
|
||||
(r/single-cljs (r/get-by-field :contacts :whisper-identity identity)))
|
||||
(if (= identity "console")
|
||||
{:phone-number ""
|
||||
:whisper-identity "console"
|
||||
:name "Console"
|
||||
:photo-path ""}
|
||||
(r/single-cljs (r/get-by-field :contacts :whisper-identity identity))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;----------------------------------------------
|
||||
|
||||
(defn set-contact-identity [db contact-id]
|
||||
(assoc-in db db/contact-identity-path contact-id))
|
||||
|
||||
(defn contact-identity [db]
|
||||
(get-in db db/contact-identity-path))
|
||||
|
||||
(comment
|
||||
|
||||
|
|
|
@ -11,13 +11,17 @@
|
|||
[syng-im.models.messages :refer [get-messages]]
|
||||
[syng-im.models.contacts :refer [contacts-list
|
||||
contacts-list-exclude
|
||||
contacts-list-include]]
|
||||
contacts-list-include
|
||||
contact-identity
|
||||
contact-by-identity]]
|
||||
[syng-im.models.commands :refer [get-commands
|
||||
get-chat-command
|
||||
get-chat-command-content
|
||||
get-chat-command-request
|
||||
parse-command-request]]
|
||||
[syng-im.handlers.suggestions :refer [get-suggestions]]))
|
||||
[syng-im.handlers.suggestions :refer [get-suggestions
|
||||
typing-command?]]
|
||||
[syng-im.handlers.content-suggestions :refer [get-content-suggestions]]))
|
||||
|
||||
;; -- Chat --------------------------------------------------------------
|
||||
|
||||
|
@ -38,6 +42,16 @@
|
|||
(reaction))]
|
||||
(reaction (get-suggestions @db @input-text)))))
|
||||
|
||||
(register-sub :typing-command?
|
||||
(fn [db _]
|
||||
(reaction (typing-command? @db))))
|
||||
|
||||
(register-sub :get-content-suggestions
|
||||
(fn [db _]
|
||||
(let [command (reaction (get-chat-command @db))
|
||||
text (reaction (get-chat-command-content @db))]
|
||||
(reaction (get-content-suggestions @db @command @text)))))
|
||||
|
||||
(register-sub :get-commands
|
||||
(fn [db _]
|
||||
(reaction (get-commands @db))))
|
||||
|
@ -120,6 +134,11 @@
|
|||
(reaction
|
||||
(contacts-list))))
|
||||
|
||||
(register-sub :contact
|
||||
(fn [db _]
|
||||
(let [identity (reaction (get-in @db db/contact-identity-path))]
|
||||
(reaction (contact-by-identity @identity)))))
|
||||
|
||||
(register-sub :all-new-contacts
|
||||
(fn [db _]
|
||||
(let [current-chat-id (reaction (current-chat-id @db))
|
||||
|
|
|
@ -7,3 +7,9 @@
|
|||
|
||||
(defn format-phone-number [number]
|
||||
(str (.getNumber (js/PhoneNumber. number country-code "international"))))
|
||||
|
||||
(defn valid-mobile-number? [number]
|
||||
(when (string? number)
|
||||
(let [number-obj (js/PhoneNumber. number country-code "international")]
|
||||
(and (.isValid number-obj)
|
||||
(.isMobile number-obj)))))
|
||||
|
|