diff --git a/deps.edn b/deps.edn index 7ce63a056d..ea82590f7e 100644 --- a/deps.edn +++ b/deps.edn @@ -8,7 +8,8 @@ com.andrewmcveigh/cljs-time {:mvn/version "0.5.2"} com.taoensso/timbre {:mvn/version "4.10.0"} hickory {:mvn/version "0.7.1"} - com.cognitect/transit-cljs {:mvn/version "0.8.248"}} + com.cognitect/transit-cljs {:mvn/version "0.8.248"} + day8.re-frame/async-flow-fx {:mvn/version "0.0.10"}} :aliases {:repl {:extra-deps diff --git a/project.clj b/project.clj index 8d78455ccd..4d56bc4155 100644 --- a/project.clj +++ b/project.clj @@ -10,7 +10,8 @@ [com.andrewmcveigh/cljs-time "0.5.2"] [com.taoensso/timbre "4.10.0"] [hickory "0.7.1"] - [com.cognitect/transit-cljs "0.8.248"]] + [com.cognitect/transit-cljs "0.8.248"] + [day8.re-frame/async-flow-fx "0.0.10"]] :plugins [[lein-cljsbuild "1.1.7"] [lein-re-frisk "0.5.8"]] :clean-targets ["target/" "index.ios.js" "index.android.js"] diff --git a/src/status_im/network/events.cljs b/src/status_im/network/events.cljs index 88b2abbb59..75d23174ae 100644 --- a/src/status_im/network/events.cljs +++ b/src/status_im/network/events.cljs @@ -1,8 +1,10 @@ (ns status-im.network.events (:require [re-frame.core :as re-frame] [status-im.utils.handlers :as handlers] + [status-im.utils.handlers-macro :as handlers-macro] [status-im.network.net-info :as net-info] - [status-im.native-module.core :as status])) + [status-im.native-module.core :as status] + [status-im.transport.inbox :as inbox])) (re-frame/reg-fx ::listen-to-network-status @@ -18,23 +20,26 @@ (status/connection-change data))) (handlers/register-handler-fx - :listen-to-network-status - (fn [] - {::listen-to-network-status [#(re-frame/dispatch [::update-connection-status %]) - #(re-frame/dispatch [::update-network-status %])]})) + :listen-to-network-status + (fn [] + {::listen-to-network-status [#(re-frame/dispatch [::update-connection-status %]) + #(re-frame/dispatch [::update-network-status %])]})) (handlers/register-handler-fx - ::update-connection-status - [re-frame/trim-v] - (fn [{:keys [db]} [is-connected?]] - (cond-> - {:db (assoc db :network-status (if is-connected? :online :offline))} - - is-connected? - (assoc :drain-mixpanel-events nil)))) + ::update-connection-status + [re-frame/trim-v] + (fn [{:keys [db] :as cofx} [is-connected?]] + (let [previous-status (:network-status db) + back-online? (and (= previous-status :offline) + is-connected?)] + (cond-> (handlers-macro/merge-fx cofx + {:db (assoc db :network-status (if is-connected? :online :offline))} + (inbox/recover-offline-inbox back-online?)) + is-connected? + (assoc :drain-mixpanel-events nil))))) (handlers/register-handler-fx - ::update-network-status - [re-frame/trim-v] - (fn [_ [data]] - {::notify-status-go data})) + ::update-network-status + [re-frame/trim-v] + (fn [_ [data]] + {::notify-status-go data})) diff --git a/src/status_im/translations/en.cljs b/src/status_im/translations/en.cljs index fbb55379a7..b65ed0ded5 100644 --- a/src/status_im/translations/en.cljs +++ b/src/status_im/translations/en.cljs @@ -27,6 +27,7 @@ :no "No" :on "On" :off "Off" + :mailserver-connection-error "Could not connect to mailserver" :camera-access-error "To grant the required camera permission, please go to your system settings and make sure that Status > Camera is selected." :photos-access-error "To grant the required photos permission, please go to your system settings and make sure that Status > Photos is selected." diff --git a/src/status_im/transport/inbox.cljs b/src/status_im/transport/inbox.cljs index 104cce53a4..e8a040f182 100644 --- a/src/status_im/transport/inbox.cljs +++ b/src/status_im/transport/inbox.cljs @@ -6,7 +6,10 @@ [status-im.transport.utils :as web3.utils] [status-im.utils.config :as config] [taoensso.timbre :as log] - [status-im.utils.ethereum.core :as ethereum])) + [status-im.utils.ethereum.core :as ethereum] + [status-im.utils.utils :as utils] + [status-im.i18n :as i18n] + [day8.re-frame.async-flow-fx])) (defn- parse-json ;; NOTE(dmitryn) Expects JSON response like: @@ -41,15 +44,38 @@ wnode-id (get-in db [:account/account :settings :wnode network])] (get-in db [:inbox/wnodes network wnode-id :address]))) +(defn initialize-offline-inbox-flow [] + {:first-dispatch [:inbox/get-sym-key] + :rules [{:when :seen-both? + :events [:inbox/get-sym-key-success :inbox/connection-success] + :dispatch [:inbox/request-messages]}]}) + +(defn recover-offline-inbox-flow [] + {:first-dispatch [:inbox/fetch-peers] + :rules [{:when :seen? + :events :inbox/connection-success + :dispatch [:inbox/request-messages]}]}) + (defn initialize-offline-inbox - "Initialises offline inbox by producing `::add-peer` effect if inboxing enabled in config, - then the event chan is: - add-peer -> fetch-peers -> check-peer-added -> mark-trusted-peer -> get-sym-key -> request-messages" + "Initialises offline inbox if inboxing enabled in config" [{:keys [db]}] (when config/offline-inbox-enabled? (let [wnode (get-current-wnode-address db)] - (log/info "offline inbox: initialize") - {::add-peer {:wnode wnode}}))) + (log/info "offline inbox: initialize " wnode) + (when wnode + {:async-flow (initialize-offline-inbox-flow) + ::add-peer {:wnode wnode}})))) + +(defn recover-offline-inbox + "Recover offline inbox connection after being offline because of connectivity loss" + [back-online? {:keys [db]}] + (when config/offline-inbox-enabled? + (let [wnode (get-current-wnode-address db)] + (when (and back-online? + wnode + (:account/account db)) + (log/info "offline inbox: recover" wnode) + {:async-flow (recover-offline-inbox-flow)})))) (defn add-peer [enode success-fn error-fn] (status/add-peer enode (response-handler error-fn success-fn))) @@ -71,7 +97,7 @@ enode-id (web3.utils/extract-enode-id enode)] (contains? peer-ids enode-id))) -(defn mark-trusted-peer [web3 enode peers success-fn error-fn] +(defn mark-trusted-peer [web3 enode success-fn error-fn] (.markTrustedPeer (web3.utils/shh web3) enode (fn [err resp] @@ -80,7 +106,6 @@ (error-fn err))))) (defn request-messages [web3 wnode topics to from sym-key-id success-fn error-fn] - (log/info "offline inbox: sym-key-id" sym-key-id) (let [opts (merge {:mailServerPeer wnode :symKeyID sym-key-id} (when from {:from from}) @@ -100,22 +125,21 @@ ::add-peer (fn [{:keys [wnode]}] (add-peer wnode - #(re-frame/dispatch [::fetch-peers %]) + #(re-frame/dispatch [:inbox/fetch-peers]) #(log/error "offline inbox: add-peer error" %)))) (re-frame/reg-fx ::fetch-peers (fn [retries] - (fetch-peers #(re-frame/dispatch [::check-peer-added % retries]) + (fetch-peers #(re-frame/dispatch [:inbox/check-peer-added % retries]) #(log/error "offline inbox: fetch-peers error" %)))) (re-frame/reg-fx ::mark-trusted-peer - (fn [{:keys [wnode web3 peers]}] + (fn [{:keys [wnode web3]}] (mark-trusted-peer web3 wnode - peers - #(re-frame/dispatch [::get-sym-key %]) + #(re-frame/dispatch [:inbox/connection-success %]) #(log/error "offline inbox: mark-trusted-peer error" % wnode)))) (re-frame/reg-fx @@ -133,14 +157,20 @@ ;;;; Handlers (handlers/register-handler-fx - ::fetch-peers + :inbox/add-peer + ;; This event adds a wnode to the list of peers + (fn [_ [_ wnode]] + {::add-peer {:wnode wnode}})) + +(handlers/register-handler-fx + :inbox/fetch-peers ;; This event fetches the list of peers ;; We want it to check if the node has been added (fn [_ [_ retries]] {::fetch-peers (or retries 0)})) (handlers/register-handler-fx - ::check-peer-added + :inbox/check-peer-added ;; We check if the wnode is part of the peers list ;; if not we dispatch a new fetch-peer event for later (fn [{:keys [db]} [_ peers retries]] @@ -149,44 +179,54 @@ (log/info "offline inbox: fetch-peers response" peers) (if (registered-peer? peers wnode) {::mark-trusted-peer {:web3 web3 - :wnode wnode - :peers peers}} + :wnode wnode}} (do (log/info "Peer" wnode "is not registered. Retrying fetch peers.") (let [delay (if (< retries 3) 300 5000)] (if (> retries 10) - (log/error "Could not connect to mailserver") - {:dispatch-later [{:ms delay :dispatch [::fetch-peers (inc retries)]}]}))))))) + (do (log/error :mailserver-connection-error) + (utils/show-popup (i18n/label :t/error) + (i18n/label :t/mailserver-connection-error))) + {:dispatch-later [{:ms delay :dispatch [:inbox/fetch-peers (inc retries)]}]}))))))) + + (handlers/register-handler-fx - ::get-sym-key - ;; TODO(yenda): using core async flow this event can be done in parallel - ;; with add-peer - (fn [{:keys [db]} [_ response]] + :inbox/get-sym-key + (fn [{:keys [db]} _] (let [web3 (:web3 db) wnode (get-current-wnode-address db) password (:inbox/password db)] - (log/info "offline inbox: mark-trusted-peer response" wnode response) {:shh/generate-sym-key-from-password {:password password :web3 web3 :on-success (fn [_ sym-key-id] - (re-frame/dispatch [::request-messages sym-key-id])) + (re-frame/dispatch [:inbox/get-sym-key-success sym-key-id])) :on-error #(log/error "offline inbox: get-sym-key error" %)}}))) (handlers/register-handler-fx - ::request-messages - ;; TODO(yenda): we want to request-message once per topic and for specific timespan so - ;; we want a plural version of this function that does the right thing + :inbox/get-sym-key-success (fn [{:keys [db]} [_ sym-key-id]] - (log/info "offline inbox: get-sym-key response") sym-key-id + {:db (assoc db :inbox/sym-key-id sym-key-id)})) + +(handlers/register-handler-fx + :inbox/connection-success + (fn [{:keys [db]} _] + {:db (assoc db :mailserver-status :connected)})) + +(handlers/register-handler-fx + :inbox/request-messages + (fn [{:keys [db now]} [_ {:keys [from topics]}]] (let [web3 (:web3 db) wnode (get-current-wnode-address db) - topics (map #(:topic %) (vals (:transport/chats db))) - to nil - from nil] + topics (or topics + (map #(:topic %) (vals (:transport/chats db)))) + from (or from (:inbox/last-request db) nil) + sym-key-id (:inbox/sym-key-id db)] {::request-messages {:wnode wnode :topics topics - :to to - :from from + ;;TODO (yenda) fix from, right now mailserver is dropping us + ;;when we send a requestMessage with a from field + ;;:from from :sym-key-id sym-key-id - :web3 web3}}))) + :web3 web3} + :db (assoc db :inbox/last-request (quot now 1000))}))) diff --git a/src/status_im/transport/message/v1/public_chat.cljs b/src/status_im/transport/message/v1/public_chat.cljs index 18e9962d92..89a5a7bc3b 100644 --- a/src/status_im/transport/message/v1/public_chat.cljs +++ b/src/status_im/transport/message/v1/public_chat.cljs @@ -25,18 +25,19 @@ (protocol/init-chat chat-id))))) (handlers/register-handler-fx - ::add-new-sym-key - (fn [{:keys [db] :as cofx} [_ {:keys [sym-key-id sym-key chat-id]}]] - (let [{:keys [web3]} db] - (handlers-macro/merge-fx - cofx - {:db (assoc-in db [:transport/chats chat-id :sym-key-id] sym-key-id) - :shh/add-filter {:web3 web3 - :sym-key-id sym-key-id - :topic (transport.utils/get-topic chat-id) - :chat-id chat-id} + ::add-new-sym-key + (fn [{:keys [db] :as cofx} [_ {:keys [sym-key-id sym-key chat-id]}]] + (let [{:keys [web3]} db + topic (transport.utils/get-topic chat-id)] + {:db (assoc-in db [:transport/chats chat-id :sym-key-id] sym-key-id) + :shh/add-filter {:web3 web3 + :sym-key-id sym-key-id + :topic topic + :chat-id chat-id} :data-store.transport/save {:chat-id chat-id :chat (-> (get-in db [:transport/chats chat-id]) (assoc :sym-key-id sym-key-id) ;;TODO (yenda) remove once go implements persistence - (assoc :sym-key sym-key))}})))) + (assoc :sym-key sym-key))} + :dispatch [:inbox/request-messages {:topics [topic] + :from 0}]}))) diff --git a/src/status_im/ui/screens/db.cljs b/src/status_im/ui/screens/db.cljs index 83855493b0..13c0c6ea34 100644 --- a/src/status_im/ui/screens/db.cljs +++ b/src/status_im/ui/screens/db.cljs @@ -166,6 +166,8 @@ :node/after-stop :inbox/wnodes :inbox/password + :inbox/sym-key-id + :inbox/last-request :browser/browsers :browser/options :new/open-dapp diff --git a/src/status_im/ui/screens/offline_messaging_settings/db.cljs b/src/status_im/ui/screens/offline_messaging_settings/db.cljs index 15a82674bc..36b8f59d47 100644 --- a/src/status_im/ui/screens/offline_messaging_settings/db.cljs +++ b/src/status_im/ui/screens/offline_messaging_settings/db.cljs @@ -13,3 +13,5 @@ (spec/def :inbox/password ::not-blank-string) (spec/def :inbox/wnodes (spec/nilable (spec/map-of keyword? (spec/map-of :wnode/id :wnode/wnode)))) +(spec/def :inbox/sym-key-id string?) +(spec/def :inbox/last-request integer?)