From c440b7a3a7b2f76b446f7252dcd516ee0976b318 Mon Sep 17 00:00:00 2001 From: Roman Volosovskyi Date: Sun, 9 Dec 2018 17:55:28 +0200 Subject: [PATCH] [slow sign in] Denorlmalize last message The last messages of the chats are necessary to properly show the chat list, which is shown right after signing in. Before this commit, the last message was retrieved as one of 20 last messages fetched for each chat. Implementation: - `:last-message-content` and `:last-message-type` fields were added to `chat` entity - both fields are updated when messages are received/sent - loading of the last 20 messages for each chat was removed as initialization step --- src/status_im/chat/models/loading.cljs | 38 +++---------------- src/status_im/chat/models/message.cljs | 30 +++++++++++++-- src/status_im/chat/subs.cljs | 12 ------ src/status_im/data_store/chats.cljs | 16 +++++--- src/status_im/data_store/messages.cljs | 2 +- .../realm/schemas/account/chat.cljs | 7 ++++ .../realm/schemas/account/core.cljs | 18 ++++++++- .../realm/schemas/account/migrations.cljs | 18 +++++++++ src/status_im/events.cljs | 8 ---- src/status_im/ui/screens/chat/views.cljs | 21 +++++++--- .../screens/desktop/main/tabs/home/views.cljs | 24 ++++++------ src/status_im/ui/screens/home/views.cljs | 9 +---- .../ui/screens/home/views/inner_item.cljs | 13 ++++--- .../cljs/status_im/test/data_store/chats.cljs | 11 ++++-- 14 files changed, 129 insertions(+), 98 deletions(-) diff --git a/src/status_im/chat/models/loading.cljs b/src/status_im/chat/models/loading.cljs index beabba41a0..72507c6acd 100644 --- a/src/status_im/chat/models/loading.cljs +++ b/src/status_im/chat/models/loading.cljs @@ -66,42 +66,15 @@ message-id))) statuses)) -(fx/defn load-chats-messages - [{:keys [db get-stored-messages get-stored-user-statuses get-referenced-messages] - :as cofx}] - (let [chats (:chats db) - public-key (accounts.db/current-public-key cofx)] - (fx/merge - cofx - {:db (assoc - db :chats - (reduce - (fn [chats chat-id] - (let [{:keys [messages]} (get-stored-messages chat-id) - chat-messages (index-messages messages) - message-ids (keys chat-messages) - statuses (get-stored-user-statuses chat-id message-ids) - unviewed-messages-ids (get-unviewed-messages-ids statuses public-key)] - (update - chats - chat-id - assoc - - :messages chat-messages - :message-statuses statuses - :loaded-unviewed-messages-ids unviewed-messages-ids - :referenced-messages (into {} - (get-referenced-messages - (get-referenced-ids chat-messages)))))) - chats - (keys chats)))} - (group-messages)))) - (fx/defn initialize-chats "Initialize all persisted chats on startup" [{:keys [db default-dapps all-stored-chats] :as cofx}] (let [chats (reduce (fn [acc {:keys [chat-id] :as chat}] - (assoc acc chat-id chat)) + (assoc acc chat-id + (assoc chat + :messages-initialized? false + :referenced-messages {} + :messages empty-message-map))) {} all-stored-chats)] (fx/merge cofx @@ -154,6 +127,7 @@ loaded-unviewed-messages (get-unviewed-messages-ids new-statuses public-key)] (fx/merge cofx {:db (-> db + (assoc-in [:chats current-chat-id :messages-initialized?] true) (update-in [:chats current-chat-id :messages] merge indexed-messages) (update-in [:chats current-chat-id :message-statuses] merge new-statuses) (update-in [:chats current-chat-id :referenced-messages] diff --git a/src/status_im/chat/models/message.cljs b/src/status_im/chat/models/message.cljs index 2352051bb0..7ca339b705 100644 --- a/src/status_im/chat/models/message.cljs +++ b/src/status_im/chat/models/message.cljs @@ -266,6 +266,26 @@ (= from current-public-key)) messages)))))) +(defn- update-last-message [all-chats chat-id] + (let [{:keys [messages message-groups]} + (get all-chats chat-id) + {:keys [content message-type]} + (->> (chat.db/sort-message-groups message-groups messages) + first + second + last + :message-id + (get messages))] + (chat-model/upsert-chat + {:chat-id chat-id + :last-message-content content + :last-message-type message-type}))) + +(fx/defn update-last-messages + [{:keys [db] :as cofx} chat-ids] + (apply fx/merge cofx + (map (partial update-last-message (:chats db)) chat-ids))) + (fx/defn receive-many [{:keys [now] :as cofx} messages] (let [valid-messages (keep #(when-let [chat-id (extract-chat-id cofx %)] @@ -293,7 +313,8 @@ messages-fx-fns groups-fx-fns (when platform/desktop? - [(chat-model/update-dock-badge-label)]))))) + [(chat-model/update-dock-badge-label)]) + [(update-last-messages chat-ids)])))) (defn system-message [{:keys [now] :as cofx} {:keys [clock-value chat-id content from]}] (let [{:keys [last-clock-value]} (get-in cofx [:db :chats chat-id]) @@ -347,8 +368,11 @@ :raw-payload-hash (transport.utils/sha3 raw-payload))] (fx/merge cofx - (chat-model/upsert-chat {:chat-id chat-id - :timestamp now}) + (chat-model/upsert-chat + {:chat-id chat-id + :timestamp now + :last-message-content (:content message) + :last-message-type (:message-type message)}) (add-message false message-with-id true) (add-own-status chat-id message-id :sending) (send chat-id message-id wrapped-record)))) diff --git a/src/status_im/chat/subs.cljs b/src/status_im/chat/subs.cljs index 980b82fa11..72c2b7c945 100644 --- a/src/status_im/chat/subs.cljs +++ b/src/status_im/chat/subs.cljs @@ -221,18 +221,6 @@ (when (= id (:public-key account)) (:photo-path account))))) -(re-frame/reg-sub - :chats/last-message - (fn [[_ chat-id]] - (re-frame/subscribe [:chats/chat chat-id])) - (fn [{:keys [messages message-groups]}] - (->> (chat.db/sort-message-groups message-groups messages) - first - second - last - :message-id - (get messages)))) - (re-frame/reg-sub :chats/unread-messages-number :<- [:chats/active-chats] diff --git a/src/status_im/data_store/chats.cljs b/src/status_im/data_store/chats.cljs index a6e4595d7b..b84f412fa6 100644 --- a/src/status_im/data_store/chats.cljs +++ b/src/status_im/data_store/chats.cljs @@ -1,10 +1,12 @@ (ns status-im.data-store.chats (:require [goog.object :as object] - [cljs.core.async :as async] [re-frame.core :as re-frame] [status-im.utils.ethereum.core :as utils.ethereum] [status-im.utils.clocks :as utils.clocks] - [status-im.data-store.realm.core :as core])) + [status-im.data-store.realm.core :as core] + [status-im.data-store.messages :as messages] + [status-im.utils.core :as utils] + [cljs.tools.reader.edn :as edn])) (defn remove-empty-vals "Remove key/value when empty seq or nil" @@ -60,7 +62,9 @@ (update :tags #(into #{} %)) (update :membership-updates (partial unmarshal-membership-updates chat-id)) ;; We cap the clock value to a safe value in case the db has been polluted - (assoc :last-clock-value (get-last-clock-value chat-id)))) + (assoc :last-clock-value (get-last-clock-value chat-id)) + (update :last-message-type keyword) + (update :last-message-content edn/read-string))) (re-frame/reg-cofx :data-store/all-chats @@ -73,12 +77,14 @@ (defn save-chat-tx "Returns tx function for saving chat" - [{:keys [chat-id] :as chat}] + [chat] (fn [realm] (core/create realm :chat - (update chat :membership-updates marshal-membership-updates) + (-> chat + (update :membership-updates marshal-membership-updates) + (utils/update-if-present :last-message-content messages/prepare-content)) true))) ;; Only used in debug mode diff --git a/src/status_im/data_store/messages.cljs b/src/status_im/data_store/messages.cljs index 1f35b36ceb..16b7535d85 100644 --- a/src/status_im/data_store/messages.cljs +++ b/src/status_im/data_store/messages.cljs @@ -66,7 +66,7 @@ (fn [cofx _] (assoc cofx :get-referenced-messages get-references-by-ids))) -(defn- prepare-content [content] +(defn prepare-content [content] (if (string? content) content (pr-str content))) diff --git a/src/status_im/data_store/realm/schemas/account/chat.cljs b/src/status_im/data_store/realm/schemas/account/chat.cljs index f75a704c5c..d917af0c6f 100644 --- a/src/status_im/data_store/realm/schemas/account/chat.cljs +++ b/src/status_im/data_store/realm/schemas/account/chat.cljs @@ -223,3 +223,10 @@ :tags {:type "string[]"} :unviewed-messages-count {:type :int :default 0}}}) + +(def v10 + (update v9 :properties merge + {:last-message-content {:type :string + :optional true} + :last-message-type {:type :string + :optional true}})) diff --git a/src/status_im/data_store/realm/schemas/account/core.cljs b/src/status_im/data_store/realm/schemas/account/core.cljs index 6df783e841..09c2c95a4e 100644 --- a/src/status_im/data_store/realm/schemas/account/core.cljs +++ b/src/status_im/data_store/realm/schemas/account/core.cljs @@ -291,6 +291,19 @@ browser/v8 dapp-permissions/v9]) +(def v28 [chat/v10 + transport/v7 + contact/v3 + message/v9 + mailserver/v11 + mailserver-topic/v1 + user-status/v2 + membership-update/v1 + installation/v2 + local-storage/v1 + browser/v8 + dapp-permissions/v9]) + ;; put schemas ordered by version (def schemas [{:schema v1 :schemaVersion 1 @@ -372,4 +385,7 @@ :migration migrations/v26} {:schema v27 :schemaVersion 27 - :migration migrations/v27}]) + :migration migrations/v27} + {:schema v28 + :schemaVersion 28 + :migration migrations/v28}]) diff --git a/src/status_im/data_store/realm/schemas/account/migrations.cljs b/src/status_im/data_store/realm/schemas/account/migrations.cljs index 0d85ed5fa0..7e8f838c09 100644 --- a/src/status_im/data_store/realm/schemas/account/migrations.cljs +++ b/src/status_im/data_store/realm/schemas/account/migrations.cljs @@ -298,3 +298,21 @@ (doseq [status @statuses-to-be-deleted] (.delete new-realm status)))) + +(defn get-last-message [realm chat-id] + (-> + (.objects realm "message") + (.filtered (str "chat-id=\"" chat-id "\"")) + (.sorted "timestamp" true) + (aget 0))) + +(defn v28 [old-realm new-realm] + (let [chats (.objects new-realm "chat")] + (dotimes [i (.-length chats)] + (let [chat (aget chats i) + chat-id (aget chat "chat-id")] + (when-let [last-message (get-last-message new-realm chat-id)] + (let [content (aget last-message "content") + message-type (aget last-message "message-type")] + (aset chat "last-message-content" content) + (aset chat "last-message-type" message-type))))))) diff --git a/src/status_im/events.cljs b/src/status_im/events.cljs index 9ed7fbd476..ebf0631834 100644 --- a/src/status_im/events.cljs +++ b/src/status_im/events.cljs @@ -91,14 +91,6 @@ (fn [cofx [_ encryption-key error]] (init/handle-init-store-error cofx encryption-key))) -(handlers/register-handler-fx - :load-chats-messages - [(re-frame/inject-cofx :data-store/get-messages) - (re-frame/inject-cofx :data-store/get-referenced-messages) - (re-frame/inject-cofx :data-store/get-user-statuses)] - (fn [cofx] - (chat-loading/load-chats-messages cofx))) - (handlers/register-handler-fx :init-chats [(re-frame/inject-cofx :web3/get-web3) diff --git a/src/status_im/ui/screens/chat/views.cljs b/src/status_im/ui/screens/chat/views.cljs index 8bde7a55bb..f031ea8af3 100644 --- a/src/status_im/ui/screens/chat/views.cljs +++ b/src/status_im/ui/screens/chat/views.cljs @@ -114,13 +114,18 @@ [react/text {:style style/empty-chat-text-name} (:name contact)]] (i18n/label :t/empty-chat-description))]]))) -(defview messages-view [group-chat modal?] +(defview messages-view [chat group-chat modal?] (letsubs [messages [:chats/current-chat-messages-stream] - chat [:chats/current-chat] current-public-key [:account/public-key]] - {:component-did-mount #(re-frame/dispatch [:chat.ui/set-chat-ui-props {:messages-focused? true - :input-focused? false}])} - (if (empty? messages) + {:component-did-mount + (fn [args] + (when-not (:messages-initialized? (second (.-argv (.-props args)))) + (re-frame/dispatch [:chat.ui/load-more-messages])) + (re-frame/dispatch [:chat.ui/set-chat-ui-props + {:messages-focused? true + :input-focused? false}]))} + (if (and (empty? messages) + (:messages-initialized? chat)) [empty-chat-container chat] [list/flat-list {:data messages :key-fn #(or (:message-id %) (:value %)) @@ -134,6 +139,10 @@ :enableEmptySections true :keyboardShouldPersistTaps :handled}]))) +(defview messages-view-wrapper [group-chat modal?] + (letsubs [chat [:chats/current-chat]] + [messages-view chat group-chat modal?])) + (defview chat-root [modal?] (letsubs [{:keys [group-chat public?]} [:chats/current-chat] show-bottom-info? [:chats/current-chat-ui-prop :show-bottom-info?] @@ -151,7 +160,7 @@ [chat-toolbar public? modal?] (if (or (= :chat current-view) modal?) [messages-view-animation - [messages-view group-chat modal?]] + [messages-view-wrapper group-chat modal?]] [react/view style/message-view-preview]) [input/container] (when show-bottom-info? diff --git a/src/status_im/ui/screens/desktop/main/tabs/home/views.cljs b/src/status_im/ui/screens/desktop/main/tabs/home/views.cljs index 26eeaf0129..25a96a824c 100644 --- a/src/status_im/ui/screens/desktop/main/tabs/home/views.cljs +++ b/src/status_im/ui/screens/desktop/main/tabs/home/views.cljs @@ -19,13 +19,18 @@ [status-im.ui.components.action-button.action-button :as action-button] [status-im.utils.config :as config])) -(views/defview chat-list-item-inner-view [{:keys [chat-id name group-chat color public? public-key] :as chat-item}] +(views/defview chat-list-item-inner-view [{:keys [chat-id name group-chat + color public? public-key + last-message-content + last-message-type] + :as chat-item}] (views/letsubs [photo-path [:contacts/chat-photo chat-id] unviewed-messages-count [:chats/unviewed-messages-count chat-id] chat-name [:chats/chat-name chat-id] - current-chat-id [:chats/current-chat-id] - {:keys [content] :as last-message} [:chats/last-message chat-id]] - (let [name (or chat-name + current-chat-id [:chats/current-chat-id]] + (let [last-message {:content last-message-content + :message-type last-message-type} + name (or chat-name (gfycat/generate-gfy public-key)) [unviewed-messages-label large?] [(utils/unread-messages-count unviewed-messages-count) true] current? (= current-chat-id chat-id)] @@ -52,7 +57,7 @@ :style styles/chat-last-message} (if (= constants/content-type-command (:content-type last-message)) [chat-item/command-short-preview last-message] - (or (:text content) + (or (:text last-message-content) (i18n/label :no-messages-yet)))]] [react/view {:style styles/timestamp} [chat-item/message-timestamp (:timestamp last-message)] @@ -140,14 +145,7 @@ (fn [this] (let [[_ loading?] (.. this -props -argv)] (when loading? - (re-frame/dispatch [:init-chats])))) - - :component-did-update - (fn [this [_ old-loading?]] - (let [[_ loading?] (.. this -props -argv)] - (when (and (false? loading?) - (true? old-loading?)) - (re-frame/dispatch [:load-chats-messages]))))} + (re-frame/dispatch [:init-chats]))))} [react/view {:style styles/chat-list-view} [react/view {:style styles/chat-list-header} [search-input search-filter] diff --git a/src/status_im/ui/screens/home/views.cljs b/src/status_im/ui/screens/home/views.cljs index 8fe4439384..00f6d8a8f8 100644 --- a/src/status_im/ui/screens/home/views.cljs +++ b/src/status_im/ui/screens/home/views.cljs @@ -100,14 +100,7 @@ (when loading? (utils/set-timeout #(re-frame/dispatch [:init-chats]) - 100)))) - - :component-did-update - (fn [this [_ old-loading?]] - (let [[_ loading?] (.. this -props -argv)] - (when (and (false? loading?) - (true? old-loading?)) - (re-frame/dispatch [:load-chats-messages]))))} + 100))))} [react/view styles/container [toolbar show-welcome? (and network-initialized? (not rpc-network?)) sync-state latest-block-number] (cond show-welcome? 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 cd8520e0cf..0915b27cc4 100644 --- a/src/status_im/ui/screens/home/views/inner_item.cljs +++ b/src/status_im/ui/screens/home/views/inner_item.cljs @@ -88,9 +88,10 @@ (defview home-list-chat-item-inner-view [{:keys [chat-id name color online group-chat public? public-key - timestamp]}] - (letsubs [last-message [:chats/last-message chat-id] - chat-name [:chats/chat-name chat-id]] + timestamp + last-message-content + last-message-type]}] + (letsubs [chat-name [:chats/chat-name chat-id]] (let [truncated-chat-name (utils/truncate-str chat-name 30)] [react/touchable-highlight {:on-press #(re-frame/dispatch [:chat.ui/navigate-to-chat chat-id])} [react/view styles/chat-container @@ -100,10 +101,10 @@ [react/view styles/item-upper-container [chat-list-item-name truncated-chat-name group-chat public? public-key] [react/view styles/message-status-container - [message-timestamp (or (:timestamp last-message) - timestamp)]]] + [message-timestamp timestamp]]] [react/view styles/item-lower-container - [message-content-text last-message] + [message-content-text {:content last-message-content + :content-type last-message-type}] [unviewed-indicator chat-id]]]]]))) (defn home-list-browser-item-inner-view [{:keys [dapp url name browser-id] :as browser}] diff --git a/test/cljs/status_im/test/data_store/chats.cljs b/test/cljs/status_im/test/data_store/chats.cljs index 1064c8087d..534e7b9743 100644 --- a/test/cljs/status_im/test/data_store/chats.cljs +++ b/test/cljs/status_im/test/data_store/chats.cljs @@ -10,9 +10,14 @@ :admins #{4} :contacts #{2} :tags #{} - :membership-updates []} - (chats/normalize-chat {:admins [4] - :contacts [2]}))))) + :membership-updates [] + :last-message-type :message-type + :last-message-content {:foo "bar"}} + (chats/normalize-chat + {:admins [4] + :contacts [2] + :last-message-type "message-type" + :last-message-content "{:foo \"bar\"}"}))))) (testing "membership-updates" (with-redefs [chats/get-last-clock-value (constantly 42)] (let [raw-events {"1" {:id "1" :type "members-added" :clock-value 10 :members [1 2] :signature "a" :from "id-1"}