173 lines
8.8 KiB
Clojure
173 lines
8.8 KiB
Clojure
(ns status-im.chat.models.loading
|
|
(:require [re-frame.core :as re-frame]
|
|
[status-im.constants :as constants]
|
|
[status-im.ui.screens.chat.state :as chat.state]
|
|
[status-im.data-store.chats :as data-store.chats]
|
|
[status-im.data-store.messages :as data-store.messages]
|
|
[status-im.transport.filters.core :as filters]
|
|
[status-im.mailserver.core :as mailserver]
|
|
[status-im.utils.fx :as fx]
|
|
[status-im.chat.models.reactions :as reactions]
|
|
[status-im.chat.models.message-list :as message-list]
|
|
[taoensso.timbre :as log]
|
|
[status-im.chat.models.message-seen :as message-seen]
|
|
[status-im.chat.models.mentions :as mentions]))
|
|
|
|
(defn cursor->clock-value
|
|
[^js cursor]
|
|
(js/parseInt (.substring cursor 51 64)))
|
|
|
|
(defn clock-value->cursor [clock-value]
|
|
(str "000000000000000000000000000000000000000000000000000" clock-value "0x0000000000000000000000000000000000000000000000000000000000000000"))
|
|
|
|
(fx/defn update-chats-in-app-db
|
|
{:events [:chats-list/load-success]}
|
|
[{:keys [db] :as cofx} new-chats]
|
|
(let [old-chats (:chats db)
|
|
chats (reduce (fn [acc {:keys [chat-id] :as chat}]
|
|
(assoc acc chat-id chat))
|
|
{}
|
|
new-chats)
|
|
chats (merge old-chats chats)]
|
|
(fx/merge cofx
|
|
{:db (assoc db :chats chats
|
|
:chats/loading? false)}
|
|
(filters/load-filters))))
|
|
|
|
(fx/defn handle-chat-visibility-changed
|
|
{:events [:chat.ui/message-visibility-changed]}
|
|
[{:keys [db] :as cofx} ^js event]
|
|
(let [^js viewable-items (.-viewableItems event)
|
|
^js last-element (aget viewable-items (dec (.-length viewable-items)))]
|
|
(when last-element
|
|
(let [last-element-clock-value (:clock-value (.-item last-element))
|
|
chat-id (:chat-id (.-item last-element))]
|
|
(when (and last-element-clock-value
|
|
(get-in db [:pagination-info chat-id :messages-initialized?]))
|
|
(let [new-messages (reduce-kv (fn [acc message-id {:keys [clock-value] :as v}]
|
|
(if (<= last-element-clock-value clock-value)
|
|
(assoc acc message-id v)
|
|
acc))
|
|
{}
|
|
(get-in db [:messages chat-id]))]
|
|
{:db (-> db
|
|
(assoc-in [:messages chat-id] new-messages)
|
|
(assoc-in [:pagination-info chat-id] {:all-loaded? false
|
|
:messages-initialized? true
|
|
: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"
|
|
[cofx]
|
|
(data-store.chats/fetch-chats-rpc cofx {:on-success
|
|
#(re-frame/dispatch
|
|
[:chats-list/load-success %])}))
|
|
(fx/defn handle-failed-loading-messages
|
|
{:events [::failed-loading-messages]}
|
|
[{:keys [db]} current-chat-id _ err]
|
|
(log/error "failed loading messages" current-chat-id err)
|
|
(when current-chat-id
|
|
{:db (assoc-in db [:pagination-info current-chat-id :loading-messages?] false)}))
|
|
|
|
(fx/defn messages-loaded
|
|
"Loads more messages for current chat"
|
|
{:events [::messages-loaded]}
|
|
[{{:keys [current-chat-id] :as db} :db :as cofx}
|
|
chat-id
|
|
session-id
|
|
{:keys [cursor messages]}]
|
|
(when-not (or (nil? current-chat-id)
|
|
(not= chat-id current-chat-id)
|
|
(and (get-in db [:pagination-info current-chat-id :messages-initialized?])
|
|
(not= session-id
|
|
(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] #{})
|
|
users (get-in db [:chats current-chat-id :users] {})
|
|
;; We remove those messages that are already loaded, as we might get some duplicates
|
|
{:keys [all-messages
|
|
new-messages
|
|
last-clock-value
|
|
unviewed-message-ids
|
|
users]}
|
|
(reduce (fn [{:keys [last-clock-value all-messages users] :as acc}
|
|
{:keys [clock-value seen message-id alias name identicon from]
|
|
:as message}]
|
|
(let [nickname (get-in db [:contacts/contacts from :nickname])]
|
|
(cond-> acc
|
|
(and alias (not= alias ""))
|
|
(update :users assoc from
|
|
(mentions/add-searchable-phrases
|
|
{:alias alias
|
|
:name (or name alias)
|
|
:identicon identicon
|
|
:public-key from
|
|
:nickname nickname}))
|
|
(or (nil? last-clock-value)
|
|
(> last-clock-value clock-value))
|
|
(assoc :last-clock-value clock-value)
|
|
|
|
(not seen)
|
|
(update :unviewed-message-ids conj message-id)
|
|
|
|
(nil? (get all-messages message-id))
|
|
(update :new-messages conj message)
|
|
|
|
:always
|
|
(update :all-messages assoc message-id message))))
|
|
{:all-messages already-loaded-messages
|
|
:unviewed-message-ids loaded-unviewed-messages-ids
|
|
:users users
|
|
:new-messages []}
|
|
messages)]
|
|
(fx/merge cofx
|
|
{:db (-> db
|
|
(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 :users] users)
|
|
(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 [: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
|
|
current-chat-id
|
|
cursor
|
|
constants/default-number-of-messages
|
|
#(re-frame/dispatch [::messages-loaded current-chat-id session-id %])
|
|
#(re-frame/dispatch [::failed-loading-messages current-chat-id session-id %]))]
|
|
(fx/merge cofx
|
|
load-messages-fx
|
|
(reactions/load-more-reactions cursor)
|
|
(mailserver/load-gaps-fx current-chat-id)))))))
|
|
|
|
(fx/defn load-messages
|
|
{:events [:load-messages]}
|
|
[{:keys [db now] :as cofx}]
|
|
(when-let [current-chat-id (:current-chat-id db)]
|
|
(if-not (get-in db [:pagination-info current-chat-id :messages-initialized?])
|
|
(do
|
|
; reset chat first-not-visible-items state
|
|
(chat.state/reset)
|
|
(fx/merge cofx
|
|
{:db (-> db
|
|
;; 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 [: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
|
|
(message-seen/mark-messages-seen cofx current-chat-id))))
|