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"}