Move mailserver logic/gaps/ranges to status-go

This commit moves most of the mailserver logic to status-go.

- Filters are now removed and not passed to the client anymore
- Ranges have been removed
- Gaps are now messages with a different content type
- Upsert/Save chat has been removed and instead we have more specific
endpoints such as CreatePublicChat/CreateOneToOneChat/CreateProfileChat
- Creation of timeline/profile chat has been moved to status-go

Signed-off-by: Andrea Maria Piana <andrea.maria.piana@gmail.com>
This commit is contained in:
Andrea Maria Piana 2021-04-22 12:09:25 +02:00
parent 929925af39
commit dd5c30b7a0
No known key found for this signature in database
GPG Key ID: AA6CCA6DE0E06424
33 changed files with 433 additions and 3235 deletions

View File

@ -1,6 +1,5 @@
(ns status-im.chat.db (ns status-im.chat.db
(:require [clojure.string :as clojure.string] (:require [status-im.constants :as constants]))
[status-im.mailserver.constants :as mailserver.constants]))
(defn group-chat-name (defn group-chat-name
[{:keys [public? name]}] [{:keys [public? name]}]
@ -48,89 +47,42 @@
(conj messages-with-datemarks {:value (:datemark (peek messages-with-datemarks)) (conj messages-with-datemarks {:value (:datemark (peek messages-with-datemarks))
:type :datemark})))) :type :datemark}))))
(defn gap? [{:keys [type]}] (defn last-gap
(= type :gap)) "last-gap is a special gap that is put last in the message stream"
[chat-id synced-from]
{:message-id "0x123"
:message-type constants/message-type-gap
:chat-id chat-id
:content-type constants/content-type-gap
:gap-ids #{:first-gap}
:gap-parameters {:from synced-from}})
(defn check-gap (defn collapse-gaps
[gaps previous-message next-message] "collapse-gaps will take an array of messages and collapse any gap next to
(let [previous-timestamp (:whisper-timestamp previous-message) each other in a single gap.
next-whisper-timestamp (:whisper-timestamp next-message) It will also append one last gap if the last message is a non-gap"
next-timestamp (:timestamp next-message) [messages chat-id synced-from]
ignore-next-message? (> (js/Math.abs (let [messages-with-gaps (reduce
(- next-whisper-timestamp next-timestamp)) (fn [acc {:keys [gap-parameters message-id] :as message}]
120000)] (let [last-element (peek acc)]
(reduce (cond
(fn [acc {:keys [from to id]}] ;; If it's a message, just add
(let [from-ms (* from 1000) (empty? gap-parameters)
to-ms (* to 1000)] (conj acc message)
(if (and next-message
(not ignore-next-message?) ;; Both are gaps, merge them
(or
(and (nil? previous-timestamp)
(< from-ms next-whisper-timestamp))
(and (and
(< previous-timestamp from-ms) (seq (:gap-parameters last-element))
(< to-ms next-whisper-timestamp)) (seq gap-parameters))
(and (conj (pop acc) (update last-element :gap-ids conj message-id))
(< from-ms previous-timestamp)
(< to-ms next-whisper-timestamp))))
(-> acc
(update :gaps-number inc)
(update-in [:gap :ids] conj id))
(reduced acc))))
{:gaps-number 0
:gap nil}
gaps)))
(defn add-gap [messages gaps] ;; it's a gap
(conj messages :else
{:type :gap (conj acc (assoc message :gap-ids #{message-id})))))
:value (clojure.string/join (:ids gaps)) []
:gaps gaps})) messages)]
;; If it's a gap or the chat is still syncing, do nothing
(defn add-gaps (if (or (nil? synced-from)
"Converts message groups into sequence of messages interspersed with datemarks, (:gap-ids (peek messages-with-gaps)))
with correct user statuses associated into message" messages-with-gaps
[message-list messages-gaps (conj messages-with-gaps (last-gap chat-id synced-from)))))
{:keys [highest-request-to lowest-request-from]} all-loaded? public?]
(transduce
(map identity)
(fn
([]
(let [acc {:messages (list)
:previous-message nil
:gaps messages-gaps}]
(if (and
public?
all-loaded?
(not (nil? highest-request-to))
(not (nil? lowest-request-from))
(< (- highest-request-to lowest-request-from)
mailserver.constants/max-gaps-range))
(update acc :messages conj {:type :gap
:value (str :first-gap)
:first-gap? true})
acc)))
([{:keys [messages previous-message gaps]} message]
(let [{:keys [gaps-number gap]}
(check-gap gaps previous-message message)
add-gap? (pos? gaps-number)]
{:messages (cond-> messages
add-gap?
(add-gap gap)
:always
(conj message))
:previous-message message
:gaps (if add-gap?
(drop gaps-number gaps)
gaps)}))
([{:keys [messages gaps]}]
(cond-> messages
(seq gaps)
(add-gap {:ids (map :id gaps)}))))
(reverse message-list)))
(def map->sorted-seq
(comp (partial map second) (partial sort-by first)))

View File

@ -38,127 +38,3 @@
(:datemark m4))) (:datemark m4)))
(is (= {:type :datemark (is (= {:type :datemark
:value "Dec 31, 1999"} d2))))) :value "Dec 31, 1999"} d2)))))
(deftest add-gaps
(testing "empty state"
(is (empty?
(db/add-gaps
nil
nil
nil
false
false))))
(testing "empty state pub-chat"
(is (=
[{:type :gap
:value ":first-gap"
:first-gap? true}]
(db/add-gaps
nil
nil
{:lowest-request-from 10
:highest-request-to 30}
true
true))))
(testing "simple case with gap"
(is (= '({:whisper-timestamp 40000
:message-id :m4
:timestamp 40000}
{:type :gap
:value ":gapid1"
:gaps {:ids [:gapid1]}}
{:whisper-timestamp 30000
:timestamp 30000
:message-id :m3}
{:value "today"
:type :datemark
:whisper-timestamp 30000
:timestamp 30000}
{:whisper-timestamp 20000
:timestamp 20000
:message-id :m2}
{:whisper-timestamp 10000
:timestamp 10000
:message-id :m1}
{:value "yesterday"
:type :datemark
:whisper-timestamp 10000
:timestamp 10000})
(db/add-gaps
[{:message-id :m4
:whisper-timestamp 40000
:timestamp 40000}
{:message-id :m3
:whisper-timestamp 30000
:timestamp 30000}
{:type :datemark
:value "today"
:whisper-timestamp 30000
:timestamp 30000}
{:message-id :m2
:whisper-timestamp 20000
:timestamp 20000}
{:message-id :m1
:whisper-timestamp 10000
:timestamp 10000}
{:type :datemark
:value "yesterday"
:whisper-timestamp 10000
:timestamp 10000}]
[{:from 25
:to 30
:id :gapid1}]
nil
nil
nil))))
(testing "simple case with gap after all messages"
(is (= '({:type :gap
:value ":gapid1"
:gaps {:ids (:gapid1)}}
{:whisper-timestamp 40000
:message-id :m4
:timestamp 40000}
{:whisper-timestamp 30000
:message-id :m3
:timestamp 30000}
{:value "today"
:type :datemark
:whisper-timestamp 30000
:timestamp 30000}
{:whisper-timestamp 20000
:message-id :m2
:timestamp 20000}
{:whisper-timestamp 10000
:message-id :m1
:timestamp 10000}
{:value "yesterday"
:type :datemark
:whisper-timestamp 10000
:timestamp 10000})
(db/add-gaps
[{:message-id :m4
:whisper-timestamp 40000
:timestamp 40000}
{:message-id :m3
:whisper-timestamp 30000
:timestamp 30000}
{:type :datemark
:value "today"
:whisper-timestamp 30000
:timestamp 30000}
{:message-id :m2
:whisper-timestamp 20000
:timestamp 20000}
{:message-id :m1
:whisper-timestamp 10000
:timestamp 10000}
{:type :datemark
:value "yesterday"
:whisper-timestamp 10000
:timestamp 10000}]
[{:from 100
:to 110
:id :gapid1}]
nil
nil
nil)))))

View File

@ -2,12 +2,10 @@
(:require [re-frame.core :as re-frame] (:require [re-frame.core :as re-frame]
[taoensso.timbre :as log] [taoensso.timbre :as log]
[status-im.multiaccounts.model :as multiaccounts.model] [status-im.multiaccounts.model :as multiaccounts.model]
[status-im.transport.filters.core :as transport.filters] [status-im.chat.models.message-list :as message-list]
[status-im.data-store.chats :as chats-store] [status-im.data-store.chats :as chats-store]
[status-im.data-store.messages :as messages-store]
[status-im.ethereum.json-rpc :as json-rpc] [status-im.ethereum.json-rpc :as json-rpc]
[status-im.i18n.i18n :as i18n] [status-im.i18n.i18n :as i18n]
[status-im.mailserver.core :as mailserver]
[status-im.ui.components.colors :as colors] [status-im.ui.components.colors :as colors]
[status-im.constants :as constants] [status-im.constants :as constants]
[status-im.navigation :as navigation] [status-im.navigation :as navigation]
@ -16,8 +14,6 @@
[status-im.utils.utils :as utils] [status-im.utils.utils :as utils]
[status-im.utils.types :as types] [status-im.utils.types :as types]
[status-im.add-new.db :as new-public-chat.db] [status-im.add-new.db :as new-public-chat.db]
[status-im.mailserver.topics :as mailserver.topics]
[status-im.mailserver.constants :as mailserver.constants]
[status-im.chat.models.loading :as loading] [status-im.chat.models.loading :as loading]
[status-im.ui.screens.chat.state :as chat.state])) [status-im.ui.screens.chat.state :as chat.state]))
@ -83,33 +79,6 @@
[{:keys [current-chat-id] :as db} kvs] [{:keys [current-chat-id] :as db} kvs]
(update-in db [:chat-ui-props current-chat-id] merge kvs)) (update-in db [:chat-ui-props current-chat-id] merge kvs))
(defn dissoc-join-time-fields [db chat-id]
(update-in db [:chats chat-id] dissoc
:join-time-mail-request-id
:might-have-join-time-messages?))
(fx/defn join-time-messages-checked
"The key :might-have-join-time-messages? in public chats signals that
the public chat is freshly (re)created and requests for messages to the
mailserver for the topic has not completed yet. Likewise, the key
:join-time-mail-request-id is associated a little bit after, to signal that
the request to mailserver was a success. When request is signalled complete
by mailserver, corresponding event :chat.ui/join-time-messages-checked
dissociates these two fileds via this function, thereby signalling that the
public chat is not fresh anymore."
{:events [:chat.ui/join-time-messages-checked]}
[{:keys [db] :as cofx} chat-id]
(when (:might-have-join-time-messages? (get-chat cofx chat-id))
{:db (dissoc-join-time-fields db chat-id)}))
(fx/defn join-time-messages-checked-for-chats
[{:keys [db]} chat-ids]
{:db (reduce #(if (:might-have-join-time-messages? (get-chat {:db %1} %2))
(dissoc-join-time-fields %1 %2)
%1)
db
chat-ids)})
(defn- create-new-chat (defn- create-new-chat
[chat-id {:keys [db now]}] [chat-id {:keys [db now]}]
(let [name (get-in db [:contacts/contacts chat-id :name])] (let [name (get-in db [:contacts/contacts chat-id :name])]
@ -132,10 +101,7 @@
chat-props) chat-props)
new? (not (get-in db [:chats chat-id])) new? (not (get-in db [:chats chat-id]))
public? (public-chat? chat)] public? (public-chat? chat)]
(fx/merge cofx {:db (update-in db [:chats chat-id] merge chat)}))
{:db (update-in db [:chats chat-id] merge chat)}
(when (and public? new? (not timeline?))
(transport.filters/load-chat chat-id)))))
(defn map-chats [{:keys [db] :as cofx}] (defn map-chats [{:keys [db] :as cofx}]
(fn [val] (fn [val]
@ -155,52 +121,14 @@
[{:keys [db] :as cofx} chats] [{:keys [db] :as cofx} chats]
(let [chats (map (map-chats cofx) chats) (let [chats (map (map-chats cofx) chats)
filtered-chats (filter (filter-chats db) chats)] filtered-chats (filter (filter-chats db) chats)]
(fx/merge cofx
{:db (update db :chats #(reduce {:db (update db :chats #(reduce
(fn [acc {:keys [chat-id] :as chat}] (fn [acc {:keys [chat-id] :as chat}]
(update acc chat-id merge chat)) (update acc chat-id merge chat))
% %
chats))} chats))}))
(transport.filters/load-chats filtered-chats))))
(fx/defn upsert-chat
"Upsert chat when not deleted"
[{:keys [db] :as cofx} {:keys [chat-id] :as chat-props} on-success]
(fx/merge cofx
(ensure-chat chat-props)
#(chats-store/save-chat % (get-in % [:db :chats chat-id]) on-success)))
(fx/defn handle-save-chat
{:events [::save-chat]}
[{:keys [db] :as cofx} chat-id on-success]
(chats-store/save-chat cofx (get-in db [:chats chat-id]) on-success))
(fx/defn add-public-chat
"Adds new public group chat to db"
[cofx topic profile-public-key timeline?]
(upsert-chat cofx
{:chat-id topic
:timeline? timeline?
:profile-public-key profile-public-key
:is-active true
:name topic
:chat-name (str "#" topic)
:group-chat true
:chat-type (cond timeline?
constants/timeline-chat-type
profile-public-key
constants/profile-chat-type
:else
constants/public-chat-type)
:contacts #{}
:public? true
:might-have-join-time-messages? (get-in cofx [:db :multiaccount :use-mailservers?])
:unviewed-messages-count 0}
nil))
(fx/defn clear-history (fx/defn clear-history
"Clears history of the particular chat" "Clears history of the particular chat"
{:events [:chat.ui/clear-history]}
[{:keys [db] :as cofx} chat-id remove-chat?] [{:keys [db] :as cofx} chat-id remove-chat?]
(let [{:keys [last-message public? (let [{:keys [last-message public?
deleted-at-clock-value]} (get-in db [:chats chat-id]) deleted-at-clock-value]} (get-in db [:chats chat-id])
@ -209,24 +137,39 @@
(or (:clock-value last-message) (or (:clock-value last-message)
deleted-at-clock-value deleted-at-clock-value
(utils.clocks/send 0)))] (utils.clocks/send 0)))]
(fx/merge
cofx
{:db (-> db {:db (-> db
(assoc-in [:messages chat-id] {}) (assoc-in [:messages chat-id] {})
(update-in [:message-lists] dissoc chat-id) (update-in [:message-lists] dissoc chat-id)
(update-in [:chats chat-id] merge (update-in [:chats chat-id] merge
{:last-message nil {:last-message nil
:unviewed-messages-count 0 :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]) nil)))) (fx/defn clear-history-handler
"Clears history of the particular chat"
{:events [:chat.ui/clear-history]}
[{:keys [db] :as cofx} chat-id remove-chat?]
(fx/merge cofx
{:db db
::json-rpc/call [{:method "wakuext_clearHistory"
:params [{:id chat-id}]
:on-success #(re-frame/dispatch [::history-cleared chat-id %])
:on-error #(log/error "failed to clear history " chat-id %)}]}
(clear-history chat-id remove-chat?)))
(fx/defn deactivate-chat (fx/defn deactivate-chat
"Deactivate chat in db, no side effects" "Deactivate chat in db, no side effects"
[{:keys [db now] :as cofx} chat-id] [{:keys [db now] :as cofx} chat-id]
(fx/merge
cofx
{:db (-> db {:db (-> db
(assoc-in [:chats chat-id :is-active] false) (assoc-in [:chats chat-id :is-active] false)
(assoc-in [:current-chat-id] nil))}) (assoc-in [:current-chat-id] nil))
::json-rpc/call [{:method "wakuext_deactivateChat"
:params [{:id chat-id}]
:on-success #(log/debug "chat deactivated" chat-id)
:on-error #(log/error "failed to create public chat" chat-id %)}]}
(clear-history chat-id true)))
(fx/defn offload-messages (fx/defn offload-messages
{:events [:offload-messages]} {:events [:offload-messages]}
@ -250,12 +193,8 @@
{:events [:chat.ui/remove-chat]} {:events [:chat.ui/remove-chat]}
[{:keys [db now] :as cofx} chat-id] [{:keys [db now] :as cofx} chat-id]
(fx/merge cofx (fx/merge cofx
(mailserver/remove-gaps chat-id)
(mailserver/remove-range chat-id)
(deactivate-chat chat-id) (deactivate-chat chat-id)
(offload-messages chat-id) (offload-messages chat-id)
(clear-history chat-id true)
(transport.filters/stop-listening chat-id)
(when (not (= (:view-id db) :home)) (when (not (= (:view-id db) :home))
(navigation/navigate-to-cofx :home {})))) (navigation/navigate-to-cofx :home {}))))
@ -263,10 +202,7 @@
"Takes chat-id and coeffects map, returns effects necessary when navigating to chat" "Takes chat-id and coeffects map, returns effects necessary when navigating to chat"
{:events [:chat.ui/preload-chat-data]} {:events [:chat.ui/preload-chat-data]}
[{:keys [db] :as cofx} chat-id] [{:keys [db] :as cofx} chat-id]
(fx/merge cofx (loading/load-messages cofx chat-id))
(when-not (or (group-chat? cofx chat-id) (timeline-chat? cofx chat-id))
(transport.filters/load-chat chat-id))
(loading/load-messages chat-id)))
(fx/defn navigate-to-chat (fx/defn navigate-to-chat
"Takes coeffects map and chat-id, returns effects necessary for navigation and preloading data" "Takes coeffects map and chat-id, returns effects necessary for navigation and preloading data"
@ -279,18 +215,29 @@
(preload-chat-data chat-id) (preload-chat-data chat-id)
(navigation/navigate-to-cofx :chat-stack {:screen :chat}))) (navigation/navigate-to-cofx :chat-stack {:screen :chat})))
(fx/defn handle-clear-history-response
{:events [::history-cleared]}
[{:keys [db] :as cofx} chat-id response]
(let [chat (chats-store/<-rpc (first (:chats response)))]
{:db (assoc-in db [:chats chat-id] chat)}))
(fx/defn handle-one-to-one-chat-created
{:events [::one-to-one-chat-created]}
[{:keys [db] :as cofx} chat-id response]
(let [chat (chats-store/<-rpc (first (:chats response)))]
{:db (assoc-in db [:chats chat-id] chat)
:dispatch [:chat.ui/navigate-to-chat chat-id]}))
(fx/defn start-chat (fx/defn start-chat
"Start a chat, making sure it exists" "Start a chat, making sure it exists"
{:events [:chat.ui/start-chat]} {:events [:chat.ui/start-chat]}
[{:keys [db] :as cofx} chat-id] [{:keys [db] :as cofx} chat-id]
;; don't allow to open chat with yourself ;; don't allow to open chat with yourself
(when (not= (multiaccounts.model/current-public-key cofx) chat-id) (when (not= (multiaccounts.model/current-public-key cofx) chat-id)
(fx/merge cofx {::json-rpc/call [{:method "wakuext_createOneToOneChat"
{:dispatch [:chat.ui/navigate-to-chat chat-id]} :params [{:id chat-id}]
(upsert-chat {:chat-id chat-id :on-success #(re-frame/dispatch [::one-to-one-chat-created chat-id %])
:is-active true} :on-error #(log/error "failed to create one-to-on chat" chat-id %)}]}))
nil)
(transport.filters/load-chat chat-id))))
(defn profile-chat-topic [public-key] (defn profile-chat-topic [public-key]
(str "@" public-key)) (str "@" public-key))
@ -298,44 +245,60 @@
(defn my-profile-chat-topic [db] (defn my-profile-chat-topic [db]
(profile-chat-topic (get-in db [:multiaccount :public-key]))) (profile-chat-topic (get-in db [:multiaccount :public-key])))
(fx/defn handle-public-chat-created
{:events [::public-chat-created]}
[{:keys [db] :as cofx} chat-id {:keys [dont-navigate?]} response]
(let [chat (chats-store/<-rpc (first (:chats response)))
db-with-chat {:db (assoc-in db [:chats chat-id] chat)}]
(if dont-navigate?
db-with-chat
(assoc db-with-chat :dispatch [:chat.ui/navigate-to-chat chat-id]))))
(fx/defn create-public-chat-go [cofx chat-id opts]
{::json-rpc/call [{:method "wakuext_createPublicChat"
:params [{:id chat-id}]
:on-success #(re-frame/dispatch [::public-chat-created chat-id opts %])
:on-error #(log/error "failed to create public chat" chat-id %)}]})
(fx/defn start-public-chat (fx/defn start-public-chat
"Starts a new public chat" "Starts a new public chat"
{:events [:chat.ui/start-public-chat]} {:events [:chat.ui/start-public-chat]}
[cofx topic {:keys [dont-navigate? profile-public-key navigation-reset?]}] [cofx topic {:keys [dont-navigate? profile-public-key] :as opts}]
(if (or (new-public-chat.db/valid-topic? topic) profile-public-key) (if (or (new-public-chat.db/valid-topic? topic) profile-public-key)
(if (active-chat? cofx topic) (if (active-chat? cofx topic)
(when-not dont-navigate? (when-not dont-navigate?
(if navigation-reset? (navigate-to-chat cofx topic))
(fx/merge cofx (create-public-chat-go
{:dispatch [:chat.ui/navigate-to-chat topic]} cofx
(navigation/navigate-to-cofx :home {})) topic
(navigate-to-chat cofx topic))) opts))
(fx/merge cofx
(add-public-chat topic profile-public-key false)
(transport.filters/load-chat topic)
#(when navigation-reset?
(navigation/navigate-to-cofx % :home {}))
#(when-not dont-navigate?
{:dispatch [:chat.ui/navigate-to-chat topic]})))
{:utils/show-popup {:title (i18n/label :t/cant-open-public-chat) {:utils/show-popup {:title (i18n/label :t/cant-open-public-chat)
:content (i18n/label :t/invalid-public-chat-topic)}})) :content (i18n/label :t/invalid-public-chat-topic)}}))
(fx/defn profile-chat-created
{:events [::profile-chat-created]}
[{:keys [db] :as cofx} chat-id response navigate-to?]
(fx/merge
cofx
{:db db}
#(when response
(let [chat (chats-store/<-rpc (first (:chats response)))]
{:db (assoc-in db [:chats chat-id] chat)}))
#(when navigate-to?
{:dispatch-n [[:chat.ui/preload-chat-data chat-id]
[:navigate-to :profile nil]]})))
(fx/defn start-profile-chat (fx/defn start-profile-chat
"Starts a new profile chat" "Starts a new profile chat"
{:events [:start-profile-chat]} {:events [:start-profile-chat]}
[cofx profile-public-key] [cofx profile-public-key navigate-to?]
(let [topic (profile-chat-topic profile-public-key)] (let [chat-id (profile-chat-topic profile-public-key)]
(when-not (active-chat? cofx topic) (if (active-chat? cofx chat-id)
(fx/merge cofx {:dispatch [::profile-chat-created chat-id nil navigate-to?]}
(add-public-chat topic profile-public-key false) {::json-rpc/call [{:method "wakuext_createProfileChat"
(transport.filters/load-chat topic))))) :params [{:id profile-public-key}]
:on-success #(re-frame/dispatch [::profile-chat-created chat-id % navigate-to?])
(fx/defn start-timeline-chat :on-error #(log/error "failed to create profile chat" chat-id %)}]})))
"Starts a new timeline chat"
{:events [:chat/start-timeline-chat]}
[cofx]
(when-not (active-chat? cofx constants/timeline-chat-id)
(add-public-chat cofx constants/timeline-chat-id nil true)))
(fx/defn disable-chat-cooldown (fx/defn disable-chat-cooldown
"Turns off chat cooldown (protection against message spamming)" "Turns off chat cooldown (protection against message spamming)"
@ -357,22 +320,21 @@
(log/error "mute chat failed" chat-id error) (log/error "mute chat failed" chat-id error)
{:db (assoc-in db [:chats chat-id :muted] (not muted?))}) {:db (assoc-in db [:chats chat-id :muted] (not muted?))})
(fx/defn mute-chat-successful
{:events [::mute-chat-successful]}
[{:keys [db]} chat-id response]
(let [chat (chats-store/<-rpc (first (:chats response)))]
{:db (assoc-in db [:chats chat-id] chat)}))
(fx/defn mute-chat (fx/defn mute-chat
{:events [::mute-chat-toggled]} {:events [::mute-chat-toggled]}
[{:keys [db] :as cofx} chat-id muted?] [{:keys [db] :as cofx} chat-id muted?]
(let [method (if muted? "muteChat" "unmuteChat") (let [method (if muted? "muteChat" "unmuteChat")]
chat (get-in db [:chats chat-id])]
;; chat does not exist, create and then mute
(if-not chat
(upsert-chat cofx
{:is-active true
:chat-id chat-id}
#(re-frame/dispatch [::mute-chat-toggled chat-id muted?]))
{:db (assoc-in db [:chats chat-id :muted] muted?) {:db (assoc-in db [:chats chat-id :muted] muted?)
::json-rpc/call [{:method (json-rpc/call-ext-method method) ::json-rpc/call [{:method (json-rpc/call-ext-method method)
:params [chat-id] :params [chat-id]
:on-error #(re-frame/dispatch [::mute-chat-failed chat-id muted? %]) :on-error #(re-frame/dispatch [::mute-chat-failed chat-id muted? %])
:on-success #(log/info method "successful" chat-id)}]}))) :on-success #(re-frame/dispatch [::mute-chat-successful chat-id %])}]}))
(fx/defn show-profile (fx/defn show-profile
{:events [:chat.ui/show-profile]} {:events [:chat.ui/show-profile]}
@ -382,10 +344,8 @@
(navigation/navigate-to-cofx cofx :profile-stack {:screen :my-profile}) (navigation/navigate-to-cofx cofx :profile-stack {:screen :my-profile})
(fx/merge (fx/merge
cofx cofx
{:db (assoc db :contacts/identity identity) {:db (assoc db :contacts/identity identity)}
:dispatch [:chat.ui/preload-chat-data (profile-chat-topic identity)]} (start-profile-chat identity true)))))
(start-profile-chat identity)
(navigation/navigate-to-cofx :profile nil)))))
(fx/defn clear-history-pressed (fx/defn clear-history-pressed
{:events [:chat.ui/clear-history-pressed]} {:events [:chat.ui/clear-history-pressed]}
@ -398,35 +358,61 @@
(re-frame/dispatch [:bottom-sheet/hide]) (re-frame/dispatch [:bottom-sheet/hide])
(re-frame/dispatch [:chat.ui/clear-history chat-id false]))}}) (re-frame/dispatch [:chat.ui/clear-history chat-id false]))}})
(fx/defn gaps-failed
{:events [::gaps-failed]}
[{:keys [db]} chat-id gap-ids error]
(log/error "failed to fetch gaps" chat-id gap-ids error)
{:db (dissoc db :mailserver/fetching-gaps-in-progress)})
(fx/defn sync-chat-from-sync-from-failed
{:events [::sync-chat-from-sync-from-failed]}
[{:keys [db]} chat-id error]
(log/error "failed to sync chat" chat-id error)
{:db (dissoc db :mailserver/fetching-gaps-in-progress)})
(fx/defn sync-chat-from-sync-from-success
{:events [::sync-chat-from-sync-from-success]}
[{:keys [db] :as cofx} chat-id synced-from]
(log/debug "synced success" chat-id synced-from)
{:db
(-> db
(assoc-in [:chats chat-id :synced-from] synced-from)
(dissoc :mailserver/fetching-gaps-in-progress))})
(fx/defn gaps-filled
{:events [::gaps-filled]}
[{:keys [db] :as cofx} chat-id message-ids]
(fx/merge
cofx
{:db (-> db
(update-in [:messages chat-id] (fn [messages] (apply dissoc messages message-ids)))
(dissoc :mailserver/fetching-gaps-in-progress))}
(message-list/rebuild-message-list chat-id)))
(fx/defn fill-gaps
[cofx chat-id gap-ids]
{::json-rpc/call [{:method "wakuext_fillGaps"
:params [chat-id gap-ids]
:on-success #(re-frame/dispatch [::gaps-filled chat-id gap-ids %])
:on-error #(re-frame/dispatch [::gaps-failed chat-id gap-ids %])}]})
(fx/defn sync-chat-from-sync-from
[cofx chat-id]
(log/debug "syncing chat from sync from")
{::json-rpc/call [{:method "wakuext_syncChatFromSyncedFrom"
:params [chat-id]
:on-success #(re-frame/dispatch [::sync-chat-from-sync-from-success chat-id %])
:on-error #(re-frame/dispatch [::sync-chat-from-sync-from-failed chat-id %])}]})
(fx/defn chat-ui-fill-gaps (fx/defn chat-ui-fill-gaps
{:events [:chat.ui/fill-gaps]} {:events [:chat.ui/fill-gaps]}
[{:keys [db] :as cofx} gap-ids chat-id] [{:keys [db] :as cofx} chat-id gap-ids]
(let [topics (mailserver.topics/topics-for-chat db chat-id) (log/info "filling gaps" chat-id gap-ids)
gaps (keep (fx/merge cofx
(fn [id] {:db (assoc db :mailserver/fetching-gaps-in-progress gap-ids)}
(get-in db [:mailserver/gaps chat-id id])) (if (= gap-ids #{:first-gap})
gap-ids)] (sync-chat-from-sync-from chat-id)
(mailserver/fill-the-gap (fill-gaps chat-id gap-ids))))
cofx
{:gaps gaps
:topics topics
:chat-id chat-id})))
(fx/defn chat-ui-fetch-more
{:events [:chat.ui/fetch-more]}
[{:keys [db] :as cofx} chat-id]
(let [{:keys [lowest-request-from]}
(get-in db [:mailserver/ranges chat-id])
topics (mailserver.topics/topics-for-chat db chat-id)
gaps [{:id :first-gap
:to lowest-request-from
:from (- lowest-request-from mailserver.constants/one-day)}]]
(mailserver/fill-the-gap
cofx
{:gaps gaps
:topics topics
:chat-id chat-id})))
(fx/defn chat-ui-remove-chat-pressed (fx/defn chat-ui-remove-chat-pressed
{:events [:chat.ui/remove-chat-pressed]} {:events [:chat.ui/remove-chat-pressed]}

View File

@ -28,9 +28,7 @@
new-chats) new-chats)
chats (merge old-chats chats)] chats (merge old-chats chats)]
{:db (assoc db :chats chats {:db (assoc db :chats chats
:chats/loading? false) :chats/loading? false)}))
:dispatch-n [[:chat/start-timeline-chat]
[:start-profile-chat (get-in db [:multiaccount :public-key])]]}))
(fx/defn initialize-chats (fx/defn initialize-chats
"Initialize persisted chats on startup" "Initialize persisted chats on startup"
@ -115,8 +113,7 @@
(when (or first-request cursor) (when (or first-request cursor)
(merge (merge
{:db (assoc-in db [:pagination-info chat-id :loading-messages?] true)} {:db (assoc-in db [:pagination-info chat-id :loading-messages?] true)}
{:utils/dispatch-later [{:ms 100 :dispatch [:load-more-reactions cursor chat-id]} {:utils/dispatch-later [{:ms 100 :dispatch [:load-more-reactions cursor chat-id]}]}
{:ms 100 :dispatch [:load-gaps chat-id]}]}
(data-store.messages/messages-by-chat-id-rpc (data-store.messages/messages-by-chat-id-rpc
chat-id chat-id
cursor cursor

View File

@ -28,35 +28,11 @@
(update-in [:db :messages chat-id] assoc message-id message) (update-in [:db :messages chat-id] assoc message-id message)
(update-in [:db :message-lists chat-id] message-list/add message))) (update-in [:db :message-lists chat-id] message-list/add message)))
;;TODO this is too expensive, probably we could mark message somehow and just hide it in the UI
(fx/defn rebuild-message-list
[{:keys [db]} chat-id]
{:db (assoc-in db [:message-lists chat-id]
(message-list/add-many nil (vals (get-in db [:messages chat-id]))))})
(defn hide-message (defn hide-message
"Hide chat message, rebuild message-list" "Hide chat message, rebuild message-list"
[{:keys [db]} chat-id message-id] [{:keys [db]} chat-id message-id]
;;TODO this is too expensive, probably we could mark message somehow and just hide it in the UI ;;TODO this is too expensive, probably we could mark message somehow and just hide it in the UI
(rebuild-message-list {:db (update-in db [:messages chat-id] dissoc message-id)} chat-id)) (message-list/rebuild-message-list {:db (update-in db [:messages chat-id] dissoc message-id)} chat-id))
(fx/defn join-times-messages-checked
"The key :might-have-join-time-messages? in public chats signals that
the public chat is freshly (re)created and requests for messages to the
mailserver for the topic has not completed yet. Likewise, the key
:join-time-mail-request-id is associated a little bit after, to signal that
the request to mailserver was a success. When request is signalled complete
by mailserver, corresponding event :chat.ui/join-times-messages-checked
dissociates these two fileds via this function, thereby signalling that the
public chat is not fresh anymore."
{:events [:chat/join-times-messages-checked]}
[{:keys [db] :as cofx} chat-ids]
(reduce (fn [acc chat-id]
(cond-> acc
(:might-have-join-time-messages? (chat-model/get-chat cofx chat-id))
(update :db #(chat-model/dissoc-join-time-fields % chat-id))))
{:db db}
chat-ids))
(fx/defn add-senders-to-chat-users (fx/defn add-senders-to-chat-users
{:events [:chat/add-senders-to-chat-users]} {:events [:chat/add-senders-to-chat-users]}
@ -209,7 +185,7 @@
(fx/merge cofx (fx/merge cofx
{:db (update-in db [:messages chat-id] dissoc message-id)} {:db (update-in db [:messages chat-id] dissoc message-id)}
(data-store.messages/delete-message message-id) (data-store.messages/delete-message message-id)
(rebuild-message-list chat-id))) (message-list/rebuild-message-list chat-id)))
(fx/defn send-message (fx/defn send-message
[cofx message] [cofx message]

View File

@ -1,5 +1,6 @@
(ns status-im.chat.models.message-list (ns status-im.chat.models.message-list
(:require [status-im.constants :as constants] (:require [status-im.constants :as constants]
[status-im.utils.fx :as fx]
[status-im.utils.datetime :as time] [status-im.utils.datetime :as time]
["functional-red-black-tree" :as rb-tree])) ["functional-red-black-tree" :as rb-tree]))
@ -180,3 +181,9 @@
(if message-list (if message-list
(array-seq (.-values message-list)) (array-seq (.-values message-list))
[])) []))
;;TODO this is too expensive, probably we could mark message somehow and just hide it in the UI
(fx/defn rebuild-message-list
[{:keys [db]} chat-id]
{:db (assoc-in db [:message-lists chat-id]
(add-many nil (vals (get-in db [:messages chat-id]))))})

View File

@ -1,67 +1,8 @@
(ns status-im.chat.models-test (ns status-im.chat.models-test
(:require [cljs.test :refer-macros [deftest is testing]] (:require [cljs.test :refer-macros [deftest is testing]]
[status-im.utils.gfycat.core :as gfycat]
[status-im.utils.identicon :as identicon]
[status-im.ethereum.json-rpc :as json-rpc]
[status-im.utils.clocks :as utils.clocks] [status-im.utils.clocks :as utils.clocks]
[status-im.chat.models :as chat])) [status-im.chat.models :as chat]))
(deftest upsert-chat-test
(testing "upserting a non existing chat"
(let [chat-id "some-chat-id"
contact-name "contact-name"
chat-props {:chat-id chat-id
:extra-prop "some"}
cofx {:now "now"
:db {:contacts/contacts {chat-id
{:name contact-name}}}}
response (chat/upsert-chat cofx chat-props nil)
actual-chat (get-in response [:db :chats chat-id])]
(testing "it adds the chat to the chats collection"
(is actual-chat))
(testing "it adds the extra props"
(is (= "some" (:extra-prop actual-chat))))
(testing "it adds the chat id"
(is (= chat-id (:chat-id actual-chat))))
(testing "it pulls the name from the contacts"
(is (= contact-name (:name actual-chat))))
(testing "it sets the timestamp"
(is (= "now" (:timestamp actual-chat))))
(testing "it adds the contact-id to the contact field"
(is (= chat-id (-> actual-chat :contacts first))))))
(testing "upserting an existing chat"
(let [chat-id "some-chat-id"
chat-props {:chat-id chat-id
:name "new-name"
:extra-prop "some"}
cofx {:db {:chats {chat-id {:is-active true
:name "old-name"}}}}
response (chat/upsert-chat cofx chat-props nil)
actual-chat (get-in response [:db :chats chat-id])]
(testing "it adds the chat to the chats collection"
(is actual-chat))
(testing "it adds the extra props"
(is (= "some" (:extra-prop actual-chat))))
(testing "it updates existins props"
(is (= "new-name" (:name actual-chat)))))))
(deftest add-public-chat
(with-redefs [gfycat/generate-gfy (constantly "generated")
identicon/identicon (constantly "generated")]
(let [topic "topic"
fx (chat/add-public-chat {:db {}} topic nil nil)
chat (get-in fx [:db :chats topic])]
(testing "it sets the name"
(is (= topic (:name chat))))
(testing "it sets the participants"
(is (= #{} (:contacts chat))))
(testing "it sets the chat-id"
(is (= topic (:chat-id chat))))
(testing "it sets the group-chat flag"
(is (:group-chat chat)))
(testing "it does not sets the public flag"
(is (:public? chat))))))
(deftest clear-history-test (deftest clear-history-test
(let [chat-id "1" (let [chat-id "1"
cofx {:db {:message-lists {chat-id [{:something "a"}]} cofx {:db {:message-lists {chat-id [{:something "a"}]}
@ -96,11 +37,7 @@
:last-message nil) :last-message nil)
chat-id chat-id
true)] true)]
(is (= 42 (get-in actual [:db :chats chat-id :deleted-at-clock-value])))))) (is (= 42 (get-in actual [:db :chats chat-id :deleted-at-clock-value]))))))))
(testing "it adds the relevant rpc calls"
(let [actual (chat/clear-history cofx chat-id true)]
(is (::json-rpc/call actual))
(is (= 2 (count (::json-rpc/call actual))))))))
(deftest remove-chat-test (deftest remove-chat-test
(let [chat-id "1" (let [chat-id "1"
@ -110,17 +47,13 @@
:chats {chat-id {:last-message {:clock-value 10}}}}}] :chats {chat-id {:last-message {:clock-value 10}}}}}]
(testing "it deletes all the messages" (testing "it deletes all the messages"
(let [actual (chat/remove-chat cofx chat-id)] (let [actual (chat/remove-chat cofx chat-id)]
(is (= {} (get-in actual [:db :messages chat-id]))))) (is (= nil (get-in actual [:db :messages chat-id])))))
(testing "it sets a deleted-at-clock-value equal to the last message clock-value" (testing "it sets a deleted-at-clock-value equal to the last message clock-value"
(let [actual (chat/remove-chat cofx chat-id)] (let [actual (chat/remove-chat cofx chat-id)]
(is (= 10 (get-in actual [:db :chats chat-id :deleted-at-clock-value]))))) (is (= 10 (get-in actual [:db :chats chat-id :deleted-at-clock-value])))))
(testing "it sets the chat as inactive" (testing "it sets the chat as inactive"
(let [actual (chat/remove-chat cofx chat-id)] (let [actual (chat/remove-chat cofx chat-id)]
(is (= false (get-in actual [:db :chats chat-id :is-active]))))) (is (= false (get-in actual [:db :chats chat-id :is-active])))))))
(testing "it makes the relevant json-rpc calls"
(let [actual (chat/remove-chat cofx chat-id)]
(is (::json-rpc/call actual))
(is (= 4 (count (::json-rpc/call actual))))))))
(deftest multi-user-chat? (deftest multi-user-chat?
(let [chat-id "1"] (let [chat-id "1"]

View File

@ -12,6 +12,7 @@
(def ^:const content-type-image 7) (def ^:const content-type-image 7)
(def ^:const content-type-audio 8) (def ^:const content-type-audio 8)
(def ^:const content-type-community 9) (def ^:const content-type-community 9)
(def ^:const content-type-gap 10)
(def ^:const emoji-reaction-love 1) (def ^:const emoji-reaction-love 1)
(def ^:const emoji-reaction-thumbs-up 2) (def ^:const emoji-reaction-thumbs-up 2)
@ -47,6 +48,8 @@
(def ^:const message-type-public-group 2) (def ^:const message-type-public-group 2)
(def ^:const message-type-private-group 3) (def ^:const message-type-private-group 3)
(def ^:const message-type-private-group-system-message 4) (def ^:const message-type-private-group-system-message 4)
(def ^:const message-type-community-chat 5)
(def ^:const message-type-gap 6)
(def ^:const command-state-request-address-for-transaction 1) (def ^:const command-state-request-address-for-transaction 1)
(def ^:const command-state-request-address-for-transaction-declined 2) (def ^:const command-state-request-address-for-transaction-declined 2)

View File

@ -4,7 +4,6 @@
[status-im.data-store.contacts :as contacts-store] [status-im.data-store.contacts :as contacts-store]
[status-im.ethereum.json-rpc :as json-rpc] [status-im.ethereum.json-rpc :as json-rpc]
[status-im.mailserver.core :as mailserver] [status-im.mailserver.core :as mailserver]
[status-im.transport.filters.core :as transport.filters]
[status-im.navigation :as navigation] [status-im.navigation :as navigation]
[status-im.utils.fx :as fx] [status-im.utils.fx :as fx]
[taoensso.timbre :as log] [taoensso.timbre :as log]
@ -57,7 +56,6 @@
(fx/merge cofx (fx/merge cofx
{:db (-> db {:db (-> db
(update-in [:contacts/contacts public-key] merge contact))} (update-in [:contacts/contacts public-key] merge contact))}
(transport.filters/load-contact contact)
(fn [cf] (fn [cf]
(contacts-store/save-contact cf (contacts-store/save-contact cf
(get-in cf [:db :contacts/contacts public-key]) (get-in cf [:db :contacts/contacts public-key])
@ -98,10 +96,11 @@
(let [new-contact (update contact (let [new-contact (update contact
:system-tags :system-tags
(fnil #(disj % :contact/added) #{}))] (fnil #(disj % :contact/added) #{}))]
(fx/merge cofx
{:db (assoc-in db [:contacts/contacts public-key] new-contact) {:db (assoc-in db [:contacts/contacts public-key] new-contact)
:dispatch [:offload-messages constants/timeline-chat-id]} ::json-rpc/call [{:method "wakuext_removeContact"
(contacts-store/save-contact new-contact nil)))) :params [public-key]
:on-success #(log/debug "contact removed successfully")}]
:dispatch [:offload-messages constants/timeline-chat-id]}))
(fx/defn create-contact (fx/defn create-contact
"Create entry in contacts" "Create entry in contacts"
@ -137,8 +136,7 @@
(update-in [:contacts/contacts public-key] merge contact)) (update-in [:contacts/contacts public-key] merge contact))
::json-rpc/call [{:method "wakuext_ensVerified" ::json-rpc/call [{:method "wakuext_ensVerified"
:params [public-key ens-name] :params [public-key ens-name]
:on-success #(log/debug "ens name verified successuful")}]} :on-success #(log/debug "ens name verified successuful")}]})))
(transport.filters/load-contact contact))))
(fx/defn update-nickname (fx/defn update-nickname
{:events [:contacts/update-nickname]} {:events [:contacts/update-nickname]}

View File

@ -75,6 +75,8 @@
type->rpc type->rpc
(clojure.set/rename-keys {:chat-id :id (clojure.set/rename-keys {:chat-id :id
:membership-update-events :membershipUpdateEvents :membership-update-events :membershipUpdateEvents
:synced-from :syncedFrom
:synced-to :syncedTo
:unviewed-messages-count :unviewedMessagesCount :unviewed-messages-count :unviewedMessagesCount
:last-message :lastMessage :last-message :lastMessage
:community-id :communityId :community-id :communityId
@ -83,7 +85,7 @@
:last-clock-value :lastClockValue :last-clock-value :lastClockValue
:profile-public-key :profile}) :profile-public-key :profile})
(dissoc :public? :group-chat :messages (dissoc :public? :group-chat :messages
:might-have-join-time-messages? :chat-type :chat-type
:contacts :admins :members-joined))) :contacts :admins :members-joined)))
(defn <-rpc [chat] (defn <-rpc [chat]
@ -92,6 +94,8 @@
unmarshal-members unmarshal-members
(clojure.set/rename-keys {:id :chat-id (clojure.set/rename-keys {:id :chat-id
:communityId :community-id :communityId :community-id
:syncedFrom :synced-from
:syncedTo :synced-to
:membershipUpdateEvents :membership-update-events :membershipUpdateEvents :membership-update-events
:deletedAtClockValue :deleted-at-clock-value :deletedAtClockValue :deleted-at-clock-value
:chatType :chat-type :chatType :chat-type
@ -104,16 +108,8 @@
(update :last-message #(when % (messages/<-rpc %))) (update :last-message #(when % (messages/<-rpc %)))
(dissoc :members))) (dissoc :members)))
(fx/defn save-chat [cofx {:keys [chat-id] :as chat} on-success]
{::json-rpc/call [{:method (json-rpc/call-ext-method "saveChat")
:params [(->rpc chat)]
:on-success #(do
(log/debug "saved chat" chat-id "successfuly")
(when on-success (on-success)))
:on-failure #(log/error "failed to save chat" chat-id %)}]})
(fx/defn fetch-chats-rpc [cofx {:keys [on-success]}] (fx/defn fetch-chats-rpc [cofx {:keys [on-success]}]
{::json-rpc/call [{:method (json-rpc/call-ext-method "chats") {::json-rpc/call [{:method (json-rpc/call-ext-method "activeChats")
:params [] :params []
:on-success #(on-success (map <-rpc %)) :on-success #(on-success (map <-rpc %))
:on-failure #(log/error "failed to fetch chats" 0 -1 %)}]}) :on-failure #(log/error "failed to fetch chats" 0 -1 %)}]})

View File

@ -1,42 +0,0 @@
(ns status-im.data-store.mailservers
(:require [status-im.ethereum.json-rpc :as json-rpc]
[status-im.utils.fx :as fx]
[taoensso.timbre :as log]))
(defn mailserver-request-gaps->rpc
[{:keys [chat-id] :as gap}]
(-> gap
(assoc :chatId chat-id)
(dissoc :chat-id)))
(fx/defn load-gaps
[cofx chat-id success-fn]
{::json-rpc/call [{:method "mailservers_getMailserverRequestGaps"
:params [chat-id]
:on-success #(let [indexed-gaps (reduce (fn [acc {:keys [id] :as g}]
(assoc acc id g))
{}
%)]
(success-fn chat-id indexed-gaps))
:on-failure #(log/error "failed to fetch gaps" %)}]})
(fx/defn save-gaps
[cofx gaps]
{::json-rpc/call [{:method "mailservers_addMailserverRequestGaps"
:params [(map mailserver-request-gaps->rpc gaps)]
:on-success #(log/info "saved gaps successfully")
:on-failure #(log/error "failed to save gap" %)}]})
(fx/defn delete-gaps
[cofx ids]
{::json-rpc/call [{:method "mailservers_deleteMailserverRequestGaps"
:params [ids]
:on-success #(log/info "deleted gaps successfully")
:on-failure #(log/error "failed to delete gap" %)}]})
(fx/defn delete-gaps-by-chat-id
[cofx chat-id]
{::json-rpc/call [{:method "mailservers_deleteMailserverRequestGapsByChatID"
:params [chat-id]
:on-success #(log/info "deleted gaps successfully")
:on-failure #(log/error "failed to delete gap" %)}]})

View File

@ -19,6 +19,7 @@
(clojure.set/rename-keys {:id :message-id (clojure.set/rename-keys {:id :message-id
:whisperTimestamp :whisper-timestamp :whisperTimestamp :whisper-timestamp
:commandParameters :command-parameters :commandParameters :command-parameters
:gapParameters :gap-parameters
:messageType :message-type :messageType :message-type
:localChatId :chat-id :localChatId :chat-id
:communityId :community-id :communityId :community-id

View File

@ -36,6 +36,12 @@
"waku_getSymKey" {} "waku_getSymKey" {}
"waku_markTrustedPeer" {} "waku_markTrustedPeer" {}
"wakuext_post" {} "wakuext_post" {}
"wakuext_requestAllHistoricMessages" {}
"wakuext_fillGaps" {}
"wakuext_syncChatFromSyncedFrom" {}
"wakuext_createPublicChat" {}
"wakuext_createOneToOneChat" {}
"wakuext_createProfileChat" {}
"wakuext_startMessenger" {} "wakuext_startMessenger" {}
"wakuext_sendPairInstallation" {} "wakuext_sendPairInstallation" {}
"wakuext_syncDevices" {} "wakuext_syncDevices" {}
@ -63,6 +69,7 @@
"wakuext_sendContactUpdate" {} "wakuext_sendContactUpdate" {}
"wakuext_sendContactUpdates" {} "wakuext_sendContactUpdates" {}
"wakuext_chats" {} "wakuext_chats" {}
"wakuext_activeChats" {}
"wakuext_addSystemMessages" {} "wakuext_addSystemMessages" {}
"wakuext_deleteMessagesFrom" {} "wakuext_deleteMessagesFrom" {}
"wakuext_deleteMessagesByChatID" {} "wakuext_deleteMessagesByChatID" {}
@ -76,6 +83,8 @@
"wakuext_muteChat" {} "wakuext_muteChat" {}
"wakuext_unmuteChat" {} "wakuext_unmuteChat" {}
"wakuext_contacts" {} "wakuext_contacts" {}
"wakuext_removeContact" {}
"wakuext_clearHistory" {}
"wakuext_prepareContent" {} "wakuext_prepareContent" {}
"wakuext_blockContact" {} "wakuext_blockContact" {}
"wakuext_updateMailservers" {} "wakuext_updateMailservers" {}
@ -85,6 +94,7 @@
"wakuext_getLinkPreviewWhitelist" {} "wakuext_getLinkPreviewWhitelist" {}
"wakuext_getLinkPreviewData" {} "wakuext_getLinkPreviewData" {}
"wakuext_requestCommunityInfoFromMailserver" {} "wakuext_requestCommunityInfoFromMailserver" {}
"wakuext_deactivateChat" {}
;;TODO not used anywhere? ;;TODO not used anywhere?
"wakuext_deleteChat" {} "wakuext_deleteChat" {}
"wakuext_saveContact" {} "wakuext_saveContact" {}

View File

@ -33,7 +33,6 @@
status-im.profile.core status-im.profile.core
status-im.chat.models.images status-im.chat.models.images
status-im.ui.screens.privacy-and-security-settings.events status-im.ui.screens.privacy-and-security-settings.events
status-im.mailserver.topics
status-im.multiaccounts.login.core status-im.multiaccounts.login.core
status-im.multiaccounts.logout.core status-im.multiaccounts.logout.core
status-im.multiaccounts.update.core status-im.multiaccounts.update.core

View File

@ -5,9 +5,6 @@
[re-frame.core :as re-frame] [re-frame.core :as re-frame]
[status-im.chat.models :as models.chat] [status-im.chat.models :as models.chat]
[status-im.ethereum.json-rpc :as json-rpc] [status-im.ethereum.json-rpc :as json-rpc]
[status-im.group-chats.db :as group-chats.db]
[status-im.multiaccounts.model :as multiaccounts.model]
[status-im.transport.filters.core :as transport.filters]
[status-im.navigation :as navigation] [status-im.navigation :as navigation]
[status-im.utils.fx :as fx] [status-im.utils.fx :as fx]
[status-im.constants :as constants] [status-im.constants :as constants]
@ -17,8 +14,13 @@
{:events [:navigate-chat-updated]} {:events [:navigate-chat-updated]}
[cofx chat-id] [cofx chat-id]
(if (get-in cofx [:db :chats chat-id :is-active]) (if (get-in cofx [:db :chats chat-id :is-active])
(models.chat/navigate-to-chat cofx chat-id) (models.chat/navigate-to-chat cofx chat-id)))
(navigation/navigate-to-cofx cofx :home {})))
(fx/defn handle-chat-removed
{:events [:chat-removed]}
[_ response]
{:dispatch-n [[:sanitize-messages-and-process-response response]
[:navigate-to :home]]})
(fx/defn handle-chat-update (fx/defn handle-chat-update
{:events [:chat-updated]} {:events [:chat-updated]}
@ -35,21 +37,6 @@
:js-response true :js-response true
:on-success #(re-frame/dispatch [:chat-updated %])}]}) :on-success #(re-frame/dispatch [:chat-updated %])}]})
(fx/defn set-up-filter
"Listen/Tear down the shared topic/contact-codes. Stop listening for members who
have left the chat"
[cofx chat-id previous-chat]
(let [my-public-key (multiaccounts.model/current-public-key cofx)
new-chat (get-in cofx [:db :chats chat-id])
members (:members-joined new-chat)]
;; If we left the chat do nothing
(when-not (and (group-chats.db/joined? my-public-key previous-chat)
(not (group-chats.db/joined? my-public-key new-chat)))
(fx/merge
cofx
(transport.filters/upsert-group-chat-topics)
(transport.filters/load-members members)))))
(fx/defn join-chat (fx/defn join-chat
{:events [:group-chats.ui/join-pressed]} {:events [:group-chats.ui/join-pressed]}
[_ chat-id] [_ chat-id]
@ -111,7 +98,7 @@
{::json-rpc/call [{:method (json-rpc/call-ext-method "leaveGroupChat") {::json-rpc/call [{:method (json-rpc/call-ext-method "leaveGroupChat")
:params [nil chat-id true] :params [nil chat-id true]
:js-response true :js-response true
:on-success #(re-frame/dispatch [:chat-updated %])}]}) :on-success #(re-frame/dispatch [:chat-removed %])}]})
(fx/defn remove (fx/defn remove
"Remove chat" "Remove chat"
@ -119,9 +106,6 @@
[cofx chat-id] [cofx chat-id]
(fx/merge cofx (fx/merge cofx
(models.chat/deactivate-chat chat-id) (models.chat/deactivate-chat chat-id)
(models.chat/upsert-chat {:chat-id chat-id
:is-active false}
nil)
(navigation/navigate-to-cofx :home {}))) (navigation/navigate-to-cofx :home {})))
(def not-blank? (def not-blank?
@ -231,4 +215,3 @@
:on-accept #(do :on-accept #(do
(re-frame/dispatch [:bottom-sheet/hide]) (re-frame/dispatch [:bottom-sheet/hide])
(re-frame/dispatch [:group-chats.ui/leave-chat-confirmed chat-id]))}}) (re-frame/dispatch [:group-chats.ui/leave-chat-confirmed chat-id]))}})

View File

@ -1,13 +1,10 @@
(ns ^{:doc "Mailserver events and API"} (ns ^{:doc "Mailserver events and API"}
status-im.mailserver.core status-im.mailserver.core
(:require [clojure.string :as string] (:require [clojure.string :as string]
[clojure.set :as clojure.set]
[re-frame.core :as re-frame] [re-frame.core :as re-frame]
[status-im.data-store.mailservers :as data-store.mailservers]
[status-im.ethereum.json-rpc :as json-rpc] [status-im.ethereum.json-rpc :as json-rpc]
[status-im.i18n.i18n :as i18n] [status-im.i18n.i18n :as i18n]
[status-im.mailserver.constants :as constants] [status-im.mailserver.constants :as constants]
[status-im.multiaccounts.model :as multiaccounts.model]
[status-im.multiaccounts.update.core :as multiaccounts.update] [status-im.multiaccounts.update.core :as multiaccounts.update]
[status-im.native-module.core :as status] [status-im.native-module.core :as status]
[status-im.node.core :as node] [status-im.node.core :as node]
@ -16,9 +13,6 @@
[status-im.utils.config :as config] [status-im.utils.config :as config]
[status-im.utils.fx :as fx] [status-im.utils.fx :as fx]
[status-im.utils.handlers :as handlers] [status-im.utils.handlers :as handlers]
[status-im.utils.random :as rand]
[status-im.utils.utils :as utils]
[status-im.mailserver.topics :as topics]
[taoensso.timbre :as log])) [taoensso.timbre :as log]))
;; How do mailserver work ? ;; How do mailserver work ?
@ -34,9 +28,6 @@
;; as soon as the mailserver becomes available ;; as soon as the mailserver becomes available
(def limit (atom constants/default-limit))
(def success-counter (atom 0))
(defn connected? [db id] (defn connected? [db id]
(= (:mailserver/current-id db) id)) (= (:mailserver/current-id db) id))
@ -77,19 +68,16 @@
(fetch db preference)) (fetch db preference))
preference))) preference)))
(defn add-peer! [enode]
(status/add-peer enode
(handlers/response-handler
#(log/debug "mailserver: add-peer success" %)
#(log/error "mailserver: add-peer error" %))))
;; We now wait for a confirmation from the mailserver before marking the message ;; We now wait for a confirmation from the mailserver before marking the message
;; as sent. ;; as sent.
(defn update-mailservers! [enodes] (defn update-mailservers! [enodes on-success]
(json-rpc/call (json-rpc/call
{:method (json-rpc/call-ext-method "updateMailservers") {:method (json-rpc/call-ext-method "updateMailservers")
:params [enodes] :params [enodes]
:on-success #(log/debug "mailserver: update-mailservers success" %) :on-success #(do
(log/debug "mailserver: update-mailservers success" %)
(when on-success
(on-success)))
:on-error #(log/error "mailserver: update-mailservers error" %)})) :on-error #(log/error "mailserver: update-mailservers error" %)}))
(defn remove-peer! [enode] (defn remove-peer! [enode]
@ -104,11 +92,6 @@
#(log/info "mailserver: remove-peer success" %) #(log/info "mailserver: remove-peer success" %)
#(log/error "mailserver: remove-peer error" %)))))) #(log/error "mailserver: remove-peer error" %))))))
(re-frame/reg-fx
:mailserver/add-peer
(fn [enode]
(add-peer! enode)))
(re-frame/reg-fx (re-frame/reg-fx
:mailserver/remove-peer :mailserver/remove-peer
(fn [enode] (fn [enode]
@ -116,61 +99,8 @@
(re-frame/reg-fx (re-frame/reg-fx
:mailserver/update-mailservers :mailserver/update-mailservers
(fn [enodes] (fn [[enodes on-success]]
(update-mailservers! enodes))) (update-mailservers! enodes on-success)))
(defn decrease-limit []
(max constants/min-limit (/ @limit 2)))
(defn increase-limit []
(min constants/max-limit (* @limit 2)))
(re-frame/reg-fx
:mailserver/set-limit
(fn [n]
(reset! limit n)))
(re-frame/reg-fx
:mailserver/increase-limit
(fn []
(if (>= @success-counter 2)
(reset! limit (increase-limit))
(swap! success-counter inc))))
(re-frame/reg-fx
:mailserver/decrease-limit
(fn []
(reset! limit (decrease-limit))
(reset! success-counter 0)))
(defn mark-trusted-peer! [enode]
(json-rpc/call
{:method "waku_markTrustedPeer"
:params [enode]
:on-success
#(re-frame/dispatch [:mailserver.callback/mark-trusted-peer-success %])
:on-error
#(re-frame/dispatch [:mailserver.callback/mark-trusted-peer-error %])}))
(re-frame/reg-fx
:mailserver/mark-trusted-peer
(fn [enode]
(mark-trusted-peer! enode)))
(fx/defn generate-mailserver-symkey
[{:keys [db] :as cofx} {:keys [password id] :as mailserver}]
(let [current-fleet (node/current-fleet-key db)]
{:db (assoc-in db [:mailserver/mailservers current-fleet id
:generating-sym-key?]
true)
:shh/generate-sym-key-from-password
{:password password
:on-success
(fn [_ sym-key-id]
(re-frame/dispatch
[:mailserver.callback/generate-mailserver-symkey-success
mailserver sym-key-id]))
:on-error #(log/error "mailserver: get-sym-key error" %)}}))
(defn registered-peer? (defn registered-peer?
"truthy if the enode is a registered peer" "truthy if the enode is a registered peer"
@ -181,80 +111,28 @@
(defn update-mailserver-state [db state] (defn update-mailserver-state [db state]
(assoc db :mailserver/state state)) (assoc db :mailserver/state state))
(fx/defn mark-trusted-peer
[{:keys [db] :as cofx}]
(let [{:keys [address sym-key-id generating-sym-key?] :as mailserver}
(fetch-current db)]
(fx/merge cofx
{:db (update-mailserver-state db :added)
:mailserver/mark-trusted-peer address}
(when-not (or sym-key-id generating-sym-key?)
(generate-mailserver-symkey mailserver)))))
(fx/defn add-peer (fx/defn add-peer
[{:keys [db now] :as cofx}] [{:keys [db now] :as cofx}]
(let [{:keys [address sym-key-id generating-sym-key?] :as mailserver} (let [{:keys [address] :as mailserver}
(fetch-current db)] (fetch-current db)]
(fx/merge
cofx
{:db (assoc {:db (assoc
(update-mailserver-state db :connecting) (update-mailserver-state db :connecting)
:mailserver/last-connection-attempt now) :mailserver/last-connection-attempt now)
:mailserver/add-peer address
;; Any message sent before this takes effect will not be marked as sent ;; Any message sent before this takes effect will not be marked as sent
;; probably we should improve the UX so that is more transparent to the ;; probably we should improve the UX so that is more transparent to the
;; user ;; user
:mailserver/update-mailservers [address]} :mailserver/update-mailservers [[address]]}))
(when-not (or sym-key-id generating-sym-key?)
(generate-mailserver-symkey mailserver)))))
(defn executing-gap-request?
[{:mailserver/keys [current-request fetching-gaps-in-progress]}]
(= (get fetching-gaps-in-progress (:chat-id current-request))
(select-keys
current-request
[:from :to :force-to? :topics :chat-id])))
(fx/defn disconnect-from-mailserver (fx/defn disconnect-from-mailserver
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(let [{:keys [address]} (fetch-current db) (let [{:keys [address]} (fetch-current db)
{:keys [peers-summary]} db {:keys [peers-summary]} db]
gap-request? (executing-gap-request? db)] {:db (dissoc db :mailserver/current-request :mailserver/fetching-gaps-in-progress)
{:db (cond-> (dissoc db :mailserver/current-request)
gap-request?
(-> (assoc :mailserver/fetching-gaps-in-progress {})
(dissoc :mailserver/planned-gap-requests)))
:mailserver/remove-peer address})) :mailserver/remove-peer address}))
(defn fetch-use-mailservers? [{:keys [db]}] (defn fetch-use-mailservers? [{:keys [db]}]
(get-in db [:multiaccount :use-mailservers?])) (get-in db [:multiaccount :use-mailservers?]))
(fx/defn connect-to-mailserver
"Add mailserver as a peer using `::add-peer` cofx and generate sym-key when
it doesn't exists
Peer summary will change and we will receive a signal from status go when
this is successful
A connection-check is made after `connection timeout` is reached and
mailserver-state is changed to error if it is not connected by then
No attempt is made if mailserver usage is disabled"
{:events [:mailserver.ui/reconnect-mailserver-pressed]}
[{:keys [db] :as cofx}]
(let [{:keys [address]} (fetch-current db)
{:keys [peers-summary]} db
use-mailservers? (fetch-use-mailservers? cofx)
added? (registered-peer? peers-summary address)
gap-request? (executing-gap-request? db)]
(when use-mailservers?
(fx/merge cofx
{:db (cond-> (dissoc db :mailserver/current-request)
gap-request?
(-> (assoc :mailserver/fetching-gaps-in-progress {})
(dissoc :mailserver/planned-gap-requests)))}
(if added?
(mark-trusted-peer)
(add-peer))))))
(defn pool-size [fleet-size] (defn pool-size [fleet-size]
(.ceil js/Math (/ fleet-size 4))) (.ceil js/Math (/ fleet-size 4)))
@ -296,6 +174,66 @@
has-b-failed? -1)))] has-b-failed? -1)))]
(sort sort-fn mailservers))) (sort sort-fn mailservers)))
(defn get-mailserver-when-ready
"return the mailserver if the mailserver is ready"
[{:keys [db]}]
(let [mailserver (fetch-current db)
mailserver-state (:mailserver/state db)]
(when (= :connected mailserver-state)
mailserver)))
(fx/defn handle-successful-request
{:events [::request-success]}
[{:keys [db]} response-js]
{:db (dissoc db :mailserver/current-request)})
(fx/defn process-next-messages-request
{:events [::request-messages]}
[{:keys [db now] :as cofx}]
(when (and
(:messenger/started? db)
(mobile-network-utils/syncing-allowed? cofx)
(fetch-use-mailservers? cofx)
(not (:mailserver/current-request db)))
(when-let [mailserver (get-mailserver-when-ready cofx)]
{:db (assoc db :mailserver/current-request true)
::json-rpc/call [{:method "wakuext_requestAllHistoricMessages"
:params []
:js-response true
:on-success #(do
(log/info "fetched historical messages")
(re-frame/dispatch [::request-success %]))
:on-failure #(log/error "failed retrieve historical messages" %)}]})))
(fx/defn connected-to-mailserver
[{:keys [db] :as cofx}]
(let [{:keys [address]} (fetch-current db)]
(fx/merge
cofx
{:db (update-mailserver-state db :connected)
:mailserver/update-mailservers [[address] #(re-frame/dispatch [::request-messages])]})))
(fx/defn connect-to-mailserver
"Add mailserver as a peer using `::add-peer` cofx and generate sym-key when
it doesn't exists
Peer summary will change and we will receive a signal from status go when
this is successful
A connection-check is made after `connection timeout` is reached and
mailserver-state is changed to error if it is not connected by then
No attempt is made if mailserver usage is disabled"
{:events [:mailserver.ui/reconnect-mailserver-pressed]}
[{:keys [db] :as cofx}]
(let [{:keys [address]} (fetch-current db)
{:keys [peers-summary]} db
use-mailservers? (fetch-use-mailservers? cofx)
added? (registered-peer? peers-summary address)]
(when use-mailservers?
(fx/merge cofx
{:db (dissoc db :mailserver/current-request)}
(if added?
(connected-to-mailserver)
(add-peer))))))
(fx/defn set-current-mailserver-with-lowest-latency (fx/defn set-current-mailserver-with-lowest-latency
"Picks a random mailserver amongs the ones with the lowest latency "Picks a random mailserver amongs the ones with the lowest latency
The results with error are ignored The results with error are ignored
@ -329,7 +267,7 @@
(when (:multiaccount db) (when (:multiaccount db)
(if (:mailserver/current-id db) (if (:mailserver/current-id db)
(let [{:keys [peers-summary peers-count]} db (let [{:keys [peers-summary peers-count]} db
{:keys [address sym-key-id] :as mailserver} (fetch-current db) {:keys [address] :as mailserver} (fetch-current db)
mailserver-was-registered? (registered-peer? previous-summary mailserver-was-registered? (registered-peer? previous-summary
address) address)
mailserver-is-registered? (registered-peer? peers-summary mailserver-is-registered? (registered-peer? peers-summary
@ -340,210 +278,19 @@
(not mailserver-is-registered?))] (not mailserver-is-registered?))]
(cond (cond
mailserver-added? mailserver-added?
(mark-trusted-peer cofx) (connected-to-mailserver cofx)
mailserver-removed? mailserver-removed?
(connect-to-mailserver cofx))) (connect-to-mailserver cofx)))
;; if there is no current mailserver defined ;; if there is no current mailserver defined
;; we set it first ;; we set it first
(set-current-mailserver cofx)))) (set-current-mailserver cofx))))
(defn adjust-request-for-transit-time
[from]
(let [ttl (:ttl whisper-opts)
whisper-tolerance (:whisper-drift-tolerance whisper-opts)
adjustment (+ whisper-tolerance ttl)
adjusted-from (- (max from adjustment) adjustment)]
(log/debug "Adjusting mailserver request" "from:" from
"adjusted-from:" adjusted-from)
adjusted-from))
(defn chats->never-synced-public-chats [chats]
(into {} (filter (fn [[_ v]] (:might-have-join-time-messages? v)) chats)))
(defn- assoc-topic-chat [chat chats topic]
(assoc chats topic chat))
(defn- reduce-assoc-topic-chat [db chats-map chat-id chat]
(let [assoc-topic-chat (partial assoc-topic-chat chat)
topics (topics/topics-for-chat db chat-id)]
(reduce assoc-topic-chat chats-map topics)))
(fx/defn handle-request-success (fx/defn handle-request-success
{:events [:mailserver.callback/request-success]} {:events [:mailserver.callback/request-success]}
[{{:keys [chats] :as db} :db} {:keys [request-id topics]}] [{{:keys [chats] :as db} :db} {:keys [request-id topics]}]
(when (:mailserver/current-request db) (when (:mailserver/current-request db)
(let [by-topic-never-synced-chats
(reduce-kv
(partial reduce-assoc-topic-chat db)
{}
(chats->never-synced-public-chats chats))
never-synced-chats-in-this-request
(select-keys by-topic-never-synced-chats (vec topics))]
(if (seq never-synced-chats-in-this-request)
{:db
(-> db
((fn [db]
(reduce
(fn [db chat]
(assoc-in db [:chats (:chat-id chat)
:join-time-mail-request-id] request-id))
db
(vals never-synced-chats-in-this-request))))
(assoc-in [:mailserver/current-request :request-id]
request-id))}
{:db (assoc-in db [:mailserver/current-request :request-id] {:db (assoc-in db [:mailserver/current-request :request-id]
request-id)})))) request-id)}))
(defn request-messages!
[{:keys [sym-key-id address]}
{:keys [topics cursor to from force-to?] :as request}]
;; Add some room to from, unless we break day boundaries so that
;; messages that have been received after the last request are also fetched
(let [actual-from (adjust-request-for-transit-time from)
actual-limit (or (:limit request)
@limit)]
(log/info "mailserver: request-messages for: "
" topics " topics
" from " actual-from
" force-to? " force-to?
" to " to
" range " (- to from)
" cursor " cursor
" limit " actual-limit)
(json-rpc/call
{:method (json-rpc/call-ext-method "requestMessages")
:params [(cond-> {:topics topics
:mailServerPeer address
:symKeyID sym-key-id
:timeout constants/request-timeout
:limit actual-limit
:cursor cursor
:from actual-from}
force-to?
(assoc :to to))]
:on-success (fn [request-id]
(log/info "mailserver: messages request success for topic "
topics "from" from "to" to)
(re-frame/dispatch
[:mailserver.callback/request-success
{:request-id request-id :topics topics}]))
:on-error (fn [error]
(log/error "mailserver: messages request error for topic "
topics ": " error)
(utils/set-timeout
#(re-frame/dispatch
[:mailserver.callback/resend-request {:request-id nil}])
constants/backoff-interval-ms))})))
(re-frame/reg-fx
:mailserver/request-messages
(fn [{:keys [mailserver request]}]
(request-messages! mailserver request)))
(defn get-mailserver-when-ready
"return the mailserver if the mailserver is ready"
[{:keys [db]}]
(let [{:keys [sym-key-id] :as mailserver} (fetch-current db)
mailserver-state (:mailserver/state db)]
(when (and (= :connected mailserver-state)
sym-key-id)
mailserver)))
(defn topic->request
[default-request-to requests-from requests-to]
(fn [[topic {:keys [last-request]}]]
(let [force-request-from (get requests-from topic)
force-request-to (get requests-to topic)]
(when (or force-request-from
(> default-request-to last-request))
(let [from (or force-request-from
(max last-request
(- default-request-to constants/max-request-range)))
to (or force-request-to default-request-to)]
{:gap-topics #{topic}
:from from
:to to
:force-to? (not (nil? force-request-to))})))))
(defn aggregate-requests
[acc {:keys [gap-topics from to force-to? gap chat-id]}]
(when from
(update acc [from to force-to?]
(fn [{:keys [topics]}]
{:topics ((fnil clojure.set/union #{}) topics gap-topics)
:from from
:to to
;; To is sent to the mailserver only when force-to? is true,
;; also we use to calculate when the last-request was sent.
:force-to? force-to?
:gap gap
:chat-id chat-id}))))
(defn prepare-messages-requests
[{{:keys [:mailserver/requests-from
:mailserver/requests-to
:mailserver/topics
:mailserver/planned-gap-requests]} :db}
default-request-to]
(transduce
(keep (topic->request default-request-to requests-from requests-to))
(completing aggregate-requests vals)
(reduce
aggregate-requests
{}
(vals planned-gap-requests))
topics))
(fx/defn process-next-messages-request
[{:keys [db now] :as cofx}]
(when (and
(:messenger/started? db)
(mobile-network-utils/syncing-allowed? cofx)
(fetch-use-mailservers? cofx)
(not (:mailserver/current-request db)))
(when-let [mailserver (get-mailserver-when-ready cofx)]
(let [request-to (or (:mailserver/request-to db)
(quot now 1000))
requests (prepare-messages-requests cofx request-to)]
(log/debug "Mailserver: planned requests " requests)
(if-let [request (first requests)]
{:db (assoc db
:mailserver/pending-requests (count requests)
:mailserver/current-request request
:mailserver/request-to request-to)
:mailserver/request-messages {:mailserver mailserver
:request request}}
{:db (dissoc db
:mailserver/pending-requests
:mailserver/request-to
:mailserver/requests-from
:mailserver/requests-to)})))))
(fx/defn add-mailserver-trusted
"the current mailserver has been trusted
update mailserver status to `:connected` and request messages
if mailserver is ready"
{:events [:mailserver.callback/mark-trusted-peer-success]}
[{:keys [db] :as cofx}]
(fx/merge cofx
{:db (update-mailserver-state db :connected)}
(process-next-messages-request)))
(fx/defn add-mailserver-sym-key
"the current mailserver sym-key has been generated
add sym-key to the mailserver in app-db and request messages if
mailserver is ready"
{:events [:mailserver.callback/generate-mailserver-symkey-success]}
[{:keys [db] :as cofx} {:keys [id]} sym-key-id]
(let [current-fleet (node/current-fleet-key db)]
(fx/merge
cofx
{:db (-> db
(assoc-in [:mailserver/mailservers current-fleet id :sym-key-id]
sym-key-id)
(update-in [:mailserver/mailservers current-fleet id]
dissoc :generating-sym-key?))}
(process-next-messages-request))))
(fx/defn update-use-mailservers (fx/defn update-use-mailservers
{:events [:mailserver.ui/use-history-switch-pressed]} {:events [:mailserver.ui/use-history-switch-pressed]}
@ -606,12 +353,12 @@
(fx/defn check-connection (fx/defn check-connection
"Check connection checks that the connection is successfully connected, "Check connection checks that the connection is successfully connected,
otherwise it will try to change mailserver and connect again" otherwise it will try to change mailserver and connect again"
{:events [:mailserver/check-connection-timeout :mailserver.callback/mark-trusted-peer-error]} {:events [:mailserver/check-connection-timeout]}
[{:keys [db now] :as cofx}] [{:keys [db now] :as cofx}]
;; check if logged into multiaccount ;; check if logged into multiaccount
(when (contains? db :multiaccount) (when (contains? db :multiaccount)
(let [last-connection-attempt (:mailserver/last-connection-attempt db)] (let [last-connection-attempt (:mailserver/last-connection-attempt db)]
(when (and (fetch-use-mailservers? cofx) (if (and (fetch-use-mailservers? cofx)
;; We are not connected ;; We are not connected
(not= :connected (:mailserver/state db)) (not= :connected (:mailserver/state db))
;; We either never tried to connect to this peer ;; We either never tried to connect to this peer
@ -619,282 +366,12 @@
;; Or 30 seconds have passed and no luck ;; Or 30 seconds have passed and no luck
(>= (- now last-connection-attempt) (* constants/connection-timeout 3)))) (>= (- now last-connection-attempt) (* constants/connection-timeout 3))))
;; Then we change mailserver ;; Then we change mailserver
(change-mailserver cofx))))) (change-mailserver cofx)
;; Just make sure it's set
(fx/defn reset-request-to (let [{:keys [address] :as mailserver}
[{:keys [db]}] (fetch-current db)]
{:db (dissoc db :mailserver/request-to)}) (when address
{:mailserver/update-mailservers [[address]]}))))))
(fx/defn remove-gaps
[{:keys [db] :as cofx} chat-id]
(fx/merge cofx
{:db (update db :mailserver/gaps dissoc chat-id)}
(data-store.mailservers/delete-gaps-by-chat-id chat-id)))
(fx/defn remove-range
[{:keys [db]} chat-id]
{:db (update db :mailserver/ranges dissoc chat-id)
::json-rpc/call
[{:method "mailservers_deleteChatRequestRange"
:params [chat-id]
:on-success #(log/debug "deleted chat request range successfully")
:on-failure #(log/error "failed to delete chat request range" %)}]})
(defn update-mailserver-topic
[{:keys [last-request] :as config}
{:keys [request-to]}]
(cond-> config
(> request-to last-request)
(assoc :last-request request-to)))
(defn check-existing-gaps
[chat-id chat-gaps request]
(let [request-from (:from request)
request-to (:to request)]
(reduce
(fn [acc {:keys [from to id] :as gap}]
(cond
;; F----T
;; RF---RT
(< to request-from)
acc
;; F----T
;; RF---RT
(< request-to from)
(reduced acc)
;; F------T
;; RF-----RT
(and (<= request-from from)
(< from request-to to))
(let [updated-gap (assoc gap
:from request-to
:to to)]
(reduced
(update acc :updated-gaps assoc id updated-gap)))
;; F------T
;; RF----------RT
(and (<= request-from from)
(<= to request-to))
(update acc :deleted-gaps conj (:id gap))
;; F---------T
;; RF-------RT
(and (< from request-from to)
(<= to request-to))
(let [updated-gap (assoc gap
:from from
:to request-from)]
(update acc :updated-gaps assoc id updated-gap))
;; F---------T
;; RF---RT
(and (< from request-from)
(< request-to to))
(reduced
(-> acc
(update :deleted-gaps conj (:id gap))
(update :new-gaps concat [{:chat-id chat-id
:from from
:to request-from}
{:chat-id chat-id
:from request-to
:to to}])))
:else acc))
{}
(sort-by :from (vals chat-gaps)))))
(defn check-all-gaps
[gaps chat-ids request]
(transduce
(map (fn [chat-id]
(let [chat-gaps (get gaps chat-id)]
[chat-id (check-existing-gaps chat-id chat-gaps request)])))
(completing
(fn [acc [chat-id {:keys [new-gaps updated-gaps deleted-gaps]}]]
(cond-> acc
(seq new-gaps)
(assoc-in [:new-gaps chat-id] new-gaps)
(seq updated-gaps)
(assoc-in [:updated-gaps chat-id] updated-gaps)
(seq deleted-gaps)
(assoc-in [:deleted-gaps chat-id] deleted-gaps))))
{}
chat-ids))
(fx/defn update-ranges
[{:keys [db] :as cofx}]
(let [{:keys [topics from to]}
(get db :mailserver/current-request)
chat-ids (mapcat
:chat-ids
(-> (:mailserver/topics db)
(select-keys topics)
vals))
ranges (:mailserver/ranges db)
updated-ranges (into
{}
(keep
(fn [chat-id]
(let [chat-id (str chat-id)
{:keys [lowest-request-from
highest-request-to]
:as range}
(get ranges chat-id)]
[chat-id
(cond-> (assoc range :chat-id chat-id)
(or (nil? highest-request-to)
(> to highest-request-to))
(assoc :highest-request-to to)
(or (nil? lowest-request-from)
(< from lowest-request-from))
(assoc :lowest-request-from from))])))
chat-ids)]
(fx/merge cofx
{:db (update db :mailserver/ranges merge updated-ranges)
::json-rpc/call
[{:method "mailservers_addChatRequestRanges"
:params [(vals updated-ranges)]
:on-success #()
:on-failure
#(log/error "failed to save chat request range" %)}]})))
(defn prepare-new-gaps [new-gaps ranges {:keys [from to]} chat-ids]
(into
{}
(comp
(map (fn [chat-id]
(let [gaps (get new-gaps chat-id)
{:keys [highest-request-to lowest-request-from]}
(get ranges chat-id)]
[chat-id (cond-> gaps
(and
(not (nil? highest-request-to))
(< highest-request-to from))
(conj {:chat-id chat-id
:from highest-request-to
:to from})
(and
(not (nil? lowest-request-from))
(< to lowest-request-from))
(conj {:chat-id chat-id
:from to
:to lowest-request-from}))])))
(keep (fn [[chat-id gaps]]
[chat-id
(into {}
(map (fn [gap]
(let [id (rand/guid)]
[id (assoc gap :id id)])))
gaps)])))
chat-ids))
(fx/defn update-gaps
[{:keys [db] :as cofx}]
(let [{:keys [topics] :as request} (get db :mailserver/current-request)
chat-ids (into #{}
(comp
(keep #(get-in db [:mailserver/topics %]))
(mapcat :chat-ids)
(map str))
topics)
{:keys [updated-gaps new-gaps deleted-gaps]}
(check-all-gaps (get db :mailserver/gaps) chat-ids request)
ranges (:mailserver/ranges db)
prepared-new-gaps (prepare-new-gaps new-gaps ranges request chat-ids)]
(fx/merge
cofx
{:db
(reduce (fn [db chat-id]
(let [chats-deleted-gaps (get deleted-gaps chat-id)
chats-updated-gaps (merge (get updated-gaps chat-id)
(get prepared-new-gaps chat-id))]
(update-in db [:mailserver/gaps chat-id]
(fn [chat-gaps]
(-> (apply dissoc chat-gaps chats-deleted-gaps)
(merge chats-updated-gaps))))))
db
chat-ids)}
(data-store.mailservers/delete-gaps (mapcat val deleted-gaps))
(data-store.mailservers/save-gaps
(concat (mapcat vals (vals updated-gaps))
(mapcat vals (vals prepared-new-gaps)))))))
(fx/defn update-chats-and-gaps
[cofx cursor]
(when (or (nil? cursor)
(and (string? cursor)
(clojure.string/blank? cursor)))
(fx/merge
cofx
(update-gaps)
(update-ranges))))
(defn get-updated-mailserver-topics [db requested-topics from to]
(into
{}
(keep (fn [topic]
(when-let [config (get-in db [:mailserver/topics topic])]
[topic (update-mailserver-topic config
{:request-from from
:request-to to})])))
requested-topics))
(fx/defn update-mailserver-topics
"TODO: add support for cursors
if there is a cursor, do not update `last-request`"
[{:keys [db now] :as cofx} {:keys [request-id cursor]}]
(when-let [request (get db :mailserver/current-request)]
(let [{:keys [from to topics]} request
mailserver-topics (get-updated-mailserver-topics db topics from to)]
(log/info "mailserver: message request " request-id
"completed for mailserver topics" topics "from" from "to" to)
(if (empty? mailserver-topics)
;; when topics were deleted (filter was removed while request was pending)
(fx/merge cofx
{:db (dissoc db :mailserver/current-request)}
(process-next-messages-request))
;; If a cursor is returned, add cursor and fire request again
(if (seq cursor)
(when-let [mailserver (get-mailserver-when-ready cofx)]
(let [request-with-cursor (assoc request :cursor cursor)]
{:db (assoc db :mailserver/current-request request-with-cursor)
:mailserver/request-messages {:mailserver mailserver
:request request-with-cursor}}))
(let [{:keys [gap chat-id]} request]
(fx/merge
cofx
{:db (-> db
(dissoc :mailserver/current-request)
(update :mailserver/requests-from
#(apply dissoc % topics))
(update :mailserver/requests-to
#(apply dissoc % topics))
(update :mailserver/topics merge mailserver-topics)
(update :mailserver/fetching-gaps-in-progress
(fn [gaps]
(if gap
(update gaps chat-id dissoc gap)
gaps)))
(update :mailserver/planned-gap-requests
dissoc gap))
::json-rpc/call
[{:method "mailservers_addMailserverTopics"
:params [(mapv (fn [[topic mailserver-topic]]
(assoc mailserver-topic :topic topic)) mailserver-topics)]
:on-success
#(log/debug "added mailserver-topic successfully")
:on-failure
#(log/error "failed to add mailserver topic" %)}]}
(process-next-messages-request))))))))
(fx/defn retry-next-messages-request (fx/defn retry-next-messages-request
{:events [:mailserver.ui/retry-request-pressed]} {:events [:mailserver.ui/retry-request-pressed]}
@ -907,55 +384,11 @@
;; on, rather then keep asking for the same data, say after n amounts of attempts ;; on, rather then keep asking for the same data, say after n amounts of attempts
(fx/defn handle-request-error (fx/defn handle-request-error
[{:keys [db]} error] [{:keys [db]} error]
{:mailserver/decrease-limit [] {:db (-> db
:db (-> db
(assoc :mailserver/request-error error) (assoc :mailserver/request-error error)
(dissoc :mailserver/current-request (dissoc :mailserver/current-request
:mailserver/pending-requests))}) :mailserver/pending-requests))})
(fx/defn handle-request-completed
[{{:keys [chats]} :db :as cofx}
{:keys [requestID lastEnvelopeHash cursor errorMessage]}]
(when (multiaccounts.model/logged-in? cofx)
(if (empty? errorMessage)
(let [never-synced-chats-in-request
(->> (chats->never-synced-public-chats chats)
(filter (fn [[k v]] (= requestID (:join-time-mail-request-id v))))
keys)]
(if (seq never-synced-chats-in-request)
(if (= lastEnvelopeHash
"0x0000000000000000000000000000000000000000000000000000000000000000")
(fx/merge
cofx
{:mailserver/increase-limit []
:dispatch-n
(map
#(identity [:chat.ui/join-time-messages-checked %])
never-synced-chats-in-request)}
(update-chats-and-gaps cursor)
(update-mailserver-topics {:request-id requestID
:cursor cursor}))
(fx/merge
cofx
{:mailserver/increase-limit []
:dispatch-later
(vec
(map
#(identity
{:ms 1000
:dispatch [:chat.ui/join-time-messages-checked %]})
never-synced-chats-in-request))}
(update-chats-and-gaps cursor)
(update-mailserver-topics {:request-id requestID
:cursor cursor})))
(fx/merge
cofx
{:mailserver/increase-limit []}
(update-chats-and-gaps cursor)
(update-mailserver-topics {:request-id requestID
:cursor cursor}))))
(handle-request-error cofx errorMessage))))
(fx/defn show-request-error-popup (fx/defn show-request-error-popup
{:events [:mailserver.ui/request-error-pressed]} {:events [:mailserver.ui/request-error-pressed]}
[{:keys [db]}] [{:keys [db]}]
@ -967,87 +400,11 @@
:on-accept #(re-frame/dispatch [:mailserver.ui/retry-request-pressed]) :on-accept #(re-frame/dispatch [:mailserver.ui/retry-request-pressed])
:confirm-button-text (i18n/label :t/mailserver-request-retry)}})) :confirm-button-text (i18n/label :t/mailserver-request-retry)}}))
(fx/defn fill-the-gap
[{:keys [db] :as cofx} {:keys [gaps topics chat-id]}]
(let [mailserver (get-mailserver-when-ready cofx)
requests (into {}
(map
(fn [{:keys [from to id]}]
[id
{:from (max from
(- to constants/max-request-range))
:to to
:force-to? true
:topics topics
:gap-topics topics
:chat-id chat-id
:gap id}]))
gaps)
first-request (val (first requests))
current-request (:mailserver/current-request db)]
(cond-> {:db (-> db
(assoc :mailserver/planned-gap-requests requests)
(update :mailserver/fetching-gaps-in-progress
assoc chat-id requests))}
(not current-request)
(-> (assoc-in [:db :mailserver/current-request] first-request)
(assoc :mailserver/request-messages
{:mailserver mailserver
:request first-request})))))
(fx/defn resend-request
{:events [:mailserver.callback/resend-request]}
[{:keys [db] :as cofx} {:keys [request-id]}]
(let [current-request (:mailserver/current-request db)
gap-request? (executing-gap-request? db)]
;; no inflight request, do nothing
(when (and current-request
;; the request was never successful
(or (nil? request-id)
;; we haven't received the request-id yet, but has expired,
;; so we retry even though we are not sure it's the current
;; request that failed
(nil? (:request-id current-request))
;; this is the same request that we are currently processing
(= request-id (:request-id current-request))))
(if (<= constants/maximum-number-of-attempts
(:attempts current-request))
(fx/merge cofx
{:db (update db :mailserver/current-request dissoc :attempts)}
(log-mailserver-failure)
(change-mailserver))
(let [mailserver (get-mailserver-when-ready cofx)
offline? (= :offline (:network-status db))]
(cond
(and gap-request? offline?)
{:db (-> db
(dissoc :mailserver/current-request)
(update :mailserver/fetching-gaps-in-progress
dissoc (:chat-id current-request))
(dissoc :mailserver/planned-gap-requests))}
mailserver
(let [{:keys [topics from to cursor limit] :as request}
current-request]
(log/info "mailserver: message request " request-id
"expired for mailserver topic" topics "from" from
"to" to "cursor" cursor "limit" (decrease-limit))
{:db (update-in db [:mailserver/current-request :attempts] inc)
:mailserver/decrease-limit []
:mailserver/request-messages
{:mailserver mailserver
:request (assoc request :limit (decrease-limit))}})
:else
{:mailserver/decrease-limit []}))))))
(fx/defn initialize-mailserver (fx/defn initialize-mailserver
[cofx] [{:keys [db] :as cofx}]
(fx/merge cofx (fx/merge cofx
{:mailserver/set-limit constants/default-limit} {:db db}
(set-current-mailserver) (set-current-mailserver)
(reset-request-to)
(process-next-messages-request))) (process-next-messages-request)))
(def enode-address-regex (def enode-address-regex
@ -1256,33 +613,6 @@
{}) {})
(dismiss-connection-error false)))) (dismiss-connection-error false))))
(fx/defn load-gaps-fx
{:events [:load-gaps]}
[{:keys [db] :as cofx} chat-id]
(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))))
(fx/defn load-gaps
{:events [::gaps-loaded]}
[{:keys [db now] :as cofx} chat-id gaps]
(let [now-s (quot now 1000)
outdated-gaps
(into []
(comp (filter #(< (:to %)
(- now-s constants/max-gaps-range)))
(map :id))
(vals gaps))
gaps (apply dissoc gaps outdated-gaps)]
(fx/merge
cofx
{:db
(-> db
(assoc-in [:gaps-loaded? chat-id] true)
(assoc-in [:mailserver/gaps chat-id] gaps))}
(data-store.mailservers/delete-gaps outdated-gaps))))
(fx/defn mailserver-ui-add-pressed (fx/defn mailserver-ui-add-pressed
{:events [:mailserver.ui/add-pressed]} {:events [:mailserver.ui/add-pressed]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]

View File

@ -1,898 +0,0 @@
(ns status-im.mailserver.core-test
(:require [cljs.test :refer-macros [deftest is testing]]
[clojure.string :as string]
[status-im.ethereum.json-rpc :as json-rpc]
[status-im.mailserver.constants :as constants]
[status-im.mailserver.core :as mailserver]
[status-im.transport.utils :as utils]
[status-im.utils.random :as rand]))
(def enode "enode://08d8eb6177b187049f6c97ed3f6c74fbbefb94c7ad10bafcaf4b65ce89c314dcfee0a8bc4e7a5b824dfa08b45b360cc78f34f0aff981f8386caa07652d2e601b@163.172.177.138:40404")
(def enode2 "enode://12d8eb6177b187049f6c97ed3f6c74fbbefb94c7ad10bafcaf4b65ce89c314dcfee0a8bc4e7a5b824dfa08b45b360cc78f34f0aff981f8386caa07652d2e601b@163.172.177.138:40404")
(deftest test-extract-enode-id
(testing "Get enode id from enode uri"
(is (= "08d8eb6177b187049f6c97ed3f6c74fbbefb94c7ad10bafcaf4b65ce89c314dcfee0a8bc4e7a5b824dfa08b45b360cc78f34f0aff981f8386caa07652d2e601b"
(utils/extract-enode-id enode))))
(testing "Get enode id from mailformed enode uri"
(is (= ""
(utils/extract-enode-id "08d8eb6177b187049f6c97ed3f6c74fbbefb94c7ad10bafcaf4b65ce89c314dcfee0a8bc4e7a5b824dfa08b45b360cc78f34f0aff981f8386caa07652d2e601b@163.172.177.138:40404"))))
(testing "Test empty string"
(is (= ""
(utils/extract-enode-id ""))))
(testing "Test nil"
(is (= ""
(utils/extract-enode-id nil)))))
(def peers
[{:id "08d8eb6177b187049f6c97ed3f6c74fbbefb94c7ad10bafcaf4b65ce89c314dcfee0a8bc4e7a5b824dfa08b45b360cc78f34f0aff981f8386caa07652d2e601b"
:enode "enode://08d8eb6177b187049f6c97ed3f6c74fbbefb94c7ad10bafcaf4b65ce89c314dcfee0a8bc4e7a5b824dfa08b45b360cc78f34f0aff981f8386caa07652d2e601b@163.172.177.138:40404"
:name "StatusIM/v0.9.9-unstable/linux-amd64/go1.9.2"}
{:id "0f7c65277f916ff4379fe520b875082a56e587eb3ce1c1567d9ff94206bdb05ba167c52272f20f634cd1ebdec5d9dfeb393018bfde1595d8e64a717c8b46692f"
:enode "enode://0f7c65277f916ff4379fe520b875082a56e587eb3ce1c1567d9ff94206bdb05ba167c52272f20f634cd1ebdec5d9dfeb393018bfde1595d8e64a717c8b46692f@203.136.241.111:40404"
:name "Geth/v1.7.2-stable/linux-amd64/go1.9.1"}])
(deftest change-mailserver
(testing "we are offline"
(testing "it does not change mailserver"
(is (not (mailserver/change-mailserver {:db {:multiaccount {:use-mailservers? true}
:peers-count 0}})))))
(testing "we are online"
(testing "there's a preferred mailserver"
(testing "it shows the popup"
(is (:ui/show-confirmation (mailserver/change-mailserver
{:db {:multiaccount {:fleet :staging
:use-mailservers? true
:pinned-mailservers {:staging "id"}}
:peers-count 1}})))))
(testing "there's not a preferred mailserver"
(testing "it changes the mailserver"
(is (= "mailservers_ping"
(get-in
(mailserver/change-mailserver
{:db {:mailserver/mailservers {:staging {:a "b"}}
:multiaccount {:use-mailservers? true
:fleet :staging}
:peers-count 1}})
[::json-rpc/call 0 :method]))))
(testing "it does not show the popup"
(is (not (:ui/show-confirmation (mailserver/change-mailserver
{:db {:multiaccount {:use-mailservers? true}
:peers-count 1}}))))))))
(deftest test-registered-peer?
(testing "Peer is registered"
(is (mailserver/registered-peer? peers enode)))
(testing "Peer is not peers list"
(is (not (mailserver/registered-peer? peers enode2))))
(testing "Empty peers"
(is (not (mailserver/registered-peer? [] enode))))
(testing "Empty peer"
(is (not (mailserver/registered-peer? peers ""))))
(testing "Nil peer"
(is (not (mailserver/registered-peer? peers nil)))))
(def enode-id "1da276e34126e93babf24ec88aac1a7602b4cbb2e11b0961d0ab5e989ca9c261aa7f7c1c85f15550a5f1e5a5ca2305b53b9280cf5894d5ecf7d257b173136d40")
(def password "password")
(def host "167.99.209.61:30504")
(def valid-enode-address (str "enode://" enode-id "@" host))
(def valid-enode-url (str "enode://" enode-id ":" password "@" host))
(deftest valid-enode-address-test
(testing "url without password"
(let [address "enode://1da276e34126e93babf24ec88aac1a7602b4cbb2e11b0961d0ab5e989ca9c261aa7f7c1c85f15550a5f1e5a5ca2305b53b9280cf5894d5ecf7d257b173136d40@167.99.209.61:30504"]
(is (mailserver/valid-enode-address? address))))
(testing "url with password"
(let [address "enode://1da276e34126e93babf24ec88aac1a7602b4cbb2e11b0961d0ab5e989ca9c261aa7f7c1c85f15550a5f1e5a5ca2305b53b9280cf5894d5ecf7d257b173136d40:somepasswordwith@and:@@167.99.209.61:30504"]
(is (not (mailserver/valid-enode-address? address)))))
(testing "invalid url"
(is (not (mailserver/valid-enode-address? "something not valid")))))
(deftest valid-enode-url-test
(testing "url without password"
(let [address "enode://1da276e34126e93babf24ec88aac1a7602b4cbb2e11b0961d0ab5e989ca9c261aa7f7c1c85f15550a5f1e5a5ca2305b53b9280cf5894d5ecf7d257b173136d40@167.99.209.61:30504"]
(is (not (mailserver/valid-enode-url? address)))))
(testing "url with password"
(let [address "enode://1da276e34126e93babf24ec88aac1a7602b4cbb2e11b0961d0ab5e989ca9c261aa7f7c1c85f15550a5f1e5a5ca2305b53b9280cf5894d5ecf7d257b173136d40:somepasswordwith@and:@@167.99.209.61:30504"]
(is (mailserver/valid-enode-url? address))))
(testing "invalid url"
(is (not (mailserver/valid-enode-url? "something not valid")))))
(deftest address->mailserver
(testing "with password"
(let [address "enode://some-id:the-password@206.189.56.154:30504"]
(is (= {:address "enode://some-id@206.189.56.154:30504"
:password "the-password"
:user-defined true}
(#'status-im.mailserver.core/address->mailserver address)))))
(testing "without password"
(let [address "enode://some-id@206.189.56.154:30504"]
(is (= {:address "enode://some-id@206.189.56.154:30504"
:user-defined true}
(#'status-im.mailserver.core/address->mailserver address))))))
(deftest set-input
(testing "it validates names"
(testing "correct name"
(is (= {:db {:mailserver.edit/mailserver {:name {:value "value"
:error false}}}}
(mailserver/set-input {:db {}} :name "value"))))
(testing "blank name"
(is (= {:db {:mailserver.edit/mailserver {:name {:value ""
:error true}}}}
(mailserver/set-input {:db {}} :name "")))))
(testing "it validates enodes url"
(testing "correct url"
(is (= {:db {:mailserver.edit/mailserver {:url {:value valid-enode-url
:error false}}}}
(mailserver/set-input {:db {}} :url valid-enode-url))))
(testing "broken url"
(is (= {:db {:mailserver.edit/mailserver {:url {:value "broken"
:error true}}}}
(mailserver/set-input {:db {}} :url "broken"))))))
(deftest edit-mailserver
(let [db {:mailserver/mailservers
{:eth.staging {"a" {:id "a"
:address valid-enode-address
:password password
:name "name"}}}}
cofx {:db db}]
(testing "when no id is given"
(let [actual (mailserver/edit cofx nil)]
(testing "it resets :mailserver/manage"
(is (= {:id {:value nil
:error false}
:url {:value ""
:error true}
:name {:value ""
:error true}}
(-> actual :db :mailserver.edit/mailserver))))
(testing "it navigates to edit-mailserver view"
(is (= [:edit-mailserver nil]
(:status-im.navigation/navigate-to actual))))))
(testing "when an id is given"
(testing "when the mailserver is in the list"
(let [actual (mailserver/edit cofx "a")]
(testing "it populates the fields with the correct values"
(is (= {:id {:value "a"
:error false}
:url {:value valid-enode-url
:error false}
:name {:value "name"
:error false}}
(-> actual :db :mailserver.edit/mailserver))))
(testing "it navigates to edit-mailserver view"
(is (= [:edit-mailserver nil]
(:status-im.navigation/navigate-to actual))))))
(testing "when the mailserver is not in the list"
(let [actual (mailserver/edit cofx "not-existing")]
(testing "it populates the fields with the correct values"
(is (= {:id {:value nil
:error false}
:url {:value ""
:error true}
:name {:value ""
:error true}}
(-> actual :db :mailserver.edit/mailserver))))
(testing "it navigates to edit-mailserver view"
(is (= [:edit-mailserver nil]
(:status-im.navigation/navigate-to actual)))))))))
(deftest connected-mailserver
(testing "it returns true when set in mailserver/current-id"
(let [db {:mailserver/current-id "a"}]
(is (mailserver/connected? db "a"))))
(testing "it returns false otherwise"
(is (not (mailserver/connected? {} "a")))))
(deftest fetch-mailserver
(testing "it fetches the mailserver from the db"
(let [db {:mailserver/mailservers {:eth.staging {"a" {:id "a"
:name "old-name"
:address "enode://old-id:old-password@url:port"}}}}]
(is (mailserver/fetch db "a")))))
(deftest fetch-current-mailserver
(testing "it fetches the mailserver from the db with corresponding id"
(let [db {:mailserver/current-id "a"
:mailserver/mailservers {:eth.staging {"a" {:id "a"
:name "old-name"
:address "enode://old-id:old-password@url:port"}}}}]
(is (mailserver/fetch-current db)))))
(deftest set-current-mailserver
(let [cofx {:db {:mailserver/mailservers {:eth.staging {"a" {}
"b" {}
"c" {}
"d" {}}}}}]
(testing "the user has already a preference"
(let [cofx (assoc-in cofx
[:db :multiaccount]
{:pinned-mailservers {:eth.staging "a"}})]
(testing "the mailserver exists"
(testing "it sets the preferred mailserver"
(is (= "a" (-> (mailserver/set-current-mailserver cofx)
:db
:mailserver/current-id)))))
(testing "the mailserver does not exists"
(let [cofx (update-in cofx [:db :mailserver/mailservers :eth.staging] dissoc "a")]
(testing "look for fastest mailserver"
(is (= "mailservers_ping"
(-> (mailserver/set-current-mailserver cofx)
::json-rpc/call
first
:method))))))))
(testing "the user has not set an explicit preference"
(testing "current-id is not set"
(testing "it looks for fastest mailserver"
(is (= "mailservers_ping"
(-> (mailserver/set-current-mailserver cofx)
::json-rpc/call
first
:method)))))
(testing "current-id is set"
(testing "it looks for fastest mailserver"
(is (= "mailservers_ping"
(-> (mailserver/set-current-mailserver (assoc-in
cofx
[:db :mailserver/current-id]
"b"))
::json-rpc/call
first
:method)))
(is (= "mailservers_ping"
(-> (mailserver/set-current-mailserver (assoc-in
cofx
[:db :mailserver/current-id]
"d"))
::json-rpc/call
first
:method)))
(is (= "mailservers_ping"
(-> (mailserver/set-current-mailserver (assoc-in
cofx
[:db :mailserver/current-id]
"non-existing"))
::json-rpc/call
first
:method))))))))
(deftest delete-mailserver
(testing "the user is not connected to the mailserver"
(let [cofx {:random-id-generator (constantly "random-id")
:db {:mailserver/mailservers {:eth.staging {"a" {:id "a"
:name "old-name"
:user-defined true
:address "enode://old-id:old-password@url:port"}}}}}
actual (mailserver/delete cofx "a")]
(testing "it removes the mailserver from the list"
(is (not (mailserver/fetch (:db actual) "a"))))
(testing "it stores it in the db"
(is (= 1 (count (::json-rpc/call actual)))))))
(testing "the mailserver is not user-defined"
(let [cofx {:random-id-generator (constantly "random-id")
:db {:mailserver/mailservers {:eth.staging {"a" {:id "a"
:name "old-name"
:address "enode://old-id:old-password@url:port"}}}}}
actual (mailserver/delete cofx "a")]
(testing "it does not delete the mailserver"
(is (= {:dispatch [:navigate-back]} actual)))))
(testing "the user is connected to the mailserver"
(let [cofx {:random-id-generator (constantly "random-id")
:db {:mailserver/mailservers {:eth.staging {"a" {:id "a"
:name "old-name"
:address "enode://old-id:old-password@url:port"}}}}}
actual (mailserver/delete cofx "a")]
(testing "it does not remove the mailserver from the list"
(is (= {:dispatch [:navigate-back]} actual))))))
(deftest upsert-mailserver
(testing "new mailserver"
(let [cofx {:random-id-generator (constantly "random-id")
:db {:mailserver.edit/mailserver {:name {:value "test-name"}
:url {:value valid-enode-url}}
:mailserver/mailservers {}}}
actual (mailserver/upsert cofx)]
(testing "it adds the enode to mailserver/mailservers"
(is (= {:eth.staging {:randomid {:password password
:address valid-enode-address
:name "test-name"
:id :randomid
:user-defined true}}}
(get-in actual [:db :mailserver/mailservers]))))
(testing "it navigates back"
(is (= [:navigate-back]
(:dispatch actual))))
(testing "it stores it in the db"
(is (= 1 (count (::json-rpc/call actual)))))))
(testing "existing mailserver"
(let [new-enode-url (string/replace valid-enode-url "password" "new-password")
cofx {:random-id-generator (constantly "random-id")
:db {:mailserver.edit/mailserver {:id {:value :a}
:name {:value "new-name"}
:url {:value new-enode-url}}
:mailserver/mailservers {:eth.staging {:a {:id :a
:name "old-name"
:address valid-enode-address}}}}}
actual (mailserver/upsert cofx)]
(testing "it navigates back"
(is (= [:navigate-back]
(:dispatch actual))))
(testing "it updates the enode to mailserver/mailservers"
(is (= {:eth.staging {:a {:password "new-password"
:address valid-enode-address
:name "new-name"
:id :a
:user-defined true}}}
(get-in actual [:db :mailserver/mailservers]))))
(testing "it stores it in the db"
(is (= 1 (count (::json-rpc/call actual))))))))
(defn cofx-fixtures [sym-key registered-peer?]
{:db {:mailserver/state :connected
:peers-summary (if registered-peer?
[{:id "mailserver-id" :enode "enode://mailserver-id@ip"}]
[])
:multiaccount {:fleet :eth.staging
:use-mailservers? true}
:mailserver/current-id "mailserver-a"
:mailserver/mailservers {:eth.staging {"mailserver-a" {:sym-key-id sym-key
:address "enode://mailserver-id@ip"}}}}})
(defn peers-summary-change-result [sym-key registered-peer? registered-peer-before?]
(mailserver/peers-summary-change (cofx-fixtures sym-key
registered-peer?)
(if registered-peer-before?
[{:id "mailserver-id" :enode "enode://mailserver-id@ip"}]
[])))
(deftest test-resend-request
(testing "there's no current request"
(is (not (mailserver/resend-request {:db {}} {}))))
(testing "there's a current request"
(testing "it reached the maximum number of attempts"
(testing "it changes mailserver"
(is (= "mailservers_ping"
(-> (mailserver/resend-request
{:db {:multiaccount {:use-mailservers? true}
:mailserver/current-request
{:attempts constants/maximum-number-of-attempts}}}
{})
::json-rpc/call
first
:method)))))
(testing "it did not reach the maximum number of attempts"
(testing "it reached the maximum number of attempts"
(testing "it decrease the limit")
(is (= {:mailserver/decrease-limit []} (mailserver/resend-request {:db {:mailserver/current-request
{}}}
{})))))))
(deftest test-resend-request-request-id
(testing "request-id passed is nil"
(testing "it resends the request"
(is (mailserver/resend-request {:db {:mailserver/current-request {}}} {}))))
(testing "request-id is nil in db"
(testing "it resends the request"
(is (mailserver/resend-request {:db {:mailserver/current-request {}}}
{:request-id "a"}))))
(testing "request id matches"
(testing "it resends the request"
(is (mailserver/resend-request {:db {:mailserver/current-request {:request-id "a"}}}
{:request-id "a"}))))
(testing "request id does not match"
(testing "it does not resend the request"
(is (not (mailserver/resend-request {:db {:mailserver/current-request {:request-id "a"}}}
{:request-id "b"}))))))
(def cofx-no-pub-topic
{:db
{:multiaccount {:public-key "me"}
:chats
{"chat-id-1" {:is-active true
:might-have-join-time-messages? true
:group-chat true
:public? true
:chat-id "chat-id-1"}
"chat-id-2" {:is-active true
:group-chat true
:public? true
:chat-id "chat-id-2"}}}})
(def cofx-single-pub-topic
{:db
{:multiaccount {:public-key "me"}
:chats
{"chat-id-1" {:is-active true
: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
:group-chat true
:public? true
:chat-id "chat-id-2"}}}})
(def cofx-multiple-pub-topic
{:db
{:multiaccount {:public-key "me"}
:chats
{"chat-id-1" {:is-active true
: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
: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
:group-chat true
:public? true
:chat-id "chat-id-3"}
"chat-id-4" {:is-active true
:join-time-mail-request-id "a"
:might-have-join-time-messages? true
:group-chat true
:public? true
:chat-id "chat-id-4"}}}})
(def mailserver-completed-event
{:requestID "a"
:lastEnvelopeHash "0xC0FFEE"
:cursor ""
:errorMessage ""})
(def mailserver-completed-event-zero-for-envelope
{:requestID "a"
:lastEnvelopeHash "0x0000000000000000000000000000000000000000000000000000000000000000"
:cursor ""
:errorMessage ""})
(deftest test-public-chat-related-handling-of-request-completed
(testing "Request does not include any public chat topic"
(testing "It does not dispatch any event"
(is (not (or (contains?
(mailserver/handle-request-completed cofx-no-pub-topic mailserver-completed-event)
:dispatch-n)
(contains?
(mailserver/handle-request-completed cofx-no-pub-topic mailserver-completed-event)
:dispatch-later)))))
(testing "It has :mailserver/increase-limit effect"
(is (contains? (mailserver/handle-request-completed cofx-no-pub-topic mailserver-completed-event)
:mailserver/increase-limit))))
(testing "Request includes one public chat topic"
(testing "Event has non-zero envelope"
(let [handeled-effects (mailserver/handle-request-completed
cofx-single-pub-topic
mailserver-completed-event)]
(testing "It has no :dispatch-n event"
(is (not (contains?
handeled-effects
:dispatch-n))))
(testing "It has one :dispatch-later event"
(is (= 1 (count (get
handeled-effects
:dispatch-later)))))
(testing "The :dispatch-later event is :chat.ui/join-time-messages-checked"
(is (= :chat.ui/join-time-messages-checked
(-> (get
handeled-effects
:dispatch-later)
first
:dispatch
first))))
(testing "The :dispatch-later event argument is the chat-id/topic that the request included"
(is (= "chat-id-1"
(-> (get
handeled-effects
:dispatch-later)
first
:dispatch
second))))
(testing "It has :mailserver/increase-limit effect"
(is (contains? handeled-effects
:mailserver/increase-limit)))))
(testing "Event has zero-valued envelope"
(let [handeled-effects (mailserver/handle-request-completed
cofx-single-pub-topic
mailserver-completed-event-zero-for-envelope)]
(testing "It has one :dispatch-n event"
(is (= 1 (count (get
handeled-effects
:dispatch-n)))))
(testing "It has no :dispatch-later event"
(is (not (contains?
handeled-effects
:dispatch-later))))
(testing "The :dispatch-n event is :chat.ui/join-time-messages-checked"
(is (= :chat.ui/join-time-messages-checked
(-> (get
handeled-effects
:dispatch-n)
first
first))))
(testing "The :dispatch-n event argument is the chat-id/topic that the request included"
(is (= "chat-id-1"
(-> (get
handeled-effects
:dispatch-n)
first
second))))
(testing "It has :mailserver/increase-limit effect"
(is (contains? handeled-effects
:mailserver/increase-limit))))))
(testing "Request includes multiple public chat topics (3)"
(testing "Event has non-zero envelope"
(let [handeled-effects (mailserver/handle-request-completed
cofx-multiple-pub-topic
mailserver-completed-event)]
(testing "It has no :dispatch-n event"
(is (not (contains?
handeled-effects
:dispatch-n))))
(testing "It has one :dispatch-later event"
(is (= 3 (count (get
handeled-effects
:dispatch-later)))))
(testing "It has :mailserver/increase-limit effect"
(is (contains? handeled-effects
:mailserver/increase-limit)))))
(testing "Event has zero-valued envelope"
(let [handeled-effects (mailserver/handle-request-completed
cofx-multiple-pub-topic
mailserver-completed-event-zero-for-envelope)]
(testing "It has one :dispatch-n event"
(is (= 3 (count (get
handeled-effects
:dispatch-n)))))
(testing "It has no :dispatch-later event"
(is (not (contains?
handeled-effects
:dispatch-later))))
(testing "It has :mailserver/increase-limit effect"
(is (contains? handeled-effects
:mailserver/increase-limit)))))))
(deftest peers-summary-change
(testing "Mailserver added, sym-key doesn't exist"
(let [result (peers-summary-change-result false true false)]
(is (= (into #{} (keys result))
#{:mailserver/mark-trusted-peer :shh/generate-sym-key-from-password :db}))))
(testing "Mailserver disconnected, sym-key exists"
(let [result (peers-summary-change-result true false true)]
(is (= (into #{} (keys result))
#{:db :mailserver/add-peer :mailserver/update-mailservers}))
(is (= (get-in result [:db :mailserver/state])
:connecting))))
(testing "Mailserver disconnected, sym-key doesn't exists (unlikely situation in practice)"
(let [result (peers-summary-change-result false false true)]
(is (= (into #{} (keys result))
#{:db :mailserver/add-peer :shh/generate-sym-key-from-password :mailserver/update-mailservers}))
(is (= (get-in result [:db :mailserver/state])
:connecting))))
(testing "Mailserver isn't concerned by peer summary changes"
(is (= (into #{} (keys (peers-summary-change-result true true true)))
#{}))
(is (= (into #{} (keys (peers-summary-change-result true false false)))
#{}))))
(deftest unpin-test
(testing "it removes the preference"
(let [db {:mailserver/current-id "mailserverid"
:mailserver/mailservers
{:eth.staging {"mailserverid" {:address "mailserver-address"
:password "mailserver-password"}}}
:multiaccount
{:fleet :eth.staging
:pinned-mailservers {:eth.staging "mailserverid"}}}]
(is (not (get-in (mailserver/unpin {:db db})
[:db :multiaccount :pinned-mailservers :eth.staging]))))))
(deftest pin-test
(testing "it removes the preference"
(let [db {:mailserver/current-id "mailserverid"
:mailserver/mailservers
{:eth.staging {"mailserverid" {:address "mailserver-address"
:password "mailserver-password"}}}
:multiaccount
{:fleet :eth.staging
:pinned-mailservers {}}}]
(is (= "mailserverid" (get-in (mailserver/pin {:db db})
[:db :multiaccount :pinned-mailservers :eth.staging]))))))
(deftest connect-to-mailserver
(let [db {:mailserver/current-id "mailserverid"
:mailserver/mailservers
{:eth.staging {"mailserverid" {:address "mailserver-address"
:password "mailserver-password"}}}
:multiaccount
{:fleet :eth.staging
:pinned-mailservers {:eth.staging "mailserverid"}
:use-mailservers? true}}]
(testing "it adds the peer"
(is (= "mailserver-address"
(:mailserver/add-peer (mailserver/connect-to-mailserver {:db db})))))
(testing "it generates a sym key if hasn't been generated before"
(is (= "mailserver-password"
(-> (mailserver/connect-to-mailserver {:db db})
:shh/generate-sym-key-from-password
:password))))
(let [mailserver-with-sym-key-db (assoc-in db
[:mailserver/mailservers :eth.staging "mailserverid" :sym-key-id]
"somesymkeyid")]
(testing "it does not generate a sym key if already present"
(is (not (-> (mailserver/connect-to-mailserver {:db mailserver-with-sym-key-db})
:shh/generate-sym-key-from-password)))))
(testing "it returns noops when use-mailservers? is false"
(let [no-mailservers-cofx {:db (assoc-in db [:multiaccount :use-mailservers?] false)}]
(is (= (mailserver/connect-to-mailserver no-mailservers-cofx)
nil))))))
(deftest check-existing-gaps
(testing "no gaps"
(is (= {}
(mailserver/check-existing-gaps
:chat-id
nil
{:from 1
:to 2}))))
(testing "request before gaps"
(is (= {}
(mailserver/check-existing-gaps
:chat-id
{:g1 {:from 10
:to 20
:id :g1}
:g2 {:from 30
:to 40
:id :g2}
:g3 {:from 50
:to 60
:id :g3}}
{:from 1
:to 2}))))
(testing "request between gaps"
(is (= {}
(mailserver/check-existing-gaps
:chat-id
{:g1 {:from 10
:to 20
:id :g1}
:g2 {:from 30
:to 40
:id :g2}
:g3 {:from 50
:to 60
:id :g3}}
{:from 22
:to 28}))))
(testing "request between gaps"
(is (= {}
(mailserver/check-existing-gaps
:chat-id
{:g1 {:from 10
:to 20
:id :g1}
:g2 {:from 30
:to 40
:id :g2}
:g3 {:from 50
:to 60
:id :g3}}
{:from 22
:to 28}))))
(testing "request after gaps"
(is (= {}
(mailserver/check-existing-gaps
:chat-id
{:g1 {:from 10
:to 20
:id :g1}
:g2 {:from 30
:to 40
:id :g2}
:g3 {:from 50
:to 60
:id :g3}}
{:from 70
:to 80}))))
(testing "request covers all gaps"
(is (= {:deleted-gaps [:g3 :g2 :g1]}
(mailserver/check-existing-gaps
:chat-id
{:g1 {:from 10
:to 20
:id :g1}
:g2 {:from 30
:to 40
:id :g2}
:g3 {:from 50
:to 60
:id :g3}}
{:from 10
:to 60}))))
(testing "request splits gap in two"
(is (= {:deleted-gaps [:g1]
:new-gaps [{:chat-id :chat-id :from 10 :to 12}
{:chat-id :chat-id :from 18 :to 20}]}
(mailserver/check-existing-gaps
:chat-id
{:g1 {:from 10
:to 20
:id :g1}
:g2 {:from 30
:to 40
:id :g2}
:g3 {:from 50
:to 60
:id :g3}}
{:from 12
:to 18}))))
(testing "request partially covers one gap #1"
(is (= {:updated-gaps {:g1 {:from 15
:to 20
:id :g1}}}
(mailserver/check-existing-gaps
:chat-id
{:g1 {:from 10
:to 20
:id :g1}
:g2 {:from 30
:to 40
:id :g2}
:g3 {:from 50
:to 60
:id :g3}}
{:from 8
:to 15}))))
(testing "request partially covers one gap #2"
(is (= {:updated-gaps {:g1 {:from 10
:to 15
:id :g1}}}
(mailserver/check-existing-gaps
:chat-id
{:g1 {:from 10
:to 20
:id :g1}
:g2 {:from 30
:to 40
:id :g2}
:g3 {:from 50
:to 60
:id :g3}}
{:from 15
:to 25}))))
(testing "request partially covers two gaps #2"
(is (= {:updated-gaps {:g1 {:from 10
:to 15
:id :g1}
:g2 {:from 35
:to 40
:id :g2}}}
(mailserver/check-existing-gaps
:chat-id
{:g1 {:from 10
:to 20
:id :g1}
:g2 {:from 30
:to 40
:id :g2}
:g3 {:from 50
:to 60
:id :g3}}
{:from 15
:to 35}))))
(testing "request covers one gap and two other partially"
(is (= {:updated-gaps {:g1 {:from 10
:to 15
:id :g1}
:g3 {:from 55
:to 60
:id :g3}}
:deleted-gaps [:g2]}
(mailserver/check-existing-gaps
:chat-id
{:g1 {:from 10
:to 20
:id :g1}
:g2 {:from 30
:to 40
:id :g2}
:g3 {:from 50
:to 60
:id :g3}}
{:from 15
:to 55})))))
(defn rand-guid []
(let [gap-id (atom 0)]
(fn []
(swap! gap-id inc)
(str "gap" @gap-id))))
(deftest prepare-new-gaps
(testing "prepare-new-gaps"
(with-redefs [rand/guid (rand-guid)]
(is (= {"chat1" {"gap1" {:id "gap1"
:chat-id "chat1"
:from 20
:to 30}}}
(mailserver/prepare-new-gaps
nil
{"chat1"
{:chat-id "chat1"
:lowest-request-from 10
:highest-request-to 20}}
{:from 30
:to 50}
#{"chat1"})))))
(testing "prepare-new-gaps request after known range"
(with-redefs [rand/guid (rand-guid)]
(is (= {"chat1" {"gap1" {:id "gap1"
:chat-id "chat1"
:from 12
:to 14}
"gap2" {:chat-id "chat1"
:from 20
:to 30
:id "gap2"}}}
(mailserver/prepare-new-gaps
{"chat1" [{:chat-id "chat1"
:from 12
:to 14}]}
{"chat1"
{:chat-id "chat1"
:lowest-request-from 10
:highest-request-to 20}}
{:from 30
:to 50}
#{"chat1"})))))
(testing "prepare-new-gaps request before known range"
(with-redefs [rand/guid (rand-guid)]
(is (= {"chat1" {"gap1" {:chat-id "chat1"
:from 12
:to 14
:id "gap1"}
"gap2" {:chat-id "chat1"
:from 8
:to 10
:id "gap2"}}}
(mailserver/prepare-new-gaps
{"chat1" [{:chat-id "chat1"
:from 12
:to 14}]}
{"chat1"
{:chat-id "chat1"
:lowest-request-from 10
:highest-request-to 20}}
{:from 2
:to 8}
#{"chat1"}))))))
(deftest sort-mailserver-test
(testing "it orders them by whether they have failed first and by rtts"
(let [now (inc constants/cooloff-period)
mailserver-failures {:a 1
:b 0}
cofx {:now now
:db {:mailserver/failures mailserver-failures}}
mailserver-pings [{:address :a :rttMs 2}
{:address :b :rttMs 3}
{:address :d :rttMs 1}
{:address :e :rttMs 4}]
expected-order [{:address :d :rttMs 1}
{:address :b :rttMs 3}
{:address :e :rttMs 4}
{:address :a :rttMs 2}]]
(is (= expected-order (mailserver/sort-mailservers cofx mailserver-pings))))))

View File

@ -1,171 +0,0 @@
(ns ^{:doc "Mailserver events and API"}
status-im.mailserver.topics
(:require [clojure.set :as clojure.set]
[status-im.ethereum.json-rpc :as json-rpc]
[status-im.mailserver.constants :as constants]
[status-im.utils.fx :as fx]
[taoensso.timbre :as log]))
(defn calculate-last-request [{:keys [negotiated?
discovery?]}
{:keys [previous-last-request
now-s]}]
;; New topic, if discovery we don't fetch history
(if (and (nil? previous-last-request)
(or discovery?
negotiated?))
(- now-s 10)
(max previous-last-request
(- now-s constants/max-request-range))))
(fx/defn store [{:keys [db]} {:keys [topic
filter-ids
chat-ids] :as mailserver-topic}]
(if (or (empty? chat-ids)
(empty? filter-ids))
{:db (update db :mailserver/topics dissoc topic)}
{:db (assoc-in db [:mailserver/topics topic] mailserver-topic)}))
(fx/defn persist [_ {:keys [chat-ids topic filter-ids]
:as mailserver-topic}]
(if (or (empty? chat-ids)
(empty? filter-ids))
{::json-rpc/call [{:method "mailservers_deleteMailserverTopic"
:params [topic]
:on-success #(log/debug "deleted mailserver topic successfully")
:on-failure #(log/error "failed to delete mailserver topic" %)}]}
{::json-rpc/call [{:method "mailservers_addMailserverTopic"
:params [mailserver-topic]
:on-success #(log/debug "added mailserver-topic successfully")
:on-failure #(log/error "failed to add mailserver topic" %)}]}))
(defn new-chat-ids? [previous-mailserver-topic new-mailserver-topic]
(seq (clojure.set/difference (:chat-ids new-mailserver-topic)
(:chat-ids previous-mailserver-topic))))
(defn merge-topic
"Calculate last-request and merge chat-ids keeping the old ones and new ones"
[old-mailserver-topic
new-mailserver-topic
{:keys [now-s]}]
(let [last-request (calculate-last-request
new-mailserver-topic
{:previous-last-request (:last-request old-mailserver-topic)
:now-s now-s})]
(-> old-mailserver-topic
(assoc
:topic (:topic new-mailserver-topic)
:discovery? (boolean (:discovery? new-mailserver-topic))
:negotiated? (boolean (:negotiated? new-mailserver-topic))
:last-request last-request)
(update :filter-ids
clojure.set/union
(set (:filter-ids new-mailserver-topic)))
(update :chat-ids
clojure.set/union
(set (:chat-ids new-mailserver-topic))))))
(fx/defn update-topic [cofx persist? topic]
(fx/merge cofx
(store topic)
(when persist? (persist topic))))
(fx/defn upsert
"if the topic didn't exist
create the topic
else if chat-id is not in the topic
add the chat-id to the topic and reset last-request
there was no filter for the chat and messages for that
so the whole history for that topic needs to be re-fetched"
[{:keys [db now] :as cofx} new-mailserver-topic]
(let [old-mailserver-topic (get-in db [:mailserver/topics (:topic new-mailserver-topic)]
{:topic (:topic new-mailserver-topic)
:filter-ids #{}
:chat-ids #{}})]
(let [updated-topic (merge-topic old-mailserver-topic
new-mailserver-topic
{:now-s (quot now 1000)})]
(update-topic cofx
true
updated-topic))))
(fx/defn upsert-many [cofx mailserver-topics]
(apply fx/merge cofx (map upsert mailserver-topics)))
(fx/defn update-many [cofx mailserver-topics]
(apply fx/merge cofx (map (partial update-topic true) mailserver-topics)))
(fx/defn delete [{:keys [db] :as cofx} {:keys [filter-id]}]
(when-let [matching-topics (filter (fn [{:keys [filter-ids] :as topic}]
(if (not filter-ids)
(do (log/warn "topic not initialized, removing" topic)
true)
(filter-ids filter-id)))
(vals (:mailserver/topics db)))]
(update-many cofx (map #(update % :filter-ids disj filter-id) matching-topics))))
(fx/defn delete-many
"Remove filter-ids from any topics and save"
[cofx filters]
(apply fx/merge cofx (map delete filters)))
(defn extract-topics
"return all the topics for this chat, including discovery topics if specified"
[topics chat-id include-discovery?]
(reduce-kv
(fn [acc topic {:keys [negotiated?
discovery?
chat-ids]}]
(if (or (and (or discovery?
negotiated?)
include-discovery?)
(chat-ids chat-id))
(conj acc topic)
acc))
#{}
topics))
(defn changed-for-group-chat
"Returns all the discovery topics, or those topics that have at least one of the members.
Returns those topic that had chat-id but the member is not there anymore"
[topics chat-id members]
(reduce
(fn [acc {:keys [chat-ids] :as topic}]
(cond (some chat-ids members)
(update acc :modified conj
(assoc topic
:chat-ids #{chat-id}))
(and
(chat-ids chat-id)
(not-any? chat-ids members))
(update acc :removed conj
(-> topic
(assoc :topic (:topic topic))
(update :chat-ids disj chat-id)))
:else
acc))
{:modified [] :removed []}
topics))
(fx/defn upsert-group-chat
"Based on the members it will upsert a mailserver topic for any discovery topic
and any personal topic that is in members. It will also remove the chat-id from any existing topic if not with a member"
[{:keys [db] :as cofx} chat-id members]
(let [topics (reduce-kv
(fn [acc topic-id topic]
(conj acc (assoc topic :topic topic-id)))
[]
(:mailserver/topics db))
{:keys [modified
removed]} (changed-for-group-chat
topics
chat-id
members)]
(fx/merge cofx
(upsert-many modified)
(update-many removed))))
(defn topics-for-chat [db chat-id]
(extract-topics (:mailserver/topics db)
chat-id
(not (get-in (:chats db) [chat-id :public?]))))

View File

@ -1,157 +0,0 @@
(ns status-im.mailserver.topics-test
(:require [cljs.test :refer-macros [deftest is testing]]
[status-im.mailserver.constants :as c]
[status-im.mailserver.topics :as t]))
(def now-s 100000)
(deftest test-merge-topic-basic-functionality
(testing "a new topic"
(let [old-topic {:chat-ids #{}}
new-topic {:topic "a"
:chat-ids #{"a"}
:filter-ids #{"b" "c"}}
expected-topic {:last-request (- now-s c/max-request-range)
:topic "a"
:filter-ids #{"b" "c"}
:discovery? false
:negotiated? false
:chat-ids #{"a"}}]
(is (= expected-topic
(t/merge-topic old-topic new-topic {:now-s now-s})))))
(testing "an already existing topic"
(let [old-topic {:filter-ids #{"a" "b"}
:chat-ids #{"a" "b"}}
new-topic {:topic "a"
:filter-ids #{"a" "c"}
:chat-ids #{"a" "c"}}
expected-topic {:last-request (- now-s c/max-request-range)
:topic "a"
:discovery? false
:negotiated? false
:filter-ids #{"a" "b" "c"}
:chat-ids #{"a" "b" "c"}}]
(is (= expected-topic
(t/merge-topic old-topic new-topic {:now-s now-s}))))))
(deftest test-merge-topic
(testing "previous last request is nil and not discovery"
(let [old-topic {:chat-ids #{}}
new-topic {:chat-ids #{"a"}}
expected-topic {:last-request (- now-s c/max-request-range)
:discovery? false
:negotiated? false
:topic nil
:filter-ids nil
:chat-ids #{"a"}}]
(is (= expected-topic
(t/merge-topic old-topic new-topic {:now-s now-s})))))
(testing "previous last request is nil and discovery"
(let [old-topic {:chat-ids #{}}
new-topic {:discovery? true
:chat-ids #{"a"}}
expected-topic {:last-request (- now-s 10)
:discovery? true
:negotiated? false
:topic nil
:filter-ids nil
:chat-ids #{"a"}}]
(is (= expected-topic
(t/merge-topic old-topic new-topic {:now-s now-s})))))
(testing "previous last request is set to less then max range ago"
(let [old-last-request (inc (- now-s c/max-request-range))
old-topic {:last-request old-last-request
:chat-ids #{}}
new-topic {:chat-ids #{"a"}}
expected-topic {:last-request old-last-request
:discovery? false
:negotiated? false
:topic nil
:filter-ids nil
:chat-ids #{"a"}}]
(is (= expected-topic
(t/merge-topic old-topic new-topic {:now-s now-s})))))
(testing "previous last request is set to less then max range ago"
(let [old-last-request (- now-s (* 2 c/max-request-range))
old-topic {:last-request old-last-request
:chat-ids #{}}
new-topic {:chat-ids #{"a"}}
expected-topic {:last-request (- now-s c/max-request-range)
:discovery? false
:negotiated? false
:topic nil
:filter-ids nil
:chat-ids #{"a"}}]
(is (= expected-topic
(t/merge-topic old-topic new-topic {:now-s now-s}))))))
(deftest new-chat-ids?
(testing "new-chat-ids?"
(is (not (t/new-chat-ids? {:chat-ids #{"a" "b" "c"}}
{:chat-ids #{"a"}})))
(is (t/new-chat-ids? {:chat-ids #{"a" "b"}}
{:chat-ids #{"a" "b" "c"}}))))
(deftest topics-for-chat
(testing "a public chat"
(testing "the chat is in multiple topics"
(is (= #{"a" "b"}
(t/topics-for-chat {:chats {"chat-id-1" {:public? true}}
:mailserver/topics {"a" {:chat-ids #{"chat-id-1" "chat-id-2"}}
"b" {:chat-ids #{"chat-id-1"}}
"c" {:discovery? true
:chat-ids #{"chat-id-2"}}}}
"chat-id-1"))))
(testing "the chat is not there"
(is (= #{}
(t/topics-for-chat {:chats {"chat-id-3" {:public? true}}
:mailserver/topics {"a" {:chat-ids #{"chat-id-1" "chat-id-2"}}
"b" {:chat-ids #{"chat-id-1"}}
"c" {:discovery? true
:chat-ids #{"chat-id-2"}}}}
"chat-id-3")))))
(testing "a one to one"
(is (= #{"a" "c"}
(t/topics-for-chat {:mailserver/topics {"a" {:chat-ids #{"chat-id-1" "chat-id-2"}}
"b" {:chat-ids #{"chat-id-1"}}
"c" {:discovery? true}}}
"chat-id-2")))))
(deftest upsert-group-chat-test
(testing "new group chat"
(let [expected-topics {:modified [{:topic "2"
:chat-ids #{"chat-id"}}
{:topic "4"
:chat-ids #{"chat-id"}}]
:removed []}]
(is (= expected-topics
(t/changed-for-group-chat [{:topic "1"
:discovery? true
:chat-ids #{}}
{:topic "2"
:chat-ids #{"a"}}
{:topic "3"
:chat-ids #{"b"}}
{:topic "4"
:chat-ids #{"c"}}]
"chat-id"
["a" "c"])))))
(testing "existing group chat"
(let [expected-topics {:modified [{:topic "2"
:chat-ids #{"chat-id"}}
{:topic "4"
:chat-ids #{"chat-id"}}]
:removed [{:topic "3"
:chat-ids #{"b"}}]}]
(is (= expected-topics
(t/changed-for-group-chat [{:topic "1"
:discovery? true
:chat-ids #{}}
{:topic "2"
:chat-ids #{"a"}}
{:topic "3"
:chat-ids #{"chat-id" "b"}}
{:topic "4"
:chat-ids #{"c"}}]
"chat-id"
["a" "c"]))))))

View File

@ -35,7 +35,6 @@
[status-im.chat.models.link-preview :as link-preview] [status-im.chat.models.link-preview :as link-preview]
[status-im.utils.mobile-sync :as utils.mobile-sync] [status-im.utils.mobile-sync :as utils.mobile-sync]
[status-im.async-storage.core :as async-storage] [status-im.async-storage.core :as async-storage]
[status-im.chat.models :as chat.models]
[status-im.notifications-center.core :as notifications-center])) [status-im.notifications-center.core :as notifications-center]))
(re-frame/reg-fx (re-frame/reg-fx
@ -320,9 +319,8 @@
:dispatch-later [{:ms 2000 :dispatch [::initialize-wallet accounts nil nil (:recovered multiaccount) true]}]} :dispatch-later [{:ms 2000 :dispatch [::initialize-wallet accounts nil nil (:recovered multiaccount) true]}]}
(finish-keycard-setup) (finish-keycard-setup)
(transport/start-messenger) (transport/start-messenger)
(chat.loading/initialize-chats)
(communities/fetch) (communities/fetch)
(chat.models/start-timeline-chat)
(chat.models/start-profile-chat (:public-key multiaccount))
(multiaccounts/switch-preview-privacy-mode-flag) (multiaccounts/switch-preview-privacy-mode-flag)
(link-preview/request-link-preview-whitelist) (link-preview/request-link-preview-whitelist)
(logging/set-log-level (:log-level multiaccount))))) (logging/set-log-level (:log-level multiaccount)))))

View File

@ -3,8 +3,6 @@
[status-im.i18n.i18n :as i18n] [status-im.i18n.i18n :as i18n]
[status-im.mailserver.core :as mailserver] [status-im.mailserver.core :as mailserver]
[status-im.multiaccounts.login.core :as login] [status-im.multiaccounts.login.core :as login]
[status-im.multiaccounts.model :as multiaccounts.model]
[status-im.transport.filters.core :as transport.filters]
[status-im.transport.message.core :as transport.message] [status-im.transport.message.core :as transport.message]
[status-im.notifications.local :as local-notifications] [status-im.notifications.local :as local-notifications]
[status-im.chat.models.message :as models.message] [status-im.chat.models.message :as models.message]
@ -60,13 +58,9 @@
"envelope.expired" (transport.message/update-envelopes-status cofx (:ids (js->clj event-js :keywordize-keys true)) :not-sent) "envelope.expired" (transport.message/update-envelopes-status cofx (:ids (js->clj event-js :keywordize-keys true)) :not-sent)
"message.delivered" (let [{:keys [chatID messageID]} (js->clj event-js :keywordize-keys true)] "message.delivered" (let [{:keys [chatID messageID]} (js->clj event-js :keywordize-keys true)]
(models.message/update-db-message-status cofx chatID messageID :delivered)) (models.message/update-db-message-status cofx chatID messageID :delivered))
"mailserver.request.completed" (mailserver/handle-request-completed cofx (js->clj event-js :keywordize-keys true))
"mailserver.request.expired" (when (multiaccounts.model/logged-in? cofx)
(mailserver/resend-request cofx {:request-id (.-hash event-js)}))
"discovery.summary" (summary cofx (js->clj event-js :keywordize-keys true)) "discovery.summary" (summary cofx (js->clj event-js :keywordize-keys true))
"subscriptions.data" (ethereum.subscriptions/handle-signal cofx (js->clj event-js :keywordize-keys true)) "subscriptions.data" (ethereum.subscriptions/handle-signal cofx (js->clj event-js :keywordize-keys true))
"subscriptions.error" (ethereum.subscriptions/handle-error cofx (js->clj event-js :keywordize-keys true)) "subscriptions.error" (ethereum.subscriptions/handle-error cofx (js->clj event-js :keywordize-keys true))
"whisper.filter.added" (transport.filters/handle-negotiated-filter cofx (js->clj event-js :keywordize-keys true))
"messages.new" (transport.message/sanitize-messages-and-process-response cofx event-js true) "messages.new" (transport.message/sanitize-messages-and-process-response cofx event-js true)
"wallet" (ethereum.subscriptions/new-wallet-event cofx (js->clj event-js :keywordize-keys true)) "wallet" (ethereum.subscriptions/new-wallet-event cofx (js->clj event-js :keywordize-keys true))
"local-notifications" (local-notifications/process cofx (js->clj event-js :keywordize-keys true)) "local-notifications" (local-notifications/process cofx (js->clj event-js :keywordize-keys true))

View File

@ -136,8 +136,6 @@
(reg-root-key-sub :mailserver/pending-requests :mailserver/pending-requests) (reg-root-key-sub :mailserver/pending-requests :mailserver/pending-requests)
(reg-root-key-sub :mailserver/request-error? :mailserver/request-error) (reg-root-key-sub :mailserver/request-error? :mailserver/request-error)
(reg-root-key-sub :mailserver/fetching-gaps-in-progress :mailserver/fetching-gaps-in-progress) (reg-root-key-sub :mailserver/fetching-gaps-in-progress :mailserver/fetching-gaps-in-progress)
(reg-root-key-sub :mailserver/gaps :mailserver/gaps)
(reg-root-key-sub :mailserver/ranges :mailserver/ranges)
;;contacts ;;contacts
(reg-root-key-sub ::contacts :contacts/contacts) (reg-root-key-sub ::contacts :contacts/contacts)
@ -763,6 +761,20 @@
(fn [chats [_ chat-id]] (fn [chats [_ chat-id]]
(get chats chat-id))) (get chats chat-id)))
(re-frame/reg-sub
:chats/synced-from
(fn [[_ chat-id] _]
(re-frame/subscribe [:chat-by-id chat-id]))
(fn [{:keys [synced-from]}]
synced-from))
(re-frame/reg-sub
:chats/synced-to-and-from
(fn [[_ chat-id] _]
(re-frame/subscribe [:chat-by-id chat-id]))
(fn [chat]
(select-keys chat [:synced-to :synced-from])))
(re-frame/reg-sub (re-frame/reg-sub
:chats/current-raw-chat :chats/current-raw-chat
:<- [::chats] :<- [::chats]
@ -826,7 +838,7 @@
:chats/current-chat-chat-view :chats/current-chat-chat-view
:<- [:chats/current-chat] :<- [:chats/current-chat]
(fn [current-chat] (fn [current-chat]
(select-keys current-chat [:chat-id :show-input? :group-chat :admins :invitation-admin :public? :chat-type :color :chat-name]))) (select-keys current-chat [:chat-id :show-input? :group-chat :admins :invitation-admin :public? :chat-type :color :chat-name :synced-to :synced-from])))
(re-frame/reg-sub (re-frame/reg-sub
:current-chat/metadata :current-chat/metadata
@ -871,24 +883,6 @@
current-public-key current-public-key
(get-in reactions [chat-id message-id])))) (get-in reactions [chat-id message-id]))))
(re-frame/reg-sub
:chats/messages-gaps
:<- [:mailserver/gaps]
(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]
(fn [ranges [_ chat-id]]
(get ranges chat-id)))
(re-frame/reg-sub (re-frame/reg-sub
:mailserver/current-name :mailserver/current-name
:<- [:mailserver/current-id] :<- [:mailserver/current-id]
@ -915,12 +909,6 @@
(fn [chats [_ chat-id]] (fn [chats [_ chat-id]]
(get-in chats [chat-id :public?]))) (get-in chats [chat-id :public?])))
(re-frame/reg-sub
:chats/might-have-join-time-messages?
:<- [::chats]
(fn [chats [_ chat-id]]
(get-in chats [chat-id :might-have-join-time-messages?])))
(re-frame/reg-sub (re-frame/reg-sub
:chats/message-list :chats/message-list
:<- [::message-lists] :<- [::message-lists]
@ -948,16 +936,18 @@
(fn [[_ chat-id] _] (fn [[_ chat-id] _]
[(re-frame/subscribe [:chats/message-list chat-id]) [(re-frame/subscribe [:chats/message-list chat-id])
(re-frame/subscribe [:chats/chat-messages chat-id]) (re-frame/subscribe [:chats/chat-messages chat-id])
(re-frame/subscribe [:chats/messages-gaps chat-id]) (re-frame/subscribe [:chats/loading-messages? chat-id])
(re-frame/subscribe [:chats/range chat-id]) (re-frame/subscribe [:chats/synced-from chat-id])])
(re-frame/subscribe [:chats/all-loaded? chat-id]) (fn [[message-list messages loading-messages? synced-from] [_ chat-id]]
(re-frame/subscribe [:chats/public? chat-id])])
(fn [[message-list messages messages-gaps range all-loaded? public?]]
;;TODO (perf) ;;TODO (perf)
(-> (models.message-list/->seq message-list) (let [message-list-seq (models.message-list/->seq message-list)]
; Don't show gaps if that's the case as we are still loading messages
(if (and (empty? message-list-seq) loading-messages?)
[]
(-> message-list-seq
(chat.db/add-datemarks) (chat.db/add-datemarks)
(hydrate-messages messages) (hydrate-messages messages)
(chat.db/add-gaps messages-gaps range all-loaded? public?)))) (chat.db/collapse-gaps chat-id synced-from))))))
;;we want to keep data unchanged so react doesn't change component when we leave screen ;;we want to keep data unchanged so react doesn't change component when we leave screen
(def memo-chat-messages-stream (atom nil)) (def memo-chat-messages-stream (atom nil))
@ -2133,8 +2123,8 @@
(re-frame/reg-sub (re-frame/reg-sub
:chats/fetching-gap-in-progress? :chats/fetching-gap-in-progress?
:<- [:mailserver/fetching-gaps-in-progress] :<- [:mailserver/fetching-gaps-in-progress]
(fn [gaps [_ ids chat-id]] (fn [gaps [_ ids _]]
(seq (select-keys (get gaps chat-id) ids)))) (seq (select-keys gaps ids))))
(re-frame/reg-sub (re-frame/reg-sub
:mailserver/fetching? :mailserver/fetching?

View File

@ -8,7 +8,6 @@
[status-im.utils.fx :as fx] [status-im.utils.fx :as fx]
[status-im.utils.handlers :as handlers] [status-im.utils.handlers :as handlers]
[status-im.utils.publisher :as publisher] [status-im.utils.publisher :as publisher]
[status-im.transport.filters.core :as transport.filters]
status-im.transport.shh status-im.transport.shh
[taoensso.timbre :as log] [taoensso.timbre :as log]
[status-im.utils.universal-links.core :as universal-links])) [status-im.utils.universal-links.core :as universal-links]))
@ -49,27 +48,6 @@
db db
custom-mailservers)) custom-mailservers))
(defn add-mailserver-topics
[db mailserver-topics]
(assoc db
:mailserver/topics
(reduce (fn [acc {:keys [topic]
:as mailserver-topic}]
(assoc acc topic
(update mailserver-topic :chat-ids
#(into #{} %))))
{}
mailserver-topics)))
(defn add-mailserver-ranges
[db mailserver-ranges]
(assoc db
:mailserver/ranges
(reduce (fn [acc {:keys [chat-id] :as range}]
(assoc acc chat-id range))
{}
mailserver-ranges)))
(fx/defn start-messenger (fx/defn start-messenger
"We should only start receiving messages/processing topics once all the "We should only start receiving messages/processing topics once all the
initializiation is completed, otherwise we might receive messages/topics initializiation is completed, otherwise we might receive messages/topics
@ -81,18 +59,12 @@
(fx/defn messenger-started (fx/defn messenger-started
{:events [::messenger-started]} {:events [::messenger-started]}
[{:keys [db] :as cofx} {:keys [filters [{:keys [db] :as cofx} {:keys [mailservers] :as response}]
mailserverTopics
mailservers
mailserverRanges] :as response}]
(log/info "Messenger started") (log/info "Messenger started")
(fx/merge cofx (fx/merge cofx
{:db (-> db {:db (-> db
(assoc :messenger/started? true) (assoc :messenger/started? true)
(add-mailserver-ranges mailserverRanges)
(add-mailserver-topics mailserverTopics)
(add-custom-mailservers mailservers))} (add-custom-mailservers mailservers))}
(transport.filters/handle-loaded-filters filters)
(fetch-node-info-fx) (fetch-node-info-fx)
(pairing/init) (pairing/init)
(publisher/start-fx) (publisher/start-fx)

View File

@ -1,350 +0,0 @@
(ns status-im.transport.filters.core
"This namespace is used to handle filters loading and unloading from statusgo"
(:require [clojure.string :as string]
[re-frame.core :as re-frame]
[status-im.constants :as constants]
[status-im.contact.db :as contact.db]
[status-im.ethereum.json-rpc :as json-rpc]
[status-im.mailserver.core :as mailserver]
[status-im.mailserver.topics :as mailserver.topics]
[status-im.multiaccounts.model :as multiaccounts.model]
[status-im.utils.fx :as fx]
[taoensso.timbre :as log]))
(defn is-public-key? [k]
(and
(string? k)
(string/starts-with? k "0x")))
(defn load-filters-rpc [chats on-success on-failure]
(json-rpc/call {:method (json-rpc/call-ext-method "loadFilters")
:params [chats]
:on-success on-success
:on-failure on-failure}))
(defn remove-filters-rpc [chats on-success on-failure]
(json-rpc/call {:method (json-rpc/call-ext-method "removeFilters")
:params [chats]
:on-success on-success
:on-failure on-failure}))
;; fx functions
(defn load-filter-fx [filters]
{:filters/load-filters [[filters]]})
(defn remove-filter-fx [filters]
(when (seq filters)
{:filters/remove-filters [filters]}))
;; dispatches
(defn filters-added! [filters]
(re-frame/dispatch [:filters.callback/filters-added filters]))
(defn filters-removed! [filters]
(re-frame/dispatch [:filters.callback/filters-removed filters]))
;; Mailserver topics
(fx/defn upsert-mailserver-topic
"Update the topics with the newly created filter"
[cofx {:keys [discovery?
negotiated?
filter-id
topic
chat-id
filter]}]
(mailserver.topics/upsert cofx {:topic topic
:negotiated? negotiated?
:filter-ids #{filter-id}
:discovery? discovery?
:chat-ids #{chat-id}}))
;; Every time we load a filter, we want to update group chats where the user is a member, has it might be a negotiated filter, so should be included in the request topics
(fx/defn upsert-group-chat-topics
"Update topics for each member of the group chat"
[{:keys [db] :as cofx}]
(let [group-chats (filter (fn [{:keys [chat-type]}]
(= chat-type constants/private-group-chat-type))
(vals (:chats db)))]
(apply fx/merge
cofx
(map
#(mailserver.topics/upsert-group-chat (:chat-id %) (:members-joined %))
group-chats))))
;; Filter db
;; We use two structures for filters:
;; filter/filters -> {"filter-id" filter} which is just a map of filters indexed by filte-id
;; filter/chat-ids -> which is a set of loaded chat-ids, which is set everytime we load
;; a non negotiated filter for a chat.
(defn loaded?
"Given a filter, check if we already loaded it"
[db {:keys [filter-id]}]
(get-in db [:filter/filters filter-id]))
(def not-loaded?
(complement loaded?))
(defn chat-loaded?
[db chat-id]
(get-in db [:filter/chat-ids chat-id]))
(defn new-filters? [db filters]
(some
(partial not-loaded? db)
filters))
(fx/defn set-raw-filter
"Update filter ids cached and set filter in the db"
[{:keys [db]} {:keys [chat-id negotiated? filter-id] :as filter}]
{:db (cond-> (assoc-in db [:filter/filters filter-id] filter)
;; We only set non negotiated filters as negotiated filters are not to
;; be removed
(not negotiated?)
(update :filter/chat-ids (fnil conj #{}) chat-id))})
(fx/defn unset-raw-filter
"Remove filter from db and from chat-id"
[{:keys [db]} {:keys [chat-id filter-id]}]
{:db (-> db
(update :filter/chat-ids disj chat-id)
(update :filter/filters dissoc filter-id))})
(fx/defn add-filter-to-db
"Set the filter in the db and upsert a mailserver topic"
[{:keys [db] :as cofx} filter]
(when (and (not (loaded? db filter))
(not (:ephemeral? filter))
(:listen? filter))
(fx/merge cofx
(set-raw-filter filter)
(upsert-mailserver-topic filter))))
(fx/defn remove-filter-from-db
"Remve the filter from the db"
[cofx filter]
(fx/merge cofx
(unset-raw-filter filter)))
(fx/defn add-filters-to-db [cofx filters]
(apply fx/merge cofx (map add-filter-to-db filters)))
(fx/defn remove-filters-from-db [cofx filters]
(apply fx/merge cofx (map remove-filter-from-db filters)))
(defn non-negotiated-filters-for-chat-id
"Returns all the non-negotiated filters matching chat-id"
[db chat-id]
(filter
(fn [{:keys [negotiated?] :as f}]
(and (= chat-id
(:chat-id f))
(not negotiated?)))
(vals (:filter/filters db))))
;; Filter requests
(defn- ->remove-filter-request
[{:keys [id filter-id]}]
{:chatId id
:filterId filter-id})
(defn- ->filter-request
"Transform input in a filter-request. For a group chat we need a filter-request
for each member."
[{:keys [chat-id
group-chat
members-joined
timeline?
public?]}]
(cond
;;ignore timeline chats
timeline?
nil
(not group-chat)
;; Some legacy one-to-one chats (bots), have not a public key for id, we exclude those
(when (is-public-key? chat-id)
[{:ChatID chat-id
:OneToOne true
:Identity (subs chat-id 2)}])
public?
[{:ChatID chat-id
:OneToOne false}]
:else
(mapcat #(->filter-request {:chat-id %}) members-joined)))
(defn- chats->filter-requests
"Convert a list of active chats to filter requests"
[chats]
(->> chats
(filter :is-active)
(mapcat ->filter-request)))
(defn- contacts->filter-requests
"Convert added contacts to filter requests"
[contacts]
(->> contacts
(filter contact.db/added?)
(map #(hash-map :chat-id (:public-key %)))
(mapcat ->filter-request)))
;; shh filters
(defn responses->filters [{:keys [negotiated
discovery
filterId
chatId
listen
ephemeral
topic
identity]}]
{:chat-id (if (not= identity "") (str "0x" identity) chatId)
:id chatId
:filter-id filterId
:negotiated? negotiated
:ephemeral? ephemeral
:listen? listen
:discovery? discovery
:topic topic})
(defn messenger-started? [db]
(:messenger/started? db))
(fx/defn handle-filters-added
"Called every time we load a filter from statusgo, either from explicit call
or through signals. It stores the filter in the db and upsert the relevant
mailserver topics."
{:events [:filters.callback/filters-added]}
[{:keys [db] :as cofx} filters]
(fx/merge cofx
(mailserver/reset-request-to)
(add-filters-to-db filters)
(upsert-group-chat-topics)
(when (new-filters? db filters)
(mailserver/process-next-messages-request))))
(fx/defn handle-filters [cofx filters]
(handle-filters-added cofx (map responses->filters filters)))
(fx/defn handle-loaded-filter [cofx filter]
(when (and (not (:ephemeral? filter))
(:listen? filter))
(set-raw-filter cofx filter)))
(fx/defn handle-loaded-filters [cofx filters]
(let [processed-filters (map responses->filters filters)]
(apply fx/merge cofx (map handle-loaded-filter processed-filters))))
(fx/defn handle-filters-removed
"Called when we remove a filter from status-go, it will update the mailserver
topics"
{:events [:filters.callback/filters-removed]}
[cofx filters]
(fx/merge cofx
(remove-filters-from-db filters)
(mailserver.topics/delete-many filters)
(mailserver/process-next-messages-request)))
;; Public functions
(fx/defn handle-negotiated-filter
"Check if it's a new filter, if so create an shh filter and process it"
[{:keys [db] :as cofx} {:keys [filters]}]
(let [processed-filters (map #(responses->filters (assoc % :negotiated true)) filters)
new-filters (filter
(partial not-loaded? db)
processed-filters)]
(when (seq new-filters)
{:filters/add-raw-filters
{:filters new-filters}})))
;; Load functions: utility function to load filters
(fx/defn load-chat
"Check if a filter already exists for that chat, otherw load the filter"
[{:keys [db] :as cofx} chat-id]
(when (and (messenger-started? db)
(not (chat-loaded? db chat-id)))
(let [chat (get-in db [:chats chat-id])]
(load-filter-fx (->filter-request chat)))))
(fx/defn load-chats
"Check if a filter already exists for that chat, otherw load the filter"
[{:keys [db] :as cofx} chats]
(let [chats (filter #(chat-loaded? db (:chat-id %)) chats)]
(when (and (messenger-started? db) (seq chats))
(load-filter-fx (chats->filter-requests chats)))))
(fx/defn load-contact
"Check if we already have a filter for that contact, otherwise load the filter
if the contact has been added"
[{:keys [db] :as cofx} contact]
(when-not (chat-loaded? db (:public-key contact))
(load-filter-fx (contacts->filter-requests [contact]))))
(fx/defn load-member
"Check if we already have a filter for that member, otherwise load the filter, regardless of whether is in our contacts"
[{:keys [db] :as cofx} public-key]
(when-not (chat-loaded? db public-key)
(load-filter-fx (->filter-request {:chat-id public-key}))))
(fx/defn load-members
"Load multiple members"
[cofx members]
(apply fx/merge cofx (map load-member members)))
(fx/defn stop-listening
"We can stop listening to contact codes when we don't have any active chat
with the user (one-to-one or group-chat), and it is not in our contacts"
[{:keys [db] :as cofx} chat-id]
(let [my-public-key (multiaccounts.model/current-public-key cofx)
one-to-one? (not (get-in db [:chats chat-id :group-chat]))
public? (get-in db [:chats chat-id :public?])
active-group-chats (filter (fn [{:keys [is-active members members-joined]}]
(and is-active
(contains? members-joined my-public-key)
(contains? members chat-id)))
(vals (:chats db)))]
(when (or public?
(and one-to-one?
(not (contact.db/active? db chat-id))
(not= my-public-key chat-id)
(not (get-in db [:chats chat-id :is-active]))
(empty? active-group-chats)))
(fx/merge cofx
;; we exclude the negotiated filters as those are not to be removed
;; otherwise we might miss messages
(remove-filter-fx
(non-negotiated-filters-for-chat-id db chat-id))))))
(re-frame/reg-fx
:filters/add-raw-filters
(fn [{:keys [filters]}]
(log/debug "PERF" :filters/add-raw-filters)
(filters-added! filters)))
;; Here we stop first polling and then we hit status-go, otherwise it would throw
;; an error trying to poll from a delete filter. If we fail to remove the filter though
;; we should recreate it.
(re-frame/reg-fx
:filters/remove-filters
(fn [[filters]]
(log/debug "removing filters" filters)
(remove-filters-rpc
(map ->remove-filter-request filters)
#(filters-removed! filters)
#(log/error "remove-filters: failed error" %))))
(re-frame/reg-fx
:filters/load-filters
(fn [raw-filters]
(let [all-filters (mapcat first raw-filters)]
(load-filters-rpc
all-filters
#(filters-added! (map responses->filters %))
#(log/error "load-filters: failed error" %)))))

View File

@ -1,158 +0,0 @@
(ns status-im.transport.filters.core-test
(:require [cljs.test :refer-macros [deftest is testing]]
[status-im.utils.fx :as fx]
[status-im.mailserver.topics :as mailserver.topics]
[status-im.transport.filters.core :as transport.filters]))
(def me "me")
(def member-1 "member-1")
(def member-2 "member-2")
(def chat-id "chat-id")
(deftest stop-listening
(testing "the user is in our contacts"
(testing "it does not remove filters"
(is (not (transport.filters/stop-listening
{:db {:contacts/contacts
{chat-id {:system-tags #{:contact/added}}}}}
chat-id)))))
(testing "the user is not in our contacts"
(testing "the user is not in any group chats or 1-to1-"
(testing "it removes the filters"
(let [fx (transport.filters/stop-listening {:db {:filter/filters
{"a" {:chat-id chat-id :filter-id "a"}
"b" {:chat-id chat-id :negotiated? true}}}}
chat-id)]
(is fx)
(is (= fx {:filters/remove-filters [[{:chat-id chat-id :filter-id "a"}]]})))))
(testing "the user is still in some group chats"
(testing "we joined, and group chat is active it does not remove filters"
(let [fx (transport.filters/stop-listening {:db {:multiaccount {:public-key me}
:chats
{chat-id {:is-active true
:members-joined #{me}
:members #{member-1}}}
:filter/filters
{member-1 {}}}}
member-1)]
(is (not fx))))
(testing "we didn't join, it removes transport"
(let [fx (transport.filters/stop-listening {:db {:multiaccount {:public-key me}
:chats
{chat-id {:is-active true
:members-joined #{member-1}
:members #{member-1}}}
:filter/filters
{member-1 {:chat-id member-1 :filter-id "a"}}}}
member-1)]
(is fx)
(is (= fx {:filters/remove-filters [[{:chat-id member-1 :filter-id "a"}]]})))))
(testing "we have a 1-to-1 chat with the user"
(testing "it does not remove filter"
(let [fx (transport.filters/stop-listening {:db {:chats
{member-1 {:is-active true}}}}
member-1)]
(is (not fx)))))))
(deftest chats->filter-requests
(testing "a single one to one chat"
(is (= [{:ChatID "0xchat-id"
:OneToOne true
:Identity "chat-id"}]
(#'status-im.transport.filters.core/chats->filter-requests [{:is-active true
:group-chat false
:chat-id "0xchat-id"}]))))
(testing "a malformed one to one chat"
(is (= []
(#'status-im.transport.filters.core/chats->filter-requests [{:is-active true
:group-chat false
:chat-id "malformed"}]))))
(testing "a single public chat"
(is (= [{:ChatID "chat-id"
:OneToOne false}]
(#'status-im.transport.filters.core/chats->filter-requests [{:is-active true
:group-chat true
:public? true
:chat-id "chat-id"}]))))
(testing "a single group chat"
(is (= [{:ChatID "0xchat-id-2"
:OneToOne true
:Identity "chat-id-2"}
{:ChatID "0xchat-id-1"
:OneToOne true
:Identity "chat-id-1"}]
(#'status-im.transport.filters.core/chats->filter-requests [{:is-active true
:group-chat true
:members-joined #{"0xchat-id-1" "0xchat-id-2"}
:chat-id "chat-id"}])))))
(deftest contacts->filters
(testing "converting contacts to filters"
(is (= [{:ChatID "0xchat-id-2"
:OneToOne true
:Identity "chat-id-2"}]
(#'status-im.transport.filters.core/contacts->filter-requests [{:system-tags #{}
:public-key "0xchat-id-1"}
{:system-tags #{:contact/added}
:public-key "0xchat-id-2"}])))))
(deftest load-member
(testing "it returns fx for a member"
(is (= {:filters/load-filters [[[{:ChatID "0xchat-id-2"
:OneToOne true
:Identity "chat-id-2"}]]]}
(transport.filters/load-member {:db {}} "0xchat-id-2"))))
(testing "merging fx"
(is (=
{:db {}
:filters/load-filters [[[{:ChatID "0xchat-id-1"
:OneToOne true
:Identity "chat-id-1"}]]
[[{:ChatID "0xchat-id-2"
:OneToOne true
:Identity "chat-id-2"}]]]}
(apply fx/merge {:db {}}
(map transport.filters/load-member ["0xchat-id-1" "0xchat-id-2"]))))))
(deftest add-filter-to-db
(with-redefs [mailserver.topics/upsert (fn [{:keys [db]} r] {:db (assoc db :upsert r)})]
(let [expected {:db {:filter/chat-ids #{"chat-id"}
:filter/filters {"filter-id" {:filter-id "filter-id"
:discovery? false
:listen? true
:chat-id "chat-id"
:negotiated? false
:topic "topic"}}
:upsert {:topic "topic"
:negotiated? false
:discovery? false
:chat-ids #{"chat-id"}
:filter-ids #{"filter-id"}}}}]
(is (= expected
(transport.filters/add-filter-to-db {:db {}} {:filter-id "filter-id"
:discovery? false
:chat-id "chat-id"
:negotiated? false
:listen? true
:topic "topic"}))))))
(deftest add-filter-to-db-not-listen
(with-redefs [mailserver.topics/upsert (fn [{:keys [db]} r] {:db (assoc db :upsert r)})]
(is (not (transport.filters/add-filter-to-db {:db {}} {:filter-id "filter-id"
:discovery? false
:chat-id "chat-id"
:negotiated? false
:topic "topic"})))))
(deftest new-filters?
(testing "new-filters?"
(let [db {:filter/filters {"a" {}
"b" {}
"c" {}}}]
(is (not (transport.filters/new-filters? db [{:filter-id "a"}
{:filter-id "b"}
{:filter-id "c"}])))
(is (not (transport.filters/new-filters? db [{:filter-id "a"}])))
(is (transport.filters/new-filters? db [{:filter-id "d"}]))
(is (transport.filters/new-filters? db [{:filter-id "a"}
{:filter-id "d"}])))))

View File

@ -6,7 +6,6 @@
[status-im.contact.core :as models.contact] [status-im.contact.core :as models.contact]
[status-im.communities.core :as models.communities] [status-im.communities.core :as models.communities]
[status-im.pairing.core :as models.pairing] [status-im.pairing.core :as models.pairing]
[status-im.transport.filters.core :as models.filters]
[status-im.data-store.reactions :as data-store.reactions] [status-im.data-store.reactions :as data-store.reactions]
[status-im.data-store.contacts :as data-store.contacts] [status-im.data-store.contacts :as data-store.contacts]
[status-im.data-store.chats :as data-store.chats] [status-im.data-store.chats :as data-store.chats]
@ -36,8 +35,6 @@
^js installations (.-installations response-js) ^js installations (.-installations response-js)
^js messages (.-messages response-js) ^js messages (.-messages response-js)
^js emoji-reactions (.-emojiReactions response-js) ^js emoji-reactions (.-emojiReactions response-js)
^js filters (.-filters response-js)
^js removed-filters (.-removedFilters response-js)
^js invitations (.-invitations response-js) ^js invitations (.-invitations response-js)
^js removed-chats (.-removedChats response-js) ^js removed-chats (.-removedChats response-js)
^js activity-notifications (.-activityCenterNotifications response-js) ^js activity-notifications (.-activityCenterNotifications response-js)
@ -113,20 +110,7 @@
(js-delete response-js "invitations") (js-delete response-js "invitations")
(fx/merge cofx (fx/merge cofx
(process-next response-js sync-handler) (process-next response-js sync-handler)
(models.group/handle-invitations (map data-store.invitations/<-rpc invitations)))) (models.group/handle-invitations (map data-store.invitations/<-rpc invitations)))))))
(seq filters)
(let [filters (types/js->clj filters)]
(js-delete response-js "filters")
(fx/merge cofx
(process-next response-js sync-handler)
(models.filters/handle-filters filters)))
(seq removed-filters)
(let [removed-filters (types/js->clj removed-filters)]
(js-delete response-js "removedFilters")
(fx/merge cofx
(process-next response-js sync-handler)
(models.filters/handle-filters-removed filters))))))
(defn group-by-and-update-unviewed-counts (defn group-by-and-update-unviewed-counts
"group messages by current chat, profile updates, transactions and update unviewed counters in db for not curent chats" "group messages by current chat, profile updates, transactions and update unviewed counters in db for not curent chats"
@ -188,9 +172,7 @@
[{:ms 100 :dispatch [:process-statuses statuses]}]) [{:ms 100 :dispatch [:process-statuses statuses]}])
(when (seq transactions) (when (seq transactions)
(for [transaction-hash transactions] (for [transaction-hash transactions]
{:ms 100 :dispatch [:watch-tx transaction-hash]})) {:ms 100 :dispatch [:watch-tx transaction-hash]})))}
(when (seq chats)
[{:ms 100 :dispatch [:chat/join-times-messages-checked chats]}]))}
(process-response response-js process-async)))) (process-response response-js process-async))))
(fx/defn remove-hash (fx/defn remove-hash

View File

@ -19,7 +19,6 @@
[status-im.ui.components.invite.views :as invite] [status-im.ui.components.invite.views :as invite]
[status-im.ethereum.ens :as ens] [status-im.ethereum.ens :as ens]
[quo.platform :as platform] [quo.platform :as platform]
[status-im.transport.filters.core :as filters]
[status-im.utils.identicon :as identicon] [status-im.utils.identicon :as identicon]
[status-im.ui.components.keyboard-avoid-presentation :as kb-presentation] [status-im.ui.components.keyboard-avoid-presentation :as kb-presentation]
[status-im.ui.components.animation :as animation] [status-im.ui.components.animation :as animation]
@ -82,8 +81,13 @@
(filter (partial search-contacts lower-filter-text) contacts) (filter (partial search-contacts lower-filter-text) contacts)
contacts))) contacts)))
(defn is-public-key? [k]
(and
(string? k)
(string/starts-with? k "0x")))
(defn is-valid-username? [username] (defn is-valid-username? [username]
(let [is-chat-key? (and (filters/is-public-key? username) (let [is-chat-key? (and (is-public-key? username)
(= (count username) 132)) (= (count username) 132))
is-ens? (ens/valid-eth-name-prefix? username)] is-ens? (ens/valid-eth-name-prefix? username)]
(or is-chat-key? is-ens?))) (or is-chat-key? is-ens?)))

View File

@ -83,9 +83,9 @@
:size :small :size :small
:color colors/gray}]]) :color colors/gray}]])
(defn calculate-quiet-time [highest-request-to (defn calculate-quiet-time [synced-to
lowest-request-from] synced-from]
(let [quiet-hours (quot (- highest-request-to lowest-request-from) (let [quiet-hours (quot (- synced-to synced-from)
(* 60 60))] (* 60 60))]
(if (<= quiet-hours 24) (if (<= quiet-hours 24)
(i18n/label :t/quiet-hours (i18n/label :t/quiet-hours
@ -94,22 +94,22 @@
{:quiet-days (quot quiet-hours 24)})))) {:quiet-days (quot quiet-hours 24)}))))
(defview no-messages-community-chat-description-container [chat-id] (defview no-messages-community-chat-description-container [chat-id]
(letsubs [{:keys [highest-request-to lowest-request-from]} (letsubs [{:keys [synced-to synced-from]}
[:mailserver/ranges-by-chat-id chat-id]] [:chats/synced-to-and-from chat-id]]
[react/text {:style (merge style/intro-header-description [react/text {:style (merge style/intro-header-description
{:margin-bottom 36})} {:margin-bottom 36})}
(let [quiet-time (calculate-quiet-time highest-request-to (let [quiet-time (calculate-quiet-time synced-to
lowest-request-from)] synced-from)]
(i18n/label :t/empty-chat-description-community (i18n/label :t/empty-chat-description-community
{:quiet-hours quiet-time}))])) {:quiet-hours quiet-time}))]))
(defview no-messages-private-group-chat-description-container [chat-id] (defview no-messages-private-group-chat-description-container [chat-id]
(letsubs [{:keys [highest-request-to lowest-request-from]} (letsubs [{:keys [synced-to synced-from]}
[:mailserver/ranges-by-chat-id chat-id]] [:chats/synced-to-and-from chat-id]]
[react/nested-text {:style (merge style/intro-header-description [react/nested-text {:style (merge style/intro-header-description
{:margin-bottom 36})} {:margin-bottom 36})}
(let [quiet-time (calculate-quiet-time highest-request-to (let [quiet-time (calculate-quiet-time synced-to
lowest-request-from)] synced-from)]
(i18n/label :t/empty-chat-description-public (i18n/label :t/empty-chat-description-public
{:quiet-hours quiet-time})) {:quiet-hours quiet-time}))
[{:style {:color colors/blue} [{:style {:color colors/blue}

View File

@ -7,47 +7,33 @@
[status-im.ui.screens.chat.styles.input.gap :as style])) [status-im.ui.screens.chat.styles.input.gap :as style]))
(defn on-press (defn on-press
[ids first-gap? idx list-ref chat-id] [chat-id gap-ids]
(fn [] (fn []
(when (and list-ref @list-ref) (re-frame/dispatch [:chat.ui/fill-gaps chat-id gap-ids])))
(.scrollToIndex ^js @list-ref
#js {:index (max 0 (dec idx))
:viewOffset 20
:viewPosition 0.5}))
(if first-gap?
(re-frame/dispatch [:chat.ui/fetch-more chat-id])
(re-frame/dispatch [:chat.ui/fill-gaps ids chat-id]))))
(views/defview gap (views/defview gap
[{:keys [gaps first-gap?]} idx list-ref timeline chat-id] [{:keys [gap-ids chat-id gap-parameters]}]
(views/letsubs [range [:chats/range chat-id] (views/letsubs [in-progress? [:chats/fetching-gap-in-progress?
{:keys [might-have-join-time-messages?]} [:chat-by-id chat-id] gap-ids
in-progress? [:chats/fetching-gap-in-progress?
(if first-gap?
[:first-gap]
(:ids gaps))
chat-id] chat-id]
connected? [:mailserver/connected?]] connected? [:mailserver/connected?]
(let [ids (:ids gaps)] first-gap? (= gap-ids #{:first-gap})]
(when-not (and first-gap? might-have-join-time-messages?)
[react/view {:style (style/gap-container)} [react/view {:style (style/gap-container)}
[react/touchable-highlight [react/touchable-highlight
{:on-press (when (and connected? (not in-progress?)) {:on-press (when (and connected? (not in-progress?))
(on-press ids first-gap? idx list-ref chat-id)) (on-press chat-id gap-ids))
:style style/touchable} :style style/touchable}
[react/view {:style style/label-container} [react/view {:style style/label-container}
(if in-progress? (if in-progress?
[react/activity-indicator] [react/activity-indicator]
[react/nested-text [react/nested-text
{:style (style/gap-text connected?)} {:style (style/gap-text connected?)}
(i18n/label (if first-gap? (i18n/label (if first-gap? :t/load-more-messages :t/fetch-messages))
(if timeline :t/load-more-timeline :t/load-more-messages)
(if timeline :t/fetch-timeline :t/fetch-messages)))
(when first-gap? (when first-gap?
[{:style style/date} [{:style style/date}
(let [date (datetime/timestamp->long-date (let [date (datetime/timestamp->long-date
(* 1000 (:lowest-request-from range)))] (* 1000 (:from gap-parameters)))]
(str (str
"\n" "\n"
(i18n/label :t/load-messages-before (i18n/label :t/load-messages-before
{:date date})))])])]]])))) {:date date})))])])]]]))

View File

@ -12,6 +12,7 @@
[status-im.ui.screens.chat.message.command :as message.command] [status-im.ui.screens.chat.message.command :as message.command]
[status-im.ui.screens.chat.photos :as photos] [status-im.ui.screens.chat.photos :as photos]
[status-im.ui.screens.chat.sheets :as sheets] [status-im.ui.screens.chat.sheets :as sheets]
[status-im.ui.screens.chat.message.gap :as message.gap]
[status-im.ui.screens.chat.styles.message.message :as style] [status-im.ui.screens.chat.styles.message.message :as style]
[status-im.ui.screens.chat.utils :as chat.utils] [status-im.ui.screens.chat.utils :as chat.utils]
[status-im.utils.contenthash :as contenthash] [status-im.utils.contenthash :as contenthash]
@ -316,6 +317,10 @@
[message] [message]
[message.command/command-content message-content-wrapper message]) [message.command/command-content message-content-wrapper message])
(defmethod ->message constants/content-type-gap
[message]
[message.gap/gap message])
(defmethod ->message constants/content-type-system-text [{:keys [content] :as message}] (defmethod ->message constants/content-type-system-text [{:keys [content] :as message}]
[react/view {:accessibility-label :chat-item} [react/view {:accessibility-label :chat-item}
[react/view (style/system-message-body message) [react/view (style/system-message-body message)

View File

@ -123,6 +123,7 @@
(defn chat-intro-header-container (defn chat-intro-header-container
[{:keys [group-chat invitation-admin [{:keys [group-chat invitation-admin
chat-type chat-type
synced-to
color chat-id chat-name color chat-id chat-name
public?]} public?]}
no-messages] no-messages]
@ -139,7 +140,7 @@
:chat-name chat-name :chat-name chat-name
:public? public? :public? public?
:color color :color color
:loading-messages? @(re-frame/subscribe [:chats/might-have-join-time-messages? chat-id]) :loading-messages? (not (pos? synced-to))
:no-messages? no-messages}] :no-messages? no-messages}]
(if group-chat (if group-chat
[chat-intro opts] [chat-intro opts]

View File

@ -1,8 +1,7 @@
(ns status-im.utils.prices (ns status-im.utils.prices
(:require [clojure.string :as string] (:require [clojure.string :as string]
[status-im.utils.http :as http] [status-im.utils.http :as http]
[status-im.utils.types :as types] [status-im.utils.types :as types]))
[taoensso.timbre :as log]))
;; Responsible for interacting with Cryptocompare API to get current prices for ;; Responsible for interacting with Cryptocompare API to get current prices for
;; currencies and tokens. ;; currencies and tokens.
@ -39,10 +38,6 @@
:last-day (:OPEN24HOUR entry)}}))})))) :last-day (:OPEN24HOUR entry)}}))}))))
(defn get-prices [from to mainnet? on-success on-error] (defn get-prices [from to mainnet? on-success on-error]
(log/debug "[prices] get-prices"
"from" from
"to" to
"mainnet?" mainnet?)
(http/get (http/get
(gen-price-url from to) (gen-price-url from to)
(fn [resp] (on-success (format-price-resp resp mainnet?))) (fn [resp] (on-success (format-price-resp resp mainnet?)))

View File

@ -2,7 +2,7 @@
"_comment": "DO NOT EDIT THIS FILE BY HAND. USE 'scripts/update-status-go.sh <tag>' instead", "_comment": "DO NOT EDIT THIS FILE BY HAND. USE 'scripts/update-status-go.sh <tag>' instead",
"owner": "status-im", "owner": "status-im",
"repo": "status-go", "repo": "status-go",
"version": "v0.77.1", "version": "v0.79.0",
"commit-sha1": "6a930ed0c601aca3dd13c9d4dedf3ea2d444848f", "commit-sha1": "d50fee6bb2e392351a5bce6187725ab80c7420eb",
"src-sha256": "0jz4696xm99cy4dxi3dfd0b4rj5pqrsfj92zsfyxilgzjkwyzf78" "src-sha256": "0zx0dr3p5vvr3rbnq0gkvdgzkyj15i41yrlc95c3152mwgkh3bzf"
} }