Improve chat loading performance
This commit does a few things: Move collections top level Move `messages`,`message-lists`,`pagination-info` from nested in `chats` to top level at the db. The reason for this change is that if any of the `messages` fields change, any `sub` that relies on `chat` will be recomputed, which is unnecessary. Move chat-name to events `chat-name` was computed dynamically, while it is now only calculated when loading chat the first time around. Remove `enrich-chats` Enrich chats was doing a lot of work, and many subscriptions were relying on it. Not all the computations were necessary, for example it would always calculate the name of who invited the user to a group chat, regardless of whether it was actually used in the view. This commit changes that behavior so that we use smaller subscriptions to calculate such fields. In general we should move computations to events, if that's not desirable (there are some cases where we might not want to do that), we should have "bottom/leaf heavy" subscriptions as opposed to "top heavy", especially if they are to be shared, so only when (and if) we load that particular view, the subscription is triggered, while others can be re-used. I have compared performance with current release, and there's a noticeable difference. Opening a chat is faster (messages are loaded faster), and clicking on the home view on a chat is more responsing (the animation on-press is much quicker).
This commit is contained in:
parent
5f7c795567
commit
f69ae44d50
|
@ -1,69 +1,11 @@
|
|||
(ns status-im.chat.db
|
||||
(:require [clojure.set :as clojure.set]
|
||||
[clojure.string :as clojure.string]
|
||||
[status-im.group-chats.db :as group-chats.db]
|
||||
[status-im.mailserver.constants :as mailserver.constants]
|
||||
[status-im.multiaccounts.core :as multiaccounts]
|
||||
[status-im.utils.identicon :as identicon]
|
||||
[status-im.utils.gfycat.core :as gfycat]))
|
||||
(:require [clojure.string :as clojure.string]
|
||||
[status-im.mailserver.constants :as mailserver.constants]))
|
||||
|
||||
(defn group-chat-name
|
||||
[{:keys [public? name]}]
|
||||
(str (when public? "#") name))
|
||||
|
||||
(defn enrich-active-chat
|
||||
[contacts {:keys [chat-id
|
||||
identicon
|
||||
alias
|
||||
group-chat] :as chat} current-public-key]
|
||||
(if group-chat
|
||||
(let [pending-invite-inviter-name
|
||||
(group-chats.db/get-pending-invite-inviter-name contacts
|
||||
chat
|
||||
current-public-key)
|
||||
inviter-name
|
||||
(group-chats.db/get-inviter-name contacts
|
||||
chat
|
||||
current-public-key)]
|
||||
(cond-> chat
|
||||
pending-invite-inviter-name
|
||||
(assoc :pending-invite-inviter-name pending-invite-inviter-name)
|
||||
inviter-name
|
||||
(assoc :inviter-name inviter-name)
|
||||
:always
|
||||
(assoc :chat-name (group-chat-name chat))))
|
||||
(let [photo (if (seq identicon)
|
||||
identicon
|
||||
(identicon/identicon chat-id))
|
||||
alias (if (seq alias)
|
||||
alias
|
||||
(gfycat/generate-gfy chat-id))
|
||||
{contact-name :name :as contact}
|
||||
(get contacts chat-id
|
||||
{:public-key chat-id
|
||||
:identicon photo
|
||||
:alias alias
|
||||
:name alias
|
||||
:system-tags #{}})]
|
||||
(-> chat
|
||||
(assoc :contact contact
|
||||
:chat-name (multiaccounts/displayed-name contact)
|
||||
:name contact-name
|
||||
:identicon photo
|
||||
:alias alias)
|
||||
(update :tags clojure.set/union (:tags contact))))))
|
||||
|
||||
(defn active-chats
|
||||
[contacts chats {:keys [public-key]}]
|
||||
(reduce-kv (fn [acc chat-id {:keys [is-active] :as chat}]
|
||||
(if is-active
|
||||
(assoc acc
|
||||
chat-id
|
||||
(enrich-active-chat contacts chat public-key))
|
||||
acc))
|
||||
{}
|
||||
chats))
|
||||
|
||||
(defn datemark? [{:keys [type]}]
|
||||
(= type :datemark))
|
||||
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
(ns status-im.chat.db-test
|
||||
(:require [cljs.test :refer-macros [deftest is testing]]
|
||||
[status-im.utils.gfycat.core :as gfycat]
|
||||
[status-im.utils.identicon :as identicon]
|
||||
[status-im.chat.db :as db]))
|
||||
|
||||
(deftest group-chat-name
|
||||
|
@ -41,18 +39,6 @@
|
|||
(is (= {:type :datemark
|
||||
:value "Dec 31, 1999"} d2)))))
|
||||
|
||||
(deftest active-chats-test
|
||||
(with-redefs [gfycat/generate-gfy (constantly "generated")
|
||||
identicon/identicon (constantly "generated")]
|
||||
(let [active-chat-1 {:is-active true :chat-id "1"}
|
||||
active-chat-2 {:is-active true :chat-id "2"}
|
||||
chats {"1" active-chat-1
|
||||
"2" active-chat-2
|
||||
"3" {:is-active false :chat-id "3"}}]
|
||||
(testing "it returns only chats with is-active"
|
||||
(is (= #{"1" "2"}
|
||||
(set (keys (db/active-chats {} chats {})))))))))
|
||||
|
||||
(deftest add-gaps
|
||||
(testing "empty state"
|
||||
(is (empty?
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
[status-im.transport.filters.core :as transport.filters]
|
||||
[status-im.contact.core :as contact.core]
|
||||
[status-im.waku.core :as waku]
|
||||
[status-im.contact.db :as contact.db]
|
||||
[status-im.data-store.chats :as chats-store]
|
||||
[status-im.data-store.messages :as messages-store]
|
||||
[status-im.ethereum.json-rpc :as json-rpc]
|
||||
|
@ -92,8 +91,7 @@
|
|||
:is-active true
|
||||
:timestamp now
|
||||
:contacts #{chat-id}
|
||||
:last-clock-value 0
|
||||
:messages {}}))
|
||||
:last-clock-value 0}))
|
||||
|
||||
(fx/defn ensure-chat
|
||||
"Add chat to db and update"
|
||||
|
@ -165,6 +163,7 @@
|
|||
{:chat-id topic
|
||||
:is-active true
|
||||
:name topic
|
||||
:chat-name (str "#" topic)
|
||||
:group-chat true
|
||||
:contacts #{}
|
||||
:public? true
|
||||
|
@ -175,20 +174,20 @@
|
|||
(fx/defn clear-history
|
||||
"Clears history of the particular chat"
|
||||
[{:keys [db] :as cofx} chat-id]
|
||||
(let [{:keys [messages
|
||||
last-message
|
||||
(let [{:keys [last-message
|
||||
deleted-at-clock-value]} (get-in db [:chats chat-id])
|
||||
last-message-clock-value (or (:clock-value last-message)
|
||||
deleted-at-clock-value
|
||||
(utils.clocks/send 0))]
|
||||
(fx/merge
|
||||
cofx
|
||||
{:db (update-in db [:chats chat-id] merge
|
||||
{:messages {}
|
||||
:message-list nil
|
||||
:last-message nil
|
||||
{:db (-> db
|
||||
(assoc-in [:messages chat-id] {})
|
||||
(update-in [:message-lists] dissoc chat-id)
|
||||
(update-in [:chats chat-id] merge
|
||||
{:last-message nil
|
||||
:unviewed-messages-count 0
|
||||
:deleted-at-clock-value last-message-clock-value})}
|
||||
:deleted-at-clock-value last-message-clock-value}))}
|
||||
(messages-store/delete-messages-by-chat-id chat-id)
|
||||
#(chats-store/save-chat % (get-in % [:db :chats chat-id])))))
|
||||
|
||||
|
@ -218,13 +217,9 @@
|
|||
{:db
|
||||
(-> db
|
||||
(dissoc :loaded-chat-id)
|
||||
(update-in [:chats current-chat-id]
|
||||
assoc
|
||||
:all-loaded? false
|
||||
:cursor nil
|
||||
:messages-initialized? false
|
||||
:messages {}
|
||||
:message-list nil))}))
|
||||
(update :messages dissoc current-chat-id)
|
||||
(update :message-lists dissoc current-chat-id)
|
||||
(update :pagination-info dissoc current-chat-id))}))
|
||||
|
||||
(fx/defn preload-chat-data
|
||||
"Takes chat-id and coeffects map, returns effects necessary when navigating to chat"
|
||||
|
@ -241,8 +236,6 @@
|
|||
(transport.filters/load-chat chat-id))
|
||||
(when platform/desktop?
|
||||
(message-seen/mark-messages-seen chat-id))
|
||||
(when (and (one-to-one-chat? cofx chat-id) (not (contact.db/contact-exists? db chat-id)))
|
||||
(contact.core/create-contact chat-id))
|
||||
(loading/load-messages))))
|
||||
|
||||
(fx/defn navigate-to-chat
|
||||
|
|
|
@ -24,10 +24,7 @@
|
|||
[{:keys [db] :as cofx} new-chats]
|
||||
(let [old-chats (:chats db)
|
||||
chats (reduce (fn [acc {:keys [chat-id] :as chat}]
|
||||
(assoc acc chat-id
|
||||
(assoc chat
|
||||
:messages-initialized? false
|
||||
:messages {})))
|
||||
(assoc acc chat-id chat))
|
||||
{}
|
||||
new-chats)
|
||||
chats (merge old-chats chats)]
|
||||
|
@ -52,12 +49,11 @@
|
|||
acc))
|
||||
{}
|
||||
(get-in db [:chats chat-id :messages]))]
|
||||
{:db (update-in db [:chats chat-id]
|
||||
assoc
|
||||
:messages new-messages
|
||||
:all-loaded? false
|
||||
:message-list (message-list/add-many nil (vals new-messages))
|
||||
:cursor (clock-value->cursor last-element-clock-value))}))))))
|
||||
{:db (-> db
|
||||
(assoc-in [:messages chat-id] new-messages)
|
||||
(assoc-in [:pagination-info chat-id] {:all-loaded? false
|
||||
: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"
|
||||
|
@ -70,7 +66,7 @@
|
|||
[{:keys [db]} current-chat-id _ err]
|
||||
(log/error "failed loading messages" current-chat-id err)
|
||||
(when current-chat-id
|
||||
{:db (assoc-in db [:chats current-chat-id :loading-messages?] false)}))
|
||||
{:db (assoc-in db [:pagination-info current-chat-id :loading-messages?] false)}))
|
||||
|
||||
(fx/defn messages-loaded
|
||||
"Loads more messages for current chat"
|
||||
|
@ -81,10 +77,10 @@
|
|||
{:keys [cursor messages]}]
|
||||
(when-not (or (nil? current-chat-id)
|
||||
(not= chat-id current-chat-id)
|
||||
(and (get-in db [:chats current-chat-id :messages-initialized?])
|
||||
(and (get-in db [:pagination-info current-chat-id :messages-initialized?])
|
||||
(not= session-id
|
||||
(get-in db [:chats current-chat-id :messages-initialized?]))))
|
||||
(let [already-loaded-messages (get-in db [:chats current-chat-id :messages])
|
||||
(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] #{})
|
||||
;; We remove those messages that are already loaded, as we might get some duplicates
|
||||
{:keys [all-messages
|
||||
|
@ -111,23 +107,23 @@
|
|||
messages)]
|
||||
(fx/merge cofx
|
||||
{:db (-> db
|
||||
(assoc-in [:chats current-chat-id :cursor-clock-value] (when (seq cursor) (cursor->clock-value cursor)))
|
||||
(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 :loading-messages?] false)
|
||||
(assoc-in [:chats current-chat-id :messages] all-messages)
|
||||
(update-in [:chats current-chat-id :message-list] message-list/add-many new-messages)
|
||||
(assoc-in [:chats current-chat-id :cursor] cursor)
|
||||
(assoc-in [:chats current-chat-id :all-loaded?]
|
||||
(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 [:chats current-chat-id :messages-initialized?])]
|
||||
(when-not (or (get-in db [:chats current-chat-id :all-loaded?])
|
||||
(get-in db [:chats current-chat-id :loading-messages?]))
|
||||
(let [cursor (get-in db [:chats current-chat-id :cursor])
|
||||
(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
|
||||
(waku/enabled? cofx)
|
||||
current-chat-id
|
||||
|
@ -142,7 +138,7 @@
|
|||
(fx/defn load-messages
|
||||
[{:keys [db now] :as cofx}]
|
||||
(when-let [current-chat-id (:current-chat-id db)]
|
||||
(if-not (get-in db [:chats current-chat-id :messages-initialized?])
|
||||
(if-not (get-in db [:pagination-info current-chat-id :messages-initialized?])
|
||||
(do
|
||||
; reset chat first-not-visible-items state
|
||||
(chat.state/reset)
|
||||
|
@ -151,7 +147,7 @@
|
|||
;; 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 [:chats current-chat-id :messages-initialized?] now))}
|
||||
(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
|
||||
|
|
|
@ -28,8 +28,8 @@
|
|||
|
||||
(fx/defn rebuild-message-list
|
||||
[{:keys [db]} chat-id]
|
||||
{:db (assoc-in db [:chats chat-id :message-list]
|
||||
(message-list/add-many nil (vals (get-in db [:chats chat-id :messages]))))})
|
||||
{:db (assoc-in db [:message-lists chat-id]
|
||||
(message-list/add-many nil (vals (get-in db [:messages chat-id]))))})
|
||||
|
||||
(fx/defn hidden-message-marked-as-seen
|
||||
{:events [::hidden-message-marked-as-seen]}
|
||||
|
@ -42,7 +42,7 @@
|
|||
"Hide chat message, rebuild message-list"
|
||||
[{:keys [db] :as cofx} chat-id {:keys [seen message-id]}]
|
||||
(fx/merge cofx
|
||||
{:db (update-in db [:chats chat-id :messages] dissoc message-id)}
|
||||
{:db (update-in db [:messages chat-id] dissoc message-id)}
|
||||
(data-store.messages/mark-messages-seen chat-id [message-id] #(re-frame/dispatch [::hidden-message-marked-as-seen %1 %2 %3]))
|
||||
(rebuild-message-list chat-id)))
|
||||
|
||||
|
@ -52,7 +52,7 @@
|
|||
:keys [seen-by-user?]}]
|
||||
(let [current-public-key (multiaccounts.model/current-public-key cofx)
|
||||
message-to-be-removed (when replace
|
||||
(get-in db [:chats chat-id :messages replace]))
|
||||
(get-in db [:messages chat-id replace]))
|
||||
prepared-message (prepare-message message seen-by-user?)]
|
||||
(fx/merge cofx
|
||||
(when message-to-be-removed
|
||||
|
@ -62,8 +62,8 @@
|
|||
;; We should not be always adding to the list, as it does not make sense
|
||||
;; if the chat has not been initialized, but run into
|
||||
;; some troubles disabling it, so next time
|
||||
(update-in [:chats chat-id :messages] assoc message-id prepared-message)
|
||||
(update-in [:chats chat-id :message-list] message-list/add prepared-message))
|
||||
(update-in [:messages chat-id] assoc message-id prepared-message)
|
||||
(update-in [:message-lists chat-id] message-list/add prepared-message))
|
||||
(and (not seen-by-user?)
|
||||
(not= from current-public-key))
|
||||
(update-in [:chats chat-id :loaded-unviewed-messages-ids]
|
||||
|
@ -94,7 +94,7 @@
|
|||
|
||||
(defn- message-loaded?
|
||||
[{:keys [db]} {:keys [chat-id message-id]}]
|
||||
(get-in db [:chats chat-id :messages message-id]))
|
||||
(get-in db [:messages chat-id message-id]))
|
||||
|
||||
(defn- earlier-than-deleted-at?
|
||||
[{:keys [db]} {:keys [chat-id clock-value]}]
|
||||
|
@ -145,7 +145,7 @@
|
|||
;; If the message is already loaded, it means it's an update, that
|
||||
;; happens when a message that was missing a reply had the reply
|
||||
;; coming through, in which case we just insert the new message
|
||||
{:db (assoc-in db [:chats chat-id :messages message-id] message-with-chat-id)}
|
||||
{:db (assoc-in db [:messages chat-id message-id] message-with-chat-id)}
|
||||
(fx/merge cofx
|
||||
(add-received-message message-with-chat-id)
|
||||
(update-unviewed-count message-with-chat-id)
|
||||
|
@ -202,7 +202,7 @@
|
|||
[{:keys [db] :as cofx} chat-id message-id status]
|
||||
(fx/merge cofx
|
||||
{:db (assoc-in db
|
||||
[:chats chat-id :messages message-id :outgoing-status]
|
||||
[:messages chat-id message-id :outgoing-status]
|
||||
status)}
|
||||
(data-store.messages/update-outgoing-status message-id status)))
|
||||
|
||||
|
@ -219,7 +219,7 @@
|
|||
"Deletes chat message, rebuild message-list"
|
||||
[{:keys [db] :as cofx} chat-id message-id]
|
||||
(fx/merge cofx
|
||||
{:db (update-in db [:chats chat-id :messages] dissoc message-id)}
|
||||
{:db (update-in db [:messages chat-id] dissoc message-id)}
|
||||
(data-store.messages/delete-message message-id)
|
||||
(rebuild-message-list chat-id)))
|
||||
|
||||
|
@ -229,4 +229,4 @@
|
|||
|
||||
(fx/defn toggle-expand-message
|
||||
[{:keys [db]} chat-id message-id]
|
||||
{:db (update-in db [:chats chat-id :messages message-id :expanded?] not)})
|
||||
{:db (update-in db [:messages chat-id message-id :expanded?] not)})
|
||||
|
|
|
@ -41,8 +41,7 @@
|
|||
(when (seq loaded-unviewed-ids)
|
||||
(fx/merge cofx
|
||||
{:db (reduce (fn [acc message-id]
|
||||
(assoc-in acc [:chats chat-id :messages
|
||||
message-id :seen]
|
||||
(assoc-in acc [:messages chat-id message-id :seen]
|
||||
true))
|
||||
db
|
||||
loaded-unviewed-ids)}
|
||||
|
|
|
@ -94,7 +94,8 @@
|
|||
:clock-value 1
|
||||
:chat-id "a"}))))
|
||||
(testing "it returns true when it's already in the loaded message"
|
||||
(is (#'status-im.chat.models.message/message-loaded? {:db {:chats {"a" {:messages {"message-id" {}}}}}}
|
||||
(is (#'status-im.chat.models.message/message-loaded? {:db
|
||||
{:messages {"a" {"message-id" {}}}}}
|
||||
{:message-id "message-id"
|
||||
:from "a"
|
||||
:clock-value 1
|
||||
|
@ -124,7 +125,8 @@
|
|||
:view-id :chat
|
||||
:loaded-chat-id "chat-id"
|
||||
:current-chat-id "chat-id"
|
||||
:chats {"chat-id" {:messages {}}}}]
|
||||
:messages {"chat-id" {}}
|
||||
:chats {"chat-id" {}}}]
|
||||
(testing "a message coming from you!"
|
||||
(let [actual (message/receive-one {:db db}
|
||||
{:from "me"
|
||||
|
@ -136,7 +138,7 @@
|
|||
:outgoing true
|
||||
:content "b"
|
||||
:clock-value 1})
|
||||
message (get-in actual [:db :chats "chat-id" :messages "id"])]
|
||||
message (get-in actual [:db :messages "chat-id" "id"])]
|
||||
(testing "it adds the message"
|
||||
(is message))))))
|
||||
|
||||
|
@ -170,7 +172,7 @@
|
|||
:whisper-timestamp 0
|
||||
:timestamp 0}]
|
||||
(testing "a valid message"
|
||||
(is (get-in (message/receive-one cofx valid-message) [:db :chats "chat-id" :messages "1"])))
|
||||
(is (get-in (message/receive-one cofx valid-message) [:db :messages "chat-id" "1"])))
|
||||
(testing "a message from someone not in the list of participants"
|
||||
(is (not (message/receive-one cofx bad-from-message))))
|
||||
(testing "a message with non existing chat-id"
|
||||
|
@ -199,7 +201,7 @@
|
|||
:whisper-timestamp 0
|
||||
:timestamp 0}]
|
||||
(testing "a valid message"
|
||||
(is (get-in (message/receive-one cofx valid-message) [:db :chats "chat-id" :messages "1"])))
|
||||
(is (get-in (message/receive-one cofx valid-message) [:db :messages "chat-id" "1"])))
|
||||
(testing "a message with non existing chat-id"
|
||||
(is (not (message/receive-one cofx bad-chat-id-message))))))
|
||||
|
||||
|
@ -235,16 +237,16 @@
|
|||
:whisper-timestamp 0
|
||||
:timestamp 0}]
|
||||
(testing "a valid message"
|
||||
(is (get-in (message/receive-one cofx valid-message) [:db :chats "matching" :messages "1"])))
|
||||
(is (get-in (message/receive-one cofx valid-message) [:db :messages "matching" "1"])))
|
||||
(testing "our own message"
|
||||
(is (get-in (message/receive-one cofx own-message) [:db :chats "matching" :messages "1"])))
|
||||
(is (get-in (message/receive-one cofx own-message) [:db :messages "matching" "1"])))
|
||||
(testing "a message with non matching chat-id"
|
||||
(is (not (get-in (message/receive-one cofx bad-chat-id-message) [:db :chats "not-matching" :messages "1"])))))))
|
||||
(is (not (get-in (message/receive-one cofx bad-chat-id-message) [:db :messages "not-matching" "1"])))))))
|
||||
|
||||
(deftest delete-message
|
||||
(with-redefs [time/day-relative (constantly "day-relative")
|
||||
time/timestamp->time (constantly "timestamp")]
|
||||
(let [cofx1 {:db {:chats {"chat-id" {:messages {0 {:message-id 0
|
||||
(let [cofx1 {:db {:messages {"chat-id" {0 {:message-id 0
|
||||
:content "a"
|
||||
:clock-value 0
|
||||
:whisper-timestamp 0
|
||||
|
@ -253,19 +255,21 @@
|
|||
:content "b"
|
||||
:clock-value 1
|
||||
:whisper-timestamp 1
|
||||
:timestamp 1}}
|
||||
:message-list [{:something :something}]}}}}
|
||||
cofx2 {:db {:chats {"chat-id" {:messages {0 {:message-id 0
|
||||
:timestamp 1}}}
|
||||
:message-lists {"chat-id" [{:something :something}]}
|
||||
:chats {"chat-id" {}}}}
|
||||
cofx2 {:db {:messages {"chat-id" {0 {:message-id 0
|
||||
:content "a"
|
||||
:clock-value 0
|
||||
:whisper-timestamp 1
|
||||
:timestamp 1}}
|
||||
:message-list [{:something :something}]}}}}
|
||||
:timestamp 1}}}
|
||||
:message-list {"chat-id" [{:something :something}]}
|
||||
:chats {"chat-id" {}}}}
|
||||
fx1 (message/delete-message cofx1 "chat-id" 1)
|
||||
fx2 (message/delete-message cofx2 "chat-id" 0)]
|
||||
(testing "Deleting message deletes it along with all references"
|
||||
(is (= '(0)
|
||||
(keys (get-in fx1 [:db :chats "chat-id" :messages]))))
|
||||
(keys (get-in fx1 [:db :messages "chat-id"]))))
|
||||
(is (= [{:one-to-one? false
|
||||
:message-id 0
|
||||
:whisper-timestamp 0
|
||||
|
@ -284,8 +288,8 @@
|
|||
:display-username? true
|
||||
:outgoing false}]
|
||||
(models.message-list/->seq
|
||||
(get-in fx1 [:db :chats "chat-id" :message-list]))))
|
||||
(get-in fx1 [:db :message-lists "chat-id"]))))
|
||||
(is (= {}
|
||||
(get-in fx2 [:db :chats "chat-id" :messages])))
|
||||
(get-in fx2 [:db :messages "chat-id"])))
|
||||
(is (= nil
|
||||
(get-in fx2 [:db :chats "chat-id" :message-list])))))))
|
||||
(get-in fx2 [:db :message-lists "chat-id"])))))))
|
||||
|
|
|
@ -65,15 +65,15 @@
|
|||
|
||||
(deftest clear-history-test
|
||||
(let [chat-id "1"
|
||||
cofx {:db {:chats {chat-id {:message-list [{:something "a"}]
|
||||
:last-message {:clock-value 10}
|
||||
cofx {:db {:message-lists {chat-id [{:something "a"}]}
|
||||
:chats {chat-id {:last-message {:clock-value 10}
|
||||
:unviewed-messages-count 1}}}}]
|
||||
(testing "it deletes all the messages"
|
||||
(let [actual (chat/clear-history cofx chat-id)]
|
||||
(is (= {} (get-in actual [:db :chats chat-id :messages])))))
|
||||
(is (= {} (get-in actual [:db :messages chat-id])))))
|
||||
(testing "it deletes all the message groups"
|
||||
(let [actual (chat/clear-history cofx chat-id)]
|
||||
(is (= nil (get-in actual [:db :chats chat-id :message-list])))))
|
||||
(is (= nil (get-in actual [:db :message-lists chat-id])))))
|
||||
(testing "it deletes unviewed messages set"
|
||||
(let [actual (chat/clear-history cofx chat-id)]
|
||||
(is (= 0 (get-in actual [:db :chats chat-id :unviewed-messages-count])))))
|
||||
|
@ -103,13 +103,13 @@
|
|||
|
||||
(deftest remove-chat-test
|
||||
(let [chat-id "1"
|
||||
cofx {:db {:chats {chat-id {:last-message {:clock-value 10}
|
||||
:messages {"1" {:clock-value 1}
|
||||
cofx {:db {:messages {chat-id {"1" {:clock-value 1}
|
||||
"2" {:clock-value 10}
|
||||
"3" {:clock-value 2}}}}}}]
|
||||
"3" {:clock-value 2}}}
|
||||
:chats {chat-id {:last-message {:clock-value 10}}}}}]
|
||||
(testing "it deletes all the messages"
|
||||
(let [actual (chat/remove-chat cofx chat-id)]
|
||||
(is (= {} (get-in actual [:db :chats chat-id :messages])))))
|
||||
(is (= {} (get-in actual [:db :messages chat-id])))))
|
||||
(testing "it sets a deleted-at-clock-value equal to the last message clock-value"
|
||||
(let [actual (chat/remove-chat cofx chat-id)]
|
||||
(is (= 10 (get-in actual [:db :chats chat-id :deleted-at-clock-value])))))
|
||||
|
@ -147,9 +147,10 @@
|
|||
|
||||
(def test-db
|
||||
{:multiaccount {:public-key "me"}
|
||||
|
||||
:messages {"status" {"4" {} "5" {} "6" {}}}
|
||||
:chats {"status" {:public? true
|
||||
:group-chat true
|
||||
:messages {"4" {} "5" {} "6" {}}
|
||||
:loaded-unviewed-messages-ids #{"6" "5" "4"}}
|
||||
"opened" {:loaded-unviewed-messages-ids #{}}
|
||||
"1-1" {:loaded-unviewed-messages-ids #{"6" "5" "4"}}}})
|
||||
|
|
|
@ -16,10 +16,16 @@
|
|||
group-chat private-group-chat-type
|
||||
:else one-to-one-chat-type)))
|
||||
|
||||
(defn rpc->type [{:keys [chatType] :as chat}]
|
||||
(defn rpc->type [{:keys [chatType name] :as chat}]
|
||||
(cond
|
||||
(= public-chat-type chatType) (assoc chat :public? true :group-chat true)
|
||||
(= private-group-chat-type chatType) (assoc chat :public? false :group-chat true)
|
||||
(= public-chat-type chatType) (assoc chat
|
||||
:chat-name (str "#" name)
|
||||
:public? true
|
||||
:group-chat true)
|
||||
(= private-group-chat-type chatType) (assoc chat
|
||||
:chat-name name
|
||||
:public? false
|
||||
:group-chat true)
|
||||
:else (assoc chat :public? false :group-chat false)))
|
||||
|
||||
(defn- marshal-members [{:keys [admins contacts members-joined chatType] :as chat}]
|
||||
|
@ -68,11 +74,10 @@
|
|||
:deleted-at-clock-value :deletedAtClockValue
|
||||
:is-active :active
|
||||
:last-clock-value :lastClockValue})
|
||||
(dissoc :message-list :gaps-loaded? :pagination-info
|
||||
:public? :group-chat :messages
|
||||
(dissoc :public? :group-chat :messages
|
||||
:might-have-join-time-messages?
|
||||
:loaded-unviewed-messages-ids
|
||||
:messages-initialized? :contacts :admins :members-joined)))
|
||||
:contacts :admins :members-joined)))
|
||||
|
||||
(defn <-rpc [chat]
|
||||
(-> chat
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
(deftest ->to-rpc
|
||||
(let [chat {:public? false
|
||||
:group-chat true
|
||||
:message-list []
|
||||
:color "color"
|
||||
:contacts #{"a" "b" "c" "d"}
|
||||
:last-clock-value 10
|
||||
|
@ -13,15 +12,11 @@
|
|||
:members-joined #{"a" "c"}
|
||||
:name "name"
|
||||
:membership-update-events :events
|
||||
:gaps-loaded? true
|
||||
:unviewed-messages-count 2
|
||||
:is-active true
|
||||
:messages {}
|
||||
:pagination-info {}
|
||||
:chat-id "chat-id"
|
||||
:loaded-unviewed-messages-ids []
|
||||
:timestamp 2
|
||||
:messages-initialized? true}
|
||||
:timestamp 2}
|
||||
expected-chat {:id "chat-id"
|
||||
:color "color"
|
||||
:name "name"
|
||||
|
@ -73,6 +68,7 @@
|
|||
expected-chat {:public? false
|
||||
:group-chat true
|
||||
:color "color"
|
||||
:chat-name "name"
|
||||
:contacts #{"a" "b" "c" "d"}
|
||||
:last-clock-value 10
|
||||
:last-message nil
|
||||
|
|
|
@ -519,7 +519,7 @@
|
|||
(handlers/register-handler-fx
|
||||
:chat.ui/resend-message
|
||||
(fn [{:keys [db] :as cofx} [_ chat-id message-id]]
|
||||
(let [message (get-in db [:chats chat-id :messages message-id])]
|
||||
(let [message (get-in db [:messages chat-id message-id])]
|
||||
(fx/merge
|
||||
cofx
|
||||
(transport.message/set-message-envelope-hash chat-id message-id (:message-type message) 1)
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
(ns status-im.group-chats.db
|
||||
(:require [status-im.multiaccounts.core :as multiaccounts]))
|
||||
(ns status-im.group-chats.db)
|
||||
|
||||
(def members-added-type 3)
|
||||
|
||||
|
@ -23,21 +22,3 @@
|
|||
|
||||
(defn group-chat? [chat]
|
||||
(and (:group-chat chat) (not (:public? chat))))
|
||||
|
||||
(defn get-pending-invite-inviter-name
|
||||
"when the chat is a private group chat in which the user has been
|
||||
invited and didn't accept the invitation yet, return inviter-name"
|
||||
[contacts chat my-public-key]
|
||||
(when (and (group-chat? chat)
|
||||
(invited? my-public-key chat)
|
||||
(not (joined? my-public-key chat)))
|
||||
(let [inviter-pk (get-inviter-pk my-public-key chat)]
|
||||
(multiaccounts/displayed-name (or (get contacts inviter-pk) {:public-key inviter-pk})))))
|
||||
|
||||
(defn get-inviter-name
|
||||
"when the chat is a private group chat in which the user has been
|
||||
invited and didn't accept the invitation yet, return inviter-name"
|
||||
[contacts chat my-public-key]
|
||||
(when (group-chat? chat)
|
||||
(let [inviter-pk (get-inviter-pk my-public-key chat)]
|
||||
(multiaccounts/displayed-name (or (get contacts inviter-pk) {:public-key inviter-pk})))))
|
||||
|
|
|
@ -1171,7 +1171,7 @@
|
|||
{})))
|
||||
|
||||
(fx/defn load-gaps-fx [{:keys [db] :as cofx} chat-id]
|
||||
(when-not (get-in db [:chats chat-id :gaps-loaded?])
|
||||
(when-not (get-in db [:gaps-loaded? chat-id])
|
||||
(let [success-fn #(re-frame/dispatch [::gaps-loaded %1 %2])]
|
||||
(data-store.mailservers/load-gaps cofx chat-id success-fn))))
|
||||
|
||||
|
@ -1190,7 +1190,7 @@
|
|||
cofx
|
||||
{:db
|
||||
(-> db
|
||||
(assoc-in [:chats chat-id :gaps-loaded?] true)
|
||||
(assoc-in [:gaps-loaded? chat-id] true)
|
||||
(assoc-in [:mailserver/gaps chat-id] gaps))}
|
||||
|
||||
(data-store.mailservers/delete-gaps outdated-gaps))))
|
||||
|
|
|
@ -389,13 +389,11 @@
|
|||
{:multiaccount {:public-key "me"}
|
||||
:chats
|
||||
{"chat-id-1" {:is-active true
|
||||
:messages {}
|
||||
:might-have-join-time-messages? true
|
||||
:group-chat true
|
||||
:public? true
|
||||
:chat-id "chat-id-1"}
|
||||
"chat-id-2" {:is-active true
|
||||
:messages {}
|
||||
:group-chat true
|
||||
:public? true
|
||||
:chat-id "chat-id-2"}}}})
|
||||
|
@ -405,14 +403,12 @@
|
|||
{:multiaccount {:public-key "me"}
|
||||
:chats
|
||||
{"chat-id-1" {:is-active true
|
||||
:messages {}
|
||||
:join-time-mail-request-id "a"
|
||||
:might-have-join-time-messages? true
|
||||
:group-chat true
|
||||
:public? true
|
||||
:chat-id "chat-id-1"}
|
||||
"chat-id-2" {:is-active true
|
||||
:messages {}
|
||||
:group-chat true
|
||||
:public? true
|
||||
:chat-id "chat-id-2"}}}})
|
||||
|
@ -422,26 +418,22 @@
|
|||
{:multiaccount {:public-key "me"}
|
||||
:chats
|
||||
{"chat-id-1" {:is-active true
|
||||
:messages {}
|
||||
:join-time-mail-request-id "a"
|
||||
:might-have-join-time-messages? true
|
||||
:group-chat true
|
||||
:public? true
|
||||
:chat-id "chat-id-1"}
|
||||
"chat-id-2" {:is-active true
|
||||
:messages {}
|
||||
:join-time-mail-request-id "a"
|
||||
:might-have-join-time-messages? true
|
||||
:group-chat true
|
||||
:public? true
|
||||
:chat-id "chat-id-2"}
|
||||
"chat-id-3" {:is-active true
|
||||
:messages {}
|
||||
:group-chat true
|
||||
:public? true
|
||||
:chat-id "chat-id-3"}
|
||||
"chat-id-4" {:is-active true
|
||||
:messages {}
|
||||
:join-time-mail-request-id "a"
|
||||
:might-have-join-time-messages? true
|
||||
:group-chat true
|
||||
|
|
|
@ -37,7 +37,6 @@
|
|||
[status-im.utils.money :as money]
|
||||
[status-im.utils.platform :as platform]
|
||||
[status-im.utils.security :as security]
|
||||
[status-im.utils.universal-links.core :as links]
|
||||
[status-im.wallet.db :as wallet.db]
|
||||
[status-im.wallet.utils :as wallet.utils]
|
||||
status-im.ui.screens.keycard.subs
|
||||
|
@ -185,6 +184,10 @@
|
|||
|
||||
(reg-root-key-sub :multiaccounts/loading :multiaccounts/loading)
|
||||
|
||||
(reg-root-key-sub ::messages :messages)
|
||||
(reg-root-key-sub ::message-lists :message-lists)
|
||||
(reg-root-key-sub ::pagination-info :pagination-info)
|
||||
|
||||
;;GENERAL ==============================================================================================================
|
||||
|
||||
(re-frame/reg-sub
|
||||
|
@ -450,6 +453,21 @@
|
|||
(fn [{:keys [public-key]}]
|
||||
public-key))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:multiaccount/preferred-name
|
||||
:<- [:multiaccount]
|
||||
(fn [{:keys [preferred-name]}]
|
||||
preferred-name))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:multiaccount/my-name
|
||||
:<- [:multiaccount/public-key]
|
||||
:<- [:multiaccount/preferred-name]
|
||||
(fn [[identity preferred-name]]
|
||||
(if preferred-name
|
||||
(stateofus/username (str "@" preferred-name))
|
||||
(gfycat/generate-gfy identity))))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:multiaccount/default-account
|
||||
:<- [:multiaccount/accounts]
|
||||
|
@ -608,77 +626,24 @@
|
|||
|
||||
(re-frame/reg-sub
|
||||
:chats/active-chats
|
||||
:<- [:contacts/contacts]
|
||||
:<- [::chats]
|
||||
:<- [:multiaccount]
|
||||
(fn [[contacts chats multiaccount]]
|
||||
(chat.db/active-chats contacts chats multiaccount)))
|
||||
(fn [chats]
|
||||
(reduce-kv (fn [acc id {:keys [is-active] :as chat}]
|
||||
(if is-active
|
||||
(assoc acc id chat)
|
||||
acc))
|
||||
{}
|
||||
chats)))
|
||||
|
||||
;; TODO: this is no useful without tribute to talk
|
||||
#_(defn enrich-current-one-to-one-chat
|
||||
[{:keys [contact] :as current-chat} my-public-key ttt-settings
|
||||
chain-keyword prices currency]
|
||||
(let [{:keys [tribute-to-talk]} contact
|
||||
{:keys [disabled? snt-amount message]} tribute-to-talk
|
||||
whitelisted-by? (whitelist/whitelisted-by? contact)
|
||||
loading? (and (not whitelisted-by?)
|
||||
(not tribute-to-talk))
|
||||
show-input? (or whitelisted-by?
|
||||
disabled?)
|
||||
token (case chain-keyword
|
||||
:mainnet :SNT
|
||||
:STT)
|
||||
tribute-status (if loading?
|
||||
:loading
|
||||
(tribute-to-talk.db/tribute-status contact))
|
||||
tribute-label (tribute-to-talk.db/status-label tribute-status snt-amount)]
|
||||
|
||||
(cond-> (assoc current-chat
|
||||
:tribute-to-talk/tribute-status tribute-status
|
||||
:tribute-to-talk/tribute-label tribute-label)
|
||||
|
||||
(#{:required :pending :paid} tribute-status)
|
||||
(assoc :tribute-to-talk/snt-amount
|
||||
(tribute-to-talk.db/from-wei snt-amount)
|
||||
:tribute-to-talk/message
|
||||
message
|
||||
:tribute-to-talk/fiat-amount (if snt-amount
|
||||
(money/fiat-amount-value
|
||||
snt-amount
|
||||
token
|
||||
(-> currency :code keyword)
|
||||
prices)
|
||||
"0")
|
||||
:tribute-to-talk/fiat-currency (:code currency)
|
||||
:tribute-to-talk/token (str " " (name token)))
|
||||
|
||||
(tribute-to-talk.db/enabled? ttt-settings)
|
||||
(assoc :tribute-to-talk/received? (tribute-to-talk.db/tribute-received?
|
||||
contact))
|
||||
|
||||
(= tribute-status :required)
|
||||
(assoc :tribute-to-talk/on-share-my-profile
|
||||
#(re-frame/dispatch
|
||||
[:profile/share-profile-link my-public-key]))
|
||||
|
||||
show-input?
|
||||
(assoc :show-input? true))))
|
||||
|
||||
(defn enrich-current-chat
|
||||
[{:keys [messages chat-id might-have-join-time-messages?] :as chat} ranges]
|
||||
(assoc chat
|
||||
:range
|
||||
(get ranges chat-id)
|
||||
:intro-status
|
||||
(if might-have-join-time-messages?
|
||||
:loading
|
||||
(if (empty? messages)
|
||||
:empty
|
||||
:messages))))
|
||||
(re-frame/reg-sub
|
||||
::chat
|
||||
:<- [::chats]
|
||||
(fn [chats [_ chat-id]]
|
||||
(get chats chat-id)))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:chats/current-raw-chat
|
||||
:<- [:chats/active-chats]
|
||||
:<- [::chats]
|
||||
:<- [:chats/current-chat-id]
|
||||
(fn [[chats current-chat-id]]
|
||||
(get chats current-chat-id)))
|
||||
|
@ -694,15 +659,10 @@
|
|||
:chats/current-chat
|
||||
:<- [:chats/current-raw-chat]
|
||||
:<- [:multiaccount/public-key]
|
||||
:<- [:mailserver/ranges]
|
||||
(fn [[{:keys [group-chat chat-id messages] :as current-chat}
|
||||
my-public-key ranges]]
|
||||
(fn [[{:keys [group-chat] :as current-chat}
|
||||
my-public-key]]
|
||||
(when current-chat
|
||||
(cond-> (enrich-current-chat current-chat ranges)
|
||||
(empty? messages)
|
||||
(assoc :universal-link
|
||||
(links/generate-link :public-chat :external chat-id))
|
||||
|
||||
(cond-> current-chat
|
||||
(chat.models/public-chat? current-chat)
|
||||
(assoc :show-input? true)
|
||||
|
||||
|
@ -727,17 +687,12 @@
|
|||
(fn [current-chat]
|
||||
(chat.models/public-chat? current-chat)))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:chats/current-chat-message
|
||||
:<- [:chats/current-chat]
|
||||
(fn [{:keys [messages]} [_ message-id]]
|
||||
(get messages message-id)))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:chats/current-chat-messages
|
||||
:<- [:chats/current-chat]
|
||||
(fn [{:keys [messages]}]
|
||||
(or messages {})))
|
||||
:<- [::messages]
|
||||
:<- [:chats/current-chat-id]
|
||||
(fn [[messages chat-id]]
|
||||
(get messages chat-id {})))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:chats/messages-gaps
|
||||
|
@ -746,6 +701,12 @@
|
|||
(fn [[gaps chat-id]]
|
||||
(sort-by :from (vals (get gaps chat-id)))))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:mailserver/ranges-by-chat-id
|
||||
:<- [:mailserver/ranges]
|
||||
(fn [ranges [_ chat-id]]
|
||||
(get ranges chat-id)))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:chats/range
|
||||
:<- [:mailserver/ranges]
|
||||
|
@ -755,27 +716,23 @@
|
|||
|
||||
(re-frame/reg-sub
|
||||
:chats/all-loaded?
|
||||
:<- [:chats/current-chat]
|
||||
(fn [chat]
|
||||
(:all-loaded? chat)))
|
||||
:<- [::pagination-info]
|
||||
:<- [:chats/current-chat-id]
|
||||
(fn [[pagination-info chat-id]]
|
||||
(get-in pagination-info [chat-id :all-loaded?])))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:chats/public?
|
||||
:<- [:chats/current-chat]
|
||||
:<- [:chats/current-raw-chat]
|
||||
(fn [chat]
|
||||
(:public? chat)))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:chats/message-list
|
||||
:<- [:chats/current-chat]
|
||||
(fn [chat]
|
||||
(:message-list chat)))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:chats/messages
|
||||
:<- [:chats/current-chat]
|
||||
(fn [chat]
|
||||
(:messages chat)))
|
||||
:<- [::message-lists]
|
||||
:<- [:chats/current-chat-id]
|
||||
(fn [[message-lists chat-id]]
|
||||
(get message-lists chat-id)))
|
||||
|
||||
(defn hydrate-messages
|
||||
"Pull data from messages and add it to the sorted list"
|
||||
|
@ -786,10 +743,16 @@
|
|||
%)
|
||||
message-list))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:chats/current-chat-no-messages?
|
||||
:<- [:chats/current-chat-messages]
|
||||
(fn [messages]
|
||||
(empty? messages)))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:chats/current-chat-messages-stream
|
||||
:<- [:chats/message-list]
|
||||
:<- [:chats/messages]
|
||||
:<- [:chats/current-chat-messages]
|
||||
:<- [:chats/messages-gaps]
|
||||
:<- [:chats/range]
|
||||
:<- [:chats/all-loaded?]
|
||||
|
@ -801,17 +764,6 @@
|
|||
(hydrate-messages messages)
|
||||
(chat.db/add-gaps messages-gaps range all-loaded? public?))))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:chats/current-chat-intro-status
|
||||
:<- [:chats/current-chat]
|
||||
:<- [:chats/current-chat-messages]
|
||||
(fn [[{:keys [might-have-join-time-messages?]} messages]]
|
||||
(if might-have-join-time-messages?
|
||||
:loading
|
||||
(if (empty? messages)
|
||||
:empty
|
||||
:messages))))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:chats/photo-path
|
||||
:<- [:contacts/contacts]
|
||||
|
@ -826,9 +778,15 @@
|
|||
:chats/unread-messages-number
|
||||
:<- [:chats/active-chats]
|
||||
(fn [chats _]
|
||||
(let [grouped-chats (group-by :public? (vals chats))]
|
||||
{:public (apply + (map :unviewed-messages-count (get grouped-chats true)))
|
||||
:other (apply + (map :unviewed-messages-count (get grouped-chats false)))})))
|
||||
(reduce-kv (fn [{:keys [public other]} _ {:keys [unviewed-messages-count public?]}]
|
||||
(if public?
|
||||
{:public (+ public unviewed-messages-count)
|
||||
:other other}
|
||||
{:other (+ other unviewed-messages-count)
|
||||
:public public}))
|
||||
{:public 0
|
||||
:other 0}
|
||||
chats)))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:chats/cooldown-enabled?
|
||||
|
@ -886,13 +844,13 @@
|
|||
(filter-contacts selected-contacts active-contacts)))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:group-chat/chat-joined?
|
||||
:<- [:multiaccount/public-key]
|
||||
:<- [:chats/active-chats]
|
||||
(fn [[my-public-key chats] [_ chat-id]]
|
||||
(let [current-chat (get chats chat-id)]
|
||||
(and (chat.models/group-chat? current-chat)
|
||||
(group-chats.db/joined? my-public-key current-chat)))))
|
||||
:group-chat/inviter-info
|
||||
(fn [[_ chat-id] _]
|
||||
[(re-frame/subscribe [::chat chat-id])
|
||||
(re-frame/subscribe [:multiaccount/public-key])])
|
||||
(fn [[chat my-public-key]]
|
||||
{:joined? (group-chats.db/joined? my-public-key chat)
|
||||
:inviter-pk (group-chats.db/get-inviter-pk my-public-key chat)}))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:chats/transaction-status
|
||||
|
@ -1631,21 +1589,40 @@
|
|||
contact.db/public-key->new-contact
|
||||
contact.db/enrich-contact))))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:contacts/contact-by-identity
|
||||
:<- [::contacts]
|
||||
(fn [contacts [_ identity]]
|
||||
(get contacts identity)))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:contacts/contact-added?
|
||||
(fn [[_ identity] _]
|
||||
[(re-frame/subscribe [:contacts/contact-by-identity identity])])
|
||||
(fn [[contact] _]
|
||||
(contact.db/added? contact)))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:contacts/raw-contact-name-by-identity
|
||||
(fn [[_ identity] _]
|
||||
[(re-frame/subscribe [:contacts/contact-by-identity identity])])
|
||||
(fn [[db-contact] _]
|
||||
(if (and (:ens-verified db-contact) (seq (:name db-contact)))
|
||||
(stateofus/username (str "@" (:name db-contact)))
|
||||
(:alias db-contact))))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:contacts/contact-name-by-identity
|
||||
:<- [:contacts/contacts]
|
||||
:<- [:multiaccount]
|
||||
(fn [[contacts current-multiaccount] [_ identity]]
|
||||
(fn [[_ identity] _]
|
||||
[(re-frame/subscribe [:contacts/raw-contact-name-by-identity identity])
|
||||
(re-frame/subscribe [:multiaccount])])
|
||||
(fn [[contact-name current-multiaccount] [_ identity]]
|
||||
(let [me? (= (:public-key current-multiaccount) identity)]
|
||||
(if me?
|
||||
{:ens-name (:preferred-name current-multiaccount)
|
||||
:alias (gfycat/generate-gfy identity)}
|
||||
(let [contact (or (contacts identity)
|
||||
(contact.db/public-key->new-contact identity))]
|
||||
{:ens-name (when (:ens-verified contact)
|
||||
(:name contact))
|
||||
:alias (or (:alias contact)
|
||||
(gfycat/generate-gfy identity))})))))
|
||||
(or (:preferred-name current-multiaccount)
|
||||
(gfycat/generate-gfy identity))
|
||||
(or contact-name
|
||||
(gfycat/generate-gfy identity))))))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:messages/quote-info
|
||||
|
@ -1694,17 +1671,6 @@
|
|||
(fn [[chat all-contacts] [_ query-fn]]
|
||||
(contact.db/query-chat-contacts chat all-contacts query-fn)))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:contacts/chat-photo
|
||||
(fn [[_ chat-id] _]
|
||||
[(re-frame/subscribe [:chats/chat chat-id])
|
||||
(re-frame/subscribe [:contacts/contacts-by-chat filter chat-id])])
|
||||
(fn [[chat contacts] [_ _]]
|
||||
(when (and chat (not (:group-chat chat)))
|
||||
(if (pos? (count contacts))
|
||||
(multiaccounts/displayed-photo (first contacts))
|
||||
(multiaccounts/displayed-photo chat)))))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:contacts/contact-by-address
|
||||
:<- [:contacts/contacts]
|
||||
|
|
|
@ -60,7 +60,8 @@
|
|||
(let [chat-id "chat-id"
|
||||
from "from"
|
||||
message-id "message-id"
|
||||
initial-cofx {:db {:chats {chat-id {:messages {message-id {:from from}}}}}}]
|
||||
initial-cofx {:db {:messages {chat-id {message-id {:from from}}}
|
||||
:chats {chat-id {}}}}]
|
||||
|
||||
(testing "a single envelope message"
|
||||
(let [cofx (message/set-message-envelope-hash initial-cofx chat-id message-id :message-type 1)]
|
||||
|
@ -72,12 +73,12 @@
|
|||
(is (= :sent
|
||||
(get-in
|
||||
(message/update-envelope-status cofx message-id :sent)
|
||||
[:db :chats chat-id :messages message-id :outgoing-status]))))
|
||||
[:db :messages chat-id message-id :outgoing-status]))))
|
||||
(testing "the message is not sent"
|
||||
(is (= :not-sent
|
||||
(get-in
|
||||
(message/update-envelope-status cofx message-id :not-sent)
|
||||
[:db :chats chat-id :messages message-id :outgoing-status]))))))
|
||||
[:db :messages chat-id message-id :outgoing-status]))))))
|
||||
(testing "multi envelope message"
|
||||
(testing "only inserts"
|
||||
(let [cofx (fx/merge
|
||||
|
@ -103,7 +104,7 @@
|
|||
(is (= :sent
|
||||
(get-in
|
||||
cofx
|
||||
[:db :chats chat-id :messages message-id :outgoing-status]))))))
|
||||
[:db :messages chat-id message-id :outgoing-status]))))))
|
||||
(testing "order of events is reversed"
|
||||
(let [cofx (fx/merge
|
||||
initial-cofx
|
||||
|
@ -119,7 +120,7 @@
|
|||
(is (= :sent
|
||||
(get-in
|
||||
cofx
|
||||
[:db :chats chat-id :messages message-id :outgoing-status]))))))
|
||||
[:db :messages chat-id message-id :outgoing-status]))))))
|
||||
(testing "message not sent"
|
||||
(let [cofx (fx/merge
|
||||
initial-cofx
|
||||
|
@ -135,4 +136,4 @@
|
|||
(is (= :not-sent
|
||||
(get-in
|
||||
cofx
|
||||
[:db :chats chat-id :messages message-id :outgoing-status])))))))))
|
||||
[:db :messages chat-id message-id :outgoing-status])))))))))
|
||||
|
|
|
@ -91,7 +91,7 @@
|
|||
[{:keys [db] :as cofx} message-id status]
|
||||
(if-let [{:keys [chat-id]}
|
||||
(get-in db [:transport/message-envelopes message-id])]
|
||||
(when-let [{:keys [from]} (get-in db [:chats chat-id :messages message-id])]
|
||||
(when-let [{:keys [from]} (get-in db [:messages chat-id message-id])]
|
||||
(check-confirmations cofx status chat-id message-id))
|
||||
;; We don't have a message-envelope for this, might be that the confirmation
|
||||
;; came too early
|
||||
|
|
|
@ -8,8 +8,6 @@
|
|||
[status-im.ui.screens.chat.photos :as photos]
|
||||
[status-im.utils.platform :as platform]))
|
||||
|
||||
;;TODO REWORK THIS NAMESPACE
|
||||
|
||||
(defn default-chat-icon [name styles]
|
||||
(when-not (string/blank? name)
|
||||
[react/view (:default-chat-icon styles)
|
||||
|
@ -32,15 +30,16 @@
|
|||
[react/view online-dot-right]]]])
|
||||
|
||||
(defn chat-icon-view
|
||||
[contact group-chat name _online styles]
|
||||
[chat-id group-chat name styles]
|
||||
[react/view (:container styles)
|
||||
(if-not group-chat
|
||||
[photos/photo (multiaccounts/displayed-photo contact) styles]
|
||||
[default-chat-icon name styles])])
|
||||
(if group-chat
|
||||
[default-chat-icon name styles]
|
||||
(let [photo-path @(re-frame.core/subscribe [:chats/photo-path chat-id])]
|
||||
[photos/photo photo-path styles]))])
|
||||
|
||||
(defn chat-icon-view-toolbar
|
||||
[contact group-chat name color online]
|
||||
[chat-icon-view contact group-chat name online
|
||||
[chat-id group-chat name color]
|
||||
[chat-icon-view chat-id group-chat name
|
||||
{:container styles/container-chat-toolbar
|
||||
:online-view-wrapper styles/online-view-wrapper
|
||||
:online-view styles/online-view
|
||||
|
@ -52,8 +51,8 @@
|
|||
:default-chat-icon-text (styles/default-chat-icon-text 36)}])
|
||||
|
||||
(defn chat-icon-view-chat-list
|
||||
[contact group-chat name color online]
|
||||
[chat-icon-view contact group-chat name online
|
||||
[chat-id group-chat name color]
|
||||
[chat-icon-view chat-id group-chat name
|
||||
{:container styles/container-chat-list
|
||||
:online-view-wrapper styles/online-view-wrapper
|
||||
:online-view styles/online-view
|
||||
|
@ -65,8 +64,8 @@
|
|||
:default-chat-icon-text (styles/default-chat-icon-text 40)}])
|
||||
|
||||
(defn chat-icon-view-chat-sheet
|
||||
[contact group-chat name color online]
|
||||
[chat-icon-view contact group-chat name online
|
||||
[chat-id group-chat name color]
|
||||
[chat-icon-view chat-id group-chat name
|
||||
{:container styles/container-chat-list
|
||||
:online-view-wrapper styles/online-view-wrapper
|
||||
:online-view styles/online-view
|
||||
|
@ -116,11 +115,12 @@
|
|||
:default-chat-icon (styles/default-chat-icon-profile colors/default-chat-color size)
|
||||
:default-chat-icon-text (styles/default-chat-icon-text size)}])
|
||||
|
||||
(defn chat-intro-icon-view [icon-text chat-id styles]
|
||||
(let [photo-path (re-frame.core/subscribe [:contacts/chat-photo chat-id])]
|
||||
(if-not (string/blank? @photo-path)
|
||||
[photos/photo @photo-path styles]
|
||||
[default-chat-icon icon-text styles])))
|
||||
(defn chat-intro-icon-view [icon-text chat-id group-chat styles]
|
||||
(if group-chat
|
||||
[default-chat-icon icon-text styles]
|
||||
(let [photo-path @(re-frame.core/subscribe [:chats/photo-path chat-id])]
|
||||
(if-not (string/blank? photo-path)
|
||||
[photos/photo photo-path styles]))))
|
||||
|
||||
(defn profile-icon-view [photo-path name color edit? size override-styles]
|
||||
(let [styles (merge {:container {:width size :height size}
|
||||
|
|
|
@ -2,10 +2,12 @@
|
|||
(:require [re-frame.core :as re-frame]
|
||||
[status-im.ui.components.button :as button]
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im.utils.universal-links.core :as links]
|
||||
[status-im.ui.screens.chat.styles.main :as style]
|
||||
[status-im.i18n :as i18n]
|
||||
[status-im.ui.components.list-selection :as list-selection]
|
||||
[status-im.ui.components.colors :as colors]))
|
||||
[status-im.ui.components.colors :as colors])
|
||||
(:require-macros [status-im.utils.views :refer [defview letsubs]]))
|
||||
|
||||
(defn join-chat-button [chat-id]
|
||||
[button/button
|
||||
|
@ -20,15 +22,16 @@
|
|||
[react/text {:style style/decline-chat}
|
||||
(i18n/label :t/group-chat-decline-invitation)]])
|
||||
|
||||
(defn group-chat-footer
|
||||
(defview group-chat-footer
|
||||
[chat-id]
|
||||
(letsubs [{:keys [joined?]} [:group-chat/inviter-info chat-id]]
|
||||
(when-not joined?
|
||||
[react/view {:style style/group-chat-join-footer}
|
||||
[react/view {:style style/group-chat-join-container}
|
||||
[join-chat-button chat-id]
|
||||
[decline-chat chat-id]]])
|
||||
[decline-chat chat-id]]])))
|
||||
|
||||
(defn group-chat-description-loading
|
||||
[]
|
||||
(def group-chat-description-loading
|
||||
[react/view {:style (merge style/intro-header-description-container
|
||||
{:margin-bottom 36
|
||||
:height 44})}
|
||||
|
@ -38,16 +41,9 @@
|
|||
:size :small
|
||||
:color colors/gray}]])
|
||||
|
||||
(defn group-chat-description-container
|
||||
[{:keys [pending-invite-inviter-name inviter-name chat-name public?
|
||||
universal-link range intro-status]}]
|
||||
(let [{:keys [lowest-request-from highest-request-to]} range]
|
||||
(case intro-status
|
||||
:loading
|
||||
[group-chat-description-loading]
|
||||
|
||||
:empty
|
||||
(when public?
|
||||
(defview no-messages-group-chat-description-container [chat-id]
|
||||
(letsubs [{:keys [highest-request-to lowest-request-from]}
|
||||
[:mailserver/ranges-by-chat-id chat-id]]
|
||||
[react/nested-text {:style (merge style/intro-header-description
|
||||
{:margin-bottom 36})}
|
||||
(let [quiet-hours (quot (- highest-request-to lowest-request-from)
|
||||
|
@ -63,23 +59,54 @@
|
|||
:on-press #(list-selection/open-share
|
||||
{:message
|
||||
(i18n/label
|
||||
:t/share-public-chat-text {:link universal-link})})}
|
||||
(i18n/label :t/empty-chat-description-public-share-this)]])
|
||||
:t/share-public-chat-text {:link (links/generate-link :public-chat :external chat-id)})})}
|
||||
(i18n/label :t/empty-chat-description-public-share-this)]]))
|
||||
|
||||
:messages
|
||||
(when (not public?)
|
||||
(if pending-invite-inviter-name
|
||||
(defview pending-invitation-description
|
||||
[inviter-pk chat-name]
|
||||
(letsubs [inviter-name [:contacts/contact-name-by-identity inviter-pk]]
|
||||
[react/nested-text {:style style/intro-header-description}
|
||||
[{:style {:color colors/black}} pending-invite-inviter-name]
|
||||
[{:style {:color colors/black}} inviter-name]
|
||||
(i18n/label :t/join-group-chat-description
|
||||
{:username ""
|
||||
:group-name chat-name})]
|
||||
(if (not= inviter-name "Unknown")
|
||||
:group-name chat-name})]))
|
||||
|
||||
(defview joined-group-chat-description
|
||||
[inviter-pk chat-name]
|
||||
(letsubs [inviter-name [:contacts/contact-name-by-identity inviter-pk]]
|
||||
[react/nested-text {:style style/intro-header-description}
|
||||
(i18n/label :t/joined-group-chat-description
|
||||
{:username ""
|
||||
:group-name chat-name})
|
||||
[{:style {:color colors/black}} inviter-name]]
|
||||
[{:style {:color colors/black}} inviter-name]]))
|
||||
|
||||
(defn created-group-chat-description [chat-name]
|
||||
[react/text {:style style/intro-header-description}
|
||||
(i18n/label :t/created-group-chat-description
|
||||
{:group-name chat-name})]))))))
|
||||
{:group-name chat-name})])
|
||||
|
||||
(defview group-chat-inviter-description-container [chat-id chat-name]
|
||||
(letsubs [{:keys [joined? inviter-pk]}
|
||||
[:group-chat/inviter-info chat-id]]
|
||||
(cond
|
||||
(not joined?)
|
||||
[pending-invitation-description inviter-pk chat-name]
|
||||
inviter-pk
|
||||
[joined-group-chat-description inviter-pk chat-name]
|
||||
:else
|
||||
[created-group-chat-description chat-name])))
|
||||
|
||||
(defn group-chat-description-container
|
||||
[{:keys [public?
|
||||
chat-id
|
||||
chat-name
|
||||
loading-messages?
|
||||
no-messages?]}]
|
||||
(cond loading-messages?
|
||||
group-chat-description-loading
|
||||
|
||||
(and no-messages? public?)
|
||||
[no-messages-group-chat-description-container chat-id]
|
||||
|
||||
(not public?)
|
||||
[group-chat-inviter-description-container chat-id chat-name]))
|
||||
|
|
|
@ -33,21 +33,6 @@
|
|||
:placeholder-text-color colors/gray
|
||||
:auto-capitalize :sentences}])
|
||||
|
||||
(defview reply-message [from alias content]
|
||||
(letsubs [{:keys [ens-name]} [:contacts/contact-name-by-identity from]
|
||||
current-public-key [:multiaccount/public-key]]
|
||||
[react/scroll-view {:style style/reply-message-content}
|
||||
[react/view {:style style/reply-message-to-container}
|
||||
(chat-utils/format-reply-author from alias ens-name current-public-key style/reply-message-author)]
|
||||
(if (:image content)
|
||||
[react/image {:style {:width 56
|
||||
:height 56
|
||||
:background-color :black
|
||||
:border-radius 4}
|
||||
:source {:uri (:image content)}}]
|
||||
[react/text {:style (assoc (message-style/style-message-text false) :font-size 14)
|
||||
:number-of-lines 3} (:text content)])]))
|
||||
|
||||
(defn close-button [on-press]
|
||||
[react/touchable-highlight
|
||||
{:style style/cancel-reply-highlight
|
||||
|
@ -58,12 +43,6 @@
|
|||
:height 19
|
||||
:color colors/white}]])
|
||||
|
||||
(defn reply-message-view [{:keys [content from alias]}]
|
||||
[react/view {:style style/reply-message}
|
||||
[photos/member-photo from]
|
||||
[reply-message from alias content]
|
||||
[close-button #(re-frame/dispatch [:chat.ui/cancel-message-reply])]])
|
||||
|
||||
(defn send-image-view [{:keys [uri]}]
|
||||
[react/view {:style style/reply-message}
|
||||
[react/image {:style {:width 56 :height 56
|
||||
|
@ -71,6 +50,29 @@
|
|||
:source {:uri uri}}]
|
||||
[close-button #(re-frame/dispatch [:chat.ui/cancel-sending-image])]])
|
||||
|
||||
(defview reply-message [from message-text image]
|
||||
(letsubs [contact-name [:contacts/contact-name-by-identity from]
|
||||
current-public-key [:multiaccount/public-key]]
|
||||
[react/scroll-view {:style style/reply-message-content}
|
||||
[react/view {:style style/reply-message-to-container}
|
||||
(chat-utils/format-reply-author from contact-name current-public-key style/reply-message-author)]
|
||||
(if image
|
||||
[react/image {:style {:width 56
|
||||
:height 56
|
||||
:background-color :black
|
||||
:border-radius 4}
|
||||
:source {:uri image}}]
|
||||
[react/text {:style (assoc (message-style/style-message-text false) :font-size 14)
|
||||
:number-of-lines 3} message-text])]))
|
||||
|
||||
(defview reply-message-view []
|
||||
(letsubs [{:keys [content from] :as message} [:chats/reply-message]]
|
||||
(when message
|
||||
[react/view {:style style/reply-message}
|
||||
[photos/member-photo from]
|
||||
[reply-message from (:text content) (:image content)]
|
||||
[close-button #(re-frame/dispatch [:chat.ui/cancel-message-reply])]])))
|
||||
|
||||
(defview container []
|
||||
(letsubs [mainnet? [:mainnet?]
|
||||
input-text [:chats/current-chat-input-text]
|
||||
|
|
|
@ -21,15 +21,15 @@
|
|||
|
||||
(views/defview gap
|
||||
[{:keys [gaps first-gap?]} idx list-ref]
|
||||
(views/letsubs [{:keys [range intro-status]} [:chats/current-chat]
|
||||
(views/letsubs [range [:chats/range]
|
||||
{:keys [might-have-join-time-messages?]} [:chats/current-raw-chat]
|
||||
in-progress? [:chats/fetching-gap-in-progress?
|
||||
(if first-gap?
|
||||
[:first-gap]
|
||||
(:ids gaps))]
|
||||
connected? [:mailserver/connected?]]
|
||||
(let [ids (:ids gaps)
|
||||
intro-loading? (= intro-status :loading)]
|
||||
(when-not (and first-gap? intro-loading?)
|
||||
(let [ids (:ids gaps)]
|
||||
(when-not (and first-gap? might-have-join-time-messages?)
|
||||
[react/view {:style style/gap-container}
|
||||
[react/touchable-highlight
|
||||
{:on-press (when (and connected? (not in-progress?))
|
||||
|
|
|
@ -18,8 +18,8 @@
|
|||
(:require-macros [status-im.utils.views :refer [defview letsubs]]))
|
||||
|
||||
(defview mention-element [from]
|
||||
(letsubs [{:keys [ens-name alias]} [:contacts/contact-name-by-identity from]]
|
||||
(if ens-name (str "@" ens-name) alias)))
|
||||
(letsubs [contact-name [:contacts/contact-name-by-identity from]]
|
||||
contact-name))
|
||||
|
||||
(defn message-timestamp
|
||||
([message]
|
||||
|
@ -38,14 +38,13 @@
|
|||
appender])
|
||||
|
||||
(defview quoted-message
|
||||
[_ {:keys [from text image]} outgoing current-public-key public?]
|
||||
(letsubs [{:keys [ens-name alias]} [:contacts/contact-name-by-identity from]]
|
||||
[_ {:keys [from text]} outgoing current-public-key public?]
|
||||
(letsubs [contact-name [:contacts/contact-name-by-identity from]]
|
||||
[react/view {:style (style/quoted-message-container outgoing)}
|
||||
[react/view {:style style/quoted-message-author-container}
|
||||
[chat.utils/format-reply-author
|
||||
from
|
||||
alias
|
||||
ens-name
|
||||
contact-name
|
||||
current-public-key
|
||||
(partial style/quoted-message-author outgoing)]]
|
||||
(if (and image
|
||||
|
@ -239,8 +238,8 @@
|
|||
nil)))
|
||||
|
||||
(defview message-author-name [from alias]
|
||||
(letsubs [{:keys [ens-name]} [:contacts/contact-name-by-identity from]]
|
||||
(chat.utils/format-author alias style/message-author-name-container ens-name)))
|
||||
(letsubs [contact-name [:contacts/raw-contact-name-by-identity from]]
|
||||
(chat.utils/format-author (or contact-name alias) style/message-author-name-container)))
|
||||
|
||||
(defn message-content-wrapper
|
||||
"Author, userpic and delivery wrapper"
|
||||
|
@ -261,7 +260,6 @@
|
|||
(when display-username?
|
||||
[react/touchable-opacity {:style style/message-author-touchable
|
||||
:on-press #(re-frame/dispatch [:chat.ui/show-profile from])}
|
||||
;;TODO (perf) move to event
|
||||
[message-author-name from alias]])
|
||||
;;MESSAGE CONTENT
|
||||
content]]
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
[status-im.ui.components.react :as react]
|
||||
[status-im.ui.components.list-selection :as list-selection]
|
||||
[status-im.utils.universal-links.core :as universal-links]
|
||||
[status-im.multiaccounts.core :as multiaccounts]
|
||||
[status-im.ui.components.chat-icon.screen :as chat-icon]
|
||||
[status-im.ui.components.colors :as colors]
|
||||
[status-im.utils.platform :as platform]
|
||||
|
@ -27,12 +26,14 @@
|
|||
:color colors/gray}}
|
||||
(i18n/label helper)]])
|
||||
|
||||
(defn chat-actions [{:keys [chat-id contact chat-name]}]
|
||||
(defn one-to-one-chat-actions [{:keys [chat-id]}]
|
||||
(let [photo @(re-frame/subscribe [:chats/photo-path chat-id])
|
||||
contact-name @(re-frame/subscribe [:contacts/contact-name-by-identity chat-id])]
|
||||
[react/view
|
||||
[list-item/list-item
|
||||
{:theme :action
|
||||
:icon (multiaccounts/displayed-photo contact)
|
||||
:title [view-profile {:name chat-name
|
||||
:icon photo
|
||||
:title [view-profile {:name contact-name
|
||||
:helper :t/view-profile}]
|
||||
:accessibility-label :view-chat-details-button
|
||||
:accessories [:chevron]
|
||||
|
@ -60,7 +61,7 @@
|
|||
:title :t/delete-chat
|
||||
:accessibility-label :delete-chat-button
|
||||
:icon :main-icons/delete
|
||||
:on-press #(hide-sheet-and-dispatch [:chat.ui/remove-chat-pressed chat-id])}]])
|
||||
:on-press #(hide-sheet-and-dispatch [:chat.ui/remove-chat-pressed chat-id])}]]))
|
||||
|
||||
(defn public-chat-actions [{:keys [chat-id]}]
|
||||
(let [link (universal-links/generate-link :public-chat :external chat-id)
|
||||
|
@ -101,15 +102,15 @@
|
|||
:on-press #(hide-sheet-and-dispatch [:chat.ui/remove-chat-pressed chat-id])}]]))
|
||||
|
||||
(defn group-chat-actions []
|
||||
(fn [{:keys [chat-id contact group-chat chat-name color online]}]
|
||||
(let [joined @(re-frame/subscribe [:group-chat/chat-joined? chat-id])]
|
||||
(fn [{:keys [chat-id group-chat chat-name color]}]
|
||||
(let [{:keys [joined?]} @(re-frame/subscribe [:group-chat/inviter-info chat-id])]
|
||||
[react/view
|
||||
[list-item/list-item
|
||||
{:theme :action
|
||||
:title [view-profile {:name chat-name
|
||||
:helper :t/group-info}]
|
||||
:icon [chat-icon/chat-icon-view-chat-sheet
|
||||
contact group-chat chat-name color online]
|
||||
chat-id group-chat chat-name color]
|
||||
:accessories [:chevron]
|
||||
:on-press #(hide-sheet-and-dispatch [:show-group-chat-profile chat-id])}]
|
||||
[list-item/list-item
|
||||
|
@ -130,7 +131,7 @@
|
|||
:accessibility-label :fetch-history-button
|
||||
:icon :main-icons/arrow-down
|
||||
:on-press #(hide-sheet-and-dispatch [:chat.ui/fetch-history-pressed chat-id])}]
|
||||
(when joined
|
||||
(when joined?
|
||||
[list-item/list-item
|
||||
{:theme :action
|
||||
:title :t/leave-chat
|
||||
|
@ -143,7 +144,7 @@
|
|||
(cond
|
||||
public? [public-chat-actions current-chat]
|
||||
group-chat [group-chat-actions current-chat]
|
||||
:else [chat-actions current-chat]))
|
||||
:else [one-to-one-chat-actions current-chat]))
|
||||
|
||||
(defn options [chat-id message-id]
|
||||
(fn []
|
||||
|
@ -162,16 +163,16 @@
|
|||
:accessibility-label :delete-transaction-button
|
||||
:on-press #(hide-sheet-and-dispatch [:chat.ui/delete-message chat-id message-id])}]]))
|
||||
|
||||
(defn message-long-press [{:keys [content identicon from outgoing] :as message}]
|
||||
(defn message-long-press [{:keys [content from outgoing] :as message}]
|
||||
(fn []
|
||||
(let [{:keys [ens-name alias]} @(re-frame/subscribe [:contacts/contact-name-by-identity from])]
|
||||
(let [photo @(re-frame/subscribe [:chats/photo-path from])
|
||||
contact-name @(re-frame/subscribe [:contacts/contact-name-by-identity from])]
|
||||
[react/view
|
||||
(when-not outgoing
|
||||
[list-item/list-item
|
||||
{:theme :action
|
||||
:icon (multiaccounts/displayed-photo {:identicon identicon
|
||||
:public-key from})
|
||||
:title [view-profile {:name (or ens-name alias)
|
||||
:icon photo
|
||||
:title [view-profile {:name contact-name
|
||||
:helper :t/view-profile}]
|
||||
:accessibility-label :view-chat-details-button
|
||||
:accessories [:chevron]
|
||||
|
@ -197,15 +198,15 @@
|
|||
(re-frame/dispatch [:bottom-sheet/hide-sheet])
|
||||
(list-selection/open-share {:message (:text content)}))}])])))
|
||||
|
||||
(defn sticker-long-press [{:keys [from identicon]}]
|
||||
(defn sticker-long-press [{:keys [from]}]
|
||||
(fn []
|
||||
(let [{:keys [ens-name alias]} @(re-frame/subscribe [:contacts/contact-name-by-identity from])]
|
||||
(let [photo @(re-frame/subscribe [:chats/photo-path from])
|
||||
contact-name @(re-frame/subscribe [:contacts/contact-name-by-identity from])]
|
||||
[react/view
|
||||
[list-item/list-item
|
||||
{:theme :action
|
||||
:icon (multiaccounts/displayed-photo {:identicon identicon
|
||||
:public-key from})
|
||||
:title [view-profile {:name (or ens-name alias)
|
||||
:icon photo
|
||||
:title [view-profile {:name contact-name
|
||||
:helper :t/view-profile}]
|
||||
:accessibility-label :view-chat-details-button
|
||||
:accessories [:chevron]
|
||||
|
|
|
@ -47,8 +47,8 @@
|
|||
:margin-right 6})
|
||||
|
||||
(defn intro-header-container
|
||||
[status no-messages]
|
||||
(if (or no-messages (= status (or :loading :empty)))
|
||||
[loading-messages? no-messages?]
|
||||
(if (or loading-messages? no-messages?)
|
||||
{:flex 1
|
||||
:flex-direction :column
|
||||
:justify-content :center
|
||||
|
|
|
@ -5,29 +5,7 @@
|
|||
[status-im.ui.screens.chat.styles.main :as st])
|
||||
(:require-macros [status-im.utils.views :refer [defview letsubs]]))
|
||||
|
||||
(defn- in-progress-text [{:keys [highestBlock currentBlock startBlock]}]
|
||||
(let [total (- highestBlock startBlock)
|
||||
ready (- currentBlock startBlock)
|
||||
percentage (if (zero? ready)
|
||||
0
|
||||
(->> (/ ready total)
|
||||
(* 100)
|
||||
(.round js/Math)))]
|
||||
|
||||
(str (i18n/label :t/sync-in-progress) " " percentage "% " currentBlock)))
|
||||
|
||||
(defview last-activity [{:keys [sync-state accessibility-label]}]
|
||||
(letsubs [state [:sync-data]]
|
||||
[react/text {:style st/last-activity-text
|
||||
:accessibility-label accessibility-label}
|
||||
(case sync-state
|
||||
:in-progress (in-progress-text state)
|
||||
:synced (i18n/label :t/sync-synced))]))
|
||||
|
||||
(defn- group-last-activity [{:keys [contacts sync-state public?]}]
|
||||
(if (or (= sync-state :in-progress)
|
||||
(= sync-state :synced))
|
||||
[last-activity {:sync-state sync-state}]
|
||||
(defn- group-last-activity [{:keys [contacts public?]}]
|
||||
[react/view {:flex-direction :row}
|
||||
[react/text {:style st/toolbar-subtitle}
|
||||
(if public?
|
||||
|
@ -35,34 +13,40 @@
|
|||
(let [cnt (count contacts)]
|
||||
(if (zero? cnt)
|
||||
(i18n/label :members-active-none)
|
||||
(i18n/label-pluralize cnt :t/members-active))))]]))
|
||||
(i18n/label-pluralize cnt :t/members-active))))]])
|
||||
|
||||
(defn- contact-indicator [{:keys [added?]}]
|
||||
(defview one-to-one-name [from]
|
||||
(letsubs [contact-name [:contacts/contact-name-by-identity from]]
|
||||
contact-name))
|
||||
|
||||
(defview contact-indicator [contact-id]
|
||||
(letsubs [added? [:contacts/contact-added? contact-id]]
|
||||
[react/view {:flex-direction :row}
|
||||
[react/text {:style st/toolbar-subtitle}
|
||||
(if added?
|
||||
(i18n/label :chat-is-a-contact)
|
||||
(i18n/label :chat-is-not-a-contact))]])
|
||||
(i18n/label :chat-is-not-a-contact))]]))
|
||||
|
||||
(defview toolbar-content-view []
|
||||
(letsubs [{:keys [group-chat color online contacts chat-name contact public?]}
|
||||
[:chats/current-chat]
|
||||
sync-state [:sync-state]]
|
||||
(let [has-subtitle? (or group-chat (not= :done sync-state))]
|
||||
(letsubs [{:keys [group-chat
|
||||
color
|
||||
chat-id
|
||||
contacts
|
||||
chat-name
|
||||
public?]}
|
||||
[:chats/current-chat]]
|
||||
[react/view {:style st/toolbar-container}
|
||||
[react/view {:margin-right 10}
|
||||
[chat-icon.screen/chat-icon-view-toolbar contact group-chat chat-name color online]]
|
||||
[chat-icon.screen/chat-icon-view-toolbar chat-id group-chat chat-name color]]
|
||||
[react/view {:style st/chat-name-view}
|
||||
[react/text {:style st/chat-name-text
|
||||
:number-of-lines 1
|
||||
:accessibility-label :chat-name-text}
|
||||
chat-name]
|
||||
(when contact
|
||||
[contact-indicator contact])
|
||||
(if group-chat
|
||||
chat-name
|
||||
[one-to-one-name chat-id])]
|
||||
(when-not group-chat
|
||||
[contact-indicator chat-id])
|
||||
(when group-chat
|
||||
[group-last-activity {:contacts contacts
|
||||
:public? public?
|
||||
:sync-state sync-state}]
|
||||
(when has-subtitle?
|
||||
[last-activity {:sync-state sync-state
|
||||
:accessibility-label :last-seen-text}]))]])))
|
||||
:public? public?}])]]))
|
||||
|
|
|
@ -4,30 +4,26 @@
|
|||
[status-im.ui.components.react :as react]
|
||||
[status-im.ui.components.colors :as colors]))
|
||||
|
||||
(defn format-author [alias style name]
|
||||
(defn format-author [contact-name style]
|
||||
(let [additional-styles (style false)]
|
||||
(if name
|
||||
(let [name (subs name 0 80)]
|
||||
(if (= (aget contact-name 0) "@")
|
||||
(let [trimmed-name (subs contact-name 0 81)]
|
||||
[react/text {:number-of-lines 2
|
||||
:style (merge {:color colors/blue
|
||||
:font-size 13
|
||||
:line-height 18
|
||||
:font-weight "500"} additional-styles)}
|
||||
(str "@" (or (stateofus/username name) name))])
|
||||
(or (stateofus/username trimmed-name) trimmed-name)])
|
||||
[react/text {:style (merge {:color colors/gray
|
||||
:font-size 12
|
||||
:line-height 18
|
||||
:font-weight "400"} additional-styles)}
|
||||
alias])))
|
||||
contact-name])))
|
||||
|
||||
(def ^:private reply-symbol "↪ ")
|
||||
|
||||
(defn format-reply-author [from alias username current-public-key style]
|
||||
(let [reply-name (or (some->> username
|
||||
(str "@")
|
||||
(str reply-symbol))
|
||||
(str reply-symbol alias))]
|
||||
(defn format-reply-author [from username current-public-key style]
|
||||
(or (and (= from current-public-key)
|
||||
[react/text {:style (style true)}
|
||||
(str reply-symbol (i18n/label :t/You))])
|
||||
(format-author (subs reply-name 0 80) style false))))
|
||||
(format-author username style)))
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
(ns status-im.ui.screens.chat.views
|
||||
(:require [re-frame.core :as re-frame]
|
||||
[status-im.contact.db :as contact.db]
|
||||
[status-im.i18n :as i18n]
|
||||
[status-im.multiaccounts.core :as multiaccounts]
|
||||
[status-im.ui.components.chat-icon.screen :as chat-icon.screen]
|
||||
[status-im.ui.components.colors :as colors]
|
||||
[status-im.ui.components.connectivity.view :as connectivity]
|
||||
|
@ -41,8 +39,9 @@
|
|||
[sheets/actions current-chat])
|
||||
:height 256}])}]}])
|
||||
|
||||
(defn add-contact-bar
|
||||
[public-key]
|
||||
(defview add-contact-bar [public-key]
|
||||
(letsubs [added? [:contacts/contact-added? public-key]]
|
||||
(when-not added?
|
||||
[react/touchable-highlight
|
||||
{:on-press
|
||||
#(re-frame/dispatch [:contact.ui/add-to-contact-pressed public-key])
|
||||
|
@ -50,24 +49,58 @@
|
|||
[react/view {:style (style/add-contact)}
|
||||
[vector-icons/icon :main-icons/add
|
||||
{:color colors/blue}]
|
||||
[react/i18n-text {:style style/add-contact-text :key :add-to-contacts}]]])
|
||||
[react/i18n-text {:style style/add-contact-text :key :add-to-contacts}]]])))
|
||||
|
||||
(defn intro-header
|
||||
[contact]
|
||||
(defn intro-header [name]
|
||||
[react/text {:style (assoc style/intro-header-description
|
||||
:margin-bottom 32)}
|
||||
(str
|
||||
(i18n/label :t/empty-chat-description-one-to-one)
|
||||
(multiaccounts/displayed-name contact))])
|
||||
name)])
|
||||
|
||||
(defn chat-intro [{:keys [chat-id
|
||||
chat-name
|
||||
group-chat
|
||||
contact-name
|
||||
public?
|
||||
color
|
||||
loading-messages?
|
||||
no-messages?]}]
|
||||
[react/view (style/intro-header-container loading-messages? no-messages?)
|
||||
;; Icon section
|
||||
[react/view {:style {:margin-top 42
|
||||
:margin-bottom 24}}
|
||||
[chat-icon.screen/chat-intro-icon-view
|
||||
chat-name chat-id group-chat
|
||||
{:default-chat-icon (style/intro-header-icon 120 color)
|
||||
:default-chat-icon-text style/intro-header-icon-text
|
||||
:size 120}]]
|
||||
;; Chat title section
|
||||
[react/text {:style (style/intro-header-chat-name)} (if group-chat chat-name contact-name)]
|
||||
;; Description section
|
||||
(if group-chat
|
||||
[chat.group/group-chat-description-container {:chat-id chat-id
|
||||
:loading-messages? loading-messages?
|
||||
:chat-name chat-name
|
||||
:public? public?
|
||||
:no-messages? no-messages?}]
|
||||
[react/text {:style (assoc style/intro-header-description
|
||||
:margin-bottom 32)}
|
||||
|
||||
(str
|
||||
(i18n/label :t/empty-chat-description-one-to-one)
|
||||
contact-name)])])
|
||||
|
||||
(defview chat-intro-one-to-one [{:keys [chat-id] :as opts}]
|
||||
(letsubs [contact-name [:contacts/contact-name-by-identity chat-id]]
|
||||
(chat-intro (assoc opts :contact-name contact-name))))
|
||||
|
||||
(defn chat-intro-header-container
|
||||
[{:keys [group-chat name pending-invite-inviter-name color chat-id chat-name
|
||||
public? contact intro-status] :as chat}
|
||||
[{:keys [group-chat
|
||||
might-have-join-time-messages?
|
||||
color chat-id chat-name
|
||||
public?]}
|
||||
no-messages]
|
||||
(let [icon-text (if public? chat-id name)
|
||||
intro-name (if public? chat-name (multiaccounts/displayed-name contact))]
|
||||
(when (or pending-invite-inviter-name
|
||||
(not= (get-in contact [:tribute-to-talk :snt-amount]) 0))
|
||||
[react/touchable-without-feedback
|
||||
{:style {:flex 1
|
||||
:align-items :flex-start}
|
||||
|
@ -75,22 +108,17 @@
|
|||
(re-frame/dispatch
|
||||
[:chat.ui/set-chat-ui-props {:input-bottom-sheet nil}])
|
||||
(react/dismiss-keyboard!))}
|
||||
[react/view (style/intro-header-container intro-status no-messages)
|
||||
;; Icon section
|
||||
[react/view {:style {:margin-top 42
|
||||
:margin-bottom 24}}
|
||||
[chat-icon.screen/chat-intro-icon-view
|
||||
icon-text chat-id
|
||||
{:default-chat-icon (style/intro-header-icon 120 color)
|
||||
:default-chat-icon-text style/intro-header-icon-text
|
||||
:size 120}]]
|
||||
;; Chat title section
|
||||
[react/text {:style (style/intro-header-chat-name)}
|
||||
(if group-chat chat-name intro-name)]
|
||||
;; Description section
|
||||
(let [opts
|
||||
{:chat-id chat-id
|
||||
:group-chat group-chat
|
||||
:chat-name chat-name
|
||||
:public? public?
|
||||
:color color
|
||||
:loading-messages? might-have-join-time-messages?
|
||||
:no-messages? no-messages}]
|
||||
(if group-chat
|
||||
[chat.group/group-chat-description-container chat]
|
||||
[intro-header contact])]])))
|
||||
[chat-intro opts]
|
||||
[chat-intro-one-to-one opts]))])
|
||||
|
||||
(defonce messages-list-ref (atom nil))
|
||||
|
||||
|
@ -110,15 +138,16 @@
|
|||
(debounce/debounce-and-dispatch [:chat.ui/message-visibility-changed e] 5000))
|
||||
|
||||
(defview messages-view
|
||||
[{:keys [public? group-chat chat-id pending-invite-inviter-name] :as chat}]
|
||||
[{:keys [group-chat chat-id public?] :as chat}]
|
||||
(letsubs [messages [:chats/current-chat-messages-stream]
|
||||
no-messages? [:chats/current-chat-no-messages?]
|
||||
current-public-key [:multiaccount/public-key]]
|
||||
[list/flat-list
|
||||
{:key-fn #(or (:message-id %) (:value %))
|
||||
:ref #(reset! messages-list-ref %)
|
||||
:header (when pending-invite-inviter-name
|
||||
:header (when (and group-chat (not public?))
|
||||
[chat.group/group-chat-footer chat-id])
|
||||
:footer [chat-intro-header-container chat (empty? messages)]
|
||||
:footer [chat-intro-header-container chat no-messages?]
|
||||
:data messages
|
||||
:inverted true
|
||||
:render-fn (fn [{:keys [outgoing type] :as message} idx]
|
||||
|
@ -154,14 +183,13 @@
|
|||
[empty-bottom-sheet])))
|
||||
|
||||
(defview chat []
|
||||
(letsubs [{:keys [chat-id show-input? group-chat contact] :as current-chat}
|
||||
(letsubs [{:keys [chat-id show-input? group-chat] :as current-chat}
|
||||
[:chats/current-chat]]
|
||||
[react/view {:style {:flex 1}}
|
||||
[connectivity/connectivity
|
||||
[topbar current-chat]
|
||||
[react/view {:style {:flex 1}}
|
||||
;;TODO contact.db/added? looks weird here, move to events
|
||||
(when (and (not group-chat) (not (contact.db/added? contact)))
|
||||
(when-not group-chat
|
||||
[add-contact-bar chat-id])
|
||||
[messages-view current-chat]]]
|
||||
(when show-input?
|
||||
|
|
|
@ -18,7 +18,9 @@
|
|||
[status-im.ui.components.radio :as radio]
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im.ui.components.topbar :as topbar]
|
||||
[status-im.ui.screens.chat.utils :as chat.utils]
|
||||
[status-im.ui.screens.chat.message.message :as message]
|
||||
[status-im.ui.screens.chat.styles.message.message :as message.style]
|
||||
[status-im.ui.screens.chat.photos :as photos]
|
||||
[status-im.ui.screens.profile.components.views :as profile.components]
|
||||
[status-im.utils.debounce :as debounce])
|
||||
|
@ -624,7 +626,11 @@
|
|||
[name-item {:name name :hide-chevron? true :action action}]]
|
||||
[radio/radio (= name preferred-name)]]]))]]]])
|
||||
|
||||
(defn- registered [names {:keys [preferred-name public-key] :as account} _]
|
||||
(views/defview my-name []
|
||||
(views/letsubs [contact-name [:multiaccount/my-name]]
|
||||
(chat.utils/format-author contact-name message.style/message-author-name-container)))
|
||||
|
||||
(defn- registered [names {:keys [preferred-name] :as account} _]
|
||||
[react/view {:style {:flex 1}}
|
||||
[react/scroll-view
|
||||
[react/view {:style {:margin-top 8}}
|
||||
|
@ -663,7 +669,7 @@
|
|||
:timestamp-str "9:41 AM"}]
|
||||
[react/view
|
||||
[react/view {:padding-left 72}
|
||||
[message/message-author-name public-key]]
|
||||
[my-name]]
|
||||
[react/view {:flex-direction :row}
|
||||
[react/view {:padding-left 16 :padding-right 8 :padding-top 4}
|
||||
[photos/photo (multiaccounts/displayed-photo account) {:size 36}]]
|
||||
|
|
|
@ -15,8 +15,8 @@
|
|||
(:require-macros [status-im.utils.views :refer [defview letsubs]]))
|
||||
|
||||
(defview mention-element [from]
|
||||
(letsubs [{:keys [ens-name alias]} [:contacts/contact-name-by-identity from]]
|
||||
(if ens-name (str "@" ens-name) alias)))
|
||||
(letsubs [contact-name [:contacts/contact-name-by-identity from]]
|
||||
contact-name))
|
||||
|
||||
(defn render-subheader-inline [acc {:keys [type destination literal children]}]
|
||||
(case type
|
||||
|
@ -108,20 +108,20 @@
|
|||
|
||||
(defn home-list-item [[_ home-item]]
|
||||
(let [{:keys [chat-id chat-name color online group-chat
|
||||
public? contact timestamp last-message]}
|
||||
public? timestamp last-message]}
|
||||
home-item
|
||||
private-group? (and group-chat (not public?))
|
||||
public-group? (and group-chat public?)
|
||||
;;TODO (perf) move to event
|
||||
truncated-chat-name (utils/truncate-str chat-name 30)]
|
||||
public-group? (and group-chat public?)]
|
||||
[list-item/list-item
|
||||
{:icon [chat-icon.screen/chat-icon-view-chat-list
|
||||
contact group-chat truncated-chat-name color online false]
|
||||
chat-id group-chat chat-name color online false]
|
||||
:title-prefix (cond
|
||||
private-group? :main-icons/tiny-group
|
||||
public-group? :main-icons/tiny-public
|
||||
:else nil)
|
||||
:title truncated-chat-name
|
||||
:title (if group-chat
|
||||
(utils/truncate-str chat-name 30)
|
||||
@(re-frame/subscribe [:contacts/contact-name-by-identity chat-id]))
|
||||
:title-accessibility-label :chat-name-text
|
||||
:title-row-accessory [message-timestamp (if (pos? (:whisper-timestamp last-message))
|
||||
(:whisper-timestamp last-message)
|
||||
|
|
Loading…
Reference in New Issue