From f69ae44d50091f93c6c1fdc2bc331f7ebc408d26 Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Wed, 27 May 2020 16:29:33 +0200 Subject: [PATCH] Improve chat loading performance This commit does a few things: Move collections top level Move `messages`,`message-lists`,`pagination-info` from nested in `chats` to top level at the db. The reason for this change is that if any of the `messages` fields change, any `sub` that relies on `chat` will be recomputed, which is unnecessary. Move chat-name to events `chat-name` was computed dynamically, while it is now only calculated when loading chat the first time around. Remove `enrich-chats` Enrich chats was doing a lot of work, and many subscriptions were relying on it. Not all the computations were necessary, for example it would always calculate the name of who invited the user to a group chat, regardless of whether it was actually used in the view. This commit changes that behavior so that we use smaller subscriptions to calculate such fields. In general we should move computations to events, if that's not desirable (there are some cases where we might not want to do that), we should have "bottom/leaf heavy" subscriptions as opposed to "top heavy", especially if they are to be shared, so only when (and if) we load that particular view, the subscription is triggered, while others can be re-used. I have compared performance with current release, and there's a noticeable difference. Opening a chat is faster (messages are loaded faster), and clicking on the home view on a chat is more responsing (the animation on-press is much quicker). --- src/status_im/chat/db.cljs | 62 +---- src/status_im/chat/db_test.cljs | 14 - src/status_im/chat/models.cljs | 33 +-- src/status_im/chat/models/loading.cljs | 48 ++-- src/status_im/chat/models/message.cljs | 22 +- src/status_im/chat/models/message_seen.cljs | 5 +- src/status_im/chat/models/message_test.cljs | 62 +++-- src/status_im/chat/models_test.cljs | 21 +- src/status_im/data_store/chats.cljs | 17 +- src/status_im/data_store/chats_test.cljs | 8 +- src/status_im/events.cljs | 2 +- src/status_im/group_chats/db.cljs | 21 +- src/status_im/mailserver/core.cljs | 4 +- src/status_im/mailserver/core_test.cljs | 8 - src/status_im/subs.cljs | 248 ++++++++---------- src/status_im/transport/core_test.cljs | 13 +- src/status_im/transport/message/core.cljs | 4 +- .../ui/components/chat_icon/screen.cljs | 34 +-- src/status_im/ui/screens/chat/group.cljs | 127 +++++---- .../ui/screens/chat/input/input.cljs | 44 ++-- .../ui/screens/chat/message/gap.cljs | 8 +- .../ui/screens/chat/message/message.cljs | 16 +- src/status_im/ui/screens/chat/sheets.cljs | 101 +++---- .../ui/screens/chat/styles/main.cljs | 6 +- .../ui/screens/chat/toolbar_content.cljs | 98 +++---- src/status_im/ui/screens/chat/utils.cljs | 24 +- src/status_im/ui/screens/chat/views.cljs | 128 +++++---- src/status_im/ui/screens/ens/views.cljs | 10 +- .../ui/screens/home/views/inner_item.cljs | 16 +- 29 files changed, 554 insertions(+), 650 deletions(-) diff --git a/src/status_im/chat/db.cljs b/src/status_im/chat/db.cljs index fb9a6217d3..f9de641c88 100644 --- a/src/status_im/chat/db.cljs +++ b/src/status_im/chat/db.cljs @@ -1,69 +1,11 @@ (ns status-im.chat.db - (:require [clojure.set :as clojure.set] - [clojure.string :as clojure.string] - [status-im.group-chats.db :as group-chats.db] - [status-im.mailserver.constants :as mailserver.constants] - [status-im.multiaccounts.core :as multiaccounts] - [status-im.utils.identicon :as identicon] - [status-im.utils.gfycat.core :as gfycat])) + (:require [clojure.string :as clojure.string] + [status-im.mailserver.constants :as mailserver.constants])) (defn group-chat-name [{:keys [public? name]}] (str (when public? "#") name)) -(defn enrich-active-chat - [contacts {:keys [chat-id - identicon - alias - group-chat] :as chat} current-public-key] - (if group-chat - (let [pending-invite-inviter-name - (group-chats.db/get-pending-invite-inviter-name contacts - chat - current-public-key) - inviter-name - (group-chats.db/get-inviter-name contacts - chat - current-public-key)] - (cond-> chat - pending-invite-inviter-name - (assoc :pending-invite-inviter-name pending-invite-inviter-name) - inviter-name - (assoc :inviter-name inviter-name) - :always - (assoc :chat-name (group-chat-name chat)))) - (let [photo (if (seq identicon) - identicon - (identicon/identicon chat-id)) - alias (if (seq alias) - alias - (gfycat/generate-gfy chat-id)) - {contact-name :name :as contact} - (get contacts chat-id - {:public-key chat-id - :identicon photo - :alias alias - :name alias - :system-tags #{}})] - (-> chat - (assoc :contact contact - :chat-name (multiaccounts/displayed-name contact) - :name contact-name - :identicon photo - :alias alias) - (update :tags clojure.set/union (:tags contact)))))) - -(defn active-chats - [contacts chats {:keys [public-key]}] - (reduce-kv (fn [acc chat-id {:keys [is-active] :as chat}] - (if is-active - (assoc acc - chat-id - (enrich-active-chat contacts chat public-key)) - acc)) - {} - chats)) - (defn datemark? [{:keys [type]}] (= type :datemark)) diff --git a/src/status_im/chat/db_test.cljs b/src/status_im/chat/db_test.cljs index d5a1f9cc38..4d1f03eb22 100644 --- a/src/status_im/chat/db_test.cljs +++ b/src/status_im/chat/db_test.cljs @@ -1,7 +1,5 @@ (ns status-im.chat.db-test (:require [cljs.test :refer-macros [deftest is testing]] - [status-im.utils.gfycat.core :as gfycat] - [status-im.utils.identicon :as identicon] [status-im.chat.db :as db])) (deftest group-chat-name @@ -41,18 +39,6 @@ (is (= {:type :datemark :value "Dec 31, 1999"} d2))))) -(deftest active-chats-test - (with-redefs [gfycat/generate-gfy (constantly "generated") - identicon/identicon (constantly "generated")] - (let [active-chat-1 {:is-active true :chat-id "1"} - active-chat-2 {:is-active true :chat-id "2"} - chats {"1" active-chat-1 - "2" active-chat-2 - "3" {:is-active false :chat-id "3"}}] - (testing "it returns only chats with is-active" - (is (= #{"1" "2"} - (set (keys (db/active-chats {} chats {}))))))))) - (deftest add-gaps (testing "empty state" (is (empty? diff --git a/src/status_im/chat/models.cljs b/src/status_im/chat/models.cljs index 32f15e6e0d..38ed94bb92 100644 --- a/src/status_im/chat/models.cljs +++ b/src/status_im/chat/models.cljs @@ -4,7 +4,6 @@ [status-im.transport.filters.core :as transport.filters] [status-im.contact.core :as contact.core] [status-im.waku.core :as waku] - [status-im.contact.db :as contact.db] [status-im.data-store.chats :as chats-store] [status-im.data-store.messages :as messages-store] [status-im.ethereum.json-rpc :as json-rpc] @@ -92,8 +91,7 @@ :is-active true :timestamp now :contacts #{chat-id} - :last-clock-value 0 - :messages {}})) + :last-clock-value 0})) (fx/defn ensure-chat "Add chat to db and update" @@ -165,6 +163,7 @@ {:chat-id topic :is-active true :name topic + :chat-name (str "#" topic) :group-chat true :contacts #{} :public? true @@ -175,20 +174,20 @@ (fx/defn clear-history "Clears history of the particular chat" [{:keys [db] :as cofx} chat-id] - (let [{:keys [messages - last-message + (let [{:keys [last-message deleted-at-clock-value]} (get-in db [:chats chat-id]) last-message-clock-value (or (:clock-value last-message) deleted-at-clock-value (utils.clocks/send 0))] (fx/merge cofx - {:db (update-in db [:chats chat-id] merge - {:messages {} - :message-list nil - :last-message nil - :unviewed-messages-count 0 - :deleted-at-clock-value last-message-clock-value})} + {:db (-> db + (assoc-in [:messages chat-id] {}) + (update-in [:message-lists] dissoc chat-id) + (update-in [:chats chat-id] merge + {:last-message nil + :unviewed-messages-count 0 + :deleted-at-clock-value last-message-clock-value}))} (messages-store/delete-messages-by-chat-id chat-id) #(chats-store/save-chat % (get-in % [:db :chats chat-id]))))) @@ -218,13 +217,9 @@ {:db (-> db (dissoc :loaded-chat-id) - (update-in [:chats current-chat-id] - assoc - :all-loaded? false - :cursor nil - :messages-initialized? false - :messages {} - :message-list nil))})) + (update :messages dissoc current-chat-id) + (update :message-lists dissoc current-chat-id) + (update :pagination-info dissoc current-chat-id))})) (fx/defn preload-chat-data "Takes chat-id and coeffects map, returns effects necessary when navigating to chat" @@ -241,8 +236,6 @@ (transport.filters/load-chat chat-id)) (when platform/desktop? (message-seen/mark-messages-seen chat-id)) - (when (and (one-to-one-chat? cofx chat-id) (not (contact.db/contact-exists? db chat-id))) - (contact.core/create-contact chat-id)) (loading/load-messages)))) (fx/defn navigate-to-chat diff --git a/src/status_im/chat/models/loading.cljs b/src/status_im/chat/models/loading.cljs index ba78fad937..8c7cb824c2 100644 --- a/src/status_im/chat/models/loading.cljs +++ b/src/status_im/chat/models/loading.cljs @@ -24,10 +24,7 @@ [{:keys [db] :as cofx} new-chats] (let [old-chats (:chats db) chats (reduce (fn [acc {:keys [chat-id] :as chat}] - (assoc acc chat-id - (assoc chat - :messages-initialized? false - :messages {}))) + (assoc acc chat-id chat)) {} new-chats) chats (merge old-chats chats)] @@ -52,12 +49,11 @@ acc)) {} (get-in db [:chats chat-id :messages]))] - {:db (update-in db [:chats chat-id] - assoc - :messages new-messages - :all-loaded? false - :message-list (message-list/add-many nil (vals new-messages)) - :cursor (clock-value->cursor last-element-clock-value))})))))) + {:db (-> db + (assoc-in [:messages chat-id] new-messages) + (assoc-in [:pagination-info chat-id] {:all-loaded? false + :cursor (clock-value->cursor last-element-clock-value)}) + (assoc-in [:message-lists chat-id] (message-list/add-many nil (vals new-messages))))})))))) (fx/defn initialize-chats "Initialize persisted chats on startup" @@ -70,7 +66,7 @@ [{:keys [db]} current-chat-id _ err] (log/error "failed loading messages" current-chat-id err) (when current-chat-id - {:db (assoc-in db [:chats current-chat-id :loading-messages?] false)})) + {:db (assoc-in db [:pagination-info current-chat-id :loading-messages?] false)})) (fx/defn messages-loaded "Loads more messages for current chat" @@ -81,10 +77,10 @@ {:keys [cursor messages]}] (when-not (or (nil? current-chat-id) (not= chat-id current-chat-id) - (and (get-in db [:chats current-chat-id :messages-initialized?]) + (and (get-in db [:pagination-info current-chat-id :messages-initialized?]) (not= session-id - (get-in db [:chats current-chat-id :messages-initialized?])))) - (let [already-loaded-messages (get-in db [:chats current-chat-id :messages]) + (get-in db [:pagination-info current-chat-id :messages-initialized?])))) + (let [already-loaded-messages (get-in db [:messages current-chat-id]) loaded-unviewed-messages-ids (get-in db [:chats current-chat-id :loaded-unviewed-messages-ids] #{}) ;; We remove those messages that are already loaded, as we might get some duplicates {:keys [all-messages @@ -111,23 +107,23 @@ messages)] (fx/merge cofx {:db (-> db - (assoc-in [:chats current-chat-id :cursor-clock-value] (when (seq cursor) (cursor->clock-value cursor))) + (assoc-in [:pagination-info current-chat-id :cursor-clock-value] (when (seq cursor) (cursor->clock-value cursor))) (assoc-in [:chats current-chat-id :loaded-unviewed-messages-ids] unviewed-message-ids) - (assoc-in [:chats current-chat-id :loading-messages?] false) - (assoc-in [:chats current-chat-id :messages] all-messages) - (update-in [:chats current-chat-id :message-list] message-list/add-many new-messages) - (assoc-in [:chats current-chat-id :cursor] cursor) - (assoc-in [:chats current-chat-id :all-loaded?] + (assoc-in [:pagination-info current-chat-id :loading-messages?] false) + (assoc-in [:messages current-chat-id] all-messages) + (update-in [:message-lists current-chat-id] message-list/add-many new-messages) + (assoc-in [:pagination-info current-chat-id :cursor] cursor) + (assoc-in [:pagination-info current-chat-id :all-loaded?] (empty? cursor)))} (message-seen/mark-messages-seen current-chat-id))))) (fx/defn load-more-messages [{:keys [db] :as cofx}] (when-let [current-chat-id (:current-chat-id db)] - (when-let [session-id (get-in db [:chats current-chat-id :messages-initialized?])] - (when-not (or (get-in db [:chats current-chat-id :all-loaded?]) - (get-in db [:chats current-chat-id :loading-messages?])) - (let [cursor (get-in db [:chats current-chat-id :cursor]) + (when-let [session-id (get-in db [:pagination-info current-chat-id :messages-initialized?])] + (when-not (or (get-in db [:pagination-info current-chat-id :all-loaded?]) + (get-in db [:pagination-info current-chat-id :loading-messages?])) + (let [cursor (get-in db [:pagination-info current-chat-id :cursor]) load-messages-fx (data-store.messages/messages-by-chat-id-rpc (waku/enabled? cofx) current-chat-id @@ -142,7 +138,7 @@ (fx/defn load-messages [{:keys [db now] :as cofx}] (when-let [current-chat-id (:current-chat-id db)] - (if-not (get-in db [:chats current-chat-id :messages-initialized?]) + (if-not (get-in db [:pagination-info current-chat-id :messages-initialized?]) (do ; reset chat first-not-visible-items state (chat.state/reset) @@ -151,7 +147,7 @@ ;; We keep track of whether there's a loaded chat ;; which will be reset only if we hit home (assoc :loaded-chat-id current-chat-id) - (assoc-in [:chats current-chat-id :messages-initialized?] now))} + (assoc-in [:pagination-info current-chat-id :messages-initialized?] now))} (message-seen/mark-messages-seen current-chat-id) (load-more-messages))) ;; We mark messages as seen in case we received them while on a different tab diff --git a/src/status_im/chat/models/message.cljs b/src/status_im/chat/models/message.cljs index 91d40b27a2..c870dc67c3 100644 --- a/src/status_im/chat/models/message.cljs +++ b/src/status_im/chat/models/message.cljs @@ -28,8 +28,8 @@ (fx/defn rebuild-message-list [{:keys [db]} chat-id] - {:db (assoc-in db [:chats chat-id :message-list] - (message-list/add-many nil (vals (get-in db [:chats chat-id :messages]))))}) + {:db (assoc-in db [:message-lists chat-id] + (message-list/add-many nil (vals (get-in db [:messages chat-id]))))}) (fx/defn hidden-message-marked-as-seen {:events [::hidden-message-marked-as-seen]} @@ -42,7 +42,7 @@ "Hide chat message, rebuild message-list" [{:keys [db] :as cofx} chat-id {:keys [seen message-id]}] (fx/merge cofx - {:db (update-in db [:chats chat-id :messages] dissoc message-id)} + {:db (update-in db [:messages chat-id] dissoc message-id)} (data-store.messages/mark-messages-seen chat-id [message-id] #(re-frame/dispatch [::hidden-message-marked-as-seen %1 %2 %3])) (rebuild-message-list chat-id))) @@ -52,7 +52,7 @@ :keys [seen-by-user?]}] (let [current-public-key (multiaccounts.model/current-public-key cofx) message-to-be-removed (when replace - (get-in db [:chats chat-id :messages replace])) + (get-in db [:messages chat-id replace])) prepared-message (prepare-message message seen-by-user?)] (fx/merge cofx (when message-to-be-removed @@ -62,8 +62,8 @@ ;; We should not be always adding to the list, as it does not make sense ;; if the chat has not been initialized, but run into ;; some troubles disabling it, so next time - (update-in [:chats chat-id :messages] assoc message-id prepared-message) - (update-in [:chats chat-id :message-list] message-list/add prepared-message)) + (update-in [:messages chat-id] assoc message-id prepared-message) + (update-in [:message-lists chat-id] message-list/add prepared-message)) (and (not seen-by-user?) (not= from current-public-key)) (update-in [:chats chat-id :loaded-unviewed-messages-ids] @@ -94,7 +94,7 @@ (defn- message-loaded? [{:keys [db]} {:keys [chat-id message-id]}] - (get-in db [:chats chat-id :messages message-id])) + (get-in db [:messages chat-id message-id])) (defn- earlier-than-deleted-at? [{:keys [db]} {:keys [chat-id clock-value]}] @@ -145,7 +145,7 @@ ;; If the message is already loaded, it means it's an update, that ;; happens when a message that was missing a reply had the reply ;; coming through, in which case we just insert the new message - {:db (assoc-in db [:chats chat-id :messages message-id] message-with-chat-id)} + {:db (assoc-in db [:messages chat-id message-id] message-with-chat-id)} (fx/merge cofx (add-received-message message-with-chat-id) (update-unviewed-count message-with-chat-id) @@ -202,7 +202,7 @@ [{:keys [db] :as cofx} chat-id message-id status] (fx/merge cofx {:db (assoc-in db - [:chats chat-id :messages message-id :outgoing-status] + [:messages chat-id message-id :outgoing-status] status)} (data-store.messages/update-outgoing-status message-id status))) @@ -219,7 +219,7 @@ "Deletes chat message, rebuild message-list" [{:keys [db] :as cofx} chat-id message-id] (fx/merge cofx - {:db (update-in db [:chats chat-id :messages] dissoc message-id)} + {:db (update-in db [:messages chat-id] dissoc message-id)} (data-store.messages/delete-message message-id) (rebuild-message-list chat-id))) @@ -229,4 +229,4 @@ (fx/defn toggle-expand-message [{:keys [db]} chat-id message-id] - {:db (update-in db [:chats chat-id :messages message-id :expanded?] not)}) + {:db (update-in db [:messages chat-id message-id :expanded?] not)}) diff --git a/src/status_im/chat/models/message_seen.cljs b/src/status_im/chat/models/message_seen.cljs index c387ba61f3..948dc50b1a 100644 --- a/src/status_im/chat/models/message_seen.cljs +++ b/src/status_im/chat/models/message_seen.cljs @@ -41,12 +41,11 @@ (when (seq loaded-unviewed-ids) (fx/merge cofx {:db (reduce (fn [acc message-id] - (assoc-in acc [:chats chat-id :messages - message-id :seen] + (assoc-in acc [:messages chat-id message-id :seen] true)) db loaded-unviewed-ids)} (messages-store/mark-messages-seen chat-id loaded-unviewed-ids nil) (update-chats-unviewed-messages-count {:chat-id chat-id}) (when platform/desktop? - (update-dock-badge-label)))))) \ No newline at end of file + (update-dock-badge-label)))))) diff --git a/src/status_im/chat/models/message_test.cljs b/src/status_im/chat/models/message_test.cljs index 0513bd9346..2079dd65b6 100644 --- a/src/status_im/chat/models/message_test.cljs +++ b/src/status_im/chat/models/message_test.cljs @@ -94,7 +94,8 @@ :clock-value 1 :chat-id "a"})))) (testing "it returns true when it's already in the loaded message" - (is (#'status-im.chat.models.message/message-loaded? {:db {:chats {"a" {:messages {"message-id" {}}}}}} + (is (#'status-im.chat.models.message/message-loaded? {:db + {:messages {"a" {"message-id" {}}}}} {:message-id "message-id" :from "a" :clock-value 1 @@ -124,7 +125,8 @@ :view-id :chat :loaded-chat-id "chat-id" :current-chat-id "chat-id" - :chats {"chat-id" {:messages {}}}}] + :messages {"chat-id" {}} + :chats {"chat-id" {}}}] (testing "a message coming from you!" (let [actual (message/receive-one {:db db} {:from "me" @@ -136,7 +138,7 @@ :outgoing true :content "b" :clock-value 1}) - message (get-in actual [:db :chats "chat-id" :messages "id"])] + message (get-in actual [:db :messages "chat-id" "id"])] (testing "it adds the message" (is message)))))) @@ -170,7 +172,7 @@ :whisper-timestamp 0 :timestamp 0}] (testing "a valid message" - (is (get-in (message/receive-one cofx valid-message) [:db :chats "chat-id" :messages "1"]))) + (is (get-in (message/receive-one cofx valid-message) [:db :messages "chat-id" "1"]))) (testing "a message from someone not in the list of participants" (is (not (message/receive-one cofx bad-from-message)))) (testing "a message with non existing chat-id" @@ -199,7 +201,7 @@ :whisper-timestamp 0 :timestamp 0}] (testing "a valid message" - (is (get-in (message/receive-one cofx valid-message) [:db :chats "chat-id" :messages "1"]))) + (is (get-in (message/receive-one cofx valid-message) [:db :messages "chat-id" "1"]))) (testing "a message with non existing chat-id" (is (not (message/receive-one cofx bad-chat-id-message)))))) @@ -235,37 +237,39 @@ :whisper-timestamp 0 :timestamp 0}] (testing "a valid message" - (is (get-in (message/receive-one cofx valid-message) [:db :chats "matching" :messages "1"]))) + (is (get-in (message/receive-one cofx valid-message) [:db :messages "matching" "1"]))) (testing "our own message" - (is (get-in (message/receive-one cofx own-message) [:db :chats "matching" :messages "1"]))) + (is (get-in (message/receive-one cofx own-message) [:db :messages "matching" "1"]))) (testing "a message with non matching chat-id" - (is (not (get-in (message/receive-one cofx bad-chat-id-message) [:db :chats "not-matching" :messages "1"]))))))) + (is (not (get-in (message/receive-one cofx bad-chat-id-message) [:db :messages "not-matching" "1"]))))))) (deftest delete-message (with-redefs [time/day-relative (constantly "day-relative") time/timestamp->time (constantly "timestamp")] - (let [cofx1 {:db {:chats {"chat-id" {:messages {0 {:message-id 0 - :content "a" - :clock-value 0 - :whisper-timestamp 0 - :timestamp 0} - 1 {:message-id 1 - :content "b" - :clock-value 1 - :whisper-timestamp 1 - :timestamp 1}} - :message-list [{:something :something}]}}}} - cofx2 {:db {:chats {"chat-id" {:messages {0 {:message-id 0 - :content "a" - :clock-value 0 - :whisper-timestamp 1 - :timestamp 1}} - :message-list [{:something :something}]}}}} + (let [cofx1 {:db {:messages {"chat-id" {0 {:message-id 0 + :content "a" + :clock-value 0 + :whisper-timestamp 0 + :timestamp 0} + 1 {:message-id 1 + :content "b" + :clock-value 1 + :whisper-timestamp 1 + :timestamp 1}}} + :message-lists {"chat-id" [{:something :something}]} + :chats {"chat-id" {}}}} + cofx2 {:db {:messages {"chat-id" {0 {:message-id 0 + :content "a" + :clock-value 0 + :whisper-timestamp 1 + :timestamp 1}}} + :message-list {"chat-id" [{:something :something}]} + :chats {"chat-id" {}}}} fx1 (message/delete-message cofx1 "chat-id" 1) fx2 (message/delete-message cofx2 "chat-id" 0)] (testing "Deleting message deletes it along with all references" (is (= '(0) - (keys (get-in fx1 [:db :chats "chat-id" :messages])))) + (keys (get-in fx1 [:db :messages "chat-id"])))) (is (= [{:one-to-one? false :message-id 0 :whisper-timestamp 0 @@ -284,8 +288,8 @@ :display-username? true :outgoing false}] (models.message-list/->seq - (get-in fx1 [:db :chats "chat-id" :message-list])))) + (get-in fx1 [:db :message-lists "chat-id"])))) (is (= {} - (get-in fx2 [:db :chats "chat-id" :messages]))) + (get-in fx2 [:db :messages "chat-id"]))) (is (= nil - (get-in fx2 [:db :chats "chat-id" :message-list]))))))) + (get-in fx2 [:db :message-lists "chat-id"]))))))) diff --git a/src/status_im/chat/models_test.cljs b/src/status_im/chat/models_test.cljs index 01cd0bffc4..cff1e9c497 100644 --- a/src/status_im/chat/models_test.cljs +++ b/src/status_im/chat/models_test.cljs @@ -65,15 +65,15 @@ (deftest clear-history-test (let [chat-id "1" - cofx {:db {:chats {chat-id {:message-list [{:something "a"}] - :last-message {:clock-value 10} + cofx {:db {:message-lists {chat-id [{:something "a"}]} + :chats {chat-id {:last-message {:clock-value 10} :unviewed-messages-count 1}}}}] (testing "it deletes all the messages" (let [actual (chat/clear-history cofx chat-id)] - (is (= {} (get-in actual [:db :chats chat-id :messages]))))) + (is (= {} (get-in actual [:db :messages chat-id]))))) (testing "it deletes all the message groups" (let [actual (chat/clear-history cofx chat-id)] - (is (= nil (get-in actual [:db :chats chat-id :message-list]))))) + (is (= nil (get-in actual [:db :message-lists chat-id]))))) (testing "it deletes unviewed messages set" (let [actual (chat/clear-history cofx chat-id)] (is (= 0 (get-in actual [:db :chats chat-id :unviewed-messages-count]))))) @@ -103,13 +103,13 @@ (deftest remove-chat-test (let [chat-id "1" - cofx {:db {:chats {chat-id {:last-message {:clock-value 10} - :messages {"1" {:clock-value 1} - "2" {:clock-value 10} - "3" {:clock-value 2}}}}}}] + cofx {:db {:messages {chat-id {"1" {:clock-value 1} + "2" {:clock-value 10} + "3" {:clock-value 2}}} + :chats {chat-id {:last-message {:clock-value 10}}}}}] (testing "it deletes all the messages" (let [actual (chat/remove-chat cofx chat-id)] - (is (= {} (get-in actual [:db :chats chat-id :messages]))))) + (is (= {} (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]))))) @@ -147,9 +147,10 @@ (def test-db {:multiaccount {:public-key "me"} + + :messages {"status" {"4" {} "5" {} "6" {}}} :chats {"status" {:public? true :group-chat true - :messages {"4" {} "5" {} "6" {}} :loaded-unviewed-messages-ids #{"6" "5" "4"}} "opened" {:loaded-unviewed-messages-ids #{}} "1-1" {:loaded-unviewed-messages-ids #{"6" "5" "4"}}}}) diff --git a/src/status_im/data_store/chats.cljs b/src/status_im/data_store/chats.cljs index cb736fc4df..2950950823 100644 --- a/src/status_im/data_store/chats.cljs +++ b/src/status_im/data_store/chats.cljs @@ -16,10 +16,16 @@ group-chat private-group-chat-type :else one-to-one-chat-type))) -(defn rpc->type [{:keys [chatType] :as chat}] +(defn rpc->type [{:keys [chatType name] :as chat}] (cond - (= public-chat-type chatType) (assoc chat :public? true :group-chat true) - (= private-group-chat-type chatType) (assoc chat :public? false :group-chat true) + (= public-chat-type chatType) (assoc chat + :chat-name (str "#" name) + :public? true + :group-chat true) + (= private-group-chat-type chatType) (assoc chat + :chat-name name + :public? false + :group-chat true) :else (assoc chat :public? false :group-chat false))) (defn- marshal-members [{:keys [admins contacts members-joined chatType] :as chat}] @@ -68,11 +74,10 @@ :deleted-at-clock-value :deletedAtClockValue :is-active :active :last-clock-value :lastClockValue}) - (dissoc :message-list :gaps-loaded? :pagination-info - :public? :group-chat :messages + (dissoc :public? :group-chat :messages :might-have-join-time-messages? :loaded-unviewed-messages-ids - :messages-initialized? :contacts :admins :members-joined))) + :contacts :admins :members-joined))) (defn <-rpc [chat] (-> chat diff --git a/src/status_im/data_store/chats_test.cljs b/src/status_im/data_store/chats_test.cljs index 692a48e2e7..2e30591083 100644 --- a/src/status_im/data_store/chats_test.cljs +++ b/src/status_im/data_store/chats_test.cljs @@ -5,7 +5,6 @@ (deftest ->to-rpc (let [chat {:public? false :group-chat true - :message-list [] :color "color" :contacts #{"a" "b" "c" "d"} :last-clock-value 10 @@ -13,15 +12,11 @@ :members-joined #{"a" "c"} :name "name" :membership-update-events :events - :gaps-loaded? true :unviewed-messages-count 2 :is-active true - :messages {} - :pagination-info {} :chat-id "chat-id" :loaded-unviewed-messages-ids [] - :timestamp 2 - :messages-initialized? true} + :timestamp 2} expected-chat {:id "chat-id" :color "color" :name "name" @@ -73,6 +68,7 @@ expected-chat {:public? false :group-chat true :color "color" + :chat-name "name" :contacts #{"a" "b" "c" "d"} :last-clock-value 10 :last-message nil diff --git a/src/status_im/events.cljs b/src/status_im/events.cljs index c7d8aa6cd4..ccad314f6a 100644 --- a/src/status_im/events.cljs +++ b/src/status_im/events.cljs @@ -519,7 +519,7 @@ (handlers/register-handler-fx :chat.ui/resend-message (fn [{:keys [db] :as cofx} [_ chat-id message-id]] - (let [message (get-in db [:chats chat-id :messages message-id])] + (let [message (get-in db [:messages chat-id message-id])] (fx/merge cofx (transport.message/set-message-envelope-hash chat-id message-id (:message-type message) 1) diff --git a/src/status_im/group_chats/db.cljs b/src/status_im/group_chats/db.cljs index b7eb21f81d..6bb6283344 100644 --- a/src/status_im/group_chats/db.cljs +++ b/src/status_im/group_chats/db.cljs @@ -1,5 +1,4 @@ -(ns status-im.group-chats.db - (:require [status-im.multiaccounts.core :as multiaccounts])) +(ns status-im.group-chats.db) (def members-added-type 3) @@ -23,21 +22,3 @@ (defn group-chat? [chat] (and (:group-chat chat) (not (:public? chat)))) - -(defn get-pending-invite-inviter-name - "when the chat is a private group chat in which the user has been - invited and didn't accept the invitation yet, return inviter-name" - [contacts chat my-public-key] - (when (and (group-chat? chat) - (invited? my-public-key chat) - (not (joined? my-public-key chat))) - (let [inviter-pk (get-inviter-pk my-public-key chat)] - (multiaccounts/displayed-name (or (get contacts inviter-pk) {:public-key inviter-pk}))))) - -(defn get-inviter-name - "when the chat is a private group chat in which the user has been - invited and didn't accept the invitation yet, return inviter-name" - [contacts chat my-public-key] - (when (group-chat? chat) - (let [inviter-pk (get-inviter-pk my-public-key chat)] - (multiaccounts/displayed-name (or (get contacts inviter-pk) {:public-key inviter-pk}))))) diff --git a/src/status_im/mailserver/core.cljs b/src/status_im/mailserver/core.cljs index 85f6a097de..80787b1f95 100644 --- a/src/status_im/mailserver/core.cljs +++ b/src/status_im/mailserver/core.cljs @@ -1171,7 +1171,7 @@ {}))) (fx/defn load-gaps-fx [{:keys [db] :as cofx} chat-id] - (when-not (get-in db [:chats chat-id :gaps-loaded?]) + (when-not (get-in db [:gaps-loaded? chat-id]) (let [success-fn #(re-frame/dispatch [::gaps-loaded %1 %2])] (data-store.mailservers/load-gaps cofx chat-id success-fn)))) @@ -1190,7 +1190,7 @@ cofx {:db (-> db - (assoc-in [:chats chat-id :gaps-loaded?] true) + (assoc-in [:gaps-loaded? chat-id] true) (assoc-in [:mailserver/gaps chat-id] gaps))} (data-store.mailservers/delete-gaps outdated-gaps)))) diff --git a/src/status_im/mailserver/core_test.cljs b/src/status_im/mailserver/core_test.cljs index 26833c62b2..c849fbb936 100644 --- a/src/status_im/mailserver/core_test.cljs +++ b/src/status_im/mailserver/core_test.cljs @@ -389,13 +389,11 @@ {:multiaccount {:public-key "me"} :chats {"chat-id-1" {:is-active true - :messages {} :might-have-join-time-messages? true :group-chat true :public? true :chat-id "chat-id-1"} "chat-id-2" {:is-active true - :messages {} :group-chat true :public? true :chat-id "chat-id-2"}}}}) @@ -405,14 +403,12 @@ {:multiaccount {:public-key "me"} :chats {"chat-id-1" {:is-active true - :messages {} :join-time-mail-request-id "a" :might-have-join-time-messages? true :group-chat true :public? true :chat-id "chat-id-1"} "chat-id-2" {:is-active true - :messages {} :group-chat true :public? true :chat-id "chat-id-2"}}}}) @@ -422,26 +418,22 @@ {:multiaccount {:public-key "me"} :chats {"chat-id-1" {:is-active true - :messages {} :join-time-mail-request-id "a" :might-have-join-time-messages? true :group-chat true :public? true :chat-id "chat-id-1"} "chat-id-2" {:is-active true - :messages {} :join-time-mail-request-id "a" :might-have-join-time-messages? true :group-chat true :public? true :chat-id "chat-id-2"} "chat-id-3" {:is-active true - :messages {} :group-chat true :public? true :chat-id "chat-id-3"} "chat-id-4" {:is-active true - :messages {} :join-time-mail-request-id "a" :might-have-join-time-messages? true :group-chat true diff --git a/src/status_im/subs.cljs b/src/status_im/subs.cljs index 570ae60013..096907c0c7 100644 --- a/src/status_im/subs.cljs +++ b/src/status_im/subs.cljs @@ -37,7 +37,6 @@ [status-im.utils.money :as money] [status-im.utils.platform :as platform] [status-im.utils.security :as security] - [status-im.utils.universal-links.core :as links] [status-im.wallet.db :as wallet.db] [status-im.wallet.utils :as wallet.utils] status-im.ui.screens.keycard.subs @@ -185,6 +184,10 @@ (reg-root-key-sub :multiaccounts/loading :multiaccounts/loading) +(reg-root-key-sub ::messages :messages) +(reg-root-key-sub ::message-lists :message-lists) +(reg-root-key-sub ::pagination-info :pagination-info) + ;;GENERAL ============================================================================================================== (re-frame/reg-sub @@ -450,6 +453,21 @@ (fn [{:keys [public-key]}] public-key)) +(re-frame/reg-sub + :multiaccount/preferred-name + :<- [:multiaccount] + (fn [{:keys [preferred-name]}] + preferred-name)) + +(re-frame/reg-sub + :multiaccount/my-name + :<- [:multiaccount/public-key] + :<- [:multiaccount/preferred-name] + (fn [[identity preferred-name]] + (if preferred-name + (stateofus/username (str "@" preferred-name)) + (gfycat/generate-gfy identity)))) + (re-frame/reg-sub :multiaccount/default-account :<- [:multiaccount/accounts] @@ -608,77 +626,24 @@ (re-frame/reg-sub :chats/active-chats - :<- [:contacts/contacts] :<- [::chats] - :<- [:multiaccount] - (fn [[contacts chats multiaccount]] - (chat.db/active-chats contacts chats multiaccount))) + (fn [chats] + (reduce-kv (fn [acc id {:keys [is-active] :as chat}] + (if is-active + (assoc acc id chat) + acc)) + {} + chats))) -;; TODO: this is no useful without tribute to talk -#_(defn enrich-current-one-to-one-chat - [{:keys [contact] :as current-chat} my-public-key ttt-settings - chain-keyword prices currency] - (let [{:keys [tribute-to-talk]} contact - {:keys [disabled? snt-amount message]} tribute-to-talk - whitelisted-by? (whitelist/whitelisted-by? contact) - loading? (and (not whitelisted-by?) - (not tribute-to-talk)) - show-input? (or whitelisted-by? - disabled?) - token (case chain-keyword - :mainnet :SNT - :STT) - tribute-status (if loading? - :loading - (tribute-to-talk.db/tribute-status contact)) - tribute-label (tribute-to-talk.db/status-label tribute-status snt-amount)] - - (cond-> (assoc current-chat - :tribute-to-talk/tribute-status tribute-status - :tribute-to-talk/tribute-label tribute-label) - - (#{:required :pending :paid} tribute-status) - (assoc :tribute-to-talk/snt-amount - (tribute-to-talk.db/from-wei snt-amount) - :tribute-to-talk/message - message - :tribute-to-talk/fiat-amount (if snt-amount - (money/fiat-amount-value - snt-amount - token - (-> currency :code keyword) - prices) - "0") - :tribute-to-talk/fiat-currency (:code currency) - :tribute-to-talk/token (str " " (name token))) - - (tribute-to-talk.db/enabled? ttt-settings) - (assoc :tribute-to-talk/received? (tribute-to-talk.db/tribute-received? - contact)) - - (= tribute-status :required) - (assoc :tribute-to-talk/on-share-my-profile - #(re-frame/dispatch - [:profile/share-profile-link my-public-key])) - - show-input? - (assoc :show-input? true)))) - -(defn enrich-current-chat - [{:keys [messages chat-id might-have-join-time-messages?] :as chat} ranges] - (assoc chat - :range - (get ranges chat-id) - :intro-status - (if might-have-join-time-messages? - :loading - (if (empty? messages) - :empty - :messages)))) +(re-frame/reg-sub + ::chat + :<- [::chats] + (fn [chats [_ chat-id]] + (get chats chat-id))) (re-frame/reg-sub :chats/current-raw-chat - :<- [:chats/active-chats] + :<- [::chats] :<- [:chats/current-chat-id] (fn [[chats current-chat-id]] (get chats current-chat-id))) @@ -694,15 +659,10 @@ :chats/current-chat :<- [:chats/current-raw-chat] :<- [:multiaccount/public-key] - :<- [:mailserver/ranges] - (fn [[{:keys [group-chat chat-id messages] :as current-chat} - my-public-key ranges]] + (fn [[{:keys [group-chat] :as current-chat} + my-public-key]] (when current-chat - (cond-> (enrich-current-chat current-chat ranges) - (empty? messages) - (assoc :universal-link - (links/generate-link :public-chat :external chat-id)) - + (cond-> current-chat (chat.models/public-chat? current-chat) (assoc :show-input? true) @@ -727,17 +687,12 @@ (fn [current-chat] (chat.models/public-chat? current-chat))) -(re-frame/reg-sub - :chats/current-chat-message - :<- [:chats/current-chat] - (fn [{:keys [messages]} [_ message-id]] - (get messages message-id))) - (re-frame/reg-sub :chats/current-chat-messages - :<- [:chats/current-chat] - (fn [{:keys [messages]}] - (or messages {}))) + :<- [::messages] + :<- [:chats/current-chat-id] + (fn [[messages chat-id]] + (get messages chat-id {}))) (re-frame/reg-sub :chats/messages-gaps @@ -746,6 +701,12 @@ (fn [[gaps chat-id]] (sort-by :from (vals (get gaps chat-id))))) +(re-frame/reg-sub + :mailserver/ranges-by-chat-id + :<- [:mailserver/ranges] + (fn [ranges [_ chat-id]] + (get ranges chat-id))) + (re-frame/reg-sub :chats/range :<- [:mailserver/ranges] @@ -755,27 +716,23 @@ (re-frame/reg-sub :chats/all-loaded? - :<- [:chats/current-chat] - (fn [chat] - (:all-loaded? chat))) + :<- [::pagination-info] + :<- [:chats/current-chat-id] + (fn [[pagination-info chat-id]] + (get-in pagination-info [chat-id :all-loaded?]))) (re-frame/reg-sub :chats/public? - :<- [:chats/current-chat] + :<- [:chats/current-raw-chat] (fn [chat] (:public? chat))) (re-frame/reg-sub :chats/message-list - :<- [:chats/current-chat] - (fn [chat] - (:message-list chat))) - -(re-frame/reg-sub - :chats/messages - :<- [:chats/current-chat] - (fn [chat] - (:messages chat))) + :<- [::message-lists] + :<- [:chats/current-chat-id] + (fn [[message-lists chat-id]] + (get message-lists chat-id))) (defn hydrate-messages "Pull data from messages and add it to the sorted list" @@ -786,10 +743,16 @@ %) message-list)) +(re-frame/reg-sub + :chats/current-chat-no-messages? + :<- [:chats/current-chat-messages] + (fn [messages] + (empty? messages))) + (re-frame/reg-sub :chats/current-chat-messages-stream :<- [:chats/message-list] - :<- [:chats/messages] + :<- [:chats/current-chat-messages] :<- [:chats/messages-gaps] :<- [:chats/range] :<- [:chats/all-loaded?] @@ -801,17 +764,6 @@ (hydrate-messages messages) (chat.db/add-gaps messages-gaps range all-loaded? public?)))) -(re-frame/reg-sub - :chats/current-chat-intro-status - :<- [:chats/current-chat] - :<- [:chats/current-chat-messages] - (fn [[{:keys [might-have-join-time-messages?]} messages]] - (if might-have-join-time-messages? - :loading - (if (empty? messages) - :empty - :messages)))) - (re-frame/reg-sub :chats/photo-path :<- [:contacts/contacts] @@ -826,9 +778,15 @@ :chats/unread-messages-number :<- [:chats/active-chats] (fn [chats _] - (let [grouped-chats (group-by :public? (vals chats))] - {:public (apply + (map :unviewed-messages-count (get grouped-chats true))) - :other (apply + (map :unviewed-messages-count (get grouped-chats false)))}))) + (reduce-kv (fn [{:keys [public other]} _ {:keys [unviewed-messages-count public?]}] + (if public? + {:public (+ public unviewed-messages-count) + :other other} + {:other (+ other unviewed-messages-count) + :public public})) + {:public 0 + :other 0} + chats))) (re-frame/reg-sub :chats/cooldown-enabled? @@ -886,13 +844,13 @@ (filter-contacts selected-contacts active-contacts))) (re-frame/reg-sub - :group-chat/chat-joined? - :<- [:multiaccount/public-key] - :<- [:chats/active-chats] - (fn [[my-public-key chats] [_ chat-id]] - (let [current-chat (get chats chat-id)] - (and (chat.models/group-chat? current-chat) - (group-chats.db/joined? my-public-key current-chat))))) + :group-chat/inviter-info + (fn [[_ chat-id] _] + [(re-frame/subscribe [::chat chat-id]) + (re-frame/subscribe [:multiaccount/public-key])]) + (fn [[chat my-public-key]] + {:joined? (group-chats.db/joined? my-public-key chat) + :inviter-pk (group-chats.db/get-inviter-pk my-public-key chat)})) (re-frame/reg-sub :chats/transaction-status @@ -1631,21 +1589,40 @@ contact.db/public-key->new-contact contact.db/enrich-contact)))) +(re-frame/reg-sub + :contacts/contact-by-identity + :<- [::contacts] + (fn [contacts [_ identity]] + (get contacts identity))) + +(re-frame/reg-sub + :contacts/contact-added? + (fn [[_ identity] _] + [(re-frame/subscribe [:contacts/contact-by-identity identity])]) + (fn [[contact] _] + (contact.db/added? contact))) + +(re-frame/reg-sub + :contacts/raw-contact-name-by-identity + (fn [[_ identity] _] + [(re-frame/subscribe [:contacts/contact-by-identity identity])]) + (fn [[db-contact] _] + (if (and (:ens-verified db-contact) (seq (:name db-contact))) + (stateofus/username (str "@" (:name db-contact))) + (:alias db-contact)))) + (re-frame/reg-sub :contacts/contact-name-by-identity - :<- [:contacts/contacts] - :<- [:multiaccount] - (fn [[contacts current-multiaccount] [_ identity]] + (fn [[_ identity] _] + [(re-frame/subscribe [:contacts/raw-contact-name-by-identity identity]) + (re-frame/subscribe [:multiaccount])]) + (fn [[contact-name current-multiaccount] [_ identity]] (let [me? (= (:public-key current-multiaccount) identity)] (if me? - {:ens-name (:preferred-name current-multiaccount) - :alias (gfycat/generate-gfy identity)} - (let [contact (or (contacts identity) - (contact.db/public-key->new-contact identity))] - {:ens-name (when (:ens-verified contact) - (:name contact)) - :alias (or (:alias contact) - (gfycat/generate-gfy identity))}))))) + (or (:preferred-name current-multiaccount) + (gfycat/generate-gfy identity)) + (or contact-name + (gfycat/generate-gfy identity)))))) (re-frame/reg-sub :messages/quote-info @@ -1694,17 +1671,6 @@ (fn [[chat all-contacts] [_ query-fn]] (contact.db/query-chat-contacts chat all-contacts query-fn))) -(re-frame/reg-sub - :contacts/chat-photo - (fn [[_ chat-id] _] - [(re-frame/subscribe [:chats/chat chat-id]) - (re-frame/subscribe [:contacts/contacts-by-chat filter chat-id])]) - (fn [[chat contacts] [_ _]] - (when (and chat (not (:group-chat chat))) - (if (pos? (count contacts)) - (multiaccounts/displayed-photo (first contacts)) - (multiaccounts/displayed-photo chat))))) - (re-frame/reg-sub :contacts/contact-by-address :<- [:contacts/contacts] diff --git a/src/status_im/transport/core_test.cljs b/src/status_im/transport/core_test.cljs index 8be7d492f8..a1eb80eabf 100644 --- a/src/status_im/transport/core_test.cljs +++ b/src/status_im/transport/core_test.cljs @@ -60,7 +60,8 @@ (let [chat-id "chat-id" from "from" message-id "message-id" - initial-cofx {:db {:chats {chat-id {:messages {message-id {:from from}}}}}}] + initial-cofx {:db {:messages {chat-id {message-id {:from from}}} + :chats {chat-id {}}}}] (testing "a single envelope message" (let [cofx (message/set-message-envelope-hash initial-cofx chat-id message-id :message-type 1)] @@ -72,12 +73,12 @@ (is (= :sent (get-in (message/update-envelope-status cofx message-id :sent) - [:db :chats chat-id :messages message-id :outgoing-status])))) + [:db :messages chat-id message-id :outgoing-status])))) (testing "the message is not sent" (is (= :not-sent (get-in (message/update-envelope-status cofx message-id :not-sent) - [:db :chats chat-id :messages message-id :outgoing-status])))))) + [:db :messages chat-id message-id :outgoing-status])))))) (testing "multi envelope message" (testing "only inserts" (let [cofx (fx/merge @@ -103,7 +104,7 @@ (is (= :sent (get-in cofx - [:db :chats chat-id :messages message-id :outgoing-status])))))) + [:db :messages chat-id message-id :outgoing-status])))))) (testing "order of events is reversed" (let [cofx (fx/merge initial-cofx @@ -119,7 +120,7 @@ (is (= :sent (get-in cofx - [:db :chats chat-id :messages message-id :outgoing-status])))))) + [:db :messages chat-id message-id :outgoing-status])))))) (testing "message not sent" (let [cofx (fx/merge initial-cofx @@ -135,4 +136,4 @@ (is (= :not-sent (get-in cofx - [:db :chats chat-id :messages message-id :outgoing-status]))))))))) + [:db :messages chat-id message-id :outgoing-status]))))))))) diff --git a/src/status_im/transport/message/core.cljs b/src/status_im/transport/message/core.cljs index c06d9ea261..fefe2e0f62 100644 --- a/src/status_im/transport/message/core.cljs +++ b/src/status_im/transport/message/core.cljs @@ -91,7 +91,7 @@ [{:keys [db] :as cofx} message-id status] (if-let [{:keys [chat-id]} (get-in db [:transport/message-envelopes message-id])] - (when-let [{:keys [from]} (get-in db [:chats chat-id :messages message-id])] + (when-let [{:keys [from]} (get-in db [:messages chat-id message-id])] (check-confirmations cofx status chat-id message-id)) ;; We don't have a message-envelope for this, might be that the confirmation ;; came too early @@ -118,4 +118,4 @@ :message-type message-type}) (update-in [:transport/message-ids->confirmations message-id] #(or % {:pending-confirmations messages-count})))})] - (apply fx/merge cofx (conj check-confirmations-fx add-envelope-data)))) \ No newline at end of file + (apply fx/merge cofx (conj check-confirmations-fx add-envelope-data)))) diff --git a/src/status_im/ui/components/chat_icon/screen.cljs b/src/status_im/ui/components/chat_icon/screen.cljs index 1a7f131dc1..33d80cc0b8 100644 --- a/src/status_im/ui/components/chat_icon/screen.cljs +++ b/src/status_im/ui/components/chat_icon/screen.cljs @@ -8,8 +8,6 @@ [status-im.ui.screens.chat.photos :as photos] [status-im.utils.platform :as platform])) -;;TODO REWORK THIS NAMESPACE - (defn default-chat-icon [name styles] (when-not (string/blank? name) [react/view (:default-chat-icon styles) @@ -32,15 +30,16 @@ [react/view online-dot-right]]]]) (defn chat-icon-view - [contact group-chat name _online styles] + [chat-id group-chat name styles] [react/view (:container styles) - (if-not group-chat - [photos/photo (multiaccounts/displayed-photo contact) styles] - [default-chat-icon name styles])]) + (if group-chat + [default-chat-icon name styles] + (let [photo-path @(re-frame.core/subscribe [:chats/photo-path chat-id])] + [photos/photo photo-path styles]))]) (defn chat-icon-view-toolbar - [contact group-chat name color online] - [chat-icon-view contact group-chat name online + [chat-id group-chat name color] + [chat-icon-view chat-id group-chat name {:container styles/container-chat-toolbar :online-view-wrapper styles/online-view-wrapper :online-view styles/online-view @@ -52,8 +51,8 @@ :default-chat-icon-text (styles/default-chat-icon-text 36)}]) (defn chat-icon-view-chat-list - [contact group-chat name color online] - [chat-icon-view contact group-chat name online + [chat-id group-chat name color] + [chat-icon-view chat-id group-chat name {:container styles/container-chat-list :online-view-wrapper styles/online-view-wrapper :online-view styles/online-view @@ -65,8 +64,8 @@ :default-chat-icon-text (styles/default-chat-icon-text 40)}]) (defn chat-icon-view-chat-sheet - [contact group-chat name color online] - [chat-icon-view contact group-chat name online + [chat-id group-chat name color] + [chat-icon-view chat-id group-chat name {:container styles/container-chat-list :online-view-wrapper styles/online-view-wrapper :online-view styles/online-view @@ -116,11 +115,12 @@ :default-chat-icon (styles/default-chat-icon-profile colors/default-chat-color size) :default-chat-icon-text (styles/default-chat-icon-text size)}]) -(defn chat-intro-icon-view [icon-text chat-id styles] - (let [photo-path (re-frame.core/subscribe [:contacts/chat-photo chat-id])] - (if-not (string/blank? @photo-path) - [photos/photo @photo-path styles] - [default-chat-icon icon-text styles]))) +(defn chat-intro-icon-view [icon-text chat-id group-chat styles] + (if group-chat + [default-chat-icon icon-text styles] + (let [photo-path @(re-frame.core/subscribe [:chats/photo-path chat-id])] + (if-not (string/blank? photo-path) + [photos/photo photo-path styles])))) (defn profile-icon-view [photo-path name color edit? size override-styles] (let [styles (merge {:container {:width size :height size} diff --git a/src/status_im/ui/screens/chat/group.cljs b/src/status_im/ui/screens/chat/group.cljs index 208a2444db..5814fc49c9 100644 --- a/src/status_im/ui/screens/chat/group.cljs +++ b/src/status_im/ui/screens/chat/group.cljs @@ -2,10 +2,12 @@ (:require [re-frame.core :as re-frame] [status-im.ui.components.button :as button] [status-im.ui.components.react :as react] + [status-im.utils.universal-links.core :as links] [status-im.ui.screens.chat.styles.main :as style] [status-im.i18n :as i18n] [status-im.ui.components.list-selection :as list-selection] - [status-im.ui.components.colors :as colors])) + [status-im.ui.components.colors :as colors]) + (:require-macros [status-im.utils.views :refer [defview letsubs]])) (defn join-chat-button [chat-id] [button/button @@ -20,15 +22,16 @@ [react/text {:style style/decline-chat} (i18n/label :t/group-chat-decline-invitation)]]) -(defn group-chat-footer +(defview group-chat-footer [chat-id] - [react/view {:style style/group-chat-join-footer} - [react/view {:style style/group-chat-join-container} - [join-chat-button chat-id] - [decline-chat chat-id]]]) + (letsubs [{:keys [joined?]} [:group-chat/inviter-info chat-id]] + (when-not joined? + [react/view {:style style/group-chat-join-footer} + [react/view {:style style/group-chat-join-container} + [join-chat-button chat-id] + [decline-chat chat-id]]]))) -(defn group-chat-description-loading - [] +(def group-chat-description-loading [react/view {:style (merge style/intro-header-description-container {:margin-bottom 36 :height 44})} @@ -38,48 +41,72 @@ :size :small :color colors/gray}]]) +(defview no-messages-group-chat-description-container [chat-id] + (letsubs [{:keys [highest-request-to lowest-request-from]} + [:mailserver/ranges-by-chat-id chat-id]] + [react/nested-text {:style (merge style/intro-header-description + {:margin-bottom 36})} + (let [quiet-hours (quot (- highest-request-to lowest-request-from) + (* 60 60)) + quiet-time (if (<= quiet-hours 24) + (i18n/label :t/quiet-hours + {:quiet-hours quiet-hours}) + (i18n/label :t/quiet-days + {:quiet-days (quot quiet-hours 24)}))] + (i18n/label :t/empty-chat-description-public + {:quiet-hours quiet-time})) + [{:style {:color colors/blue} + :on-press #(list-selection/open-share + {:message + (i18n/label + :t/share-public-chat-text {:link (links/generate-link :public-chat :external chat-id)})})} + (i18n/label :t/empty-chat-description-public-share-this)]])) + +(defview pending-invitation-description + [inviter-pk chat-name] + (letsubs [inviter-name [:contacts/contact-name-by-identity inviter-pk]] + [react/nested-text {:style style/intro-header-description} + [{:style {:color colors/black}} inviter-name] + (i18n/label :t/join-group-chat-description + {:username "" + :group-name chat-name})])) + +(defview joined-group-chat-description + [inviter-pk chat-name] + (letsubs [inviter-name [:contacts/contact-name-by-identity inviter-pk]] + [react/nested-text {:style style/intro-header-description} + (i18n/label :t/joined-group-chat-description + {:username "" + :group-name chat-name}) + [{:style {:color colors/black}} inviter-name]])) + +(defn created-group-chat-description [chat-name] + [react/text {:style style/intro-header-description} + (i18n/label :t/created-group-chat-description + {:group-name chat-name})]) + +(defview group-chat-inviter-description-container [chat-id chat-name] + (letsubs [{:keys [joined? inviter-pk]} + [:group-chat/inviter-info chat-id]] + (cond + (not joined?) + [pending-invitation-description inviter-pk chat-name] + inviter-pk + [joined-group-chat-description inviter-pk chat-name] + :else + [created-group-chat-description chat-name]))) + (defn group-chat-description-container - [{:keys [pending-invite-inviter-name inviter-name chat-name public? - universal-link range intro-status]}] - (let [{:keys [lowest-request-from highest-request-to]} range] - (case intro-status - :loading - [group-chat-description-loading] + [{:keys [public? + chat-id + chat-name + loading-messages? + no-messages?]}] + (cond loading-messages? + group-chat-description-loading - :empty - (when public? - [react/nested-text {:style (merge style/intro-header-description - {:margin-bottom 36})} - (let [quiet-hours (quot (- highest-request-to lowest-request-from) - (* 60 60)) - quiet-time (if (<= quiet-hours 24) - (i18n/label :t/quiet-hours - {:quiet-hours quiet-hours}) - (i18n/label :t/quiet-days - {:quiet-days (quot quiet-hours 24)}))] - (i18n/label :t/empty-chat-description-public - {:quiet-hours quiet-time})) - [{:style {:color colors/blue} - :on-press #(list-selection/open-share - {:message - (i18n/label - :t/share-public-chat-text {:link universal-link})})} - (i18n/label :t/empty-chat-description-public-share-this)]]) + (and no-messages? public?) + [no-messages-group-chat-description-container chat-id] - :messages - (when (not public?) - (if pending-invite-inviter-name - [react/nested-text {:style style/intro-header-description} - [{:style {:color colors/black}} pending-invite-inviter-name] - (i18n/label :t/join-group-chat-description - {:username "" - :group-name chat-name})] - (if (not= inviter-name "Unknown") - [react/nested-text {:style style/intro-header-description} - (i18n/label :t/joined-group-chat-description - {:username "" - :group-name chat-name}) - [{:style {:color colors/black}} inviter-name]] - [react/text {:style style/intro-header-description} - (i18n/label :t/created-group-chat-description - {:group-name chat-name})])))))) + (not public?) + [group-chat-inviter-description-container chat-id chat-name])) diff --git a/src/status_im/ui/screens/chat/input/input.cljs b/src/status_im/ui/screens/chat/input/input.cljs index 501f4be08d..6cb5c6f6de 100644 --- a/src/status_im/ui/screens/chat/input/input.cljs +++ b/src/status_im/ui/screens/chat/input/input.cljs @@ -33,21 +33,6 @@ :placeholder-text-color colors/gray :auto-capitalize :sentences}]) -(defview reply-message [from alias content] - (letsubs [{:keys [ens-name]} [:contacts/contact-name-by-identity from] - current-public-key [:multiaccount/public-key]] - [react/scroll-view {:style style/reply-message-content} - [react/view {:style style/reply-message-to-container} - (chat-utils/format-reply-author from alias ens-name current-public-key style/reply-message-author)] - (if (:image content) - [react/image {:style {:width 56 - :height 56 - :background-color :black - :border-radius 4} - :source {:uri (:image content)}}] - [react/text {:style (assoc (message-style/style-message-text false) :font-size 14) - :number-of-lines 3} (:text content)])])) - (defn close-button [on-press] [react/touchable-highlight {:style style/cancel-reply-highlight @@ -58,12 +43,6 @@ :height 19 :color colors/white}]]) -(defn reply-message-view [{:keys [content from alias]}] - [react/view {:style style/reply-message} - [photos/member-photo from] - [reply-message from alias content] - [close-button #(re-frame/dispatch [:chat.ui/cancel-message-reply])]]) - (defn send-image-view [{:keys [uri]}] [react/view {:style style/reply-message} [react/image {:style {:width 56 :height 56 @@ -71,6 +50,29 @@ :source {:uri uri}}] [close-button #(re-frame/dispatch [:chat.ui/cancel-sending-image])]]) +(defview reply-message [from message-text image] + (letsubs [contact-name [:contacts/contact-name-by-identity from] + current-public-key [:multiaccount/public-key]] + [react/scroll-view {:style style/reply-message-content} + [react/view {:style style/reply-message-to-container} + (chat-utils/format-reply-author from contact-name current-public-key style/reply-message-author)] + (if image + [react/image {:style {:width 56 + :height 56 + :background-color :black + :border-radius 4} + :source {:uri image}}] + [react/text {:style (assoc (message-style/style-message-text false) :font-size 14) + :number-of-lines 3} message-text])])) + +(defview reply-message-view [] + (letsubs [{:keys [content from] :as message} [:chats/reply-message]] + (when message + [react/view {:style style/reply-message} + [photos/member-photo from] + [reply-message from (:text content) (:image content)] + [close-button #(re-frame/dispatch [:chat.ui/cancel-message-reply])]]))) + (defview container [] (letsubs [mainnet? [:mainnet?] input-text [:chats/current-chat-input-text] diff --git a/src/status_im/ui/screens/chat/message/gap.cljs b/src/status_im/ui/screens/chat/message/gap.cljs index 66b6e8bc59..d5aa38809c 100644 --- a/src/status_im/ui/screens/chat/message/gap.cljs +++ b/src/status_im/ui/screens/chat/message/gap.cljs @@ -21,15 +21,15 @@ (views/defview gap [{:keys [gaps first-gap?]} idx list-ref] - (views/letsubs [{:keys [range intro-status]} [:chats/current-chat] + (views/letsubs [range [:chats/range] + {:keys [might-have-join-time-messages?]} [:chats/current-raw-chat] in-progress? [:chats/fetching-gap-in-progress? (if first-gap? [:first-gap] (:ids gaps))] connected? [:mailserver/connected?]] - (let [ids (:ids gaps) - intro-loading? (= intro-status :loading)] - (when-not (and first-gap? intro-loading?) + (let [ids (:ids gaps)] + (when-not (and first-gap? might-have-join-time-messages?) [react/view {:style style/gap-container} [react/touchable-highlight {:on-press (when (and connected? (not in-progress?)) diff --git a/src/status_im/ui/screens/chat/message/message.cljs b/src/status_im/ui/screens/chat/message/message.cljs index 2946244579..1a53dca15e 100644 --- a/src/status_im/ui/screens/chat/message/message.cljs +++ b/src/status_im/ui/screens/chat/message/message.cljs @@ -18,8 +18,8 @@ (:require-macros [status-im.utils.views :refer [defview letsubs]])) (defview mention-element [from] - (letsubs [{:keys [ens-name alias]} [:contacts/contact-name-by-identity from]] - (if ens-name (str "@" ens-name) alias))) + (letsubs [contact-name [:contacts/contact-name-by-identity from]] + contact-name)) (defn message-timestamp ([message] @@ -38,14 +38,13 @@ appender]) (defview quoted-message - [_ {:keys [from text image]} outgoing current-public-key public?] - (letsubs [{:keys [ens-name alias]} [:contacts/contact-name-by-identity from]] + [_ {:keys [from text]} outgoing current-public-key public?] + (letsubs [contact-name [:contacts/contact-name-by-identity from]] [react/view {:style (style/quoted-message-container outgoing)} [react/view {:style style/quoted-message-author-container} [chat.utils/format-reply-author from - alias - ens-name + contact-name current-public-key (partial style/quoted-message-author outgoing)]] (if (and image @@ -239,8 +238,8 @@ nil))) (defview message-author-name [from alias] - (letsubs [{:keys [ens-name]} [:contacts/contact-name-by-identity from]] - (chat.utils/format-author alias style/message-author-name-container ens-name))) + (letsubs [contact-name [:contacts/raw-contact-name-by-identity from]] + (chat.utils/format-author (or contact-name alias) style/message-author-name-container))) (defn message-content-wrapper "Author, userpic and delivery wrapper" @@ -261,7 +260,6 @@ (when display-username? [react/touchable-opacity {:style style/message-author-touchable :on-press #(re-frame/dispatch [:chat.ui/show-profile from])} - ;;TODO (perf) move to event [message-author-name from alias]]) ;;MESSAGE CONTENT content]] diff --git a/src/status_im/ui/screens/chat/sheets.cljs b/src/status_im/ui/screens/chat/sheets.cljs index 79361e7223..bede117ca4 100644 --- a/src/status_im/ui/screens/chat/sheets.cljs +++ b/src/status_im/ui/screens/chat/sheets.cljs @@ -4,7 +4,6 @@ [status-im.ui.components.react :as react] [status-im.ui.components.list-selection :as list-selection] [status-im.utils.universal-links.core :as universal-links] - [status-im.multiaccounts.core :as multiaccounts] [status-im.ui.components.chat-icon.screen :as chat-icon] [status-im.ui.components.colors :as colors] [status-im.utils.platform :as platform] @@ -27,40 +26,42 @@ :color colors/gray}} (i18n/label helper)]]) -(defn chat-actions [{:keys [chat-id contact chat-name]}] - [react/view - [list-item/list-item - {:theme :action - :icon (multiaccounts/displayed-photo contact) - :title [view-profile {:name chat-name - :helper :t/view-profile}] - :accessibility-label :view-chat-details-button - :accessories [:chevron] - :on-press #(hide-sheet-and-dispatch [:chat.ui/show-profile chat-id])}] - [list-item/list-item - {:theme :action - :title :t/mark-all-read - :accessibility-label :mark-all-read-button - :icon :main-icons/check - :on-press #(hide-sheet-and-dispatch [:chat.ui/mark-all-read-pressed chat-id])}] - [list-item/list-item - {:theme :action - :title :t/clear-history - :accessibility-label :clear-history-button - :icon :main-icons/close - :on-press #(hide-sheet-and-dispatch [:chat.ui/clear-history-pressed chat-id])}] - [list-item/list-item - {:theme :action - :title :t/fetch-history - :accessibility-label :fetch-history-button - :icon :main-icons/arrow-down - :on-press #(hide-sheet-and-dispatch [:chat.ui/fetch-history-pressed chat-id])}] - [list-item/list-item - {:theme :action-destructive - :title :t/delete-chat - :accessibility-label :delete-chat-button - :icon :main-icons/delete - :on-press #(hide-sheet-and-dispatch [:chat.ui/remove-chat-pressed chat-id])}]]) +(defn one-to-one-chat-actions [{:keys [chat-id]}] + (let [photo @(re-frame/subscribe [:chats/photo-path chat-id]) + contact-name @(re-frame/subscribe [:contacts/contact-name-by-identity chat-id])] + [react/view + [list-item/list-item + {:theme :action + :icon photo + :title [view-profile {:name contact-name + :helper :t/view-profile}] + :accessibility-label :view-chat-details-button + :accessories [:chevron] + :on-press #(hide-sheet-and-dispatch [:chat.ui/show-profile chat-id])}] + [list-item/list-item + {:theme :action + :title :t/mark-all-read + :accessibility-label :mark-all-read-button + :icon :main-icons/check + :on-press #(hide-sheet-and-dispatch [:chat.ui/mark-all-read-pressed chat-id])}] + [list-item/list-item + {:theme :action + :title :t/clear-history + :accessibility-label :clear-history-button + :icon :main-icons/close + :on-press #(hide-sheet-and-dispatch [:chat.ui/clear-history-pressed chat-id])}] + [list-item/list-item + {:theme :action + :title :t/fetch-history + :accessibility-label :fetch-history-button + :icon :main-icons/arrow-down + :on-press #(hide-sheet-and-dispatch [:chat.ui/fetch-history-pressed chat-id])}] + [list-item/list-item + {:theme :action-destructive + :title :t/delete-chat + :accessibility-label :delete-chat-button + :icon :main-icons/delete + :on-press #(hide-sheet-and-dispatch [:chat.ui/remove-chat-pressed chat-id])}]])) (defn public-chat-actions [{:keys [chat-id]}] (let [link (universal-links/generate-link :public-chat :external chat-id) @@ -101,15 +102,15 @@ :on-press #(hide-sheet-and-dispatch [:chat.ui/remove-chat-pressed chat-id])}]])) (defn group-chat-actions [] - (fn [{:keys [chat-id contact group-chat chat-name color online]}] - (let [joined @(re-frame/subscribe [:group-chat/chat-joined? chat-id])] + (fn [{:keys [chat-id group-chat chat-name color]}] + (let [{:keys [joined?]} @(re-frame/subscribe [:group-chat/inviter-info chat-id])] [react/view [list-item/list-item {:theme :action :title [view-profile {:name chat-name :helper :t/group-info}] :icon [chat-icon/chat-icon-view-chat-sheet - contact group-chat chat-name color online] + chat-id group-chat chat-name color] :accessories [:chevron] :on-press #(hide-sheet-and-dispatch [:show-group-chat-profile chat-id])}] [list-item/list-item @@ -130,7 +131,7 @@ :accessibility-label :fetch-history-button :icon :main-icons/arrow-down :on-press #(hide-sheet-and-dispatch [:chat.ui/fetch-history-pressed chat-id])}] - (when joined + (when joined? [list-item/list-item {:theme :action :title :t/leave-chat @@ -143,7 +144,7 @@ (cond public? [public-chat-actions current-chat] group-chat [group-chat-actions current-chat] - :else [chat-actions current-chat])) + :else [one-to-one-chat-actions current-chat])) (defn options [chat-id message-id] (fn [] @@ -162,16 +163,16 @@ :accessibility-label :delete-transaction-button :on-press #(hide-sheet-and-dispatch [:chat.ui/delete-message chat-id message-id])}]])) -(defn message-long-press [{:keys [content identicon from outgoing] :as message}] +(defn message-long-press [{:keys [content from outgoing] :as message}] (fn [] - (let [{:keys [ens-name alias]} @(re-frame/subscribe [:contacts/contact-name-by-identity from])] + (let [photo @(re-frame/subscribe [:chats/photo-path from]) + contact-name @(re-frame/subscribe [:contacts/contact-name-by-identity from])] [react/view (when-not outgoing [list-item/list-item {:theme :action - :icon (multiaccounts/displayed-photo {:identicon identicon - :public-key from}) - :title [view-profile {:name (or ens-name alias) + :icon photo + :title [view-profile {:name contact-name :helper :t/view-profile}] :accessibility-label :view-chat-details-button :accessories [:chevron] @@ -197,15 +198,15 @@ (re-frame/dispatch [:bottom-sheet/hide-sheet]) (list-selection/open-share {:message (:text content)}))}])]))) -(defn sticker-long-press [{:keys [from identicon]}] +(defn sticker-long-press [{:keys [from]}] (fn [] - (let [{:keys [ens-name alias]} @(re-frame/subscribe [:contacts/contact-name-by-identity from])] + (let [photo @(re-frame/subscribe [:chats/photo-path from]) + contact-name @(re-frame/subscribe [:contacts/contact-name-by-identity from])] [react/view [list-item/list-item {:theme :action - :icon (multiaccounts/displayed-photo {:identicon identicon - :public-key from}) - :title [view-profile {:name (or ens-name alias) + :icon photo + :title [view-profile {:name contact-name :helper :t/view-profile}] :accessibility-label :view-chat-details-button :accessories [:chevron] diff --git a/src/status_im/ui/screens/chat/styles/main.cljs b/src/status_im/ui/screens/chat/styles/main.cljs index 5c913b26c6..cc663b546a 100644 --- a/src/status_im/ui/screens/chat/styles/main.cljs +++ b/src/status_im/ui/screens/chat/styles/main.cljs @@ -47,8 +47,8 @@ :margin-right 6}) (defn intro-header-container - [status no-messages] - (if (or no-messages (= status (or :loading :empty))) + [loading-messages? no-messages?] + (if (or loading-messages? no-messages?) {:flex 1 :flex-direction :column :justify-content :center @@ -143,4 +143,4 @@ (def tribute-received-note {:font-size 13 :line-height 18 - :text-align :center}) \ No newline at end of file + :text-align :center}) diff --git a/src/status_im/ui/screens/chat/toolbar_content.cljs b/src/status_im/ui/screens/chat/toolbar_content.cljs index c8fe47daea..d6bad19039 100644 --- a/src/status_im/ui/screens/chat/toolbar_content.cljs +++ b/src/status_im/ui/screens/chat/toolbar_content.cljs @@ -5,64 +5,48 @@ [status-im.ui.screens.chat.styles.main :as st]) (:require-macros [status-im.utils.views :refer [defview letsubs]])) -(defn- in-progress-text [{:keys [highestBlock currentBlock startBlock]}] - (let [total (- highestBlock startBlock) - ready (- currentBlock startBlock) - percentage (if (zero? ready) - 0 - (->> (/ ready total) - (* 100) - (.round js/Math)))] - - (str (i18n/label :t/sync-in-progress) " " percentage "% " currentBlock))) - -(defview last-activity [{:keys [sync-state accessibility-label]}] - (letsubs [state [:sync-data]] - [react/text {:style st/last-activity-text - :accessibility-label accessibility-label} - (case sync-state - :in-progress (in-progress-text state) - :synced (i18n/label :t/sync-synced))])) - -(defn- group-last-activity [{:keys [contacts sync-state public?]}] - (if (or (= sync-state :in-progress) - (= sync-state :synced)) - [last-activity {:sync-state sync-state}] - [react/view {:flex-direction :row} - [react/text {:style st/toolbar-subtitle} - (if public? - (i18n/label :t/public-group-status) - (let [cnt (count contacts)] - (if (zero? cnt) - (i18n/label :members-active-none) - (i18n/label-pluralize cnt :t/members-active))))]])) - -(defn- contact-indicator [{:keys [added?]}] +(defn- group-last-activity [{:keys [contacts public?]}] [react/view {:flex-direction :row} [react/text {:style st/toolbar-subtitle} - (if added? - (i18n/label :chat-is-a-contact) - (i18n/label :chat-is-not-a-contact))]]) + (if public? + (i18n/label :t/public-group-status) + (let [cnt (count contacts)] + (if (zero? cnt) + (i18n/label :members-active-none) + (i18n/label-pluralize cnt :t/members-active))))]]) + +(defview one-to-one-name [from] + (letsubs [contact-name [:contacts/contact-name-by-identity from]] + contact-name)) + +(defview contact-indicator [contact-id] + (letsubs [added? [:contacts/contact-added? contact-id]] + [react/view {:flex-direction :row} + [react/text {:style st/toolbar-subtitle} + (if added? + (i18n/label :chat-is-a-contact) + (i18n/label :chat-is-not-a-contact))]])) (defview toolbar-content-view [] - (letsubs [{:keys [group-chat color online contacts chat-name contact public?]} - [:chats/current-chat] - sync-state [:sync-state]] - (let [has-subtitle? (or group-chat (not= :done sync-state))] - [react/view {:style st/toolbar-container} - [react/view {:margin-right 10} - [chat-icon.screen/chat-icon-view-toolbar contact group-chat chat-name color online]] - [react/view {:style st/chat-name-view} - [react/text {:style st/chat-name-text - :number-of-lines 1 - :accessibility-label :chat-name-text} - chat-name] - (when contact - [contact-indicator contact]) - (if group-chat - [group-last-activity {:contacts contacts - :public? public? - :sync-state sync-state}] - (when has-subtitle? - [last-activity {:sync-state sync-state - :accessibility-label :last-seen-text}]))]]))) + (letsubs [{:keys [group-chat + color + chat-id + contacts + chat-name + public?]} + [:chats/current-chat]] + [react/view {:style st/toolbar-container} + [react/view {:margin-right 10} + [chat-icon.screen/chat-icon-view-toolbar chat-id group-chat chat-name color]] + [react/view {:style st/chat-name-view} + [react/text {:style st/chat-name-text + :number-of-lines 1 + :accessibility-label :chat-name-text} + (if group-chat + chat-name + [one-to-one-name chat-id])] + (when-not group-chat + [contact-indicator chat-id]) + (when group-chat + [group-last-activity {:contacts contacts + :public? public?}])]])) diff --git a/src/status_im/ui/screens/chat/utils.cljs b/src/status_im/ui/screens/chat/utils.cljs index 08c4142640..ce25f66ef0 100644 --- a/src/status_im/ui/screens/chat/utils.cljs +++ b/src/status_im/ui/screens/chat/utils.cljs @@ -4,30 +4,26 @@ [status-im.ui.components.react :as react] [status-im.ui.components.colors :as colors])) -(defn format-author [alias style name] +(defn format-author [contact-name style] (let [additional-styles (style false)] - (if name - (let [name (subs name 0 80)] + (if (= (aget contact-name 0) "@") + (let [trimmed-name (subs contact-name 0 81)] [react/text {:number-of-lines 2 :style (merge {:color colors/blue :font-size 13 :line-height 18 :font-weight "500"} additional-styles)} - (str "@" (or (stateofus/username name) name))]) + (or (stateofus/username trimmed-name) trimmed-name)]) [react/text {:style (merge {:color colors/gray :font-size 12 :line-height 18 :font-weight "400"} additional-styles)} - alias]))) + contact-name]))) (def ^:private reply-symbol "↪ ") -(defn format-reply-author [from alias username current-public-key style] - (let [reply-name (or (some->> username - (str "@") - (str reply-symbol)) - (str reply-symbol alias))] - (or (and (= from current-public-key) - [react/text {:style (style true)} - (str reply-symbol (i18n/label :t/You))]) - (format-author (subs reply-name 0 80) style false)))) \ No newline at end of file +(defn format-reply-author [from username current-public-key style] + (or (and (= from current-public-key) + [react/text {:style (style true)} + (str reply-symbol (i18n/label :t/You))]) + (format-author username style))) diff --git a/src/status_im/ui/screens/chat/views.cljs b/src/status_im/ui/screens/chat/views.cljs index ef47f28ab1..a225d02d6c 100644 --- a/src/status_im/ui/screens/chat/views.cljs +++ b/src/status_im/ui/screens/chat/views.cljs @@ -1,8 +1,6 @@ (ns status-im.ui.screens.chat.views (:require [re-frame.core :as re-frame] - [status-im.contact.db :as contact.db] [status-im.i18n :as i18n] - [status-im.multiaccounts.core :as multiaccounts] [status-im.ui.components.chat-icon.screen :as chat-icon.screen] [status-im.ui.components.colors :as colors] [status-im.ui.components.connectivity.view :as connectivity] @@ -41,56 +39,86 @@ [sheets/actions current-chat]) :height 256}])}]}]) -(defn add-contact-bar - [public-key] - [react/touchable-highlight - {:on-press - #(re-frame/dispatch [:contact.ui/add-to-contact-pressed public-key]) - :accessibility-label :add-to-contacts-button} - [react/view {:style (style/add-contact)} - [vector-icons/icon :main-icons/add - {:color colors/blue}] - [react/i18n-text {:style style/add-contact-text :key :add-to-contacts}]]]) +(defview add-contact-bar [public-key] + (letsubs [added? [:contacts/contact-added? public-key]] + (when-not added? + [react/touchable-highlight + {:on-press + #(re-frame/dispatch [:contact.ui/add-to-contact-pressed public-key]) + :accessibility-label :add-to-contacts-button} + [react/view {:style (style/add-contact)} + [vector-icons/icon :main-icons/add + {:color colors/blue}] + [react/i18n-text {:style style/add-contact-text :key :add-to-contacts}]]]))) -(defn intro-header - [contact] +(defn intro-header [name] [react/text {:style (assoc style/intro-header-description :margin-bottom 32)} (str (i18n/label :t/empty-chat-description-one-to-one) - (multiaccounts/displayed-name contact))]) + name)]) + +(defn chat-intro [{:keys [chat-id + chat-name + group-chat + contact-name + public? + color + loading-messages? + no-messages?]}] + [react/view (style/intro-header-container loading-messages? no-messages?) + ;; Icon section + [react/view {:style {:margin-top 42 + :margin-bottom 24}} + [chat-icon.screen/chat-intro-icon-view + chat-name chat-id group-chat + {:default-chat-icon (style/intro-header-icon 120 color) + :default-chat-icon-text style/intro-header-icon-text + :size 120}]] + ;; Chat title section + [react/text {:style (style/intro-header-chat-name)} (if group-chat chat-name contact-name)] + ;; Description section + (if group-chat + [chat.group/group-chat-description-container {:chat-id chat-id + :loading-messages? loading-messages? + :chat-name chat-name + :public? public? + :no-messages? no-messages?}] + [react/text {:style (assoc style/intro-header-description + :margin-bottom 32)} + + (str + (i18n/label :t/empty-chat-description-one-to-one) + contact-name)])]) + +(defview chat-intro-one-to-one [{:keys [chat-id] :as opts}] + (letsubs [contact-name [:contacts/contact-name-by-identity chat-id]] + (chat-intro (assoc opts :contact-name contact-name)))) (defn chat-intro-header-container - [{:keys [group-chat name pending-invite-inviter-name color chat-id chat-name - public? contact intro-status] :as chat} + [{:keys [group-chat + might-have-join-time-messages? + color chat-id chat-name + public?]} no-messages] - (let [icon-text (if public? chat-id name) - intro-name (if public? chat-name (multiaccounts/displayed-name contact))] - (when (or pending-invite-inviter-name - (not= (get-in contact [:tribute-to-talk :snt-amount]) 0)) - [react/touchable-without-feedback - {:style {:flex 1 - :align-items :flex-start} - :on-press (fn [_] - (re-frame/dispatch - [:chat.ui/set-chat-ui-props {:input-bottom-sheet nil}]) - (react/dismiss-keyboard!))} - [react/view (style/intro-header-container intro-status no-messages) - ;; Icon section - [react/view {:style {:margin-top 42 - :margin-bottom 24}} - [chat-icon.screen/chat-intro-icon-view - icon-text chat-id - {:default-chat-icon (style/intro-header-icon 120 color) - :default-chat-icon-text style/intro-header-icon-text - :size 120}]] - ;; Chat title section - [react/text {:style (style/intro-header-chat-name)} - (if group-chat chat-name intro-name)] - ;; Description section - (if group-chat - [chat.group/group-chat-description-container chat] - [intro-header contact])]]))) + [react/touchable-without-feedback + {:style {:flex 1 + :align-items :flex-start} + :on-press (fn [_] + (re-frame/dispatch + [:chat.ui/set-chat-ui-props {:input-bottom-sheet nil}]) + (react/dismiss-keyboard!))} + (let [opts + {:chat-id chat-id + :group-chat group-chat + :chat-name chat-name + :public? public? + :color color + :loading-messages? might-have-join-time-messages? + :no-messages? no-messages}] + (if group-chat + [chat-intro opts] + [chat-intro-one-to-one opts]))]) (defonce messages-list-ref (atom nil)) @@ -110,15 +138,16 @@ (debounce/debounce-and-dispatch [:chat.ui/message-visibility-changed e] 5000)) (defview messages-view - [{:keys [public? group-chat chat-id pending-invite-inviter-name] :as chat}] + [{:keys [group-chat chat-id public?] :as chat}] (letsubs [messages [:chats/current-chat-messages-stream] + no-messages? [:chats/current-chat-no-messages?] current-public-key [:multiaccount/public-key]] [list/flat-list {:key-fn #(or (:message-id %) (:value %)) :ref #(reset! messages-list-ref %) - :header (when pending-invite-inviter-name + :header (when (and group-chat (not public?)) [chat.group/group-chat-footer chat-id]) - :footer [chat-intro-header-container chat (empty? messages)] + :footer [chat-intro-header-container chat no-messages?] :data messages :inverted true :render-fn (fn [{:keys [outgoing type] :as message} idx] @@ -154,14 +183,13 @@ [empty-bottom-sheet]))) (defview chat [] - (letsubs [{:keys [chat-id show-input? group-chat contact] :as current-chat} + (letsubs [{:keys [chat-id show-input? group-chat] :as current-chat} [:chats/current-chat]] [react/view {:style {:flex 1}} [connectivity/connectivity [topbar current-chat] [react/view {:style {:flex 1}} - ;;TODO contact.db/added? looks weird here, move to events - (when (and (not group-chat) (not (contact.db/added? contact))) + (when-not group-chat [add-contact-bar chat-id]) [messages-view current-chat]]] (when show-input? diff --git a/src/status_im/ui/screens/ens/views.cljs b/src/status_im/ui/screens/ens/views.cljs index ffd399cc3f..6e2633dba0 100644 --- a/src/status_im/ui/screens/ens/views.cljs +++ b/src/status_im/ui/screens/ens/views.cljs @@ -18,7 +18,9 @@ [status-im.ui.components.radio :as radio] [status-im.ui.components.react :as react] [status-im.ui.components.topbar :as topbar] + [status-im.ui.screens.chat.utils :as chat.utils] [status-im.ui.screens.chat.message.message :as message] + [status-im.ui.screens.chat.styles.message.message :as message.style] [status-im.ui.screens.chat.photos :as photos] [status-im.ui.screens.profile.components.views :as profile.components] [status-im.utils.debounce :as debounce]) @@ -624,7 +626,11 @@ [name-item {:name name :hide-chevron? true :action action}]] [radio/radio (= name preferred-name)]]]))]]]]) -(defn- registered [names {:keys [preferred-name public-key] :as account} _] +(views/defview my-name [] + (views/letsubs [contact-name [:multiaccount/my-name]] + (chat.utils/format-author contact-name message.style/message-author-name-container))) + +(defn- registered [names {:keys [preferred-name] :as account} _] [react/view {:style {:flex 1}} [react/scroll-view [react/view {:style {:margin-top 8}} @@ -663,7 +669,7 @@ :timestamp-str "9:41 AM"}] [react/view [react/view {:padding-left 72} - [message/message-author-name public-key]] + [my-name]] [react/view {:flex-direction :row} [react/view {:padding-left 16 :padding-right 8 :padding-top 4} [photos/photo (multiaccounts/displayed-photo account) {:size 36}]] diff --git a/src/status_im/ui/screens/home/views/inner_item.cljs b/src/status_im/ui/screens/home/views/inner_item.cljs index 7d8227bc22..6b88030b15 100644 --- a/src/status_im/ui/screens/home/views/inner_item.cljs +++ b/src/status_im/ui/screens/home/views/inner_item.cljs @@ -15,8 +15,8 @@ (:require-macros [status-im.utils.views :refer [defview letsubs]])) (defview mention-element [from] - (letsubs [{:keys [ens-name alias]} [:contacts/contact-name-by-identity from]] - (if ens-name (str "@" ens-name) alias))) + (letsubs [contact-name [:contacts/contact-name-by-identity from]] + contact-name)) (defn render-subheader-inline [acc {:keys [type destination literal children]}] (case type @@ -108,20 +108,20 @@ (defn home-list-item [[_ home-item]] (let [{:keys [chat-id chat-name color online group-chat - public? contact timestamp last-message]} + public? timestamp last-message]} home-item private-group? (and group-chat (not public?)) - public-group? (and group-chat public?) - ;;TODO (perf) move to event - truncated-chat-name (utils/truncate-str chat-name 30)] + public-group? (and group-chat public?)] [list-item/list-item {:icon [chat-icon.screen/chat-icon-view-chat-list - contact group-chat truncated-chat-name color online false] + chat-id group-chat chat-name color online false] :title-prefix (cond private-group? :main-icons/tiny-group public-group? :main-icons/tiny-public :else nil) - :title truncated-chat-name + :title (if group-chat + (utils/truncate-str chat-name 30) + @(re-frame/subscribe [:contacts/contact-name-by-identity chat-id])) :title-accessibility-label :chat-name-text :title-row-accessory [message-timestamp (if (pos? (:whisper-timestamp last-message)) (:whisper-timestamp last-message)