[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
This commit is contained in:
Roman Volosovskyi 2018-12-09 17:55:28 +02:00
parent 07e8f6908d
commit c440b7a3a7
No known key found for this signature in database
GPG Key ID: 0238A4B5ECEE70DE
14 changed files with 129 additions and 98 deletions

View File

@ -66,42 +66,15 @@
message-id))) message-id)))
statuses)) 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 (fx/defn initialize-chats
"Initialize all persisted chats on startup" "Initialize all persisted chats on startup"
[{:keys [db default-dapps all-stored-chats] :as cofx}] [{:keys [db default-dapps all-stored-chats] :as cofx}]
(let [chats (reduce (fn [acc {:keys [chat-id] :as chat}] (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)] all-stored-chats)]
(fx/merge cofx (fx/merge cofx
@ -154,6 +127,7 @@
loaded-unviewed-messages (get-unviewed-messages-ids new-statuses public-key)] loaded-unviewed-messages (get-unviewed-messages-ids new-statuses public-key)]
(fx/merge cofx (fx/merge cofx
{:db (-> db {: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 :messages] merge indexed-messages)
(update-in [:chats current-chat-id :message-statuses] merge new-statuses) (update-in [:chats current-chat-id :message-statuses] merge new-statuses)
(update-in [:chats current-chat-id :referenced-messages] (update-in [:chats current-chat-id :referenced-messages]

View File

@ -266,6 +266,26 @@
(= from current-public-key)) (= from current-public-key))
messages)))))) 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 (fx/defn receive-many
[{:keys [now] :as cofx} messages] [{:keys [now] :as cofx} messages]
(let [valid-messages (keep #(when-let [chat-id (extract-chat-id cofx %)] (let [valid-messages (keep #(when-let [chat-id (extract-chat-id cofx %)]
@ -293,7 +313,8 @@
messages-fx-fns messages-fx-fns
groups-fx-fns groups-fx-fns
(when platform/desktop? (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]}] (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]) (let [{:keys [last-clock-value]} (get-in cofx [:db :chats chat-id])
@ -347,8 +368,11 @@
:raw-payload-hash (transport.utils/sha3 raw-payload))] :raw-payload-hash (transport.utils/sha3 raw-payload))]
(fx/merge cofx (fx/merge cofx
(chat-model/upsert-chat {:chat-id chat-id (chat-model/upsert-chat
:timestamp now}) {: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-message false message-with-id true)
(add-own-status chat-id message-id :sending) (add-own-status chat-id message-id :sending)
(send chat-id message-id wrapped-record)))) (send chat-id message-id wrapped-record))))

View File

@ -221,18 +221,6 @@
(when (= id (:public-key account)) (when (= id (:public-key account))
(:photo-path 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 (re-frame/reg-sub
:chats/unread-messages-number :chats/unread-messages-number
:<- [:chats/active-chats] :<- [:chats/active-chats]

View File

@ -1,10 +1,12 @@
(ns status-im.data-store.chats (ns status-im.data-store.chats
(:require [goog.object :as object] (:require [goog.object :as object]
[cljs.core.async :as async]
[re-frame.core :as re-frame] [re-frame.core :as re-frame]
[status-im.utils.ethereum.core :as utils.ethereum] [status-im.utils.ethereum.core :as utils.ethereum]
[status-im.utils.clocks :as utils.clocks] [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 (defn remove-empty-vals
"Remove key/value when empty seq or nil" "Remove key/value when empty seq or nil"
@ -60,7 +62,9 @@
(update :tags #(into #{} %)) (update :tags #(into #{} %))
(update :membership-updates (partial unmarshal-membership-updates chat-id)) (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 ;; 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 (re-frame/reg-cofx
:data-store/all-chats :data-store/all-chats
@ -73,12 +77,14 @@
(defn save-chat-tx (defn save-chat-tx
"Returns tx function for saving chat" "Returns tx function for saving chat"
[{:keys [chat-id] :as chat}] [chat]
(fn [realm] (fn [realm]
(core/create (core/create
realm realm
:chat :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))) true)))
;; Only used in debug mode ;; Only used in debug mode

View File

@ -66,7 +66,7 @@
(fn [cofx _] (fn [cofx _]
(assoc cofx :get-referenced-messages get-references-by-ids))) (assoc cofx :get-referenced-messages get-references-by-ids)))
(defn- prepare-content [content] (defn prepare-content [content]
(if (string? content) (if (string? content)
content content
(pr-str content))) (pr-str content)))

View File

@ -223,3 +223,10 @@
:tags {:type "string[]"} :tags {:type "string[]"}
:unviewed-messages-count {:type :int :unviewed-messages-count {:type :int
:default 0}}}) :default 0}}})
(def v10
(update v9 :properties merge
{:last-message-content {:type :string
:optional true}
:last-message-type {:type :string
:optional true}}))

View File

@ -291,6 +291,19 @@
browser/v8 browser/v8
dapp-permissions/v9]) 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 ;; put schemas ordered by version
(def schemas [{:schema v1 (def schemas [{:schema v1
:schemaVersion 1 :schemaVersion 1
@ -372,4 +385,7 @@
:migration migrations/v26} :migration migrations/v26}
{:schema v27 {:schema v27
:schemaVersion 27 :schemaVersion 27
:migration migrations/v27}]) :migration migrations/v27}
{:schema v28
:schemaVersion 28
:migration migrations/v28}])

View File

@ -298,3 +298,21 @@
(doseq [status @statuses-to-be-deleted] (doseq [status @statuses-to-be-deleted]
(.delete new-realm status)))) (.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)))))))

View File

@ -91,14 +91,6 @@
(fn [cofx [_ encryption-key error]] (fn [cofx [_ encryption-key error]]
(init/handle-init-store-error cofx encryption-key))) (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 (handlers/register-handler-fx
:init-chats :init-chats
[(re-frame/inject-cofx :web3/get-web3) [(re-frame/inject-cofx :web3/get-web3)

View File

@ -114,13 +114,18 @@
[react/text {:style style/empty-chat-text-name} (:name contact)]] [react/text {:style style/empty-chat-text-name} (:name contact)]]
(i18n/label :t/empty-chat-description))]]))) (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] (letsubs [messages [:chats/current-chat-messages-stream]
chat [:chats/current-chat]
current-public-key [:account/public-key]] current-public-key [:account/public-key]]
{:component-did-mount #(re-frame/dispatch [:chat.ui/set-chat-ui-props {:messages-focused? true {:component-did-mount
:input-focused? false}])} (fn [args]
(if (empty? messages) (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] [empty-chat-container chat]
[list/flat-list {:data messages [list/flat-list {:data messages
:key-fn #(or (:message-id %) (:value %)) :key-fn #(or (:message-id %) (:value %))
@ -134,6 +139,10 @@
:enableEmptySections true :enableEmptySections true
:keyboardShouldPersistTaps :handled}]))) :keyboardShouldPersistTaps :handled}])))
(defview messages-view-wrapper [group-chat modal?]
(letsubs [chat [:chats/current-chat]]
[messages-view chat group-chat modal?]))
(defview chat-root [modal?] (defview chat-root [modal?]
(letsubs [{:keys [group-chat public?]} [:chats/current-chat] (letsubs [{:keys [group-chat public?]} [:chats/current-chat]
show-bottom-info? [:chats/current-chat-ui-prop :show-bottom-info?] show-bottom-info? [:chats/current-chat-ui-prop :show-bottom-info?]
@ -151,7 +160,7 @@
[chat-toolbar public? modal?] [chat-toolbar public? modal?]
(if (or (= :chat current-view) modal?) (if (or (= :chat current-view) modal?)
[messages-view-animation [messages-view-animation
[messages-view group-chat modal?]] [messages-view-wrapper group-chat modal?]]
[react/view style/message-view-preview]) [react/view style/message-view-preview])
[input/container] [input/container]
(when show-bottom-info? (when show-bottom-info?

View File

@ -19,13 +19,18 @@
[status-im.ui.components.action-button.action-button :as action-button] [status-im.ui.components.action-button.action-button :as action-button]
[status-im.utils.config :as config])) [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] (views/letsubs [photo-path [:contacts/chat-photo chat-id]
unviewed-messages-count [:chats/unviewed-messages-count chat-id] unviewed-messages-count [:chats/unviewed-messages-count chat-id]
chat-name [:chats/chat-name chat-id] chat-name [:chats/chat-name chat-id]
current-chat-id [:chats/current-chat-id] current-chat-id [:chats/current-chat-id]]
{:keys [content] :as last-message} [:chats/last-message chat-id]] (let [last-message {:content last-message-content
(let [name (or chat-name :message-type last-message-type}
name (or chat-name
(gfycat/generate-gfy public-key)) (gfycat/generate-gfy public-key))
[unviewed-messages-label large?] [(utils/unread-messages-count unviewed-messages-count) true] [unviewed-messages-label large?] [(utils/unread-messages-count unviewed-messages-count) true]
current? (= current-chat-id chat-id)] current? (= current-chat-id chat-id)]
@ -52,7 +57,7 @@
:style styles/chat-last-message} :style styles/chat-last-message}
(if (= constants/content-type-command (:content-type last-message)) (if (= constants/content-type-command (:content-type last-message))
[chat-item/command-short-preview last-message] [chat-item/command-short-preview last-message]
(or (:text content) (or (:text last-message-content)
(i18n/label :no-messages-yet)))]] (i18n/label :no-messages-yet)))]]
[react/view {:style styles/timestamp} [react/view {:style styles/timestamp}
[chat-item/message-timestamp (:timestamp last-message)] [chat-item/message-timestamp (:timestamp last-message)]
@ -140,14 +145,7 @@
(fn [this] (fn [this]
(let [[_ loading?] (.. this -props -argv)] (let [[_ loading?] (.. this -props -argv)]
(when loading? (when loading?
(re-frame/dispatch [:init-chats])))) (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]))))}
[react/view {:style styles/chat-list-view} [react/view {:style styles/chat-list-view}
[react/view {:style styles/chat-list-header} [react/view {:style styles/chat-list-header}
[search-input search-filter] [search-input search-filter]

View File

@ -100,14 +100,7 @@
(when loading? (when loading?
(utils/set-timeout (utils/set-timeout
#(re-frame/dispatch [:init-chats]) #(re-frame/dispatch [:init-chats])
100)))) 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]))))}
[react/view styles/container [react/view styles/container
[toolbar show-welcome? (and network-initialized? (not rpc-network?)) sync-state latest-block-number] [toolbar show-welcome? (and network-initialized? (not rpc-network?)) sync-state latest-block-number]
(cond show-welcome? (cond show-welcome?

View File

@ -88,9 +88,10 @@
(defview home-list-chat-item-inner-view [{:keys [chat-id name color online (defview home-list-chat-item-inner-view [{:keys [chat-id name color online
group-chat public? group-chat public?
public-key public-key
timestamp]}] timestamp
(letsubs [last-message [:chats/last-message chat-id] last-message-content
chat-name [:chats/chat-name chat-id]] last-message-type]}]
(letsubs [chat-name [:chats/chat-name chat-id]]
(let [truncated-chat-name (utils/truncate-str chat-name 30)] (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/touchable-highlight {:on-press #(re-frame/dispatch [:chat.ui/navigate-to-chat chat-id])}
[react/view styles/chat-container [react/view styles/chat-container
@ -100,10 +101,10 @@
[react/view styles/item-upper-container [react/view styles/item-upper-container
[chat-list-item-name truncated-chat-name group-chat public? public-key] [chat-list-item-name truncated-chat-name group-chat public? public-key]
[react/view styles/message-status-container [react/view styles/message-status-container
[message-timestamp (or (:timestamp last-message) [message-timestamp timestamp]]]
timestamp)]]]
[react/view styles/item-lower-container [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]]]]]))) [unviewed-indicator chat-id]]]]])))
(defn home-list-browser-item-inner-view [{:keys [dapp url name browser-id] :as browser}] (defn home-list-browser-item-inner-view [{:keys [dapp url name browser-id] :as browser}]

View File

@ -10,9 +10,14 @@
:admins #{4} :admins #{4}
:contacts #{2} :contacts #{2}
:tags #{} :tags #{}
:membership-updates []} :membership-updates []
(chats/normalize-chat {:admins [4] :last-message-type :message-type
:contacts [2]}))))) :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" (testing "membership-updates"
(with-redefs [chats/get-last-clock-value (constantly 42)] (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"} (let [raw-events {"1" {:id "1" :type "members-added" :clock-value 10 :members [1 2] :signature "a" :from "id-1"}