Merge pull request #56 from status-im/custom-toolbar

Custom toolbar
This commit is contained in:
Jarrad 2016-05-05 13:14:15 +02:00
commit daa2ca491c
40 changed files with 431 additions and 271 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 684 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 945 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 315 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 868 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 829 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 474 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 520 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 243 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 881 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 969 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 351 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 518 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 679 B

View File

@ -1,12 +1,11 @@
(ns syng-im.components.chat (ns syng-im.components.chat
(:require [clojure.string :as s] (:require [clojure.string :as s]
[re-frame.core :refer [subscribe dispatch dispatch-sync]] [re-frame.core :refer [subscribe dispatch dispatch-sync]]
[syng-im.components.react :refer [android? [syng-im.components.react :refer [view
view
text text
image image
navigator navigator
toolbar-android]] touchable-highlight]]
[syng-im.components.realm :refer [list-view]] [syng-im.components.realm :refer [list-view]]
[syng-im.components.styles :refer [font [syng-im.components.styles :refer [font
title-font title-font
@ -14,12 +13,13 @@
chat-background chat-background
online-color online-color
selected-message-color selected-message-color
separator-color
text1-color text1-color
text2-color]] text2-color
toolbar-background1]]
[syng-im.utils.logging :as log] [syng-im.utils.logging :as log]
[syng-im.navigation :refer [nav-pop]] [syng-im.navigation :refer [nav-pop]]
[syng-im.resources :as res] [syng-im.resources :as res]
[syng-im.constants :refer [content-type-status]]
[syng-im.utils.listview :refer [to-realm-datasource]] [syng-im.utils.listview :refer [to-realm-datasource]]
[syng-im.components.invertible-scroll-view :refer [invertible-scroll-view]] [syng-im.components.invertible-scroll-view :refer [invertible-scroll-view]]
[reagent.core :as r] [reagent.core :as r]
@ -42,7 +42,8 @@
:background-color background-color)))) :background-color background-color))))
(defn chat-photo [{:keys [photo-path]}] (defn chat-photo [{:keys [photo-path]}]
[view {:borderRadius 50} [view {:margin 10
:borderRadius 50}
[image {:source (if (s/blank? photo-path) [image {:source (if (s/blank? photo-path)
res/user-no-photo res/user-no-photo
{:uri photo-path}) {:uri photo-path})
@ -53,8 +54,8 @@
(defn contact-online [{:keys [online]}] (defn contact-online [{:keys [online]}]
(when online (when online
[view {:position "absolute" [view {:position "absolute"
:top 20 :top 30
:left 20 :left 30
:width 20 :width 20
:height 20 :height 20
:borderRadius 50 :borderRadius 50
@ -78,8 +79,7 @@
(defn typing [member] (defn typing [member]
[view {:style {:width 260 [view {:style {:width 260
:paddingTop 2 :marginTop 10
:paddingBottom 8
:paddingLeft 8 :paddingLeft 8
:paddingRight 8 :paddingRight 8
:alignItems "flex-start" :alignItems "flex-start"
@ -95,139 +95,188 @@
(str member " is typing")]]]) (str member " is typing")]]])
(defn typing-all [] (defn typing-all []
[view {:style {:marginBottom 12}} [view {:style {:marginBottom 20}}
(for [member ["Geoff" "Justas"]] (for [member ["Geoff" "Justas"]]
^{:key member} [typing member])]) ^{:key member} [typing member])])
(defn toolbar-content-chat [chat] (defn action-view [action]
(let [group? (:group-chat chat)] [touchable-highlight {:on-press (fn []
(dispatch [:set-show-actions false])
((:handler action)))
:underlay-color :transparent}
[view {:style {:flexDirection "row"
:height 56}}
[view {:width 56
:height 56
:alignItems "center"
:justifyContent "center"}
[image {:source {:uri (:icon action)}
:style (:icon-style action)}]]
[view {:style {:flex 1 [view {:style {:flex 1
:flexDirection "row" :alignItems "flex-start"
:backgroundColor "transparent"}} :justifyContent "center"}}
[view {:style {:flex 1 [text {:style {:marginTop -2.5
:alignItems "flex-start" :color text1-color
:justifyContent "center" :fontSize 14
:marginRight 112}} :fontFamily font}}
[text {:style {:marginTop -2.5 (:title action)]
:color text1-color (when-let [subtitle (:subtitle action)]
:fontSize 16 [text {:style {:marginTop 1
:color text2-color
:fontSize 12
:fontFamily font}}
subtitle])]]])
(defn actions-list-view [navigator chat]
(when-let [actions (when (and (:group-chat chat)
(:is-active chat))
[{:title "Add Contact to chat"
:icon "icon_menu_group"
:icon-style {:width 25
:height 19}
:handler #(dispatch [:show-add-participants navigator])}
{:title "Remove Contact from chat"
:subtitle "Alex, John"
:icon "icon_search_gray_copy"
:icon-style {:width 17
:height 17}
:handler #(dispatch [:show-remove-participants navigator])}
{:title "Leave Chat"
:icon "icon_muted"
:icon-style {:width 18
:height 21}
:handler #(dispatch [:leave-group-chat navigator])}
{:title "Settings"
:subtitle "Not implemented"
:icon "icon_settings"
:icon-style {:width 20
:height 13}
:handler (fn [] )}])]
[view {:style {:backgroundColor toolbar-background1
:elevation 2
:position "absolute"
:top 56
:left 0
:right 0}}
[view {:style {:marginLeft 16
: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]
[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 actions-view [navigator chat]
[overlay {:on-click-outside (fn []
(dispatch [:set-show-actions false]))}
[actions-list-view navigator chat]])
(defn toolbar [navigator chat show-actions]
[view {:style {:flexDirection "row"
:height 56
:backgroundColor toolbar-background1
:elevation 2}}
(when (not show-actions)
[touchable-highlight {:on-press (fn []
(nav-pop navigator))
: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 (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}} :fontFamily font}}
(or (chat :name) "Active a minute ago"])]
"Chat name")] (if show-actions
(if group? [touchable-highlight {:on-press (fn []
[view {:style {:flexDirection "row"}} (dispatch [:set-show-actions false]))
[image {:source {:uri "icon_group"} :underlay-color :transparent}
:style {:marginTop 4 [view {:style {:width 56
:width 14 :height 56}}
:height 9}}] [image {:source {:uri "icon_up"}
[text {:style {:marginTop -0.5 :style {:marginTop 23
:marginLeft 4 :marginLeft 21
:fontFamily font :width 14
:fontSize 12 :height 8}}]]]
:color text2-color}} [touchable-highlight {:on-press (fn []
(str (count (:contacts chat)) (dispatch [:set-show-actions true]))
(if (< 1 (count (:contacts chat))) :underlay-color :transparent}
" members" [view {:style {:width 56
" member") :height 56}}
", " (count (:contacts chat)) " active")]] [chat-photo {}]
[text {:style {:marginTop 1 [contact-online {:online true}]]])])
:color text2-color
:fontSize 12
:fontFamily font}}
"Active a minute ago"])]
(when-not group?
[view {:style {:position "absolute"
:top 10
:right 66}}
[chat-photo {}]
[contact-online {:online true}]])]))
(defn chat [{:keys [navigator]}] (defn chat [{:keys [navigator]}]
(let [messages (subscribe [:get-chat-messages]) (let [messages (subscribe [:get-chat-messages])
chat (subscribe [:get-current-chat])] chat (subscribe [:get-current-chat])
show-actions-atom (subscribe [:show-actions])]
(fn [] (fn []
(let [msgs @messages (let [msgs @messages
;_ (log/debug "messages=" msgs) ;_ (log/debug "messages=" msgs)
;; temp to show first status ;; temp
msgs-clj (as-> (js->clj msgs) ms typing (:group-chat @chat)
(assoc ms "-1"
{:msg-id "-1"
:content (str "The brash businessmans braggadocio "
"and public exchange with candidates "
"in the US presidential election")
:delivery-status "seen"
:from "Status"
:chat-id "-"
:content-type content-type-status
:timestamp 1
"outgoing" false
:to nil})
(reduce (fn [items [n m]]
(assoc items (.parseInt js/window n) m
;; (assoc m "from" (if (< 0.5 (rand))
;; "Status"
;; "abc"))
))
{} ms)
(into (sorted-map) ms)
(map (fn [[n current] [_ next]]
[n (-> current
(assoc :same-author
(if next
(= (get current "from") (get next "from"))
true))
(assoc :same-direction
(if next
(= (get current "outgoing") (get next "outgoing"))
true))
(assoc :last-msg (= 0 n)))
current])
ms (conj (vec (rest ms)) nil))
(reduce (fn [items [n m]]
(assoc items n m))
{} ms))
msgs (clj->js msgs-clj)
;; end temp ;; end temp
datasource (to-realm-datasource msgs) datasource (to-realm-datasource msgs)
contacts (:contacts @chat) contacts (:contacts @chat)
contact-by-identity (contacts-by-identity contacts)] contact-by-identity (contacts-by-identity contacts)]
[view {:style {:flex 1 [view {:style {:flex 1
:backgroundColor chat-background}} :backgroundColor chat-background}}
(when android? [toolbar navigator @chat @show-actions-atom]
;; TODO add IOS version (let [last-msg-id (:last-msg-id @chat)]
[toolbar-android {:navIcon {:uri "icon_back"} [list-view {:dataSource datasource
:style {:backgroundColor color-white :renderScrollComponent (fn [props]
:height 56 (invertible-scroll-view (js->clj props)))
:elevation 2} :renderRow (fn [row section-id row-id]
:actions (when (and (:group-chat @chat) (let [msg (-> (js->clj row :keywordize-keys true)
(:is-active @chat)) (add-msg-color contact-by-identity)
[{:title "Add Contact to chat" (assoc :group-chat (:group-chat @chat))
:icon res/add-icon (assoc :typing typing))]
:showWithText true} (r/as-element [chat-message msg last-msg-id])))}])
{:title "Remove Contact from chat"
:icon res/trash-icon
:showWithText true}
{:title "Leave Chat"
:icon res/leave-icon
:showWithText true}])
:onActionSelected (fn [position]
(case position
0 (dispatch [:show-add-participants navigator])
1 (dispatch [:show-remove-participants navigator])
2 (dispatch [:leave-group-chat navigator])))
:onIconClicked (fn []
(nav-pop navigator))}
[toolbar-content-chat @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)))]
(r/as-element [chat-message msg])))
:style {:backgroundColor "white"}}]
(when (:group-chat @chat) (when (:group-chat @chat)
[typing-all]) [typing-all])
(when (:is-active @chat) (when (:is-active @chat)
[chat-message-new])])))) [chat-message-new])
(when @show-actions-atom
[actions-view navigator @chat])]))))

View File

@ -6,8 +6,7 @@
text text
image image
touchable-highlight touchable-highlight
navigator navigator]]
toolbar-android]]
[syng-im.components.styles :refer [font [syng-im.components.styles :refer [font
color-light-blue-transparent color-light-blue-transparent
color-white color-white
@ -287,7 +286,7 @@
(defn incoming-group-message-body [{:keys [msg-id from content content-type outgoing (defn incoming-group-message-body [{:keys [msg-id from content content-type outgoing
delivery-status selected new-day same-author delivery-status selected new-day same-author
same-direction last-msg]}] same-direction last-msg typing]}]
(let [delivery-status :seen-by-everyone] (let [delivery-status :seen-by-everyone]
[view {:style {:flexDirection "column"}} [view {:style {:flexDirection "column"}}
(when selected (when selected
@ -306,7 +305,7 @@
:else 10) :else 10)
:paddingRight 8 :paddingRight 8
:paddingLeft 8} :paddingLeft 8}
(when last-msg (when (and last-msg (not typing))
{:paddingBottom 20}))} {:paddingBottom 20}))}
[view {:style {:width 24}} [view {:style {:width 24}}
(when (not same-author) (when (not same-author)
@ -327,7 +326,7 @@
[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 [{:keys [msg-id content content-type outgoing delivery-status
group-chat new-day same-author same-direction last-msg]}] group-chat new-day same-author same-direction last-msg typing]}]
(let [delivery-status :seen] (let [delivery-status :seen]
[view {:style (merge {:flexDirection "column" [view {:style (merge {:flexDirection "column"
:width 260 :width 260
@ -343,7 +342,7 @@
:alignItems "flex-end"} :alignItems "flex-end"}
{:alignItems "flex-start" {:alignItems "flex-start"
:alignSelf "flex-start"}) :alignSelf "flex-start"})
(when last-msg (when (and last-msg (not typing))
{:paddingBottom 20}))} {:paddingBottom 20}))}
[message-content {:msg-id msg-id [message-content {:msg-id msg-id
:content-type content-type :content-type content-type
@ -353,22 +352,26 @@
(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 date new-day group-chat selected same-author same-direction last-msg] :as msg}] (defn chat-message [{:keys [msg-id from content content-type outgoing delivery-status
date new-day group-chat selected same-author same-direction
last-msg typing] :as msg}
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 {:msg-id msg-id
:from from :from from
:content content :content content
:content-type content-type :content-type content-type
:outgoing outgoing :outgoing outgoing
:delivery-status (keyword delivery-status) :delivery-status (keyword delivery-status)
:group-chat group-chat :group-chat group-chat
:selected selected :selected selected
:new-day new-day :new-day new-day
:same-author same-author :same-author same-author
:same-direction same-direction :same-direction same-direction
:last-msg last-msg}] :last-msg (= last-msg-id msg-id)
:typing typing}]
[view {} [view {}
(when (= content-type content-type-status) (when (= content-type content-type-status)
[message-content-status from content]) [message-content-status from content])

View File

@ -1,35 +1,39 @@
(ns syng-im.components.chat.new-participants (ns syng-im.components.chat.new-participants
(:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]]
[syng-im.resources :as res] [syng-im.resources :as res]
[syng-im.components.react :refer [view toolbar-android android? text-input]] [syng-im.components.react :refer [view android? text-input text image
touchable-highlight]]
[syng-im.components.realm :refer [list-view]] [syng-im.components.realm :refer [list-view]]
[syng-im.components.styles :refer [font
title-font
color-white
color-black
color-blue
text1-color
text2-color
toolbar-background1]]
[syng-im.components.toolbar :refer [toolbar]]
[syng-im.utils.listview :refer [to-realm-datasource]] [syng-im.utils.listview :refer [to-realm-datasource]]
[syng-im.components.chats.new-participant-contact :refer [new-participant-contact]] [syng-im.components.chats.new-participant-contact :refer [new-participant-contact]]
[reagent.core :as r] [reagent.core :as r]
[syng-im.navigation :refer [nav-pop]])) [syng-im.navigation :refer [nav-pop]]))
(defn new-participants-toolbar [navigator]
[toolbar {:navigator navigator
:title "Add Participants"
:action {:image {:source res/v ;; {:uri "icon_search"}
:style {:width 20
:height 18}}
:handler (fn []
(dispatch [:add-new-participants navigator]))}}])
(defn new-participants [{:keys [navigator]}] (defn new-participants [{:keys [navigator]}]
(let [contacts (subscribe [:all-new-contacts])] (let [contacts (subscribe [:all-new-contacts])]
(fn [] (fn []
(let [contacts-ds (to-realm-datasource @contacts)] (let [contacts-ds (to-realm-datasource @contacts)]
[view {:style {:flex 1 [view {:style {:flex 1
:backgroundColor "white"}} :backgroundColor "white"}}
(when android? [new-participants-toolbar navigator]
;; TODO add IOS version
[toolbar-android {:logo res/logo-icon
:title "Add Participants"
:titleColor "#4A5258"
:style {:backgroundColor "white"
:height 56
:elevation 2}
:actions [{:title "Add"
:icon res/v
:show "always"}]
:onActionSelected (fn [position]
(dispatch [:add-new-participants navigator]))
:navIcon res/nav-back-icon
:onIconClicked (fn []
(nav-pop navigator))}])
[list-view {:dataSource contacts-ds [list-view {:dataSource contacts-ds
:renderRow (fn [row section-id row-id] :renderRow (fn [row section-id row-id]
(r/as-element [new-participant-contact (js->clj row :keywordize-keys true) navigator])) (r/as-element [new-participant-contact (js->clj row :keywordize-keys true) navigator]))

View File

@ -1,35 +1,39 @@
(ns syng-im.components.chat.remove-participants (ns syng-im.components.chat.remove-participants
(:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]]
[syng-im.resources :as res] [syng-im.resources :as res]
[syng-im.components.react :refer [view toolbar-android android? text-input]] [syng-im.components.react :refer [view text-input text image
touchable-highlight]]
[syng-im.components.realm :refer [list-view]] [syng-im.components.realm :refer [list-view]]
[syng-im.components.styles :refer [font
title-font
color-white
color-black
color-blue
text1-color
text2-color
toolbar-background1]]
[syng-im.components.toolbar :refer [toolbar]]
[syng-im.utils.listview :refer [to-realm-datasource]] [syng-im.utils.listview :refer [to-realm-datasource]]
[syng-im.components.chats.new-participant-contact :refer [new-participant-contact]] [syng-im.components.chats.new-participant-contact :refer [new-participant-contact]]
[reagent.core :as r] [reagent.core :as r]
[syng-im.navigation :refer [nav-pop]])) [syng-im.navigation :refer [nav-pop]]))
(defn remove-participants-toolbar [navigator]
[toolbar {:navigator navigator
:title "Remove Participants"
:action {:image {:source res/trash-icon ;; {:uri "icon_search"}
:style {:width 22
:height 30}}
:handler (fn []
(dispatch [:remove-selected-participants navigator]))}}])
(defn remove-participants [{:keys [navigator]}] (defn remove-participants [{:keys [navigator]}]
(let [contacts (subscribe [:current-chat-contacts])] (let [contacts (subscribe [:current-chat-contacts])]
(fn [] (fn []
(let [contacts-ds (to-realm-datasource @contacts)] (let [contacts-ds (to-realm-datasource @contacts)]
[view {:style {:flex 1 [view {:style {:flex 1
:backgroundColor "white"}} :backgroundColor "white"}}
(when android? [remove-participants-toolbar navigator]
;; TODO add IOS version
[toolbar-android {:logo res/logo-icon
:title "Remove Participants"
:titleColor "#4A5258"
:style {:backgroundColor "white"
:height 56
:elevation 2}
:actions [{:title "Remove"
:icon res/trash-icon
:show "always"}]
:onActionSelected (fn [position]
(dispatch [:remove-selected-participants navigator]))
:navIcon res/nav-back-icon
:onIconClicked (fn []
(nav-pop navigator))}])
[list-view {:dataSource contacts-ds [list-view {:dataSource contacts-ds
:renderRow (fn [row section-id row-id] :renderRow (fn [row section-id row-id]
(r/as-element [new-participant-contact (js->clj row :keywordize-keys true) navigator])) (r/as-element [new-participant-contact (js->clj row :keywordize-keys true) navigator]))

View File

@ -4,8 +4,8 @@
view view
text text
image image
navigator touchable-highlight
toolbar-android]] navigator]]
[syng-im.components.realm :refer [list-view]] [syng-im.components.realm :refer [list-view]]
[syng-im.utils.logging :as log] [syng-im.utils.logging :as log]
[syng-im.navigation :refer [nav-pop]] [syng-im.navigation :refer [nav-pop]]
@ -22,8 +22,19 @@
color-blue color-blue
text1-color text1-color
text2-color]] text2-color]]
[syng-im.components.toolbar :refer [toolbar]]
[syng-im.components.icons.ionicons :refer [icon]])) [syng-im.components.icons.ionicons :refer [icon]]))
(defn chats-list-toolbar []
[toolbar {:nav-action {:image {:source {:uri "icon_hamburger"}
:style {:width 16
:height 12}}
:handler (fn [])}
:title "Chats"
:action {:image {:source {:uri "icon_search"}
:style {:width 17
:height 17}}
:handler (fn [])}}])
(defn chats-list [{:keys [navigator]}] (defn chats-list [{:keys [navigator]}]
(let [chats (subscribe [:get-chats])] (let [chats (subscribe [:get-chats])]
@ -33,27 +44,7 @@
datasource (to-realm-datasource chats)] datasource (to-realm-datasource chats)]
[view {:style {:flex 1 [view {:style {:flex 1
:backgroundColor "white"}} :backgroundColor "white"}}
(when android? [chats-list-toolbar]
;; TODO add IOS version
[toolbar-android {:navIcon {:uri "icon_hamburger"}
:style {:backgroundColor color-white
:height 56
:elevation 2}
:onIconClicked (fn []
(nav-pop navigator))
:actions [{:title "Search"
:icon {:uri "icon_search"}
:show "always"}]}
[view {:style {:flex 1
:alignItems "center"
:justifyContent "center"
:marginRight 112
:backgroundColor "transparent"}}
[text {:style {:marginTop -2.5
:color text1-color
:fontSize 16
:fontFamily font}}
"Chats"]]])
[list-view {:dataSource datasource [list-view {:dataSource datasource
:renderRow (fn [row section-id row-id] :renderRow (fn [row section-id row-id]
(r/as-element [chat-list-item row navigator])) (r/as-element [chat-list-item row navigator]))

View File

@ -2,55 +2,42 @@
(:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]]
[syng-im.resources :as res] [syng-im.resources :as res]
[syng-im.components.react :refer [view [syng-im.components.react :refer [view
toolbar-android
android?
text-input text-input
text text
image image
touchable-highlight]] touchable-highlight]]
[syng-im.components.styles :refer [font [syng-im.components.styles :refer [font
title-font
color-white
color-purple
text1-color text1-color
text2-color text2-color
color-white toolbar-background1]]
color-purple]] [syng-im.components.toolbar :refer [toolbar]]
[syng-im.components.realm :refer [list-view]] [syng-im.components.realm :refer [list-view]]
[syng-im.utils.listview :refer [to-realm-datasource]] [syng-im.utils.listview :refer [to-realm-datasource]]
[syng-im.components.chats.new-group-contact :refer [new-group-contact]] [syng-im.components.chats.new-group-contact :refer [new-group-contact]]
[reagent.core :as r] [reagent.core :as r]
[syng-im.navigation :refer [nav-pop]])) [syng-im.navigation :refer [nav-pop]]))
(defn new-group-toolbar [navigator group-name]
[toolbar {:navigator navigator
:title "New group chat"
:action {:image {:source res/v ;; {:uri "icon_search"}
:style {:width 20
:height 18}}
:handler (fn []
(dispatch [:create-new-group group-name navigator]))}}])
(defn new-group [{:keys [navigator]}] (defn new-group [{:keys [navigator]}]
(let [contacts (subscribe [:all-contacts]) (let [contacts (subscribe [:all-contacts])
group-name (atom nil)] group-name (r/atom nil)]
(fn [] (fn [{:keys [navigator]}]
(let [contacts-ds (to-realm-datasource @contacts)] (let [contacts-ds (to-realm-datasource @contacts)]
[view {:style {:flex 1 [view {:style {:flex 1
:flexDirection "column" :flexDirection "column"
:backgroundColor color-white}} :backgroundColor color-white}}
(when android? [new-group-toolbar navigator @group-name]
;; TODO add IOS version
[toolbar-android {:navIcon {:uri "icon_back"}
:style {:backgroundColor color-white
:height 56
:elevation 2}
:onIconClicked (fn []
(nav-pop navigator))
:actions [{:title "Create"
;; :icon res/icon-ok
:show "always"
:showWithText true}]
:onActionSelected (fn [position]
(dispatch [:create-new-group @group-name navigator]))}
[view {:style {:flex 1
:alignItems "center"
:justifyContent "center"
:marginRight 112
:backgroundColor "transparent"}}
[text {:style {:marginTop -2.5
:color text1-color
:fontSize 16
:fontFamily font}}
"New group chat"]]])
[view {:style {:marginHorizontal 16}} [view {:style {:marginHorizontal 16}}
[text {:style {:marginTop 24 [text {:style {:marginTop 24
:marginBottom 16 :marginBottom 16

View File

@ -4,9 +4,19 @@
[natal-shell.core :refer [with-error-view]]) [natal-shell.core :refer [with-error-view]])
(:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]]
[syng-im.components.react :refer [view text image touchable-highlight [syng-im.components.react :refer [view text image touchable-highlight
navigator list-view toolbar-android navigator list-view
list-item]] list-item]]
[syng-im.components.contact-list.contact :refer [contact-view]] [syng-im.components.contact-list.contact :refer [contact-view]]
[syng-im.components.styles :refer [font
title-font
color-white
color-black
color-blue
text1-color
text2-color
toolbar-background2]]
[syng-im.components.toolbar :refer [toolbar]]
[syng-im.navigation :refer [nav-pop]]
[syng-im.resources :as res] [syng-im.resources :as res]
[syng-im.utils.logging :as log])) [syng-im.utils.logging :as log]))
@ -19,18 +29,22 @@
(not= row1 row2))}) (not= row1 row2))})
contacts)) contacts))
(defn contact-list-toolbar [navigator]
[toolbar {:navigator navigator
:title "Contacts"
:background-color toolbar-background2
:action {:image {:source {:uri "icon_search"}
:style {:width 17
:height 17}}
:handler (fn [])}}])
(defn contact-list [{:keys [navigator]}] (defn contact-list [{:keys [navigator]}]
(let [contacts (subscribe [:get-contacts])] (let [contacts (subscribe [:get-contacts])]
(fn [] (fn []
(let [contacts-ds (get-data-source @contacts)] (let [contacts-ds (get-data-source @contacts)]
[view {:style {:flex 1 [view {:style {:flex 1
:backgroundColor "white"}} :backgroundColor "white"}}
[toolbar-android {:logo res/logo-icon [contact-list-toolbar navigator]
:title "Contacts"
:titleColor "#4A5258"
:style {:backgroundColor "white"
:height 56
:elevation 2}}]
(when contacts-ds (when contacts-ds
[list-view {:dataSource contacts-ds [list-view {:dataSource contacts-ds
:renderRow (partial render-row navigator) :renderRow (partial render-row navigator)

View File

@ -12,10 +12,14 @@
(def color-light-blue "#bbc4cb") (def color-light-blue "#bbc4cb")
(def color-light-blue-transparent "#bbc4cb32") (def color-light-blue-transparent "#bbc4cb32")
(def color-dark-mint "#5fc48d") (def color-dark-mint "#5fc48d")
(def color-light-gray "#EEF2F5")
(def text1-color color-black) (def text1-color color-black)
(def text2-color color-gray) (def text2-color color-gray)
(def online-color color-blue) (def online-color color-blue)
(def new-messages-count-color "#7099e632") (def new-messages-count-color "#7099e632")
(def chat-background "#EBF0F4") (def chat-background color-light-gray)
(def selected-message-color "#E4E9ED") (def selected-message-color "#E4E9ED")
(def separator-color "#0000001f")
(def toolbar-background1 color-white)
(def toolbar-background2 color-light-gray)

View File

@ -0,0 +1,58 @@
(ns syng-im.components.toolbar
(:require [re-frame.core :refer [subscribe dispatch dispatch-sync]]
[syng-im.resources :as res]
[syng-im.components.react :refer [view
text-input
text
image
touchable-highlight]]
[syng-im.components.styles :refer [font
title-font
color-white
color-purple
text1-color
text2-color
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]]))
(defn toolbar [{:keys [navigator title nav-action action background-color]}]
[view {:style {:flexDirection "row"
:backgroundColor (or background-color toolbar-background1)
:height 56
:elevation 2}}
(if nav-action
[touchable-highlight {:on-press (:handler nav-action)
:underlay-color :transparent}
[view {:width 56
:height 56
:alignItems "center"
:justifyContent "center"}
[image (:image nav-action)]]]
[touchable-highlight {:on-press (fn []
(nav-pop navigator))
: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
:alignItems "center"
:justifyContent "center"}}
[text {:style {:marginTop -2.5
:color text1-color
:fontSize 16
:fontFamily font}}
title]]
[touchable-highlight {:on-press (:handler action)
:underlay-color :transparent}
[view {:width 56
:height 56
:alignItems "center"
:justifyContent "center"}
[image (:image action)]]]])

View File

@ -9,9 +9,11 @@
:identity-password "replace-me-with-user-entered-password" :identity-password "replace-me-with-user-entered-password"
:contacts [] :contacts []
:chat {:current-chat-id "0x0479a5ed1f38cadfad1db6cd56c4b659b0ebe052bbe9efa950f6660058519fa4ca6be2dda66afa80de96ab00eb97a2605d5267a1e8f4c2a166ab551f6826608cdd" :chat {:current-chat-id "0x0479a5ed1f38cadfad1db6cd56c4b659b0ebe052bbe9efa950f6660058519fa4ca6be2dda66afa80de96ab00eb97a2605d5267a1e8f4c2a166ab551f6826608cdd"
:command nil} :command nil
:last-message nil}
:chats {} :chats {}
:chats-updated-signal 0 :chats-updated-signal 0
:show-actions false
:new-group #{} :new-group #{}
:new-participants #{} :new-participants #{}
:signed-up false}) :signed-up false})
@ -37,5 +39,6 @@
[:chats chat-id :command-requests]) [:chats chat-id :command-requests])
(defn chat-command-request-path [chat-id msg-id] (defn chat-command-request-path [chat-id msg-id]
[:chats chat-id :command-requests msg-id]) [:chats chat-id :command-requests msg-id])
(def show-actions-path [:show-actions])
(def new-group-path [:new-group]) (def new-group-path [:new-group])
(def new-participants-path [:new-participants]) (def new-participants-path [:new-participants])

View File

@ -119,6 +119,11 @@
(log/debug action commands) (log/debug action commands)
(set-commands db commands))) (set-commands db commands)))
(register-handler :set-show-actions
(fn [db [action show-actions]]
(log/debug action)
(assoc-in db db/show-actions-path show-actions)))
;; -- Protocol -------------------------------------------------------------- ;; -- Protocol --------------------------------------------------------------
(register-handler :initialize-protocol (register-handler :initialize-protocol
@ -136,10 +141,9 @@
(fn [db [action {chat-id :from (fn [db [action {chat-id :from
msg-id :msg-id :as msg}]] msg-id :msg-id :as msg}]]
(log/debug action "msg" msg) (log/debug action "msg" msg)
(save-message chat-id msg) (let [db (create-chat db chat-id [chat-id] false)]
(-> db (save-message chat-id msg)
(create-chat chat-id [chat-id] false) (signal-chat-updated db chat-id))))
(signal-chat-updated 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}]]

View File

@ -1,10 +1,12 @@
(ns syng-im.models.chats (ns syng-im.models.chats
(:require [clojure.set :refer [difference]] (:require [clojure.set :refer [difference]]
[syng-im.persistence.realm :as r] [syng-im.persistence.realm :as r]
[syng-im.utils.random :refer [timestamp]] [syng-im.utils.random :as random :refer [timestamp]]
[clojure.string :refer [join blank?]] [clojure.string :refer [join blank?]]
[syng-im.db :as db] [syng-im.db :as db]
[syng-im.utils.logging :as log] [syng-im.utils.logging :as log]
[syng-im.constants :refer [content-type-status]]
[syng-im.models.messages :refer [save-message]]
[syng-im.persistence.realm-queries :refer [include-query]] [syng-im.persistence.realm-queries :refer [include-query]]
[syng-im.models.chat :refer [signal-chat-updated]])) [syng-im.models.chat :refer [signal-chat-updated]]))
@ -35,6 +37,18 @@
(defn chat-exists? [chat-id] (defn chat-exists? [chat-id]
(r/exists? :chats :chat-id chat-id)) (r/exists? :chats :chat-id chat-id))
(defn add-status-message [chat-id]
;; TODO Get real status
(save-message chat-id
{:from "Status"
:to nil
:msg-id (random/id)
:content (str "The brash businessmans braggadocio "
"and public exchange with candidates "
"in the US presidential election")
:content-type content-type-status
:outgoing false}))
(defn create-chat (defn create-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))
@ -48,12 +62,14 @@
(fn [] (fn []
(let [contacts (mapv (fn [ident] (let [contacts (mapv (fn [ident]
{:identity ident}) identities)] {:identity ident}) identities)]
(r/create :chats {:chat-id chat-id (r/create :chats {:chat-id chat-id
:is-active true :is-active true
:name chat-name :name chat-name
:group-chat group-chat? :group-chat group-chat?
:timestamp (timestamp) :timestamp (timestamp)
:contacts contacts})))) :contacts contacts
:last-msg-id ""}))))
(add-status-message chat-id)
(signal-chats-updated db))))) (signal-chats-updated db)))))
(defn chat-contacts [chat-id] (defn chat-contacts [chat-id]

View File

@ -5,21 +5,36 @@
[syng-im.db :as db] [syng-im.db :as db]
[syng-im.utils.logging :as log])) [syng-im.utils.logging :as log]))
(defn select-chat-last-message [chat]
(when-let [last-msg-id (:last-msg-id chat)]
(r/single-cljs (r/get-by-field :msgs :msg-id last-msg-id))))
(defn save-message [chat-id {:keys [from to msg-id content content-type outgoing] :or {outgoing false (defn save-message [chat-id {:keys [from to msg-id content content-type outgoing] :or {outgoing false
to nil} :as msg}] to nil} :as msg}]
(log/debug "save-message" chat-id msg) (log/debug "save-message" chat-id msg)
(when-not (r/exists? :msgs :msg-id msg-id) (when-not (r/exists? :msgs :msg-id msg-id)
(r/write (r/write
(fn [] (fn []
(r/create :msgs {:chat-id chat-id (let [chat (r/single-cljs (r/get-by-field :chats :chat-id chat-id))
:msg-id msg-id last-message (select-chat-last-message chat)]
:from from (r/create :msgs {:chat-id chat-id
:to to :msg-id msg-id
:content content :from from
:content-type content-type :to to
:outgoing outgoing :content content
:timestamp (timestamp) :content-type content-type
:delivery-status nil} true))))) :outgoing outgoing
:timestamp (timestamp)
:delivery-status nil
:same-author (if last-message
(= (:from last-message) from)
true)
:same-direction (if last-message
(= (:outgoing last-message) outgoing)
true)} true)
(r/create :chats {:chat-id (:chat-id chat)
:last-msg-id msg-id}
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/sorted (r/get-by-field :msgs :chat-id chat-id) :timestamp :desc))

View File

@ -32,21 +32,24 @@
:indexed true} :indexed true}
:outgoing "bool" :outgoing "bool"
:delivery-status {:type "string" :delivery-status {:type "string"
:optional true}}} :optional true}
:same-author "bool"
:same-direction "bool"}}
{:name :chat-contact {:name :chat-contact
:properties {:identity "string" :properties {:identity "string"
:is-in-chat {:type "bool" :is-in-chat {:type "bool"
:default true}}} :default true}}}
{:name :chats {:name :chats
:primaryKey :chat-id :primaryKey :chat-id
:properties {:chat-id "string" :properties {:chat-id "string"
:name "string" :name "string"
:group-chat {:type "bool" :group-chat {:type "bool"
:indexed true} :indexed true}
:is-active "bool" :is-active "bool"
:timestamp "int" :timestamp "int"
:contacts {:type "list" :contacts {:type "list"
:objectType "chat-contact"}}}]}) :objectType "chat-contact"}
:last-msg-id "string"}}]})
(def realm (js/Realm. (clj->js opts))) (def realm (js/Realm. (clj->js opts)))

View File

@ -109,6 +109,11 @@
(reaction (reaction
(get @db :signed-up)))) (get @db :signed-up))))
(register-sub
:show-actions
(fn [db _]
(reaction (get-in @db db/show-actions-path))))
(register-sub (register-sub
:get-contacts :get-contacts
(fn [db _] (fn [db _]