From d39357996b10e2a016dd1ca6f05439bf49dc0003 Mon Sep 17 00:00:00 2001 From: janherich Date: Fri, 6 Oct 2017 02:28:59 +0200 Subject: [PATCH] Proceed with move from side-effect to functional --- src/status_im/chat/events.cljs | 203 ++++++++--- .../chat/events/receive_message.cljs | 26 +- src/status_im/chat/handlers.cljs | 332 +++++------------- src/status_im/chat/handlers/send_message.cljs | 4 +- src/status_im/chat/models.cljs | 37 +- src/status_im/commands/handlers/loading.cljs | 7 +- src/status_im/data_store/chats.cljs | 1 + .../ui/screens/accounts/login/events.cljs | 2 +- .../ui/screens/chats_list/views.cljs | 2 +- src/status_im/ui/screens/contacts/events.cljs | 10 +- src/status_im/ui/screens/navigation.cljs | 105 +++--- src/status_im/ui/screens/profile/events.cljs | 2 +- .../ui/screens/wallet/request/events.cljs | 24 +- 13 files changed, 376 insertions(+), 379 deletions(-) diff --git a/src/status_im/chat/events.cljs b/src/status_im/chat/events.cljs index 55eb03bd1a..c7b562e1ca 100644 --- a/src/status_im/chat/events.cljs +++ b/src/status_im/chat/events.cljs @@ -2,6 +2,7 @@ (:require [re-frame.core :as re-frame] [taoensso.timbre :as log] [status-im.utils.handlers :as handlers] + [status-im.utils.gfycat.core :as gfycat] [status-im.chat.models :as model] [status-im.chat.models.unviewed-messages :as unviewed-messages-model] [status-im.chat.sign-up :as sign-up] @@ -10,6 +11,7 @@ [status-im.data-store.messages :as msg-store] [status-im.data-store.contacts :as contacts-store] [status-im.data-store.chats :as chats-store] + [status-im.ui.screens.navigation :as navigation] [status-im.protocol.core :as protocol] [status-im.constants :as const] [status-im.components.list-selection :as list-selection] @@ -23,34 +25,44 @@ ;;;; Coeffects (re-frame/reg-cofx - :stored-unviewed-messages - (fn [cofx _] - (assoc cofx :stored-unviewed-messages (msg-store/get-unviewed)))) + :stored-unviewed-messages + (fn [cofx _] + (assoc cofx :stored-unviewed-messages (msg-store/get-unviewed)))) (re-frame/reg-cofx - :get-stored-message - (fn [cofx _] - (assoc cofx :get-stored-message msg-store/get-by-id))) + :get-stored-message + (fn [cofx _] + (assoc cofx :get-stored-message msg-store/get-by-id))) (re-frame/reg-cofx - :get-stored-messages - (fn [cofx _] - (assoc cofx :get-stored-messages msg-store/get-by-chat-id))) + :get-stored-messages + (fn [cofx _] + (assoc cofx :get-stored-messages msg-store/get-by-chat-id))) (re-frame/reg-cofx - :get-last-stored-message - (fn [cofx _] - (assoc cofx :get-last-stored-message msg-store/get-last-message))) + :get-last-stored-message + (fn [cofx _] + (assoc cofx :get-last-stored-message msg-store/get-last-message))) (re-frame/reg-cofx - :get-message-previews - (fn [cofx _] - (assoc cofx :message-previews (msg-store/get-previews)))) + :get-message-previews + (fn [cofx _] + (assoc cofx :message-previews (msg-store/get-previews)))) (re-frame/reg-cofx - :all-stored-chats - (fn [cofx _] - (assoc cofx :all-stored-chats (chats-store/get-all)))) + :all-stored-chats + (fn [cofx _] + (assoc cofx :all-stored-chats (chats-store/get-all)))) + +(re-frame/reg-cofx + :get-stored-chat + (fn [cofx _] + (assoc cofx :get-stored-chat chats-store/get-by-id))) + +(re-frame/reg-cofx + :gfy-generator + (fn [cofx _] + (assoc cofx :gfy-generator gfycat/generate-gfy))) ;;;; Effects @@ -84,26 +96,6 @@ (fn [[command link]] (list-selection/browse command link))) -;;;; Helper fns - -(defn init-console-chat - [{:keys [chats] :accounts/keys [current-account-id] :as db} existing-account?] - (if (get chats const/console-chat-id) - {:db db} - (cond-> {:db (-> db - (assoc :new-chat sign-up/console-chat) - (update :chats assoc const/console-chat-id sign-up/console-chat) - (assoc :current-chat-id const/console-chat-id)) - :dispatch-n [[:add-contacts [sign-up/console-contact]]] - :save-chat sign-up/console-chat - :save-all-contacts [sign-up/console-contact]} - - (not current-account-id) - (update :dispatch-n concat sign-up/intro-events) - - existing-account? - (update :dispatch-n concat sign-up/start-signup-events)))) - ;;;; Handlers (handlers/register-handler-db @@ -165,6 +157,24 @@ message)) messages))))) +(defn- init-console-chat + [{:keys [chats] :accounts/keys [current-account-id] :as db} existing-account?] + (if (chats const/console-chat-id) + {:db db} + (cond-> {:db (-> db + (assoc :new-chat sign-up/console-chat + :current-chat-id const/console-chat-id) + (update :chats assoc const/console-chat-id sign-up/console-chat)) + :dispatch-n [[:add-contacts [sign-up/console-contact]]] + :save-chat sign-up/console-chat + :save-all-contacts [sign-up/console-contact]} + + (not current-account-id) + (update :dispatch-n concat sign-up/intro-events) + + existing-account? + (update :dispatch-n concat sign-up/start-signup-events)))) + (handlers/register-handler-fx :init-console-chat (fn [{:keys [db]} _] @@ -248,17 +258,116 @@ (fn [{:keys [get-stored-message]} _] (when-not (get-stored-message chat-const/move-to-internal-failure-message-id) {:dispatch sign-up/move-to-internal-failure-event}))) -(comment - (handlers/register-handler-fx - :init-chat - [(re-frame/inject-cofx :get-stored-messages)] - (fn [{:keys [db get-stored-messages]} _] - (let [current-chat-id (:current-chat-id db)] - {:db (assoc-in [:chats current-chat-id :messages] (get-stored-messages current-chat-id)) - ;; TODO(janherich): make this dispatch into fn call once commands loading is refactored - :dispatch [:load-commands! current-chat-id]})))) (handlers/register-handler-fx :browse-link-from-message (fn [{{:keys [global-commands]} :db} [_ link]] {:browse [(:browse global-commands) link]})) + +(handlers/register-handler-fx + :init-chat + [(re-frame/inject-cofx :get-stored-messages)] + (fn [{:keys [db get-stored-messages]} _] + (let [current-chat-id (:current-chat-id db)] + {:db (assoc-in db [:chats current-chat-id :messages] (get-stored-messages current-chat-id)) + ;; TODO(janherich): make this dispatch into fn call once commands loading is refactored + :dispatch [:load-commands! current-chat-id]}))) + +(defn- jail-init-callback + [{:keys [db] :as fx} chat-id] + (let [bot-url (get-in db [:contacts/contacts chat-id :bot-url]) + was-opened? (get-in db [:chats chat-id :was-opened?])] + (if (and (not was-opened?) bot-url) + (assoc fx :call-jail-function {:chat-id chat-id + :function :init + :context {:from (:accounts/current-account-id db)}}) + fx))) + +(defn preload-chat-data + "Takes coeffects map and chat-id, returns effects necessary when navigating to chat" + [{:keys [db get-stored-messages]} chat-id] + (let [messages (get-in db [:chats chat-id :messages]) + chat-loaded-event (get-in db [:chats chat-id :chat-loaded-event]) + commands-loaded? (get-in db [:contacts/contacts chat-id :commands-loaded?])] + (cond-> {:db (-> db + (assoc :current-chat-id chat-id) + (assoc-in [:chats chat-id :was-opened?] true) + (model/set-chat-ui-props {:validation-messages nil}) + (update-in [:chats chat-id] dissoc :chat-loaded-event)) + :dispatch-n [[:load-requests! chat-id]]} + (not commands-loaded?) + (update :dispatch-n conj [:load-commands! chat-id #(re-frame/dispatch [::jail-init-callback chat-id])]) + + commands-loaded? + (jail-init-callback chat-id) + ;; TODO(janherich): what's the purpose of the second term in AND ? + (and (seq messages) + (not= (count messages) 1)) + (assoc-in [:db :chats chat-id :messages] (get-stored-messages chat-id)) + + chat-loaded-event + (update :dispatch-n conj chat-loaded-event)))) + +(handlers/register-handler-db + :add-chat-loaded-event + [re-frame/trim-v] + (fn [db [chat-id event]] + (assoc-in db [:chats chat-id :chat-loaded-event] event))) + +(handlers/register-handler-fx + ::jail-init-callback + [re-frame/trim-v] + (fn [{:keys [db]} [chat-id]] + (jail-init-callback {:db db} chat-id))) + +;; TODO(janherich): remove this unnecessary event in the future (only model function `add-chat` will stay) +(handlers/register-handler-fx + :add-chat + [(re-frame/inject-cofx :gfy-generator) re-frame/trim-v] + (fn [cofx [chat-id]] + (model/add-chat cofx chat-id))) + +(defn- navigate-to-chat + [cofx chat-id navigation-replace?] + (let [nav-fn (if navigation-replace? + #(navigation/navigate-to % :chat) + #(navigation/replace-view % :chat))] + (-> cofx + (preload-chat-data chat-id) + (update :db nav-fn)))) + +(handlers/register-handler-fx + :navigate-to-chat + [(re-frame/inject-cofx :get-stored-messages) re-frame/trim-v] + (fn [cofx [chat-id {:keys [navigation-replace?]}]] + (navigate-to-chat cofx chat-id navigation-replace?))) + +(handlers/register-handler-fx + :start-chat + [(re-frame/inject-cofx :gfy-generator) + (re-frame/inject-cofx :get-stored-messages) + re-frame/trim-v] + (fn [{:keys [db] :as cofx} [contact-id {:keys [navigation-replace?]}]] + (when (not= (:current-public-key db) contact-id) ; don't allow to open chat with yourself + (if (get (:chats db) contact-id) + (navigate-to-chat cofx contact-id navigation-replace?) ; existing chat, just preload and displey + (let [add-chat-fx (model/add-chat cofx contact-id)] ; new chat, create before preload & display + (merge add-chat-fx + (navigate-to-chat (assoc cofx :db (:db add-chat-fx)) + contact-id + navigation-replace?))))))) + +;; TODO(janherich): remove this unnecessary event in the future (only model function `update-chat` will stay) +(handlers/register-handler-fx + :update-chat! + [(re-frame/inject-cofx :get-stored-chat) re-frame/trim-v] + (fn [cofx [chat]] + (model/update-chat cofx chat))) + +;; TODO(janherich): remove this unnecessary event in the future (only model function `upsert-chat` will stay) +(handlers/register-handler-fx + :upsert-chat! + [(re-frame/inject-cofx :get-stored-chat) re-frame/trim-v] + (fn [cofx [chat]] + (model/upsert-chat cofx chat))) + diff --git a/src/status_im/chat/events/receive_message.cljs b/src/status_im/chat/events/receive_message.cljs index bfb0113751..d8cb7d89cc 100644 --- a/src/status_im/chat/events/receive_message.cljs +++ b/src/status_im/chat/events/receive_message.cljs @@ -6,6 +6,7 @@ [status-im.utils.clocks :as clocks] [status-im.constants :as const] [status-im.chat.utils :as chat-utils] + [status-im.chat.models :as model] [status-im.chat.models.unviewed-messages :as unviewed-messages-model] [status-im.data-store.chats :as chat-store] [status-im.data-store.messages :as msg-store])) @@ -33,7 +34,7 @@ (defn add-message [{:keys [db message-exists? get-last-stored-message pop-up-chat? - get-last-clock-value now random-id]} + get-last-clock-value now random-id] :as cofx} {:keys [from group-id chat-id content-type message-id timestamp clock-value] :as message @@ -54,16 +55,17 @@ :timestamp (or timestamp now) :clock-value (clocks/receive clock-value - (get-last-clock-value chat-identifier)))] - (cond-> {:db (-> db - (chat-utils/add-message-to-db chat-identifier chat-identifier enriched-message - (:new? enriched-message)) - (unviewed-messages-model/add-unviewed-message chat-identifier message-id) - (assoc-in [:chats chat-identifier :last-message] message)) - :dispatch-n [[:upsert-chat! {:chat-id chat-identifier - :group-chat group-chat?}] - [:request-command-message-data enriched-message :short-preview]] - :save-message (dissoc enriched-message :new?)} + (get-last-clock-value chat-identifier))) + fx (model/upsert-chat cofx {:chat-id chat-identifier + :group-chat group-chat?})] + (cond-> (-> fx + (update :db #(-> % + (chat-utils/add-message-to-db chat-identifier chat-identifier enriched-message + (:new? enriched-message)) + (unviewed-messages-model/add-unviewed-message chat-identifier message-id) + (assoc-in [:chats chat-identifier :last-message] message))) + (assoc :dispatch-n [[:request-command-message-data enriched-message :short-preview]] + :save-message (dissoc enriched-message :new?))) (get-in enriched-message [:content :command]) (update :dispatch-n conj [:request-command-preview enriched-message]) @@ -78,7 +80,7 @@ (def ^:private receive-interceptors [(re-frame/inject-cofx :message-exists?) (re-frame/inject-cofx :get-last-stored-message) (re-frame/inject-cofx :pop-up-chat?) (re-frame/inject-cofx :get-last-clock-value) - (re-frame/inject-cofx :random-id) re-frame/trim-v]) + (re-frame/inject-cofx :random-id) (re-frame/inject-cofx :get-stored-chat) re-frame/trim-v]) (handlers/register-handler-fx :received-protocol-message! diff --git a/src/status_im/chat/handlers.cljs b/src/status_im/chat/handlers.cljs index 0a319c894a..941f264245 100644 --- a/src/status_im/chat/handlers.cljs +++ b/src/status_im/chat/handlers.cljs @@ -1,186 +1,24 @@ (ns status-im.chat.handlers (:require-macros [cljs.core.async.macros :as am]) (:require [re-frame.core :refer [enrich after debug dispatch reg-fx]] + [cljs.core.async :as a] [clojure.string :as string] - [status-im.components.styles :refer [default-chat-color]] - [status-im.chat.constants :as chat-const] + [status-im.components.styles :refer [default-chat-color]] + [status-im.chat.constants :as chat-consts] [status-im.protocol.core :as protocol] - [status-im.data-store.chats :as chats] - [status-im.data-store.contacts :as contacts] + [status-im.data-store.chats :as chats] [status-im.data-store.messages :as messages] [status-im.data-store.pending-messages :as pending-messages] [status-im.constants :refer [text-content-type content-type-command content-type-command-request console-chat-id]] - [status-im.utils.random :as random] - [status-im.chat.sign-up :as sign-up-service] - [status-im.ui.screens.navigation :as nav] - [status-im.utils.handlers :refer [register-handler register-handler-fx] :as u] - [status-im.utils.phone-number :refer [format-phone-number - valid-mobile-number?]] - [status-im.native-module.core :as status] - [status-im.utils.types :refer [json->clj]] - [status-im.chat.utils :refer [console? not-console? safe-trim]] - [status-im.utils.gfycat.core :refer [generate-gfy]] + [status-im.utils.random :as random] + [status-im.utils.handlers :refer [register-handler register-handler-fx] :as u] status-im.chat.events status-im.chat.handlers.requests - status-im.chat.handlers.send-message - [cljs.core.async :as a] - status-im.chat.handlers.webview-bridge - [taoensso.timbre :as log])) - -(defn load-messages! - ([db] (load-messages! db nil)) - ([{:keys [current-chat-id] :as db} _] - (let [messages (messages/get-by-chat-id current-chat-id)] - (assoc db :messages messages)))) - -(defn init-chat - ([db] (init-chat db nil)) - ([{:keys [messages current-chat-id] :as db} _] - (-> db - (assoc-in [:chats current-chat-id :messages] messages) - (dissoc :messages)))) - -(defn load-commands! - [{:keys [current-chat-id]} _] - (dispatch [:load-commands! current-chat-id])) - -(register-handler :init-chat - (after #(dispatch [:load-requests!])) - - (u/handlers-> - load-messages! - init-chat - load-commands!)) - -(defmethod nav/preload-data! :chat - [{:keys [current-chat-id] :accounts/keys [current-account-id] :as db} [_ _ id]] - (let [chat-id (or id current-chat-id) - messages (get-in db [:chats chat-id :messages]) - db' (-> db - (assoc :current-chat-id chat-id) - (assoc-in [:chats chat-id :was-opened?] true)) - commands-loaded? (get-in db [:contacts/contacts chat-id :commands-loaded?]) - bot-url (get-in db [:contacts/contacts chat-id :bot-url]) - was-opened? (get-in db [:chats chat-id :was-opened?]) - call-init-command #(do - (dispatch [:invoke-chat-loaded-callbacks chat-id]) - (when (and (not was-opened?) bot-url) - (status/call-function! - {:chat-id chat-id - :function :init - :context {:from current-account-id}})))] - ; Reset validation messages, if any - (dispatch [:set-chat-ui-props {:validation-messages nil}]) - (dispatch [:load-requests! chat-id]) - ;; todo rewrite this. temporary fix for https://github.com/status-im/status-react/issues/607 - #_(dispatch [:load-commands! chat-id]) - (if-not commands-loaded? - (dispatch [:load-commands! chat-id call-init-command]) - (call-init-command)) - (if (and (seq messages) - (not= (count messages) 1)) - db' - (-> db' - load-messages! - init-chat)))) - -(register-handler :add-chat-loaded-callback - (fn [db [_ chat-id callback]] - (log/debug "Add chat loaded callback: " chat-id callback) - (update-in db [:chat-loaded-callbacks chat-id] conj callback))) - -(register-handler ::clear-chat-loaded-callbacks - (fn [db [_ chat-id]] - (log/debug "Clear chat loaded callback: " chat-id) - (assoc-in db [:chat-loaded-callbacks chat-id] nil))) - -(register-handler :invoke-chat-loaded-callbacks - (u/side-effect! - (fn [db [_ chat-id]] - (log/debug "Invoking chat loaded callbacks: " chat-id) - (let [callbacks (get-in db [:chat-loaded-callbacks chat-id])] - (log/debug "Invoking chat loaded callbacks: " callbacks) - (doseq [callback callbacks] - (callback)) - (dispatch [::clear-chat-loaded-callbacks chat-id]))))) - -(defn prepare-chat [{:contacts/keys [contacts]} chat-id chat] - (let [name (get-in contacts [chat-id :name]) - whisper-identity (get-in contacts [chat-id :whisper-identity])] - (merge {:chat-id chat-id - :name (or name (generate-gfy whisper-identity)) - :color default-chat-color - :group-chat false - :is-active true - :timestamp (.getTime (js/Date.)) - :contacts [{:identity chat-id}]} - chat))) - -(defn add-new-chat - [db [_ chat-id chat]] - (assoc db :new-chat (prepare-chat db chat-id chat))) - -(defn add-chat [{:keys [new-chat chats] :as db} [_ chat-id]] - (if-not (get chats chat-id) - (update db :chats assoc chat-id new-chat) - db)) - -(defn save-new-chat! - [{{:keys [chat-id] :as new-chat} :new-chat} _] - (when-not (chats/exists? chat-id) - (chats/save new-chat))) - -(defn open-chat! - [_ [_ chat-id _ navigation-type]] - (dispatch [(or navigation-type :navigate-to) :chat chat-id])) - -(register-handler ::start-chat! - (u/handlers-> - add-new-chat - add-chat - save-new-chat! - open-chat!)) - -(register-handler :start-chat - (u/side-effect! - (fn [{:keys [chats current-public-key]} - [_ contact-id options navigation-type]] - (when-not (= current-public-key contact-id) - (if (chats contact-id) - (dispatch [(or navigation-type :navigate-to) :chat contact-id]) - (dispatch [::start-chat! contact-id options navigation-type])))))) - -(register-handler :add-chat - (u/handlers-> - add-new-chat - add-chat - save-new-chat!)) - -(defn update-chat! - [_ [_ {:keys [name] :as chat}]] - (let [chat' (if name chat (dissoc chat :name))] - (chats/save chat'))) - -(register-handler :update-chat! - (after update-chat!) - (fn [db [_ {:keys [chat-id name] :as chat}]] - (let [chat' (if name chat (dissoc chat :name))] - (if (get-in db [:chats chat-id]) - (update-in db [:chats chat-id] merge chat') - db)))) - -(register-handler :upsert-chat! - (fn [db [_ {:keys [chat-id] :as opts}]] - (let [chat (if (chats/exists? chat-id) - (-> (chats/get-by-id chat-id) - (assoc :timestamp (random/timestamp)) - (merge opts)) - (prepare-chat db chat-id opts))] - (chats/save chat) - (update-in db [:chats chat-id] merge chat)))) + status-im.chat.handlers.send-message + status-im.chat.handlers.webview-bridge)) (defn remove-chat [db [_ chat-id]] @@ -207,33 +45,36 @@ [_ [_ chat-id]] (pending-messages/delete-all-by-chat-id chat-id)) -(register-handler :leave-group-chat - ;; todo oreder of operations tbd +(register-handler + :leave-group-chat + ;; todo order of operations tbd (after (fn [_ _] (dispatch [:navigation-replace :chat-list]))) (u/side-effect! - (fn [{:keys [web3 current-chat-id chats current-public-key]} _] - (let [{:keys [public-key private-key public?]} (chats current-chat-id)] - (protocol/stop-watching-group! + (fn [{:keys [web3 current-chat-id chats current-public-key]} _] + (let [{:keys [public-key private-key public?]} (chats current-chat-id)] + (protocol/stop-watching-group! + {:web3 web3 + :group-id current-chat-id}) + (when-not public? + (protocol/leave-group-chat! {:web3 web3 - :group-id current-chat-id}) - (when-not public? - (protocol/leave-group-chat! - {:web3 web3 - :group-id current-chat-id - :keypair {:public public-key - :private private-key} - :message {:from current-public-key - :message-id (random/id)}}))) - (dispatch [:remove-chat current-chat-id])))) + :group-id current-chat-id + :keypair {:public public-key + :private private-key} + :message {:from current-public-key + :message-id (random/id)}}))) + (dispatch [:remove-chat current-chat-id])))) -(register-handler :remove-chat +(register-handler + :remove-chat (u/handlers-> - remove-chat - delete-messages! - remove-pending-messages! - delete-chat!)) + remove-chat + delete-messages! + remove-pending-messages! + delete-chat!)) -(register-handler :check-and-open-dapp! +(register-handler + :check-and-open-dapp! (u/side-effect! (fn [{:keys [current-chat-id global-commands] :contacts/keys [contacts]}] @@ -249,33 +90,34 @@ (register-handler :update-group-message (u/side-effect! - (fn [{:keys [current-public-key web3 chats]} - [_ {:keys [from] - {:keys [group-id keypair timestamp]} :payload}]] - (let [{:keys [private public]} keypair] - (let [is-active (chats/is-active? group-id) - chat {:chat-id group-id - :public-key public - :private-key private - :updated-at timestamp}] - (when (and (= from (get-in chats [group-id :group-admin])) - (or (not (chats/exists? group-id)) - (chats/new-update? timestamp group-id))) - (dispatch [:update-chat! chat]) - (when is-active - (protocol/start-watching-group! - {:web3 web3 - :group-id group-id - :identity current-public-key - :keypair keypair - :callback #(dispatch [:incoming-message %1 %2])})))))))) + (fn [{:keys [current-public-key web3 chats]} + [_ {:keys [from] + {:keys [group-id keypair timestamp]} :payload}]] + (let [{:keys [private public]} keypair] + (let [is-active (chats/is-active? group-id) + chat {:chat-id group-id + :public-key public + :private-key private + :updated-at timestamp}] + (when (and (= from (get-in chats [group-id :group-admin])) + (or (not (chats/exists? group-id)) + (chats/new-update? timestamp group-id))) + (dispatch [:update-chat! chat]) + (when is-active + (protocol/start-watching-group! + {:web3 web3 + :group-id group-id + :identity current-public-key + :keypair keypair + :callback #(dispatch [:incoming-message %1 %2])})))))))) -(register-handler :update-message-overhead! +(register-handler + :update-message-overhead! (u/side-effect! - (fn [_ [_ chat-id network-status]] - (if (= network-status :offline) - (chats/inc-message-overhead chat-id) - (chats/reset-message-overhead chat-id))))) + (fn [_ [_ chat-id network-status]] + (if (= network-status :offline) + (chats/inc-message-overhead chat-id) + (chats/reset-message-overhead chat-id))))) (reg-fx ::save-public-chat @@ -286,11 +128,11 @@ ::start-watching-group (fn [{:keys [group-id web3 current-public-key keypair]}] (protocol/start-watching-group! - {:web3 web3 - :group-id group-id - :identity current-public-key - :keypair keypair - :callback #(dispatch [:incoming-message %1 %2])}))) + {:web3 web3 + :group-id group-id + :identity current-public-key + :keypair keypair + :callback #(dispatch [:incoming-message %1 %2])}))) (register-handler-fx :create-new-public-chat @@ -304,13 +146,13 @@ :is-active true :timestamp (random/timestamp)}] (merge - (when-not exists? - {:db (assoc-in db [:chats (:chat-id chat)] chat) - ::save-public-chat chat - ::start-watching-group (merge {:group-id topic} - (select-keys db [:web3 :current-public-key]))}) - {:dispatch-n [[:navigate-to-clean :chat-list] - [:navigate-to :chat topic]]})))) + (when-not exists? + {:db (assoc-in db [:chats (:chat-id chat)] chat) + ::save-public-chat chat + ::start-watching-group (merge {:group-id topic} + (select-keys db [:web3 :current-public-key]))}) + {:dispatch-n [[:navigate-to-clean :chat-list] + [:navigate-to-chat topic]]})))) (reg-fx ::save-chat @@ -323,23 +165,23 @@ (let [{:keys [chat-id public-key private-key contacts name]} new-chat identities (mapv :identity contacts)] (protocol/invite-to-group! - {:web3 web3 - :group {:id chat-id - :name name - :contacts (conj identities current-public-key) - :admin current-public-key - :keypair {:public public-key - :private private-key}} - :identities identities - :message {:from current-public-key - :message-id (random/id)}}) + {:web3 web3 + :group {:id chat-id + :name name + :contacts (conj identities current-public-key) + :admin current-public-key + :keypair {:public public-key + :private private-key}} + :identities identities + :message {:from current-public-key + :message-id (random/id)}}) (protocol/start-watching-group! - {:web3 web3 - :group-id chat-id - :identity current-public-key - :keypair {:public public-key - :private private-key} - :callback #(dispatch [:incoming-message %1 %2])})))) + {:web3 web3 + :group-id chat-id + :identity current-public-key + :keypair {:public public-key + :private private-key} + :callback #(dispatch [:incoming-message %1 %2])})))) (defn group-name-from-contacts [contacts selected-contacts username] (->> (select-keys contacts selected-contacts) @@ -381,7 +223,7 @@ ::start-listen-group (merge {:new-chat new-chat} (select-keys db [:web3 :current-public-key])) :dispatch-n [[:navigate-to-clean :chat-list] - [:navigate-to :chat (:chat-id new-chat)]]}))) + [:navigate-to-chat (:chat-id new-chat)]]}))) (register-handler-fx :group-chat-invite-received diff --git a/src/status_im/chat/handlers/send_message.cljs b/src/status_im/chat/handlers/send_message.cljs index 3a5884e417..f29e4e8312 100644 --- a/src/status_im/chat/handlers/send_message.cljs +++ b/src/status_im/chat/handlers/send_message.cljs @@ -119,6 +119,7 @@ (update-in [:content :params] #(apply dissoc % hidden-params)) (dissoc :to-message :has-handler :raw-input)) preview (assoc :preview (pr-str preview)))] + (dispatch [:upsert-chat! {:chat-id chat-id}]) (messages/save chat-id command))))) (register-handler ::dispatch-responded-requests! @@ -201,8 +202,7 @@ (dispatch [::send-message! params]))) (u/side-effect! (fn [_ [_ {:keys [chat-id message]}]] - (dispatch [:upsert-chat! {:chat-id chat-id - :timestamp (datetime/now-ms)}]) + (dispatch [:upsert-chat! {:chat-id chat-id}]) (messages/save chat-id message)))) (register-handler ::send-dapp-message diff --git a/src/status_im/chat/models.cljs b/src/status_im/chat/models.cljs index cfae35d53c..7328766114 100644 --- a/src/status_im/chat/models.cljs +++ b/src/status_im/chat/models.cljs @@ -1,4 +1,5 @@ -(ns status-im.chat.models) +(ns status-im.chat.models + (:require [status-im.components.styles :as styles])) (defn set-chat-ui-props "Updates ui-props in active chat by merging provided kvs into them" @@ -9,3 +10,37 @@ "Toggles chat ui prop in active chat" [{:keys [current-chat-id] :as db} ui-element] (update-in db [:chat-ui-props current-chat-id ui-element] not)) + +(defn- create-new-chat + [{:keys [db gfy-generator now]} chat-id] + (let [{:keys [name whisper-identity]} (get-in db [:contacts/contacts chat-id])] + {:chat-id chat-id + :name (or name (gfy-generator whisper-identity)) + :color styles/default-chat-color + :group-chat false + :is-active true + :timestamp now + :contacts [{:identity chat-id}]})) + +(defn add-chat + [{:keys [db] :as cofx} chat-id] + (let [new-chat (create-new-chat cofx chat-id) + existing-chats (:chats db)] + {:db (cond-> (assoc db :new-chat new-chat) + (not (contains? existing-chats chat-id)) + (update :chats assoc chat-id new-chat)) + :save-chat new-chat})) + +(defn update-chat + "Updates chat properties, if chat is not present in db, creates a default new one" + [{:keys [db get-stored-chat]} {:keys [chat-id] :as chat}] + (let [chat (merge (or (get-stored-chat chat-id) + (create-new-chat db chat-id)) + chat)] + {:db (update-in db [:chats chat-id] merge chat) + :save-chat chat})) + +(defn upsert-chat + "Just like `update-chat` only implicitely updates timestamp" + [cofx chat] + (update-chat cofx (assoc chat :timestamp (:now cofx)))) diff --git a/src/status_im/commands/handlers/loading.cljs b/src/status_im/commands/handlers/loading.cljs index 9dff2ab653..78e2772a28 100644 --- a/src/status_im/commands/handlers/loading.cljs +++ b/src/status_im/commands/handlers/loading.cljs @@ -182,10 +182,9 @@ (reg-handler ::parse-commands! (u/side-effect! parse-commands!)) (reg-handler ::add-commands - [(after (fn [_ [id]] - (dispatch [:invoke-commands-loading-callbacks id]) - (dispatch [:invoke-chat-loaded-callbacks id]))) - (after #(dispatch [:update-suggestions]))] + (after (fn [_ [id]] + (dispatch [:invoke-commands-loading-callbacks id]) + (dispatch [:update-suggestions]))) add-commands) (reg-handler ::loading-failed! (u/side-effect! loading-failed!)) diff --git a/src/status_im/data_store/chats.cljs b/src/status_im/data_store/chats.cljs index b6cfe23344..c34fd88f1a 100644 --- a/src/status_im/data_store/chats.cljs +++ b/src/status_im/data_store/chats.cljs @@ -21,6 +21,7 @@ (defn save [{:keys [last-message-id chat-id] :as chat}] + ;; TODO(janherich): remove `:last-message-id`, seems like it's not used anywhere anymore (let [chat (assoc chat :last-message-id (or last-message-id ""))] (data-store/save chat (data-store/exists? chat-id)))) diff --git a/src/status_im/ui/screens/accounts/login/events.cljs b/src/status_im/ui/screens/accounts/login/events.cljs index 5e999b40b3..7959ba79a2 100644 --- a/src/status_im/ui/screens/accounts/login/events.cljs +++ b/src/status_im/ui/screens/accounts/login/events.cljs @@ -125,7 +125,7 @@ [:initialize-account address]] (if new-account? [[:navigate-to-clean :chat-list] - [:navigate-to :chat console-chat-id]] + [:navigate-to-chat console-chat-id]] [[:navigate-to-clean :chat-list] [:navigate-to :chat-list]]))} (log/debug "Error changing acount: " error)))) diff --git a/src/status_im/ui/screens/chats_list/views.cljs b/src/status_im/ui/screens/chats_list/views.cljs index 3b6bd63a55..439b664d45 100644 --- a/src/status_im/ui/screens/chats_list/views.cljs +++ b/src/status_im/ui/screens/chats_list/views.cljs @@ -70,7 +70,7 @@ :on-press #(re-frame/dispatch [:navigate-to :new-chat])}]) (defn chat-list-item [[chat-id chat] edit?] - [react/touchable-highlight {:on-press #(re-frame/dispatch [:navigate-to :chat chat-id])} + [react/touchable-highlight {:on-press #(re-frame/dispatch [:navigate-to-chat chat-id])} [react/view [inner-item/chat-list-item-inner-view (assoc chat :chat-id chat-id) edit?]]]) diff --git a/src/status_im/ui/screens/contacts/events.cljs b/src/status_im/ui/screens/contacts/events.cljs index 6f523529e5..10117135d3 100644 --- a/src/status_im/ui/screens/contacts/events.cljs +++ b/src/status_im/ui/screens/contacts/events.cljs @@ -323,7 +323,7 @@ (when-not (get-in db [:contacts/contacts whisper-identity]) (let [contact (assoc contact :address (public-key->address whisper-identity))] {:dispatch-n [[::add-new-contact contact] - [:start-chat whisper-identity {} :navigation-replace]]})))) + [:start-chat whisper-identity {:navigation-replace? true}]]})))) (register-handler-fx :add-pending-contact @@ -419,10 +419,10 @@ :open-chat-with-contact (fn [_ [_ {:keys [whisper-identity dapp?] :as contact}]] {:dispatch-n (concat - [[:navigate-to-clean :chat-list] - [:start-chat whisper-identity {}]] - (when-not dapp? - [[::send-contact-request contact]]))})) + [[:navigate-to-clean :chat-list] + [:start-chat whisper-identity]] + (when-not dapp? + [[::send-contact-request contact]]))})) (register-handler-fx :add-contact-handler diff --git a/src/status_im/ui/screens/navigation.cljs b/src/status_im/ui/screens/navigation.cljs index 5adf63d4a0..b3a59c0611 100644 --- a/src/status_im/ui/screens/navigation.cljs +++ b/src/status_im/ui/screens/navigation.cljs @@ -1,70 +1,86 @@ (ns status-im.ui.screens.navigation - (:require [re-frame.core :refer [enrich]] + (:require [re-frame.core :as re-frame] [status-im.utils.handlers :refer [register-handler-db]] [status-im.constants :refer [console-chat-id]])) -(defmulti preload-data! - (fn [db [_ view-id]] (or view-id (:view-id db)))) +;; private helper fns -(defmethod preload-data! :default [db _] db) - -(defn -preload-data! [{:keys [was-modal?] :as db} & args] - (if was-modal? - (dissoc db :was-modal?) ;;TODO check how it worked with this bug - (apply preload-data! db args))) - -(register-handler-db - :navigate-forget - (enrich preload-data!) - (fn [db [_ new-view-id]] - (assoc db :view-id new-view-id))) - -(defn push-view [db view-id] +(defn- push-view [db view-id] (-> db (update :navigation-stack conj view-id) (assoc :view-id view-id))) -(register-handler-db - :navigate-to - (enrich preload-data!) - (fn [{:keys [view-id] :as db} [_ new-view-id]] - (if (= view-id new-view-id) - db - (push-view db new-view-id)))) - -(register-handler-db - :navigate-to-modal - (enrich preload-data!) - (fn [db [_ modal-view]] - (assoc db :modal modal-view))) - -(defn replace-top-element [stack view-id] +(defn- replace-top-element [stack view-id] (let [stack' (if (> 2 (count stack)) (list :chat-list) (pop stack))] (conj stack' view-id))) -(defn replace-view [db view-id] +(defn- replace-view [db view-id] (-> db (update :navigation-stack replace-top-element view-id) (assoc :view-id view-id))) -(register-handler-db - :navigation-replace - (enrich preload-data!) - (fn [db [_ view-id]] - (replace-view db view-id))) - (defn- can-navigate-back? [db] (not (get db :accounts/creating-account?))) +(defn- navigate-to-clean [db view-id] + (-> db + (assoc :navigation-stack (list)) + (push-view view-id))) + +;; public fns + +(defmulti preload-data! + (fn [db [_ view-id]] (or view-id (:view-id db)))) + +(defmethod preload-data! :default [db _] db) + +(defn- -preload-data! [{:keys [was-modal?] :as db} & args] + (if was-modal? + (dissoc db :was-modal?) ;;TODO check how it worked with this bug + (apply preload-data! db args))) + +(defn navigate-to + "Navigates to particular view" + [{:keys [view-id] :as db} go-to-view-id] + (if (= view-id go-to-view-id) + db + (push-view db go-to-view-id))) + +;; event handlers + +(register-handler-db + :navigate-forget + (re-frame/enrich preload-data!) + (fn [db [_ new-view-id]] + (assoc db :view-id new-view-id))) + +(register-handler-db + :navigate-to + (re-frame/enrich preload-data!) + (fn [db [_ new-view-id]] + (navigate-to db new-view-id))) + +(register-handler-db + :navigate-to-modal + (re-frame/enrich preload-data!) + (fn [db [_ modal-view]] + (assoc db :modal modal-view))) + +(register-handler-db + :navigation-replace + (re-frame/enrich preload-data!) + (fn [db [_ view-id]] + (replace-view db view-id))) + (register-handler-db :navigate-back - (enrich -preload-data!) + (re-frame/enrich -preload-data!) (fn [{:keys [navigation-stack view-id modal] :as db} _] (cond modal (assoc db :modal nil - :was-modal? true) + :was-modal? true) (>= 1 (count navigation-stack)) db :else @@ -78,11 +94,6 @@ (assoc db :view-id first-in-stack))) db)))) -(defn navigate-to-clean [db view-id] - (-> db - (assoc :navigation-stack (list)) - (push-view view-id))) - (register-handler-db :navigate-to-clean (fn [db [_ view-id]] @@ -90,7 +101,7 @@ (register-handler-db :navigate-to-tab - (enrich preload-data!) + (re-frame/enrich preload-data!) (fn [db [_ view-id]] (-> db (assoc :prev-tab-view-id (:view-id db)) diff --git a/src/status_im/ui/screens/profile/events.cljs b/src/status_im/ui/screens/profile/events.cljs index 8d1fcaec79..84b6043046 100644 --- a/src/status_im/ui/screens/profile/events.cljs +++ b/src/status_im/ui/screens/profile/events.cljs @@ -48,7 +48,7 @@ ;; We allow to change phone number only from console because this requires entering SMS verification code. (fn [{:keys [db]} _] (let [phone-command (first (get-in db [:contacts/contacts "console" :responses :phone]))] - {:dispatch-n [[:navigate-to :chat console-chat-id] + {:dispatch-n [[:navigate-to-chat console-chat-id] [:select-chat-input-command phone-command]]}))) (defn get-current-account [{:keys [:accounts/current-account-id] :as db}] diff --git a/src/status_im/ui/screens/wallet/request/events.cljs b/src/status_im/ui/screens/wallet/request/events.cljs index 4018c027c1..417a334ef1 100644 --- a/src/status_im/ui/screens/wallet/request/events.cljs +++ b/src/status_im/ui/screens/wallet/request/events.cljs @@ -4,22 +4,20 @@ [status-im.ui.screens.wallet.db :as wallet.db] [re-frame.core :as re-frame])) -(defn chat-loaded-callback [request-command] - (fn [] - (re-frame/dispatch [:select-chat-input-command request-command]) - ;;TODO get rid of timeout - (js/setTimeout #(re-frame/dispatch [:send-current-message]) 100))) +(handlers/register-handler-fx + ::wallet-send-chat-request + (fn [_ [_ amount]] + {:dispatch [:select-chat-input-command {:name "request" :prefill [amount]}] + ;; TODO get rid of the timeout + :dispatch-later [{:ms 100 :dispatch [:send-current-message]}]})) (handlers/register-handler-fx :wallet-send-request - (fn [{{:wallet/keys [request-transaction] :as db} :db} [_ {:keys [whisper-identity]}]] - (let [request-command (first (get-in db [:contacts/contacts "transactor-personal" :commands :request]))] - {:dispatch-n [[:navigate-back] - [:navigate-to-clean :chat-list] - [:add-chat-loaded-callback whisper-identity (chat-loaded-callback - (assoc request-command - :prefill [(:amount request-transaction)]))] - [:start-chat whisper-identity]]}))) + (fn [{{:wallet/keys [request-transaction]} :db} [_ {:keys [whisper-identity]}]] + {:dispatch-n [[:navigate-back] + [:navigate-to-clean :chat-list] + [:add-chat-loaded-event whisper-identity [::wallet-send-chat-request (:amount request-transaction)]] + [:start-chat whisper-identity]]})) (handlers/register-handler-fx :wallet-validate-request-amount