Move mailserver logic/gaps/ranges to status-go

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

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

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

View File

@ -1,6 +1,5 @@
(ns status-im.chat.db
(: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)))))

View File

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

View File

@ -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]))}})

View File

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

View File

@ -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]

View File

@ -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]))))})

View File

@ -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"]

View File

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

View File

@ -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]}

View File

@ -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 %)}]})

View File

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

View File

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

View File

@ -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" {}

View File

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

View File

@ -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]))}})

View File

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

View File

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

View File

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

View File

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

View File

@ -35,7 +35,6 @@
[status-im.chat.models.link-preview :as link-preview]
[status-im.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)))))

View File

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

View File

@ -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?

View File

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

View File

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

View File

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

View File

@ -6,7 +6,6 @@
[status-im.contact.core :as models.contact]
[status-im.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

View File

@ -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")]]]))

View File

@ -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}

View File

@ -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})))])])]]]))

View File

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

View File

@ -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]

View File

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

View File

@ -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"
}