mirror of
https://github.com/status-im/status-mobile.git
synced 2025-02-26 07:15:53 +00:00
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:
parent
929925af39
commit
dd5c30b7a0
@ -1,6 +1,5 @@
|
||||
(ns status-im.chat.db
|
||||
(:require [clojure.string :as clojure.string]
|
||||
[status-im.mailserver.constants :as mailserver.constants]))
|
||||
(:require [status-im.constants :as constants]))
|
||||
|
||||
(defn group-chat-name
|
||||
[{:keys [public? name]}]
|
||||
@ -48,89 +47,42 @@
|
||||
(conj messages-with-datemarks {:value (:datemark (peek messages-with-datemarks))
|
||||
:type :datemark}))))
|
||||
|
||||
(defn gap? [{:keys [type]}]
|
||||
(= type :gap))
|
||||
(defn last-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
|
||||
[gaps previous-message next-message]
|
||||
(let [previous-timestamp (:whisper-timestamp previous-message)
|
||||
next-whisper-timestamp (:whisper-timestamp next-message)
|
||||
next-timestamp (:timestamp next-message)
|
||||
ignore-next-message? (> (js/Math.abs
|
||||
(- next-whisper-timestamp next-timestamp))
|
||||
120000)]
|
||||
(reduce
|
||||
(fn [acc {:keys [from to id]}]
|
||||
(let [from-ms (* from 1000)
|
||||
to-ms (* to 1000)]
|
||||
(if (and next-message
|
||||
(not ignore-next-message?)
|
||||
(or
|
||||
(and (nil? previous-timestamp)
|
||||
(< from-ms next-whisper-timestamp))
|
||||
(and
|
||||
(< previous-timestamp from-ms)
|
||||
(< to-ms next-whisper-timestamp))
|
||||
(and
|
||||
(< 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 collapse-gaps
|
||||
"collapse-gaps will take an array of messages and collapse any gap next to
|
||||
each other in a single gap.
|
||||
It will also append one last gap if the last message is a non-gap"
|
||||
[messages chat-id synced-from]
|
||||
(let [messages-with-gaps (reduce
|
||||
(fn [acc {:keys [gap-parameters message-id] :as message}]
|
||||
(let [last-element (peek acc)]
|
||||
(cond
|
||||
;; If it's a message, just add
|
||||
(empty? gap-parameters)
|
||||
(conj acc message)
|
||||
|
||||
(defn add-gap [messages gaps]
|
||||
(conj messages
|
||||
{:type :gap
|
||||
:value (clojure.string/join (:ids gaps))
|
||||
:gaps gaps}))
|
||||
;; Both are gaps, merge them
|
||||
(and
|
||||
(seq (:gap-parameters last-element))
|
||||
(seq gap-parameters))
|
||||
(conj (pop acc) (update last-element :gap-ids conj message-id))
|
||||
|
||||
(defn add-gaps
|
||||
"Converts message groups into sequence of messages interspersed with datemarks,
|
||||
with correct user statuses associated into message"
|
||||
[message-list messages-gaps
|
||||
{: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)))
|
||||
;; it's a gap
|
||||
:else
|
||||
(conj acc (assoc message :gap-ids #{message-id})))))
|
||||
[]
|
||||
messages)]
|
||||
;; If it's a gap or the chat is still syncing, do nothing
|
||||
(if (or (nil? synced-from)
|
||||
(:gap-ids (peek messages-with-gaps)))
|
||||
messages-with-gaps
|
||||
(conj messages-with-gaps (last-gap chat-id synced-from)))))
|
||||
|
@ -38,127 +38,3 @@
|
||||
(:datemark m4)))
|
||||
(is (= {:type :datemark
|
||||
: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)))))
|
||||
|
@ -2,12 +2,10 @@
|
||||
(:require [re-frame.core :as re-frame]
|
||||
[taoensso.timbre :as log]
|
||||
[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.messages :as messages-store]
|
||||
[status-im.ethereum.json-rpc :as json-rpc]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im.mailserver.core :as mailserver]
|
||||
[status-im.ui.components.colors :as colors]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.navigation :as navigation]
|
||||
@ -16,8 +14,6 @@
|
||||
[status-im.utils.utils :as utils]
|
||||
[status-im.utils.types :as types]
|
||||
[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.ui.screens.chat.state :as chat.state]))
|
||||
|
||||
@ -83,33 +79,6 @@
|
||||
[{:keys [current-chat-id] :as db} 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
|
||||
[chat-id {:keys [db now]}]
|
||||
(let [name (get-in db [:contacts/contacts chat-id :name])]
|
||||
@ -132,10 +101,7 @@
|
||||
chat-props)
|
||||
new? (not (get-in db [:chats chat-id]))
|
||||
public? (public-chat? chat)]
|
||||
(fx/merge cofx
|
||||
{:db (update-in db [:chats chat-id] merge chat)}
|
||||
(when (and public? new? (not timeline?))
|
||||
(transport.filters/load-chat chat-id)))))
|
||||
{:db (update-in db [:chats chat-id] merge chat)}))
|
||||
|
||||
(defn map-chats [{:keys [db] :as cofx}]
|
||||
(fn [val]
|
||||
@ -155,52 +121,14 @@
|
||||
[{:keys [db] :as cofx} chats]
|
||||
(let [chats (map (map-chats cofx) chats)
|
||||
filtered-chats (filter (filter-chats db) chats)]
|
||||
(fx/merge cofx
|
||||
{:db (update db :chats #(reduce
|
||||
(fn [acc {:keys [chat-id] :as chat}]
|
||||
(update acc chat-id merge chat))
|
||||
%
|
||||
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))
|
||||
{:db (update db :chats #(reduce
|
||||
(fn [acc {:keys [chat-id] :as chat}]
|
||||
(update acc chat-id merge chat))
|
||||
%
|
||||
chats))}))
|
||||
|
||||
(fx/defn clear-history
|
||||
"Clears history of the particular chat"
|
||||
{:events [:chat.ui/clear-history]}
|
||||
[{:keys [db] :as cofx} chat-id remove-chat?]
|
||||
(let [{:keys [last-message public?
|
||||
deleted-at-clock-value]} (get-in db [:chats chat-id])
|
||||
@ -209,24 +137,39 @@
|
||||
(or (:clock-value last-message)
|
||||
deleted-at-clock-value
|
||||
(utils.clocks/send 0)))]
|
||||
(fx/merge
|
||||
cofx
|
||||
{:db (-> db
|
||||
(assoc-in [:messages chat-id] {})
|
||||
(update-in [:message-lists] dissoc chat-id)
|
||||
(update-in [:chats chat-id] merge
|
||||
{:last-message nil
|
||||
:unviewed-messages-count 0
|
||||
:deleted-at-clock-value last-message-clock-value}))}
|
||||
(messages-store/delete-messages-by-chat-id chat-id)
|
||||
#(chats-store/save-chat % (get-in % [:db :chats chat-id]) nil))))
|
||||
{:db (-> db
|
||||
(assoc-in [:messages chat-id] {})
|
||||
(update-in [:message-lists] dissoc chat-id)
|
||||
(update-in [:chats chat-id] merge
|
||||
{:last-message nil
|
||||
:unviewed-messages-count 0
|
||||
:deleted-at-clock-value last-message-clock-value}))}))
|
||||
|
||||
(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
|
||||
"Deactivate chat in db, no side effects"
|
||||
[{:keys [db now] :as cofx} chat-id]
|
||||
{:db (-> db
|
||||
(assoc-in [:chats chat-id :is-active] false)
|
||||
(assoc-in [:current-chat-id] nil))})
|
||||
(fx/merge
|
||||
cofx
|
||||
{:db (-> db
|
||||
(assoc-in [:chats chat-id :is-active] false)
|
||||
(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
|
||||
{:events [:offload-messages]}
|
||||
@ -250,12 +193,8 @@
|
||||
{:events [:chat.ui/remove-chat]}
|
||||
[{:keys [db now] :as cofx} chat-id]
|
||||
(fx/merge cofx
|
||||
(mailserver/remove-gaps chat-id)
|
||||
(mailserver/remove-range chat-id)
|
||||
(deactivate-chat chat-id)
|
||||
(offload-messages chat-id)
|
||||
(clear-history chat-id true)
|
||||
(transport.filters/stop-listening chat-id)
|
||||
(when (not (= (:view-id db) :home))
|
||||
(navigation/navigate-to-cofx :home {}))))
|
||||
|
||||
@ -263,10 +202,7 @@
|
||||
"Takes chat-id and coeffects map, returns effects necessary when navigating to chat"
|
||||
{:events [:chat.ui/preload-chat-data]}
|
||||
[{:keys [db] :as cofx} chat-id]
|
||||
(fx/merge cofx
|
||||
(when-not (or (group-chat? cofx chat-id) (timeline-chat? cofx chat-id))
|
||||
(transport.filters/load-chat chat-id))
|
||||
(loading/load-messages chat-id)))
|
||||
(loading/load-messages cofx chat-id))
|
||||
|
||||
(fx/defn navigate-to-chat
|
||||
"Takes coeffects map and chat-id, returns effects necessary for navigation and preloading data"
|
||||
@ -277,7 +213,20 @@
|
||||
(fn [{:keys [db]}]
|
||||
{:db (assoc db :current-chat-id 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
|
||||
"Start a chat, making sure it exists"
|
||||
@ -285,12 +234,10 @@
|
||||
[{:keys [db] :as cofx} chat-id]
|
||||
;; don't allow to open chat with yourself
|
||||
(when (not= (multiaccounts.model/current-public-key cofx) chat-id)
|
||||
(fx/merge cofx
|
||||
{:dispatch [:chat.ui/navigate-to-chat chat-id]}
|
||||
(upsert-chat {:chat-id chat-id
|
||||
:is-active true}
|
||||
nil)
|
||||
(transport.filters/load-chat chat-id))))
|
||||
{::json-rpc/call [{:method "wakuext_createOneToOneChat"
|
||||
:params [{:id chat-id}]
|
||||
:on-success #(re-frame/dispatch [::one-to-one-chat-created chat-id %])
|
||||
:on-error #(log/error "failed to create one-to-on chat" chat-id %)}]}))
|
||||
|
||||
(defn profile-chat-topic [public-key]
|
||||
(str "@" public-key))
|
||||
@ -298,44 +245,60 @@
|
||||
(defn my-profile-chat-topic [db]
|
||||
(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
|
||||
"Starts a new 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 (active-chat? cofx topic)
|
||||
(when-not dont-navigate?
|
||||
(if navigation-reset?
|
||||
(fx/merge cofx
|
||||
{:dispatch [:chat.ui/navigate-to-chat topic]}
|
||||
(navigation/navigate-to-cofx :home {}))
|
||||
(navigate-to-chat cofx topic)))
|
||||
(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]})))
|
||||
(navigate-to-chat cofx topic))
|
||||
(create-public-chat-go
|
||||
cofx
|
||||
topic
|
||||
opts))
|
||||
{:utils/show-popup {:title (i18n/label :t/cant-open-public-chat)
|
||||
: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
|
||||
"Starts a new profile chat"
|
||||
{:events [:start-profile-chat]}
|
||||
[cofx profile-public-key]
|
||||
(let [topic (profile-chat-topic profile-public-key)]
|
||||
(when-not (active-chat? cofx topic)
|
||||
(fx/merge cofx
|
||||
(add-public-chat topic profile-public-key false)
|
||||
(transport.filters/load-chat topic)))))
|
||||
|
||||
(fx/defn start-timeline-chat
|
||||
"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)))
|
||||
[cofx profile-public-key navigate-to?]
|
||||
(let [chat-id (profile-chat-topic profile-public-key)]
|
||||
(if (active-chat? cofx chat-id)
|
||||
{:dispatch [::profile-chat-created chat-id nil navigate-to?]}
|
||||
{::json-rpc/call [{:method "wakuext_createProfileChat"
|
||||
:params [{:id profile-public-key}]
|
||||
:on-success #(re-frame/dispatch [::profile-chat-created chat-id % navigate-to?])
|
||||
:on-error #(log/error "failed to create profile chat" chat-id %)}]})))
|
||||
|
||||
(fx/defn disable-chat-cooldown
|
||||
"Turns off chat cooldown (protection against message spamming)"
|
||||
@ -357,22 +320,21 @@
|
||||
(log/error "mute chat failed" chat-id error)
|
||||
{: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
|
||||
{:events [::mute-chat-toggled]}
|
||||
[{:keys [db] :as cofx} chat-id muted?]
|
||||
(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?)
|
||||
::json-rpc/call [{:method (json-rpc/call-ext-method method)
|
||||
:params [chat-id]
|
||||
:on-error #(re-frame/dispatch [::mute-chat-failed chat-id muted? %])
|
||||
:on-success #(log/info method "successful" chat-id)}]})))
|
||||
(let [method (if muted? "muteChat" "unmuteChat")]
|
||||
{:db (assoc-in db [:chats chat-id :muted] muted?)
|
||||
::json-rpc/call [{:method (json-rpc/call-ext-method method)
|
||||
:params [chat-id]
|
||||
:on-error #(re-frame/dispatch [::mute-chat-failed chat-id muted? %])
|
||||
:on-success #(re-frame/dispatch [::mute-chat-successful chat-id %])}]}))
|
||||
|
||||
(fx/defn show-profile
|
||||
{:events [:chat.ui/show-profile]}
|
||||
@ -382,10 +344,8 @@
|
||||
(navigation/navigate-to-cofx cofx :profile-stack {:screen :my-profile})
|
||||
(fx/merge
|
||||
cofx
|
||||
{:db (assoc db :contacts/identity identity)
|
||||
:dispatch [:chat.ui/preload-chat-data (profile-chat-topic identity)]}
|
||||
(start-profile-chat identity)
|
||||
(navigation/navigate-to-cofx :profile nil)))))
|
||||
{:db (assoc db :contacts/identity identity)}
|
||||
(start-profile-chat identity true)))))
|
||||
|
||||
(fx/defn clear-history-pressed
|
||||
{:events [:chat.ui/clear-history-pressed]}
|
||||
@ -398,35 +358,61 @@
|
||||
(re-frame/dispatch [:bottom-sheet/hide])
|
||||
(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
|
||||
{:events [:chat.ui/fill-gaps]}
|
||||
[{:keys [db] :as cofx} gap-ids chat-id]
|
||||
(let [topics (mailserver.topics/topics-for-chat db chat-id)
|
||||
gaps (keep
|
||||
(fn [id]
|
||||
(get-in db [:mailserver/gaps chat-id id]))
|
||||
gap-ids)]
|
||||
(mailserver/fill-the-gap
|
||||
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})))
|
||||
[{:keys [db] :as cofx} chat-id gap-ids]
|
||||
(log/info "filling gaps" chat-id gap-ids)
|
||||
(fx/merge cofx
|
||||
{:db (assoc db :mailserver/fetching-gaps-in-progress gap-ids)}
|
||||
(if (= gap-ids #{:first-gap})
|
||||
(sync-chat-from-sync-from chat-id)
|
||||
(fill-gaps chat-id gap-ids))))
|
||||
|
||||
(fx/defn chat-ui-remove-chat-pressed
|
||||
{:events [:chat.ui/remove-chat-pressed]}
|
||||
@ -437,4 +423,4 @@
|
||||
:confirm-button-text (i18n/label :t/delete)
|
||||
:on-accept #(do
|
||||
(re-frame/dispatch [:bottom-sheet/hide])
|
||||
(re-frame/dispatch [:chat.ui/remove-chat chat-id]))}})
|
||||
(re-frame/dispatch [:chat.ui/remove-chat chat-id]))}})
|
||||
|
@ -28,9 +28,7 @@
|
||||
new-chats)
|
||||
chats (merge old-chats chats)]
|
||||
{:db (assoc db :chats chats
|
||||
:chats/loading? false)
|
||||
:dispatch-n [[:chat/start-timeline-chat]
|
||||
[:start-profile-chat (get-in db [:multiaccount :public-key])]]}))
|
||||
:chats/loading? false)}))
|
||||
|
||||
(fx/defn initialize-chats
|
||||
"Initialize persisted chats on startup"
|
||||
@ -115,8 +113,7 @@
|
||||
(when (or first-request cursor)
|
||||
(merge
|
||||
{:db (assoc-in db [:pagination-info chat-id :loading-messages?] true)}
|
||||
{:utils/dispatch-later [{:ms 100 :dispatch [:load-more-reactions cursor chat-id]}
|
||||
{:ms 100 :dispatch [:load-gaps chat-id]}]}
|
||||
{:utils/dispatch-later [{:ms 100 :dispatch [:load-more-reactions cursor chat-id]}]}
|
||||
(data-store.messages/messages-by-chat-id-rpc
|
||||
chat-id
|
||||
cursor
|
||||
|
@ -28,35 +28,11 @@
|
||||
(update-in [:db :messages chat-id] assoc message-id 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
|
||||
"Hide chat message, rebuild message-list"
|
||||
[{:keys [db]} chat-id message-id]
|
||||
;;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))
|
||||
|
||||
(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))
|
||||
(message-list/rebuild-message-list {:db (update-in db [:messages chat-id] dissoc message-id)} chat-id))
|
||||
|
||||
(fx/defn add-senders-to-chat-users
|
||||
{:events [:chat/add-senders-to-chat-users]}
|
||||
@ -209,7 +185,7 @@
|
||||
(fx/merge cofx
|
||||
{:db (update-in db [:messages chat-id] dissoc 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
|
||||
[cofx message]
|
||||
|
@ -1,5 +1,6 @@
|
||||
(ns status-im.chat.models.message-list
|
||||
(:require [status-im.constants :as constants]
|
||||
[status-im.utils.fx :as fx]
|
||||
[status-im.utils.datetime :as time]
|
||||
["functional-red-black-tree" :as rb-tree]))
|
||||
|
||||
@ -180,3 +181,9 @@
|
||||
(if 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]))))})
|
||||
|
@ -1,67 +1,8 @@
|
||||
(ns status-im.chat.models-test
|
||||
(: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.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
|
||||
(let [chat-id "1"
|
||||
cofx {:db {:message-lists {chat-id [{:something "a"}]}
|
||||
@ -96,11 +37,7 @@
|
||||
:last-message nil)
|
||||
chat-id
|
||||
true)]
|
||||
(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))))))))
|
||||
(is (= 42 (get-in actual [:db :chats chat-id :deleted-at-clock-value]))))))))
|
||||
|
||||
(deftest remove-chat-test
|
||||
(let [chat-id "1"
|
||||
@ -110,17 +47,13 @@
|
||||
:chats {chat-id {:last-message {:clock-value 10}}}}}]
|
||||
(testing "it deletes all the messages"
|
||||
(let [actual (chat/remove-chat cofx chat-id)]
|
||||
(is (= {} (get-in actual [:db :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"
|
||||
(let [actual (chat/remove-chat cofx chat-id)]
|
||||
(is (= 10 (get-in actual [:db :chats chat-id :deleted-at-clock-value])))))
|
||||
(testing "it sets the chat as inactive"
|
||||
(let [actual (chat/remove-chat cofx chat-id)]
|
||||
(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))))))))
|
||||
(is (= false (get-in actual [:db :chats chat-id :is-active])))))))
|
||||
|
||||
(deftest multi-user-chat?
|
||||
(let [chat-id "1"]
|
||||
|
@ -12,6 +12,7 @@
|
||||
(def ^:const content-type-image 7)
|
||||
(def ^:const content-type-audio 8)
|
||||
(def ^:const content-type-community 9)
|
||||
(def ^:const content-type-gap 10)
|
||||
|
||||
(def ^:const emoji-reaction-love 1)
|
||||
(def ^:const emoji-reaction-thumbs-up 2)
|
||||
@ -47,6 +48,8 @@
|
||||
(def ^:const message-type-public-group 2)
|
||||
(def ^:const message-type-private-group 3)
|
||||
(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-declined 2)
|
||||
|
@ -4,7 +4,6 @@
|
||||
[status-im.data-store.contacts :as contacts-store]
|
||||
[status-im.ethereum.json-rpc :as json-rpc]
|
||||
[status-im.mailserver.core :as mailserver]
|
||||
[status-im.transport.filters.core :as transport.filters]
|
||||
[status-im.navigation :as navigation]
|
||||
[status-im.utils.fx :as fx]
|
||||
[taoensso.timbre :as log]
|
||||
@ -57,7 +56,6 @@
|
||||
(fx/merge cofx
|
||||
{:db (-> db
|
||||
(update-in [:contacts/contacts public-key] merge contact))}
|
||||
(transport.filters/load-contact contact)
|
||||
(fn [cf]
|
||||
(contacts-store/save-contact cf
|
||||
(get-in cf [:db :contacts/contacts public-key])
|
||||
@ -98,10 +96,11 @@
|
||||
(let [new-contact (update contact
|
||||
:system-tags
|
||||
(fnil #(disj % :contact/added) #{}))]
|
||||
(fx/merge cofx
|
||||
{:db (assoc-in db [:contacts/contacts public-key] new-contact)
|
||||
:dispatch [:offload-messages constants/timeline-chat-id]}
|
||||
(contacts-store/save-contact new-contact nil))))
|
||||
{:db (assoc-in db [:contacts/contacts public-key] new-contact)
|
||||
::json-rpc/call [{:method "wakuext_removeContact"
|
||||
:params [public-key]
|
||||
:on-success #(log/debug "contact removed successfully")}]
|
||||
:dispatch [:offload-messages constants/timeline-chat-id]}))
|
||||
|
||||
(fx/defn create-contact
|
||||
"Create entry in contacts"
|
||||
@ -137,8 +136,7 @@
|
||||
(update-in [:contacts/contacts public-key] merge contact))
|
||||
::json-rpc/call [{:method "wakuext_ensVerified"
|
||||
:params [public-key ens-name]
|
||||
:on-success #(log/debug "ens name verified successuful")}]}
|
||||
(transport.filters/load-contact contact))))
|
||||
:on-success #(log/debug "ens name verified successuful")}]})))
|
||||
|
||||
(fx/defn update-nickname
|
||||
{:events [:contacts/update-nickname]}
|
||||
|
@ -75,6 +75,8 @@
|
||||
type->rpc
|
||||
(clojure.set/rename-keys {:chat-id :id
|
||||
:membership-update-events :membershipUpdateEvents
|
||||
:synced-from :syncedFrom
|
||||
:synced-to :syncedTo
|
||||
:unviewed-messages-count :unviewedMessagesCount
|
||||
:last-message :lastMessage
|
||||
:community-id :communityId
|
||||
@ -83,7 +85,7 @@
|
||||
:last-clock-value :lastClockValue
|
||||
:profile-public-key :profile})
|
||||
(dissoc :public? :group-chat :messages
|
||||
:might-have-join-time-messages? :chat-type
|
||||
:chat-type
|
||||
:contacts :admins :members-joined)))
|
||||
|
||||
(defn <-rpc [chat]
|
||||
@ -92,6 +94,8 @@
|
||||
unmarshal-members
|
||||
(clojure.set/rename-keys {:id :chat-id
|
||||
:communityId :community-id
|
||||
:syncedFrom :synced-from
|
||||
:syncedTo :synced-to
|
||||
:membershipUpdateEvents :membership-update-events
|
||||
:deletedAtClockValue :deleted-at-clock-value
|
||||
:chatType :chat-type
|
||||
@ -104,16 +108,8 @@
|
||||
(update :last-message #(when % (messages/<-rpc %)))
|
||||
(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]}]
|
||||
{::json-rpc/call [{:method (json-rpc/call-ext-method "chats")
|
||||
{::json-rpc/call [{:method (json-rpc/call-ext-method "activeChats")
|
||||
:params []
|
||||
:on-success #(on-success (map <-rpc %))
|
||||
:on-failure #(log/error "failed to fetch chats" 0 -1 %)}]})
|
||||
|
@ -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" %)}]})
|
@ -19,6 +19,7 @@
|
||||
(clojure.set/rename-keys {:id :message-id
|
||||
:whisperTimestamp :whisper-timestamp
|
||||
:commandParameters :command-parameters
|
||||
:gapParameters :gap-parameters
|
||||
:messageType :message-type
|
||||
:localChatId :chat-id
|
||||
:communityId :community-id
|
||||
|
@ -36,6 +36,12 @@
|
||||
"waku_getSymKey" {}
|
||||
"waku_markTrustedPeer" {}
|
||||
"wakuext_post" {}
|
||||
"wakuext_requestAllHistoricMessages" {}
|
||||
"wakuext_fillGaps" {}
|
||||
"wakuext_syncChatFromSyncedFrom" {}
|
||||
"wakuext_createPublicChat" {}
|
||||
"wakuext_createOneToOneChat" {}
|
||||
"wakuext_createProfileChat" {}
|
||||
"wakuext_startMessenger" {}
|
||||
"wakuext_sendPairInstallation" {}
|
||||
"wakuext_syncDevices" {}
|
||||
@ -63,6 +69,7 @@
|
||||
"wakuext_sendContactUpdate" {}
|
||||
"wakuext_sendContactUpdates" {}
|
||||
"wakuext_chats" {}
|
||||
"wakuext_activeChats" {}
|
||||
"wakuext_addSystemMessages" {}
|
||||
"wakuext_deleteMessagesFrom" {}
|
||||
"wakuext_deleteMessagesByChatID" {}
|
||||
@ -76,6 +83,8 @@
|
||||
"wakuext_muteChat" {}
|
||||
"wakuext_unmuteChat" {}
|
||||
"wakuext_contacts" {}
|
||||
"wakuext_removeContact" {}
|
||||
"wakuext_clearHistory" {}
|
||||
"wakuext_prepareContent" {}
|
||||
"wakuext_blockContact" {}
|
||||
"wakuext_updateMailservers" {}
|
||||
@ -85,6 +94,7 @@
|
||||
"wakuext_getLinkPreviewWhitelist" {}
|
||||
"wakuext_getLinkPreviewData" {}
|
||||
"wakuext_requestCommunityInfoFromMailserver" {}
|
||||
"wakuext_deactivateChat" {}
|
||||
;;TODO not used anywhere?
|
||||
"wakuext_deleteChat" {}
|
||||
"wakuext_saveContact" {}
|
||||
|
@ -33,7 +33,6 @@
|
||||
status-im.profile.core
|
||||
status-im.chat.models.images
|
||||
status-im.ui.screens.privacy-and-security-settings.events
|
||||
status-im.mailserver.topics
|
||||
status-im.multiaccounts.login.core
|
||||
status-im.multiaccounts.logout.core
|
||||
status-im.multiaccounts.update.core
|
||||
|
@ -5,9 +5,6 @@
|
||||
[re-frame.core :as re-frame]
|
||||
[status-im.chat.models :as models.chat]
|
||||
[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.utils.fx :as fx]
|
||||
[status-im.constants :as constants]
|
||||
@ -17,8 +14,13 @@
|
||||
{:events [:navigate-chat-updated]}
|
||||
[cofx chat-id]
|
||||
(if (get-in cofx [:db :chats chat-id :is-active])
|
||||
(models.chat/navigate-to-chat cofx chat-id)
|
||||
(navigation/navigate-to-cofx cofx :home {})))
|
||||
(models.chat/navigate-to-chat cofx chat-id)))
|
||||
|
||||
(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
|
||||
{:events [:chat-updated]}
|
||||
@ -35,21 +37,6 @@
|
||||
:js-response true
|
||||
: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
|
||||
{:events [:group-chats.ui/join-pressed]}
|
||||
[_ chat-id]
|
||||
@ -111,7 +98,7 @@
|
||||
{::json-rpc/call [{:method (json-rpc/call-ext-method "leaveGroupChat")
|
||||
:params [nil chat-id true]
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [:chat-updated %])}]})
|
||||
:on-success #(re-frame/dispatch [:chat-removed %])}]})
|
||||
|
||||
(fx/defn remove
|
||||
"Remove chat"
|
||||
@ -119,9 +106,6 @@
|
||||
[cofx chat-id]
|
||||
(fx/merge cofx
|
||||
(models.chat/deactivate-chat chat-id)
|
||||
(models.chat/upsert-chat {:chat-id chat-id
|
||||
:is-active false}
|
||||
nil)
|
||||
(navigation/navigate-to-cofx :home {})))
|
||||
|
||||
(def not-blank?
|
||||
@ -231,4 +215,3 @@
|
||||
:on-accept #(do
|
||||
(re-frame/dispatch [:bottom-sheet/hide])
|
||||
(re-frame/dispatch [:group-chats.ui/leave-chat-confirmed chat-id]))}})
|
||||
|
||||
|
@ -1,13 +1,10 @@
|
||||
(ns ^{:doc "Mailserver events and API"}
|
||||
status-im.mailserver.core
|
||||
(:require [clojure.string :as string]
|
||||
[clojure.set :as clojure.set]
|
||||
[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.i18n.i18n :as i18n]
|
||||
[status-im.mailserver.constants :as constants]
|
||||
[status-im.multiaccounts.model :as multiaccounts.model]
|
||||
[status-im.multiaccounts.update.core :as multiaccounts.update]
|
||||
[status-im.native-module.core :as status]
|
||||
[status-im.node.core :as node]
|
||||
@ -16,9 +13,6 @@
|
||||
[status-im.utils.config :as config]
|
||||
[status-im.utils.fx :as fx]
|
||||
[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]))
|
||||
|
||||
;; How do mailserver work ?
|
||||
@ -34,9 +28,6 @@
|
||||
;; as soon as the mailserver becomes available
|
||||
|
||||
|
||||
(def limit (atom constants/default-limit))
|
||||
(def success-counter (atom 0))
|
||||
|
||||
(defn connected? [db id]
|
||||
(= (:mailserver/current-id db) id))
|
||||
|
||||
@ -77,19 +68,16 @@
|
||||
(fetch db 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
|
||||
;; as sent.
|
||||
(defn update-mailservers! [enodes]
|
||||
(defn update-mailservers! [enodes on-success]
|
||||
(json-rpc/call
|
||||
{:method (json-rpc/call-ext-method "updateMailservers")
|
||||
: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" %)}))
|
||||
|
||||
(defn remove-peer! [enode]
|
||||
@ -104,11 +92,6 @@
|
||||
#(log/info "mailserver: remove-peer success" %)
|
||||
#(log/error "mailserver: remove-peer error" %))))))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:mailserver/add-peer
|
||||
(fn [enode]
|
||||
(add-peer! enode)))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:mailserver/remove-peer
|
||||
(fn [enode]
|
||||
@ -116,61 +99,8 @@
|
||||
|
||||
(re-frame/reg-fx
|
||||
:mailserver/update-mailservers
|
||||
(fn [enodes]
|
||||
(update-mailservers! enodes)))
|
||||
|
||||
(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" %)}}))
|
||||
(fn [[enodes on-success]]
|
||||
(update-mailservers! enodes on-success)))
|
||||
|
||||
(defn registered-peer?
|
||||
"truthy if the enode is a registered peer"
|
||||
@ -181,80 +111,28 @@
|
||||
(defn update-mailserver-state [db 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
|
||||
[{:keys [db now] :as cofx}]
|
||||
(let [{:keys [address sym-key-id generating-sym-key?] :as mailserver}
|
||||
(let [{:keys [address] :as mailserver}
|
||||
(fetch-current db)]
|
||||
(fx/merge
|
||||
cofx
|
||||
{:db (assoc
|
||||
(update-mailserver-state db :connecting)
|
||||
:mailserver/last-connection-attempt now)
|
||||
:mailserver/add-peer address
|
||||
;; 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
|
||||
;; user
|
||||
: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])))
|
||||
{:db (assoc
|
||||
(update-mailserver-state db :connecting)
|
||||
:mailserver/last-connection-attempt now)
|
||||
;; 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
|
||||
;; user
|
||||
:mailserver/update-mailservers [[address]]}))
|
||||
|
||||
(fx/defn disconnect-from-mailserver
|
||||
[{:keys [db] :as cofx}]
|
||||
(let [{:keys [address]} (fetch-current db)
|
||||
{:keys [peers-summary]} db
|
||||
gap-request? (executing-gap-request? db)]
|
||||
{:db (cond-> (dissoc db :mailserver/current-request)
|
||||
gap-request?
|
||||
(-> (assoc :mailserver/fetching-gaps-in-progress {})
|
||||
(dissoc :mailserver/planned-gap-requests)))
|
||||
|
||||
{:keys [peers-summary]} db]
|
||||
{:db (dissoc db :mailserver/current-request :mailserver/fetching-gaps-in-progress)
|
||||
:mailserver/remove-peer address}))
|
||||
|
||||
(defn fetch-use-mailservers? [{:keys [db]}]
|
||||
(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]
|
||||
(.ceil js/Math (/ fleet-size 4)))
|
||||
|
||||
@ -296,6 +174,66 @@
|
||||
has-b-failed? -1)))]
|
||||
(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
|
||||
"Picks a random mailserver amongs the ones with the lowest latency
|
||||
The results with error are ignored
|
||||
@ -329,7 +267,7 @@
|
||||
(when (:multiaccount db)
|
||||
(if (:mailserver/current-id 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
|
||||
address)
|
||||
mailserver-is-registered? (registered-peer? peers-summary
|
||||
@ -340,210 +278,19 @@
|
||||
(not mailserver-is-registered?))]
|
||||
(cond
|
||||
mailserver-added?
|
||||
(mark-trusted-peer cofx)
|
||||
(connected-to-mailserver cofx)
|
||||
mailserver-removed?
|
||||
(connect-to-mailserver cofx)))
|
||||
;; if there is no current mailserver defined
|
||||
;; we set it first
|
||||
(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
|
||||
{:events [:mailserver.callback/request-success]}
|
||||
[{{:keys [chats] :as db} :db} {:keys [request-id topics]}]
|
||||
(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]
|
||||
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))))
|
||||
{:db (assoc-in db [:mailserver/current-request :request-id]
|
||||
request-id)}))
|
||||
|
||||
(fx/defn update-use-mailservers
|
||||
{:events [:mailserver.ui/use-history-switch-pressed]}
|
||||
@ -606,295 +353,25 @@
|
||||
(fx/defn check-connection
|
||||
"Check connection checks that the connection is successfully connected,
|
||||
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}]
|
||||
;; check if logged into multiaccount
|
||||
(when (contains? db :multiaccount)
|
||||
(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
|
||||
(not= :connected (:mailserver/state db))
|
||||
(not= :connected (:mailserver/state db))
|
||||
;; We either never tried to connect to this peer
|
||||
(or (nil? last-connection-attempt)
|
||||
(or (nil? last-connection-attempt)
|
||||
;; 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
|
||||
(change-mailserver cofx)))))
|
||||
|
||||
(fx/defn reset-request-to
|
||||
[{:keys [db]}]
|
||||
{:db (dissoc db :mailserver/request-to)})
|
||||
|
||||
(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))))))))
|
||||
(change-mailserver cofx)
|
||||
;; Just make sure it's set
|
||||
(let [{:keys [address] :as mailserver}
|
||||
(fetch-current db)]
|
||||
(when address
|
||||
{:mailserver/update-mailservers [[address]]}))))))
|
||||
|
||||
(fx/defn retry-next-messages-request
|
||||
{: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
|
||||
(fx/defn handle-request-error
|
||||
[{:keys [db]} error]
|
||||
{:mailserver/decrease-limit []
|
||||
:db (-> db
|
||||
{:db (-> db
|
||||
(assoc :mailserver/request-error error)
|
||||
(dissoc :mailserver/current-request
|
||||
: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
|
||||
{:events [:mailserver.ui/request-error-pressed]}
|
||||
[{:keys [db]}]
|
||||
@ -967,87 +400,11 @@
|
||||
:on-accept #(re-frame/dispatch [:mailserver.ui/retry-request-pressed])
|
||||
: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
|
||||
[cofx]
|
||||
[{:keys [db] :as cofx}]
|
||||
(fx/merge cofx
|
||||
{:mailserver/set-limit constants/default-limit}
|
||||
{:db db}
|
||||
(set-current-mailserver)
|
||||
(reset-request-to)
|
||||
(process-next-messages-request)))
|
||||
|
||||
(def enode-address-regex
|
||||
@ -1256,36 +613,9 @@
|
||||
{})
|
||||
(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
|
||||
{:events [:mailserver.ui/add-pressed]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(fx/merge cofx
|
||||
{:db (dissoc db :mailserver.edit/mailserver)}
|
||||
(navigation/navigate-to-cofx :edit-mailserver nil)))
|
||||
(navigation/navigate-to-cofx :edit-mailserver nil)))
|
||||
|
@ -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))))))
|
@ -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?]))))
|
@ -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"]))))))
|
@ -35,7 +35,6 @@
|
||||
[status-im.chat.models.link-preview :as link-preview]
|
||||
[status-im.utils.mobile-sync :as utils.mobile-sync]
|
||||
[status-im.async-storage.core :as async-storage]
|
||||
[status-im.chat.models :as chat.models]
|
||||
[status-im.notifications-center.core :as notifications-center]))
|
||||
|
||||
(re-frame/reg-fx
|
||||
@ -320,9 +319,8 @@
|
||||
:dispatch-later [{:ms 2000 :dispatch [::initialize-wallet accounts nil nil (:recovered multiaccount) true]}]}
|
||||
(finish-keycard-setup)
|
||||
(transport/start-messenger)
|
||||
(chat.loading/initialize-chats)
|
||||
(communities/fetch)
|
||||
(chat.models/start-timeline-chat)
|
||||
(chat.models/start-profile-chat (:public-key multiaccount))
|
||||
(multiaccounts/switch-preview-privacy-mode-flag)
|
||||
(link-preview/request-link-preview-whitelist)
|
||||
(logging/set-log-level (:log-level multiaccount)))))
|
||||
|
@ -3,8 +3,6 @@
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im.mailserver.core :as mailserver]
|
||||
[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.notifications.local :as local-notifications]
|
||||
[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)
|
||||
"message.delivered" (let [{:keys [chatID messageID]} (js->clj event-js :keywordize-keys true)]
|
||||
(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))
|
||||
"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))
|
||||
"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)
|
||||
"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))
|
||||
|
@ -136,8 +136,6 @@
|
||||
(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/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
|
||||
(reg-root-key-sub ::contacts :contacts/contacts)
|
||||
@ -763,6 +761,20 @@
|
||||
(fn [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
|
||||
:chats/current-raw-chat
|
||||
:<- [::chats]
|
||||
@ -826,7 +838,7 @@
|
||||
:chats/current-chat-chat-view
|
||||
:<- [:chats/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
|
||||
:current-chat/metadata
|
||||
@ -871,24 +883,6 @@
|
||||
current-public-key
|
||||
(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
|
||||
:mailserver/current-name
|
||||
:<- [:mailserver/current-id]
|
||||
@ -915,12 +909,6 @@
|
||||
(fn [chats [_ chat-id]]
|
||||
(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
|
||||
:chats/message-list
|
||||
:<- [::message-lists]
|
||||
@ -948,16 +936,18 @@
|
||||
(fn [[_ chat-id] _]
|
||||
[(re-frame/subscribe [:chats/message-list chat-id])
|
||||
(re-frame/subscribe [:chats/chat-messages chat-id])
|
||||
(re-frame/subscribe [:chats/messages-gaps chat-id])
|
||||
(re-frame/subscribe [:chats/range chat-id])
|
||||
(re-frame/subscribe [:chats/all-loaded? chat-id])
|
||||
(re-frame/subscribe [:chats/public? chat-id])])
|
||||
(fn [[message-list messages messages-gaps range all-loaded? public?]]
|
||||
(re-frame/subscribe [:chats/loading-messages? chat-id])
|
||||
(re-frame/subscribe [:chats/synced-from chat-id])])
|
||||
(fn [[message-list messages loading-messages? synced-from] [_ chat-id]]
|
||||
;;TODO (perf)
|
||||
(-> (models.message-list/->seq message-list)
|
||||
(chat.db/add-datemarks)
|
||||
(hydrate-messages messages)
|
||||
(chat.db/add-gaps messages-gaps range all-loaded? public?))))
|
||||
(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)
|
||||
(hydrate-messages messages)
|
||||
(chat.db/collapse-gaps chat-id synced-from))))))
|
||||
|
||||
;;we want to keep data unchanged so react doesn't change component when we leave screen
|
||||
(def memo-chat-messages-stream (atom nil))
|
||||
@ -2133,8 +2123,8 @@
|
||||
(re-frame/reg-sub
|
||||
:chats/fetching-gap-in-progress?
|
||||
:<- [:mailserver/fetching-gaps-in-progress]
|
||||
(fn [gaps [_ ids chat-id]]
|
||||
(seq (select-keys (get gaps chat-id) ids))))
|
||||
(fn [gaps [_ ids _]]
|
||||
(seq (select-keys gaps ids))))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:mailserver/fetching?
|
||||
|
@ -8,7 +8,6 @@
|
||||
[status-im.utils.fx :as fx]
|
||||
[status-im.utils.handlers :as handlers]
|
||||
[status-im.utils.publisher :as publisher]
|
||||
[status-im.transport.filters.core :as transport.filters]
|
||||
status-im.transport.shh
|
||||
[taoensso.timbre :as log]
|
||||
[status-im.utils.universal-links.core :as universal-links]))
|
||||
@ -49,27 +48,6 @@
|
||||
db
|
||||
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
|
||||
"We should only start receiving messages/processing topics once all the
|
||||
initializiation is completed, otherwise we might receive messages/topics
|
||||
@ -81,18 +59,12 @@
|
||||
|
||||
(fx/defn messenger-started
|
||||
{:events [::messenger-started]}
|
||||
[{:keys [db] :as cofx} {:keys [filters
|
||||
mailserverTopics
|
||||
mailservers
|
||||
mailserverRanges] :as response}]
|
||||
[{:keys [db] :as cofx} {:keys [mailservers] :as response}]
|
||||
(log/info "Messenger started")
|
||||
(fx/merge cofx
|
||||
{:db (-> db
|
||||
(assoc :messenger/started? true)
|
||||
(add-mailserver-ranges mailserverRanges)
|
||||
(add-mailserver-topics mailserverTopics)
|
||||
(add-custom-mailservers mailservers))}
|
||||
(transport.filters/handle-loaded-filters filters)
|
||||
(fetch-node-info-fx)
|
||||
(pairing/init)
|
||||
(publisher/start-fx)
|
||||
|
@ -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" %)))))
|
@ -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"}])))))
|
@ -6,7 +6,6 @@
|
||||
[status-im.contact.core :as models.contact]
|
||||
[status-im.communities.core :as models.communities]
|
||||
[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.contacts :as data-store.contacts]
|
||||
[status-im.data-store.chats :as data-store.chats]
|
||||
@ -36,8 +35,6 @@
|
||||
^js installations (.-installations response-js)
|
||||
^js messages (.-messages 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 removed-chats (.-removedChats response-js)
|
||||
^js activity-notifications (.-activityCenterNotifications response-js)
|
||||
@ -113,20 +110,7 @@
|
||||
(js-delete response-js "invitations")
|
||||
(fx/merge cofx
|
||||
(process-next response-js sync-handler)
|
||||
(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))))))
|
||||
(models.group/handle-invitations (map data-store.invitations/<-rpc invitations)))))))
|
||||
|
||||
(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"
|
||||
@ -188,9 +172,7 @@
|
||||
[{:ms 100 :dispatch [:process-statuses statuses]}])
|
||||
(when (seq transactions)
|
||||
(for [transaction-hash transactions]
|
||||
{:ms 100 :dispatch [:watch-tx transaction-hash]}))
|
||||
(when (seq chats)
|
||||
[{:ms 100 :dispatch [:chat/join-times-messages-checked chats]}]))}
|
||||
{:ms 100 :dispatch [:watch-tx transaction-hash]})))}
|
||||
(process-response response-js process-async))))
|
||||
|
||||
(fx/defn remove-hash
|
||||
|
@ -19,7 +19,6 @@
|
||||
[status-im.ui.components.invite.views :as invite]
|
||||
[status-im.ethereum.ens :as ens]
|
||||
[quo.platform :as platform]
|
||||
[status-im.transport.filters.core :as filters]
|
||||
[status-im.utils.identicon :as identicon]
|
||||
[status-im.ui.components.keyboard-avoid-presentation :as kb-presentation]
|
||||
[status-im.ui.components.animation :as animation]
|
||||
@ -82,8 +81,13 @@
|
||||
(filter (partial search-contacts lower-filter-text) contacts)
|
||||
contacts)))
|
||||
|
||||
(defn is-public-key? [k]
|
||||
(and
|
||||
(string? k)
|
||||
(string/starts-with? k "0x")))
|
||||
|
||||
(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))
|
||||
is-ens? (ens/valid-eth-name-prefix? username)]
|
||||
(or is-chat-key? is-ens?)))
|
||||
@ -299,4 +303,4 @@
|
||||
[nickname-input entered-nickname]
|
||||
[react/text {:style {:align-self :flex-end :margin-top 16
|
||||
:color colors/gray}}
|
||||
(str (count @entered-nickname) " / 32")]]]))
|
||||
(str (count @entered-nickname) " / 32")]]]))
|
||||
|
@ -83,9 +83,9 @@
|
||||
:size :small
|
||||
:color colors/gray}]])
|
||||
|
||||
(defn calculate-quiet-time [highest-request-to
|
||||
lowest-request-from]
|
||||
(let [quiet-hours (quot (- highest-request-to lowest-request-from)
|
||||
(defn calculate-quiet-time [synced-to
|
||||
synced-from]
|
||||
(let [quiet-hours (quot (- synced-to synced-from)
|
||||
(* 60 60))]
|
||||
(if (<= quiet-hours 24)
|
||||
(i18n/label :t/quiet-hours
|
||||
@ -94,22 +94,22 @@
|
||||
{:quiet-days (quot quiet-hours 24)}))))
|
||||
|
||||
(defview no-messages-community-chat-description-container [chat-id]
|
||||
(letsubs [{:keys [highest-request-to lowest-request-from]}
|
||||
[:mailserver/ranges-by-chat-id chat-id]]
|
||||
(letsubs [{:keys [synced-to synced-from]}
|
||||
[:chats/synced-to-and-from chat-id]]
|
||||
[react/text {:style (merge style/intro-header-description
|
||||
{:margin-bottom 36})}
|
||||
(let [quiet-time (calculate-quiet-time highest-request-to
|
||||
lowest-request-from)]
|
||||
(let [quiet-time (calculate-quiet-time synced-to
|
||||
synced-from)]
|
||||
(i18n/label :t/empty-chat-description-community
|
||||
{:quiet-hours quiet-time}))]))
|
||||
|
||||
(defview no-messages-private-group-chat-description-container [chat-id]
|
||||
(letsubs [{:keys [highest-request-to lowest-request-from]}
|
||||
[:mailserver/ranges-by-chat-id chat-id]]
|
||||
(letsubs [{:keys [synced-to synced-from]}
|
||||
[:chats/synced-to-and-from chat-id]]
|
||||
[react/nested-text {:style (merge style/intro-header-description
|
||||
{:margin-bottom 36})}
|
||||
(let [quiet-time (calculate-quiet-time highest-request-to
|
||||
lowest-request-from)]
|
||||
(let [quiet-time (calculate-quiet-time synced-to
|
||||
synced-from)]
|
||||
(i18n/label :t/empty-chat-description-public
|
||||
{:quiet-hours quiet-time}))
|
||||
[{:style {:color colors/blue}
|
||||
|
@ -7,47 +7,33 @@
|
||||
[status-im.ui.screens.chat.styles.input.gap :as style]))
|
||||
|
||||
(defn on-press
|
||||
[ids first-gap? idx list-ref chat-id]
|
||||
[chat-id gap-ids]
|
||||
(fn []
|
||||
(when (and list-ref @list-ref)
|
||||
(.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]))))
|
||||
(re-frame/dispatch [:chat.ui/fill-gaps chat-id gap-ids])))
|
||||
|
||||
(views/defview gap
|
||||
[{:keys [gaps first-gap?]} idx list-ref timeline chat-id]
|
||||
(views/letsubs [range [:chats/range chat-id]
|
||||
{:keys [might-have-join-time-messages?]} [:chat-by-id chat-id]
|
||||
in-progress? [:chats/fetching-gap-in-progress?
|
||||
(if first-gap?
|
||||
[:first-gap]
|
||||
(:ids gaps))
|
||||
[{:keys [gap-ids chat-id gap-parameters]}]
|
||||
(views/letsubs [in-progress? [:chats/fetching-gap-in-progress?
|
||||
gap-ids
|
||||
chat-id]
|
||||
connected? [:mailserver/connected?]]
|
||||
(let [ids (:ids gaps)]
|
||||
(when-not (and first-gap? might-have-join-time-messages?)
|
||||
[react/view {:style (style/gap-container)}
|
||||
[react/touchable-highlight
|
||||
{:on-press (when (and connected? (not in-progress?))
|
||||
(on-press ids first-gap? idx list-ref chat-id))
|
||||
:style style/touchable}
|
||||
[react/view {:style style/label-container}
|
||||
(if in-progress?
|
||||
[react/activity-indicator]
|
||||
[react/nested-text
|
||||
{:style (style/gap-text connected?)}
|
||||
(i18n/label (if first-gap?
|
||||
(if timeline :t/load-more-timeline :t/load-more-messages)
|
||||
(if timeline :t/fetch-timeline :t/fetch-messages)))
|
||||
(when first-gap?
|
||||
[{:style style/date}
|
||||
(let [date (datetime/timestamp->long-date
|
||||
(* 1000 (:lowest-request-from range)))]
|
||||
(str
|
||||
"\n"
|
||||
(i18n/label :t/load-messages-before
|
||||
{:date date})))])])]]]))))
|
||||
connected? [:mailserver/connected?]
|
||||
first-gap? (= gap-ids #{:first-gap})]
|
||||
[react/view {:style (style/gap-container)}
|
||||
[react/touchable-highlight
|
||||
{:on-press (when (and connected? (not in-progress?))
|
||||
(on-press chat-id gap-ids))
|
||||
:style style/touchable}
|
||||
[react/view {:style style/label-container}
|
||||
(if in-progress?
|
||||
[react/activity-indicator]
|
||||
[react/nested-text
|
||||
{:style (style/gap-text connected?)}
|
||||
(i18n/label (if first-gap? :t/load-more-messages :t/fetch-messages))
|
||||
(when first-gap?
|
||||
[{:style style/date}
|
||||
(let [date (datetime/timestamp->long-date
|
||||
(* 1000 (:from gap-parameters)))]
|
||||
(str
|
||||
"\n"
|
||||
(i18n/label :t/load-messages-before
|
||||
{:date date})))])])]]]))
|
||||
|
@ -12,6 +12,7 @@
|
||||
[status-im.ui.screens.chat.message.command :as message.command]
|
||||
[status-im.ui.screens.chat.photos :as photos]
|
||||
[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.utils :as chat.utils]
|
||||
[status-im.utils.contenthash :as contenthash]
|
||||
@ -316,6 +317,10 @@
|
||||
[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}]
|
||||
[react/view {:accessibility-label :chat-item}
|
||||
[react/view (style/system-message-body message)
|
||||
|
@ -123,6 +123,7 @@
|
||||
(defn chat-intro-header-container
|
||||
[{:keys [group-chat invitation-admin
|
||||
chat-type
|
||||
synced-to
|
||||
color chat-id chat-name
|
||||
public?]}
|
||||
no-messages]
|
||||
@ -139,7 +140,7 @@
|
||||
:chat-name chat-name
|
||||
:public? public?
|
||||
: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}]
|
||||
(if group-chat
|
||||
[chat-intro opts]
|
||||
|
@ -1,8 +1,7 @@
|
||||
(ns status-im.utils.prices
|
||||
(:require [clojure.string :as string]
|
||||
[status-im.utils.http :as http]
|
||||
[status-im.utils.types :as types]
|
||||
[taoensso.timbre :as log]))
|
||||
[status-im.utils.types :as types]))
|
||||
|
||||
;; Responsible for interacting with Cryptocompare API to get current prices for
|
||||
;; currencies and tokens.
|
||||
@ -39,10 +38,6 @@
|
||||
:last-day (:OPEN24HOUR entry)}}))}))))
|
||||
|
||||
(defn get-prices [from to mainnet? on-success on-error]
|
||||
(log/debug "[prices] get-prices"
|
||||
"from" from
|
||||
"to" to
|
||||
"mainnet?" mainnet?)
|
||||
(http/get
|
||||
(gen-price-url from to)
|
||||
(fn [resp] (on-success (format-price-resp resp mainnet?)))
|
||||
|
@ -2,7 +2,7 @@
|
||||
"_comment": "DO NOT EDIT THIS FILE BY HAND. USE 'scripts/update-status-go.sh <tag>' instead",
|
||||
"owner": "status-im",
|
||||
"repo": "status-go",
|
||||
"version": "v0.77.1",
|
||||
"commit-sha1": "6a930ed0c601aca3dd13c9d4dedf3ea2d444848f",
|
||||
"src-sha256": "0jz4696xm99cy4dxi3dfd0b4rj5pqrsfj92zsfyxilgzjkwyzf78"
|
||||
"version": "v0.79.0",
|
||||
"commit-sha1": "d50fee6bb2e392351a5bce6187725ab80c7420eb",
|
||||
"src-sha256": "0zx0dr3p5vvr3rbnq0gkvdgzkyj15i41yrlc95c3152mwgkh3bzf"
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user