status-react/src/status_im/chat/models/loading.cljs

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))))