diff --git a/android/app/src/main/res/drawable-hdpi/icon_menu_group.png b/android/app/src/main/res/drawable-hdpi/icon_menu_group.png new file mode 100644 index 0000000000..40e9b7dc41 Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_menu_group.png differ diff --git a/android/app/src/main/res/drawable-hdpi/icon_muted.png b/android/app/src/main/res/drawable-hdpi/icon_muted.png new file mode 100644 index 0000000000..2c862042ba Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_muted.png differ diff --git a/android/app/src/main/res/drawable-hdpi/icon_search_gray_copy.png b/android/app/src/main/res/drawable-hdpi/icon_search_gray_copy.png new file mode 100644 index 0000000000..fe1fd1299c Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_search_gray_copy.png differ diff --git a/android/app/src/main/res/drawable-hdpi/icon_settings.png b/android/app/src/main/res/drawable-hdpi/icon_settings.png new file mode 100644 index 0000000000..ab60673483 Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_settings.png differ diff --git a/android/app/src/main/res/drawable-hdpi/icon_up.png b/android/app/src/main/res/drawable-hdpi/icon_up.png new file mode 100644 index 0000000000..421b5f459d Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/icon_up.png differ diff --git a/android/app/src/main/res/drawable-mdpi/icon_menu_group.png b/android/app/src/main/res/drawable-mdpi/icon_menu_group.png new file mode 100644 index 0000000000..f17ab21946 Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_menu_group.png differ diff --git a/android/app/src/main/res/drawable-mdpi/icon_muted.png b/android/app/src/main/res/drawable-mdpi/icon_muted.png new file mode 100644 index 0000000000..21c7dc3962 Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_muted.png differ diff --git a/android/app/src/main/res/drawable-mdpi/icon_search_gray_copy.png b/android/app/src/main/res/drawable-mdpi/icon_search_gray_copy.png new file mode 100644 index 0000000000..61041d5c3b Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_search_gray_copy.png differ diff --git a/android/app/src/main/res/drawable-mdpi/icon_settings.png b/android/app/src/main/res/drawable-mdpi/icon_settings.png new file mode 100644 index 0000000000..f01c784c4c Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_settings.png differ diff --git a/android/app/src/main/res/drawable-mdpi/icon_up.png b/android/app/src/main/res/drawable-mdpi/icon_up.png new file mode 100644 index 0000000000..a2db655bd6 Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/icon_up.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/icon_menu_group.png b/android/app/src/main/res/drawable-xhdpi/icon_menu_group.png new file mode 100644 index 0000000000..28c99ec40f Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_menu_group.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/icon_muted.png b/android/app/src/main/res/drawable-xhdpi/icon_muted.png new file mode 100644 index 0000000000..4195619670 Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_muted.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/icon_search_gray_copy.png b/android/app/src/main/res/drawable-xhdpi/icon_search_gray_copy.png new file mode 100644 index 0000000000..f0a3045947 Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_search_gray_copy.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/icon_settings.png b/android/app/src/main/res/drawable-xhdpi/icon_settings.png new file mode 100644 index 0000000000..72aaee326f Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_settings.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/icon_up.png b/android/app/src/main/res/drawable-xhdpi/icon_up.png new file mode 100644 index 0000000000..9b150cd9e1 Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/icon_up.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_menu_group.png b/android/app/src/main/res/drawable-xxhdpi/icon_menu_group.png new file mode 100644 index 0000000000..a356f7ccdd Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_menu_group.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_muted.png b/android/app/src/main/res/drawable-xxhdpi/icon_muted.png new file mode 100644 index 0000000000..674a9faa66 Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_muted.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_search_gray_copy.png b/android/app/src/main/res/drawable-xxhdpi/icon_search_gray_copy.png new file mode 100644 index 0000000000..d138f4e4b9 Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_search_gray_copy.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_settings.png b/android/app/src/main/res/drawable-xxhdpi/icon_settings.png new file mode 100644 index 0000000000..561a5dcc25 Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_settings.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_up.png b/android/app/src/main/res/drawable-xxhdpi/icon_up.png new file mode 100644 index 0000000000..01e16f8ce1 Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/icon_up.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_menu_group.png b/android/app/src/main/res/drawable-xxxhdpi/icon_menu_group.png new file mode 100644 index 0000000000..7c51fd4dd0 Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_menu_group.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_muted.png b/android/app/src/main/res/drawable-xxxhdpi/icon_muted.png new file mode 100644 index 0000000000..bb46070875 Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_muted.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_search_gray_copy.png b/android/app/src/main/res/drawable-xxxhdpi/icon_search_gray_copy.png new file mode 100644 index 0000000000..4e1948e8ff Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_search_gray_copy.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_settings.png b/android/app/src/main/res/drawable-xxxhdpi/icon_settings.png new file mode 100644 index 0000000000..b9f142032e Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_settings.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_up.png b/android/app/src/main/res/drawable-xxxhdpi/icon_up.png new file mode 100644 index 0000000000..d98dff7ff6 Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/icon_up.png differ diff --git a/src/syng_im/components/chat.cljs b/src/syng_im/components/chat.cljs index de44012e86..cffdf19a9b 100644 --- a/src/syng_im/components/chat.cljs +++ b/src/syng_im/components/chat.cljs @@ -1,12 +1,11 @@ (ns syng-im.components.chat (:require [clojure.string :as s] [re-frame.core :refer [subscribe dispatch dispatch-sync]] - [syng-im.components.react :refer [android? - view + [syng-im.components.react :refer [view text image navigator - toolbar-android]] + touchable-highlight]] [syng-im.components.realm :refer [list-view]] [syng-im.components.styles :refer [font title-font @@ -14,12 +13,13 @@ chat-background online-color selected-message-color + separator-color text1-color - text2-color]] + text2-color + toolbar-background1]] [syng-im.utils.logging :as log] [syng-im.navigation :refer [nav-pop]] [syng-im.resources :as res] - [syng-im.constants :refer [content-type-status]] [syng-im.utils.listview :refer [to-realm-datasource]] [syng-im.components.invertible-scroll-view :refer [invertible-scroll-view]] [reagent.core :as r] @@ -42,7 +42,8 @@ :background-color background-color)))) (defn chat-photo [{:keys [photo-path]}] - [view {:borderRadius 50} + [view {:margin 10 + :borderRadius 50} [image {:source (if (s/blank? photo-path) res/user-no-photo {:uri photo-path}) @@ -53,8 +54,8 @@ (defn contact-online [{:keys [online]}] (when online [view {:position "absolute" - :top 20 - :left 20 + :top 30 + :left 30 :width 20 :height 20 :borderRadius 50 @@ -78,8 +79,7 @@ (defn typing [member] [view {:style {:width 260 - :paddingTop 2 - :paddingBottom 8 + :marginTop 10 :paddingLeft 8 :paddingRight 8 :alignItems "flex-start" @@ -95,139 +95,188 @@ (str member " is typing")]]]) (defn typing-all [] - [view {:style {:marginBottom 12}} + [view {:style {:marginBottom 20}} (for [member ["Geoff" "Justas"]] ^{:key member} [typing member])]) -(defn toolbar-content-chat [chat] - (let [group? (:group-chat chat)] +(defn action-view [action] + [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 - :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 + :alignItems "flex-start" + :justifyContent "center"}} + [text {:style {:marginTop -2.5 + :color text1-color + :fontSize 14 + :fontFamily font}} + (:title action)] + (when-let [subtitle (:subtitle action)] + [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}} - (or (chat :name) - "Chat name")] - (if group? - [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"])] - (when-not group? - [view {:style {:position "absolute" - :top 10 - :right 66}} - [chat-photo {}] - [contact-online {:online true}]])])) + "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]}] - (let [messages (subscribe [:get-chat-messages]) - chat (subscribe [:get-current-chat])] + (let [messages (subscribe [:get-chat-messages]) + chat (subscribe [:get-current-chat]) + show-actions-atom (subscribe [:show-actions])] (fn [] (let [msgs @messages ;_ (log/debug "messages=" msgs) - ;; temp to show first status - msgs-clj (as-> (js->clj msgs) ms - (assoc ms "-1" - {:msg-id "-1" - :content (str "The brash businessman’s 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) + ;; temp + typing (:group-chat @chat) ;; end temp datasource (to-realm-datasource msgs) contacts (:contacts @chat) contact-by-identity (contacts-by-identity contacts)] [view {:style {:flex 1 :backgroundColor chat-background}} - (when android? - ;; TODO add IOS version - [toolbar-android {:navIcon {:uri "icon_back"} - :style {:backgroundColor color-white - :height 56 - :elevation 2} - :actions (when (and (:group-chat @chat) - (:is-active @chat)) - [{: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}]) - :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"}}] + [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])])))) + [chat-message-new]) + (when @show-actions-atom + [actions-view navigator @chat])])))) diff --git a/src/syng_im/components/chat/chat_message.cljs b/src/syng_im/components/chat/chat_message.cljs index c20930d19d..1127525b45 100644 --- a/src/syng_im/components/chat/chat_message.cljs +++ b/src/syng_im/components/chat/chat_message.cljs @@ -6,8 +6,7 @@ text image touchable-highlight - navigator - toolbar-android]] + navigator]] [syng-im.components.styles :refer [font color-light-blue-transparent color-white @@ -287,7 +286,7 @@ (defn incoming-group-message-body [{:keys [msg-id from content content-type outgoing delivery-status selected new-day same-author - same-direction last-msg]}] + same-direction last-msg typing]}] (let [delivery-status :seen-by-everyone] [view {:style {:flexDirection "column"}} (when selected @@ -306,7 +305,7 @@ :else 10) :paddingRight 8 :paddingLeft 8} - (when last-msg + (when (and last-msg (not typing)) {:paddingBottom 20}))} [view {:style {:width 24}} (when (not same-author) @@ -327,7 +326,7 @@ [message-delivery-status {:delivery-status 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] [view {:style (merge {:flexDirection "column" :width 260 @@ -343,7 +342,7 @@ :alignItems "flex-end"} {:alignItems "flex-start" :alignSelf "flex-start"}) - (when last-msg + (when (and last-msg (not typing)) {:paddingBottom 20}))} [message-content {:msg-id msg-id :content-type content-type @@ -353,22 +352,26 @@ (when (and outgoing 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 {} (when new-day [message-date {:date date}]) - (let [msg-data {:msg-id msg-id - :from from - :content content - :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}] + (let [msg-data {:msg-id msg-id + :from from + :content content + :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 {} (when (= content-type content-type-status) [message-content-status from content]) diff --git a/src/syng_im/components/chat/new_participants.cljs b/src/syng_im/components/chat/new_participants.cljs index dedff2578a..b3e1b4e446 100644 --- a/src/syng_im/components/chat/new_participants.cljs +++ b/src/syng_im/components/chat/new_participants.cljs @@ -1,35 +1,39 @@ (ns syng-im.components.chat.new-participants (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] [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.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.components.chats.new-participant-contact :refer [new-participant-contact]] [reagent.core :as r] [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]}] (let [contacts (subscribe [:all-new-contacts])] (fn [] (let [contacts-ds (to-realm-datasource @contacts)] [view {:style {:flex 1 :backgroundColor "white"}} - (when android? - ;; 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))}]) + [new-participants-toolbar navigator] [list-view {:dataSource contacts-ds :renderRow (fn [row section-id row-id] (r/as-element [new-participant-contact (js->clj row :keywordize-keys true) navigator])) diff --git a/src/syng_im/components/chat/remove_participants.cljs b/src/syng_im/components/chat/remove_participants.cljs index 1620ac3757..dded8733b8 100644 --- a/src/syng_im/components/chat/remove_participants.cljs +++ b/src/syng_im/components/chat/remove_participants.cljs @@ -1,35 +1,39 @@ (ns syng-im.components.chat.remove-participants (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] [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.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.components.chats.new-participant-contact :refer [new-participant-contact]] [reagent.core :as r] [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]}] (let [contacts (subscribe [:current-chat-contacts])] (fn [] (let [contacts-ds (to-realm-datasource @contacts)] [view {:style {:flex 1 :backgroundColor "white"}} - (when android? - ;; 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))}]) + [remove-participants-toolbar navigator] [list-view {:dataSource contacts-ds :renderRow (fn [row section-id row-id] (r/as-element [new-participant-contact (js->clj row :keywordize-keys true) navigator])) diff --git a/src/syng_im/components/chats/chats_list.cljs b/src/syng_im/components/chats/chats_list.cljs index 8295a0b24f..b41a54fbaa 100644 --- a/src/syng_im/components/chats/chats_list.cljs +++ b/src/syng_im/components/chats/chats_list.cljs @@ -4,8 +4,8 @@ view text image - navigator - toolbar-android]] + touchable-highlight + navigator]] [syng-im.components.realm :refer [list-view]] [syng-im.utils.logging :as log] [syng-im.navigation :refer [nav-pop]] @@ -22,8 +22,19 @@ color-blue text1-color text2-color]] + [syng-im.components.toolbar :refer [toolbar]] [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]}] (let [chats (subscribe [:get-chats])] @@ -33,27 +44,7 @@ datasource (to-realm-datasource chats)] [view {:style {:flex 1 :backgroundColor "white"}} - (when android? - ;; 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"]]]) + [chats-list-toolbar] [list-view {:dataSource datasource :renderRow (fn [row section-id row-id] (r/as-element [chat-list-item row navigator])) diff --git a/src/syng_im/components/chats/new_group.cljs b/src/syng_im/components/chats/new_group.cljs index 0e33f82c5b..f2461b4d73 100644 --- a/src/syng_im/components/chats/new_group.cljs +++ b/src/syng_im/components/chats/new_group.cljs @@ -2,55 +2,42 @@ (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] [syng-im.resources :as res] [syng-im.components.react :refer [view - toolbar-android - android? text-input text image touchable-highlight]] [syng-im.components.styles :refer [font + title-font + color-white + color-purple text1-color text2-color - color-white - color-purple]] + toolbar-background1]] + [syng-im.components.toolbar :refer [toolbar]] [syng-im.components.realm :refer [list-view]] [syng-im.utils.listview :refer [to-realm-datasource]] [syng-im.components.chats.new-group-contact :refer [new-group-contact]] [reagent.core :as r] [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]}] (let [contacts (subscribe [:all-contacts]) - group-name (atom nil)] - (fn [] + group-name (r/atom nil)] + (fn [{:keys [navigator]}] (let [contacts-ds (to-realm-datasource @contacts)] [view {:style {:flex 1 :flexDirection "column" :backgroundColor color-white}} - (when android? - ;; 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"]]]) + [new-group-toolbar navigator @group-name] [view {:style {:marginHorizontal 16}} [text {:style {:marginTop 24 :marginBottom 16 diff --git a/src/syng_im/components/contact_list/contact_list.cljs b/src/syng_im/components/contact_list/contact_list.cljs index d2aeb9b321..06fd584f7c 100644 --- a/src/syng_im/components/contact_list/contact_list.cljs +++ b/src/syng_im/components/contact_list/contact_list.cljs @@ -4,9 +4,19 @@ [natal-shell.core :refer [with-error-view]]) (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] [syng-im.components.react :refer [view text image touchable-highlight - navigator list-view toolbar-android + navigator list-view list-item]] [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.utils.logging :as log])) @@ -19,18 +29,22 @@ (not= row1 row2))}) 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]}] (let [contacts (subscribe [:get-contacts])] (fn [] (let [contacts-ds (get-data-source @contacts)] [view {:style {:flex 1 :backgroundColor "white"}} - [toolbar-android {:logo res/logo-icon - :title "Contacts" - :titleColor "#4A5258" - :style {:backgroundColor "white" - :height 56 - :elevation 2}}] + [contact-list-toolbar navigator] (when contacts-ds [list-view {:dataSource contacts-ds :renderRow (partial render-row navigator) diff --git a/src/syng_im/components/styles.cljs b/src/syng_im/components/styles.cljs index f0a6d7db9f..5f55404a35 100644 --- a/src/syng_im/components/styles.cljs +++ b/src/syng_im/components/styles.cljs @@ -12,10 +12,14 @@ (def color-light-blue "#bbc4cb") (def color-light-blue-transparent "#bbc4cb32") (def color-dark-mint "#5fc48d") +(def color-light-gray "#EEF2F5") (def text1-color color-black) (def text2-color color-gray) (def online-color color-blue) (def new-messages-count-color "#7099e632") -(def chat-background "#EBF0F4") +(def chat-background color-light-gray) (def selected-message-color "#E4E9ED") +(def separator-color "#0000001f") +(def toolbar-background1 color-white) +(def toolbar-background2 color-light-gray) diff --git a/src/syng_im/components/toolbar.cljs b/src/syng_im/components/toolbar.cljs new file mode 100644 index 0000000000..94155f8a6f --- /dev/null +++ b/src/syng_im/components/toolbar.cljs @@ -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)]]]]) diff --git a/src/syng_im/db.cljs b/src/syng_im/db.cljs index c7bf8b10d1..7cfaae514f 100644 --- a/src/syng_im/db.cljs +++ b/src/syng_im/db.cljs @@ -9,9 +9,11 @@ :identity-password "replace-me-with-user-entered-password" :contacts [] :chat {:current-chat-id "0x0479a5ed1f38cadfad1db6cd56c4b659b0ebe052bbe9efa950f6660058519fa4ca6be2dda66afa80de96ab00eb97a2605d5267a1e8f4c2a166ab551f6826608cdd" - :command nil} + :command nil + :last-message nil} :chats {} :chats-updated-signal 0 + :show-actions false :new-group #{} :new-participants #{} :signed-up false}) @@ -37,5 +39,6 @@ [:chats chat-id :command-requests]) (defn chat-command-request-path [chat-id msg-id] [:chats chat-id :command-requests msg-id]) +(def show-actions-path [:show-actions]) (def new-group-path [:new-group]) (def new-participants-path [:new-participants]) diff --git a/src/syng_im/handlers.cljs b/src/syng_im/handlers.cljs index 9febceb3f3..fd43a4528c 100644 --- a/src/syng_im/handlers.cljs +++ b/src/syng_im/handlers.cljs @@ -119,6 +119,11 @@ (log/debug action 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 -------------------------------------------------------------- (register-handler :initialize-protocol @@ -136,10 +141,9 @@ (fn [db [action {chat-id :from msg-id :msg-id :as msg}]] (log/debug action "msg" msg) - (save-message chat-id msg) - (-> db - (create-chat chat-id [chat-id] false) - (signal-chat-updated chat-id)))) + (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 (fn [db [action {chat-id :group-id :as msg}]] diff --git a/src/syng_im/models/chats.cljs b/src/syng_im/models/chats.cljs index a8aab3b576..9b0acd5b0d 100644 --- a/src/syng_im/models/chats.cljs +++ b/src/syng_im/models/chats.cljs @@ -1,10 +1,12 @@ (ns syng-im.models.chats (:require [clojure.set :refer [difference]] [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?]] [syng-im.db :as db] [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.models.chat :refer [signal-chat-updated]])) @@ -35,6 +37,18 @@ (defn chat-exists? [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 businessman’s braggadocio " + "and public exchange with candidates " + "in the US presidential election") + :content-type content-type-status + :outgoing false})) + (defn create-chat ([db chat-id identities group-chat?] (create-chat db chat-id identities group-chat? nil)) @@ -48,12 +62,14 @@ (fn [] (let [contacts (mapv (fn [ident] {:identity ident}) identities)] - (r/create :chats {:chat-id chat-id - :is-active true - :name chat-name - :group-chat group-chat? - :timestamp (timestamp) - :contacts contacts})))) + (r/create :chats {:chat-id chat-id + :is-active true + :name chat-name + :group-chat group-chat? + :timestamp (timestamp) + :contacts contacts + :last-msg-id ""})))) + (add-status-message chat-id) (signal-chats-updated db))))) (defn chat-contacts [chat-id] diff --git a/src/syng_im/models/messages.cljs b/src/syng_im/models/messages.cljs index c40826b9c8..e0ebfea898 100644 --- a/src/syng_im/models/messages.cljs +++ b/src/syng_im/models/messages.cljs @@ -5,21 +5,36 @@ [syng-im.db :as db] [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 to nil} :as msg}] (log/debug "save-message" chat-id msg) (when-not (r/exists? :msgs :msg-id msg-id) (r/write - (fn [] - (r/create :msgs {:chat-id chat-id - :msg-id msg-id - :from from - :to to - :content content - :content-type content-type - :outgoing outgoing - :timestamp (timestamp) - :delivery-status nil} true))))) + (fn [] + (let [chat (r/single-cljs (r/get-by-field :chats :chat-id chat-id)) + last-message (select-chat-last-message chat)] + (r/create :msgs {:chat-id chat-id + :msg-id msg-id + :from from + :to to + :content content + :content-type content-type + :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] (r/sorted (r/get-by-field :msgs :chat-id chat-id) :timestamp :desc)) diff --git a/src/syng_im/persistence/realm.cljs b/src/syng_im/persistence/realm.cljs index 074d70728f..dcb5601e10 100644 --- a/src/syng_im/persistence/realm.cljs +++ b/src/syng_im/persistence/realm.cljs @@ -32,21 +32,24 @@ :indexed true} :outgoing "bool" :delivery-status {:type "string" - :optional true}}} + :optional true} + :same-author "bool" + :same-direction "bool"}} {:name :chat-contact :properties {:identity "string" :is-in-chat {:type "bool" :default true}}} {:name :chats :primaryKey :chat-id - :properties {:chat-id "string" - :name "string" - :group-chat {:type "bool" + :properties {:chat-id "string" + :name "string" + :group-chat {:type "bool" :indexed true} - :is-active "bool" - :timestamp "int" - :contacts {:type "list" - :objectType "chat-contact"}}}]}) + :is-active "bool" + :timestamp "int" + :contacts {:type "list" + :objectType "chat-contact"} + :last-msg-id "string"}}]}) (def realm (js/Realm. (clj->js opts))) diff --git a/src/syng_im/subs.cljs b/src/syng_im/subs.cljs index 2dee0c6335..19b0ab404f 100644 --- a/src/syng_im/subs.cljs +++ b/src/syng_im/subs.cljs @@ -109,6 +109,11 @@ (reaction (get @db :signed-up)))) +(register-sub + :show-actions + (fn [db _] + (reaction (get-in @db db/show-actions-path)))) + (register-sub :get-contacts (fn [db _]