diff --git a/src/status_im/add_new/core.cljs b/src/status_im/add_new/core.cljs index fe3161a8a8..983214838c 100644 --- a/src/status_im/add_new/core.cljs +++ b/src/status_im/add_new/core.cljs @@ -2,7 +2,7 @@ (:require [clojure.string :as string] [re-frame.core :as re-frame] [status-im.add-new.db :as db] - [status-im.chat.models :as chat] + [status-im2.contexts.chat.events :as chat] [status-im.contact.core :as contact] [status-im.ethereum.core :as ethereum] [status-im.ethereum.ens :as ens] diff --git a/src/status_im/chat/db.cljs b/src/status_im/chat/db.cljs deleted file mode 100644 index fe129cbdf9..0000000000 --- a/src/status_im/chat/db.cljs +++ /dev/null @@ -1,95 +0,0 @@ -(ns status-im.chat.db - (:require [status-im2.constants :as constants])) - -(defn group-chat-name - [{:keys [public? name]}] - (str (when public? "#") name)) - -(defn intersperse-datemark - "Reduce step which expects the input list of messages to be sorted by clock value. - It makes best effort to group them by day. - We cannot sort them by :timestamp, as that represents the clock of the sender - and we have no guarantees on the order. - We naively and arbitrarly group them assuming that out-of-order timestamps - fall in the previous bucket. - A sends M1 to B with timestamp 2000-01-01T00:00:00 - B replies M2 with timestamp 1999-12-31-23:59:59 - M1 needs to be displayed before M2 - so we bucket both in 1999-12-31" - [{:keys [acc last-timestamp last-datemark]} {:keys [whisper-timestamp datemark] :as msg}] - (cond - (empty? acc) ; initial element - {:last-timestamp whisper-timestamp - :last-datemark datemark - :acc (conj acc msg)} - - (and (not= last-datemark datemark) ; not the same day - (< whisper-timestamp last-timestamp)) ; not out-of-order - {:last-timestamp whisper-timestamp - :last-datemark datemark - :acc (conj acc - {:value last-datemark ; intersperse datemark message - :type :datemark} - msg)} - :else - {:last-timestamp (min whisper-timestamp last-timestamp) ; use last datemark - :last-datemark last-datemark - :acc (conj acc (assoc msg :datemark last-datemark))})) - -(defn add-datemarks - "Add a datemark in between an ordered seq of messages when two datemarks are not - the same. Ignore messages with out-of-order timestamps" - [messages] - (when (seq messages) - (let [messages-with-datemarks (:acc (reduce intersperse-datemark {:acc []} messages))] - ; Append last datemark - (conj messages-with-datemarks - {:value (:datemark (peek messages-with-datemarks)) - :type :datemark})))) - -(defn last-gap - "last-gap is a special gap that is put last in the message stream" - [chat-id synced-from] - {:message-id "0x123" - :message-type constants/message-type-gap - :chat-id chat-id - :content-type constants/content-type-gap - :gap-ids #{:first-gap} - :gap-parameters {:from synced-from}}) - -(defn collapse-gaps - "collapse-gaps will take an array of messages and collapse any gap next to - each other in a single gap. - It will also append one last gap if the last message is a non-gap" - [messages chat-id synced-from now chat-type joined loading-messages?] - (let [messages-with-gaps (reduce - (fn [acc {:keys [gap-parameters message-id] :as message}] - (let [last-element (peek acc)] - (cond - ;; If it's a message, just add - (empty? gap-parameters) - (conj acc message) - - ;; Both are gaps, merge them - (and - (seq (:gap-parameters last-element)) - (seq gap-parameters)) - (conj (pop acc) (update last-element :gap-ids conj message-id)) - - ;; it's a gap - :else - (conj acc (assoc message :gap-ids #{message-id}))))) - [] - messages)] - (if (or loading-messages? ; it's loading messages from the database - (nil? synced-from) ; it's still syncing - (= constants/timeline-chat-type chat-type) ; it's a timeline chat - (= constants/profile-chat-type chat-type) ; it's a profile chat - (and (not (nil? synced-from)) ; it's not more than a month - (<= synced-from (- (quot now 1000) constants/one-month))) - (and (= constants/private-group-chat-type chat-type) ; it's a private group chat - (or (not (pos? joined)) ; we haven't joined - (>= (quot joined 1000) synced-from))) ; the history goes before we joined - (:gap-ids (peek messages-with-gaps))) ; there's already a gap on top of the chat history - messages-with-gaps ; don't add an extra gap - (conj messages-with-gaps (last-gap chat-id synced-from))))) diff --git a/src/status_im/chat/db_test.cljs b/src/status_im/chat/db_test.cljs deleted file mode 100644 index edecf0bbc5..0000000000 --- a/src/status_im/chat/db_test.cljs +++ /dev/null @@ -1,44 +0,0 @@ -(ns status-im.chat.db-test - (:require [cljs.test :refer-macros [deftest is testing]] - [status-im.chat.db :as db])) - -(deftest group-chat-name - (testing "it prepends # if it's a public chat" - (is (= "#withhash" - (db/group-chat-name {:group-chat true - :chat-id "1" - :public? true - :name "withhash"})))) - (testing "it leaves the name unchanged if it's a group chat" - (is (= "unchanged" - (db/group-chat-name {:group-chat true - :chat-id "1" - :name "unchanged"}))))) - -(deftest intersperse-datemarks - (testing "it mantains the order even when timestamps are across days" - (let [message-1 {:datemark "Dec 31, 1999" - :whisper-timestamp 946641600000} ; 1999} - message-2 {:datemark "Jan 1, 2000" - :whisper-timestamp 946728000000} ; 2000 this will displayed in 1999 - message-3 {:datemark "Dec 31, 1999" - :whisper-timestamp 946641600000} ; 1999 - message-4 {:datemark "Jan 1, 2000" - :whisper-timestamp 946728000000} ; 2000 - ordered-messages [message-4 - message-3 - message-2 - message-1] - [m1 d1 m2 m3 m4 d2] (db/add-datemarks ordered-messages)] - (is (= "Jan 1, 2000" - (:datemark m1))) - (is (= {:type :datemark - :value "Jan 1, 2000"} - d1)) - (is (= "Dec 31, 1999" - (:datemark m2) - (:datemark m3) - (:datemark m4))) - (is (= {:type :datemark - :value "Dec 31, 1999"} - d2))))) diff --git a/src/status_im/chat/models.cljs b/src/status_im/chat/models.cljs index 543a968809..5f202c3c30 100644 --- a/src/status_im/chat/models.cljs +++ b/src/status_im/chat/models.cljs @@ -1,344 +1,12 @@ (ns status-im.chat.models - (:require [clojure.set :as set] - [quo.design-system.colors :as colors] + (:require [utils.i18n :as i18n] [re-frame.core :as re-frame] - [status-im.add-new.db :as new-public-chat.db] - [status-im.chat.models.loading :as loading] - [status-im2.contexts.chat.messages.list.events :as message-list] - [status-im2.constants :as constants] - [status-im.data-store.chats :as chats-store] - [status-im.data-store.contacts :as contacts-store] - [utils.i18n :as i18n] - [status-im.mailserver.core :as mailserver] - [status-im.multiaccounts.model :as multiaccounts.model] - [status-im2.contexts.chat.messages.list.state :as chat.state] - [status-im.utils.clocks :as utils.clocks] [utils.re-frame :as rf] - [status-im.utils.utils :as utils] - [status-im2.contexts.chat.messages.delete-message-for-me.events :as delete-for-me] - [status-im2.contexts.chat.messages.delete-message.events :as delete-message] - [status-im2.navigation.events :as navigation] - [taoensso.timbre :as log])) + [taoensso.timbre :as log] + [status-im.add-new.db :as new-public-chat.db] + [status-im.data-store.chats :as chats-store])) -(defn- get-chat - [cofx chat-id] - (get-in cofx [:db :chats chat-id])) - -(defn multi-user-chat? - ([chat] - (:group-chat chat)) - ([cofx chat-id] - (multi-user-chat? (get-chat cofx chat-id)))) - -(def one-to-one-chat? - (complement multi-user-chat?)) - -(defn public-chat? - ([chat] - (:public? chat)) - ([cofx chat-id] - (public-chat? (get-chat cofx chat-id)))) - -(defn community-chat? - ([{:keys [chat-type]}] - (= chat-type constants/community-chat-type)) - ([cofx chat-id] - (community-chat? (get-chat cofx chat-id)))) - -(defn active-chat? - [cofx chat-id] - (let [chat (get-chat cofx chat-id)] - (:active chat))) - -(defn foreground-chat? - [{{:keys [current-chat-id view-id]} :db} chat-id] - (and (= current-chat-id chat-id) - (= view-id :chat))) - -(defn group-chat? - ([chat] - (and (multi-user-chat? chat) - (not (public-chat? chat)))) - ([cofx chat-id] - (group-chat? (get-chat cofx chat-id)))) - -(defn timeline-chat? - ([chat] - (:timeline? chat)) - ([cofx chat-id] - (timeline-chat? (get-chat cofx chat-id)))) - -(defn profile-chat? - ([chat] - (:profile-public-key chat)) - ([cofx chat-id] - (profile-chat? (get-chat cofx chat-id)))) - -(defn set-chat-ui-props - "Updates ui-props in active chat by merging provided kvs into them" - [{:keys [current-chat-id] :as db} kvs] - (update-in db [:chat-ui-props current-chat-id] merge kvs)) - -(defn- create-new-chat - [chat-id {:keys [db now]}] - (let [name (get-in db [:contacts/contacts chat-id :name])] - {:chat-id chat-id - :name (or name "") - :color (rand-nth colors/chat-colors) - :chat-type constants/one-to-one-chat-type - :group-chat false - :timestamp now - :contacts #{chat-id} - :last-clock-value 0})) - -(defn map-chats - [{:keys [db] :as cofx}] - (fn [val] - (assoc - (merge - (or (get (:chats db) (:chat-id val)) - (create-new-chat (:chat-id val) cofx)) - val) - :invitation-admin - (:invitation-admin val)))) - -(defn filter-chats - [db] - (fn [val] - (and (not (get-in db [:chats (:chat-id val)])) (:public? val)))) - -(rf/defn leave-removed-chat - [{{:keys [view-id current-chat-id chats]} :db - :as cofx}] - (when (and (= view-id :chat) - (not (contains? chats current-chat-id))) - (navigation/navigate-back cofx))) - -(rf/defn ensure-chats - "Add chats to db and update" - [{:keys [db] :as cofx} chats] - (let [{:keys [all-chats chats-home-list removed-chats]} - (reduce - (fn [acc {:keys [chat-id profile-public-key timeline? community-id active muted] :as chat}] - (if (not (or active muted)) - (update acc :removed-chats conj chat-id) - (cond-> acc - (and (not profile-public-key) (not timeline?) (not community-id) active) - (update :chats-home-list conj chat-id) - :always - (assoc-in [:all-chats chat-id] chat)))) - {:all-chats {} - :chats-home-list #{} - :removed-chats #{}} - (map (map-chats cofx) chats))] - (rf/merge - cofx - (merge {:db (-> db - (update :chats merge all-chats) - (update :chats-home-list set/union chats-home-list) - (update :chats #(apply dissoc % removed-chats)) - (update :chats-home-list set/difference removed-chats))} - (when (not-empty removed-chats) - {:clear-message-notifications - [removed-chats - (get-in db [:multiaccount :remote-push-notifications-enabled?])]})) - leave-removed-chat))) - -(rf/defn clear-history - "Clears history of the particular chat" - [{:keys [db] :as cofx} chat-id remove-chat?] - (let [{:keys [last-message public? - deleted-at-clock-value]} - (get-in db [:chats chat-id]) - last-message-clock-value (if (and public? remove-chat?) - 0 - (or (:clock-value last-message) - deleted-at-clock-value - (utils.clocks/send 0)))] - {:db (-> db - (assoc-in [:messages chat-id] {}) - (update-in [:message-lists] dissoc chat-id) - (update :chats - (fn [chats] - (if (contains? chats chat-id) - (update chats - chat-id - merge - {:last-message nil - :unviewed-messages-count 0 - :unviewed-mentions-count 0 - :deleted-at-clock-value last-message-clock-value}) - chats))))})) - -(rf/defn clear-history-handler - "Clears history of the particular chat" - {:events [:chat.ui/clear-history]} - [{:keys [db] :as cofx} chat-id remove-chat?] - (rf/merge cofx - {:db db - :json-rpc/call [{:method "wakuext_clearHistory" - :params [{:id chat-id}] - :on-success #(re-frame/dispatch [::history-cleared chat-id %]) - :on-error #(log/error "failed to clear history " chat-id %)}]} - (clear-history chat-id remove-chat?))) - -(rf/defn chat-deactivated - {:events [::chat-deactivated]} - [_ chat-id] - (log/debug "chat deactivated" chat-id)) - -(rf/defn deactivate-chat - "Deactivate chat in db, no side effects" - [{:keys [db now] :as cofx} chat-id] - (rf/merge - cofx - {:db (-> (if (get-in db [:chats chat-id :muted]) - (assoc-in db [:chats chat-id :active] false) - (update db :chats dissoc chat-id)) - (update :chats-home-list disj chat-id) - (assoc :current-chat-id nil)) - :json-rpc/call [{:method "wakuext_deactivateChat" - :params [{:id chat-id}] - :on-success #(re-frame/dispatch [::chat-deactivated chat-id]) - :on-error #(log/error "failed to create public chat" chat-id %)}]} - (clear-history chat-id true))) - -(rf/defn offload-messages - {:events [:offload-messages]} - [{:keys [db]} chat-id] - (merge {:db (-> db - (update :messages dissoc chat-id) - (update :message-lists dissoc chat-id) - (update :pagination-info dissoc chat-id))} - (when (and (= chat-id constants/timeline-chat-id) (= (:view-id db) :status)) - {:dispatch [:init-timeline-chat]}))) - -(rf/defn close-chat - {:events [:close-chat]} - [{:keys [db] :as cofx} navigate-to-shell?] - (when-let [chat-id (:current-chat-id db)] - (chat.state/reset-visible-item) - (rf/merge cofx - (merge - {:db (dissoc db :current-chat-id)} - (let [community-id (get-in db [:chats chat-id :community-id])] - ;; When navigating back from community chat to community, update switcher card - (when (and community-id (not navigate-to-shell?)) - {:dispatch [:shell/add-switcher-card - :community {:community-id community-id}]}))) - (delete-for-me/sync-all) - (delete-message/send-all) - (offload-messages chat-id)))) - -(rf/defn force-close-chat - [{:keys [db] :as cofx} chat-id] - (do - (chat.state/reset-visible-item) - (rf/merge cofx - {:db (dissoc db :current-chat-id)} - (offload-messages chat-id)))) - -(rf/defn remove-chat - "Removes chat completely from app, producing all necessary effects for that" - {:events [:chat.ui/remove-chat]} - [{:keys [db now] :as cofx} chat-id] - (rf/merge cofx - {:clear-message-notifications - [[chat-id] (get-in db [:multiaccount :remote-push-notifications-enabled?])] - :dispatch [:shell/close-switcher-card chat-id]} - (deactivate-chat chat-id) - (offload-messages chat-id))) - -(rf/defn show-more-chats - {:events [:chat.ui/show-more-chats]} - [{:keys [db]}] - (when (< (:home-items-show-number db) (count (:chats db))) - {:db (update db :home-items-show-number + 40)})) - -(rf/defn preload-chat-data - "Takes chat-id and coeffects map, returns effects necessary when navigating to chat" - {:events [:chat.ui/preload-chat-data]} - [cofx chat-id] - (loading/load-messages cofx chat-id)) - -(rf/defn navigate-to-chat - "Takes coeffects map and chat-id, returns effects necessary for navigation and preloading data" - {:events [:chat.ui/navigate-to-chat]} - [{db :db :as cofx} chat-id] - (rf/merge cofx - {:dispatch [:navigate-to :chat]} - (navigation/change-tab :chat) - (when-not (= (:view-id db) :community) - (navigation/pop-to-root-tab :chat-stack)) - (close-chat false) - (force-close-chat chat-id) - (fn [{:keys [db]}] - {:db (assoc db :current-chat-id chat-id)}) - (preload-chat-data chat-id) - #(when (group-chat? cofx chat-id) - (loading/load-chat % chat-id)))) - -(rf/defn navigate-to-chat-nav2 - "Takes coeffects map and chat-id, returns effects necessary for navigation and preloading data" - {:events [:chat.ui/navigate-to-chat-nav2]} - [{db :db :as cofx} chat-id from-shell?] - (rf/merge cofx - {:dispatch [:navigate-to-nav2 :chat chat-id from-shell?]} - (when-not (= (:view-id db) :community) - (navigation/pop-to-root-tab :shell-stack)) - (close-chat false) - (force-close-chat chat-id) - (fn [{:keys [db]}] - {:db (assoc db :current-chat-id chat-id)}) - (preload-chat-data chat-id) - #(when (group-chat? cofx chat-id) - (loading/load-chat % chat-id)))) - -(rf/defn handle-clear-history-response - {:events [::history-cleared]} - [{:keys [db]} chat-id response] - (let [chat (chats-store/<-rpc (first (:chats response)))] - {:db (assoc-in db [:chats chat-id] chat)})) - -(rf/defn handle-one-to-one-chat-created - {:events [::one-to-one-chat-created]} - [{:keys [db]} chat-id response] - (let [chat (chats-store/<-rpc (first (:chats response))) - contact-rpc (first (:contacts response)) - contact (when contact-rpc (contacts-store/<-rpc contact-rpc))] - {:db (cond-> db - contact - (assoc-in [:contacts/contacts chat-id] contact) - :always - (assoc-in [:chats chat-id] chat) - :always - (update :chats-home-list conj chat-id)) - :dispatch [:chat.ui/navigate-to-chat-nav2 chat-id]})) - -(rf/defn navigate-to-user-pinned-messages - "Takes coeffects map and chat-id, returns effects necessary for navigation and preloading data" - {:events [:chat.ui/navigate-to-pinned-messages]} - [cofx chat-id] - (navigation/navigate-to cofx :chat-pinned-messages {:chat-id chat-id})) - -(rf/defn start-chat - "Start a chat, making sure it exists" - {:events [:chat.ui/start-chat]} - [{:keys [db] :as cofx} chat-id ens-name] - ;; don't allow to open chat with yourself - (when (not= (multiaccounts.model/current-public-key cofx) chat-id) - {:json-rpc/call [{:method "wakuext_createOneToOneChat" - :params [{:id chat-id :ensName ens-name}] - :on-success #(re-frame/dispatch [::one-to-one-chat-created chat-id %]) - :on-error #(log/error "failed to create one-to-on chat" chat-id %)}]})) - -(defn profile-chat-topic - [public-key] - (str "@" public-key)) - -(defn my-profile-chat-topic - [db] - (profile-chat-topic (get-in db [:multiaccount :public-key]))) +;; OLD (rf/defn handle-public-chat-created {:events [::public-chat-created]} @@ -346,7 +14,7 @@ {:db (-> db (assoc-in [:chats chat-id] (chats-store/<-rpc (first (:chats response)))) (update :chats-home-list conj chat-id)) - :dispatch [:chat.ui/navigate-to-chat chat-id]}) + :dispatch [:chat/navigate-to-chat chat-id]}) (rf/defn create-public-chat-go [_ chat-id] @@ -360,174 +28,8 @@ {:events [:chat.ui/start-public-chat]} [cofx topic] (if (new-public-chat.db/valid-topic? topic) - (if (active-chat? cofx topic) - (navigate-to-chat cofx topic) - (create-public-chat-go - cofx - topic)) + (create-public-chat-go + cofx + topic) {:utils/show-popup {:title (i18n/label :t/cant-open-public-chat) :content (i18n/label :t/invalid-public-chat-topic)}})) - -(rf/defn profile-chat-created - {:events [::profile-chat-created]} - [{:keys [db] :as cofx} chat-id response navigate-to?] - (rf/merge - cofx - {:db db} - #(when response - (let [chat (chats-store/<-rpc (first (:chats response)))] - {:db (assoc-in db [:chats chat-id] chat)})) - #(when navigate-to? - {:dispatch-n [[:chat.ui/preload-chat-data chat-id] - [:open-modal :profile]]}))) - -(rf/defn start-profile-chat - "Starts a new profile chat" - {:events [:start-profile-chat]} - [cofx profile-public-key navigate-to?] - (let [chat-id (profile-chat-topic profile-public-key)] - (if (active-chat? cofx chat-id) - {:dispatch [::profile-chat-created chat-id nil navigate-to?]} - {:json-rpc/call [{:method "wakuext_createProfileChat" - :params [{:id profile-public-key}] - :on-success #(re-frame/dispatch [::profile-chat-created chat-id % navigate-to?]) - :on-error #(log/error "failed to create profile chat" chat-id %)}]}))) - -(rf/defn disable-chat-cooldown - "Turns off chat cooldown (protection against message spamming)" - {:events [:chat/disable-cooldown]} - [{:keys [db]}] - {:db (assoc db :chat/cooldown-enabled? false)}) - -;; effects -(re-frame/reg-fx - :show-cooldown-warning - (fn [_] - (utils/show-popup nil - (i18n/label :cooldown/warning-message) - #()))) - -(rf/defn mute-chat-failed - {:events [::mute-chat-failed]} - [{:keys [db] :as cofx} chat-id muted? error] - (log/error "mute chat failed" chat-id error) - {:db (assoc-in db [:chats chat-id :muted] (not muted?))}) - -(rf/defn mute-chat-toggled-successfully - {:events [::mute-chat-toggled-successfully]} - [_ chat-id] - (log/debug "muted chat successfully" chat-id)) - -(rf/defn mute-chat - {:events [::mute-chat-toggled]} - [{:keys [db] :as cofx} chat-id muted?] - (let [method (if muted? "wakuext_muteChat" "wakuext_unmuteChat")] - {:db (assoc-in db [:chats chat-id :muted] muted?) - :json-rpc/call [{:method method - :params [chat-id] - :on-error #(re-frame/dispatch [::mute-chat-failed chat-id muted? %]) - :on-success #(re-frame/dispatch [::mute-chat-toggled-successfully chat-id])}]})) - -(rf/defn show-profile - {:events [:chat.ui/show-profile]} - [{:keys [db] :as cofx} identity ens-name] - (let [my-public-key (get-in db [:multiaccount :public-key])] - (when (not= my-public-key identity) - (rf/merge - cofx - {:db (-> db - (assoc :contacts/identity identity) - (assoc :contacts/ens-name ens-name))} - (start-profile-chat identity true))))) - -(rf/defn clear-history-pressed - {:events [:chat.ui/clear-history-pressed]} - [_ chat-id] - {:ui/show-confirmation - {:title (i18n/label :t/clear-history-title) - :content (i18n/label :t/clear-history-confirmation-content) - :confirm-button-text (i18n/label :t/clear-history-action) - :on-accept #(do - (re-frame/dispatch [:bottom-sheet/hide]) - (re-frame/dispatch [:chat.ui/clear-history chat-id false]))}}) - -(rf/defn gaps-failed - {:events [::gaps-failed]} - [{:keys [db]} chat-id gap-ids error] - (log/error "failed to fetch gaps" chat-id gap-ids error) - {:db (dissoc db :mailserver/fetching-gaps-in-progress)}) - -(rf/defn sync-chat-from-sync-from-failed - {:events [::sync-chat-from-sync-from-failed]} - [{:keys [db]} chat-id error] - (log/error "failed to sync chat" chat-id error) - {:db (dissoc db :mailserver/fetching-gaps-in-progress)}) - -(rf/defn sync-chat-from-sync-from-success - {:events [::sync-chat-from-sync-from-success]} - [{:keys [db] :as cofx} chat-id synced-from] - (log/debug "synced success" chat-id synced-from) - {:db - (-> db - (assoc-in [:chats chat-id :synced-from] synced-from) - (dissoc :mailserver/fetching-gaps-in-progress))}) - -(rf/defn gaps-filled - {:events [::gaps-filled]} - [{:keys [db] :as cofx} chat-id message-ids] - (rf/merge - cofx - {:db (-> db - (update-in [:messages chat-id] (fn [messages] (apply dissoc messages message-ids))) - (dissoc :mailserver/fetching-gaps-in-progress))} - (message-list/rebuild-message-list chat-id))) - -(rf/defn fill-gaps - [cofx chat-id gap-ids] - {:json-rpc/call [{:method "wakuext_fillGaps" - :params [chat-id gap-ids] - :on-success #(re-frame/dispatch [::gaps-filled chat-id gap-ids %]) - :on-error #(re-frame/dispatch [::gaps-failed chat-id gap-ids %])}]}) - -(rf/defn sync-chat-from-sync-from - [cofx chat-id] - (log/debug "syncing chat from sync from") - {:json-rpc/call [{:method "wakuext_syncChatFromSyncedFrom" - :params [chat-id] - :on-success #(re-frame/dispatch [::sync-chat-from-sync-from-success chat-id %]) - :on-error #(re-frame/dispatch [::sync-chat-from-sync-from-failed chat-id %])}]}) - -(rf/defn chat-ui-fill-gaps - {:events [:chat.ui/fill-gaps]} - [{:keys [db] :as cofx} chat-id gap-ids] - (let [use-status-nodes? (mailserver/fetch-use-mailservers? {:db db})] - (log/info "filling gaps if use-status-nodes = true" chat-id gap-ids) - (when use-status-nodes? - (rf/merge cofx - {:db (assoc db :mailserver/fetching-gaps-in-progress gap-ids)} - (if (= gap-ids #{:first-gap}) - (sync-chat-from-sync-from chat-id) - (fill-gaps chat-id gap-ids)))))) - -(rf/defn chat-ui-remove-chat-pressed - {:events [:chat.ui/remove-chat-pressed]} - [_ chat-id] - {:ui/show-confirmation - {:title (i18n/label :t/delete-confirmation) - :content (i18n/label :t/delete-chat-confirmation) - :confirm-button-text (i18n/label :t/delete) - :on-accept #(do - (re-frame/dispatch [:bottom-sheet/hide]) - (re-frame/dispatch [:chat.ui/remove-chat chat-id]))}}) - -(rf/defn decrease-unviewed-count - {:events [:chat/decrease-unviewed-count]} - [{:keys [db]} chat-id {:keys [count countWithMentions]}] - {:db (-> db - ;; There might be some other requests being fired, - ;; so we need to make sure the count has not been set to - ;; 0 in the meantime - (update-in [:chats chat-id :unviewed-messages-count] - #(max (- % count) 0)) - (update-in [:chats chat-id :unviewed-mentions-count] - #(max (- % countWithMentions) 0)))}) diff --git a/src/status_im/chat/models/gaps.cljs b/src/status_im/chat/models/gaps.cljs new file mode 100644 index 0000000000..b389fcd037 --- /dev/null +++ b/src/status_im/chat/models/gaps.cljs @@ -0,0 +1,63 @@ +(ns status-im.chat.models.gaps + (:require [utils.re-frame :as rf] + [taoensso.timbre :as log] + [status-im2.contexts.chat.messages.list.events :as message-list] + [status-im.mailserver.core :as mailserver])) + +(rf/defn gaps-filled + {:events [:gaps/filled]} + [{:keys [db] :as cofx} chat-id message-ids] + (rf/merge + cofx + {:db (-> db + (update-in [:messages chat-id] (fn [messages] (apply dissoc messages message-ids))) + (dissoc :mailserver/fetching-gaps-in-progress))} + (message-list/rebuild-message-list chat-id))) + +(rf/defn gaps-failed + {:events [:gaps/failed]} + [{:keys [db]} chat-id gap-ids error] + (log/error "failed to fetch gaps" chat-id gap-ids error) + {:db (dissoc db :mailserver/fetching-gaps-in-progress)}) + +(rf/defn sync-chat-from-sync-from-failed + {:events [::sync-chat-from-sync-from-failed]} + [{:keys [db]} chat-id error] + (log/error "failed to sync chat" chat-id error) + {:db (dissoc db :mailserver/fetching-gaps-in-progress)}) + +(rf/defn sync-chat-from-sync-from-success + {:events [::sync-chat-from-sync-from-success]} + [{:keys [db] :as cofx} chat-id synced-from] + (log/debug "synced success" chat-id synced-from) + {:db + (-> db + (assoc-in [:chats chat-id :synced-from] synced-from) + (dissoc :mailserver/fetching-gaps-in-progress))}) + +(rf/defn fill-gaps + [_ chat-id gap-ids] + {:json-rpc/call [{:method "wakuext_fillGaps" + :params [chat-id gap-ids] + :on-success #(rf/dispatch [:gaps/filled chat-id gap-ids %]) + :on-error #(rf/dispatch [:gaps/failed chat-id gap-ids %])}]}) + +(rf/defn sync-chat-from-sync-from + [_ chat-id] + (log/debug "syncing chat from sync from") + {:json-rpc/call [{:method "wakuext_syncChatFromSyncedFrom" + :params [chat-id] + :on-success #(rf/dispatch [::sync-chat-from-sync-from-success chat-id %]) + :on-error #(rf/dispatch [::sync-chat-from-sync-from-failed chat-id %])}]}) + +(rf/defn chat-ui-fill-gaps + {:events [:chat.ui/fill-gaps]} + [{:keys [db] :as cofx} chat-id gap-ids] + (let [use-status-nodes? (mailserver/fetch-use-mailservers? {:db db})] + (log/info "filling gaps if use-status-nodes = true" chat-id gap-ids) + (when use-status-nodes? + (rf/merge cofx + {:db (assoc db :mailserver/fetching-gaps-in-progress gap-ids)} + (if (= gap-ids #{:first-gap}) + (sync-chat-from-sync-from chat-id) + (fill-gaps chat-id gap-ids)))))) diff --git a/src/status_im/chat/models/images.cljs b/src/status_im/chat/models/images.cljs index 5289afc6c0..df10b8a663 100644 --- a/src/status_im/chat/models/images.cljs +++ b/src/status_im/chat/models/images.cljs @@ -3,7 +3,6 @@ ["react-native-blob-util" :default ReactNativeBlobUtil] [clojure.string :as string] [re-frame.core :as re-frame] - [status-im.chat.models :as chat] [utils.i18n :as i18n] [status-im.ui.components.permissions :as permissions] [status-im.ui.components.react :as react] @@ -167,11 +166,6 @@ (let [current-chat-id (or chat-id (:current-chat-id db))] (clear-sending-images cofx current-chat-id))) -(rf/defn cancel-sending-image-timeline - {:events [:chat.ui/cancel-sending-image-timeline]} - [{:keys [db] :as cofx}] - (cancel-sending-image cofx (chat/my-profile-chat-topic db))) - (rf/defn image-selected {:events [:chat.ui/image-selected]} [{:keys [db]} current-chat-id original uri] @@ -191,11 +185,6 @@ (when (< (count images) config/max-images-batch) {::chat-open-image-picker current-chat-id}))) -(rf/defn chat-open-image-picker-timeline - {:events [:chat.ui/open-image-picker-timeline]} - [{:keys [db] :as cofx}] - (chat-open-image-picker cofx (chat/my-profile-chat-topic db))) - (rf/defn chat-show-image-picker-camera {:events [:chat.ui/show-image-picker-camera]} [{:keys [db]} chat-id] @@ -204,11 +193,6 @@ (when (< (count images) config/max-images-batch) {::chat-open-image-picker-camera current-chat-id}))) -(rf/defn chat-show-image-picker-camera-timeline - {:events [:chat.ui/show-image-picker-camera-timeline]} - [{:keys [db] :as cofx}] - (chat-show-image-picker-camera cofx (chat/my-profile-chat-topic db))) - (rf/defn camera-roll-pick {:events [:chat.ui/camera-roll-pick]} [{:keys [db]} uri chat-id] @@ -221,11 +205,6 @@ (not (get images uri))) {::image-selected [uri current-chat-id]})))) -(rf/defn camera-roll-pick-timeline - {:events [:chat.ui/camera-roll-pick-timeline]} - [{:keys [db] :as cofx} uri] - (camera-roll-pick cofx uri (chat/my-profile-chat-topic db))) - (rf/defn save-image-to-gallery {:events [:chat.ui/save-image-to-gallery]} [_ base64-uri] diff --git a/src/status_im/chat/models/input.cljs b/src/status_im/chat/models/input.cljs index 687ac01631..cdf10c18ce 100644 --- a/src/status_im/chat/models/input.cljs +++ b/src/status_im/chat/models/input.cljs @@ -3,14 +3,12 @@ [clojure.string :as string] [goog.object :as object] [re-frame.core :as re-frame] - [status-im.chat.models :as chat] [status-im.chat.models.mentions :as mentions] [status-im.chat.models.message :as chat.message] [status-im.chat.models.message-content :as message-content] [status-im2.constants :as constants] [utils.re-frame :as rf] [utils.i18n :as i18n] - [utils.datetime :as datetime] [status-im.utils.utils :as utils] [taoensso.timbre :as log] [status-im.ui.screens.chat.components.input :as input])) @@ -25,6 +23,14 @@ (.-char ^js emoji-map) original)))) +;; effects +(re-frame/reg-fx + :show-cooldown-warning + (fn [_] + (utils/show-popup nil + (i18n/label :cooldown/warning-message) + #()))) + (rf/defn set-chat-input-text "Set input text for current-chat. Takes db and input text and cofx as arguments and returns new fx. Always clear all validation messages." @@ -33,13 +39,6 @@ (let [current-chat-id (or chat-id (:current-chat-id db))] {:db (assoc-in db [:chat/inputs current-chat-id :input-text] (text->emoji new-input))})) -(rf/defn set-timeline-input-text - {:events [:chat.ui/set-timeline-input-text]} - [{db :db} new-input] - {:db (assoc-in db - [:chat/inputs (chat/my-profile-chat-topic db) :input-text] - (text->emoji new-input))}) - (rf/defn select-mention {:events [:chat.ui/select-mention]} [{:keys [db] :as cofx} text-input-ref {:keys [alias name searched-text match] :as user}] @@ -74,45 +73,11 @@ :end end})) (mentions/recheck-at-idxs {alias user})))) -(defn- start-cooldown - [{:keys [db]} cooldowns] - {:dispatch-later [{:dispatch [:chat/disable-cooldown] - :ms (constants/cooldown-periods-ms - cooldowns - constants/default-cooldown-period-ms)}] - :show-cooldown-warning nil - :db (assoc db - :chat/cooldowns (if - (= - constants/cooldown-reset-threshold - cooldowns) - 0 - cooldowns) - :chat/spam-messages-frequency 0 - :chat/cooldown-enabled? true)}) - -(rf/defn process-cooldown - "Process cooldown to protect against message spammers" - [{{:keys [chat/last-outgoing-message-sent-at - chat/cooldowns - chat/spam-messages-frequency - current-chat-id] - :as db} - :db - :as cofx}] - (when (chat/public-chat? cofx current-chat-id) - (let [spamming-fast? (< (- (datetime/timestamp) last-outgoing-message-sent-at) - (+ constants/spam-interval-ms (* 1000 cooldowns))) - spamming-frequently? (= constants/spam-message-frequency-threshold - spam-messages-frequency)] - (cond-> {:db (assoc db - :chat/last-outgoing-message-sent-at (datetime/timestamp) - :chat/spam-messages-frequency (if spamming-fast? - (inc spam-messages-frequency) - 0))} - - (and spamming-fast? spamming-frequently?) - (start-cooldown (inc cooldowns)))))) +(rf/defn disable-chat-cooldown + "Turns off chat cooldown (protection against message spamming)" + {:events [:chat/disable-cooldown]} + [{:keys [db]}] + {:db (assoc db :chat/cooldown-enabled? false)}) (rf/defn reply-to-message "Sets reference to previous chat message and focuses on input" @@ -222,21 +187,6 @@ (when (seq messages) (rf/merge cofx (clean-input (:current-chat-id db)) - (process-cooldown) - (chat.message/send-messages messages))))) - -(rf/defn send-my-status-message - "when not empty, proceed by sending text message with public key topic" - {:events [:profile.ui/send-my-status-message]} - [{db :db :as cofx}] - (let [current-chat-id (chat/my-profile-chat-topic db) - {:keys [input-text]} (get-in db [:chat/inputs current-chat-id]) - image-messages (build-image-messages cofx current-chat-id) - text-message (build-text-message cofx input-text current-chat-id) - messages (keep identity (conj image-messages text-message))] - (when (seq messages) - (rf/merge cofx - (clean-input current-chat-id) (chat.message/send-messages messages))))) (rf/defn send-audio-message @@ -274,8 +224,7 @@ :js-response true :on-error #(log/error "failed to edit message " %) :on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])}]} - (cancel-message-edit) - (process-cooldown))) + (cancel-message-edit))) (rf/defn send-current-message "Sends message from current chat input" @@ -304,8 +253,7 @@ :on-success #(re-frame/dispatch [:transport/message-sent %])}]} (mentions/clear-mentions) (mentions/clear-cursor) - (clean-input (:current-chat-id db)) - (process-cooldown))) + (clean-input (:current-chat-id db)))) (rf/defn cancel-contact-request "Cancels contact request" @@ -316,8 +264,7 @@ {:db (assoc-in db [:chat/inputs current-chat-id :metadata :sending-contact-request] nil)} (mentions/clear-mentions) (mentions/clear-cursor) - (clean-input (:current-chat-id db)) - (process-cooldown)))) + (clean-input (:current-chat-id db))))) (rf/defn chat-send-sticker {:events [:chat/send-sticker]} diff --git a/src/status_im/chat/models/input_test.cljs b/src/status_im/chat/models/input_test.cljs index 90e9da7156..2c074429bd 100644 --- a/src/status_im/chat/models/input_test.cljs +++ b/src/status_im/chat/models/input_test.cljs @@ -1,79 +1,9 @@ (ns status-im.chat.models.input-test - (:require [cljs.test :refer-macros [deftest is testing]] - [status-im2.constants :as constants] - [status-im.chat.models.input :as input] - [utils.datetime :as datetime])) + (:require [cljs.test :refer-macros [deftest is]] + [status-im.chat.models.input :as input])) (deftest text->emoji (is (nil? (input/text->emoji nil))) (is (= "" (input/text->emoji ""))) (is (= "test" (input/text->emoji "test"))) (is (= "word1 \uD83D\uDC4D word2" (input/text->emoji "word1 :+1: word2")))) - -(deftest process-cooldown-fx - (let [db {:current-chat-id "chat" - :chats {"chat" {:public? true}} - :chat/cooldowns 0 - :chat/spam-messages-frequency 0 - :chat/cooldown-enabled? false}] - (with-redefs [datetime/timestamp (constantly 1527675198542)] - (testing "no spamming detected" - (let [expected {:db (assoc db :chat/last-outgoing-message-sent-at 1527675198542)} - actual (input/process-cooldown {:db db})] - (is (= expected actual)))) - - (testing "spamming detected in 1-1" - (let [db (assoc db - :chats {"chat" {:public? false}} - :chat/spam-messages-frequency constants/spam-message-frequency-threshold - :chat/last-outgoing-message-sent-at (- 1527675198542 900)) - expected nil - actual (input/process-cooldown {:db db})] - (is (= expected actual)))) - - (testing "spamming detected" - (let [db (assoc db - :chat/last-outgoing-message-sent-at (- 1527675198542 900) - :chat/spam-messages-frequency constants/spam-message-frequency-threshold) - expected {:db (assoc db - :chat/last-outgoing-message-sent-at 1527675198542 - :chat/cooldowns 1 - :chat/spam-messages-frequency 0 - :chat/cooldown-enabled? true) - :show-cooldown-warning nil - :dispatch-later [{:dispatch [:chat/disable-cooldown] - :ms (constants/cooldown-periods-ms 1)}]} - actual (input/process-cooldown {:db db})] - (is (= expected actual)))) - - (testing "spamming detected twice" - (let [db (assoc db - :chat/cooldowns 1 - :chat/last-outgoing-message-sent-at (- 1527675198542 900) - :chat/spam-messages-frequency constants/spam-message-frequency-threshold) - expected {:db (assoc db - :chat/last-outgoing-message-sent-at 1527675198542 - :chat/cooldowns 2 - :chat/spam-messages-frequency 0 - :chat/cooldown-enabled? true) - :show-cooldown-warning nil - :dispatch-later [{:dispatch [:chat/disable-cooldown] - :ms (constants/cooldown-periods-ms 2)}]} - actual (input/process-cooldown {:db db})] - (is (= expected actual)))) - - (testing "spamming reaching cooldown threshold" - (let [db (assoc db - :chat/cooldowns (dec constants/cooldown-reset-threshold) - :chat/last-outgoing-message-sent-at (- 1527675198542 900) - :chat/spam-messages-frequency constants/spam-message-frequency-threshold) - expected {:db (assoc db - :chat/last-outgoing-message-sent-at 1527675198542 - :chat/cooldowns 0 - :chat/spam-messages-frequency 0 - :chat/cooldown-enabled? true) - :show-cooldown-warning nil - :dispatch-later [{:dispatch [:chat/disable-cooldown] - :ms (constants/cooldown-periods-ms 3)}]} - actual (input/process-cooldown {:db db})] - (is (= expected actual))))))) diff --git a/src/status_im/chat/models/message.cljs b/src/status_im/chat/models/message.cljs index c9253eff53..f9b4ffacc2 100644 --- a/src/status_im/chat/models/message.cljs +++ b/src/status_im/chat/models/message.cljs @@ -1,11 +1,9 @@ (ns status-im.chat.models.message (:require [clojure.string :as string] [re-frame.core :as re-frame] - [status-im.chat.models :as chat-model] [status-im.chat.models.loading :as chat.loading] [status-im.chat.models.mentions :as mentions] [status-im2.contexts.chat.messages.list.events :as message-list] - [status-im2.constants :as constants] [status-im.data-store.messages :as data-store.messages] [status-im.transport.message.protocol :as protocol] [status-im2.contexts.chat.messages.list.state :as view.state] @@ -56,20 +54,6 @@ {:db db} messages)) -(defn timeline-message? - [db chat-id] - (and - (get-in db [:pagination-info constants/timeline-chat-id :messages-initialized?]) - (or - (= chat-id (chat-model/my-profile-chat-topic db)) - (when-let [pub-key (get-in db [:chats chat-id :profile-public-key])] - (get-in db [:contacts/contacts pub-key :added]))))) - -(defn get-timeline-message - [db chat-id message-js] - (when (timeline-message? db chat-id) - (data-store.messages/<-rpc (types/js->clj message-js)))) - (defn add-message [{:keys [db] :as acc} message-js chat-id message-id cursor-clock-value] (let [{:keys [replace from clock-value] :as message} @@ -148,30 +132,6 @@ (when (seq senders) [{:ms 100 :dispatch [:chat/add-senders-to-chat-users (vals senders)]}]))})) -(defn reduce-js-statuses - [db ^js message-js] - (let [chat-id (.-localChatId message-js) - profile-initialized (get-in db [:pagination-info chat-id :messages-initialized?]) - timeline-message (timeline-message? db chat-id) - old-message (get-in db [:messages chat-id (.-id message-js)])] - (if (and (or profile-initialized timeline-message) (nil? old-message)) - (let [{:keys [message-id] :as message} (data-store.messages/<-rpc (types/js->clj message-js))] - (cond-> db - profile-initialized - (update-in [:messages chat-id] assoc message-id message) - profile-initialized - (update-in [:message-lists chat-id] message-list/add message) - timeline-message - (update-in [:messages constants/timeline-chat-id] assoc message-id message) - timeline-message - (update-in [:message-lists constants/timeline-chat-id] message-list/add message))) - db))) - -(rf/defn process-statuses - {:events [:process-statuses]} - [{:keys [db]} statuses] - {:db (reduce reduce-js-statuses db statuses)}) - (rf/defn update-db-message-status [{:keys [db] :as cofx} chat-id message-id status] (when (get-in db [:messages chat-id message-id]) diff --git a/src/status_im/contact/chat.cljs b/src/status_im/contact/chat.cljs index 1e07706b7f..a77fca2050 100644 --- a/src/status_im/contact/chat.cljs +++ b/src/status_im/contact/chat.cljs @@ -1,16 +1,9 @@ (ns status-im.contact.chat (:require [re-frame.core :as re-frame] - [status-im.chat.models :as chat] [status-im.contact.core :as contact] [utils.re-frame :as rf] [status-im2.navigation.events :as navigation])) -(rf/defn send-message-pressed - {:events [:contact.ui/send-message-pressed] - :interceptors [(re-frame/inject-cofx :random-id-generator)]} - [cofx {:keys [public-key ens-name]}] - (chat/start-chat cofx public-key ens-name)) - (rf/defn contact-code-submitted {:events [:contact.ui/contact-code-submitted] :interceptors [(re-frame/inject-cofx :random-id-generator)]} @@ -19,11 +12,6 @@ (rf/merge cofx #(if new-contact? (contact/add-contact % public-key nickname ens-name) - (chat/start-chat % public-key ens-name)) + {:dispatch [:chat.ui/start-chat public-key ens-name]}) #(when new-contact? (navigation/navigate-back %))))) - -(rf/defn pinned-messages-pressed - {:events [:contact.ui/pinned-messages-pressed]} - [cofx public-key] - (chat/navigate-to-user-pinned-messages cofx public-key)) diff --git a/src/status_im/contact/core.cljs b/src/status_im/contact/core.cljs index a31786f074..02c37e3990 100644 --- a/src/status_im/contact/core.cljs +++ b/src/status_im/contact/core.cljs @@ -39,8 +39,6 @@ blocked (:blocked contact) was-blocked (contact.db/blocked? db public-key)] (cond-> acc - (and added (not was-added)) - (conj [:start-profile-chat public-key]) (and (not (:has-added-us contact)) (= constants/contact-request-state-none (:contact-request-state contact))) @@ -51,7 +49,7 @@ (and blocked (not was-blocked)) (conj [::contact.block/contact-blocked contact chats])))) - [[:offload-messages constants/timeline-chat-id] + [[:chat/offload-messages constants/timeline-chat-id] [:activity-center.notifications/fetch-unread-count]] contacts)] (merge @@ -93,7 +91,7 @@ ens-name #(do (re-frame/dispatch [:sanitize-messages-and-process-response %]) - (re-frame/dispatch [:offload-messages constants/timeline-chat-id]))))) + (re-frame/dispatch [:chat/offload-messages constants/timeline-chat-id]))))) (rf/defn remove-contact "Remove a contact from current account's contact list" @@ -109,7 +107,7 @@ {:method "wakuext_retractContactRequest" :params [{:contactId public-key}] :on-success #(log/debug "contact removed successfully")}] - :dispatch [:offload-messages constants/timeline-chat-id]}) + :dispatch [:chat/offload-messages constants/timeline-chat-id]}) (rf/defn accept-contact-request {:events [:contact-requests.ui/accept-request]} diff --git a/src/status_im/events.cljs b/src/status_im/events.cljs index 9ceddd923e..8576152780 100644 --- a/src/status_im/events.cljs +++ b/src/status_im/events.cljs @@ -8,7 +8,7 @@ status-im.bootnodes.core status-im.browser.core status-im.browser.permissions - [status-im.chat.models :as chat] + status-im.chat.models status-im.chat.models.images status-im.chat.models.input status-im.chat.models.loading @@ -184,12 +184,6 @@ [{:keys [db]} dimensions] {:db (assoc db :dimensions/window (dimensions/window dimensions))}) -(rf/defn init-timeline-chat - {:events [:init-timeline-chat]} - [{:keys [db] :as cofx}] - (when-not (get-in db [:pagination-info constants/timeline-chat-id :messages-initialized?]) - (chat/preload-chat-data cofx constants/timeline-chat-id))) - (rf/defn on-will-focus {:events [:screens/on-will-focus]} [{:keys [db] :as cofx} view-id] diff --git a/src/status_im/group_chats/core.cljs b/src/status_im/group_chats/core.cljs index f382da3b69..f50c56fcc5 100644 --- a/src/status_im/group_chats/core.cljs +++ b/src/status_im/group_chats/core.cljs @@ -5,7 +5,7 @@ [utils.i18n :as i18n] [oops.core :as oops] [re-frame.core :as re-frame] - [status-im.chat.models :as models.chat] + [status-im2.contexts.chat.events :as chat.events] [status-im2.constants :as constants] [status-im2.contexts.activity-center.events :as activity-center] [status-im2.navigation.events :as navigation] @@ -15,7 +15,7 @@ {:events [:navigate-chat-updated]} [cofx chat-id] (when (get-in cofx [:db :chats chat-id]) - (models.chat/navigate-to-chat-nav2 cofx chat-id nil))) + (chat.events/navigate-to-chat cofx chat-id nil))) (rf/defn handle-chat-removed {:events [:chat-removed]} @@ -76,7 +76,7 @@ (rf/defn create-from-link [cofx {:keys [chat-id invitation-admin chat-name]}] (if (get-in cofx [:db :chats chat-id]) - {:dispatch [:chat.ui/navigate-to-chat-nav2 chat-id]} + {:dispatch [:chat/navigate-to-chat chat-id]} {:json-rpc/call [{:method "wakuext_createGroupChatFromInvitation" :params [chat-name chat-id invitation-admin] :js-response true @@ -123,7 +123,7 @@ {:events [:group-chats.ui/remove-chat-confirmed]} [cofx chat-id] (rf/merge cofx - (models.chat/deactivate-chat chat-id) + (chat.events/deactivate-chat chat-id) (navigation/pop-to-root-tab :chat-stack))) (def not-blank? diff --git a/src/status_im/integration_test.cljs b/src/status_im/integration_test.cljs index f816b75645..4d862895dd 100644 --- a/src/status_im/integration_test.cljs +++ b/src/status_im/integration_test.cljs @@ -3,9 +3,9 @@ [clojure.string :as string] [day8.re-frame.test :as rf-test] [re-frame.core :as rf] - [status-im.chat.models :as chat.models] [status-im.ethereum.core :as ethereum] status-im.events + status-im2.events [status-im.multiaccounts.logout.core :as logout] [status-im.transport.core :as transport] [status-im.utils.test :as utils.test] @@ -250,8 +250,8 @@ (assert-messenger-started) (rf/dispatch-sync [:chat.ui/start-chat chat-id]) ;; start a new chat (rf-test/wait-for - [:status-im.chat.models/one-to-one-chat-created] - (rf/dispatch-sync [:chat.ui/navigate-to-chat-nav2 chat-id]) + [:chat/one-to-one-chat-created] + (rf/dispatch-sync [:chat/navigate-to-chat chat-id]) (is (= chat-id @(rf/subscribe [:chats/current-chat-id]))) (logout!) (rf-test/wait-for [::logout/logout-method] ; we need to logout to make sure the node is not in @@ -275,20 +275,17 @@ (assert-messenger-started) (rf/dispatch-sync [:chat.ui/start-chat chat-id]) ;; start a new chat (rf-test/wait-for - [:status-im.chat.models/one-to-one-chat-created] - (rf/dispatch-sync [:chat.ui/navigate-to-chat-nav2 chat-id]) + [:chat/one-to-one-chat-created] + (rf/dispatch-sync [:chat/navigate-to-chat chat-id]) (is (= chat-id @(rf/subscribe [:chats/current-chat-id]))) (is @(rf/subscribe [:chats/chat chat-id])) - (rf/dispatch-sync [:chat.ui/remove-chat-pressed chat-id]) + (rf/dispatch-sync [:chat.ui/show-remove-confirmation chat-id]) (rf/dispatch-sync [:chat.ui/remove-chat chat-id]) - (rf-test/wait-for - [::chat.models/chat-deactivated] - (is (not @(rf/subscribe [:chats/chat chat-id]))) - (logout!) - (rf-test/wait-for [::logout/logout-method] ; we need to logout to make sure the node is not - ; in an - ; inconsistent state between tests - (assert-logout))))))))) + (logout!) + (rf-test/wait-for [::logout/logout-method] ; we need to logout to make sure the node is not + ; in an + ; inconsistent state between tests + (assert-logout)))))))) (deftest mute-chat-test (log/info "========= mute-chat-test ==================") @@ -306,18 +303,17 @@ (assert-messenger-started) (rf/dispatch-sync [:chat.ui/start-chat chat-id]) ;; start a new chat (rf-test/wait-for - [:status-im.chat.models/one-to-one-chat-created] - (rf/dispatch-sync [:chat.ui/navigate-to-chat-nav2 chat-id]) + [:chat/one-to-one-chat-created] + (rf/dispatch-sync [:chat/navigate-to-chat chat-id]) (is (= chat-id @(rf/subscribe [:chats/current-chat-id]))) (is @(rf/subscribe [:chats/chat chat-id])) - (rf/dispatch-sync [::chat.models/mute-chat-toggled chat-id true]) + (rf/dispatch-sync [:chat.ui/mute chat-id true]) (rf-test/wait-for - [::chat.models/mute-chat-toggled-successfully] + [:chat/mute-successfully] (is @(rf/subscribe [:chats/muted chat-id])) - (rf/dispatch-sync [::chat.models/mute-chat-toggled chat-id false]) + (rf/dispatch-sync [:chat.ui/mute chat-id false]) (rf-test/wait-for - [::chat.models/mute-chat-toggled-successfully] - + [:chat/mute-successfully] (is (not @(rf/subscribe [:chats/muted chat-id]))) (logout!) (rf-test/wait-for [::logout/logout-method] ; we need to logout to make sure the node is diff --git a/src/status_im/multiaccounts/login/core.cljs b/src/status_im/multiaccounts/login/core.cljs index a1fe2b7971..2464dae28c 100644 --- a/src/status_im/multiaccounts/login/core.cljs +++ b/src/status_im/multiaccounts/login/core.cljs @@ -424,7 +424,7 @@ :key-uid (fn [stored-key-uid] (when (= stored-key-uid key-uid) - (re-frame/dispatch [:chat.ui/navigate-to-chat-nav2 chat-id]))))))))) + (re-frame/dispatch [:chat/navigate-to-chat chat-id]))))))))) (rf/defn check-last-chat {:events [::check-last-chat]} diff --git a/src/status_im/navigation/core.cljs b/src/status_im/navigation/core.cljs index f05bc4f493..bd8e04a3d2 100644 --- a/src/status_im/navigation/core.cljs +++ b/src/status_im/navigation/core.cljs @@ -303,7 +303,7 @@ (re-frame/dispatch [:set :current-tab tab-key]) (when (= @state/root-comp-id comp) (when (= :chat tab-key) - (re-frame/dispatch [:close-chat])) + (re-frame/dispatch [:chat/close])) (when platform/android? (.popToRoot Navigation (name comp)))) (reset! state/root-comp-id comp))))) diff --git a/src/status_im/notifications/local.cljs b/src/status_im/notifications/local.cljs index 0356f51b59..f5f3187d62 100644 --- a/src/status_im/notifications/local.cljs +++ b/src/status_im/notifications/local.cljs @@ -5,7 +5,6 @@ [quo.platform :as platform] [re-frame.core :as re-frame] [status-im.async-storage.core :as async-storage] - [status-im.chat.models :as chat.models] [status-im.ethereum.decode :as decode] [status-im.ethereum.tokens :as tokens] [utils.i18n :as i18n] @@ -105,6 +104,11 @@ :user-info notification :message description})) +(defn foreground-chat? + [{{:keys [current-chat-id view-id]} :db} chat-id] + (and (= current-chat-id chat-id) + (= view-id :chat))) + (defn show-message-pn? [{{:keys [app-state multiaccount]} :db :as cofx} notification] @@ -113,7 +117,7 @@ (and (not= notification-author (:public-key multiaccount)) (or (= app-state "background") - (not (chat.models/foreground-chat? cofx chat-id)))))) + (not (foreground-chat? cofx chat-id)))))) (defn create-notification ([notification] diff --git a/src/status_im/profile/core.cljs b/src/status_im/profile/core.cljs index f58c2886b2..c07561cdd1 100644 --- a/src/status_im/profile/core.cljs +++ b/src/status_im/profile/core.cljs @@ -84,3 +84,12 @@ {:events [:profile/share-profile-link]} [_ value] {:profile/share-profile-link value}) + +(rf/defn show-profile + {:events [:chat.ui/show-profile]} + [{:keys [db]} identity ens-name] + (let [my-public-key (get-in db [:multiaccount :public-key])] + (when (not= my-public-key identity) + {:db (-> db + (assoc :contacts/identity identity) + (assoc :contacts/ens-name ens-name))}))) diff --git a/src/status_im/qr_scanner/core.cljs b/src/status_im/qr_scanner/core.cljs index 76cb335b85..9ce28137b3 100644 --- a/src/status_im/qr_scanner/core.cljs +++ b/src/status_im/qr_scanner/core.cljs @@ -47,7 +47,7 @@ (rf/defn handle-private-chat [{:keys [db] :as cofx} {:keys [chat-id]}] (if-not (new-chat.db/own-public-key? db chat-id) - (chat/start-chat cofx chat-id nil) + {:dispatch [:chat.ui/start-chat chat-id]} {:utils/show-popup {:title (i18n/label :t/unable-to-read-this-code) :content (i18n/label :t/can-not-add-yourself)}})) diff --git a/src/status_im/router/core.cljs b/src/status_im/router/core.cljs index bb5c16cbe5..67aba5927c 100644 --- a/src/status_im/router/core.cljs +++ b/src/status_im/router/core.cljs @@ -3,7 +3,7 @@ [clojure.string :as string] [re-frame.core :as re-frame] [status-im.add-new.db :as public-chat.db] - [status-im.chat.models :as chat.models] + [status-im2.contexts.chat.events :as chat.events] [status-im2.constants :as constants] [status-im.ethereum.core :as ethereum] [status-im.ethereum.eip681 :as eip681] @@ -121,7 +121,7 @@ :chat-name chat-name} (and (not (string/blank? chat-id)) - (chat.models/group-chat? (get chats chat-id))) + (chat.events/group-chat? (get chats chat-id))) (let [{:keys [chat-name invitation-admin]} (get chats chat-id)] {:type :group-chat :chat-id chat-id diff --git a/src/status_im/transport/message/core.cljs b/src/status_im/transport/message/core.cljs index c17e5463ed..66a9ee53fd 100644 --- a/src/status_im/transport/message/core.cljs +++ b/src/status_im/transport/message/core.cljs @@ -2,7 +2,7 @@ (:require [clojure.string :as string] [status-im.browser.core :as browser] - [status-im.chat.models :as models.chat] + [status-im2.contexts.chat.events :as chat.events] [status-im.chat.models.message :as models.message] [status-im.chat.models.reactions :as models.reactions] [status-im.communities.core :as models.communities] @@ -61,7 +61,7 @@ (js-delete response-js "chats") (rf/merge cofx (process-next response-js sync-handler) - (models.chat/ensure-chats (map data-store.chats/<-rpc (types/js->clj chats))))) + (chat.events/ensure-chats (map data-store.chats/<-rpc (types/js->clj chats))))) (seq messages) (models.message/receive-many cofx response-js) @@ -200,12 +200,10 @@ message-type (.-messageType message-js) from (.-from message-js) mentioned (.-mentioned message-js) - profile (models.chat/profile-chat? {:db db} chat-id) new (.-new message-js) current (= current-chat-id chat-id) should-update-unviewed? (and (not current) new - (not profile) (not (= message-type constants/message-type-private-group-system-message)) (not (= from (multiaccounts.model/current-public-key {:db db})))) @@ -215,9 +213,6 @@ current (update :messages conj message-js) - profile - (update :statuses conj message-js) - ;;update counter should-update-unviewed? (update-in [:db :chats chat-id :unviewed-messages-count] inc) diff --git a/src/status_im/ui/screens/chat/message/gap.cljs b/src/status_im/ui/screens/chat/message/gap.cljs index 7fe7b05ca3..58baf6d453 100644 --- a/src/status_im/ui/screens/chat/message/gap.cljs +++ b/src/status_im/ui/screens/chat/message/gap.cljs @@ -7,11 +7,6 @@ [status-im.ui.screens.chat.styles.input.gap :as style] [utils.datetime :as datetime])) -(defn on-press - [chat-id gap-ids] - (fn [] - (re-frame/dispatch [:chat.ui/fill-gaps chat-id gap-ids]))) - (views/defview gap [{:keys [gap-ids chat-id gap-parameters public? community?]}] (views/letsubs [in-progress? [:chats/fetching-gap-in-progress? @@ -25,7 +20,7 @@ [react/view {:style (when-not in-progress? style/gap-container)} [react/touchable-highlight {:on-press (when (and (not in-progress?) use-status-nodes? connected?) - (on-press chat-id gap-ids)) + (re-frame/dispatch [:chat.ui/fill-gaps chat-id gap-ids])) :style {:height (if in-progress? window-height 48)}} [react/view {:style style/label-container} (if in-progress? diff --git a/src/status_im/ui/screens/chat/sheets.cljs b/src/status_im/ui/screens/chat/sheets.cljs index 0bbb88d5aa..7adc1bd7dd 100644 --- a/src/status_im/ui/screens/chat/sheets.cljs +++ b/src/status_im/ui/screens/chat/sheets.cljs @@ -40,7 +40,7 @@ :title (i18n/label :t/delete-chat) :accessibility-label :delete-chat-button :icon :main-icons/delete - :on-press #(re-frame/dispatch [:chat.ui/remove-chat-pressed chat-id])}]])) + :on-press #(re-frame/dispatch [:chat.ui/show-remove-confirmation chat-id])}]])) (defn public-chat-accents [chat-id] @@ -69,13 +69,13 @@ :title (i18n/label :t/clear-history) :accessibility-label :clear-history-button :icon :main-icons/close - :on-press #(re-frame/dispatch [:chat.ui/clear-history-pressed chat-id])}] + :on-press #(re-frame/dispatch [:chat.ui/show-clear-history-confirmation chat-id])}] [quo/list-item {:theme :negative :title (i18n/label :t/delete-chat) :accessibility-label :delete-chat-button :icon :main-icons/delete - :on-press #(re-frame/dispatch [:chat.ui/remove-chat-pressed chat-id])}]])) + :on-press #(re-frame/dispatch [:chat.ui/show-remove-confirmation chat-id])}]])) (defn community-chat-accents [] diff --git a/src/status_im/ui/screens/communities/channel_details.cljs b/src/status_im/ui/screens/communities/channel_details.cljs index 2871d652a5..e901e750b9 100644 --- a/src/status_im/ui/screens/communities/channel_details.cljs +++ b/src/status_im/ui/screens/communities/channel_details.cljs @@ -64,4 +64,5 @@ :accessory :text :accessory-text (count pinned-messages) :chevron true - :on-press #(re-frame/dispatch [:contact.ui/pinned-messages-pressed chat-id])}]])])))) + :on-press #(re-frame/dispatch [:chat.ui/navigate-to-pinned-messages + chat-id])}]])])))) diff --git a/src/status_im/ui/screens/communities/community.cljs b/src/status_im/ui/screens/communities/community.cljs index bb5690b2e1..2247f2a6a2 100644 --- a/src/status_im/ui/screens/communities/community.cljs +++ b/src/status_im/ui/screens/communities/community.cljs @@ -171,7 +171,7 @@ (assoc home-item :public? true) {:on-press (fn [] (rf/dispatch [:dismiss-keyboard]) - (rf/dispatch [:chat.ui/navigate-to-chat-nav2 chat-id]) + (rf/dispatch [:chat/navigate-to-chat chat-id]) (rf/dispatch [:search/home-filter-changed nil])) :on-long-press #(rf/dispatch [:bottom-sheet/show-sheet {:content (fn [] diff --git a/src/status_im/ui/screens/home/views.cljs b/src/status_im/ui/screens/home/views.cljs index a6484f6a18..3648d6a8b5 100644 --- a/src/status_im/ui/screens/home/views.cljs +++ b/src/status_im/ui/screens/home/views.cljs @@ -151,7 +151,7 @@ home-item {:on-press (fn [] (re-frame/dispatch [:dismiss-keyboard]) - (re-frame/dispatch [:chat.ui/navigate-to-chat-nav2 chat-id]) + (re-frame/dispatch [:chat/navigate-to-chat chat-id]) (re-frame/dispatch [:search/home-filter-changed nil])) :on-long-press #(re-frame/dispatch [:bottom-sheet/show-sheet {:content (fn [] @@ -166,7 +166,7 @@ home-item {:on-press (fn [] (re-frame/dispatch [:dismiss-keyboard]) - (re-frame/dispatch [:chat.ui/navigate-to-chat-nav2 chat-id]) + (re-frame/dispatch [:chat/navigate-to-chat chat-id]) (re-frame/dispatch [:search/home-filter-changed nil])) :on-long-press #(re-frame/dispatch [:bottom-sheet/show-sheet {:content (fn [] @@ -193,7 +193,7 @@ [list/flat-list {:key-fn chat-list-key-fn :getItemLayout get-item-layout - :on-end-reached #(re-frame/dispatch [:chat.ui/show-more-chats]) + :on-end-reached #(re-frame/dispatch [:chat/show-more-chats]) :keyboard-should-persist-taps :always :data items :render-fn render-fn @@ -219,7 +219,7 @@ [list/flat-list {:key-fn chat-list-key-fn :getItemLayout get-item-layout - :on-end-reached #(re-frame/dispatch [:chat.ui/show-more-chats]) + :on-end-reached #(re-frame/dispatch [:chat/show-more-chats]) :keyboard-should-persist-taps :always :data items :render-fn render-fn-old diff --git a/src/status_im/ui/screens/profile/contact/views.cljs b/src/status_im/ui/screens/profile/contact/views.cljs index b20b333dd6..306bd44f11 100644 --- a/src/status_im/ui/screens/profile/contact/views.cljs +++ b/src/status_im/ui/screens/profile/contact/views.cljs @@ -5,7 +5,6 @@ [quo.design-system.colors :as colors] [re-frame.core :as re-frame] [reagent.core :as reagent] - [status-im.chat.models :as chat.models] [utils.i18n :as i18n] [status-im.multiaccounts.core :as multiaccounts] [status-im.ui.components.chat-icon.screen :as chat-icon] @@ -25,7 +24,7 @@ (concat [{:label (i18n/label :t/chat) :icon :main-icons/message :disabled (not mutual?) - :action #(re-frame/dispatch [:contact.ui/send-message-pressed + :action #(re-frame/dispatch [:chat.ui/start-chat {:public-key public-key :ens-name ens-name}]) :accessibility-label :start-conversation-button}] @@ -48,8 +47,7 @@ :selected muted? :disabled blocked? :action (when-not blocked? - #(re-frame/dispatch [::chat.models/mute-chat-toggled public-key - (not muted?)]))}] + #(re-frame/dispatch [:chat.ui/mute public-key (not muted?)]))}] [{:label (i18n/label (if blocked? :t/unblock :t/block)) :negative true :selected blocked? @@ -102,7 +100,7 @@ :accessory :text :accessory-text pin-count :disabled (zero? pin-count) - :on-press #(re-frame/dispatch [:contact.ui/pinned-messages-pressed public-key]) + :on-press #(re-frame/dispatch [:chat.ui/navigate-to-pinned-messages public-key]) :chevron true}]) (defn nickname-settings diff --git a/src/status_im/ui/screens/profile/group_chat/views.cljs b/src/status_im/ui/screens/profile/group_chat/views.cljs index b9cf0ed779..806f3e363c 100644 --- a/src/status_im/ui/screens/profile/group_chat/views.cljs +++ b/src/status_im/ui/screens/profile/group_chat/views.cljs @@ -215,7 +215,7 @@ :accessory :text :accessory-text (count pinned-messages) :chevron true - :on-press #(re-frame/dispatch [:contact.ui/pinned-messages-pressed chat-id])}] + :on-press #(re-frame/dispatch [:chat.ui/navigate-to-pinned-messages chat-id])}] (when member? [quo/list-item {:theme :negative diff --git a/src/status_im/utils/universal_links/core.cljs b/src/status_im/utils/universal_links/core.cljs index 5e51764b2b..9e82086cd1 100644 --- a/src/status_im/utils/universal_links/core.cljs +++ b/src/status_im/utils/universal_links/core.cljs @@ -65,7 +65,7 @@ (log/info "universal-links: handling private chat" chat-id) (when chat-id (if-not (new-chat.db/own-public-key? db chat-id) - (chat/start-chat cofx chat-id nil) + {:dispatch [:chat.ui/start-chat chat-id]} {:utils/show-popup {:title (i18n/label :t/unable-to-read-this-code) :content (i18n/label :t/can-not-add-yourself)}}))) @@ -96,7 +96,7 @@ (rf/defn handle-community-chat [cofx {:keys [chat-id]}] (log/info "universal-links: handling community chat" chat-id) - {:dispatch [:chat.ui/navigate-to-chat-nav2 chat-id]}) + {:dispatch [:chat/navigate-to-chat chat-id]}) (rf/defn handle-public-chat [cofx {:keys [topic]}] diff --git a/src/status_im2/common/home/actions/view.cljs b/src/status_im2/common/home/actions/view.cljs index c576988e7b..67ad783e87 100644 --- a/src/status_im2/common/home/actions/view.cljs +++ b/src/status_im2/common/home/actions/view.cljs @@ -1,9 +1,7 @@ (ns status-im2.common.home.actions.view (:require [utils.i18n :as i18n] [quo2.components.drawers.action-drawers :as drawer] - [status-im.chat.models :as chat.models] - [status-im2.common.confirmation-drawer.view :as confirmation-drawer] ;;TODO move to - ;;status-im2 + [status-im2.common.confirmation-drawer.view :as confirmation-drawer] [status-im2.constants :as constants] [utils.re-frame :as rf])) @@ -43,11 +41,11 @@ (defn mute-chat-action [chat-id] - (hide-sheet-and-dispatch [::chat.models/mute-chat-toggled chat-id true])) + (hide-sheet-and-dispatch [:chat.ui/mute chat-id true])) (defn unmute-chat-action [chat-id] - (hide-sheet-and-dispatch [::chat.models/mute-chat-toggled chat-id false])) + (hide-sheet-and-dispatch [:chat.ui/mute chat-id false])) (defn clear-history-action [{:keys [chat-id] :as item}] diff --git a/src/status_im2/contexts/activity_center/notification/contact_request/view.cljs b/src/status_im2/contexts/activity_center/notification/contact_request/view.cljs index 21312d5789..9c5d45a5ae 100644 --- a/src/status_im2/contexts/activity_center/notification/contact_request/view.cljs +++ b/src/status_im2/contexts/activity_center/notification/contact_request/view.cljs @@ -13,14 +13,14 @@ pressable (case (:contact-request-state message) constants/contact-request-message-state-accepted ;; NOTE(2022-09-21): We need to dispatch to - ;; `:contact.ui/send-message-pressed` instead of - ;; `:chat.ui/navigate-to-chat`, otherwise the chat screen + ;; `:chat.ui/start-chat` instead of + ;; `:chat/navigate-to-chat`, otherwise the chat screen ;; looks completely broken if it has never been opened ;; before for the accepted contact. [rn/touchable-opacity {:on-press (fn [] (rf/dispatch [:hide-popover]) - (rf/dispatch [:contact.ui/send-message-pressed + (rf/dispatch [:chat.ui/start-chat {:public-key author}]))}] [:<>])] (conj diff --git a/src/status_im2/contexts/activity_center/notification/mentions/view.cljs b/src/status_im2/contexts/activity_center/notification/mentions/view.cljs index 98ef916652..2e3081286c 100644 --- a/src/status_im2/contexts/activity_center/notification/mentions/view.cljs +++ b/src/status_im2/contexts/activity_center/notification/mentions/view.cljs @@ -46,7 +46,7 @@ [rn/touchable-opacity {:on-press (fn [] (rf/dispatch [:hide-popover]) - (rf/dispatch [:chat.ui/navigate-to-chat-nav2 chat-id]))} + (rf/dispatch [:chat/navigate-to-chat chat-id]))} [quo/activity-log {:title (i18n/label :t/mention) :icon :i/mention diff --git a/src/status_im2/contexts/activity_center/notification/reply/view.cljs b/src/status_im2/contexts/activity_center/notification/reply/view.cljs index 39d595ecaf..ef07cd8bc3 100644 --- a/src/status_im2/contexts/activity_center/notification/reply/view.cljs +++ b/src/status_im2/contexts/activity_center/notification/reply/view.cljs @@ -38,7 +38,7 @@ [rn/touchable-opacity {:on-press (fn [] (rf/dispatch [:hide-popover]) - (rf/dispatch [:chat.ui/navigate-to-chat-nav2 chat-id]))} + (rf/dispatch [:chat/navigate-to-chat chat-id]))} [quo/activity-log {:title (i18n/label :t/message-reply) :icon :i/reply diff --git a/src/status_im2/contexts/chat/events.cljs b/src/status_im2/contexts/chat/events.cljs new file mode 100644 index 0000000000..7f16e51c5e --- /dev/null +++ b/src/status_im2/contexts/chat/events.cljs @@ -0,0 +1,328 @@ +(ns status-im2.contexts.chat.events + (:require [clojure.set :as set] + [utils.i18n :as i18n] + [utils.re-frame :as rf] + [taoensso.timbre :as log] + [status-im2.contexts.chat.messages.list.state :as chat.state] + [status-im2.contexts.chat.messages.delete-message-for-me.events :as delete-for-me] + [status-im2.contexts.chat.messages.delete-message.events :as delete-message] + [status-im2.navigation.events :as navigation] + [status-im2.constants :as constants] + [status-im.chat.models.loading :as loading] + [status-im.data-store.chats :as chats-store] + [status-im.data-store.contacts :as contacts-store] + [status-im.multiaccounts.model :as multiaccounts.model] + [status-im.utils.clocks :as utils.clocks])) + +(defn- get-chat + [cofx chat-id] + (get-in cofx [:db :chats chat-id])) + +(defn multi-user-chat? + ([chat] + (:group-chat chat)) + ([cofx chat-id] + (multi-user-chat? (get-chat cofx chat-id)))) + +(defn public-chat? + ([chat] + (:public? chat)) + ([cofx chat-id] + (public-chat? (get-chat cofx chat-id)))) + +(defn community-chat? + ([{:keys [chat-type]}] + (= chat-type constants/community-chat-type)) + ([cofx chat-id] + (community-chat? (get-chat cofx chat-id)))) + +(defn active-chat? + [cofx chat-id] + (let [chat (get-chat cofx chat-id)] + (:active chat))) + +(defn group-chat? + ([chat] + (and (multi-user-chat? chat) + (not (public-chat? chat)))) + ([cofx chat-id] + (group-chat? (get-chat cofx chat-id)))) + +(defn- create-new-chat + [chat-id {:keys [db now]}] + (let [name (get-in db [:contacts/contacts chat-id :name])] + {:chat-id chat-id + :name (or name "") + :chat-type constants/one-to-one-chat-type + :group-chat false + :timestamp now + :contacts #{chat-id} + :last-clock-value 0})) + +(defn map-chats + [{:keys [db] :as cofx}] + (fn [val] + (assoc + (merge + (or (get (:chats db) (:chat-id val)) + (create-new-chat (:chat-id val) cofx)) + val) + :invitation-admin + (:invitation-admin val)))) + +(rf/defn leave-removed-chat + [{{:keys [view-id current-chat-id chats]} :db + :as cofx}] + (when (and (= view-id :chat) + (not (contains? chats current-chat-id))) + (navigation/navigate-back cofx))) + +(rf/defn ensure-chats + "Add chats to db and update" + [{:keys [db] :as cofx} chats] + (let [{:keys [all-chats chats-home-list removed-chats]} + (reduce + (fn [acc {:keys [chat-id profile-public-key timeline? community-id active muted] :as chat}] + (if (not (or active muted)) + (update acc :removed-chats conj chat-id) + (cond-> acc + (and (not profile-public-key) (not timeline?) (not community-id) active) + (update :chats-home-list conj chat-id) + :always + (assoc-in [:all-chats chat-id] chat)))) + {:all-chats {} + :chats-home-list #{} + :removed-chats #{}} + (map (map-chats cofx) chats))] + (rf/merge + cofx + (merge {:db (-> db + (update :chats merge all-chats) + (update :chats-home-list set/union chats-home-list) + (update :chats #(apply dissoc % removed-chats)) + (update :chats-home-list set/difference removed-chats))} + (when (not-empty removed-chats) + {:clear-message-notifications + [removed-chats + (get-in db [:multiaccount :remote-push-notifications-enabled?])]})) + leave-removed-chat))) + +(rf/defn clear-history + "Clears history of the particular chat" + [{:keys [db]} chat-id remove-chat?] + (let [{:keys [last-message public? + deleted-at-clock-value]} + (get-in db [:chats chat-id]) + last-message-clock-value (if (and public? remove-chat?) + 0 + (or (:clock-value last-message) + deleted-at-clock-value + (utils.clocks/send 0)))] + {:db (-> db + (assoc-in [:messages chat-id] {}) + (update-in [:message-lists] dissoc chat-id) + (update :chats + (fn [chats] + (if (contains? chats chat-id) + (update chats + chat-id + merge + {:last-message nil + :unviewed-messages-count 0 + :unviewed-mentions-count 0 + :deleted-at-clock-value last-message-clock-value}) + chats))))})) + +(rf/defn deactivate-chat + "Deactivate chat in db, no side effects" + [{:keys [db now] :as cofx} chat-id] + (rf/merge + cofx + {:db (-> (if (get-in db [:chats chat-id :muted]) + (assoc-in db [:chats chat-id :active] false) + (update db :chats dissoc chat-id)) + (update :chats-home-list disj chat-id) + (assoc :current-chat-id nil)) + :json-rpc/call [{:method "wakuext_deactivateChat" + :params [{:id chat-id}] + :on-success #() + :on-error #(log/error "failed to create public chat" chat-id %)}]} + (clear-history chat-id true))) + +(rf/defn offload-messages + {:events [:chat/offload-messages]} + [{:keys [db]} chat-id] + {:db (-> db + (update :messages dissoc chat-id) + (update :message-lists dissoc chat-id) + (update :pagination-info dissoc chat-id))}) + +(rf/defn close-chat + {:events [:chat/close]} + [{:keys [db] :as cofx} navigate-to-shell?] + (when-let [chat-id (:current-chat-id db)] + (chat.state/reset-visible-item) + (rf/merge cofx + (merge + {:db (dissoc db :current-chat-id)} + (let [community-id (get-in db [:chats chat-id :community-id])] + ;; When navigating back from community chat to community, update switcher card + (when (and community-id (not navigate-to-shell?)) + {:dispatch [:shell/add-switcher-card + :community {:community-id community-id}]}))) + (delete-for-me/sync-all) + (delete-message/send-all) + (offload-messages chat-id)))) + +(rf/defn force-close-chat + [{:keys [db] :as cofx} chat-id] + (do + (chat.state/reset-visible-item) + (rf/merge cofx + {:db (dissoc db :current-chat-id)} + (offload-messages chat-id)))) + +(rf/defn show-more-chats + {:events [:chat/show-more-chats]} + [{:keys [db]}] + (when (< (:home-items-show-number db) (count (:chats db))) + {:db (update db :home-items-show-number + 40)})) + +(rf/defn preload-chat-data + "Takes chat-id and coeffects map, returns effects necessary when navigating to chat" + {:events [:chat/preload-data]} + [cofx chat-id] + (loading/load-messages cofx chat-id)) + +(rf/defn navigate-to-chat + "Takes coeffects map and chat-id, returns effects necessary for navigation and preloading data" + {:events [:chat/navigate-to-chat]} + [{db :db :as cofx} chat-id from-shell?] + (rf/merge cofx + {:dispatch [:navigate-to-nav2 :chat chat-id from-shell?]} + (when-not (= (:view-id db) :community) + (navigation/pop-to-root-tab :shell-stack)) + (close-chat false) + (force-close-chat chat-id) + (fn [{:keys [db]}] + {:db (assoc db :current-chat-id chat-id)}) + (preload-chat-data chat-id) + #(when (group-chat? cofx chat-id) + (loading/load-chat % chat-id)))) + +(rf/defn handle-clear-history-response + {:events [:chat/history-cleared]} + [{:keys [db]} chat-id response] + (let [chat (chats-store/<-rpc (first (:chats response)))] + {:db (assoc-in db [:chats chat-id] chat)})) + +(rf/defn handle-one-to-one-chat-created + {:events [:chat/one-to-one-chat-created]} + [{:keys [db]} chat-id response] + (let [chat (chats-store/<-rpc (first (:chats response))) + contact-rpc (first (:contacts response)) + contact (when contact-rpc (contacts-store/<-rpc contact-rpc))] + {:db (cond-> db + contact + (assoc-in [:contacts/contacts chat-id] contact) + :always + (assoc-in [:chats chat-id] chat) + :always + (update :chats-home-list conj chat-id)) + :dispatch [:chat/navigate-to-chat chat-id]})) + +(rf/defn decrease-unviewed-count + {:events [:chat/decrease-unviewed-count]} + [{:keys [db]} chat-id {:keys [count countWithMentions]}] + {:db (-> db + ;; There might be some other requests being fired, + ;; so we need to make sure the count has not been set to + ;; 0 in the meantime + (update-in [:chats chat-id :unviewed-messages-count] + #(max (- % count) 0)) + (update-in [:chats chat-id :unviewed-mentions-count] + #(max (- % countWithMentions) 0)))}) + +;;;; UI + +(rf/defn start-chat + "Start a chat, making sure it exists" + {:events [:chat.ui/start-chat]} + [cofx chat-id ens-name] + (when (not= (multiaccounts.model/current-public-key cofx) chat-id) + {:json-rpc/call [{:method "wakuext_createOneToOneChat" + :params [{:id chat-id :ensName ens-name}] + :on-success #(rf/dispatch [:chat/one-to-one-chat-created chat-id %]) + :on-error #(log/error "failed to create one-to-on chat" chat-id %)}]})) + +(rf/defn clear-history-handler + "Clears history of the particular chat" + {:events [:chat.ui/clear-history]} + [{:keys [db] :as cofx} chat-id remove-chat?] + (rf/merge cofx + {:db db + :json-rpc/call [{:method "wakuext_clearHistory" + :params [{:id chat-id}] + :on-success #(rf/dispatch [:chat/history-cleared chat-id %]) + :on-error #(log/error "failed to clear history " chat-id %)}]} + (clear-history chat-id remove-chat?))) + +(rf/defn remove-chat + "Removes chat completely from app, producing all necessary effects for that" + {:events [:chat.ui/remove-chat]} + [{:keys [db now] :as cofx} chat-id] + (rf/merge cofx + {:clear-message-notifications + [[chat-id] (get-in db [:multiaccount :remote-push-notifications-enabled?])] + :dispatch [:shell/close-switcher-card chat-id]} + (deactivate-chat chat-id) + (offload-messages chat-id))) + +(rf/defn mute-chat-failed + {:events [:chat/mute-failed]} + [{:keys [db]} chat-id muted? error] + (log/error "mute chat failed" chat-id error) + {:db (assoc-in db [:chats chat-id :muted] (not muted?))}) + +(rf/defn mute-chat-toggled-successfully + {:events [:chat/mute-successfully]} + [_ chat-id] + (log/debug "muted chat successfully" chat-id)) + +(rf/defn mute-chat + {:events [:chat.ui/mute]} + [{:keys [db]} chat-id muted?] + (let [method (if muted? "wakuext_muteChat" "wakuext_unmuteChat")] + {:db (assoc-in db [:chats chat-id :muted] muted?) + :json-rpc/call [{:method method + :params [chat-id] + :on-error #(rf/dispatch [:chat/mute-failed chat-id muted? %]) + :on-success #(rf/dispatch [:chat/mute-successfully chat-id])}]})) + +(rf/defn show-clear-history-confirmation + {:events [:chat.ui/show-clear-history-confirmation]} + [_ chat-id] + {:ui/show-confirmation + {:title (i18n/label :t/clear-history-title) + :content (i18n/label :t/clear-history-confirmation-content) + :confirm-button-text (i18n/label :t/clear-history-action) + :on-accept #(do + (rf/dispatch [:bottom-sheet/hide]) + (rf/dispatch [:chat.ui/clear-history chat-id false]))}}) + +(rf/defn show-remove-chat-confirmation + {:events [:chat.ui/show-remove-confirmation]} + [_ chat-id] + {:ui/show-confirmation + {:title (i18n/label :t/delete-confirmation) + :content (i18n/label :t/delete-chat-confirmation) + :confirm-button-text (i18n/label :t/delete) + :on-accept #(do + (rf/dispatch [:bottom-sheet/hide]) + (rf/dispatch [:chat.ui/remove-chat chat-id]))}}) + +(rf/defn navigate-to-user-pinned-messages + "Takes coeffects map and chat-id, returns effects necessary for navigation and preloading data" + {:events [:chat.ui/navigate-to-pinned-messages]} + [cofx chat-id] + (navigation/navigate-to cofx :chat-pinned-messages {:chat-id chat-id})) diff --git a/src/status_im/chat/models_test.cljs b/src/status_im2/contexts/chat/events_test.cljs similarity index 90% rename from src/status_im/chat/models_test.cljs rename to src/status_im2/contexts/chat/events_test.cljs index f3022faf3f..0fd48fb98d 100644 --- a/src/status_im/chat/models_test.cljs +++ b/src/status_im2/contexts/chat/events_test.cljs @@ -1,6 +1,6 @@ -(ns status-im.chat.models-test +(ns status-im2.contexts.chat.events-test (:require [cljs.test :refer-macros [deftest is testing]] - [status-im.chat.models :as chat] + [status-im2.contexts.chat.events :as chat] [status-im.chat.models.images :as images] [status-im.utils.clocks :as utils.clocks])) @@ -49,10 +49,7 @@ :chats {chat-id {:last-message {:clock-value 10}}}}}] (testing "it deletes all the messages" (let [actual (chat/remove-chat cofx chat-id)] - (is (= nil (get-in actual [:db :messages chat-id]))))) - #_(testing "it sets a deleted-at-clock-value equal to the last message clock-value" - (let [actual (chat/remove-chat cofx chat-id)] - (is (= 10 (get-in actual [:db :chats chat-id :deleted-at-clock-value]))))))) + (is (= nil (get-in actual [:db :messages chat-id]))))))) (deftest multi-user-chat? (let [chat-id "1"] @@ -87,11 +84,11 @@ "opened" {} "1-1" {}}}) -(deftest navigate-to-chat-nav2 +(deftest navigate-to-chat (let [chat-id "test_chat" db {:pagination-info {chat-id {:all-loaded? true}}}] (testing "Pagination info should be reset on navigation" - (let [res (chat/navigate-to-chat-nav2 {:db db} chat-id false)] + (let [res (chat/navigate-to-chat {:db db} chat-id false)] (is (nil? (get-in res [:db :pagination-info chat-id :all-loaded?]))))))) (deftest camera-roll-loading-more-test diff --git a/src/status_im2/contexts/chat/group_details/view.cljs b/src/status_im2/contexts/chat/group_details/view.cljs index 48940fd828..1c519af94b 100644 --- a/src/status_im2/contexts/chat/group_details/view.cljs +++ b/src/status_im2/contexts/chat/group_details/view.cljs @@ -4,7 +4,6 @@ [quo2.core :as quo2] [quo2.foundations.colors :as colors] [react-native.core :as rn] - [status-im.chat.models :as chat.models] [status-im2.contexts.chat.group-details.style :as style] [status-im2.common.contact-list.view :as contact-list] [status-im2.common.contact-list-item.view :as contact-list-item] @@ -147,7 +146,7 @@ [rn/touchable-opacity {:style (style/action-container color) :accessibility-label :toggle-mute - :on-press #(rf/dispatch [::chat.models/mute-chat-toggled chat-id (not muted)])} + :on-press #(rf/dispatch [:chat.ui/mute chat-id (not muted)])} [quo2/icon (if muted :i/muted :i/activity-center) {:size 20 :color (colors/theme-colors colors/neutral-100 colors/white)}] [quo2/text {:style {:margin-top 16} :size :paragraph-1 :weight :medium} diff --git a/src/status_im2/contexts/chat/home/chat_list_item/view.cljs b/src/status_im2/contexts/chat/home/chat_list_item/view.cljs index 5a269b7273..5f392fa45a 100644 --- a/src/status_im2/contexts/chat/home/chat_list_item/view.cljs +++ b/src/status_im2/contexts/chat/home/chat_list_item/view.cljs @@ -14,7 +14,7 @@ [chat-id] (fn [] (rf/dispatch [:dismiss-keyboard]) - (rf/dispatch [:chat.ui/navigate-to-chat-nav2 chat-id]) + (rf/dispatch [:chat/navigate-to-chat chat-id]) (rf/dispatch [:search/home-filter-changed nil]))) (defn truncate-literal diff --git a/src/status_im2/contexts/chat/home/view.cljs b/src/status_im2/contexts/chat/home/view.cljs index 44ca7d16b9..07014b37dc 100644 --- a/src/status_im2/contexts/chat/home/view.cljs +++ b/src/status_im2/contexts/chat/home/view.cljs @@ -37,7 +37,7 @@ [rn/flat-list {:key-fn #(or (:chat-id %) (:public-key %) (:id %)) :get-item-layout get-item-layout - :on-end-reached #(re-frame/dispatch [:chat.ui/show-more-chats]) + :on-end-reached #(re-frame/dispatch [:chat/show-more-chats]) :keyboard-should-persist-taps :always :data items :render-fn chat-list-item/chat-list-item}]))) diff --git a/src/status_im2/contexts/chat/messages/list/view.cljs b/src/status_im2/contexts/chat/messages/list/view.cljs index 24ab630e24..50cac734f0 100644 --- a/src/status_im2/contexts/chat/messages/list/view.cljs +++ b/src/status_im2/contexts/chat/messages/list/view.cljs @@ -173,7 +173,7 @@ [quo/floating-shell-button (merge {:jump-to {:on-press #(do - (rf/dispatch [:close-chat true]) + (rf/dispatch [:chat/close true]) (rf/dispatch [:shell/navigate-to-jump-to])) :label (i18n/label :t/jump-to)}} (when @show-floating-scroll-down-button diff --git a/src/status_im2/contexts/chat/messages/view.cljs b/src/status_im2/contexts/chat/messages/view.cljs index dc695b3508..870baa723b 100644 --- a/src/status_im2/contexts/chat/messages/view.cljs +++ b/src/status_im2/contexts/chat/messages/view.cljs @@ -20,7 +20,7 @@ [] (when (and (not @navigation.state/curr-modal) (= (get @re-frame.db/app-db :view-id) :chat)) (rn/hw-back-remove-listener navigate-back-handler) - (rf/dispatch [:close-chat]) + (rf/dispatch [:chat/close]) (rf/dispatch [:navigate-back]) ;; If true is not returned back button event will bubble up, ;; and will call system back button action @@ -51,7 +51,7 @@ 1000)}) :left-section {:on-press #(do - (rf/dispatch [:close-chat]) + (rf/dispatch [:chat/close]) (rf/dispatch [:navigate-back])) :icon :i/arrow-left :accessibility-label :back-button} diff --git a/src/status_im2/events.cljs b/src/status_im2/events.cljs index c6cda8e260..a698d0884e 100644 --- a/src/status_im2/events.cljs +++ b/src/status_im2/events.cljs @@ -13,7 +13,8 @@ [status-im2.db :as db] [utils.re-frame :as rf] [utils.datetime :as datetime] - status-im2.contexts.syncing.events)) + status-im2.contexts.syncing.events + status-im2.contexts.chat.events)) (re-frame/reg-cofx :now diff --git a/src/status_im2/subs/chat/chats.cljs b/src/status_im2/subs/chat/chats.cljs index fc39662241..19b85018ab 100644 --- a/src/status_im2/subs/chat/chats.cljs +++ b/src/status_im2/subs/chat/chats.cljs @@ -3,7 +3,7 @@ [quo.design-system.colors :as colors] [re-frame.core :as re-frame] [status-im.add-new.db :as db] - [status-im.chat.models :as chat.models] + [status-im2.contexts.chat.events :as chat.events] [status-im.chat.models.mentions :as mentions] [status-im.communities.core :as communities] [status-im.group-chats.core :as group-chat] @@ -138,13 +138,6 @@ (fn [[chat-id inputs]] (get inputs chat-id))) -(re-frame/reg-sub - :chats/timeline-chat-input - :<- [:chat/inputs] - :<- [:multiaccount/public-key] - (fn [[inputs public-key]] - (get inputs (chat.models/profile-chat-topic public-key)))) - (re-frame/reg-sub :chats/sending-image :<- [:chats/current-chat-id] @@ -177,15 +170,15 @@ inputs]] (when current-chat (cond-> current-chat - (chat.models/public-chat? current-chat) + (chat.events/public-chat? current-chat) (assoc :show-input? true) - (and (chat.models/group-chat? current-chat) + (and (chat.events/group-chat? current-chat) (group-chats.db/member? my-public-key current-chat)) (assoc :show-input? true :member? true) - (and (chat.models/community-chat? current-chat) + (and (chat.events/community-chat? current-chat) (communities/can-post? community my-public-key (:chat-id current-chat))) (assoc :show-input? true) @@ -239,14 +232,8 @@ :current-chat/one-to-one-chat? :<- [:chats/current-raw-chat] (fn [current-chat] - (not (or (chat.models/group-chat? current-chat) - (chat.models/public-chat? current-chat))))) - -(re-frame/reg-sub - :chats/current-profile-chat - :<- [:contacts/current-contact-identity] - (fn [identity] - (chat.models/profile-chat-topic identity))) + (not (or (chat.events/group-chat? current-chat) + (chat.events/public-chat? current-chat))))) (re-frame/reg-sub :chats/photo-path @@ -266,7 +253,7 @@ :<- [:chats/home-list-chats] (fn [chats _] (reduce (fn [{:keys [public other]} {:keys [unviewed-messages-count public?] :as chat}] - (if (or public? (chat.models/community-chat? chat)) + (if (or public? (chat.events/community-chat? chat)) {:public (+ public unviewed-messages-count) :other other} {:other (+ other unviewed-messages-count) diff --git a/src/status_im2/subs/chat/messages.cljs b/src/status_im2/subs/chat/messages.cljs index 73b35e5b26..9d9debec91 100644 --- a/src/status_im2/subs/chat/messages.cljs +++ b/src/status_im2/subs/chat/messages.cljs @@ -1,11 +1,132 @@ (ns status-im2.subs.chat.messages (:require [re-frame.core :as re-frame] - [status-im.chat.db :as chat.db] [status-im2.contexts.chat.messages.list.events :as models.message-list] [status-im.chat.models.reactions :as models.reactions] [utils.datetime :as datetime] [status-im2.constants :as constants])) +(defn intersperse-datemark + "Reduce step which expects the input list of messages to be sorted by clock value. + It makes best effort to group them by day. + We cannot sort them by :timestamp, as that represents the clock of the sender + and we have no guarantees on the order. + We naively and arbitrarly group them assuming that out-of-order timestamps + fall in the previous bucket. + A sends M1 to B with timestamp 2000-01-01T00:00:00 + B replies M2 with timestamp 1999-12-31-23:59:59 + M1 needs to be displayed before M2 + so we bucket both in 1999-12-31" + [{:keys [acc last-timestamp last-datemark]} {:keys [whisper-timestamp datemark] :as msg}] + (cond + (empty? acc) ; initial element + {:last-timestamp whisper-timestamp + :last-datemark datemark + :acc (conj acc msg)} + + (and (not= last-datemark datemark) ; not the same day + (< whisper-timestamp last-timestamp)) ; not out-of-order + {:last-timestamp whisper-timestamp + :last-datemark datemark + :acc (conj acc + {:value last-datemark ; intersperse datemark message + :type :datemark} + msg)} + :else + {:last-timestamp (min whisper-timestamp last-timestamp) ; use last datemark + :last-datemark last-datemark + :acc (conj acc (assoc msg :datemark last-datemark))})) + +(defn add-datemarks + "Add a datemark in between an ordered seq of messages when two datemarks are not + the same. Ignore messages with out-of-order timestamps" + [messages] + (when (seq messages) + (let [messages-with-datemarks (:acc (reduce intersperse-datemark {:acc []} messages))] + ; Append last datemark + (conj messages-with-datemarks + {:value (:datemark (peek messages-with-datemarks)) + :type :datemark})))) + +(defn last-gap + "last-gap is a special gap that is put last in the message stream" + [chat-id synced-from] + {:message-id "0x123" + :message-type constants/message-type-gap + :chat-id chat-id + :content-type constants/content-type-gap + :gap-ids #{:first-gap} + :gap-parameters {:from synced-from}}) + +(defn collapse-gaps + "collapse-gaps will take an array of messages and collapse any gap next to + each other in a single gap. + It will also append one last gap if the last message is a non-gap" + [messages chat-id synced-from now chat-type joined loading-messages?] + (let [messages-with-gaps (reduce + (fn [acc {:keys [gap-parameters message-id] :as message}] + (let [last-element (peek acc)] + (cond + ;; If it's a message, just add + (empty? gap-parameters) + (conj acc message) + + ;; Both are gaps, merge them + (and + (seq (:gap-parameters last-element)) + (seq gap-parameters)) + (conj (pop acc) (update last-element :gap-ids conj message-id)) + + ;; it's a gap + :else + (conj acc (assoc message :gap-ids #{message-id}))))) + [] + messages)] + (if (or loading-messages? ; it's loading messages from the database + (nil? synced-from) ; it's still syncing + (= constants/timeline-chat-type chat-type) ; it's a timeline chat + (= constants/profile-chat-type chat-type) ; it's a profile chat + (and (not (nil? synced-from)) ; it's not more than a month + (<= synced-from (- (quot now 1000) constants/one-month))) + (and (= constants/private-group-chat-type chat-type) ; it's a private group chat + (or (not (pos? joined)) ; we haven't joined + (>= (quot joined 1000) synced-from))) ; the history goes before we joined + (:gap-ids (peek messages-with-gaps))) ; there's already a gap on top of the chat history + messages-with-gaps ; don't add an extra gap + (conj messages-with-gaps (last-gap chat-id synced-from))))) + +(defn hydrate-messages + "Pull data from messages and add it to the sorted list" + ([message-list messages] (hydrate-messages message-list messages {})) + ([message-list messages pinned-messages] + (keep #(if (= :message (% :type)) + (when-let [message (messages (% :message-id))] + (let [pinned-message (get pinned-messages (% :message-id)) + pinned (if pinned-message true (some? (message :pinned-by))) + pinned-by (when pinned (or (message :pinned-by) (pinned-message :pinned-by))) + message (assoc message :pinned pinned :pinned-by pinned-by)] + (merge message %))) + %) + message-list))) + +(defn albumize-messages + [messages] + (get (reduce (fn [{:keys [messages albums]} message] + (let [album-id (when (:albumize? message) (:album-id message)) + albums (cond-> albums album-id (update album-id conj message)) + messages (if (and album-id (> (count (get albums album-id)) 3)) + (conj (filterv #(not= album-id (:album-id %)) messages) + {:album (get albums album-id) + :album-id album-id + :message-id album-id + :content-type constants/content-type-album}) + (conj messages message))] + {:messages messages + :albums albums})) + {:messages [] + :albums {}} + messages) + :messages)) + (re-frame/reg-sub :chats/chat-messages :<- [:messages/messages] @@ -81,20 +202,6 @@ (fn [pin-message-lists [_ chat-id]] (get pin-message-lists chat-id))) -(defn hydrate-messages - "Pull data from messages and add it to the sorted list" - ([message-list messages] (hydrate-messages message-list messages {})) - ([message-list messages pinned-messages] - (keep #(if (= :message (% :type)) - (when-let [message (messages (% :message-id))] - (let [pinned-message (get pinned-messages (% :message-id)) - pinned (if pinned-message true (some? (message :pinned-by))) - pinned-by (when pinned (or (message :pinned-by) (pinned-message :pinned-by))) - message (assoc message :pinned pinned :pinned-by pinned-by)] - (merge message %))) - %) - message-list))) - (re-frame/reg-sub :chats/chat-no-messages? (fn [[_ chat-id] _] @@ -102,25 +209,6 @@ (fn [messages] (empty? messages))) -(defn albumize-messages - [messages] - (get (reduce (fn [{:keys [messages albums]} message] - (let [album-id (when (:albumize? message) (:album-id message)) - albums (cond-> albums album-id (update album-id conj message)) - messages (if (and album-id (> (count (get albums album-id)) 3)) - (conj (filterv #(not= album-id (:album-id %)) messages) - {:album (get albums album-id) - :album-id album-id - :message-id album-id - :content-type constants/content-type-album}) - (conj messages message))] - {:messages messages - :albums albums})) - {:messages [] - :albums {}} - messages) - :messages)) - (re-frame/reg-sub :chats/raw-chat-messages-stream (fn [[_ chat-id] _] @@ -138,41 +226,12 @@ (if (and (empty? message-list-seq) loading-messages?) [] (-> message-list-seq - (chat.db/add-datemarks) + (add-datemarks) (hydrate-messages messages pin-messages) - (chat.db/collapse-gaps chat-id - synced-from - (datetime/timestamp) - chat-type - joined - loading-messages?) + (collapse-gaps chat-id + synced-from + (datetime/timestamp) + chat-type + joined + loading-messages?) (albumize-messages)))))) - -;;we want to keep data unchanged so react doesn't change component when we leave screen -(def memo-profile-messages-stream (atom nil)) - -(re-frame/reg-sub - :chats/profile-messages-stream - (fn [[_ chat-id] _] - [(re-frame/subscribe [:chats/raw-chat-messages-stream chat-id]) - (re-frame/subscribe [:chats/chat-no-messages? chat-id]) - (re-frame/subscribe [:view-id])]) - (fn [[messages empty view-id]] - (when (or (= view-id :profile) empty) - (reset! memo-profile-messages-stream messages)) - @memo-profile-messages-stream)) - -(def memo-timeline-messages-stream (atom nil)) - -(re-frame/reg-sub - :chats/timeline-messages-stream - :<- [:chats/message-list constants/timeline-chat-id] - :<- [:chats/chat-messages constants/timeline-chat-id] - :<- [:view-id] - (fn [[message-list messages view-id]] - (if (= view-id :status) - (let [res (-> (models.message-list/->seq message-list) - (hydrate-messages messages))] - (reset! memo-timeline-messages-stream res) - res) - @memo-timeline-messages-stream))) diff --git a/src/status_im2/subs/chat/messages_test.cljs b/src/status_im2/subs/chat/messages_test.cljs index 4d6b09735a..d654e8f885 100644 --- a/src/status_im2/subs/chat/messages_test.cljs +++ b/src/status_im2/subs/chat/messages_test.cljs @@ -23,3 +23,31 @@ (deftest albumize-messages (testing "Finding albums in the messages list" (is (= (messages/albumize-messages messages-state) messages-albumized-state)))) + +(deftest intersperse-datemarks + (testing "it mantains the order even when timestamps are across days" + (let [message-1 {:datemark "Dec 31, 1999" + :whisper-timestamp 946641600000} ; 1999} + message-2 {:datemark "Jan 1, 2000" + :whisper-timestamp 946728000000} ; 2000 this will displayed in 1999 + message-3 {:datemark "Dec 31, 1999" + :whisper-timestamp 946641600000} ; 1999 + message-4 {:datemark "Jan 1, 2000" + :whisper-timestamp 946728000000} ; 2000 + ordered-messages [message-4 + message-3 + message-2 + message-1] + [m1 d1 m2 m3 m4 d2] (messages/add-datemarks ordered-messages)] + (is (= "Jan 1, 2000" + (:datemark m1))) + (is (= {:type :datemark + :value "Jan 1, 2000"} + d1)) + (is (= "Dec 31, 1999" + (:datemark m2) + (:datemark m3) + (:datemark m4))) + (is (= {:type :datemark + :value "Dec 31, 1999"} + d2))))) diff --git a/src/status_im2/subs/shell.cljs b/src/status_im2/subs/shell.cljs index ce3d2ff30c..6dc0c9c8eb 100644 --- a/src/status_im2/subs/shell.cljs +++ b/src/status_im2/subs/shell.cljs @@ -70,7 +70,7 @@ (str profile-picture "&addRing=0"))} :customization-color (or (:customization-color contact) :primary) :on-close #(re-frame/dispatch [:shell/close-switcher-card id]) - :on-press #(re-frame/dispatch [:chat.ui/navigate-to-chat-nav2 id true]) + :on-press #(re-frame/dispatch [:chat/navigate-to-chat id true]) :content (get-card-content chat communities)})) (defn private-group-chat-card @@ -79,7 +79,7 @@ :avatar-params {} :customization-color (or (:customization-color chat) :primary) :on-close #(re-frame/dispatch [:shell/close-switcher-card id]) - :on-press #(re-frame/dispatch [:chat.ui/navigate-to-chat-nav2 id true]) + :on-press #(re-frame/dispatch [:chat/navigate-to-chat id true]) :content (get-card-content chat communities)}) (defn community-card @@ -104,7 +104,7 @@ :on-press (fn [] (re-frame/dispatch [:navigate-to-nav2 :community {:community-id community-id}]) (js/setTimeout - #(re-frame/dispatch [:chat.ui/navigate-to-chat-nav2 channel-id true]) + #(re-frame/dispatch [:chat/navigate-to-chat channel-id true]) 100))})) (re-frame/reg-sub