diff --git a/.re-natal b/.re-natal index 56b16e17bf..1455b14c43 100644 --- a/.re-natal +++ b/.re-natal @@ -15,6 +15,7 @@ "react-native-circle-checkbox", "react-native-randombytes", "dismissKeyboard", + "react-native-linear-gradient", "react-native-android-sms-listener" ], "imageDirs": [ diff --git a/README.md b/README.md index 3635c7175e..107e330d6c 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ A Clojure library designed to ... well, that part is up to you. adb reverse tcp:8081 tcp:8081 adb reverse tcp:3449 tcp:3449 react-native run-android - +(re-frame.core/dispatch [:set-signed-up true]) ## License diff --git a/android/app/build.gradle b/android/app/build.gradle index a81c12cdc1..379551061c 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -128,6 +128,7 @@ dependencies { compile "com.facebook.react:react-native:+" // From node_modules compile project(':react-native-contacts') compile project(':react-native-i18n') + compile project(':react-native-linear-gradient') compile project(':ReactNativeAndroidSmsListener') // compile(name:'geth', ext:'aar') compile(group: 'status-im', name: 'android-geth', version: '1.4.0-201604110816-a97a114', ext: 'aar') diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index a7eb44af57..d8e1a14016 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -4,6 +4,7 @@ + db - (assoc :current-chat-id (or id current-chat-id)) - load-messages! - init-chat)) + (let [chat-id (or id current-chat-id) + messages (get-in db [:chats chat-id :messages]) + db' (assoc db :current-chat-id chat-id)] + (if (seq messages) + db' + (-> db' + load-messages! + init-chat)))) (defn prepare-chat [{:keys [contacts] :as db} [_ contcat-id]] @@ -334,3 +340,45 @@ (register-handler :switch-command-suggestions (fn [db [_]] (suggestions/switch-command-suggestions db))) + +(defn remove-chat + [{:keys [current-chat-id] :as db} _] + (update db :chats dissoc current-chat-id)) + +(defn notify-about-leaving! + [{:keys [current-chat-id]} _] + (api/leave-group-chat current-chat-id)) + +; todo do we really need this message? +(defn leaving-message! + [{:keys [current-chat-id]} _] + (messages/save-message + current-chat-id + {:from "system" + :msg-id (random/id) + :content "You left this chat" + :content-type text-content-type})) + +(defn delete-messages! + [{:keys [current-chat-id]} _] + (r/write + (fn [] + (r/delete (r/get-by-field :msgs :chat-id current-chat-id))))) + +(defn delete-chat! + [{:keys [current-chat-id]} _] + (r/write + (fn [] + (-> (r/get-by-field :chats :chat-id current-chat-id) + (r/single) + (r/delete))))) + +(register-handler :leave-group-chat + ;; todo oreder of operations tbd + (after (fn [_ _] (dispatch [:navigation-replace :chat-list]))) + (-> remove-chat + ;; todo uncomment + ;((after notify-about-leaving!)) + ;((after leaving-message!)) + ((after delete-messages!)) + ((after delete-chat!)))) diff --git a/src/status_im/chat/screen.cljs b/src/status_im/chat/screen.cljs index 8289fe7bb9..19723aba22 100644 --- a/src/status_im/chat/screen.cljs +++ b/src/status_im/chat/screen.cljs @@ -64,8 +64,8 @@ (defn on-action-selected [position] (case position - 0 (dispatch [:show-add-participants]) - 1 (dispatch [:show-remove-participants]) + 0 (dispatch [:navigate-to :add-participants]) + 1 (dispatch [:navigate-to :remove-participants]) 2 (dispatch [:leave-group-chat]))) (defn overlay [{:keys [on-click-outside]} items] diff --git a/src/status_im/chat/sign_up.cljs b/src/status_im/chat/sign_up.cljs index 62f0df393a..ef855d0021 100644 --- a/src/status_im/chat/sign_up.cljs +++ b/src/status_im/chat/sign_up.cljs @@ -4,6 +4,7 @@ [status-im.persistence.simple-kv-store :as kv] [status-im.protocol.state.storage :as s] [status-im.models.chats :as c] + [status-im.components.styles :refer [default-chat-color]] [status-im.utils.utils :refer [log on-error http-post toast]] [status-im.utils.random :as random] [status-im.utils.sms-listener :refer [add-sms-listener @@ -192,6 +193,7 @@ (def console-chat {:chat-id "console" :name "console" + :color default-chat-color :group-chat false :is-active true :timestamp (.getTime (js/Date.)) diff --git a/src/status_im/chats_list/screen.cljs b/src/status_im/chats_list/screen.cljs index 700ee209b2..48babbcee1 100644 --- a/src/status_im/chats_list/screen.cljs +++ b/src/status_im/chats_list/screen.cljs @@ -1,11 +1,11 @@ (ns status-im.chats-list.screen (:require [re-frame.core :refer [subscribe dispatch]] [status-im.components.react :refer [list-view - list-item - view - text - image - touchable-highlight]] + list-item + view + text + image + touchable-highlight]] [status-im.utils.listview :refer [to-datasource]] [reagent.core :as r] [status-im.chats-list.views.chat-list-item :refer [chat-list-item]] @@ -15,10 +15,10 @@ [status-im.components.styles :refer [color-blue toolbar-background2]] [status-im.components.toolbar :refer [toolbar]] + [status-im.components.main-tabs :refer [main-tabs]] [status-im.components.icons.ionicons :refer [icon]] [status-im.chats-list.styles :as st])) - (defn chats-list-toolbar [] [toolbar {:nav-action {:image {:source {:uri :icon_hamburger} :style st/hamburger-icon} @@ -40,7 +40,9 @@ :renderRow (fn [row _ _] (list-item [chat-list-item row])) :style st/list-container}] - [action-button {:buttonColor color-blue} + [action-button {:buttonColor color-blue + :offsetY 72 + :offsetX 16} [action-button-item {:title "New Chat" :buttonColor :#9b59b6 @@ -52,4 +54,5 @@ :buttonColor :#1abc9c :onPress #(dispatch [:show-group-new])} [icon {:name :person-stalker - :style st/person-stalker-icon}]]]]]))) + :style st/person-stalker-icon}]]] + [main-tabs]]]))) diff --git a/src/status_im/chats_list/styles.cljs b/src/status_im/chats_list/styles.cljs index ca72f89042..0fae1d82fd 100644 --- a/src/status_im/chats_list/styles.cljs +++ b/src/status_im/chats_list/styles.cljs @@ -6,7 +6,8 @@ online-color text1-color text2-color - new-messages-count-color]])) + new-messages-count-color]] + [status-im.components.tabs.styles :refer [tab-height]])) (def chat-container {:flexDirection :row @@ -102,7 +103,8 @@ :backgroundColor :white}) (def list-container - {:backgroundColor :white}) + {:backgroundColor :white + :marginBottom tab-height}) (def create-icon {:fontSize 20 diff --git a/src/status_im/chats_list/views/chat_list_item.cljs b/src/status_im/chats_list/views/chat_list_item.cljs index 215f392520..0c343f9b59 100644 --- a/src/status_im/chats_list/views/chat_list_item.cljs +++ b/src/status_im/chats_list/views/chat_list_item.cljs @@ -10,7 +10,7 @@ (defn chat-list-item [{:keys [chat-id] :as chat}] [touchable-highlight - {:on-press #(dispatch [:show-chat chat-id :push])} + {:on-press #(dispatch [:navigate-to :chat chat-id])} [view [chat-list-item-inner-view (merge chat ;; TODO stub data {:new-messages-count 3 diff --git a/src/status_im/components/main_tabs.cljs b/src/status_im/components/main_tabs.cljs new file mode 100644 index 0000000000..5b8751b39d --- /dev/null +++ b/src/status_im/components/main_tabs.cljs @@ -0,0 +1,30 @@ +(ns status-im.components.main-tabs + (:require-macros [status-im.utils.views :refer [defview]]) + (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] + [status-im.components.react :refer [view + text-input + text + image + touchable-highlight]] + [status-im.components.tabs.tabs :refer [tabs]] + [status-im.utils.logging :as log])) + +(defview main-tabs [] + [view-id [:get :view-id]] + [tabs {:selected-index (case view-id + :chat-list 0 + :discovery 1 + :contact-list 2 + 0) + :tab-list [{:handler #(dispatch [:navigate-to + :chat-list]) + :title "Chats" + :icon :icon_tab_chats} + {:handler #(dispatch [:navigate-to + :discovery]) + :title "Discover" + :icon :icon_tab_discovery} + {:handler #(dispatch [:navigate-to + :contact-list]) + :title "Contacts" + :icon :icon_tab_contacts}]}]) diff --git a/src/status_im/components/react.cljs b/src/status_im/components/react.cljs index d45088bd05..930f6deedf 100644 --- a/src/status_im/components/react.cljs +++ b/src/status_im/components/react.cljs @@ -35,9 +35,20 @@ (def picker-item (r/adapt-react-class (.-Item (.-Picker js/React)))) -(defn icon [n style] - [image {:source {:uri (keyword (str "icon_" (name n)))} - :style style}]) +(defn icon + ([n] (icon n {})) + ([n style] + [image {:source {:uri (keyword (str "icon_" (name n)))} + :style style}])) + +;(def react-linear-gradient (.-default (js/require "react-native-linear-gradient"))) +;(def linear-gradient (r/adapt-react-class react-linear-gradient)) + +(set! js/window.LinearGradient (js/require "react-native-linear-gradient")) +(defn linear-gradient [props] + (js/React.createElement js/LinearGradient + (clj->js (merge {:inverted true} props)))) + (def platform (.. js/React -Platform -OS)) diff --git a/src/status_im/components/tabs/styles.cljs b/src/status_im/components/tabs/styles.cljs new file mode 100644 index 0000000000..2c25c2417b --- /dev/null +++ b/src/status_im/components/tabs/styles.cljs @@ -0,0 +1,56 @@ +(ns status-im.components.tabs.styles + (:require [status-im.components.styles :refer [font + title-font + color-white + chat-background + online-color + selected-message-color + separator-color + text1-color + text2-color + toolbar-background1]])) + +(def tab-height 56) + +(def tabs + {:flex 1 + :position :absolute + :bottom 0 + :right 0 + :left 0 + }) + +(def top-gradient + {:flexDirection :row + :height 3}) + +(def tabs-container + {:flexDirection :row + :height tab-height + :opacity 1 + :backgroundColor :white + :justifyContent :center + :alignItems :center}) + +(def tab + {:flex 1 + :height tab-height + :justifyContent :center + :alignItems :center}) + +(def tab-title + {:fontFamily "sans-serif" + :fontSize 14 + :color "#6e93d8"}) + +(def tab-icon + {:width 24 + :height 24 + :marginBottom 1}) + +(def tab-container + {:flex 1 + :height tab-height + :justifyContent :center + :alignItems :center}) + diff --git a/src/status_im/components/tabs/tab.cljs b/src/status_im/components/tabs/tab.cljs new file mode 100644 index 0000000000..4205549a5a --- /dev/null +++ b/src/status_im/components/tabs/tab.cljs @@ -0,0 +1,19 @@ +(ns status-im.components.tabs.tab + (:require-macros [status-im.utils.views :refer [defview]]) + (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] + [status-im.components.react :refer [view + text-input + text + image + touchable-highlight]] + [reagent.core :as r] + [status-im.components.tabs.styles :as st])) + +(defview tab [{:keys [handler title icon selected-index index]}] + [touchable-highlight {:style st/tab + :onPress handler} + [view {:style st/tab-container} + [image {:source {:uri icon} + :style st/tab-icon}] + (when (= selected-index index) + [text {:style st/tab-title} title])]]) diff --git a/src/status_im/components/tabs/tabs.cljs b/src/status_im/components/tabs/tabs.cljs new file mode 100644 index 0000000000..23450c80be --- /dev/null +++ b/src/status_im/components/tabs/tabs.cljs @@ -0,0 +1,26 @@ +(ns status-im.components.tabs.tabs + (:require-macros [status-im.utils.views :refer [defview]]) + (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] + [status-im.components.react :refer [view + text-input + text + image + touchable-highlight + linear-gradient]] + [reagent.core :as r] + [status-im.components.tabs.styles :as st] + [status-im.components.tabs.tab :refer [tab]])) + +(defn create-tab [index data selected-index] + (let [data (merge data {:key index + :index index + :selected-index selected-index})] + [tab data])) + +(defview tabs [{:keys [style tab-list selected-index]}] + (let [style (merge st/tabs style)] + [view {:style style} + [linear-gradient {:colors [ "rgba(24, 52, 76, 0.01)" "rgba(24, 52, 76, 0.085)" "rgba(24, 52, 76, 0.165)"] + :style st/top-gradient}] + [view st/tabs-container + (doall (map-indexed #(create-tab %1 %2 selected-index) tab-list))]])) diff --git a/src/status_im/contacts/handlers.cljs b/src/status_im/contacts/handlers.cljs index 7a384d04fd..268b701815 100644 --- a/src/status_im/contacts/handlers.cljs +++ b/src/status_im/contacts/handlers.cljs @@ -73,7 +73,7 @@ (defn request-stored-contacts [contacts] (let [contacts-by-hash (get-contacts-by-hash contacts) - data (keys contacts-by-hash)] + data (or (keys contacts-by-hash) ())] (http-post "get-contacts" {:phone-number-hashes data} (fn [{:keys [contacts]}] (let [contacts' (add-identity contacts-by-hash contacts)] diff --git a/src/status_im/contacts/screen.cljs b/src/status_im/contacts/screen.cljs index 12cee44a65..11d75bdc2a 100644 --- a/src/status_im/contacts/screen.cljs +++ b/src/status_im/contacts/screen.cljs @@ -2,13 +2,14 @@ (:require-macros [status-im.utils.views :refer [defview]]) (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] [status-im.components.react :refer [view text - image - touchable-highlight - list-view - list-item]] + image + touchable-highlight + list-view + list-item]] [status-im.contacts.views.contact :refer [contact-view]] [status-im.components.styles :refer [toolbar-background2]] [status-im.components.toolbar :refer [toolbar]] + [status-im.components.main-tabs :refer [main-tabs]] [status-im.contacts.styles :as st] [status-im.utils.listview :as lw])) @@ -32,4 +33,5 @@ [list-view {:dataSource (lw/to-datasource contacts) :enableEmptySections true :renderRow render-row - :style st/contacts-list}])]) + :style st/contacts-list}]) + [main-tabs]]) diff --git a/src/status_im/contacts/styles.cljs b/src/status_im/contacts/styles.cljs index 4cbec2cdff..7455363d38 100644 --- a/src/status_im/contacts/styles.cljs +++ b/src/status_im/contacts/styles.cljs @@ -3,7 +3,8 @@ title-font text1-color color-white - online-color]])) + online-color]] + [status-im.components.tabs.styles :refer [tab-height]])) (def search-icon {:width 17 @@ -14,7 +15,8 @@ :backgroundColor :white}) (def contacts-list - {:backgroundColor :white}) + {:backgroundColor :white + :marginBottom tab-height}) (def contact-photo-container {:borderRadius 50}) diff --git a/src/status_im/db.cljs b/src/status_im/db.cljs index 3482925e8a..a46c61f087 100644 --- a/src/status_im/db.cljs +++ b/src/status_im/db.cljs @@ -7,28 +7,29 @@ (def default-view :chat-list) ;; initial state of app-db -(def app-db {:identity-password "replace-me-with-user-entered-password" - :identity "me" - :contacts [] - :contacts-ids #{} - :selected-contacts #{} - :current-chat-id "console" - :chat {:command nil - :last-message nil} - :chats {} - :chats-updated-signal 0 - :show-actions false - :new-participants #{} - :signed-up true - :view-id default-view - :navigation-stack (list default-view) +(def app-db {:identity-password "replace-me-with-user-entered-password" + :identity "me" + :contacts [] + :contacts-ids #{} + :selected-contacts #{} + :current-chat-id "console" + :chat {:command nil + :last-message nil} + :chats {} + :chats-updated-signal 0 + :show-actions false + :selected-participants #{} + :signed-up true + :view-id default-view + :navigation-stack (list default-view) ;; TODO fix hardcoded values - :photo-path nil - :username "My Name" - :phone-number "3147984309" - :email "myemail@gmail.com" - :status "Hi, this is my status" - :current-tag nil}) + :photo-path nil + :username "My Name" + :phone-number "3147984309" + :email "myemail@gmail.com" + :status "Hi, this is my status" + :current-tag nil + :disable-group-creation false}) (def protocol-initialized-path [:protocol-initialized]) (defn chat-input-text-path [chat-id] diff --git a/src/status_im/discovery/screen.cljs b/src/status_im/discovery/screen.cljs index d5d62f5925..ab2c80e314 100644 --- a/src/status_im/discovery/screen.cljs +++ b/src/status_im/discovery/screen.cljs @@ -3,10 +3,11 @@ (:require [re-frame.core :refer [dispatch subscribe]] [status-im.components.react :refer [view - scroll-view - text - text-input]] + scroll-view + text + text-input]] [status-im.components.toolbar :refer [toolbar]] + [status-im.components.main-tabs :refer [main-tabs]] [status-im.discovery.views.popular :refer [popular]] [status-im.discovery.views.recent :refer [discovery-recent]] [status-im.discovery.styles :as st])) @@ -22,7 +23,7 @@ :autoFocus true :placeholder "Type your search tags here" :onSubmitEditing (fn [e] - (let [search (aget e "nativeEvent" "text") + (let [search (aget e "nativeEvent" "text") hashtags (get-hashtags search)] (dispatch [:broadcast-status search hashtags])))}] [view @@ -43,10 +44,11 @@ :action {:image {:source {:uri :icon_search} :style st/search-icon} :handler #(toogle-search show-search)}}] - [scroll-view {:style {}} + [scroll-view st/scroll-view-container [view st/section-spacing [text {:style st/discovery-subtitle} "Popular tags"]] [popular] [view st/section-spacing [text {:style st/discovery-subtitle} "Recent"]] - [discovery-recent]]]) + [discovery-recent]] + [main-tabs]]) diff --git a/src/status_im/discovery/styles.cljs b/src/status_im/discovery/styles.cljs index d1d5d650eb..979cd287a0 100644 --- a/src/status_im/discovery/styles.cljs +++ b/src/status_im/discovery/styles.cljs @@ -1,14 +1,15 @@ (ns status-im.discovery.styles (:require [status-im.components.styles :refer [font - title-font - color-white - chat-background - online-color - selected-message-color - separator-color - text1-color - text2-color - toolbar-background1]])) + title-font + color-white + chat-background + online-color + selected-message-color + separator-color + text1-color + text2-color + toolbar-background1]] + [status-im.components.tabs.styles :refer [tab-height]])) ;; common @@ -39,14 +40,14 @@ (def discovery-title {:color "#000000de" - :alignSelf :center + :alignSelf :center :textAlign :center :fontFamily "sans-serif" :fontSize 16}) (def discovery-toolbar {:backgroundColor "#eef2f5" - :elevation 0}) + :elevation 0}) (def discovery-subtitle {:color "#8f838c93" @@ -58,6 +59,9 @@ :paddingTop 15 :paddingBottom 15}) +(def scroll-view-container + {:marginBottom tab-height}) + ;; discovery_popular.cljs (def carousel-page-style @@ -71,86 +75,86 @@ ;; discovery_populat_list.cljs (def tag-name - {:color "#7099e6" - :fontFamily "sans-serif-medium" - :fontSize 14 - :paddingRight 5 - :paddingBottom 2 - :alignItems :center + {:color "#7099e6" + :fontFamily "sans-serif-medium" + :fontSize 14 + :paddingRight 5 + :paddingBottom 2 + :alignItems :center :justifyContent :center}) (def tag-name-container - {:flexDirection "column" + {:flexDirection "column" :backgroundColor "#eef2f5" - :borderRadius 5 - :padding 4}) + :borderRadius 5 + :padding 4}) (def tag-count - {:color "#838c93" - :fontFamily "sans-serif" - :fontSize 12 - :paddingRight 5 - :paddingBottom 2 - :alignItems :center + {:color "#838c93" + :fontFamily "sans-serif" + :fontSize 12 + :paddingRight 5 + :paddingBottom 2 + :alignItems :center :justifyContent :center}) (def tag-count-container - {:flex 0.2 + {:flex 0.2 :flexDirection "column" - :alignItems "flex-end" - :paddingTop 10 - :paddingRight 9}) + :alignItems "flex-end" + :paddingTop 10 + :paddingRight 9}) (def popular-list-container - {:flex 1 + {:flex 1 :backgroundColor :white - :paddingLeft 10 - :paddingTop 16}) + :paddingLeft 10 + :paddingTop 16}) (def popular-list {:backgroundColor :white - :paddingTop 13}) + :paddingTop 13}) ;; discover_popular_list_item.cjls (def popular-list-item {:flexDirection :row - :paddingTop 10 + :paddingTop 10 :paddingBottom 10}) (def popular-list-item-status - {:color "black" + {:color "black" :fontFamily "sans-serif" :lineHeight 22 - :fontSize 14}) + :fontSize 14}) (def popular-list-item-name - {:color "black" + {:color "black" :fontFamily "sans-serif-medium" - :fontSize 14 + :fontSize 14 :lineHeight 24}) (def popular-list-item-name-container - {:flex 0.8 + {:flex 0.8 :flexDirection "column"}) (def popular-list-item-avatar-container - {:flex 0.2 + {:flex 0.2 :flexDirection "column" - :alignItems :center - :paddingTop 5}) + :alignItems :center + :paddingTop 5}) (def popular-list-item-avatar - {:resizeMode "contain" + {:resizeMode "contain" :borderRadius 150 - :width 40 - :height 40}) + :width 40 + :height 40}) ;; discovery_recent (def recent-list {:backgroundColor :white - :paddingLeft 15}) + :paddingLeft 15}) ;; discovery_tag @@ -158,22 +162,27 @@ {:flex 1 :backgroundColor "#eef2f5"}) +(def tag-title-container + {:flex 1 + :alignItems "center" + :justifyContent "center"}) + (def tag-title - {:color "#7099e6" - :fontFamily "sans-serif-medium" - :fontSize 14 - :paddingRight 5 + {:color "#7099e6" + :fontFamily "sans-serif-medium" + :fontSize 14 + :paddingRight 5 :paddingBottom 2}) -(def tag-title-container +(def tag-container {:backgroundColor "#eef2f5" - :flexWrap :wrap - :borderRadius 5 - :padding 4}) + :flexWrap :wrap + :borderRadius 5 + :padding 4}) (def icon-back - {:width 8 - :height 14}) + {:width 8 + :height 14}) (def icon-search {:width 17 diff --git a/src/status_im/discovery/tag.cljs b/src/status_im/discovery/tag.cljs index 1b4f0fbc9b..a872b48000 100644 --- a/src/status_im/discovery/tag.cljs +++ b/src/status_im/discovery/tag.cljs @@ -17,7 +17,8 @@ (defn title-content [tag] [view st/tag-title-container - [text {:style st/tag-title} (str " #" tag)]]) + [view {:style st/tag-container} + [text {:style st/tag-title} (str " #" tag)]]]) (defn discovery-tag [] (let [tag (subscribe [:get :current-tag]) @@ -31,7 +32,7 @@ :style st/icon-back} :handler #(dispatch [:navigate-back])} :title "Add Participants" - :content (title-content @tag) + :custom-content (title-content @tag) :action {:image {:source {:uri :icon_search} :style st/icon-search} :handler (fn [])}}] diff --git a/src/status_im/group_settings/handlers.cljs b/src/status_im/group_settings/handlers.cljs index d62ae27276..82eab18d7b 100644 --- a/src/status_im/group_settings/handlers.cljs +++ b/src/status_im/group_settings/handlers.cljs @@ -1,72 +1,144 @@ (ns status-im.group-settings.handlers - (:require [re-frame.core :refer [register-handler debug dispatch]] + (:require [re-frame.core :refer [register-handler debug dispatch after + enrich]] [status-im.persistence.realm :as r] - [status-im.models.messages :refer [clear-history]])) + [status-im.chat.handlers :refer [delete-messages!]] + [status-im.protocol.api :as api] + [status-im.utils.random :as random] + [status-im.models.contacts :as contacts] + [status-im.models.messages :as messages] + [status-im.models.chats :as chats] + [status-im.constants :refer [text-content-type]] + [status-im.utils.handlers :as u] + [status-im.navigation.handlers :as nav])) -(defn set-chat-name [db] - (let [chat-id (:current-chat-id db) - name (:new-chat-name db)] - (r/write (fn [] - (-> (r/get-by-field :chats :chat-id chat-id) - (r/single) - (aset "name" name)))) - (assoc-in db [:chats chat-id :name] name))) +(defmethod nav/preload-data! :group-settings + [db _] + (assoc db :selected-participants #{})) -(defn set-chat-color [db] - (let [chat-id (:current-chat-id db) - color (:new-chat-color db)] - (r/write (fn [] - (-> (r/get-by-field :chats :chat-id chat-id) - (r/single) - (aset "color" color)))) - (assoc-in db [:chats chat-id :color] color))) +(defn save-chat-property! + [db-name property-name] + (fn [{:keys [current-chat-id] :as db} _] + (let [property (db-name db)] + (r/write (fn [] + (-> (r/get-by-field :chats :chat-id current-chat-id) + (r/single) + (aset (name property-name) property))))))) -(defn delete-chat [chat-id] - (r/write - (fn [] - (-> (r/get-by-field :chats :chat-id chat-id) - (r/single) - (r/delete)))) - ;; TODO temp. Update chat in db atom - (dispatch [:initialize-chats])) +(defn update-chat-property + [db-name property-name] + (fn [{:keys [current-chat-id] :as db} _] + (let [property (db-name db)] + (assoc-in db [:chats current-chat-id property-name] property)))) + +(defn prepare-chat-settings + [{:keys [current-chat-id] :as db} _] + (let [{:keys [name color]} (-> db + (get-in [:chats current-chat-id]) + (select-keys [:name :color]))] + (-> db + (assoc :new-chat-name name + :new-chat-color color + :group-settings {})))) (register-handler :show-group-settings - (fn [db _] - (let [chat-id (:current-chat-id db) - chat-name (get-in db [:chats chat-id :name]) - chat-color (get-in db [:chats chat-id :color]) - db (assoc db - :new-chat-name chat-name - :new-chat-color chat-color - :group-settings-show-color-picker false - :group-settings-selected-member nil)] - (dispatch [:navigate-to :group-settings]) - db))) + (after (fn [_ _] (dispatch [:navigate-to :group-settings]))) + prepare-chat-settings) (register-handler :set-chat-name - (fn [db [action]] - (set-chat-name db))) + (after (save-chat-property! :new-chat-name :name)) + (update-chat-property :new-chat-name :name)) (register-handler :set-chat-color - (fn [db [action]] - (set-chat-color db))) + (after (save-chat-property! :new-chat-color :color)) + (update-chat-property :new-chat-color :color)) -(register-handler :set-new-chat-name - (fn [db [action name]] - (assoc db :new-chat-name name))) - -(register-handler :set-new-chat-color - (fn [db [action color]] - (assoc db :new-chat-color color))) - -(register-handler :select-group-chat-member - (fn [db [action identity]] - (assoc db :group-settings-selected-member identity))) - -(register-handler :set-group-settings-show-color-picker - (fn [db [action show?]] - (assoc db :group-settings-show-color-picker show?))) +(defn clear-messages + [{:keys [current-chat-id] :as db} _] + (assoc-in db [:chats current-chat-id :messages] '())) (register-handler :clear-history - (fn [db [action]] - (clear-history (:current-chat-id db)))) + (after delete-messages!) + clear-messages) + +(register-handler :group-settings + (fn [db [_ k v]] + (assoc-in db [:group-settings k] v))) + +(defn remove-identities [collection identities] + (remove #(identities (:identity %)) collection)) + +(defn remove-members + [{:keys [current-chat-id selected-participants] :as db} _] + (update-in db [:chats current-chat-id :contacts] + remove-identities selected-participants)) + +(defn remove-members-from-realm! + [{:keys [current-chat-id selected-participants] :as db} _] + (let [chat (get-in db [:chats current-chat-id])] + (r/write + (fn [] + (r/create + :chats + (update chat :contacts remove-identities selected-participants) + true))))) + +(defn notify-about-removing! + [{:keys [current-chat-id selected-participants]} _] + (doseq [participant selected-participants] + (api/group-remove-participant current-chat-id participant))) + +(defn system-message [msg-id content] + {:from "system" + :msg-id msg-id + :content content + :content-type text-content-type}) + +(defn removed-participant-msg [chat-id identity] + (let [contact-name (:name (contacts/contact-by-identity identity))] + (->> (str "You've removed " (or contact-name identity)) + (system-message (random/id)) + (messages/save-message chat-id)))) + +(defn create-removing-messages! + [{:keys [current-chat-id selected-participants]} _] + (doseq [participant selected-participants] + (removed-participant-msg current-chat-id participant))) + +(defn deselect-members [db _] + (assoc db :selected-participants #{})) + +(register-handler :remove-participants + ;; todo check if user have rights to add/remove participants + ;; todo order of operations tbd + (-> remove-members + ;; todo shouldn't this be done only after receiving of the "ack message" + ;; about the api call that removes participants from the group? + ((after remove-members-from-realm!)) + ;; todo uncomment + ;((after notify-about-removing!)) + ((after create-removing-messages!)) + ((enrich deselect-members)) + debug)) + +(defn add-memebers + [{:keys [current-chat-id selected-participants] :as db} _] + (let [new-identities (map #(hash-map :identity %) selected-participants)] + (update db [:chats current-chat-id :contacts] concat new-identities))) + +(defn add-members-to-realm! + [{:keys [current-chat-id selected-participants]} _] + (chats/chat-add-participants current-chat-id selected-participants)) + +(defn notify-about-new-members! + [{:keys [current-chat-id selected-participants]} _] + (doseq [identity selected-participants] + (api/group-add-participant current-chat-id identity))) + +(register-handler :add-new-participants + ;; todo order of operations tbd + (-> add-memebers + ((after add-members-to-realm!)) + ;; todo uncomment + ;((after notify-about-new-members!)) + ((enrich deselect-members)))) diff --git a/src/status_im/group_settings/screen.cljs b/src/status_im/group_settings/screen.cljs index 6fb75a1151..1c49c4f094 100644 --- a/src/status_im/group_settings/screen.cljs +++ b/src/status_im/group_settings/screen.cljs @@ -16,26 +16,26 @@ [status-im.group-settings.styles.group-settings :as st] [status-im.group-settings.views.member :refer [member-view]])) -(defn remove-member [{:keys [whisper-identity]}] - (dispatch [:chat-remove-member whisper-identity])) +(defn remove-member [] + (dispatch [:remove-participants])) (defn close-member-menu [] - (dispatch [:select-group-chat-member nil])) + (dispatch [:set :selected-participants #{}])) ;; TODO not in design (defview member-menu [] - [member [:group-settings-selected-member]] - [modal {:animated false - :transparent false - :onRequestClose close-member-menu} - [touchable-highlight {:style st/modal-container - :on-press close-member-menu} - [view st/modal-inner-container - [text {:style st/modal-member-name} - (:name member)] - [touchable-highlight {:on-press #(remove-member member)} - [text {:style st/modal-remove-text} - "Remove"]]]]]) + [{:keys [name] :as participant} [:selected-participant]] + (when participant + [modal {:animated false + :transparent false + :onRequestClose close-member-menu} + [touchable-highlight {:style st/modal-container + :on-press close-member-menu} + [view st/modal-inner-container + [text {:style st/modal-member-name} name] + [touchable-highlight {:on-press remove-member} + [text {:style st/modal-remove-text} + "Remove"]]]]])) (defview chat-members [] [members [:current-chat-contacts]] @@ -43,9 +43,6 @@ (for [member members] ^{:key member} [member-view member])]) -(defn show-chat-name-edit [] - (dispatch [:navigate-to :chat-name-edit])) - (defn setting-view [{:keys [icon-style custom-icon handler title subtitle] icon-name :icon}] [touchable-highlight {:on-press handler} @@ -60,7 +57,7 @@ subtitle])]]]) (defn close-chat-color-picker [] - (dispatch [:set-group-settings-show-color-picker false])) + (dispatch [:group-settings :show-color-picker false])) (defn set-chat-color [] (close-chat-color-picker) @@ -68,8 +65,8 @@ ;; TODO not in design (defview chat-color-picker [] - [show-color-picker [:get :group-settings-show-color-picker] - new-color [:get :new-chat-color]] + [show-color-picker [:group-settings :show-color-picker] + new-color [:get :new-chat-color]] [modal {:animated false :transparent false :onRequestClose close-chat-color-picker} @@ -77,11 +74,11 @@ :on-press close-chat-color-picker} [view st/modal-color-picker-inner-container [picker {:selectedValue new-color - :onValueChange #(dispatch [:set-new-chat-color %])} - [picker-item {:label "Blue" :value "#7099e6"}] - [picker-item {:label "Purple" :value "#a187d5"}] - [picker-item {:label "Green" :value "green"}] - [picker-item {:label "Red" :value "red"}]] + :onValueChange #(dispatch [:set :new-chat-color %])} + [picker-item {:label "Blue" :value "#7099e6"}] + [picker-item {:label "Purple" :value "#a187d5"}] + [picker-item {:label "Green" :value "green"}] + [picker-item {:label "Red" :value "red"}]] [touchable-highlight {:on-press set-chat-color} [text {:style st/modal-color-picker-save-btn-text} "Save"]]]]]) @@ -91,7 +88,7 @@ [view {:style (st/chat-color-icon chat-color)}]) (defn show-chat-color-picker [] - (dispatch [:set-group-settings-show-color-picker true])) + (dispatch [:group-settings :show-color-picker true])) (defn settings-view [] (let [settings [{:custom-icon [chat-color-icon] @@ -108,18 +105,18 @@ {:icon :muted :icon-style {:width 18 :height 21}})) - {:icon :close-gray - :icon-style {:width 12 - :height 12} - :title "Clear history" + {:icon :close-gray + :icon-style {:width 12 + :height 12} + :title "Clear history" ;; TODO show confirmation dialog? - :handler #(dispatch [:clear-history])} - {:icon :bin - :icon-style {:width 12 - :height 18} - :title "Delete and leave" + :handler #(dispatch [:clear-history])} + {:icon :bin + :icon-style {:width 12 + :height 18} + :title "Delete and leave" ;; TODO show confirmation dialog? - :handler #(dispatch [:leave-group-chat])}]] + :handler #(dispatch [:leave-group-chat])}]] [view st/settings-container (for [setting settings] ^{:key setting} [setting-view setting])])) @@ -136,26 +133,45 @@ [toolbar {:title "Chat settings" :custom-action [chat-icon]}]) +(defn focus [] + (dispatch [:set ::name-input-focused true])) + +(defn blur [] + (dispatch [:set ::name-input-focused false])) + +(defn save [] + (dispatch [:set-chat-name])) + +(defview chat-name [] + [name [:chat :name] + new-name [:get :new-chat-name] + focused? [:get ::name-input-focused]] + [view + [text {:style st/chat-name-text} "Chat name"] + [view (st/chat-name-value-container focused?) + [text-input {:style st/chat-name-value + :ref #(when (and % focused?) (.focus %)) + :on-change-text #(dispatch [:set :new-chat-name %]) + :on-focus focus + :on-blur blur} + name] + (if (or focused? (not= name new-name)) + [touchable-highlight {:style st/chat-name-btn-edit-container + :on-press save} + [view [icon :ok-purple st/add-members-icon]]] + [touchable-highlight {:style st/chat-name-btn-edit-container + :on-press focus} + [text {:style st/chat-name-btn-edit-text} "Edit"]])]]) + (defview group-settings [] - [chat-name [:chat :name] - selected-member [:group-settings-selected-member] - show-color-picker [:get :group-settings-show-color-picker]] + [show-color-picker [:group-settings :show-color-picker]] [view st/group-settings [new-group-toolbar] [scroll-view st/body - [text {:style st/chat-name-text} - "Chat name"] - [view st/chat-name-value-container - [text {:style st/chat-name-value} - chat-name] - [touchable-highlight {:style st/chat-name-btn-edit-container - :on-press show-chat-name-edit} - [text {:style st/chat-name-btn-edit-text} - "Edit"]]] - [text {:style st/members-text} - "Members"] + [chat-name] + [text {:style st/members-text} "Members"] + [touchable-highlight {:on-press #(dispatch [:navigate-to :add-participants])} ;; TODO add participants view is not in design - [touchable-highlight {:on-press #(dispatch [:show-add-participants])} [view st/add-members-container [icon :add-gray st/add-members-icon] [text {:style st/add-members-text} @@ -166,5 +182,4 @@ [settings-view]] (when show-color-picker [chat-color-picker]) - (when selected-member - [member-menu])]) + [member-menu]]) diff --git a/src/status_im/group_settings/styles/chat_name_edit.cljs b/src/status_im/group_settings/styles/chat_name_edit.cljs deleted file mode 100644 index d185480652..0000000000 --- a/src/status_im/group_settings/styles/chat_name_edit.cljs +++ /dev/null @@ -1,19 +0,0 @@ -(ns status-im.group-settings.styles.chat-name-edit - (:require [status-im.components.styles :refer [font - color-white - text1-color]])) - -(def save-action-icon - {:width 18 - :height 14}) - -(def chat-name-container - {:flex 1 - :flexDirection :column - :backgroundColor color-white}) - -(def chat-name-input - {:marginLeft 12 - :fontSize 14 - :fontFamily font - :color text1-color}) diff --git a/src/status_im/group_settings/styles/group_settings.cljs b/src/status_im/group_settings/styles/group_settings.cljs index 2deb557a63..91d407c604 100644 --- a/src/status_im/group_settings/styles/group_settings.cljs +++ b/src/status_im/group_settings/styles/group_settings.cljs @@ -24,17 +24,17 @@ :backgroundColor color-white}) (def modal-member-name - {:color text2-color - :fontFamily font - :fontSize 14 - :lineHeight 20}) + {:color text2-color + :fontFamily font + :fontSize 14 + :lineHeight 20}) (def modal-remove-text - {:margin 10 - :color text1-color - :fontFamily font - :fontSize 14 - :lineHeight 20}) + {:margin 10 + :color text1-color + :fontFamily font + :fontSize 14 + :lineHeight 20}) (def modal-color-picker-inner-container {:borderRadius 10 @@ -42,12 +42,12 @@ :backgroundColor color-white}) (def modal-color-picker-save-btn-text - {:margin 10 - :alignSelf :center - :color text1-color - :fontFamily font - :fontSize 14 - :lineHeight 20}) + {:margin 10 + :alignSelf :center + :color text1-color + :fontFamily font + :fontSize 14 + :lineHeight 20}) (def chat-members-container {:marginBottom 10}) @@ -76,14 +76,14 @@ :fontSize 14 :lineHeight 20}) -(def chat-name-value-container +(defn chat-name-value-container [focused?] {:flexDirection :row :marginLeft 16 :height 56 :alignItems :center :justifyContent :center - :borderBottomWidth 1 - :borderBottomColor separator-color}) + :borderBottomWidth 2 + :borderBottomColor (if focused? color-purple separator-color)}) (def chat-name-value {:flex 1 @@ -96,11 +96,11 @@ :justifyContent :center}) (def chat-name-btn-edit-text - {:marginTop -1 - :color text2-color - :fontFamily font - :fontSize 16 - :lineHeight 20}) + {:marginTop -1 + :color text2-color + :fontFamily font + :fontSize 16 + :lineHeight 20}) (def members-text {:marginTop 24 diff --git a/src/status_im/group_settings/subs.cljs b/src/status_im/group_settings/subs.cljs index 55085b2256..f2b6ac1db3 100644 --- a/src/status_im/group_settings/subs.cljs +++ b/src/status_im/group_settings/subs.cljs @@ -1,14 +1,13 @@ (ns status-im.group-settings.subs (:require-macros [reagent.ratom :refer [reaction]]) - (:require [re-frame.core :refer [register-sub]] - [status-im.models.contacts :refer [contact-by-identity]])) + (:require [re-frame.core :refer [register-sub]])) -(register-sub :group-settings-selected-member - (fn [db [_]] +(register-sub :selected-participant + (fn [db _] (reaction - (let [identity (get @db :group-settings-selected-member)] - (contact-by-identity identity))))) + (let [identity (first (:selected-participants @db))] + (get-in @db [:contacts identity]))))) -(register-sub :group-settings-show-color-picker - (fn [db [_]] - (reaction (get @db :group-settings-show-color-picker)))) +(register-sub :group-settings + (fn [db [_ k]] + (reaction (get-in @db [:group-settings k])))) diff --git a/src/status_im/group_settings/views/chat_name_edit.cljs b/src/status_im/group_settings/views/chat_name_edit.cljs deleted file mode 100644 index 523b94653b..0000000000 --- a/src/status_im/group_settings/views/chat_name_edit.cljs +++ /dev/null @@ -1,31 +0,0 @@ -(ns status-im.group-settings.views.chat-name-edit - (:require-macros [status-im.utils.views :refer [defview]]) - (:require [reagent.core :as r] - [re-frame.core :refer [subscribe dispatch dispatch-sync]] - [status-im.components.react :refer [view text-input]] - [status-im.components.toolbar :refer [toolbar]] - [status-im.group-settings.styles.chat-name-edit :as st] - [status-im.components.styles :refer [toolbar-background2 - text2-color]])) - -(defn save-group-chat-name [] - (dispatch [:set-chat-name]) - (dispatch [:navigate-back])) - -(defn chat-name-edit-toolbar [chat-name] - [toolbar {:background-color toolbar-background2 - :title "Edit chat name" - ;; TODO change to dark 'ok' icon - :action {:image {:source {:uri :icon_ok} - :style st/save-action-icon} - :handler save-group-chat-name}}]) - -(defview chat-name-edit [] - [new-chat-name [:get :new-chat-name]] - [view st/chat-name-container - [chat-name-edit-toolbar] - [text-input {:style st/chat-name-input - :autoFocus true - :placeholderTextColor text2-color - :onChangeText #(dispatch [:set-new-chat-name %])} - new-chat-name]]) diff --git a/src/status_im/group_settings/views/member.cljs b/src/status_im/group_settings/views/member.cljs index 73889c611f..240d6a09c8 100644 --- a/src/status_im/group_settings/views/member.cljs +++ b/src/status_im/group_settings/views/member.cljs @@ -38,6 +38,6 @@ [text {:style st/role-text} role])] [touchable-highlight - {:on-press #(dispatch [:select-group-chat-member whisper-identity])} + {:on-press #(dispatch [:set :selected-participants #{whisper-identity}])} [view st/more-btn [icon :more-vertical st/more-btn-icon]]]]) diff --git a/src/status_im/handlers.cljs b/src/status_im/handlers.cljs index e7d2687eed..4010049665 100644 --- a/src/status_im/handlers.cljs +++ b/src/status_im/handlers.cljs @@ -1,46 +1,23 @@ (ns status-im.handlers (:require - [re-frame.core :refer [register-handler after dispatch debug enrich]] + [re-frame.core :refer [register-handler after dispatch debug]] [schema.core :as s :include-macros true] - [status-im.persistence.realm :as r] [status-im.db :refer [app-db schema]] [status-im.persistence.simple-kv-store :as kv] [status-im.protocol.state.storage :as storage] - [status-im.db :as db :refer [app-db schema]] - [status-im.protocol.api :refer [init-protocol]] - [status-im.protocol.protocol-handler :refer [make-handler]] - [status-im.models.protocol :refer [update-identity - set-initialized]] - [status-im.models.contacts :as contacts] - [status-im.models.messages :refer [save-message - update-message! - clear-history]] [status-im.models.commands :refer [set-commands]] - [status-im.handlers.server :as server] [status-im.chat.suggestions :refer [load-commands]] - [status-im.models.chats :refer [chat-exists? - create-chat - chat-add-participants - chat-remove-participants - set-chat-active - re-join-group-chat - chat-by-id2]] [status-im.utils.logging :as log] - [status-im.protocol.api :as api] - [status-im.constants :refer [text-content-type - content-type-command]] - [status-im.navigation :refer [nav-push - nav-replace - nav-pop]] [status-im.utils.crypt :refer [gen-random-bytes]] - [status-im.utils.random :as random] [status-im.utils.handlers :as u] status-im.chat.handlers - [status-im.group-settings.handlers :refer [delete-chat]] + status-im.group-settings.handlers status-im.navigation.handlers - status-im.discovery.handlers status-im.contacts.handlers - status-im.new-group.handlers)) + status-im.discovery.handlers + status-im.new-group.handlers + status-im.participants.handlers + status-im.protocol.handlers)) ;; -- Middleware ------------------------------------------------------------ ;; @@ -63,6 +40,11 @@ (fn [db [_ k v]] (assoc db k v)))) +(register-handler :set-in + (debug + (fn [db [_ path v]] + (assoc-in db path v)))) + (register-handler :initialize-db (fn [_ _] (assoc app-db @@ -100,192 +82,8 @@ (log/debug action commands) (set-commands db commands))) -;; -- Protocol -------------------------------------------------------------- - -(register-handler :initialize-protocol - (u/side-effect! - (fn [db [_]] - (init-protocol (make-handler db))))) - -(register-handler :protocol-initialized - (fn [db [_ identity]] - (-> db - (update-identity identity) - (set-initialized true)))) - -(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] - (let [contact-name (:name (contacts/contact-by-identity from))] - (save-message chat-id {:from "system" - :msg-id (str msg-id "_" from) - :content (str (or contact-name from) " received chat invitation") - :content-type text-content-type}))) - -(defn participant-invited-to-group-msg [chat-id identity from msg-id] - (let [inviter-name (:name (contacts/contact-by-identity from)) - invitee-name (if (= identity (api/my-identity)) - "You" - (:name (contacts/contact-by-identity identity)))] - (save-message chat-id {:from "system" - :msg-id msg-id - :content (str (or inviter-name from) " invited " (or invitee-name identity)) - :content-type text-content-type}))) - -(defn participant-removed-from-group-msg [chat-id identity from msg-id] - (let [remover-name (:name (contacts/contact-by-identity from)) - removed-name (:name (contacts/contact-by-identity identity))] - (->> (str (or remover-name from) " removed " (or removed-name identity)) - (system-message msg-id) - (save-message chat-id)))) - -(defn you-removed-from-group-msg [chat-id from msg-id] - (let [remover-name (:name (contacts/contact-by-identity from))] - (->> (str (or remover-name from) " removed you from group chat") - (system-message msg-id) - (save-message chat-id)))) - -(defn participant-left-group-msg [chat-id from msg-id] - (let [left-name (:name (contacts/contact-by-identity from))] - (->> (str (or left-name from) " left") - (system-message msg-id) - (save-message chat-id)))) - -(defn removed-participant-msg [chat-id identity] - (let [contact-name (:name (contacts/contact-by-identity identity))] - (->> (str "You've removed " (or contact-name identity)) - (system-message (random/id)) - (save-message chat-id)))) - -(defn left-chat-msg [chat-id] - (save-message chat-id {:from "system" - :msg-id (random/id) - :content "You left this chat" - :content-type text-content-type})) - -(register-handler :group-chat-invite-acked - (u/side-effect! - (fn [_ [action from group-id ack-msg-id]] - (log/debug action from group-id ack-msg-id) - (joined-chat-msg group-id from ack-msg-id)))) - -(register-handler :participant-removed-from-group - (u/side-effect! - (fn [_ [action from group-id identity msg-id]] - (log/debug action msg-id from group-id identity) - (chat-remove-participants group-id [identity]) - (participant-removed-from-group-msg group-id identity from msg-id)))) - -(register-handler :you-removed-from-group - (u/side-effect! - (fn [_ [action from group-id msg-id]] - (log/debug action msg-id from group-id) - (you-removed-from-group-msg group-id from msg-id) - (set-chat-active group-id false)))) - -(register-handler :participant-left-group - (u/side-effect! - (fn [_ [action from group-id msg-id]] - (log/debug action msg-id from group-id) - (when-not (= (api/my-identity) from) - (participant-left-group-msg group-id from msg-id))))) - -(register-handler :participant-invited-to-group - (u/side-effect! - (fn [_ [action from group-id identity msg-id]] - (log/debug action msg-id from group-id identity) - (participant-invited-to-group-msg group-id identity from msg-id)))) - -(register-handler :acked-msg - (u/side-effect! - (fn [_ [action from msg-id]] - (log/debug action from msg-id) - (update-message! {:msg-id msg-id - :delivery-status :delivered})))) - -(register-handler :msg-delivery-failed - (u/side-effect! - (fn [_ [action msg-id]] - (log/debug action msg-id) - (update-message! {:msg-id msg-id - :delivery-status :failed})))) - -(register-handler :leave-group-chat - (u/side-effect! - (fn [db [action]] - (log/debug action) - (let [chat-id (:current-chat-id db)] - (api/leave-group-chat chat-id) - (set-chat-active chat-id false) - (left-chat-msg chat-id) - (delete-chat chat-id) - (dispatch [:navigate-back]))))) - ;; -- User data -------------------------------------------------------------- (register-handler :load-user-phone-number (fn [db [_]] ;; todo fetch phone number from db (assoc db :user-phone-number "123"))) - -;; -- Chats -------------------------------------------------------------- -(defn update-new-participants-selection [db identity add?] - (update db :new-participants (fn [new-participants] - (if add? - (conj new-participants identity) - (disj new-participants identity))))) - -(register-handler :select-new-participant - (fn [db [action identity add?]] - (log/debug action identity add?) - (update-new-participants-selection db identity add?))) - -(register-handler :remove-selected-participants - (fn [db [action]] - (log/debug action) - (let [identities (vec (:new-participants db)) - chat-id (:current-chat-id db)] - (chat-remove-participants chat-id identities) - (dispatch [:navigate-back]) - (doseq [ident identities] - (api/group-remove-participant chat-id ident) - (removed-participant-msg chat-id ident))))) - -(register-handler :add-new-participants - (fn [db [action navigator]] - (log/debug action) - (let [identities (vec (:new-participants db)) - chat-id (:current-chat-id db)] - (chat-add-participants chat-id identities) - (dispatch [:navigate-back]) - (doseq [ident identities] - (api/group-add-participant chat-id ident)) - db))) - -(defn chat-remove-member [db] - (let [chat (get-in db [:chats (:current-chat-id db)]) - identity (:group-settings-selected-member db)] - (r/write - (fn [] - (r/create :chats - (update chat :contacts - (fn [members] - (filter #(not= (:identity %) identity) members))) - true))) - ;; TODO temp. Update chat in db atom - (dispatch [:initialize-chats]) - db)) - -(register-handler :chat-remove-member - (fn [db [action]] - (let [chat-id (:current-chat-id db) - identity (:group-settings-selected-member db) - db (chat-remove-member db)] - (dispatch [:select-group-chat-member nil]) - ;; TODO fix and uncomment - (api/group-remove-participant chat-id identity) - (removed-participant-msg chat-id identity) - db))) diff --git a/src/status_im/models/messages.cljs b/src/status_im/models/messages.cljs index 80e4656e8b..5c8c17fa5f 100644 --- a/src/status_im/models/messages.cljs +++ b/src/status_im/models/messages.cljs @@ -62,10 +62,3 @@ (fn [] (when (r/exists? :msgs :msg-id msg-id) (r/create :msgs msg true))))) - -(defn clear-history [chat-id] - (r/write - (fn [] - (r/delete (r/get-by-field :msgs :chat-id chat-id)))) - ;; TODO temp. Update chat in db atom - (dispatch [:initialize-chats])) diff --git a/src/status_im/navigation.cljs b/src/status_im/navigation.cljs deleted file mode 100644 index f80b7d3df7..0000000000 --- a/src/status_im/navigation.cljs +++ /dev/null @@ -1,17 +0,0 @@ -(ns status-im.navigation) - -(def ^{:dynamic true :private true} *nav-render* - "Flag to suppress navigator re-renders from outside om when pushing/popping." - true) - -(defn nav-pop [nav] - (binding [*nav-render* true] - (.pop nav))) - -(defn nav-push [nav route] - (binding [*nav-render* true] - (.push nav (clj->js route)))) - -(defn nav-replace [nav route] - (binding [*nav-render* true] - (.replace nav (clj->js route)))) diff --git a/src/status_im/navigation/handlers.cljs b/src/status_im/navigation/handlers.cljs index 28f331a78a..c47134e429 100644 --- a/src/status_im/navigation/handlers.cljs +++ b/src/status_im/navigation/handlers.cljs @@ -18,7 +18,9 @@ (update :navigation-stack replace-top-element view-id) (assoc :view-id view-id))) -(defmulti preload-data! (fn [_ [_ view-id]] view-id)) +(defmulti preload-data! + (fn [db [_ view-id]] (or view-id (:view-id db)))) + (defmethod preload-data! :default [db _] db) (register-handler :navigate-to @@ -27,10 +29,12 @@ (push-view db view-id))) (register-handler :navigation-replace + (enrich preload-data!) (fn [db [_ view-id]] (replace-view db view-id))) (register-handler :navigate-back + (enrich preload-data!) (fn [{:keys [navigation-stack] :as db} _] (if (>= 1 (count navigation-stack)) db @@ -46,32 +50,10 @@ (push-view :new-group) (assoc :new-group #{}))))) -(register-handler :show-chat - (fn [db [_ chat-id nav-type]] - (let [update-view-id-fn (if (= :replace nav-type) replace-view push-view)] - (-> db - (update-view-id-fn :chat) - (assoc :current-chat-id chat-id))))) - (register-handler :show-contacts (fn [db _] (push-view db :contact-list))) -(defn clear-new-participants [db] - (assoc db :new-participants #{})) - -(register-handler :show-remove-participants - (fn [db _] - (-> db - (push-view :remove-participants) - clear-new-participants))) - -(register-handler :show-add-participants - (fn [db _] - (-> db - (push-view :add-participants) - clear-new-participants))) - (defn show-profile [db [_ identity]] (-> db diff --git a/src/status_im/new_group/handlers.cljs b/src/status_im/new_group/handlers.cljs index 4d934d1e23..0c34111405 100644 --- a/src/status_im/new_group/handlers.cljs +++ b/src/status_im/new_group/handlers.cljs @@ -58,14 +58,31 @@ (defn show-chat! [{:keys [new-group-id]} _] - (dispatch [:show-chat new-group-id :replace])) + (dispatch [:navigation-replace :chat new-group-id])) + +(defn enable-creat-buttion + [db _] + (assoc db :disable-group-creation false)) (register-handler :create-new-group (-> start-group-chat! ((enrich prepare-chat)) ((enrich add-chat)) ((after create-chat!)) - ((after show-chat!)))) + ((after show-chat!)) + ((enrich enable-creat-buttion)))) + +(defn disable-creat-button + [db _] + (assoc db :disable-group-creation true)) + +(defn dispatch-create-group + [_ [_ group-name]] + (dispatch [:create-new-group group-name])) + +(register-handler :init-group-creation + (after dispatch-create-group) + disable-creat-button) ; todo rewrite (register-handler :group-chat-invite-received diff --git a/src/status_im/new_group/screen.cljs b/src/status_im/new_group/screen.cljs index c58832e1dc..a620959d50 100644 --- a/src/status_im/new_group/screen.cljs +++ b/src/status_im/new_group/screen.cljs @@ -18,12 +18,14 @@ (defview new-group-toolbar [] - [group-name [:get ::group-name]] + [group-name [:get ::group-name] + creation-disabled? [:get :disable-group-creation]] [toolbar {:title "New group chat" :action {:image {:source res/v ;; {:uri "icon_search"} :style st/toolbar-icon} - :handler #(dispatch [:create-new-group group-name])}}]) + :handler (when-not creation-disabled? + #(dispatch [:init-group-creation group-name]))}}]) (defview group-name-input [] [group-name [:get ::group-name]] diff --git a/src/status_im/new_group/subs.cljs b/src/status_im/new_group/subs.cljs index 57d01e1aae..6753a5eacb 100644 --- a/src/status_im/new_group/subs.cljs +++ b/src/status_im/new_group/subs.cljs @@ -1,9 +1,7 @@ (ns status-im.new-group.subs (:require-macros [reagent.ratom :refer [reaction]]) - (:require [re-frame.core :refer [register-sub]])) + (:require [re-frame.core :refer [register-sub]] + [status-im.utils.subs :as u])) (register-sub :is-contact-selected? - (fn [db [_ id]] - (-> (:selected-contacts @db) - (contains? id) - (reaction)))) + (u/contains-sub :selected-contacts)) diff --git a/src/status_im/new_group/views/contact.cljs b/src/status_im/new_group/views/contact.cljs index a87b7d5fe2..cfec8a409d 100644 --- a/src/status_im/new_group/views/contact.cljs +++ b/src/status_im/new_group/views/contact.cljs @@ -8,7 +8,6 @@ (defn on-toggle [whisper-identity] (fn [checked?] - (println checked?) (let [action (if checked? :select-contact :deselect-contact)] (dispatch [action whisper-identity])))) diff --git a/src/status_im/participants/handlers.cljs b/src/status_im/participants/handlers.cljs new file mode 100644 index 0000000000..fe0e5581c0 --- /dev/null +++ b/src/status_im/participants/handlers.cljs @@ -0,0 +1,23 @@ +(ns status-im.participants.handlers + (:require [status-im.navigation.handlers :as nav] + [re-frame.core :refer [register-handler debug]])) + +(defmethod nav/preload-data! :add-participants + [db _] + (assoc db :selected-participants #{})) + +(defmethod nav/preload-data! :remove-participants + [db _] + (assoc db :selected-participants #{})) + +(defn deselect-participant + [db [_ id]] + (update db :selected-participants disj id)) + +(register-handler :deselect-participant deselect-participant) + +(defn select-participant + [db [_ id]] + (update db :selected-participants conj id)) + +(register-handler :select-participant (debug select-participant)) diff --git a/src/status_im/participants/subs.cljs b/src/status_im/participants/subs.cljs new file mode 100644 index 0000000000..d351667a8a --- /dev/null +++ b/src/status_im/participants/subs.cljs @@ -0,0 +1,7 @@ +(ns status-im.participants.subs + (:require-macros [reagent.ratom :refer [reaction]]) + (:require [re-frame.core :refer [register-sub]] + [status-im.utils.subs :as u])) + +(register-sub :is-participant-selected? + (u/contains-sub :selected-participants)) diff --git a/src/status_im/participants/views/add.cljs b/src/status_im/participants/views/add.cljs new file mode 100644 index 0000000000..5bab93ea1c --- /dev/null +++ b/src/status_im/participants/views/add.cljs @@ -0,0 +1,30 @@ +(ns status-im.participants.views.add + (:require-macros [status-im.utils.views :refer [defview]]) + (:require [re-frame.core :refer [subscribe dispatch]] + [status-im.resources :as res] + [status-im.components.react :refer [view list-view list-item]] + [status-im.components.toolbar :refer [toolbar]] + [status-im.utils.listview :refer [to-datasource]] + [status-im.participants.views.contact :refer [participant-contact]] + [reagent.core :as r] + [status-im.participants.styles :as st])) + +(defn new-participants-toolbar [] + [toolbar + {:title "Add Participants" + :action {:image {:source res/v ;; {:uri "icon_search"} + :style st/new-participant-image} + :handler #(do (dispatch [:add-new-participants]) + (dispatch [:navigate-back]))}}]) + +(defn new-participants-row + [row _ _] + (list-item [participant-contact row])) + +(defview new-participants [] + [contacts [:all-new-contacts]] + [view st/participants-container + [new-participants-toolbar] + [list-view {:dataSource (to-datasource contacts) + :renderRow new-participants-row + :style st/participants-list}]]) diff --git a/src/status_im/participants/views/contact.cljs b/src/status_im/participants/views/contact.cljs index de6ccd9287..0311510e9b 100644 --- a/src/status_im/participants/views/contact.cljs +++ b/src/status_im/participants/views/contact.cljs @@ -1,4 +1,5 @@ (ns status-im.participants.views.contact + (:require-macros [status-im.utils.views :refer [defview]]) (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] [status-im.components.react :refer [view]] [status-im.contacts.views.contact-inner :refer [contact-inner-view]] @@ -6,14 +7,17 @@ [reagent.core :as r] [status-im.participants.styles :as st])) -(defn participant-contact [{:keys [whisper-identity] :as contact}] - ;; todo must be moved to handlers - (let [checked (r/atom false)] - (fn [{:keys [whisper-identity] :as contact}] - [view st/participant-container - [item-checkbox {:onToggle (fn [checked?] - (reset! checked checked?) - (dispatch [:select-new-participant whisper-identity checked?])) - :checked @checked - :size 30}] - [contact-inner-view contact]]))) +;; todo duplication +(defn on-toggle [whisper-identity] + (fn [checked?] + (let [action (if checked? :select-participant :deselect-participant)] + (dispatch [action whisper-identity])))) + +(defview participant-contact + [{:keys [whisper-identity] :as contact}] + [checked [:is-participant-selected? whisper-identity]] + [view st/participant-container + [item-checkbox {:onToggle (on-toggle whisper-identity) + :checked checked + :size 30}] + [contact-inner-view contact]]) diff --git a/src/status_im/participants/views/create.cljs b/src/status_im/participants/views/create.cljs deleted file mode 100644 index a0ede9b5ef..0000000000 --- a/src/status_im/participants/views/create.cljs +++ /dev/null @@ -1,30 +0,0 @@ -(ns status-im.participants.views.create - (:require [re-frame.core :refer [subscribe dispatch]] - [status-im.resources :as res] - [status-im.components.react :refer [view list-view list-item]] - [status-im.components.toolbar :refer [toolbar]] - [status-im.utils.listview :refer [to-datasource]] - [status-im.participants.views.contact :refer [participant-contact]] - [reagent.core :as r] - [status-im.participants.styles :as st])) - -(defn new-participants-toolbar [] - [toolbar - {:title "Add Participants" - :action {:image {:source res/v ;; {:uri "icon_search"} - :style st/new-participant-image} - :handler #(dispatch [:add-new-participants])}}]) - -(defn new-participants-row - [row _ _] - (list-item [participant-contact row])) - -(defn new-participants [] - (let [contacts (subscribe [:all-new-contacts])] - (fn [] - (let [contacts-ds (to-datasource @contacts)] - [view st/participants-container - [new-participants-toolbar] - [list-view {:dataSource contacts-ds - :renderRow new-participants-row - :style st/participants-list}]])))) diff --git a/src/status_im/participants/views/remove.cljs b/src/status_im/participants/views/remove.cljs index 68aa8f0cd4..e7b8103ffe 100644 --- a/src/status_im/participants/views/remove.cljs +++ b/src/status_im/participants/views/remove.cljs @@ -1,4 +1,5 @@ (ns status-im.participants.views.remove + (:require-macros [status-im.utils.views :refer [defview]]) (:require [re-frame.core :refer [subscribe dispatch]] [status-im.resources :as res] [status-im.components.react :refer [view text-input text image @@ -13,21 +14,20 @@ (defn remove-participants-toolbar [] [toolbar - {:title "Remove Participants" - :action {:handler #(dispatch [:remove-selected-participants]) - :image {:source res/trash-icon ;; {:uri "icon_search"} - :style st/remove-participants-image}}}]) + {:title "Remove Participants" + :action {:handler #(do (dispatch [:remove-participants]) + (dispatch [:navigate-back])) + :image {:source res/trash-icon ;; {:uri "icon_search"} + :style st/remove-participants-image}}}]) (defn remove-participants-row [row _ _] (r/as-element [participant-contact row])) -(defn remove-participants [] - (let [contacts (subscribe [:current-chat-contacts])] - (fn [] - (let [contacts-ds (to-datasource @contacts)] - [view st/participants-container - [remove-participants-toolbar] - [list-view {:dataSource contacts-ds - :renderRow remove-participants-row - :style st/participants-list}]])))) +(defview remove-participants [] + [contacts [:current-chat-contacts]] + [view st/participants-container + [remove-participants-toolbar] + [list-view {:dataSource (to-datasource contacts) + :renderRow remove-participants-row + :style st/participants-list}]]) diff --git a/src/status_im/profile/screen.cljs b/src/status_im/profile/screen.cljs index f3082555fd..83470681dc 100644 --- a/src/status_im/profile/screen.cljs +++ b/src/status_im/profile/screen.cljs @@ -20,7 +20,7 @@ (defn message-user [identity] (when identity - (dispatch [:show-chat identity :push]))) + (dispatch [:navigate-to :chat identity]))) (defview profile [] [{:keys [name whisper-identity phone-number]} [:contact]] diff --git a/src/status_im/protocol/handlers.cljs b/src/status_im/protocol/handlers.cljs new file mode 100644 index 0000000000..4b51c9ec65 --- /dev/null +++ b/src/status_im/protocol/handlers.cljs @@ -0,0 +1,115 @@ +; todo everything inside this namespace must be revievew in common with future +; changes in protocol lib +(ns status-im.protocol.handlers + (:require [status-im.utils.handlers :as u] + [status-im.utils.logging :as log] + [status-im.protocol.api :as api] + [re-frame.core :refer [register-handler dispatch debug]] + [status-im.models.contacts :as contacts] + [status-im.protocol.api :refer [init-protocol]] + [status-im.protocol.protocol-handler :refer [make-handler]] + [status-im.models.protocol :refer [update-identity + set-initialized]] + [status-im.constants :refer [text-content-type]] + [status-im.models.messages :as messages] + [status-im.models.chats :as chats])) + +(register-handler :initialize-protocol + (u/side-effect! + (fn [db [_]] + (init-protocol (make-handler db))))) + +(register-handler :protocol-initialized + (fn [db [_ identity]] + (-> db + (update-identity identity) + (set-initialized true)))) + +(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] + (let [contact-name (:name (contacts/contact-by-identity from))] + (messages/save-message chat-id {:from "system" + :msg-id (str msg-id "_" from) + :content (str (or contact-name from) " received chat invitation") + :content-type text-content-type}))) + +(defn participant-invited-to-group-msg [chat-id identity from msg-id] + (let [inviter-name (:name (contacts/contact-by-identity from)) + invitee-name (if (= identity (api/my-identity)) + "You" + (:name (contacts/contact-by-identity identity)))] + (messages/save-message chat-id {:from "system" + :msg-id msg-id + :content (str (or inviter-name from) " invited " (or invitee-name identity)) + :content-type text-content-type}))) + +(defn participant-removed-from-group-msg [chat-id identity from msg-id] + (let [remover-name (:name (contacts/contact-by-identity from)) + removed-name (:name (contacts/contact-by-identity identity))] + (->> (str (or remover-name from) " removed " (or removed-name identity)) + (system-message msg-id) + (messages/save-message chat-id)))) + +(defn you-removed-from-group-msg [chat-id from msg-id] + (let [remover-name (:name (contacts/contact-by-identity from))] + (->> (str (or remover-name from) " removed you from group chat") + (system-message msg-id) + (messages/save-message chat-id)))) + +(defn participant-left-group-msg [chat-id from msg-id] + (let [left-name (:name (contacts/contact-by-identity from))] + (->> (str (or left-name from) " left") + (system-message msg-id) + (messages/save-message chat-id)))) + +(register-handler :group-chat-invite-acked + (u/side-effect! + (fn [_ [action from group-id ack-msg-id]] + (log/debug action from group-id ack-msg-id) + (joined-chat-msg group-id from ack-msg-id)))) + +(register-handler :participant-removed-from-group + (u/side-effect! + (fn [_ [action from group-id identity msg-id]] + (log/debug action msg-id from group-id identity) + (chats/chat-remove-participants group-id [identity]) + (participant-removed-from-group-msg group-id identity from msg-id)))) + +(register-handler :you-removed-from-group + (u/side-effect! + (fn [_ [action from group-id msg-id]] + (log/debug action msg-id from group-id) + (you-removed-from-group-msg group-id from msg-id) + (chats/set-chat-active group-id false)))) + +(register-handler :participant-left-group + (u/side-effect! + (fn [_ [action from group-id msg-id]] + (log/debug action msg-id from group-id) + (when-not (= (api/my-identity) from) + (participant-left-group-msg group-id from msg-id))))) + +(register-handler :participant-invited-to-group + (u/side-effect! + (fn [_ [action from group-id identity msg-id]] + (log/debug action msg-id from group-id identity) + (participant-invited-to-group-msg group-id identity from msg-id)))) + +(register-handler :acked-msg + (u/side-effect! + (fn [_ [action from msg-id]] + (log/debug action from msg-id) + (messages/update-message! {:msg-id msg-id + :delivery-status :delivered})))) + +(register-handler :msg-delivery-failed + (u/side-effect! + (fn [_ [action msg-id]] + (log/debug action msg-id) + (messages/update-message! {:msg-id msg-id + :delivery-status :failed})))) diff --git a/src/status_im/subs.cljs b/src/status_im/subs.cljs index 8c1125d35d..972c57887c 100644 --- a/src/status_im/subs.cljs +++ b/src/status_im/subs.cljs @@ -5,8 +5,13 @@ status-im.group-settings.subs status-im.discovery.subs status-im.contacts.subs - status-im.new-group.subs)) + status-im.new-group.subs + status-im.participants.subs)) (register-sub :get (fn [db [_ k]] (reaction (k @db)))) + +(register-sub :get-in + (fn [db [_ path]] + (reaction (get-in @db path)))) diff --git a/src/status_im/utils/subs.cljs b/src/status_im/utils/subs.cljs new file mode 100644 index 0000000000..3be283c37e --- /dev/null +++ b/src/status_im/utils/subs.cljs @@ -0,0 +1,11 @@ +(ns status-im.utils.subs + (:require-macros [reagent.ratom :refer [reaction]])) + +(defn contains-sub + "Creates subscrition that cheks if collection (map or set) contains element" + [collection] + (fn [db [_ element]] + (println "WWWWWWWWWW" (type db)) + (-> (collection @db) + (contains? element) + (reaction))))