Fetch missed range of messages
This commit is contained in:
parent
fbda69ff78
commit
a4d8f57b09
|
@ -5,7 +5,8 @@
|
|||
[status-im.chat.commands.core :as commands]
|
||||
[status-im.chat.commands.input :as commands.input]
|
||||
[status-im.group-chats.db :as group-chats.db]
|
||||
[status-im.utils.gfycat.core :as gfycat]))
|
||||
[status-im.utils.gfycat.core :as gfycat]
|
||||
[status-im.transport.partitioned-topic :as topic]))
|
||||
|
||||
(defn group-chat-name
|
||||
[{:keys [public? name]}]
|
||||
|
@ -49,11 +50,28 @@
|
|||
{}
|
||||
chats))
|
||||
|
||||
(defn topic-by-current-chat
|
||||
[{:keys [current-chat-id chats] :as db}]
|
||||
(let [{:keys [public?]} (get chats current-chat-id)
|
||||
public-key (get-in db [:account/account :public-key])]
|
||||
(if public?
|
||||
(get-in db [:transport/chats current-chat-id :topic])
|
||||
(topic/public-key->discovery-topic-hash public-key))))
|
||||
|
||||
(defn messages-gap
|
||||
[mailserver-topics topic]
|
||||
(let [{:keys [gap-from gap-to]}
|
||||
(get mailserver-topics topic)]
|
||||
{:from gap-from
|
||||
:to gap-to
|
||||
:exists? (and gap-from gap-to
|
||||
(> gap-to gap-from))}))
|
||||
|
||||
(defn sort-message-groups
|
||||
"Sorts message groups according to timestamp of first message in group"
|
||||
[message-groups messages]
|
||||
(sort-by
|
||||
(comp unchecked-negate :timestamp (partial get messages) :message-id first second)
|
||||
(comp :timestamp (partial get messages) :message-id first second)
|
||||
message-groups))
|
||||
|
||||
(defn quoted-message-data
|
||||
|
@ -64,26 +82,92 @@
|
|||
{:from from
|
||||
:text (:text content)}))
|
||||
|
||||
(defn messages-with-datemarks-and-statuses
|
||||
"Converts message groups into sequence of messages interspersed with datemarks,
|
||||
with correct user statuses associated into message"
|
||||
[message-groups messages message-statuses referenced-messages]
|
||||
(mapcat (fn [[datemark message-references]]
|
||||
(into (list {:value datemark
|
||||
:type :datemark})
|
||||
(map (fn [{:keys [message-id timestamp-str]}]
|
||||
(defn add-datemark
|
||||
[[datemark
|
||||
message-references]]
|
||||
(let [{:keys [whisper-timestamp timestamp]} (first message-references)]
|
||||
(conj message-references
|
||||
{:value datemark
|
||||
:type :datemark
|
||||
:whisper-timestamp whisper-timestamp
|
||||
:timestamp timestamp})))
|
||||
|
||||
(defn datemark? [{:keys [type]}]
|
||||
(= type :datemark))
|
||||
|
||||
(defn transform-message
|
||||
[messages message-statuses referenced-messages]
|
||||
(fn [{:keys [message-id timestamp-str] :as reference}]
|
||||
(if (datemark? reference)
|
||||
reference
|
||||
(let [{:keys [content] :as message} (get messages message-id)
|
||||
{:keys [response-to response-to-v2]} content
|
||||
quote (some-> (or response-to-v2 response-to)
|
||||
(quoted-message-data messages referenced-messages))]
|
||||
(cond-> (-> message
|
||||
(update :content dissoc :response-to :response-to-v2)
|
||||
(assoc :datemark datemark
|
||||
:timestamp-str timestamp-str
|
||||
(assoc :timestamp-str timestamp-str
|
||||
:user-statuses (get message-statuses message-id)))
|
||||
quote ;; quoted message reference
|
||||
(assoc-in [:content :response-to] quote)))))
|
||||
message-references))
|
||||
;; quoted message reference
|
||||
quote
|
||||
(assoc-in [:content :response-to] quote))))))
|
||||
|
||||
(defn check-gap
|
||||
[{:keys [exists? from]} previous-message next-message gap-added?]
|
||||
(let [previous-timestamp (:whisper-timestamp previous-message)
|
||||
next-whisper-timestamp (:whisper-timestamp next-message)
|
||||
next-timestamp (quot (:timestamp next-message) 1000)
|
||||
ignore-next-message? (> (js/Math.abs
|
||||
(- next-whisper-timestamp next-timestamp))
|
||||
120)]
|
||||
(and (not gap-added?)
|
||||
(or (and exists? previous-timestamp next-whisper-timestamp
|
||||
(not ignore-next-message?)
|
||||
(< previous-timestamp from next-whisper-timestamp))
|
||||
(and exists? (nil? next-message))))))
|
||||
|
||||
(defn add-gap [messages gap]
|
||||
(conj messages
|
||||
{:type :gap
|
||||
:value (str (:from gap))}))
|
||||
|
||||
(defn messages-with-datemarks-and-statuses
|
||||
"Converts message groups into sequence of messages interspersed with datemarks,
|
||||
with correct user statuses associated into message"
|
||||
[message-groups messages message-statuses referenced-messages messages-gap]
|
||||
(transduce
|
||||
(comp
|
||||
(mapcat add-datemark)
|
||||
(map (transform-message messages message-statuses referenced-messages)))
|
||||
(completing
|
||||
(fn [{:keys [messages datemark-reference previous-message gap-added?]}
|
||||
message]
|
||||
(let [new-datemark? (datemark? message)
|
||||
add-gap? (check-gap messages-gap previous-message message gap-added?)]
|
||||
{:messages (cond-> messages
|
||||
|
||||
add-gap?
|
||||
(add-gap messages-gap)
|
||||
|
||||
:always
|
||||
(conj
|
||||
(cond-> message
|
||||
(not new-datemark?)
|
||||
(assoc
|
||||
:datemark
|
||||
(:value datemark-reference)))))
|
||||
:previous-message message
|
||||
:datemark-reference (if new-datemark?
|
||||
message
|
||||
datemark-reference)
|
||||
:gap-added? (or gap-added? add-gap?)}))
|
||||
(fn [{:keys [messages previous-message gap-added?]}]
|
||||
(let [add-gap? (check-gap messages-gap previous-message nil gap-added?)]
|
||||
(cond-> messages
|
||||
add-gap?
|
||||
(add-gap messages-gap)))))
|
||||
{:messages (list)
|
||||
:previous-message nil}
|
||||
message-groups))
|
||||
|
||||
(defn- set-previous-message-info [stream]
|
||||
|
|
|
@ -27,9 +27,11 @@
|
|||
(update-in db [:chats chat-id :message-groups datemark]
|
||||
(fn [message-references]
|
||||
(->> grouped-messages
|
||||
(map (fn [{:keys [message-id timestamp]}]
|
||||
(map (fn [{:keys [message-id timestamp whisper-timestamp]}]
|
||||
{:message-id message-id
|
||||
:timestamp-str (time/timestamp->time timestamp)}))
|
||||
:timestamp-str (time/timestamp->time timestamp)
|
||||
:timestamp timestamp
|
||||
:whisper-timestamp whisper-timestamp}))
|
||||
(into (or message-references '()))
|
||||
(sort-references (get-in db [:chats chat-id :messages]))))))
|
||||
db
|
||||
|
|
|
@ -273,7 +273,7 @@
|
|||
(get all-chats chat-id)
|
||||
{:keys [content content-type clock-value]}
|
||||
(->> (chat.db/sort-message-groups message-groups messages)
|
||||
first
|
||||
last
|
||||
second
|
||||
last
|
||||
:message-id
|
||||
|
|
|
@ -166,17 +166,37 @@
|
|||
(fn [{:keys [referenced-messages]}]
|
||||
(or referenced-messages {})))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:chats/current-chat-topic
|
||||
(fn [db]
|
||||
(chat.db/topic-by-current-chat db)))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:chats/messages-gap
|
||||
:<- [:get-in [:mailserver/topics]]
|
||||
:<- [:chats/current-chat-topic]
|
||||
(fn [[mailserver-topics topic]]
|
||||
(chat.db/messages-gap mailserver-topics topic)))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:chats/current-chat-messages-stream
|
||||
:<- [:chats/current-chat-messages]
|
||||
:<- [:chats/current-chat-message-groups]
|
||||
:<- [:chats/current-chat-message-statuses]
|
||||
:<- [:chats/current-chat-referenced-messages]
|
||||
(fn [[messages message-groups message-statuses referenced-messages]]
|
||||
:<- [:chats/messages-gap]
|
||||
(fn [[messages message-groups message-statuses referenced-messages messages-gap]]
|
||||
(-> (chat.db/sort-message-groups message-groups messages)
|
||||
(chat.db/messages-with-datemarks-and-statuses messages message-statuses referenced-messages)
|
||||
(chat.db/messages-with-datemarks-and-statuses messages message-statuses referenced-messages messages-gap)
|
||||
chat.db/messages-stream)))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:chats/fetching-gap-in-progress?
|
||||
(fn [db]
|
||||
(let [chat-id (:current-chat-id db)
|
||||
gaps (:mailserver/fetching-gaps-in-progress db)]
|
||||
(contains? gaps chat-id))))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:chats/current-chat-intro-status
|
||||
:<- [:chats/current-chat]
|
||||
|
|
|
@ -461,6 +461,21 @@
|
|||
contact-device-info/v1
|
||||
contact-recovery/v1])
|
||||
|
||||
(def v41 [chat/v14
|
||||
transport/v8
|
||||
contact/v7
|
||||
message/v10
|
||||
mailserver/v11
|
||||
mailserver-topic/v2
|
||||
user-status/v2
|
||||
membership-update/v1
|
||||
installation/v3
|
||||
local-storage/v1
|
||||
browser/v8
|
||||
dapp-permissions/v9
|
||||
contact-device-info/v1
|
||||
contact-recovery/v1])
|
||||
|
||||
;; put schemas ordered by version
|
||||
(def schemas [{:schema v1
|
||||
:schemaVersion 1
|
||||
|
@ -581,4 +596,7 @@
|
|||
:migration (constantly nil)}
|
||||
{:schema v40
|
||||
:schemaVersion 40
|
||||
:migration migrations/v40}])
|
||||
:migration migrations/v40}
|
||||
{:schema v41
|
||||
:schemaVersion 41
|
||||
:migration (constantly nil)}])
|
||||
|
|
|
@ -5,3 +5,14 @@
|
|||
:properties {:topic :string
|
||||
:chat-ids :string
|
||||
:last-request {:type :int :default 1}}})
|
||||
|
||||
(def v2
|
||||
(-> v1
|
||||
(assoc-in
|
||||
[:properties :gap-from]
|
||||
{:type :int
|
||||
:optional true})
|
||||
(assoc-in
|
||||
[:properties :gap-to]
|
||||
{:type :int
|
||||
:optional true})))
|
||||
|
|
|
@ -62,3 +62,9 @@
|
|||
(-> v8
|
||||
(assoc-in [:properties :raw-payload-hash]
|
||||
{:type :string})))
|
||||
|
||||
(def v10
|
||||
(-> v9
|
||||
(assoc-in [:properties :whisper-timestamp]
|
||||
{:type :int
|
||||
:optional true})))
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
; This entity is not used in the newer version of schema
|
||||
(ns status-im.data-store.realm.schemas.account.transport-inbox-topic)
|
||||
|
||||
(def v1 {:name :transport-inbox-topic
|
||||
|
|
|
@ -54,7 +54,8 @@
|
|||
[status-im.utils.config :as config]
|
||||
[status-im.ui.components.bottom-sheet.core :as bottom-sheet]
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im.utils.build :as build]))
|
||||
[status-im.utils.build :as build]
|
||||
[status-im.chat.db :as chat.db]))
|
||||
|
||||
;; init module
|
||||
|
||||
|
@ -445,7 +446,7 @@
|
|||
(handlers/register-handler-fx
|
||||
:mailserver/fetch-history
|
||||
(fn [cofx [_ chat-id from-timestamp]]
|
||||
(mailserver/fetch-history cofx chat-id from-timestamp)))
|
||||
(mailserver/fetch-history cofx chat-id {:from from-timestamp})))
|
||||
|
||||
(handlers/register-handler-fx
|
||||
:mailserver.callback/generate-mailserver-symkey-success
|
||||
|
@ -721,8 +722,22 @@
|
|||
|
||||
(handlers/register-handler-fx
|
||||
:chat.ui/fetch-history-pressed
|
||||
(fn [cofx [_ chat-id]]
|
||||
(mailserver/fetch-history cofx chat-id 1)))
|
||||
(fn [{:keys [now] :as cofx} [_ chat-id]]
|
||||
(mailserver/fetch-history cofx chat-id
|
||||
{:from (- (quot now 1000) (* 24 3600))})))
|
||||
|
||||
(handlers/register-handler-fx
|
||||
:chat.ui/fill-the-gap
|
||||
(fn [{:keys [db] :as cofx}]
|
||||
(let [mailserver-topics (:mailserver/topics db)
|
||||
chat-id (:current-chat-id db)
|
||||
topic (chat.db/topic-by-current-chat db)
|
||||
gap (chat.db/messages-gap mailserver-topics topic)]
|
||||
(mailserver/fill-the-gap
|
||||
cofx
|
||||
(assoc gap
|
||||
:topic topic
|
||||
:chat-id chat-id)))))
|
||||
|
||||
(handlers/register-handler-fx
|
||||
:chat.ui/remove-chat-pressed
|
||||
|
|
|
@ -2,13 +2,11 @@
|
|||
status-im.mailserver.core
|
||||
(:require [re-frame.core :as re-frame]
|
||||
[status-im.accounts.db :as accounts.db]
|
||||
[status-im.data-store.core :as data-store]
|
||||
[status-im.fleet.core :as fleet]
|
||||
[status-im.native-module.core :as status]
|
||||
[status-im.utils.platform :as platform]
|
||||
[status-im.transport.utils :as transport.utils]
|
||||
[status-im.utils.fx :as fx]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.utils.utils :as utils]
|
||||
[taoensso.timbre :as log]
|
||||
[status-im.transport.db :as transport.db]
|
||||
|
@ -37,6 +35,7 @@
|
|||
|
||||
(def one-day (* 24 3600))
|
||||
(def seven-days (* 7 one-day))
|
||||
(def max-request-range one-day)
|
||||
(def maximum-number-of-attempts 2)
|
||||
(def request-timeout 30)
|
||||
(def min-limit 100)
|
||||
|
@ -298,7 +297,7 @@
|
|||
(assoc-in [:mailserver/current-request :request-id] request-id))}
|
||||
{:db (assoc-in db [:mailserver/current-request :request-id] request-id)}))))
|
||||
|
||||
(defn request-messages! [web3 {:keys [sym-key-id address]} {:keys [topics cursor to from] :as request}]
|
||||
(defn request-messages! [web3 {: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)
|
||||
|
@ -307,16 +306,20 @@
|
|||
(log/info "mailserver: request-messages for: "
|
||||
" topics " topics
|
||||
" from " actual-from
|
||||
" force-to? " force-to?
|
||||
" to " to
|
||||
" cursor " cursor
|
||||
" limit " actual-limit)
|
||||
(.requestMessages (transport.utils/shh web3)
|
||||
(clj->js {:topics topics
|
||||
(clj->js (cond-> {:topics topics
|
||||
:mailServerPeer address
|
||||
:symKeyID sym-key-id
|
||||
:timeout request-timeout
|
||||
:limit actual-limit
|
||||
:cursor cursor
|
||||
:from actual-from})
|
||||
:from actual-from}
|
||||
force-to?
|
||||
(assoc :to to)))
|
||||
(fn [error request-id]
|
||||
(if-not error
|
||||
(do
|
||||
|
@ -341,25 +344,43 @@
|
|||
sym-key-id)
|
||||
mailserver)))
|
||||
|
||||
(defn ->request
|
||||
[now-in-s [last-request topics]]
|
||||
(when (< last-request now-in-s)
|
||||
{:topics topics
|
||||
;; To is currently not sent to the mailserver, but we use to calculate
|
||||
;; when the last-request was sent.
|
||||
:to now-in-s
|
||||
:from (max last-request
|
||||
(- now-in-s one-day))}))
|
||||
(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 max-request-range)))
|
||||
to (or force-request-to default-request-to)]
|
||||
{:topic topic
|
||||
:from from
|
||||
:to to
|
||||
:force-to? (not (nil? force-request-to))})))))
|
||||
|
||||
(defn aggregate-requests
|
||||
[acc {:keys [topic from to force-to?]}]
|
||||
(update acc [from to force-to?]
|
||||
(fn [{:keys [topics]}]
|
||||
{:topics ((fnil conj #{}) topics topic)
|
||||
: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?})))
|
||||
|
||||
(defn prepare-messages-requests
|
||||
[{:keys [db now] :as cofx} request-to]
|
||||
(let [web3 (:web3 db)]
|
||||
(->>
|
||||
(:mailserver/topics db)
|
||||
(reduce (fn [acc [topic {:keys [last-request]}]]
|
||||
(update acc last-request conj topic))
|
||||
{})
|
||||
(keep (partial ->request request-to)))))
|
||||
[{{:keys [:mailserver/requests-from
|
||||
:mailserver/requests-to
|
||||
:mailserver/topics]} :db}
|
||||
default-request-to]
|
||||
(transduce
|
||||
(keep (topic->request default-request-to requests-from requests-to))
|
||||
(completing aggregate-requests vals)
|
||||
{}
|
||||
topics))
|
||||
|
||||
(fx/defn process-next-messages-request
|
||||
[{:keys [db now] :as cofx}]
|
||||
|
@ -372,6 +393,7 @@
|
|||
(quot now 1000))
|
||||
requests (prepare-messages-requests cofx request-to)
|
||||
web3 (:web3 db)]
|
||||
(log/debug "Mailserver: planned requests " requests)
|
||||
(if-let [request (first requests)]
|
||||
{:db (assoc db
|
||||
:mailserver/pending-requests (count requests)
|
||||
|
@ -382,7 +404,9 @@
|
|||
:request request}}
|
||||
{:db (dissoc db
|
||||
:mailserver/pending-requests
|
||||
:mailserver/request-to)})))))
|
||||
:mailserver/request-to
|
||||
:mailserver/requests-from
|
||||
:mailserver/requests-to)})))))
|
||||
|
||||
(fx/defn add-mailserver-trusted
|
||||
"the current mailserver has been trusted
|
||||
|
@ -485,14 +509,100 @@
|
|||
{:topic topic
|
||||
:mailserver-topic mailserver-topic})]})))
|
||||
|
||||
(defn get-updated-mailserver-topics [db topics last-request]
|
||||
(reduce (fn [acc topic]
|
||||
(if-let [mailserver-topic (some-> (get-in db [:mailserver/topics topic])
|
||||
(assoc :last-request last-request))]
|
||||
(assoc acc topic mailserver-topic)
|
||||
acc))
|
||||
(defn calculate-gap
|
||||
[{:keys [gap-from
|
||||
gap-to
|
||||
last-request] :as config}
|
||||
{:keys [request-from
|
||||
request-to]}]
|
||||
(merge config
|
||||
(cond
|
||||
(nil? gap-from)
|
||||
{:gap-from request-to
|
||||
:gap-to request-to
|
||||
:last-request request-to}
|
||||
|
||||
;;------GF GT--------LRT F---T
|
||||
(> request-from last-request)
|
||||
{:gap-from last-request
|
||||
:gap-to request-from
|
||||
:last-request request-to}
|
||||
|
||||
;;------GF GT--------LRT
|
||||
;; F----------T
|
||||
(and (>= last-request request-from gap-to)
|
||||
(> request-to last-request))
|
||||
{:last-request request-to}
|
||||
|
||||
;;------GF GT--------LRT
|
||||
;; F----T
|
||||
(and (>= last-request request-from gap-to)
|
||||
(>= last-request request-to gap-to))
|
||||
config
|
||||
|
||||
;;------GF GT--------LRT
|
||||
;; F-------T
|
||||
(and (> gap-to request-from gap-from)
|
||||
(>= last-request request-to gap-to))
|
||||
{:gap-to request-from}
|
||||
|
||||
;;------GF GT--------LRT
|
||||
;; F-T
|
||||
(and (> gap-to request-from gap-from)
|
||||
(> gap-to request-to gap-from))
|
||||
config
|
||||
|
||||
;;------GF GT--------LRT
|
||||
;; F------T
|
||||
(and (>= gap-from request-from)
|
||||
(> gap-to request-to gap-from))
|
||||
{:gap-from request-to}
|
||||
|
||||
;;---------GF=GT=LRT
|
||||
;; F---T
|
||||
(and (>= gap-from request-from)
|
||||
(>= gap-from request-to)
|
||||
(= gap-from last-request))
|
||||
{:gap-from request-to}
|
||||
|
||||
;;------GF GT--------LRT
|
||||
;; F---T
|
||||
(and (>= gap-from request-from)
|
||||
(>= gap-from request-to))
|
||||
config
|
||||
|
||||
;;------GF GT--------LRT
|
||||
;; F-------------T
|
||||
(and (>= gap-from request-from)
|
||||
(>= last-request request-to gap-to))
|
||||
{:gap-from last-request
|
||||
:gap-to last-request
|
||||
:last-request last-request}
|
||||
|
||||
;;------GF GT--------LRT
|
||||
;; F------------------------T
|
||||
(and (>= gap-from request-from)
|
||||
(>= request-to last-request))
|
||||
{:gap-from request-to
|
||||
:gap-to request-to
|
||||
:last-request request-to}
|
||||
|
||||
;;------GF GT--------LRT
|
||||
;; F-----------------T
|
||||
(and (> gap-to request-from gap-from)
|
||||
(>= request-to last-request))
|
||||
{:gap-to request-from
|
||||
:last-request request-to})))
|
||||
|
||||
(defn get-updated-mailserver-topics [db requested-topics from to]
|
||||
(into
|
||||
{}
|
||||
topics))
|
||||
(keep (fn [topic]
|
||||
(when-let [config (get-in db [:mailserver/topics topic])]
|
||||
[topic (calculate-gap config
|
||||
{:request-from from
|
||||
:request-to to})])))
|
||||
requested-topics))
|
||||
|
||||
(fx/defn update-mailserver-topics
|
||||
"TODO: add support for cursors
|
||||
|
@ -500,7 +610,7 @@
|
|||
[{: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 to)]
|
||||
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)
|
||||
|
@ -516,16 +626,31 @@
|
|||
:mailserver/request-messages {:web3 (:web3 db)
|
||||
:mailserver mailserver
|
||||
:request request-with-cursor}}))
|
||||
(let [{:keys [chat-id] :as current-request} (db :mailserver/current-request)
|
||||
gaps (db :mailserver/fetching-gaps-in-progress)
|
||||
fetching-gap-completed? (= (get gaps chat-id)
|
||||
(select-keys
|
||||
current-request
|
||||
[:from :to :force-to? :topics :chat-id]))]
|
||||
(fx/merge cofx
|
||||
{:db (-> db
|
||||
(dissoc :mailserver/current-request)
|
||||
(update :mailserver/topics merge mailserver-topics))
|
||||
(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 fetching-gap-completed?
|
||||
(dissoc gaps chat-id)
|
||||
gaps))))
|
||||
:data-store/tx (mapv (fn [[topic mailserver-topic]]
|
||||
(data-store.mailservers/save-mailserver-topic-tx
|
||||
{:topic topic
|
||||
:mailserver-topic mailserver-topic}))
|
||||
mailserver-topics)}
|
||||
(process-next-messages-request)))))))
|
||||
(process-next-messages-request))))))))
|
||||
|
||||
(fx/defn retry-next-messages-request
|
||||
[{:keys [db] :as cofx}]
|
||||
|
@ -543,8 +668,8 @@
|
|||
:mailserver/pending-requests))})
|
||||
|
||||
(fx/defn handle-request-completed
|
||||
[{{:keys [chats] :as db} :db :as cofx}
|
||||
{:keys [requestID lastEnvelopeHash cursor errorMessage] :as event}]
|
||||
[{{:keys [chats]} :db :as cofx}
|
||||
{:keys [requestID lastEnvelopeHash cursor errorMessage]}]
|
||||
(when (accounts.db/logged-in? cofx)
|
||||
(if (empty? errorMessage)
|
||||
(let [never-synced-chats-in-request
|
||||
|
@ -596,12 +721,13 @@
|
|||
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] :as cofx} {:keys [topic chat-id]}]
|
||||
[{:keys [db now] :as cofx} {:keys [topic chat-id]}]
|
||||
(let [{:keys [chat-ids last-request] :as current-mailserver-topic}
|
||||
(get-in db [:mailserver/topics topic] {:chat-ids #{}})]
|
||||
(when-let [mailserver-topic (when-not (chat-ids chat-id)
|
||||
(-> current-mailserver-topic
|
||||
(assoc :last-request 1)
|
||||
(assoc :last-request (- (quot now 1000)
|
||||
(* 24 60 60)))
|
||||
(update :chat-ids conj chat-id)))]
|
||||
(fx/merge cofx
|
||||
{:db (assoc-in db [:mailserver/topics topic] mailserver-topic)
|
||||
|
@ -610,21 +736,42 @@
|
|||
:mailserver-topic mailserver-topic})]}))))
|
||||
|
||||
(fx/defn fetch-history
|
||||
[{:keys [db] :as cofx} chat-id from-timestamp]
|
||||
(log/debug "fetch-history" "chat-id:" chat-id "from-timestamp:" from-timestamp)
|
||||
[{:keys [db] :as cofx} chat-id {:keys [from to]}]
|
||||
|
||||
(log/debug "fetch-history" "chat-id:" chat-id "from-timestamp:" from)
|
||||
(let [public-key (accounts.db/current-public-key cofx)
|
||||
topic (or (get-in db [:transport/chats chat-id :topic])
|
||||
(transport.topic/public-key->discovery-topic-hash public-key))
|
||||
{:keys [chat-ids last-request] :as current-mailserver-topic}
|
||||
(get-in db [:mailserver/topics topic] {:chat-ids #{}})]
|
||||
(let [mailserver-topic (-> current-mailserver-topic
|
||||
(assoc :last-request (min from-timestamp last-request)))]
|
||||
(transport.topic/public-key->discovery-topic-hash public-key))]
|
||||
(fx/merge cofx
|
||||
{:db (assoc-in db [:mailserver/topics topic] mailserver-topic)
|
||||
:data-store/tx [(data-store.mailservers/save-mailserver-topic-tx
|
||||
{:topic topic
|
||||
:mailserver-topic mailserver-topic})]}
|
||||
(process-next-messages-request)))))
|
||||
{:db (cond-> (assoc-in db [:mailserver/requests-from topic] from)
|
||||
|
||||
to
|
||||
(assoc-in [:mailserver/requests-to topic] to))}
|
||||
(process-next-messages-request))))
|
||||
|
||||
(fx/defn fill-the-gap
|
||||
[{:keys [db] :as cofx} {:keys [exists? from to topic chat-id]}]
|
||||
(let [mailserver (get-mailserver-when-ready cofx)
|
||||
request {:from from
|
||||
:to to
|
||||
:force-to? true
|
||||
:topics [topic]
|
||||
:chat-id chat-id}]
|
||||
(when exists?
|
||||
{:db
|
||||
(-> db
|
||||
(assoc
|
||||
:mailserver/pending-requests 1
|
||||
:mailserver/current-request request
|
||||
:mailserver/request-to to)
|
||||
|
||||
(update :mailserver/fetching-gaps-in-progress
|
||||
assoc chat-id request))
|
||||
|
||||
:mailserver/request-messages
|
||||
{:web3 (:web3 db)
|
||||
:mailserver mailserver
|
||||
:request request}})))
|
||||
|
||||
(fx/defn resend-request
|
||||
[{:keys [db] :as cofx} {:keys [request-id]}]
|
||||
|
|
|
@ -43,6 +43,13 @@
|
|||
(pos-int? pending-requests)
|
||||
(not (or connecting? connection-error? request-error?)))))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:mailserver/connected?
|
||||
(fn [db]
|
||||
(let [connected? (= :connected (:mailserver/state db))
|
||||
online? (= :online (:network-status db))]
|
||||
(and connected? online?))))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:mailserver/current-id
|
||||
(fn [db]
|
||||
|
|
|
@ -113,7 +113,7 @@
|
|||
(fx/merge cofx
|
||||
(send-direct-message current-public-key nil this)
|
||||
(send-with-pubkey params)))))
|
||||
(receive [this chat-id signature _ cofx]
|
||||
(receive [this chat-id signature timestamp cofx]
|
||||
{:chat-received-message/add-fx
|
||||
[(assoc (into {} this)
|
||||
:old-message-id (transport.utils/old-message-id this)
|
||||
|
@ -121,6 +121,7 @@
|
|||
signature
|
||||
(.-payload (:js-obj cofx)))
|
||||
:chat-id chat-id
|
||||
:whisper-timestamp timestamp
|
||||
:raw-payload-hash (transport.utils/sha3
|
||||
(.-payload (:js-obj cofx)))
|
||||
:from signature
|
||||
|
|
|
@ -70,6 +70,37 @@
|
|||
[{{:keys [value]} :row}]
|
||||
[message-datemark/chat-datemark-mobile value])
|
||||
|
||||
(defview gap []
|
||||
(letsubs [in-progress? [:chats/fetching-gap-in-progress?]
|
||||
connected? [:mailserver/connected?]]
|
||||
[react/view {:align-self :stretch
|
||||
:margin-top 24
|
||||
:margin-bottom 24
|
||||
:height 48
|
||||
:align-items :center
|
||||
:justify-content :center
|
||||
:border-color colors/gray-light
|
||||
:border-top-width 1
|
||||
:border-bottom-width 1
|
||||
:background-color :white}
|
||||
[react/touchable-highlight
|
||||
{:on-press (when (and connected? (not in-progress?))
|
||||
#(re-frame/dispatch [:chat.ui/fill-the-gap]))}
|
||||
[react/view {:flex 1
|
||||
:align-items :center
|
||||
:justify-content :center}
|
||||
(if in-progress?
|
||||
[react/activity-indicator]
|
||||
[react/text
|
||||
{:style {:color (if connected?
|
||||
colors/blue
|
||||
colors/gray)}}
|
||||
(i18n/label :t/fetch-messages)])]]]))
|
||||
|
||||
(defmethod message-row :gap
|
||||
[_]
|
||||
[gap])
|
||||
|
||||
(defmethod message-row :default
|
||||
[{:keys [group-chat current-public-key modal? row]}]
|
||||
[message/chat-message (assoc row
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
(ns status-im.test.chat.db
|
||||
(:require [cljs.test :refer-macros [deftest is testing]]
|
||||
[status-im.chat.db :as s]))
|
||||
[status-im.chat.db :as db]))
|
||||
|
||||
(deftest group-chat-name
|
||||
(testing "it prepends # if it's a public chat"
|
||||
(is (= "#withhash" (s/group-chat-name {:group-chat true
|
||||
(is (= "#withhash" (db/group-chat-name {:group-chat true
|
||||
:chat-id "1"
|
||||
:public? true
|
||||
:name "withhash"}))))
|
||||
(testing "it leaves the name unchanged if it's a group chat"
|
||||
(is (= "unchanged" (s/group-chat-name {:group-chat true
|
||||
(is (= "unchanged" (db/group-chat-name {:group-chat true
|
||||
:chat-id "1"
|
||||
:name "unchanged"})))))
|
||||
|
||||
|
@ -29,7 +29,7 @@
|
|||
messages [m1 m2 m3 dm1]
|
||||
[actual-m1
|
||||
actual-m2
|
||||
actual-m3] (s/messages-stream messages)]
|
||||
actual-m3] (db/messages-stream messages)]
|
||||
(testing "it marks only the first message as :last?"
|
||||
(is (:last? actual-m1))
|
||||
(is (not (:last? actual-m2)))
|
||||
|
@ -81,7 +81,7 @@
|
|||
actual-m2
|
||||
actual-m3
|
||||
actual-m4
|
||||
_] (s/messages-stream messages)]
|
||||
_] (db/messages-stream messages)]
|
||||
(testing "it marks the first outgoing message as :last-outgoing?"
|
||||
(is (:last-outgoing? actual-m1))
|
||||
(is (not (:last-outgoing? actual-m2)))
|
||||
|
@ -108,7 +108,7 @@
|
|||
:datemark "a"
|
||||
:outgoing false}
|
||||
messages [m1]
|
||||
[actual-m1] (s/messages-stream messages)]
|
||||
[actual-m1] (db/messages-stream messages)]
|
||||
(testing "it does display the photo"
|
||||
(is (:display-photo? actual-m1))
|
||||
(testing "it does not display the username"
|
||||
|
@ -122,4 +122,218 @@
|
|||
"3" {:is-active false :chat-id "3"}}]
|
||||
(testing "it returns only chats with is-active"
|
||||
(is (= #{"1" "2"}
|
||||
(set (keys (s/active-chats {} chats {}))))))))
|
||||
(set (keys (db/active-chats {} chats {}))))))))
|
||||
|
||||
(deftest messages-with-datemarks-and-statuses
|
||||
(testing "empty state"
|
||||
(is (empty?
|
||||
(db/messages-with-datemarks-and-statuses
|
||||
nil
|
||||
nil
|
||||
nil
|
||||
nil
|
||||
nil))))
|
||||
(testing "simple case"
|
||||
(is (=
|
||||
'({:whisper-timestamp 40
|
||||
:timestamp 40
|
||||
:content nil
|
||||
:timestamp-str "14:00"
|
||||
:user-statuses nil
|
||||
:datemark "today"}
|
||||
{:whisper-timestamp 30
|
||||
:timestamp 30
|
||||
:content nil
|
||||
:timestamp-str "13:00"
|
||||
:user-statuses nil
|
||||
:datemark "today"}
|
||||
{:value "today"
|
||||
:type :datemark
|
||||
:whisper-timestamp 30
|
||||
:timestamp 30}
|
||||
{:whisper-timestamp 20
|
||||
:timestamp 20
|
||||
:content nil
|
||||
:timestamp-str "12:00"
|
||||
:user-statuses nil
|
||||
:datemark "yesterday"}
|
||||
{:whisper-timestamp 10
|
||||
:timestamp 10
|
||||
:content nil
|
||||
:timestamp-str "11:00"
|
||||
:user-statuses nil
|
||||
:datemark "yesterday"}
|
||||
{:value "yesterday"
|
||||
:type :datemark
|
||||
:whisper-timestamp 10
|
||||
:timestamp 10})
|
||||
(db/messages-with-datemarks-and-statuses
|
||||
{"yesterday"
|
||||
(list
|
||||
{:message-id :m1
|
||||
:timestamp-str "11:00"
|
||||
:whisper-timestamp 10
|
||||
:timestamp 10}
|
||||
{:message-id :m2
|
||||
:timestamp-str "12:00"
|
||||
:whisper-timestamp 20
|
||||
:timestamp 20})
|
||||
"today"
|
||||
(list
|
||||
{:message-id :m3
|
||||
:timestamp-str "13:00"
|
||||
:whisper-timestamp 30
|
||||
:timestamp 30}
|
||||
{:message-id :m4
|
||||
:timestamp-str "14:00"
|
||||
:whisper-timestamp 40
|
||||
:timestamp 40})}
|
||||
{:m1 {:whisper-timestamp 10
|
||||
:timestamp 10}
|
||||
:m2 {:whisper-timestamp 20
|
||||
:timestamp 20}
|
||||
:m3 {:whisper-timestamp 30
|
||||
:timestamp 30}
|
||||
:m4 {:whisper-timestamp 40
|
||||
:timestamp 40}}
|
||||
nil
|
||||
nil
|
||||
nil))))
|
||||
(testing "simple case with gap"
|
||||
(is (=
|
||||
'({:whisper-timestamp 40
|
||||
:timestamp 40
|
||||
:content nil
|
||||
:timestamp-str "14:00"
|
||||
:user-statuses nil
|
||||
:datemark "today"}
|
||||
{:whisper-timestamp 30
|
||||
:timestamp 30
|
||||
:content nil
|
||||
:timestamp-str "13:00"
|
||||
:user-statuses nil
|
||||
:datemark "today"}
|
||||
{:value "today"
|
||||
:type :datemark
|
||||
:whisper-timestamp 30
|
||||
:timestamp 30}
|
||||
{:type :gap
|
||||
:value "25"}
|
||||
{:whisper-timestamp 20
|
||||
:timestamp 20
|
||||
:content nil
|
||||
:timestamp-str "12:00"
|
||||
:user-statuses nil
|
||||
:datemark "yesterday"}
|
||||
{:whisper-timestamp 10
|
||||
:timestamp 10
|
||||
:content nil
|
||||
:timestamp-str "11:00"
|
||||
:user-statuses nil
|
||||
:datemark "yesterday"}
|
||||
{:value "yesterday"
|
||||
:type :datemark
|
||||
:whisper-timestamp 10
|
||||
:timestamp 10})
|
||||
(db/messages-with-datemarks-and-statuses
|
||||
{"yesterday"
|
||||
(list
|
||||
{:message-id :m1
|
||||
:timestamp-str "11:00"
|
||||
:whisper-timestamp 10
|
||||
:timestamp 10}
|
||||
{:message-id :m2
|
||||
:timestamp-str "12:00"
|
||||
:whisper-timestamp 20
|
||||
:timestamp 20})
|
||||
"today"
|
||||
(list
|
||||
{:message-id :m3
|
||||
:timestamp-str "13:00"
|
||||
:whisper-timestamp 30
|
||||
:timestamp 30}
|
||||
{:message-id :m4
|
||||
:timestamp-str "14:00"
|
||||
:whisper-timestamp 40
|
||||
:timestamp 40})}
|
||||
{:m1 {:whisper-timestamp 10
|
||||
:timestamp 10}
|
||||
:m2 {:whisper-timestamp 20
|
||||
:timestamp 20}
|
||||
:m3 {:whisper-timestamp 30
|
||||
:timestamp 30}
|
||||
:m4 {:whisper-timestamp 40
|
||||
:timestamp 40}}
|
||||
nil
|
||||
nil
|
||||
{:from 25
|
||||
:exists? true}))))
|
||||
(testing "simple case with gap after all messages"
|
||||
(is (=
|
||||
'({:type :gap
|
||||
:value "100"}
|
||||
{:whisper-timestamp 40
|
||||
:timestamp 40
|
||||
:content nil
|
||||
:timestamp-str "14:00"
|
||||
:user-statuses nil
|
||||
:datemark "today"}
|
||||
{:whisper-timestamp 30
|
||||
:timestamp 30
|
||||
:content nil
|
||||
:timestamp-str "13:00"
|
||||
:user-statuses nil
|
||||
:datemark "today"}
|
||||
{:value "today"
|
||||
:type :datemark
|
||||
:whisper-timestamp 30
|
||||
:timestamp 30}
|
||||
{:whisper-timestamp 20
|
||||
:timestamp 20
|
||||
:content nil
|
||||
:timestamp-str "12:00"
|
||||
:user-statuses nil
|
||||
:datemark "yesterday"}
|
||||
{:whisper-timestamp 10
|
||||
:timestamp 10
|
||||
:content nil
|
||||
:timestamp-str "11:00"
|
||||
:user-statuses nil
|
||||
:datemark "yesterday"}
|
||||
{:value "yesterday"
|
||||
:type :datemark
|
||||
:whisper-timestamp 10
|
||||
:timestamp 10})
|
||||
(db/messages-with-datemarks-and-statuses
|
||||
{"yesterday"
|
||||
(list
|
||||
{:message-id :m1
|
||||
:timestamp-str "11:00"
|
||||
:whisper-timestamp 10
|
||||
:timestamp 10}
|
||||
{:message-id :m2
|
||||
:timestamp-str "12:00"
|
||||
:whisper-timestamp 20
|
||||
:timestamp 20})
|
||||
"today"
|
||||
(list
|
||||
{:message-id :m3
|
||||
:timestamp-str "13:00"
|
||||
:whisper-timestamp 30
|
||||
:timestamp 30}
|
||||
{:message-id :m4
|
||||
:timestamp-str "14:00"
|
||||
:whisper-timestamp 40
|
||||
:timestamp 40})}
|
||||
{:m1 {:whisper-timestamp 10
|
||||
:timestamp 10}
|
||||
:m2 {:whisper-timestamp 20
|
||||
:timestamp 20}
|
||||
:m3 {:whisper-timestamp 30
|
||||
:timestamp 30}
|
||||
:m4 {:whisper-timestamp 40
|
||||
:timestamp 40}}
|
||||
nil
|
||||
nil
|
||||
{:from 100
|
||||
:exists? true})))))
|
||||
|
|
|
@ -627,3 +627,187 @@
|
|||
(is (not (-> (mailserver/connect-to-mailserver {:db mailserver-with-sym-key-db})
|
||||
:shh/generate-sym-key-from-password
|
||||
first)))))))
|
||||
|
||||
(deftest calculate-gap
|
||||
(testing "new topic"
|
||||
(is (= {:gap-from 10
|
||||
:gap-to 10
|
||||
:last-request 10}
|
||||
|
||||
(mailserver/calculate-gap
|
||||
{:gap-from nil
|
||||
:gap-to nil
|
||||
:last-request nil}
|
||||
{:request-from 5
|
||||
:request-to 10}))))
|
||||
(testing "calculate-gap#1"
|
||||
(is (= {:gap-from 3
|
||||
:gap-to 4
|
||||
:last-request 5}
|
||||
|
||||
(mailserver/calculate-gap
|
||||
{:gap-from 1
|
||||
:gap-to 2
|
||||
:last-request 3}
|
||||
{:request-from 4
|
||||
:request-to 5}))))
|
||||
(testing "calculate-gap#2"
|
||||
(is (= {:gap-from 1
|
||||
:gap-to 2
|
||||
:last-request 5}
|
||||
(mailserver/calculate-gap
|
||||
{:gap-from 1
|
||||
:gap-to 2
|
||||
:last-request 4}
|
||||
{:request-from 3
|
||||
:request-to 5}))))
|
||||
(testing "calculate-gap#2-1"
|
||||
(is (= {:gap-from 1
|
||||
:gap-to 2
|
||||
:last-request 4}
|
||||
(mailserver/calculate-gap
|
||||
{:gap-from 1
|
||||
:gap-to 2
|
||||
:last-request 3}
|
||||
{:request-from 3
|
||||
:request-to 4}))))
|
||||
(testing "calculate-gap#3"
|
||||
(is (= {:gap-from 1
|
||||
:gap-to 2
|
||||
:last-request 5}
|
||||
(mailserver/calculate-gap
|
||||
{:gap-from 1
|
||||
:gap-to 2
|
||||
:last-request 5}
|
||||
{:request-from 3
|
||||
:request-to 4}))))
|
||||
(testing "calculate-gap#3-1"
|
||||
(is (= {:gap-from 1
|
||||
:gap-to 2
|
||||
:last-request 3}
|
||||
(mailserver/calculate-gap
|
||||
{:gap-from 1
|
||||
:gap-to 2
|
||||
:last-request 3}
|
||||
{:request-from 2
|
||||
:request-to 3}))))
|
||||
(testing "calculate-gap#4"
|
||||
(is (= {:gap-from 1
|
||||
:gap-to 2
|
||||
:last-request 5}
|
||||
(mailserver/calculate-gap
|
||||
{:gap-from 1
|
||||
:gap-to 2
|
||||
:last-request 5}
|
||||
{:request-from 3
|
||||
:request-to 4}))))
|
||||
(testing "calculate-gap#5"
|
||||
(is (= {:gap-from 1
|
||||
:gap-to 4
|
||||
:last-request 5}
|
||||
(mailserver/calculate-gap
|
||||
{:gap-from 1
|
||||
:gap-to 4
|
||||
:last-request 5}
|
||||
{:request-from 2
|
||||
:request-to 3}))))
|
||||
(testing "calculate-gap#6"
|
||||
(is (= {:gap-from 2
|
||||
:gap-to 3
|
||||
:last-request 4}
|
||||
(mailserver/calculate-gap
|
||||
{:gap-from 2
|
||||
:gap-to 3
|
||||
:last-request 4}
|
||||
{:request-from 1
|
||||
:request-to 2}))))
|
||||
(testing "calculate-gap#6-1"
|
||||
(is (= {:gap-from 1
|
||||
:gap-to 4
|
||||
:last-request 5}
|
||||
(mailserver/calculate-gap
|
||||
{:gap-from 1
|
||||
:gap-to 4
|
||||
:last-request 5}
|
||||
{:request-from 2
|
||||
:request-to 3}))))
|
||||
(testing "calculate-gap#0"
|
||||
(is (= {:gap-from 2
|
||||
:gap-to 3
|
||||
:last-request 3}
|
||||
(mailserver/calculate-gap
|
||||
{:gap-from 3
|
||||
:gap-to 3
|
||||
:last-request 3}
|
||||
{:request-from 1
|
||||
:request-to 2}))))
|
||||
(testing "calculate-gap#7"
|
||||
(is (= {:gap-from 3
|
||||
:gap-to 4
|
||||
:last-request 5}
|
||||
(mailserver/calculate-gap
|
||||
{:gap-from 3
|
||||
:gap-to 4
|
||||
:last-request 5}
|
||||
{:request-from 1
|
||||
:request-to 2}))))
|
||||
(testing "calculate-gap#8"
|
||||
(is (= {:gap-from 5
|
||||
:gap-to 5
|
||||
:last-request 5}
|
||||
(mailserver/calculate-gap
|
||||
{:gap-from 2
|
||||
:gap-to 3
|
||||
:last-request 5}
|
||||
{:request-from 1
|
||||
:request-to 4}))))
|
||||
(testing "calculate-gap#8-1"
|
||||
(is (= {:gap-from 3
|
||||
:gap-to 3
|
||||
:last-request 3}
|
||||
(mailserver/calculate-gap
|
||||
{:gap-from 1
|
||||
:gap-to 2
|
||||
:last-request 3}
|
||||
{:request-from 1
|
||||
:request-to 2}))))
|
||||
(testing "calculate-gap#9"
|
||||
(is (= {:gap-from 5
|
||||
:gap-to 5
|
||||
:last-request 5}
|
||||
(mailserver/calculate-gap
|
||||
{:gap-from 2
|
||||
:gap-to 3
|
||||
:last-request 4}
|
||||
{:request-from 1
|
||||
:request-to 5}))))
|
||||
(testing "calculate-gap#9-1"
|
||||
(is (= {:gap-from 3
|
||||
:gap-to 3
|
||||
:last-request 3}
|
||||
(mailserver/calculate-gap
|
||||
{:gap-from 1
|
||||
:gap-to 2
|
||||
:last-request 3}
|
||||
{:request-from 1
|
||||
:request-to 3}))))
|
||||
(testing "calculate-gap#10"
|
||||
(is (= {:gap-from 1
|
||||
:gap-to 2
|
||||
:last-request 5}
|
||||
(mailserver/calculate-gap
|
||||
{:gap-from 1
|
||||
:gap-to 3
|
||||
:last-request 4}
|
||||
{:request-from 2
|
||||
:request-to 5}))))
|
||||
(testing "calculate-gap#10-1"
|
||||
(is (= {:gap-from 1
|
||||
:gap-to 2
|
||||
:last-request 3}
|
||||
(mailserver/calculate-gap
|
||||
{:gap-from 1
|
||||
:gap-to 2
|
||||
:last-request 3}
|
||||
{:request-from 2
|
||||
:request-to 3})))))
|
||||
|
|
|
@ -997,5 +997,6 @@
|
|||
"chaos-unicorn-day-details": "\uD83E\uDD84\uD83E\uDD84\uD83E\uDD84\uD83E\uDD84\uD83E\uDD84\uD83E\uDD84\uD83E\uDD84\uD83D\uDE80!",
|
||||
"invalid-format": "Invalid format\nMust be {{format}}",
|
||||
"mailserver-format": "enode://{enode-id}:{password}@{ip-address}:{port}",
|
||||
"bootnode-format": "enode://{enode-id}@{ip-address}:{port}"
|
||||
"bootnode-format": "enode://{enode-id}@{ip-address}:{port}",
|
||||
"fetch-messages": "↓ Fetch messages"
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue