refactoring

This commit is contained in:
Roman Volosovskyi 2016-05-06 14:06:58 +03:00
parent daa2ca491c
commit 319bb656ec
18 changed files with 1003 additions and 817 deletions

View File

@ -8,7 +8,9 @@
[reagent "0.5.1" :exclusions [cljsjs/react]] [reagent "0.5.1" :exclusions [cljsjs/react]]
[re-frame "0.6.0"] [re-frame "0.6.0"]
[prismatic/schema "1.0.4"] [prismatic/schema "1.0.4"]
[syng-im/protocol "0.1.1"] ^{:voom {:repo "https://github.com/status-im/status-lib.git"
:branch "master"}}
[syng-im/protocol "0.1.1-20160430_080316-gf359cb7"]
[natal-shell "0.1.6"]] [natal-shell "0.1.6"]]
:plugins [[lein-cljsbuild "1.1.1"] :plugins [[lein-cljsbuild "1.1.1"]
[lein-figwheel "0.5.0-2"]] [lein-figwheel "0.5.0-2"]]
@ -44,4 +46,4 @@
:main "env.android.main" :main "env.android.main"
:output-dir "target/android" :output-dir "target/android"
:optimizations :simple}}}} :optimizations :simple}}}}
}}) }})

View File

@ -21,50 +21,39 @@
(def back-button-handler (cljs/atom {:nav nil (def back-button-handler (cljs/atom {:nav nil
:handler nil})) :handler nil}))
(defn init-back-button-handler! [nav] (defn init-back-button-handler! []
(let [handler @back-button-handler] (let [new-listener (fn []
(when-not (= nav (:nav handler)) ;; todo: it might be better always return false from
(remove-event-listener "hardwareBackPress" (:handler handler)) ;; this listener and handle application's closing
(let [new-listener (fn [] ;; in handlers
(binding [nav/*nav-render* false] (let [stack (subscribe [:navigation-stack])]
(when (< 1 (.-length (.getCurrentRoutes nav))) (when (< 1 (count stack))
(nav/nav-pop nav) (dispatch [:navigate-back])
true)))] true)))]
(reset! back-button-handler {:nav nav (add-event-listener "hardwareBackPress" new-listener)))
:handler new-listener})
(add-event-listener "hardwareBackPress" new-listener)))))
(defn app-root [] (defn app-root []
(let [signed-up-atom (subscribe [:signed-up])] (let [signed-up (subscribe [:signed-up])
view-id (subscribe [:view-id])]
(fn [] (fn []
(let [signed-up @signed-up-atom] (case (if @signed-up @view-id :chat)
[navigator {:initial-route (clj->js {:view-id :add-participants [new-participants]
:chat-list :remove-participants [remove-participants]
;:chat :chat-list [chats-list]
}) :new-group [new-group]
:render-scene (fn [route nav] :contact-list [contact-list]
(log/debug "route" route) :chat [chat]))))
(when true ;; nav/*nav-render*
(if signed-up
(let [{:keys [view-id]} (js->clj route :keywordize-keys true)
view-id (keyword view-id)]
(init-back-button-handler! nav)
(case view-id
:add-participants (r/as-element [new-participants {:navigator nav}])
:remove-participants (r/as-element [remove-participants {:navigator nav}])
:chat-list (r/as-element [chats-list {:navigator nav}])
:new-group (r/as-element [new-group {:navigator nav}])
:contact-list (r/as-element [contact-list {:navigator nav}])
:chat (r/as-element [chat {:navigator nav}])))
(r/as-element [chat {:navigator nav}]))))}]))))
(defn init [] (defn init []
(dispatch-sync [:initialize-db]) (dispatch-sync [:initialize-db])
(dispatch [:initialize-crypt]) (dispatch [:initialize-crypt])
(dispatch [:initialize-chats])
(dispatch [:initialize-protocol]) (dispatch [:initialize-protocol])
(dispatch [:load-user-phone-number]) (dispatch [:load-user-phone-number])
(dispatch [:load-syng-contacts]) (dispatch [:load-syng-contacts])
;; load commands from remote server (todo: uncomment) ;; load commands from remote server (todo: uncomment)
;; (dispatch [:load-commands]) ;; (dispatch [:load-commands])
(dispatch-sync [:init-console-chat]) (dispatch [:init-console-chat])
(.registerComponent app-registry "SyngIm" #(r/reactify-component app-root))) #_(dispatch [:init-chat])
#_(init-back-button-handler!)
#_(.registerComponent app-registry "SyngIm" #(r/reactify-component app-root)))

View File

@ -5,8 +5,11 @@
text text
image image
navigator navigator
touchable-highlight]] touchable-highlight
[syng-im.components.realm :refer [list-view]] toolbar-android
list-view
list-item
android?]]
[syng-im.components.styles :refer [font [syng-im.components.styles :refer [font
title-font title-font
color-white color-white
@ -18,11 +21,11 @@
text2-color text2-color
toolbar-background1]] toolbar-background1]]
[syng-im.utils.logging :as log] [syng-im.utils.logging :as log]
[syng-im.navigation :refer [nav-pop]]
[syng-im.resources :as res] [syng-im.resources :as res]
[syng-im.utils.listview :refer [to-realm-datasource]] [syng-im.constants :refer [content-type-status]]
[syng-im.utils.listview :refer [to-datasource
to-datasource2]]
[syng-im.components.invertible-scroll-view :refer [invertible-scroll-view]] [syng-im.components.invertible-scroll-view :refer [invertible-scroll-view]]
[reagent.core :as r]
[syng-im.components.chat.chat-message :refer [chat-message]] [syng-im.components.chat.chat-message :refer [chat-message]]
[syng-im.components.chat.chat-message-new :refer [chat-message-new]])) [syng-im.components.chat.chat-message-new :refer [chat-message-new]]))
@ -78,12 +81,12 @@
:backgroundColor color-white}]])) :backgroundColor color-white}]]))
(defn typing [member] (defn typing [member]
[view {:style {:width 260 [view {:style {:width 260
:marginTop 10 :marginTop 10
:paddingLeft 8 :paddingLeft 8
:paddingRight 8 :paddingRight 8
:alignItems "flex-start" :alignItems "flex-start"
:alignSelf "flex-start"}} :alignSelf "flex-start"}}
[view {:style {:borderRadius 14 [view {:style {:borderRadius 14
:padding 12 :padding 12
:height 38 :height 38
@ -99,6 +102,89 @@
(for [member ["Geoff" "Justas"]] (for [member ["Geoff" "Justas"]]
^{:key member} [typing member])]) ^{:key member} [typing member])])
(defn toolbar-content-chat [group-chat]
(let
[contacts (subscribe [:chat :contacts])
name (subscribe [:chat :name])]
(fn [group-chat]
[view {:style {:flex 1
:flexDirection "row"
:backgroundColor "transparent"}}
[view {:style {:flex 1
:alignItems "flex-start"
:justifyContent "center"
:marginRight 112}}
[text {:style {:marginTop -2.5
:color text1-color
:fontSize 16
:fontFamily font}}
(or @name "Chat name")]
(if group-chat
[view {:style {:flexDirection "row"}}
[image {:source {:uri :icon_group}
:style {:marginTop 4
:width 14
:height 9}}]
[text {:style {:marginTop -0.5
:marginLeft 4
:fontFamily font
:fontSize 12
:color text2-color}}
(let [cnt (count @contacts)]
(str cnt
(if (< 1 cnt)
;; TODO https://github.com/r0man/inflections-clj
" members"
" member")
", " cnt " active"))]]
[text {:style {:marginTop 1
:color text2-color
:fontSize 12
:fontFamily font}}
"Active a minute ago"])]
(when-not group-chat
[view {:style {:position "absolute"
:top 10
:right 66}}
[chat-photo {}]
[contact-online {:online true}]])])))
(defn message-row [contact-by-identity group-chat]
(fn [row _ _]
(let [msg (-> row
(add-msg-color contact-by-identity)
(assoc :group-chat group-chat))]
(list-item [chat-message msg]))))
(def group-caht-actions
[{:title "Add Contact to chat"
:icon res/add-icon
:showWithText true}
{:title "Remove Contact from chat"
:icon res/trash-icon
:showWithText true}
{:title "Leave Chat"
:icon res/leave-icon
:showWithText true}])
(defn on-action-selected [position]
(case position
0 (dispatch [:show-add-participants #_navigator])
1 (dispatch [:show-remove-participants #_navigator])
2 (dispatch [:leave-group-chat #_navigator])))
(defn overlay [{:keys [on-click-outside]} items]
[view {:position :absolute
:top 0
:bottom 0
:left 0
:right 0}
[touchable-highlight {:on-press on-click-outside
:underlay-color :transparent
:style {:flex 1}}
[view nil]]
items])
(defn action-view [action] (defn action-view [action]
[touchable-highlight {:on-press (fn [] [touchable-highlight {:on-press (fn []
(dispatch [:set-show-actions false]) (dispatch [:set-show-actions false])
@ -127,156 +213,140 @@
:fontFamily font}} :fontFamily font}}
subtitle])]]]) subtitle])]]])
(defn actions-list-view [navigator chat] (defn actions-list-view []
(when-let [actions (when (and (:group-chat chat) (let [{:keys [group-chat active]}
(:is-active chat)) (subscribe [:chat-properties [:group-chat :name :contacts :active]])]
[{:title "Add Contact to chat" (when-let [actions (when (and @group-chat @active)
:icon "icon_menu_group" [{:title "Add Contact to chat"
:icon-style {:width 25 :icon :icon_menu_group
:height 19} :icon-style {:width 25
:handler #(dispatch [:show-add-participants navigator])} :height 19}
{:title "Remove Contact from chat" :handler nil #_#(dispatch [:show-add-participants
:subtitle "Alex, John" navigator])}
:icon "icon_search_gray_copy" {:title "Remove Contact from chat"
:icon-style {:width 17 :subtitle "Alex, John"
:height 17} :icon :icon_search_gray_copy
:handler #(dispatch [:show-remove-participants navigator])} :icon-style {:width 17
{:title "Leave Chat" :height 17}
:icon "icon_muted" :handler nil #_#(dispatch
:icon-style {:width 18 [:show-remove-participants navigator])}
:height 21} {:title "Leave Chat"
:handler #(dispatch [:leave-group-chat navigator])} :icon :icon_muted
{:title "Settings" :icon-style {:width 18
:subtitle "Not implemented" :height 21}
:icon "icon_settings" :handler nil #_#(dispatch [:leave-group-chat
:icon-style {:width 20 navigator])}
:height 13} {:title "Settings"
:handler (fn [] )}])] :subtitle "Not implemented"
[view {:style {:backgroundColor toolbar-background1 :icon :icon_settings
:elevation 2 :icon-style {:width 20
:position "absolute" :height 13}
:top 56 :handler (fn [])}])]
:left 0 [view {:style {:backgroundColor toolbar-background1
:right 0}} :elevation 2
[view {:style {:marginLeft 16 :position :absolute
:height 1.5 :top 56
:backgroundColor separator-color}}] :left 0
[view {:style {:marginVertical 10}} :right 0}}
(for [action actions] [view {:style {:marginLeft 16
^{:key action} [action-view action])]])) :height 1.5
:backgroundColor separator-color}}]
[view {:style {:marginVertical 10}}
(for [action actions]
^{:key action} [action-view action])]])))
(defn overlay [{:keys [on-click-outside]} items] (defn actions-view []
[view {:position "absolute" [overlay {:on-click-outside #(dispatch [:set-show-actions false])}
:top 0 [actions-list-view]])
:bottom 0
:left 0
:right 0}
[touchable-highlight {:on-press on-click-outside
:underlay-color :transparent
:style {:flex 1}}
[view nil]]
items])
(defn actions-view [navigator chat] (defn toolbar []
[overlay {:on-click-outside (fn [] (let [{:keys [group-chat name contacts]}
(dispatch [:set-show-actions false]))} (subscribe [:chat-properties [:group-chat :name :contacts]])
[actions-list-view navigator chat]]) show-actions (subscribe [:show-actions])]
(fn []
[view {:style {:flexDirection "row"
:height 56
:backgroundColor toolbar-background1
:elevation 2}}
(when (not @show-actions)
[touchable-highlight {:on-press #(dispatch [:navigate-back])
:underlay-color :transparent}
[view {:width 56
:height 56}
[image {:source {:uri "icon_back"}
:style {:marginTop 21
:marginLeft 23
:width 8
:height 14}}]]])
[view {:style {:flex 1
:marginLeft (if @show-actions 16 0)
:alignItems "flex-start"
:justifyContent "center"}}
[text {:style {:marginTop -2.5
:color text1-color
:fontSize 16
:fontFamily font}}
(or @name "Chat name")]
(if @group-chat
[view {:style {:flexDirection :row}}
[image {:source {:uri :icon_group}
:style {:marginTop 4
:width 14
:height 9}}]
[text {:style {:marginTop -0.5
:marginLeft 4
:fontFamily font
:fontSize 12
:color text2-color}}
(let [cnt (count @contacts)]
(str cnt
(if (< 1 cnt)
" members"
" member")
", " cnt " active"))]]
[text {:style {:marginTop 1
:color text2-color
:fontSize 12
:fontFamily font}}
"Active a minute ago"])]
(if @show-actions
[touchable-highlight
{:on-press #(dispatch [:set-show-actions false])
:underlay-color :transparent}
[view {:style {:width 56
:height 56}}
[image {:source {:uri :icon_up}
:style {:marginTop 23
:marginLeft 21
:width 14
:height 8}}]]]
[touchable-highlight
{:on-press #(dispatch [:set-show-actions true])
:underlay-color :transparent}
[view {:style {:width 56
:height 56}}
[chat-photo {}]
[contact-online {:online true}]]])])))
(defn toolbar [navigator chat show-actions] (defn messages-view [group-chat]
[view {:style {:flexDirection "row" (let [messages (subscribe [:chat :messages])
:height 56 contacts (subscribe [:chat :contacts])]
:backgroundColor toolbar-background1 (fn [group-chat]
:elevation 2}} (let [contacts' (contacts-by-identity @contacts)]
(when (not show-actions) [list-view {:renderRow (message-row contacts' group-chat)
[touchable-highlight {:on-press (fn [] :renderScrollComponent #(invertible-scroll-view (js->clj %))
(nav-pop navigator)) :onEndReached #(dispatch [:load-more-messages])
:underlay-color :transparent} :dataSource (to-datasource2 @messages)}]))))
[view {:width 56
:height 56}
[image {:source {:uri "icon_back"}
:style {:marginTop 21
:marginLeft 23
:width 8
:height 14}}]]])
[view {:style {:flex 1
:marginLeft (if show-actions 16 0)
:alignItems "flex-start"
:justifyContent "center"}}
[text {:style {:marginTop -2.5
:color text1-color
:fontSize 16
:fontFamily font}}
(or (chat :name)
"Chat name")]
(if (:group-chat chat)
[view {:style {:flexDirection "row"}}
[image {:source {:uri "icon_group"}
:style {:marginTop 4
:width 14
:height 9}}]
[text {:style {:marginTop -0.5
:marginLeft 4
:fontFamily font
:fontSize 12
:color text2-color}}
(str (count (:contacts chat))
(if (< 1 (count (:contacts chat)))
" members"
" member")
", " (count (:contacts chat)) " active")]]
[text {:style {:marginTop 1
:color text2-color
:fontSize 12
:fontFamily font}}
"Active a minute ago"])]
(if show-actions
[touchable-highlight {:on-press (fn []
(dispatch [:set-show-actions false]))
:underlay-color :transparent}
[view {:style {:width 56
:height 56}}
[image {:source {:uri "icon_up"}
:style {:marginTop 23
:marginLeft 21
:width 14
:height 8}}]]]
[touchable-highlight {:on-press (fn []
(dispatch [:set-show-actions true]))
:underlay-color :transparent}
[view {:style {:width 56
:height 56}}
[chat-photo {}]
[contact-online {:online true}]]])])
(defn chat [{:keys [navigator]}] (defn chat []
(let [messages (subscribe [:get-chat-messages]) (let [is-active (subscribe [:chat :is-active])
chat (subscribe [:get-current-chat]) group-chat (subscribe [:chat :group-chat])
show-actions-atom (subscribe [:show-actions])] show-actions-atom (subscribe [:show-actions])]
(fn [] (fn []
(let [msgs @messages [view {:style {:flex 1
;_ (log/debug "messages=" msgs) :backgroundColor chat-background}}
;; temp [toolbar]
typing (:group-chat @chat) [messages-view @group-chat]
;; end temp (when @group-chat [typing-all])
datasource (to-realm-datasource msgs) (when is-active [chat-message-new])
contacts (:contacts @chat) (when @show-actions-atom [actions-view])])))
contact-by-identity (contacts-by-identity contacts)]
[view {:style {:flex 1
:backgroundColor chat-background}}
[toolbar navigator @chat @show-actions-atom]
(let [last-msg-id (:last-msg-id @chat)]
[list-view {:dataSource datasource
:renderScrollComponent (fn [props]
(invertible-scroll-view (js->clj props)))
:renderRow (fn [row section-id row-id]
(let [msg (-> (js->clj row :keywordize-keys true)
(add-msg-color contact-by-identity)
(assoc :group-chat (:group-chat @chat))
(assoc :typing typing))]
(r/as-element [chat-message msg last-msg-id])))}])
(when (:group-chat @chat)
[typing-all])
(when (:is-active @chat)
[chat-message-new])
(when @show-actions-atom
[actions-view navigator @chat])]))))

View File

@ -43,13 +43,12 @@
[view {:style {:backgroundColor color-light-blue-transparent [view {:style {:backgroundColor color-light-blue-transparent
:height 24 :height 24
:borderRadius 50 :borderRadius 50
:alignSelf "center" :alignSelf :center
:marginTop 20 :marginTop 20
:marginBottom 20 :marginBottom 20
:paddingTop 5 :paddingTop 5
:paddingHorizontal 12}} :paddingHorizontal 12}}
[text {:style (merge style-sub-text [text {:style (assoc style-sub-text :textAlign :center)}
{:textAlign "center"})}
date]]]) date]]])
(defn contact-photo [{:keys [photo-path]}] (defn contact-photo [{:keys [photo-path]}]
@ -63,7 +62,7 @@
(defn contact-online [{:keys [online]}] (defn contact-online [{:keys [online]}]
(when online (when online
[view {:position "absolute" [view {:position :absolute
:top 44 :top 44
:left 44 :left 44
:width 24 :width 24
@ -72,14 +71,14 @@
:backgroundColor online-color :backgroundColor online-color
:borderWidth 2 :borderWidth 2
:borderColor color-white} :borderColor color-white}
[view {:position "absolute" [view {:position :absolute
:top 8 :top 8
:left 5 :left 5
:width 4 :width 4
:height 4 :height 4
:borderRadius 50 :borderRadius 50
:backgroundColor color-white}] :backgroundColor color-white}]
[view {:position "absolute" [view {:position :absolute
:top 8 :top 8
:left 11 :left 11
:width 4 :width 4
@ -88,11 +87,11 @@
:backgroundColor color-white}]])) :backgroundColor color-white}]]))
(defn message-content-status [from content] (defn message-content-status [{:keys [from content]}]
[view {:style {:flex 1 [view {:style {:flex 1
:alignSelf "center" :alignSelf :center
:alignItems "center" :alignItems :center
:width 249}} :width 249}}
[view {:style {:marginTop 20}} [view {:style {:marginTop 20}}
[contact-photo {}] [contact-photo {}]
[contact-online {:online true}]] [contact-online {:online true}]]
@ -105,13 +104,13 @@
:fontFamily font :fontFamily font
:fontSize 14 :fontSize 14
:lineHeight 20 :lineHeight 20
:textAlign "center" :textAlign :center
:color text2-color}} :color text2-color}}
content]]) content]])
(defn message-content-audio [{:keys [content-type content-type]}] (defn message-content-audio [_]
[view {:style {:flexDirection "row" [view {:style {:flexDirection :row
:alignItems "center"}} :alignItems :center}}
[view {:style {:width 33 [view {:style {:width 33
:height 33 :height 33
:borderRadius 50 :borderRadius 50
@ -124,18 +123,18 @@
:width 120 :width 120
:height 26 :height 26
:elevation 1}} :elevation 1}}
[view {:style {:position "absolute" [view {:style {:position :absolute
:top 4 :top 4
:width 120 :width 120
:height 2 :height 2
:backgroundColor "#EC7262"}}] :backgroundColor "#EC7262"}}]
[view {:style {:position "absolute" [view {:style {:position :absolute
:left 0 :left 0
:top 0 :top 0
:width 2 :width 2
:height 10 :height 10
:backgroundColor "#4A5258"}}] :backgroundColor "#4A5258"}}]
[text {:style {:position "absolute" [text {:style {:position :absolute
:left 1 :left 1
:top 11 :top 11
:fontFamily font :fontFamily font
@ -150,25 +149,26 @@
(let [commands-atom (subscribe [:get-commands])] (let [commands-atom (subscribe [:get-commands])]
(fn [content] (fn [content]
(let [commands @commands-atom (let [commands @commands-atom
{:keys [command content]} (parse-command-msg-content commands content)] {:keys [command content]}
[view {:style {:flexDirection "column"}} (parse-command-msg-content commands content)]
[view {:style {:flexDirection "row" [view {:style {:flexDirection :column}}
[view {:style {:flexDirection :row
:marginRight 32}} :marginRight 32}}
[view {:style {:backgroundColor (:color command) [view {:style {:backgroundColor (:color command)
:height 24 :height 24
:borderRadius 50 :borderRadius 50
:paddingTop 3 :paddingTop 3
:paddingHorizontal 12}} :paddingHorizontal 12}}
[text {:style {:fontSize 12 [text {:style {:fontSize 12
:fontFamily font :fontFamily font
:color color-white}} :color color-white}}
(:text command)]]] (:text command)]]]
[image {:source (:icon command) [image {:source (:icon command)
:style {:position "absolute" :style {:position :absolute
:top 4 :top 4
:right 0 :right 0
:width 12 :width 12
:height 13}}] :height 13}}]
[text {:style (merge style-message-text [text {:style (merge style-message-text
{:marginTop 8 {:marginTop 8
:marginHorizontal 0})} :marginHorizontal 0})}
@ -180,13 +180,14 @@
(defn set-chat-command [msg-id command] (defn set-chat-command [msg-id command]
(dispatch [:set-response-chat-command msg-id (:command command)])) (dispatch [:set-response-chat-command msg-id (:command command)]))
(defn message-content-command-request [msg-id from content outgoing group-chat]
(defn message-content-command-request
[{:keys [msg-id content outgoing group-chat from]}]
(let [commands-atom (subscribe [:get-commands])] (let [commands-atom (subscribe [:get-commands])]
(fn [msg-id from content outgoing group-chat] (fn [{:keys [msg-id content outgoing group-chat from]}]
(let [commands @commands-atom (let [commands @commands-atom
{:keys [command content]} (parse-command-request-msg-content commands content)] {:keys [command content]} (parse-command-request-msg-content commands content)]
[touchable-highlight {:onPress (fn [] [touchable-highlight {:onPress #(set-chat-command msg-id command)
(set-chat-command msg-id command))
:underlay-color :transparent} :underlay-color :transparent}
[view {:style {:paddingRight 16}} [view {:style {:paddingRight 16}}
[view {:style (merge {:borderRadius 14 [view {:style (merge {:borderRadius 14
@ -199,7 +200,7 @@
from]) from])
[text {:style style-message-text} [text {:style style-message-text}
content]] content]]
[view {:style {:position "absolute" [view {:style {:position :absolute
:top 12 :top 12
:right 0 :right 0
:width 32 :width 32
@ -207,14 +208,14 @@
:borderRadius 50 :borderRadius 50
:backgroundColor (:color command)}} :backgroundColor (:color command)}}
[image {:source (:request-icon command) [image {:source (:request-icon command)
:style {:position "absolute" :style {:position :absolute
:top 9 :top 9
:left 10 :left 10
:width 12 :width 12
:height 13}}]] :height 13}}]]
(when (:request-text command) (when (:request-text command)
[view {:style {:marginTop 4 [view {:style {:marginTop 4
:height 14}} :height 14}}
[text {:style style-sub-text} [text {:style style-sub-text}
(:request-text command)]])]])))) (:request-text command)]])]]))))
@ -227,41 +228,105 @@
{:color color-white}))} {:color color-white}))}
content]) content])
(defn message-content [{:keys [msg-id from content-type content outgoing group-chat selected]}]
(if (= content-type content-type-command-request) #_(defn message-content [{:keys [msg-id from content-type content outgoing
[message-content-command-request msg-id from content outgoing group-chat] group-chat selected]}]
[view {:style (merge {:borderRadius 14 (if (= content-type content-type-command-request)
:padding 12 [message-content-command-request msg-id from content outgoing group-chat]
:backgroundColor color-white} [view {:style (merge {:borderRadius 14
(when (= content-type content-type-command) :padding 12
{:paddingTop 10 :backgroundColor color-white}
:paddingBottom 14}) (when (= content-type content-type-command)
(if outgoing {:paddingTop 10
(when (and group-chat (= content-type text-content-type)) :paddingBottom 14})
{:backgroundColor color-blue}) (if outgoing
(when selected (when (and group-chat (= content-type text-content-type))
{:backgroundColor selected-message-color})))} {:backgroundColor color-blue})
(when (and group-chat (not outgoing)) (when selected
[text {:style (merge style-sub-text {:backgroundColor selected-message-color})))}
{:marginBottom 2})} (when (and group-chat (not outgoing))
from]) [text {:style (merge style-sub-text
(cond {:marginBottom 2})}
(or (= content-type text-content-type) from])
(= content-type content-type-status)) (cond
[message-content-plain content outgoing group-chat] (or (= content-type text-content-type)
(= content-type content-type-command) (= content-type content-type-status))
[message-content-command content] [message-content-plain content outgoing group-chat]
:else [message-content-audio {:content content (= content-type content-type-command)
:content-type content-type}])])) [message-content-command content]
:else [message-content-audio {:content content
:content-type content-type}])]))
(defn message-view
[{:keys [content-type outgoing background-color group-chat selected]} content]
[view {:style (merge {:borderRadius 14
:padding 12}
(if outgoing
(if (and group-chat (= content-type text-content-type))
{:backgroundColor color-blue}
{:backgroundColor color-white})
(if selected
{:backgroundColor selected-message-color}
{:backgroundColor background-color})))}
#_(when (and group-chat (not outgoing))
[text {:style {:marginTop 0
:fontSize 12
:fontFamily font}}
"Justas"])
content])
(defmulti message-content (fn [_ message]
(message :content-type)))
(defmethod message-content content-type-command-request
[wrapper message]
[wrapper message [message-content-command-request message]])
(defn text-message
[{:keys [content outgoing text-color group-chat] :as message}]
[message-view message
[text {:style {:marginTop (if (and group-chat (not outgoing))
4
0)
:fontSize 14
:fontFamily font
:color (cond
(and outgoing group-chat) color-white
outgoing text1-color
:else text-color)}}
content]])
(defmethod message-content text-content-type
[wrapper message]
[wrapper message [text-message message]])
(defmethod message-content content-type-status
[_ message]
;; todo should it be rendered as text message?
[message-content-status message]
#_[text-message message])
(defmethod message-content content-type-command
[wrapper {:keys [content] :as message}]
[wrapper message
[message-view message [message-content-command content]]])
(defmethod message-content :default
[wrapper {:keys [content-type content] :as message}]
[wrapper message
[message-view message
[message-content-audio {:content content
:content-type content-type}]]])
(defn message-delivery-status [{:keys [delivery-status]}] (defn message-delivery-status [{:keys [delivery-status]}]
[view {:style {:flexDirection "row" [view {:style {:flexDirection :row
:marginTop 2}} :marginTop 2}}
[image {:source (case delivery-status [image {:source (case delivery-status
:delivered {:uri "icon_ok_small"} :delivered {:uri :icon_ok_small}
:seen {:uri "icon_ok_small"} :seen {:uri :icon_ok_small}
:seen-by-everyone {:uri "icon_ok_small"} :seen-by-everyone {:uri :icon_ok_small}
:failed res/delivery-failed-icon) :failed res/delivery-failed-icon)
:style {:marginTop 6 :style {:marginTop 6
:width 9 :width 9
:height 7}}] :height 7}}]
@ -284,11 +349,12 @@
:width 24 :width 24
:height 24}}]]) :height 24}}]])
(defn incoming-group-message-body [{:keys [msg-id from content content-type outgoing
delivery-status selected new-day same-author (defn incoming-group-message-body
same-direction last-msg typing]}] [{:keys [selected new-day same-author same-direction last-msg typing]}
content]
(let [delivery-status :seen-by-everyone] (let [delivery-status :seen-by-everyone]
[view {:style {:flexDirection "column"}} [view {:style {:flexDirection :column}}
(when selected (when selected
[text {:style {:marginTop 18 [text {:style {:marginTop 18
:marginLeft 40 :marginLeft 40
@ -296,13 +362,13 @@
:fontSize 12 :fontSize 12
:color text2-color}} :color text2-color}}
"Mar 7th, 15:22"]) "Mar 7th, 15:22"])
[view {:style (merge {:flexDirection "row" [view {:style (merge {:flexDirection :row
:alignSelf "flex-start" :alignSelf :flex-start
:marginTop (cond :marginTop (cond
new-day 0 new-day 0
same-author 4 same-author 4
same-direction 20 same-direction 20
:else 10) :else 10)
:paddingRight 8 :paddingRight 8
:paddingLeft 8} :paddingLeft 8}
(when (and last-msg (not typing)) (when (and last-msg (not typing))
@ -310,71 +376,50 @@
[view {:style {:width 24}} [view {:style {:width 24}}
(when (not same-author) (when (not same-author)
[member-photo {}])] [member-photo {}])]
[view {:style {:flexDirection "column" [view {:style {:flexDirection :column
:width 260 :width 260
:paddingLeft 8 :paddingLeft 8
:alignItems "flex-start"}} :alignItems :flex-start}}
[message-content {:msg-id msg-id content
:from from
:content-type content-type
:content content
:outgoing outgoing
:group-chat true
:selected selected}]
;; TODO show for last or selected ;; TODO show for last or selected
(when (and selected delivery-status) (when (and selected delivery-status)
[message-delivery-status {:delivery-status delivery-status}])]]])) [message-delivery-status {:delivery-status delivery-status}])]]]))
(defn message-body [{:keys [msg-id content content-type outgoing delivery-status (defn message-body
group-chat new-day same-author same-direction last-msg typing]}] [{:keys [outgoing new-day same-author same-direction last-msg typing]}
(let [delivery-status :seen] content]
[view {:style (merge {:flexDirection "column" (let [delivery-status :seen
align (if outgoing :flex-end :flex-start)]
[view {:style (merge {:flexDirection :column
:width 260 :width 260
:paddingTop (cond :paddingTop (cond
new-day 0 new-day 0
same-author 4 same-author 4
same-direction 20 same-direction 20
:else 10) :else 10)
:paddingRight 8 :paddingRight 8
:paddingLeft 8} :paddingLeft 8
(if outgoing :alignSelf align
{:alignSelf "flex-end" :alignItems align}
:alignItems "flex-end"}
{:alignItems "flex-start"
:alignSelf "flex-start"})
(when (and last-msg (not typing)) (when (and last-msg (not typing))
{:paddingBottom 20}))} {:paddingBottom 20}))}
[message-content {:msg-id msg-id content
:content-type content-type
:content content
:outgoing outgoing
:group-chat group-chat}]
(when (and outgoing delivery-status) (when (and outgoing delivery-status)
[message-delivery-status {:delivery-status delivery-status}])])) [message-delivery-status {:delivery-status delivery-status}])]))
(defn chat-message [{:keys [msg-id from content content-type outgoing delivery-status (defn chat-message
date new-day group-chat selected same-author same-direction [{:keys [msg-id outgoing delivery-status date new-day group-chat]
last-msg typing] :as msg} :as message}
last-msg-id] last-msg-id]
[view {} [view {}
(when new-day (when new-day
[message-date {:date date}]) [message-date {:date date}])
(let [msg-data {:msg-id msg-id (let [msg-data
:from from (merge message {:delivery-status (keyword delivery-status)
:content content :last-msg (= last-msg-id msg-id)})]
:content-type content-type
:outgoing outgoing
:delivery-status (keyword delivery-status)
:group-chat group-chat
:selected selected
:new-day new-day
:same-author same-author
:same-direction same-direction
:last-msg (= last-msg-id msg-id)
:typing typing}]
[view {} [view {}
(when (= content-type content-type-status) [message-content
[message-content-status from content]) (if (and group-chat (not outgoing))
(if (and group-chat (not outgoing)) incoming-group-message-body
[incoming-group-message-body msg-data] message-body)
[message-body msg-data])])]) msg-data]])])

View File

@ -12,31 +12,27 @@
text1-color text1-color
text2-color]] text2-color]]
[syng-im.utils.utils :refer [log toast http-post]] [syng-im.utils.utils :refer [log toast http-post]]
[syng-im.utils.logging :as log] [syng-im.resources :as res]))
[syng-im.resources :as res]
[reagent.core :as r]))
(defn cancel-command-input [] (defn cancel-command-input []
(dispatch [:set-chat-command nil])) (dispatch [:cancel-command]))
(defn set-input-message [message] (defn set-input-message [message]
(dispatch [:set-chat-command-content message])) (dispatch [:set-chat-command-content message]))
(defn send-command [chat-id command text] (defn send-command []
(dispatch [:stage-command chat-id command text]) (dispatch [:stage-command])
(cancel-command-input)) (cancel-command-input))
(defn simple-command-input-view [command input-options] (defn simple-command-input-view [command input-options]
(let [chat-id-atom (subscribe [:get-current-chat-id]) (let [message-atom (subscribe [:get-chat-command-content])]
message-atom (subscribe [:get-chat-command-content])]
(fn [command input-options] (fn [command input-options]
(let [chat-id @chat-id-atom (let [message @message-atom]
message @message-atom] [view {:style {:flexDirection :row
[view {:style {:flexDirection "row" :height 56
:height 56 :backgroundColor color-white
:backgroundColor color-white :elevation 4}}
:elevation 4}} [view {:style {:flexDirection :column
[view {:style {:flexDirection "column"
:marginTop 16 :marginTop 16
:marginBottom 16 :marginBottom 16
:marginLeft 16 :marginLeft 16
@ -50,7 +46,7 @@
:fontFamily font :fontFamily font
:color color-white}} :color color-white}}
(:text command)]] (:text command)]]
[text-input (merge {:underlineColorAndroid "transparent" [text-input (merge {:underlineColorAndroid :transparent
:style {:flex 1 :style {:flex 1
:marginLeft 8 :marginLeft 8
:marginTop 7 :marginTop 7
@ -60,36 +56,33 @@
:autoFocus true :autoFocus true
:placeholder "Type" :placeholder "Type"
:placeholderTextColor text2-color :placeholderTextColor text2-color
:onChangeText (fn [new-text] :onChangeText set-input-message
(set-input-message new-text)) :onSubmitEditing send-command}
:onSubmitEditing (fn [e]
(send-command chat-id command message))}
input-options) input-options)
message] message]
(if (pos? (count message)) (if (pos? (count message))
[touchable-highlight {:on-press (fn [] [touchable-highlight
(send-command chat-id command message)) {:on-press send-command
:underlay-color :transparent} :underlay-color :transparent}
[view {:style {:marginTop 10 [view {:style {:marginTop 10
:marginRight 10 :marginRight 10
:width 36 :width 36
:height 36 :height 36
:borderRadius 50 :borderRadius 50
:backgroundColor color-blue}} :backgroundColor color-blue}}
[image {:source {:uri "icon_send"} [image {:source {:uri :icon_send}
:style {:marginTop 10.5 :style {:marginTop 10.5
:marginLeft 12 :marginLeft 12
:width 15 :width 15
:height 15}}]]] :height 15}}]]]
[touchable-highlight {:on-press (fn [] [touchable-highlight {:on-press cancel-command-input
(cancel-command-input))
:underlay-color :transparent} :underlay-color :transparent}
[view {:style {:marginTop 10 [view {:style {:marginTop 10
:marginRight 10 :marginRight 10
:width 36 :width 36
:height 36}} :height 36}}
[image {:source res/icon-close-gray [image {:source res/icon-close-gray
:style {:marginTop 10.5 :style {:marginTop 10.5
:marginLeft 12 :marginLeft 12
:width 12 :width 12
:height 12}}]]])])))) :height 12}}]]])]))))

View File

@ -15,8 +15,8 @@
[syng-im.resources :as res] [syng-im.resources :as res]
[reagent.core :as r])) [reagent.core :as r]))
(defn cancel-command-input [chat-id staged-command] (defn cancel-command-input [staged-command]
(dispatch [:unstage-command chat-id staged-command])) (dispatch [:unstage-command staged-command]))
(defn simple-command-staged-view [staged-command] (defn simple-command-staged-view [staged-command]
(let [chat-id-atom (subscribe [:get-current-chat-id])] (let [chat-id-atom (subscribe [:get-current-chat-id])]
@ -43,11 +43,11 @@
:fontFamily font :fontFamily font
:color color-white}} :color color-white}}
(:text command)]] (:text command)]]
[touchable-highlight {:style {:position "absolute" [touchable-highlight {:style {:position "absolute"
:top 7 :top 7
:right 4} :right 4}
:onPress (fn [] :onPress #(cancel-command-input
(cancel-command-input chat-id staged-command)) staged-command)
:underlay-color :transparent} :underlay-color :transparent}
[image {:source res/icon-close-gray [image {:source res/icon-close-gray
:style {:width 10 :style {:width 10

View File

@ -4,17 +4,13 @@
view view
image image
touchable-highlight touchable-highlight
text-input text-input]]
dismiss-keyboard]]
[syng-im.components.styles :refer [font [syng-im.components.styles :refer [font
text2-color text2-color
color-white color-white
color-blue]] color-blue]]
[syng-im.components.chat.suggestions :refer [suggestions-view]] [syng-im.components.chat.suggestions :refer [suggestions-view]]
[syng-im.utils.utils :refer [log toast http-post]] [syng-im.utils.utils :refer [log toast http-post]]))
[syng-im.utils.logging :as log]
[syng-im.resources :as res]
[reagent.core :as r]))
(defn set-input-message [message] (defn set-input-message [message]
(dispatch [:set-chat-input-text message])) (dispatch [:set-chat-input-text message]))
@ -22,32 +18,30 @@
(defn send [chat input-message] (defn send [chat input-message]
(let [{:keys [group-chat chat-id]} chat] (let [{:keys [group-chat chat-id]} chat]
(if group-chat (if group-chat
(dispatch [:send-group-chat-msg chat-id ;; todo how much are different both events? is there real reason
input-message]) ;; for differentiation here?
(dispatch [:send-chat-msg chat-id (dispatch [:send-group-chat-msg chat-id input-message])
input-message]))) (dispatch [:send-chat-msg]))))
(set-input-message nil)
(dismiss-keyboard))
(defn plain-message-input-view [] (defn plain-message-input-view []
(let [chat (subscribe [:get-current-chat]) (let [chat (subscribe [:get-current-chat])
input-message-atom (subscribe [:get-chat-input-text]) input-message-atom (subscribe [:get-chat-input-text])
staged-commands-atom (subscribe [:get-chat-staged-commands])] staged-commands-atom (subscribe [:get-chat-staged-commands])]
(fn [] (fn []
(let [input-message @input-message-atom] (let [input-message @input-message-atom]
[view {:style {:flexDirection "column"}} [view {:style {:flexDirection :column}}
[suggestions-view] [suggestions-view]
[view {:style {:flexDirection "row" [view {:style {:flexDirection :row
:height 56 :height 56
:backgroundColor color-white}} :backgroundColor color-white}}
[image {:source {:uri "icon_list"} [image {:source {:uri :icon_list}
:style {:marginTop 22 :style {:marginTop 22
:marginRight 6 :marginRight 6
:marginBottom 6 :marginBottom 6
:marginLeft 21 :marginLeft 21
:width 13 :width 13
:height 12}}] :height 12}}]
[text-input {:underlineColorAndroid "transparent" [text-input {:underlineColorAndroid :transparent
:style {:flex 1 :style {:flex 1
:marginLeft 16 :marginLeft 16
:marginTop -2 :marginTop -2
@ -55,23 +49,20 @@
:fontSize 14 :fontSize 14
:fontFamily font :fontFamily font
:color text2-color} :color text2-color}
:autoFocus (< 0 (count @staged-commands-atom)) :autoFocus (pos? (count @staged-commands-atom))
:placeholder "Type" :placeholder "Type"
:placeholderTextColor text2-color :placeholderTextColor text2-color
:onChangeText (fn [new-text] :onChangeText set-input-message
(set-input-message new-text)) :onSubmitEditing #(send @chat input-message)}
:onSubmitEditing (fn [e]
(send @chat input-message))}
input-message] input-message]
[image {:source {:uri "icon_smile"} [image {:source {:uri :icon_smile}
:style {:marginTop 18 :style {:marginTop 18
:marginRight 18 :marginRight 18
:width 20 :width 20
:height 20}}] :height 20}}]
(when (or (pos? (count input-message)) (when (or (pos? (count input-message))
(pos? (count @staged-commands-atom))) (pos? (count @staged-commands-atom)))
[touchable-highlight {:on-press (fn [] [touchable-highlight {:on-press #(send @chat input-message)
(send @chat input-message))
:underlay-color :transparent} :underlay-color :transparent}
[view {:style {:marginTop 10 [view {:style {:marginTop 10
:marginRight 10 :marginRight 10
@ -79,8 +70,8 @@
:height 36 :height 36
:borderRadius 50 :borderRadius 50
:backgroundColor color-blue}} :backgroundColor color-blue}}
[image {:source {:uri "icon_send"} [image {:source {:uri :icon_send}
:style {:marginTop 10.5 :style {:marginTop 10.5
:marginLeft 12 :marginLeft 12
:width 15 :width 15
:height 15}}]]])]])))) :height 15}}]]])]]))))

View File

@ -52,8 +52,8 @@
[action-button {:buttonColor color-blue} [action-button {:buttonColor color-blue}
[action-button-item {:title "New Chat" [action-button-item {:title "New Chat"
:buttonColor "#9b59b6" :buttonColor "#9b59b6"
:onPress (fn [] :onPress #(dispatch [:navigate-to
(dispatch [:show-contacts navigator]))} :contact-list])}
[icon {:name "android-create" [icon {:name "android-create"
:style {:fontSize 20 :style {:fontSize 20
:height 22 :height 22

View File

@ -20,7 +20,7 @@
(defn list-item [component] (defn list-item [component]
(r/as-element component)) (r/as-element component))
(def dismiss-keyboard (js/require "dismissKeyboard")) (def dismiss-keyboard! (js/require "dismissKeyboard"))
(comment (comment
(.-width (.get (.. js/React -Dimensions) "window")) (.-width (.get (.. js/React -Dimensions) "window"))

View File

@ -4,24 +4,29 @@
;; schema of app-db ;; schema of app-db
(def schema {:greeting s/Str}) (def schema {:greeting s/Str})
(def default-view :chat-list)
;; initial state of app-db ;; initial state of app-db
(def app-db {:greeting "Hello Clojure in iOS and Android!" (def app-db {:greeting "Hello Clojure in iOS and Android!"
:identity-password "replace-me-with-user-entered-password" :identity-password "replace-me-with-user-entered-password"
:identity "me"
:contacts [] :contacts []
:chat {:current-chat-id "0x0479a5ed1f38cadfad1db6cd56c4b659b0ebe052bbe9efa950f6660058519fa4ca6be2dda66afa80de96ab00eb97a2605d5267a1e8f4c2a166ab551f6826608cdd" :current-chat-id "console"
:command nil :chat {:command nil
:last-message nil} :last-message nil}
:chats {} :chats {}
:chats-updated-signal 0 :chats-updated-signal 0
:show-actions false :show-actions false
:new-group #{} :new-group #{}
:new-participants #{} :new-participants #{}
:signed-up false}) :signed-up false
:view-id default-view
:navigation-stack (list default-view)})
(def protocol-initialized-path [:protocol-initialized]) (def protocol-initialized-path [:protocol-initialized])
(def identity-password-path [:identity-password]) (def identity-password-path [:identity-password])
(def current-chat-id-path [:chat :current-chat-id]) (def current-chat-id-path [:current-chat-id])
(def updated-chats-signal-path [:chats-updated-signal]) (def updated-chats-signal-path [:chats-updated-signal])
(defn updated-chat-signal-path [chat-id] (defn updated-chat-signal-path [chat-id]
[:chats chat-id :chat-updated-signal]) [:chats chat-id :chat-updated-signal])

View File

@ -1,6 +1,6 @@
(ns syng-im.handlers (ns syng-im.handlers
(:require (:require
[re-frame.core :refer [register-handler after dispatch]] [re-frame.core :refer [register-handler after dispatch debug enrich]]
[schema.core :as s :include-macros true] [schema.core :as s :include-macros true]
[syng-im.db :as db :refer [app-db schema]] [syng-im.db :as db :refer [app-db schema]]
[syng-im.protocol.api :refer [init-protocol]] [syng-im.protocol.api :refer [init-protocol]]
@ -11,7 +11,8 @@
[syng-im.models.contacts :as contacts] [syng-im.models.contacts :as contacts]
[syng-im.models.messages :refer [save-message [syng-im.models.messages :refer [save-message
update-message! update-message!
message-by-id]] message-by-id
get-messages]]
[syng-im.models.commands :as commands :refer [set-chat-command [syng-im.models.commands :as commands :refer [set-chat-command
set-response-chat-command set-response-chat-command
set-chat-command-content set-chat-command-content
@ -28,13 +29,13 @@
apply-staged-commands apply-staged-commands
check-suggestion]] check-suggestion]]
[syng-im.handlers.sign-up :as sign-up-service] [syng-im.handlers.sign-up :as sign-up-service]
[syng-im.models.chats :refer [chat-exists? [syng-im.models.chats :refer [chat-exists?
create-chat create-chat
chat-add-participants chat-add-participants
chat-remove-participants chat-remove-participants
set-chat-active set-chat-active
re-join-group-chat]] re-join-group-chat
chat-by-id2] :as chats]
[syng-im.models.chat :refer [signal-chat-updated [syng-im.models.chat :refer [signal-chat-updated
set-current-chat-id set-current-chat-id
current-chat-id current-chat-id
@ -53,7 +54,9 @@
nav-replace nav-replace
nav-pop]] nav-pop]]
[syng-im.utils.crypt :refer [gen-random-bytes]] [syng-im.utils.crypt :refer [gen-random-bytes]]
[syng-im.utils.random :as random])) [syng-im.utils.random :as random]
[clojure.string :as str]
[syng-im.components.react :as r]))
;; -- Middleware ------------------------------------------------------------ ;; -- Middleware ------------------------------------------------------------
;; ;;
@ -72,8 +75,7 @@
;; -- Common -------------------------------------------------------------- ;; -- Common --------------------------------------------------------------
(register-handler :initialize-db (register-handler :initialize-db
(fn [_ _] (fn [_ _] app-db))
app-db))
(register-handler :set-loading (register-handler :set-loading
(fn [db [_ value]] (fn [db [_ value]]
@ -100,14 +102,6 @@
(log/debug "crypt initialized") (log/debug "crypt initialized")
db)) db))
(register-handler :navigate-to
(fn [db [action navigator route nav-type]]
(log/debug action route)
(case nav-type
:push (nav-push navigator route)
:replace (nav-replace navigator route))
db))
(register-handler :load-commands (register-handler :load-commands
(fn [db [action]] (fn [db [action]]
(log/debug action) (log/debug action)
@ -137,13 +131,30 @@
(update-identity identity) (update-identity identity)
(set-initialized true)))) (set-initialized true))))
(defn gen-messages [n]
(mapv (fn [_]
(let [id (random-uuid)]
{:msg-id id
:content (str id
"ooops sdfg dsfg"
"s dfg\ndsfg dfg\ndsfgdsfgdsfg")
:content-type text-content-type
:outgoing false
:from "console"
:to "me"})) (range n)))
(defn store-message!
[_ [_ {chat-id :from :as msg}]]
(save-message chat-id msg))
(defn receive-message
[db [_ {chat-id :from :as msg}]]
(let [messages [:chats chat-id :messages]]
(update-in db messages conj msg)))
(register-handler :received-msg (register-handler :received-msg
(fn [db [action {chat-id :from (-> receive-message
msg-id :msg-id :as msg}]] ((after store-message!))))
(log/debug action "msg" msg)
(let [db (create-chat db chat-id [chat-id] false)]
(save-message chat-id msg)
(signal-chat-updated db chat-id))))
(register-handler :group-received-msg (register-handler :group-received-msg
(fn [db [action {chat-id :group-id :as msg}]] (fn [db [action {chat-id :group-id :as msg}]]
@ -151,6 +162,12 @@
(save-message chat-id msg) (save-message chat-id msg)
(signal-chat-updated db chat-id))) (signal-chat-updated db chat-id)))
(defn system-message [msg-id content]
{:from "system"
:msg-id msg-id
:content content
:content-type text-content-type})
(defn joined-chat-msg [chat-id from msg-id] (defn joined-chat-msg [chat-id from msg-id]
(let [contact-name (:name (contacts/contact-by-identity from))] (let [contact-name (:name (contacts/contact-by-identity from))]
(save-message chat-id {:from "system" (save-message chat-id {:from "system"
@ -171,31 +188,27 @@
(defn participant-removed-from-group-msg [chat-id identity from msg-id] (defn participant-removed-from-group-msg [chat-id identity from msg-id]
(let [remover-name (:name (contacts/contact-by-identity from)) (let [remover-name (:name (contacts/contact-by-identity from))
removed-name (:name (contacts/contact-by-identity identity))] removed-name (:name (contacts/contact-by-identity identity))]
(save-message chat-id {:from "system" (->> (str (or remover-name from) " removed " (or removed-name identity))
:msg-id msg-id (system-message msg-id)
:content (str (or remover-name from) " removed " (or removed-name identity)) (save-message chat-id))))
:content-type text-content-type})))
(defn you-removed-from-group-msg [chat-id from msg-id] (defn you-removed-from-group-msg [chat-id from msg-id]
(let [remover-name (:name (contacts/contact-by-identity from))] (let [remover-name (:name (contacts/contact-by-identity from))]
(save-message chat-id {:from "system" (->> (str (or remover-name from) " removed you from group chat")
:msg-id msg-id (system-message msg-id)
:content (str (or remover-name from) " removed you from group chat") (save-message chat-id))))
:content-type text-content-type})))
(defn participant-left-group-msg [chat-id from msg-id] (defn participant-left-group-msg [chat-id from msg-id]
(let [left-name (:name (contacts/contact-by-identity from))] (let [left-name (:name (contacts/contact-by-identity from))]
(save-message chat-id {:from "system" (->> (str (or left-name from) " left")
:msg-id msg-id (system-message msg-id)
:content (str (or left-name from) " left") (save-message chat-id))))
:content-type text-content-type})))
(defn removed-participant-msg [chat-id identity] (defn removed-participant-msg [chat-id identity]
(let [contact-name (:name (contacts/contact-by-identity identity))] (let [contact-name (:name (contacts/contact-by-identity identity))]
(save-message chat-id {:from "system" (->> (str "You've removed " (or contact-name identity))
:msg-id (random/id) (system-message (random/id))
:content (str "You've removed " (or contact-name identity)) (save-message chat-id))))
:content-type text-content-type})))
(defn left-chat-msg [chat-id] (defn left-chat-msg [chat-id]
(save-message chat-id {:from "system" (save-message chat-id {:from "system"
@ -252,56 +265,105 @@
(let [{:keys [chat-id]} (message-by-id msg-id)] (let [{:keys [chat-id]} (message-by-id msg-id)]
(signal-chat-updated db chat-id)))) (signal-chat-updated db chat-id))))
(defn send-staged-commands [db chat-id] (defn console? [s]
(let [staged-commands (get-in db (db/chat-staged-commands-path chat-id))] (= "console" s))
(dorun
(map (def not-console?
(fn [staged-command] (complement console?))
(let [command-key (get-in staged-command [:command :command])
content (commands/format-command-msg-content command-key (defn prepare-message
(:content staged-command)) [{:keys [identity current-chat-id] :as db} _]
msg (if (= chat-id "console") (let [text (get-in db [:chats current-chat-id :input-text])
(sign-up-service/send-console-command db command-key content) {:keys [command]} (check-suggestion db (str text " "))]
;; TODO handle command, now sends as plain message (if command
(let [{msg-id :msg-id (set-chat-command db command)
{from :from (assoc db :new-message (when-not (str/blank? text)
to :to} :msg} (api/send-user-msg {:to chat-id {:msg-id (random/id)
:content content})] :chat-id current-chat-id
{:msg-id msg-id :content text
:from from :to current-chat-id
:to to :from identity
:content content :content-type text-content-type
:content-type content-type-command :outgoing true})))))
:outgoing true}))]
(save-message chat-id msg))) (defn prepare-command [identity chat-id staged-command]
staged-commands)) (let [command-key (get-in staged-command [:command :command])
content {:command (name command-key)
:content (:content staged-command)}]
{:msg-id (random/id)
:from identity
:to chat-id
:content content
:content-type content-type-command
:outgoing true
:handler (:handler staged-command)}))
(defn prepare-staged-commans
[{:keys [current-chat-id identity] :as db} _]
(let [staged-commands (get-in db [:chats current-chat-id :staged-commands])]
(->> staged-commands
(map #(prepare-command identity current-chat-id %))
(assoc db :new-commands))))
(defn add-message
[{:keys [new-message current-chat-id] :as db}]
(if new-message
(update-in db [:chats current-chat-id :messages] conj new-message)
db)) db))
(defn add-commands
[{:keys [new-commands current-chat-id] :as db}]
(reduce
#(update-in %1 [:chats current-chat-id :messages] conj %2)
db
new-commands))
(defn clear-input
[{:keys [current-chat-id new-message] :as db} _]
(if new-message
(assoc-in db [:chats current-chat-id :input-text] nil)
db))
(defn clear-staged-commands
[{:keys [current-chat-id] :as db} _]
(assoc-in db [:chats current-chat-id :staged-commands] []))
(defn send-message!
[{:keys [new-message current-chat-id]} _]
(when (and new-message (not-console? current-chat-id))
(api/send-user-msg {:to current-chat-id
:content (:content new-message)})))
(defn save-message-to-realm!
[{:keys [new-message current-chat-id]} _]
(when new-message
(save-message current-chat-id new-message)))
(defn save-commands-to-realm!
[{:keys [new-commands current-chat-id]} _]
(doseq [new-command new-commands]
(save-message current-chat-id (dissoc new-command :handler))))
(defn handle-commands
[{:keys [new-commands]}]
(println new-commands)
(doseq [{{content :content} :content
handler :handler} new-commands]
(when handler
(handler content))))
(register-handler :send-chat-msg (register-handler :send-chat-msg
(fn [db [action chat-id text]] (-> prepare-message
(log/debug action "chat-id" chat-id "text" text) ((enrich prepare-staged-commans))
(if-let [command (get-command db text)] ((enrich add-message))
(do (dispatch [:set-chat-command (:command command)]) ((enrich add-commands))
db) ((enrich clear-input))
(let [msg (when (pos? (count text)) ((enrich clear-staged-commands))
(if (= chat-id "console") ((after (fn [_ _] (r/dismiss-keyboard!))))
(sign-up-service/send-console-msg text) ((after send-message!))
(let [{msg-id :msg-id ((after save-message-to-realm!))
{from :from ((after save-commands-to-realm!))
to :to} :msg} (api/send-user-msg {:to chat-id ((after handle-commands))))
:content text})]
{:msg-id msg-id
:from from
:to to
:content text
:content-type text-content-type
:outgoing true})))]
(when msg
(save-message chat-id msg))
(-> db
(send-staged-commands chat-id)
(apply-staged-commands)
(signal-chat-updated chat-id))))))
(register-handler :leave-group-chat (register-handler :leave-group-chat
(fn [db [action navigator]] (fn [db [action navigator]]
@ -312,28 +374,6 @@
(left-chat-msg chat-id) (left-chat-msg chat-id)
(signal-chat-updated db chat-id)))) (signal-chat-updated db chat-id))))
(register-handler :send-chat-command
(fn [db [action chat-id command content]]
(log/debug action "chat-id" chat-id "command" command "content" content)
(let [db (set-chat-input-text db nil)
msg (if (= chat-id "console")
(sign-up-service/send-console-command db command content)
;; TODO handle command, now sends as plain message
(let [{msg-id :msg-id
{from :from
to :to} :msg} (api/send-user-msg {:to chat-id
:content content})]
{:msg-id msg-id
:from from
:to to
:content content
:content-type text-content-type
:outgoing true}))]
(save-message chat-id msg)
(-> db
(handle-command command content)
(signal-chat-updated chat-id)))))
(register-handler :send-group-chat-msg (register-handler :send-group-chat-msg
(fn [db [action chat-id text]] (fn [db [action chat-id text]]
(log/debug action "chat-id" chat-id "text" text) (log/debug action "chat-id" chat-id "text" text)
@ -363,12 +403,13 @@
;; -- Sign up -------------------------------------------------------------- ;; -- Sign up --------------------------------------------------------------
(register-handler :sign-up (register-handler :sign-up
(fn [db [_ phone-number handler]] (-> (fn [db [_ phone-number]]
(server/sign-up db phone-number handler))) (assoc db :user-phone-number phone-number))
((after (fn [& _] (sign-up-service/on-sign-up-response))))))
(register-handler :sign-up-confirm (register-handler :sign-up-confirm
(fn [db [_ confirmation-code handler]] (fn [db [_ confirmation-code]]
(server/sign-up-confirm confirmation-code handler) (sign-up-service/on-send-code-response confirmation-code)
db)) db))
(register-handler :sync-contacts (register-handler :sync-contacts
@ -401,29 +442,33 @@
;; -- Chat -------------------------------------------------------------- ;; -- Chat --------------------------------------------------------------
(defn update-text [db [_ text]]
(set-chat-input-text db text))
(defn update-command [db [_ text]]
(let [{:keys [command]} (check-suggestion db text)]
(set-chat-command db command)))
(register-handler :set-chat-input-text (register-handler :set-chat-input-text
(fn [db [_ text]] ((enrich update-command) update-text))
(let [{:keys [command]} (check-suggestion db text)]
(-> db
(set-chat-input-text text)
(set-chat-command command)))))
(register-handler :set-chat-command (register-handler :set-chat-command
(fn [db [_ command-key]] (fn [db [_ command-key]]
;; todo what is going on there?!
(set-chat-command db command-key))) (set-chat-command db command-key)))
(register-handler :stage-command (register-handler :stage-command
(fn [db [action chat-id command content]] (fn [{:keys [current-chat-id] :as db} _]
(log/debug action "chat-id" chat-id "command" command "content" content)
(let [db (set-chat-input-text db nil) (let [db (set-chat-input-text db nil)
{:keys [command content]}
(get-in db [:chats current-chat-id :command-input])
command-info {:command command command-info {:command command
:content content :content content
:handler (get-command-handler db (:command command) content)}] :handler (:handler command)}]
(stage-command db command-info)))) (stage-command db command-info))))
(register-handler :unstage-command (register-handler :unstage-command
(fn [db [action chat-id staged-command]] (fn [db [_ staged-command]]
(log/debug action "chat-id" chat-id "staged-command" staged-command)
(let [] (let []
(unstage-command db staged-command)))) (unstage-command db staged-command))))
@ -441,7 +486,6 @@
(register-handler :show-contacts (register-handler :show-contacts
(fn [db [action navigator]] (fn [db [action navigator]]
(log/debug action)
(nav-push navigator {:view-id :contact-list}) (nav-push navigator {:view-id :contact-list})
db)) db))
@ -512,6 +556,76 @@
(re-join-group-chat db group-id identities group-name) (re-join-group-chat db group-id identities group-name)
(create-chat db group-id identities true group-name)))) (create-chat db group-id identities true group-name))))
(comment (register-handler :navigate-to
(dispatch [:set-signed-up true]) (fn [db [_ view-id]]
) (-> db
(assoc :view-id view-id)
(update :navigation-stack conj view-id))))
(register-handler :navigate-back
(fn [{:keys [navigation-stack] :as db} _]
(log/debug :navigate-back)
(if (>= 1 (count navigation-stack))
db
(let [[view-id :as navigation-stack'] (pop navigation-stack)]
(-> db
(assoc :view-id view-id)
(assoc :navigation-stack navigation-stack'))))))
(register-handler :load-more-messages
(fn [db _]
db
;; TODO implement
#_(let [chat-id (get-in db [:chat :current-chat-id])
messages [:chats chat-id :messages]
new-messages (gen-messages 10)]
(update-in db messages concat new-messages))))
(defn load-messages!
[db _]
db
(->> (current-chat-id db)
get-messages
(assoc db :messages)))
(defn init-chat
[{:keys [messages] :as db} _]
(let [id (current-chat-id db)]
(assoc-in db [:chats id :messages] messages)))
(register-handler :init-chat
(-> load-messages!
((enrich init-chat))
debug))
(defn initialize-chats
[{:keys [loaded-chats] :as db} _]
(let [chats (->> loaded-chats
(map (fn [{:keys [chat-id] :as chat}]
[chat-id chat]))
(into {}))]
(-> db
(assoc :chats chats)
(dissoc :loaded-chats))))
(defn load-chats!
[db _]
(assoc db :loaded-chats (chats/chats-list)))
(register-handler :initialize-chats
((enrich initialize-chats) load-chats!))
(defn safe-trim [s]
(when (string? s)
(str/trim s)))
(register-handler :cancel-command
(fn [{:keys [current-chat-id] :as db} _]
(-> db
(assoc-in [:chats current-chat-id :command-input] {})
(update-in [:chats current-chat-id :input-text] safe-trim))))
(register-handler :save-password
(fn [db [_ password]]
(sign-up-service/save-password password)
(assoc db :password-saved true)))

View File

@ -2,16 +2,15 @@
(:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]]
[syng-im.persistence.simple-kv-store :as kv] [syng-im.persistence.simple-kv-store :as kv]
[syng-im.protocol.state.storage :as s] [syng-im.protocol.state.storage :as s]
[syng-im.db :as db]
[syng-im.models.chat :refer [set-current-chat-id]] [syng-im.models.chat :refer [set-current-chat-id]]
[syng-im.models.commands :as commands] [syng-im.models.chats :as c]
[syng-im.utils.utils :refer [log on-error http-post toast]] [syng-im.utils.utils :refer [log on-error http-post toast]]
[syng-im.utils.logging :as log]
[syng-im.utils.random :as random] [syng-im.utils.random :as random]
[syng-im.utils.phone-number :refer [format-phone-number]] [syng-im.utils.phone-number :refer [format-phone-number]]
[syng-im.constants :refer [text-content-type [syng-im.constants :refer [text-content-type
content-type-command content-type-command
content-type-command-request]])) content-type-command-request
content-type-status]]))
(defn send-console-msg [text] (defn send-console-msg [text]
{:msg-id (random/id) {:msg-id (random/id)
@ -29,174 +28,173 @@
;; -- Send confirmation code and synchronize contacts--------------------------- ;; -- Send confirmation code and synchronize contacts---------------------------
(defn on-sync-contacts [] (defn on-sync-contacts []
(dispatch [:received-msg (dispatch [:received-msg
{:msg-id (random/id) {:msg-id (random/id)
:content (str "Your contacts have been synchronized") :content (str "Your contacts have been synchronized")
:content-type text-content-type :content-type text-content-type
:outgoing false :outgoing false
:from "console" :from "console"
:to "me"}]) :to "me"}])
(dispatch [:set-signed-up true])) (dispatch [:set-signed-up true]))
(defn sync-contacts [] (defn sync-contacts []
(dispatch [:sync-contacts on-sync-contacts])) (dispatch [:sync-contacts on-sync-contacts]))
(defn on-send-code-response [msg-id body] (defn on-send-code-response [body]
(if (:confirmed body) (dispatch [:received-msg
(do (dispatch [:set-chat-command-request msg-id nil]) {:msg-id (random/id)
(dispatch [:received-msg ;; todo replace by real check
{:msg-id (random/id) :content (if (= "1111" body)
:content "Confirmed" "Confirmed"
:content-type text-content-type "Wrong code")
:outgoing false :content-type text-content-type
:from "console" :outgoing false
:to "me"}]) :from "console"
(sync-contacts)) :to "me"}]))
(dispatch [:received-msg
{:msg-id (random/id)
:content "Wrong code"
:content-type text-content-type
:outgoing false
:from "console"
:to "me"}])))
(defn send-code [msg-id code] ; todo fn name is not too smart, but...
(dispatch [:sign-up-confirm code (partial on-send-code-response msg-id)])) (defn command-content
[command content]
(defn- handle-confirmation-code [msg-id command-key content] {:command (name command)
(when (= command-key :confirmation-code) :content content})
(send-code msg-id content)))
;; -- Send phone number ---------------------------------------- ;; -- Send phone number ----------------------------------------
(defn on-sign-up-response [] (defn on-sign-up-response []
(let [msg-id (random/id)] (let [msg-id (random/id)]
(dispatch [:received-msg (dispatch [:received-msg
{:msg-id msg-id {:msg-id msg-id
:content (commands/format-command-request-msg-content :content (command-content
:confirmation-code :confirmation-code
(str "Thanks! We've sent you a text message with a confirmation " (str "Thanks! We've sent you a text message with a confirmation "
"code. Please provide that code to confirm your phone number")) "code. Please provide that code to confirm your phone number"))
:content-type content-type-command-request :content-type content-type-command-request
:outgoing false :outgoing false
:from "console" :from "console"
:to "me"}]) :to "me"}])))
(dispatch [:set-chat-command-request msg-id handle-confirmation-code])))
(defn- handle-phone [msg-id command-key content]
(dispatch [:set-chat-command-request msg-id nil])
(when (= command-key :phone)
(let [phone-number (format-phone-number content)]
(dispatch [:sign-up phone-number on-sign-up-response]))))
;; -- Saving password ---------------------------------------- ;; -- Saving password ----------------------------------------
(defn- save-password [password] (defn save-password [password]
;; TODO validate and save password ;; TODO validate and save password
(dispatch [:received-msg (dispatch [:received-msg
{:msg-id (random/id) {:msg-id (random/id)
:content (str "OK great! Your password has been saved. Just to let you " :content (str "OK great! Your password has been saved. Just to let you "
"know, you can always change it in the Console, by the way, " "know you can always change it in the Console by the way "
"it's me, the Console, nice to meet you!") "it's me the Console nice to meet you!")
:content-type text-content-type :content-type text-content-type
:outgoing false :outgoing false
:from "console" :from "console"
:to "me"}]) :to "me"}])
(dispatch [:received-msg (dispatch [:received-msg
{:msg-id (random/id) {:msg-id (random/id)
:content (str "I'll generate a passphrase for you so you can restore your " :content (str "I'll generate a passphrase for you so you can restore your "
"access or log in from another device") "access or log in from another device")
:content-type text-content-type :content-type text-content-type
:outgoing false :outgoing false
:from "console" :from "console"
:to "me"}]) :to "me"}])
(dispatch [:received-msg (dispatch [:received-msg
{:msg-id (random/id) {:msg-id (random/id)
:content "Here's your passphrase:" :content "Here's your passphrase:"
:content-type text-content-type :content-type text-content-type
:outgoing false :outgoing false
:from "console" :from "console"
:to "me"}]) :to "me"}])
;; TODO generate passphrase ;; TODO generate passphrase
(let [passphrase (str "The brash businessman's braggadocio and public squabbing with " (let [passphrase (str "The brash businessman's braggadocio and public squabbing with "
"candidates in the US presidential election")] "candidates in the US presidential election")]
(dispatch [:received-msg (dispatch [:received-msg
{:msg-id (random/id) {:msg-id (random/id)
:content passphrase :content passphrase
:content-type text-content-type :content-type text-content-type
:outgoing false :outgoing false
:from "console" :from "console"
:to "me"}])) :to "me"}]))
(dispatch [:received-msg (dispatch [:received-msg
{:msg-id "8" {:msg-id "8"
:content "Make sure you had securely written it down" :content "Make sure you had securely written it down"
:content-type text-content-type :content-type text-content-type
:outgoing false :outgoing false
:from "console" :from "console"
:to "me"}]) :to "me"}])
;; TODO highlight '!phone' ;; TODO highlight '!phone'
(let [msg-id (random/id)] (let [msg-id (random/id)]
(dispatch [:received-msg (dispatch [:received-msg
{:msg-id msg-id {:msg-id msg-id
:content (commands/format-command-request-msg-content :content (command-content
:phone :phone
(str "Your phone number is also required to use the app. Type the " (str "Your phone number is also required to use the app. Type the "
"exclamation mark or hit the icon to open the command list " "exclamation mark or hit the icon to open the command list "
"and choose the !phone command")) "and choose the !phone command"))
:content-type content-type-command-request :content-type content-type-command-request
:outgoing false :outgoing false
:from "console" :from "console"
:to "me"}]) :to "me"}])))
(dispatch [:set-chat-command-request msg-id handle-phone])))
(defn- handle-password [msg-id command-key content] (def intro-status
(dispatch [:set-chat-command-request msg-id nil]) {:msg-id "intro-status"
(when (= command-key :keypair-password) :content (str "The brash businessmans braggadocio "
(save-password content))) "and public exchange with candidates "
"in the US presidential election")
:delivery-status "seen"
:from "console"
:chat-id "console"
:content-type content-type-status
:outgoing false
:to "me"})
(defn intro [db] (defn intro [db]
(dispatch [:received-msg intro-status])
(dispatch [:received-msg (dispatch [:received-msg
{:msg-id "intro-message1" {:msg-id "intro-message1"
:content "Hello there! It's Syng, a Dapp browser in your phone." :content "Hello there! It's Syng a Dapp browser in your phone."
:content-type text-content-type :content-type text-content-type
:outgoing false :outgoing false
:from "console" :from "console"
:to "me"}]) :to "me"}])
(dispatch [:received-msg (dispatch [:received-msg
{:msg-id "intro-message2" {:msg-id "intro-message2"
:content (str "Syng uses a highly secure key-pair authentication type " :content (str "Syng uses a highly secure key-pair authentication type "
"to provide you a reliable way to access your account") "to provide you a reliable way to access your account")
:content-type text-content-type :content-type text-content-type
:outgoing false :outgoing false
:from "console" :from "console"
:to "me"}]) :to "me"}])
(let [msg-id "into-message3"] (let [msg-id "into-message3"]
(dispatch [:received-msg (dispatch [:received-msg
{:msg-id msg-id {:msg-id msg-id
:content (commands/format-command-request-msg-content :content (command-content
:keypair-password :keypair-password
(str "A key pair has been generated and saved to your device. " (str "A key pair has been generated and saved to your device. "
"Create a password to secure your key")) "Create a password to secure your key"))
:content-type content-type-command-request :content-type content-type-command-request
:outgoing false :outgoing false
:from "console" :from "console"
:to "me"}]) :to "me"}]))
(dispatch [:set-chat-command-request msg-id handle-password]))
;; (dispatch [:set-chat-command :keypair-password])
db) db)
;; TODO store command key in a separate field (def console-chat
(defn send-console-command [db command-key content] {:chat-id "console"
{:msg-id (random/id) :name "console"
:from "me" :group-chat false
:to "console" :is-active true
:content content ;; (commands/format-command-msg-content command-key content) :timestamp (.getTime (js/Date.))
:content-type content-type-command :contacts [{:identity "console"
:outgoing true}) :text-color "#FFFFFF"
:background-color "#AB7967"}]})
(defn init [db] (defn create-chat [handler]
(let [signed-up (s/get kv/kv-store :signed-up) (fn [db]
db (if signed-up (let [{:keys [new-chat] :as db'} (handler db)]
db (when new-chat
(-> db (c/create-chat new-chat))
(set-current-chat-id "console") (dissoc db' :new-chat))))
intro))]
(assoc db :signed-up signed-up))) (def init
(create-chat
(fn [{:keys [chats] :as db}]
(if (chats "console")
db
(-> db
(assoc-in [:chats "console"] console-chat)
(assoc :new-chat console-chat)
(set-current-chat-id "console")
(intro))))))

View File

@ -50,6 +50,9 @@
:outgoing false})) :outgoing false}))
(defn create-chat (defn create-chat
([{:keys [last-msg-id] :as chat}]
(let [chat (assoc chat :last-msg-id (or last-msg-id ""))]
(r/write #(r/create :chats chat))))
([db chat-id identities group-chat?] ([db chat-id identities group-chat?]
(create-chat db chat-id identities group-chat? nil)) (create-chat db chat-id identities group-chat? nil))
([db chat-id identities group-chat? chat-name] ([db chat-id identities group-chat? chat-name]
@ -96,14 +99,26 @@
(-> (signal-chats-updated db) (-> (signal-chats-updated db)
(signal-chat-updated group-id))) (signal-chat-updated group-id)))
(defn normalize-contacts
[chats]
(map #(update % :contacts vals) chats))
(defn chats-list [] (defn chats-list []
(r/sorted (r/get-all :chats) :timestamp :desc)) (-> (r/get-all :chats)
(r/sorted :timestamp :desc)
r/collection->map
normalize-contacts))
(defn chat-by-id [chat-id] (defn chat-by-id [chat-id]
(-> (r/get-by-field :chats :chat-id chat-id) (-> (r/get-by-field :chats :chat-id chat-id)
(r/single-cljs) (r/single-cljs)
(r/list-to-array :contacts))) (r/list-to-array :contacts)))
(defn chat-by-id2 [chat-id]
(-> (r/get-by-field :chats :chat-id chat-id)
r/collection->map
first))
(defn chat-add-participants [chat-id identities] (defn chat-add-participants [chat-id identities]
(r/write (r/write
(fn [] (fn []
@ -136,42 +151,3 @@
(-> (r/get-by-field :chats :chat-id chat-id) (-> (r/get-by-field :chats :chat-id chat-id)
(r/single) (r/single)
(aset "is-active" active?))))) (aset "is-active" active?)))))
(comment
(active-group-chats)
(-> (r/get-by-field :chats :chat-id "0x04ed4c3797026cddeb7d64a54ca58142e57ea03cda21072358d67455b506db90c56d95033e3d221992f70d01922c3d90bf0697c49e4be118443d03ae4a1cd3c15c")
(r/single)
(aget "contacts")
(.map (fn [object index collection]
object)))
(-> (chat-by-id "0x04ed4c3797026cddeb7d64a54ca58142e57ea03cda21072358d67455b506db90c56d95033e3d221992f70d01922c3d90bf0697c49e4be118443d03ae4a1cd3c15c")
:contacts
vals
vec)
(-> (aget (aget (chats-list) 0) "contacts")
(r/cljs-list))
(r/write (fn [] (r/delete (chats-list))))
(swap! re-frame.db/app-db signal-chats-updated)
(create-chat "0x0479a5ed1f38cadfad1db6cd56c4b659b0ebe052bbe9efa950f6660058519fa4ca6be2dda66afa80de96ab00eb97a2605d5267a1e8f4c2a166ab551f6826608cdd"
["0x0479a5ed1f38cadfad1db6cd56c4b659b0ebe052bbe9efa950f6660058519fa4ca6be2dda66afa80de96ab00eb97a2605d5267a1e8f4c2a166ab551f6826608cdd"])
(+ 1 1)
(swap! re-frame.db/app-db (fn [db]
(create-chat db "A group chat")))
(-> (chats-list)
(.find (fn [object index collection]
(= "console1" (aget object "chat-id")))))
)

View File

@ -4,63 +4,63 @@
[cljs.core.async :as async :refer [chan put! <! >!]] [cljs.core.async :as async :refer [chan put! <! >!]]
[re-frame.core :refer [subscribe dispatch dispatch-sync]] [re-frame.core :refer [subscribe dispatch dispatch-sync]]
[syng-im.db :as db] [syng-im.db :as db]
[syng-im.resources :as res]
[syng-im.models.chat :refer [current-chat-id]] [syng-im.models.chat :refer [current-chat-id]]
[syng-im.components.styles :refer [color-blue [syng-im.components.styles :refer [color-blue
color-dark-mint]] color-dark-mint]]
[syng-im.utils.utils :refer [log toast]] [syng-im.utils.utils :refer [log toast]]))
[syng-im.utils.logging :as log]
[syng-im.persistence.realm :as realm]))
;; todo delete ;; todo delete
(def commands [{:command :money (def commands [{:command :money
:text "!money" :text "!money"
:description "Send money" :description "Send money"
:color color-dark-mint :color color-dark-mint
:request-icon {:uri "icon_lock_white"} :request-icon {:uri "icon_lock_white"}
:icon {:uri "icon_lock_gray"} :icon {:uri "icon_lock_gray"}
:suggestion true} :suggestion true}
{:command :location {:command :location
:text "!location" :text "!location"
:description "Send location" :description "Send location"
:color "#9a5dcf" :color "#9a5dcf"
:suggestion true} :suggestion true}
{:command :phone {:command :phone
:text "!phone" :text "!phone"
:description "Send phone number" :description "Send phone number"
:color color-dark-mint
:request-text "Phone number request" :request-text "Phone number request"
:color color-dark-mint :suggestion true
:suggestion true} :handler #(dispatch [:sign-up %])}
{:command :confirmation-code {:command :confirmation-code
:text "!confirmationCode" :text "!confirmationCode"
:description "Send confirmation code" :description "Send confirmation code"
:request-text "Confirmation code request" :request-text "Confirmation code request"
:color color-blue :color color-blue
:request-icon {:uri "icon_lock_white"} :request-icon {:uri "icon_lock_white"}
:icon {:uri "icon_lock_gray"} :icon {:uri "icon_lock_gray"}
:suggestion true} :suggestion true
{:command :send :handler #(dispatch [:sign-up-confirm %])}
:text "!send" {:command :send
:text "!send"
:description "Send location" :description "Send location"
:color "#9a5dcf" :color "#9a5dcf"
:suggestion true} :suggestion true}
{:command :request {:command :request
:text "!request" :text "!request"
:description "Send request" :description "Send request"
:color "#48ba30" :color "#48ba30"
:suggestion true} :suggestion true}
{:command :keypair-password {:command :keypair-password
:text "!keypairPassword" :text "!keypairPassword"
:description "" :description ""
:color color-blue :color color-blue
:request-icon {:uri "icon_lock_white"} :request-icon {:uri "icon_lock_white"}
:icon {:uri "icon_lock_gray"} :icon {:uri "icon_lock_gray"}
:suggestion false} :suggestion false
{:command :help :handler #(dispatch [:save-password %])}
:text "!help" {:command :help
:text "!help"
:description "Help" :description "Help"
:color "#9a5dcf" :color "#9a5dcf"
:suggestion true}]) :suggestion true}])
(defn get-commands [db] (defn get-commands [db]
;; todo: temp. must be '(get db :commands)' ;; todo: temp. must be '(get db :commands)'
@ -83,19 +83,20 @@
(get-in db (db/chat-command-content-path (current-chat-id db)))) (get-in db (db/chat-command-content-path (current-chat-id db))))
(defn set-chat-command-content [db content] (defn set-chat-command-content [db content]
(assoc-in db (db/chat-command-content-path (get-in db db/current-chat-id-path)) (assoc-in db
[:chats (get-in db db/current-chat-id-path) :command-input :content]
content)) content))
(defn get-chat-command [db] (defn get-chat-command [db]
(get-in db (db/chat-command-path (current-chat-id db)))) (get-in db (db/chat-command-path (current-chat-id db))))
(defn set-response-chat-command [db msg-id command-key] (defn set-response-chat-command [db msg-id command-key]
(-> db (let [chat-id (current-chat-id db)]
(set-chat-command-content nil) (-> db
(assoc-in (db/chat-command-path (current-chat-id db)) (assoc-in [:chats chat-id :command-input :content] nil)
(get-command db command-key)) (assoc-in [:chats chat-id :command-input :command]
(assoc-in (db/chat-command-to-msg-id-path (current-chat-id db)) (get-command db command-key))
msg-id))) (assoc-in [:chats chat-id :command-input :to-msg-id] msg-id))))
(defn set-chat-command [db command-key] (defn set-chat-command [db command-key]
(set-response-chat-command db nil command-key)) (set-response-chat-command db nil command-key))
@ -124,31 +125,10 @@
(defn set-chat-command-request [db msg-id handler] (defn set-chat-command-request [db msg-id handler]
(update-in db (db/chat-command-requests-path (current-chat-id db)) (update-in db (db/chat-command-requests-path (current-chat-id db))
(fn [requests] #(assoc % msg-id handler)))
(if requests
(assoc requests msg-id handler)
{msg-id handler}))))
(defn- map-to-str
[m]
(join ";" (map #(join "=" %) (stringify-keys m))))
(defn- str-to-map
[s]
(keywordize-keys (apply hash-map (split s #"[;=]"))))
;; TODO store command key in separate field
(defn format-command-msg-content [command content]
(map-to-str {:command (name command) :content content}))
(defn parse-command-msg-content [commands content] (defn parse-command-msg-content [commands content]
(log/info content) (update content :command #(find-command commands (keyword %))))
(log/info (update (str-to-map content) :command #(find-command commands (keyword %))))
(update (str-to-map content) :command #(find-command commands (keyword %))))
(defn format-command-request-msg-content [command content]
(map-to-str {:command (name command) :content content}))
(defn parse-command-request-msg-content [commands content] (defn parse-command-request-msg-content [commands content]
(update (str-to-map content) :command #(find-command commands (keyword %)))) (update content :command #(find-command commands (keyword %))))

View File

@ -3,7 +3,18 @@
[cljs.reader :refer [read-string]] [cljs.reader :refer [read-string]]
[syng-im.utils.random :refer [timestamp]] [syng-im.utils.random :refer [timestamp]]
[syng-im.db :as db] [syng-im.db :as db]
[syng-im.utils.logging :as log])) [syng-im.utils.logging :as log]
[clojure.string :refer [join split]]
[clojure.walk :refer [stringify-keys keywordize-keys]]
[syng-im.constants :as c]))
(defn- map-to-str
[m]
(join ";" (map #(join "=" %) (stringify-keys m))))
(defn- str-to-map
[s]
(keywordize-keys (apply hash-map (split s #"[;=]"))))
(defn select-chat-last-message [chat] (defn select-chat-last-message [chat]
(when-let [last-msg-id (:last-msg-id chat)] (when-let [last-msg-id (:last-msg-id chat)]
@ -16,7 +27,10 @@
(r/write (r/write
(fn [] (fn []
(let [chat (r/single-cljs (r/get-by-field :chats :chat-id chat-id)) (let [chat (r/single-cljs (r/get-by-field :chats :chat-id chat-id))
last-message (select-chat-last-message chat)] last-message (select-chat-last-message chat)
content (if (string? content)
content
(map-to-str content))]
(r/create :msgs {:chat-id chat-id (r/create :msgs {:chat-id chat-id
:msg-id msg-id :msg-id msg-id
:from from :from from
@ -37,7 +51,15 @@
true)))))) true))))))
(defn get-messages [chat-id] (defn get-messages [chat-id]
(r/sorted (r/get-by-field :msgs :chat-id chat-id) :timestamp :desc)) (->> (-> (r/get-by-field :msgs :chat-id chat-id)
(r/sorted :timestamp :asc)
(r/collection->map))
(into '())
(map (fn [{:keys [content-type] :as message}]
(if (#{c/content-type-command c/content-type-command-request}
content-type)
(update message :content str-to-map)
message)))))
(defn message-by-id [msg-id] (defn message-by-id [msg-id]
(r/single-cljs (r/get-by-field :msgs :msg-id msg-id))) (r/single-cljs (r/get-by-field :msgs :msg-id msg-id)))
@ -48,27 +70,3 @@
(fn [] (fn []
(when (r/exists? :msgs :msg-id msg-id) (when (r/exists? :msgs :msg-id msg-id)
(r/create :msgs msg true))))) (r/create :msgs msg true)))))
(comment
(update-message! {:msg-id "1459175391577-a2185a35-5c49-5a6b-9c08-6eb5b87ceb7f"
:delivery-status "seen2"})
(r/get-by-field :msgs :msg-id "1459175391577-a2185a35-5c49-5a6b-9c08-6eb5b87ceb7f")
(save-message "0x040028c500ff086ecf1cfbb3c1a7240179cde5b86f9802e6799b9bbe9cdd7ad1b05ae8807fa1f9ed19cc8ce930fc2e878738c59f030a6a2f94b3522dc1378ff154"
{:msg-id "153"
:content "hello!"
:content-type "text/plain"})
(get-messages* "0x040028c500ff086ecf1cfbb3c1a7240179cde5b86f9802e6799b9bbe9cdd7ad1b05ae8807fa1f9ed19cc8ce930fc2e878738c59f030a6a2f94b3522dc1378ff154")
(get-messages "0x0479a5ed1f38cadfad1db6cd56c4b659b0ebe052bbe9efa950f6660058519fa4ca6be2dda66afa80de96ab00eb97a2605d5267a1e8f4c2a166ab551f6826608cdd")
(doseq [msg (get-messages* "0x043df89d36f6e3d8ade18e55ac3e2e39406ebde152f76f2f82d674681d59319ffd9880eebfb4f5f8d5c222ec485b44d6e30ba3a03c96b1c946144fdeba1caccd43")]
(r/delete msg))
@re-frame.db/app-db
)

View File

@ -131,7 +131,6 @@
(defn get-list [schema-name] (defn get-list [schema-name]
(vals (js->clj (.objects realm (to-string schema-name)) :keywordize-keys true))) (vals (js->clj (.objects realm (to-string schema-name)) :keywordize-keys true)))
(defn collection->map [collection]
(comment (-> (.map collection (fn [object _ _] object))
(js->clj :keywordize-keys true)))
)

View File

@ -15,7 +15,6 @@
get-chat-command get-chat-command
get-chat-command-content get-chat-command-content
get-chat-command-request get-chat-command-request
parse-command-msg-content
parse-command-request-msg-content]] parse-command-request-msg-content]]
[syng-im.handlers.suggestions :refer [get-suggestions]])) [syng-im.handlers.suggestions :refer [get-suggestions]]))
@ -23,11 +22,8 @@
(register-sub :get-chat-messages (register-sub :get-chat-messages
(fn [db _] (fn [db _]
(let [chat-id (reaction (current-chat-id @db)) (let [chat-id (current-chat-id @db)]
chat-updated (reaction (chat-updated? @db @chat-id))] (reaction (get-in @db [:chats chat-id :messages])))))
(reaction
(let [_ @chat-updated]
(get-messages @chat-id))))))
(register-sub :get-current-chat-id (register-sub :get-current-chat-id
(fn [db _] (fn [db _]
@ -76,12 +72,8 @@
(register-sub :get-current-chat (register-sub :get-current-chat
(fn [db _] (fn [db _]
(let [current-chat-id (reaction (current-chat-id @db)) (let [current-chat-id (current-chat-id @db)]
chat-updated (reaction (chat-updated? @db @current-chat-id))] (reaction (get-in @db [:chats current-chat-id])))))
(reaction
(let [_ @chat-updated]
(when-let [chat-id @current-chat-id]
(chat-by-id chat-id)))))))
;; -- User data -------------------------------------------------------------- ;; -- User data --------------------------------------------------------------
@ -148,3 +140,29 @@
:contacts :contacts
(map :identity))] (map :identity))]
(contacts-list-include current-participants))))))) (contacts-list-include current-participants)))))))
(register-sub :view-id
(fn [db _]
(reaction (@db :view-id))))
(register-sub :chat
(fn [db [_ k]]
(-> @db
(get-in [:chats (current-chat-id @db) k])
(reaction))))
(register-sub :navigation-stack
(fn [db _]
(:navigation-stack @db)))
(register-sub :db
(fn [db _] (reaction @db)))
(register-sub :chat-properties
(fn [{:keys [current-chat-id] :as db} [_ properties]]
(->> properties
(map (fn [k]
[k (-> @db
(get-in [:cgats current-chat-id k])
(reaction))]))
(into {}))))

View File

@ -9,3 +9,11 @@
(-> (cljs.core/clj->js {:rowHasChanged not=}) (-> (cljs.core/clj->js {:rowHasChanged not=})
(js/RealmReactNative.ListView.DataSource.) (js/RealmReactNative.ListView.DataSource.)
(clone-with-rows items))) (clone-with-rows items)))
(defn clone-with-rows2 [ds rows]
(.cloneWithRows ds (reduce (fn [ac el] (.push ac el) ac)
(clj->js []) rows)))
(defn to-datasource2 [items]
(clone-with-rows2 (data-source {:rowHasChanged not=}) items))