refactor login flow

simplified flow:

- event `:ui/login` is dispatched
- node is initialized with user config or default config
- `node.started` signal is received, applying `:login` fx
- `:callback/login` event is dispatched, account is changed
in datastore, web-data is cleared
- `:init/initialize-account` event is dispatched

replace event dispatches by function composition

fix bug in universal links where url to be processed after login
was never removed

Signed-off-by: Eric Dvorsak <eric@dvorsak.fr>
This commit is contained in:
Eric Dvorsak 2018-08-27 06:51:04 +02:00
parent dacfe97a58
commit bb339dc39b
No known key found for this signature in database
GPG Key ID: 932AC1CE5F05DE0C
23 changed files with 432 additions and 526 deletions

View File

@ -1,25 +1,27 @@
(ns status-im.init.core
(:require [re-frame.core :as re-frame]
[status-im.constants :as constants]
[status-im.data-store.realm.core :as realm]
[status-im.i18n :as i18n]
[status-im.models.account :as models.account]
[status-im.models.browser :as browser]
[status-im.models.chat :as chat]
[status-im.models.contacts :as models.contacts]
[status-im.utils.keychain.core :as keychain]
[status-im.models.protocol :as models.protocol]
[status-im.models.transactions :as transactions]
[status-im.models.wallet :as models.wallet]
[status-im.transport.inbox :as inbox]
[status-im.data-store.realm.core :as realm]
[status-im.ui.screens.accounts.models :as accounts.models]
[status-im.node.models :as node]
[status-im.notifications.core :as notifications]
[status-im.ui.screens.accounts.login.models :as login]
[status-im.ui.screens.contacts.events :as contacts]
[status-im.i18n :as i18n]
[status-im.ui.screens.db :refer [app-db]]
[status-im.ui.screens.navigation :as navigation]
[status-im.utils.ethereum.core :as ethereum]
[status-im.utils.handlers-macro :as handlers-macro]
[status-im.utils.keychain.core :as keychain]
[status-im.utils.platform :as platform]
[taoensso.timbre :as log]
[status-im.utils.universal-links.core :as universal-links]))
[status-im.utils.universal-links.core :as universal-links]
[taoensso.timbre :as log]))
;; TODO (yenda) move keychain functions to dedicated namespace
(defn- reset-keychain []
@ -60,32 +62,9 @@
(defn set-device-uuid [device-uuid {:keys [db]}]
{:db (assoc db :device-UUID device-uuid)})
(defn initialize-views [{{:accounts/keys [accounts]
:push-notifications/keys [initial?]
:as db} :db}]
{:db (if (empty? accounts)
(assoc db :view-id :intro :navigation-stack (list :intro))
(let [{:keys [address photo-path name]} (first (sort-by :last-sign-in > (vals accounts)))]
(-> db
(assoc :view-id :login
:navigation-stack (list :login))
(update :accounts/login assoc
:address address
:photo-path photo-path
:name name))))
:notifications/handle-initial-push-notification initial?})
(defn initialize-geth [{:keys [db]}]
(when-not (:status-node-started? db)
(let [default-networks (:networks/networks db)
default-network (:network db)]
{:init/initialize-geth (get-in default-networks [default-network :config])})))
(defn initialize-db
"Initialize db to the initial state"
[{{:universal-links/keys [url]
:push-notifications/keys [initial?]
:keys [status-module-initialized? status-node-started?
"Initialize db to initial state"
[{{:keys [status-module-initialized? status-node-started?
network-status network peers-count peers-summary device-UUID]
:or {network (get app-db :network)}} :db}]
{:db (assoc app-db
@ -96,8 +75,6 @@
:status-module-initialized? (or platform/ios? js/goog.DEBUG status-module-initialized?)
:status-node-started? status-node-started?
:network network
:universal-links/url url
:push-notifications/initial? initial?
:device-UUID device-UUID)})
(defn initialize-app [encryption-key error cofx]
@ -110,29 +87,44 @@
:else
(handlers-macro/merge-fx cofx
{:init/init-device-UUID nil
:init/init-store encryption-key
:ui/listen-to-window-dimensions-change nil
:init/testfairy-alert nil}
{:init/init-device-UUID nil
:init/init-store encryption-key
:ui/listen-to-window-dimensions-change nil
:init/testfairy-alert nil
:notifications/handle-initial-push-notification nil}
(initialize-db))))
(defn load-accounts [{:keys [db all-accounts]}]
(let [accounts (->> all-accounts
(map (fn [{:keys [address] :as account}]
[address account]))
(into {}))]
{:db (assoc db :accounts/accounts accounts)}))
(defn initialize-views [cofx]
(let [{{:accounts/keys [accounts] :as db} :db} cofx]
(if (empty? accounts)
(navigation/navigate-to-clean :intro cofx)
(let [{:keys [address photo-path name]} (first (sort-by :last-sign-in > (vals accounts)))]
(login/open-login address photo-path name cofx)))))
(defn after-decryption [cofx]
(handlers-macro/merge-fx cofx
{:network/listen-to-network-status
[#(re-frame/dispatch [:network/update-connection-status %])
#(re-frame/dispatch [:network/update-network-status %])]}
(initialize-geth)
(accounts.models/load-accounts)
(node/start)
(load-accounts)
(initialize-views)))
(defn initialize-account-db [address {:keys [db web3]}]
(let [{:keys [accounts/accounts accounts/create contacts/contacts networks/networks
(let [{:universal-links/keys [url]
:keys [accounts/accounts accounts/create contacts/contacts networks/networks
network network-status peers-count peers-summary view-id navigation-stack
status-module-initialized? status-node-started? device-UUID
push-notifications/initial? semaphores]
status-module-initialized? status-node-started? device-UUID semaphores]
:or {network (get app-db :network)}} db
console-contact (get contacts constants/console-chat-id)
current-account (accounts address)
current-account (get accounts address)
account-network-id (get current-account :network network)
account-network (get-in current-account [:networks account-network-id])]
{:db (cond-> (assoc app-db
@ -147,21 +139,29 @@
:network-status network-status
:network network
:chain (ethereum/network->chain-name account-network)
:push-notifications/initial? initial?
:universal-links/url url
:peers-summary peers-summary
:peers-count peers-count
:device-UUID device-UUID
:semaphores semaphores
:web3 web3)
(= view-id :create-account)
(assoc-in [:accounts/create :step] :enter-name)
console-contact
(assoc :contacts/contacts {constants/console-chat-id console-contact}))}))
(defn initialize-account [address events-after {:keys [web3] :as cofx}]
(defn login-only-events [address {:keys [db] :as cofx}]
(when (not= (:view-id db) :create-account)
(handlers-macro/merge-fx cofx
(navigation/navigate-to-clean :home)
(universal-links/process-stored-event)
(notifications/process-stored-event address))))
(defn initialize-account [address {:keys [web3] :as cofx}]
(handlers-macro/merge-fx cofx
{:web3/set-default-account [web3 address]
:web3/fetch-node-version web3
:notifications/get-fcm-token nil
:dispatch-n (or events-after [])}
:notifications/get-fcm-token nil}
(initialize-account-db address)
(models.protocol/initialize-protocol address)
(models.contacts/load-contacts)
@ -172,17 +172,5 @@
(models.wallet/update-wallet)
(transactions/run-update)
(transactions/start-sync)
(models.account/update-sign-in-time)))
(defn status-node-started [{{:node/keys [after-start] :as db} :db}]
;;TODO (yenda) instead of passing events we can pass effects here and simply return them
(merge {:db (assoc db :status-node-started? true)}
(when after-start {:dispatch-n [after-start]})))
(defn status-node-stopped [{{:node/keys [after-stop]} :db}]
;;TODO (yenda) instead of passing events we can pass effects here and simply return them
(when after-stop {:dispatch-n [after-stop]}))
(defn status-module-initialized [{:keys [db]}]
{:db (assoc db :status-module-initialized? true)
:init/status-module-initialized-fx nil})
(models.account/update-sign-in-time)
(login-only-events address)))

View File

@ -22,11 +22,6 @@
(log/warn "Could not decrypt database" error)
(re-frame/dispatch [:init/initialize-app encryption-key :decryption-failed]))))))
(re-frame/reg-fx
:init/initialize-geth
(fn [config]
(status/start-node (types/clj->json config) config/fleet)))
(re-frame/reg-fx
:init/status-module-initialized
(fn [_]
@ -82,13 +77,8 @@
(re-frame/inject-cofx :data-store/transport)
(re-frame/inject-cofx :data-store/all-browsers)
(re-frame/inject-cofx :data-store/all-dapp-permissions)]
(fn [cofx [_ address events-after]]
(init/initialize-account address events-after cofx)))
(handlers/register-handler-fx
:init/initialize-geth
(fn [cofx _]
(init/initialize-geth cofx)))
(fn [cofx [_ address]]
(init/initialize-account address cofx)))
(handlers/register-handler-fx
:init/set-device-UUID

View File

@ -0,0 +1,14 @@
(ns status-im.node.events
(:require [re-frame.core :as re-frame]
[status-im.native-module.core :as status]
[status-im.utils.config :as config]))
(re-frame/reg-fx
:node/start
(fn [config]
(status/start-node config config/fleet)))
(re-frame/reg-fx
:node/stop
(fn [config]
(status/stop-node)))

View File

@ -0,0 +1,54 @@
(ns status-im.node.models
(:require [status-im.utils.config :as config]
[status-im.utils.types :as types]))
(defn- add-custom-bootnodes [config network all-bootnodes]
(let [bootnodes (as-> all-bootnodes $
(get $ network)
(vals $)
(map :address $))]
(if (seq bootnodes)
(assoc config :ClusterConfig {:Enabled true
:BootNodes bootnodes})
config)))
(defn get-account-network [db address]
(get-in db [:accounts/accounts address :network]))
(defn- get-account-node-config [db address]
(let [accounts (get db :accounts/accounts)
{:keys [network
settings
bootnodes
networks]} (get accounts address)
use-custom-bootnodes (get-in settings [:bootnodes network])]
(cond-> (get-in networks [network :config])
(and
config/bootnodes-settings-enabled?
use-custom-bootnodes)
(add-custom-bootnodes network bootnodes))))
(defn start
([cofx]
(start nil cofx))
([address {:keys [db]}]
(let [network (if address
(get-account-network db address)
(:network db))
node-config (if address
(get-account-node-config db address)
(get-in (:networks/networks db) [network :config]))
node-config-json (types/clj->json node-config)]
{:db (assoc db
:network network)
:node/start node-config-json})))
(defn restart
[]
{:node/stop nil})
(defn initialize
[address {{:keys [status-node-started?] :as db} :db :as cofx}]
(if (not status-node-started?)
(start address cofx)
(restart)))

View File

@ -79,59 +79,42 @@
first)]
(when address
{:db (assoc-in db [:push-notifications/stored to] from)
:dispatch [:open-login address photo-path name]})))
:dispatch [:ui/open-login address photo-path name]})))
(defn process-initial-push-notification [{:keys [initial?]} {:keys [db]}]
(when initial?
{:db (assoc db :push-notifications/initial? true)}))
(defn process-push-notification [{:keys [from to] :as event} {:keys [db] :as cofx}]
(defn handle-push-notification [{:keys [from to] :as event} {:keys [db] :as cofx}]
(let [current-public-key (get-in cofx [:db :current-public-key])]
(if current-public-key
;; TODO(yenda) why do we ignore the notification if
;; it is not for the current account ?
(when (= to current-public-key)
{:db (update db :push-notifications/stored dissoc to)
:dispatch [:navigate-to-chat from]})
(store-event event cofx))))
(defn handle-push-notification
[cofx [_ event]]
(handlers-macro/merge-fx cofx
(process-initial-push-notification event)
(process-push-notification event)))
(defn stored-event [address cofx]
(let [to (get-in cofx [:db :accounts/accounts address :public-key])
from (get-in cofx [:db :push-notifications/stored to])]
(when from
[:notification/handle-push-notification {:from from
:to to}])))
(defn parse-notification-payload [s]
(try
(js/JSON.parse s)
(catch :default _
#js {})))
(defn handle-notification-event [event {:keys [initial?]}]
(defn handle-notification-event [event]
(let [msg (object/get (.. event -notification -data) "msg")
data (parse-notification-payload msg)
from (object/get data "from")
to (object/get data "to")]
(log/debug "on notification" (pr-str msg))
(when (and from to)
(re-frame/dispatch [:notification/handle-push-notification {:from from
:to to
:initial? initial?}]))))
(re-frame/dispatch [:notification/handle-push-notification {:from from
:to to}]))))
(defn handle-initial-push-notification
[initial?]
(when-not initial?
(.. firebase
notifications
getInitialNotification
(then (fn [event]
(when event
(handle-notification-event event {:initial? true})))))))
[]
(.. firebase
notifications
getInitialNotification
(then (fn [event]
(when event
(handle-notification-event event))))))
(defn on-notification-opened []
(.. firebase
@ -166,3 +149,12 @@
(on-notification-opened)
(when platform/android?
(create-notification-channel))))
(defn process-stored-event [address cofx]
(when-not platform/desktop?
(let [to (get-in cofx [:db :accounts/accounts address :public-key])
from (get-in cofx [:db :push-notifications/stored to])]
(when from
(handle-push-notification {:from from
:to to}
cofx)))))

View File

@ -26,7 +26,8 @@
(handlers/register-handler-fx
:notifications/handle-push-notification
notifications/handle-push-notification)
(fn [cofx [_ event]]
(notifications/handle-push-notification event cofx)))
(handlers/register-handler-db
:notifications/update-fcm-token

View File

@ -2,10 +2,30 @@
(:require [status-im.init.core :as init]
[status-im.transport.handlers :as transport.handlers]
[status-im.transport.inbox :as inbox]
[status-im.ui.screens.accounts.login.models :as login]
[status-im.node.models :as node]
[status-im.utils.handlers-macro :as handlers-macro]
[status-im.utils.types :as types]
[taoensso.timbre :as log]))
(defn status-node-started
[{db :db :as cofx}]
(let [fx {:db (assoc db :status-node-started? true)}]
(if (:password (login/credentials cofx))
(handlers-macro/merge-fx cofx
fx
(login/login))
fx)))
(defn status-node-stopped
[cofx]
(let [{:keys [address]} (login/credentials cofx)]
(node/start address cofx)))
(defn status-module-initialized [{:keys [db]}]
{:db (assoc db :status-module-initialized? true)
:init/status-module-initialized nil})
(defn summary [peers-summary {:keys [db] :as cofx}]
(let [previous-summary (:peers-summary db)
peers-count (count peers-summary)]
@ -19,9 +39,9 @@
(defn process [event-str cofx]
(let [{:keys [type event]} (types/json->clj event-str)]
(case type
"node.started" (init/status-node-started cofx)
"node.stopped" (init/status-node-stopped cofx)
"module.initialized" (init/status-module-initialized cofx)
"node.started" (status-node-started cofx)
"node.stopped" (status-node-stopped cofx)
"module.initialized" (status-module-initialized cofx)
"envelope.sent" (transport.handlers/update-envelope-status (:hash event) :sent cofx)
"envelope.expired" (transport.handlers/update-envelope-status (:hash event) :sent cofx)
"discovery.summary" (summary event cofx)

View File

@ -16,7 +16,6 @@
[status-im.utils.handlers-macro :as handlers-macro]
[status-im.data-store.core :as data-store]
[status-im.models.mailserver :as models.mailserver]
[status-im.ui.screens.accounts.events :as accounts]
[status-im.data-store.transport :as transport-store]))
;; How does offline inboxing work ?

View File

@ -1,65 +1,40 @@
(ns status-im.ui.screens.accounts.login.events
(:require status-im.ui.screens.accounts.login.navigation
[re-frame.core :as re-frame]
[status-im.utils.handlers :as handlers]
[status-im.utils.handlers-macro :as handlers-macro]
[status-im.ui.screens.accounts.login.models :as models]))
(:require [re-frame.core :as re-frame]
[status-im.ui.screens.accounts.login.models :as models]
[status-im.utils.handlers :as handlers]))
;;;; FX
(re-frame/reg-fx :stop-node models/stop-node!)
(re-frame/reg-fx
:login
(fn [[address password save-password]]
(models/login! address password save-password)))
(fn [[address password save-password?]]
(models/login! address password save-password?)))
(re-frame/reg-fx
:clear-web-data
models/clear-web-data!)
(re-frame/reg-fx
:change-account
(fn [[address]]
(models/handle-change-account! address)))
:data-store/change-account
(fn [address]
(models/change-account! address)))
;;;; Handlers
(handlers/register-handler-fx
:ui/login
(fn [cofx _]
(models/user-login cofx)))
(handlers/register-handler-fx
:open-login
:callback/login
(fn [cofx [_ login-result]]
(models/user-login-callback login-result cofx)))
(handlers/register-handler-fx
:ui/open-login
(fn [cofx [_ address photo-path name]]
(models/open-login address photo-path name cofx)))
(handlers/register-handler-fx
:do-login
(fn [cofx [_ address photo-path name password]]
(handlers-macro/merge-fx cofx
(models/navigate-to-login address photo-path name password)
;; models/login-account takes care about empty password
(models/login-account address password (not (empty? password))))))
(handlers/register-handler-fx
:login-account-internal
(fn [cofx [_ address password save-password]]
(models/login-account-internal address password save-password cofx)))
(handlers/register-handler-fx
:start-node
(fn [cofx [_ address password save-password]]
(models/start-node address password save-password cofx)))
(handlers/register-handler-fx
:login-account
(fn [cofx [_ address password save-password]]
(models/login-account address password save-password cofx)))
(handlers/register-handler-fx
:login-handler
(fn [cofx [_ login-result address password save-password]]
(models/login-handler login-result address password save-password cofx)))
(handlers/register-handler-fx
:change-account-handler
(fn [cofx [_ address]]
(models/change-account-handler address cofx)))
:callback/open-login
(fn [cofx [_ password]]
(models/open-login-callback password cofx)))

View File

@ -1,148 +1,81 @@
(ns status-im.ui.screens.accounts.login.models
(:require status-im.ui.screens.accounts.login.navigation
[re-frame.core :as re-frame]
[status-im.utils.types :as types]
(:require [re-frame.core :as re-frame]
[status-im.data-store.core :as data-store]
[status-im.native-module.core :as status]
[status-im.utils.config :as config]
[status-im.node.models :as node]
[status-im.ui.screens.navigation :as navigation]
[status-im.utils.handlers-macro :as handlers-macro]
[status-im.utils.keychain.core :as keychain]
[status-im.notifications.core :as notifications]
[status-im.utils.platform :as platform]
[status-im.utils.universal-links.core :as universal-links]))
[status-im.utils.types :as types]))
;;;; FX
;; login flow:
;;
;; - event `:ui/login` is dispatched
;; - node is initialized with user config or default config
;; - `node.started` signal is received, applying `:login` fx
;; - `:callback/login` event is dispatched, account is changed in datastore, web-data is cleared
;; - `:init/initialize-account` event is dispatched
(defn stop-node! [] (status/stop-node))
(defn credentials [cofx]
(select-keys (get-in cofx [:db :accounts/login]) [:address :password :save-password?]))
(defn login! [address password save-password]
(status/login address
password
#(re-frame/dispatch [:login-handler % address password save-password])))
(defn login! [address password save-password?]
(status/login address password #(re-frame/dispatch [:callback/login %])))
(defn clear-web-data! []
(status/clear-web-data))
(defn- change-account! [address encryption-key]
(data-store/change-account address encryption-key)
(re-frame/dispatch [:change-account-handler address]))
(defn handle-change-account! [address]
(defn change-account! [address]
;; No matter what is the keychain we use, as checks are done on decrypting base
(.. (keychain/safe-get-encryption-key)
(then (partial change-account! address))
(then (fn [encryption-key]
(data-store/change-account address encryption-key)
(re-frame/dispatch [:init/initialize-account address])))
(catch (fn [error]
;; If all else fails we fallback to showing initial error
(re-frame/dispatch [:init/initialize-app "" :decryption-failed])))))
;;;; Handlers
(defn login [cofx]
(let [{:keys [address password save-password?]} (credentials cofx)]
{:login [address password save-password?]}))
(defn navigate-to-login [address photo-path name password {db :db}]
{:db (update db
:accounts/login assoc
:address address
:photo-path photo-path
:password password
:name name)
:can-save-user-password [#(re-frame/dispatch [:set-in [:accounts/login :can-save-password] %])]
:dispatch [:navigate-to :login]})
(defn user-login [{:keys [db] :as cofx}]
(handlers-macro/merge-fx cofx
{:db (assoc-in db [:accounts/login :processing] true)}
(node/initialize (get-in db [:accounts/login :address]))))
(defn wrap-with-login-account-fx [db address password save-password]
{:db db
:login [address password save-password]})
(defn open-login [address photo-path name cofx]
{:get-user-password [address #(re-frame/dispatch [:do-login address photo-path name %])]})
(defn login-account-internal [address password save-password {db :db}]
(wrap-with-login-account-fx
(assoc db :node/after-start nil)
address password save-password))
(defn- add-custom-bootnodes [config network all-bootnodes]
(let [bootnodes (as-> all-bootnodes $
(get $ network)
(vals $)
(map :address $))]
(if (seq bootnodes)
(assoc config :ClusterConfig {:Enabled true
:BootNodes bootnodes})
config)))
(defn- get-network-by-address [db address]
(let [accounts (get db :accounts/accounts)
{:keys [network
settings
bootnodes
networks]} (get accounts address)
use-custom-bootnodes (get-in settings [:bootnodes network])
config (cond-> (get-in networks [network :config])
(and
config/bootnodes-settings-enabled?
use-custom-bootnodes)
(add-custom-bootnodes network bootnodes))]
{:use-custom-bootnodes use-custom-bootnodes
:network network
:config config}))
(defn- wrap-with-initialize-geth [db address password save-password]
(let [{:keys [network config]} (get-network-by-address db address)]
{:init/initialize-geth config
:db (assoc db
:network network
:node/after-start [:login-account-internal address password save-password])}))
(defn start-node [address password save-password {db :db}]
(wrap-with-initialize-geth
(assoc db :node/after-stop nil)
address password save-password))
(defn- wrap-with-stop-node-fx [db address password]
{:db (assoc db :node/after-stop [:start-node address password])
:stop-node nil})
(defn- restart-node? [account-network network use-custom-bootnodes]
(or (not= account-network network)
(and config/bootnodes-settings-enabled?
use-custom-bootnodes)))
(defn login-account [address password save-password {{:keys [network status-node-started?] :as db} :db}]
(let [{use-custom-bootnodes :use-custom-bootnodes
account-network :network} (get-network-by-address db address)
db' (-> db
(assoc-in [:accounts/login :processing] true))
wrap-fn (cond (not status-node-started?)
wrap-with-initialize-geth
(not (restart-node? account-network
network
use-custom-bootnodes))
wrap-with-login-account-fx
:else
wrap-with-stop-node-fx)]
(when-not (empty? password)
(wrap-fn db' address password save-password))))
(defn login-handler [login-result address password save-password {db :db}]
(defn user-login-callback [login-result {db :db :as cofx}]
(let [data (types/json->clj login-result)
error (:error data)
success (zero? (count error))
db' (assoc-in db [:accounts/login :processing] false)]
success (empty? error)]
(if success
(merge
{:db db
:clear-web-data nil
:change-account [address]}
(when save-password
{:save-user-password [address password]}))
{:db (assoc-in db' [:accounts/login :error] error)})))
(let [{:keys [address password save-password?]} (credentials cofx)]
(merge {:clear-web-data nil
:data-store/change-account address}
(when save-password?
{:save-user-password [address password]})))
{:db (update db :accounts/login assoc
:error error
:processing false)})))
(defn change-account-handler [address {{:keys [view-id] :as db} :db :as cofx}]
{:db (cond-> (dissoc db :accounts/login)
(= view-id :create-account)
(assoc-in [:accounts/create :step] :enter-name))
:dispatch [:init/initialize-account address
(when (not= view-id :create-account)
[[:navigate-to-clean :home]
(universal-links/stored-url-event cofx)
(when-not platform/desktop? (notifications/stored-event address cofx))])]})
(defn open-login [address photo-path name {:keys [db]}]
{:db (-> db
(update :accounts/login assoc
:address address
:photo-path photo-path
:name name)
(update :accounts/login dissoc
:error
:password))
:can-save-user-password? nil
:get-user-password [address
#(re-frame/dispatch [:callback/open-login %])]})
(defn open-login-callback
[password {:keys [db] :as cofx}]
(if password
(handlers-macro/merge-fx cofx
{:db (assoc-in db [:accounts/login :password] password)}
(user-login))
(navigation/navigate-to-cofx :login nil cofx)))

View File

@ -1,6 +0,0 @@
(ns status-im.ui.screens.accounts.login.navigation
(:require [status-im.ui.screens.navigation :as nav]))
(defmethod nav/preload-data! :login
[db]
(update db :accounts/login dissoc :error))

View File

@ -26,9 +26,9 @@
[toolbar/nav-button act/default-back])
[toolbar/content-title (i18n/label :t/sign-in-to-status)]])
(defn login-account [password-text-input address password save-password]
(defn login-account [password-text-input]
(.blur password-text-input)
(re-frame/dispatch [:login-account address password save-password]))
(re-frame/dispatch [:ui/login]))
(defn- error-key [error]
;; TODO Improve selection logic when status-go provide an error code
@ -52,49 +52,38 @@
name]]])
(defview login []
(letsubs [{:keys [address photo-path name password error processing save-password can-save-password]} [:get :accounts/login]
(letsubs [{:keys [address photo-path name password error processing save-password? can-save-password?]} [:get :accounts/login]
can-navigate-back? [:can-navigate-back?]
password-text-input (atom nil)]
;; due to async nature of UI, this function can be called when
;; :accounts/login is already removed
;; (e.g. when the data is removed but we didn't navigate to the next screen)
;; in that case, we will show a basic progress bar.
[react/keyboard-avoiding-view {:style ast/accounts-view}
[status-bar/status-bar]
[login-toolbar can-navigate-back?]
[components.common/separator]
[react/view styles/login-view
;; so, if this component is rendered with no data
(if (or (empty? address) (empty? photo-path))
;; we will show an activiy indicator
[react/view styles/processing-view
[components/activity-indicator {:animating true}]]
;; otherwise, we will render it properly
[react/view styles/login-badge-container
[account-login-badge photo-path name]
[react/view {:style styles/password-container
:important-for-accessibility :no-hide-descendants}
[text-input/text-input-with-label
{:label (i18n/label :t/password)
:placeholder (i18n/label :t/password)
:ref #(reset! password-text-input %)
:auto-focus can-navigate-back? ;;this needed because keyboard overlays testfairy alert
:on-submit-editing #(login-account @password-text-input address password save-password)
:on-change-text #(do
(re-frame/dispatch [:set-in [:accounts/login :password] %])
(re-frame/dispatch [:set-in [:accounts/login :error] ""]))
:secure-text-entry true
:text password
:error (when (pos? (count error)) (i18n/label (error-key error)))}]]
(when platform/ios?
[react/view {:style styles/save-password-checkbox-container}
[profile.components/settings-switch-item
{:label-kw (if can-save-password
:t/save-password
:t/save-password-unavailable)
:active? can-save-password
:value save-password
:action-fn #(re-frame/dispatch [:set-in [:accounts/login :save-password] %])}]])])]
[react/view styles/login-badge-container
[account-login-badge photo-path name]
[react/view {:style styles/password-container
:important-for-accessibility :no-hide-descendants}
[text-input/text-input-with-label
{:label (i18n/label :t/password)
:placeholder (i18n/label :t/password)
:ref #(reset! password-text-input %)
:auto-focus can-navigate-back? ;;this needed because keyboard overlays testfairy alert
:on-submit-editing #(login-account @password-text-input)
:on-change-text #(do
(re-frame/dispatch [:set-in [:accounts/login :password] %])
(re-frame/dispatch [:set-in [:accounts/login :error] ""]))
:secure-text-entry true
:error (when (not-empty error) (i18n/label (error-key error)))}]]
(when platform/ios?
[react/view {:style styles/save-password-checkbox-container}
[profile.components/settings-switch-item
{:label-kw (if can-save-password?
:t/save-password
:t/save-password-unavailable)
:active? can-save-password?
:value save-password?
:action-fn #(re-frame/dispatch [:set-in [:accounts/login :save-password?] %])}]])]]
(when processing
[react/view styles/processing-view
[components/activity-indicator {:animating true}]
@ -110,4 +99,4 @@
{:forward? true
:label (i18n/label :t/sign-in)
:disabled? (not (spec/valid? ::db/password password))
:on-press #(login-account @password-text-input address password save-password)}]])]))
:on-press #(login-account @password-text-input)}]])]))

View File

@ -5,9 +5,9 @@
[status-im.data-store.accounts :as accounts-store]
[status-im.i18n :as i18n]
[status-im.native-module.core :as status]
[status-im.ui.screens.accounts.login.models :as login.models]
[status-im.ui.screens.accounts.statuses :as statuses]
[status-im.ui.screens.accounts.utils :as accounts.utils]
[status-im.ui.screens.accounts.login.models :as login.models]
[status-im.ui.screens.navigation :as navigation]
[status-im.ui.screens.wallet.settings.models :as wallet.settings.models]
[status-im.utils.config :as config]
@ -68,15 +68,11 @@
(log/debug "account-created")
(when-not (str/blank? pubkey)
(handlers-macro/merge-fx cofx
{:db (assoc db :accounts/login {:address normalized-address
:password password
:processing true})}
(add-account account)
(login.models/login-account normalized-address password false)))))
(defn load-accounts [{:keys [db all-accounts]}]
(let [accounts (->> all-accounts
(map (fn [{:keys [address] :as account}]
[address account]))
(into {}))]
{:db (assoc db :accounts/accounts accounts)}))
(login.models/user-login)))))
(defn update-settings
([settings cofx] (update-settings settings nil cofx))

View File

@ -83,8 +83,7 @@
(handlers-macro/merge-fx cofx
{:db (assoc-in db [:accounts/recover :processing?] false)}
(accounts.models/on-account-created account password true)
(login.models/open-login (:address account) (:photo-path account) (:name account)))))
(accounts.models/on-account-created account password true))))
(defn recover-account [{:keys [db]}]
(let [{:keys [password passphrase]} (:accounts/recover db)]

View File

@ -15,7 +15,7 @@
[status-im.ui.screens.privacy-policy.views :as privacy-policy]))
(defn account-view [{:keys [address photo-path name public-key]}]
[react/touchable-highlight {:on-press #(re-frame/dispatch [:open-login address photo-path name])}
[react/touchable-highlight {:on-press #(re-frame/dispatch [:ui/open-login address photo-path name])}
[react/view styles/account-view
[photos/photo photo-path {:size styles/account-image-size}]
[react/view styles/account-badge-text-view

View File

@ -55,8 +55,7 @@
:tooltips {}
:desktop/desktop {:tab-view-id :home}
:dimensions/window (dimensions/window)
:push-notifications/stored {}
:push-notifications/initial? false})
:push-notifications/stored {}})
;;;;GLOBAL
@ -147,9 +146,6 @@
;;;;NODE
(spec/def :node/after-start (spec/nilable vector?))
(spec/def :node/after-stop (spec/nilable vector?))
(spec/def ::message-envelopes (spec/nilable map?))
;;;;UUID
@ -164,10 +160,7 @@
(spec/def :dimensions/window map?)
;; PUSH NOTIFICATIONS
(spec/def :push-notifications/stored (spec/nilable map?))
; Shows that push notification used to start the application is processed
(spec/def :push-notifications/initial? (spec/nilable boolean?))
(spec/def ::semaphores set?)
@ -202,15 +195,12 @@
:networks/manage
:mailservers/manage
:bootnodes/manage
:node/after-start
:node/after-stop
:inbox/wnodes
:inbox/last-received
:inbox/current-id
:inbox/fetching?
:universal-links/url
:push-notifications/stored
:push-notifications/initial?
:browser/browsers
:browser/options
:new/open-dapp

View File

@ -15,8 +15,10 @@
status-im.ui.screens.group.events
[status-im.ui.screens.navigation :as navigation]
[status-im.utils.dimensions :as dimensions]
status-im.ui.screens.accounts.events
status-im.utils.universal-links.events
status-im.init.events
status-im.node.events
status-im.signals.events
status-im.web3.events
status-im.notifications.events
@ -137,7 +139,7 @@
(let [{:transport/keys [chats]} db]
(handlers-macro/merge-fx cofx
{:dispatch [:init/initialize-keychain]
:clear-user-password [(get-in db [:account/account :address])]}
:clear-user-password (get-in db [:account/account :address])}
(navigation/navigate-to-clean nil)
(transport/stop-whisper))))

View File

@ -2,6 +2,7 @@
(:require [re-frame.core :as re-frame]
[taoensso.timbre :as log]
[status-im.react-native.js-dependencies :as rn]
[status-im.utils.handlers :as handlers]
[status-im.utils.platform :as platform]))
(def key-bytes 64)
@ -13,9 +14,9 @@
(defn- string->js-array [s]
(.parse js/JSON (.-password s)))
;; ********************************************************************************
;; ********************************************************************************
;; Storing / Retrieving a user password to/from Keychain
;; ********************************************************************************
;; ********************************************************************************
;;
;; We are using set/get/reset internet credentials there because they are bound
;; to an address (`server`) property.
@ -26,19 +27,19 @@
;; We need a more strict access mode for keychain entries that save user password.
;; iOS
;; see this article for more details:
;; see this article for more details:
;; https://developer.apple.com/documentation/security/keychain_services/keychain_items/restricting_keychain_item_accessibility?language=objc
(def keychain-restricted-availability
;; From Apple's documentation:
;; > The kSecAttrAccessible attribute enables you to control item availability
;; > relative to the lock state of the device.
;; > It also lets you specify eligibility for restoration to a new device.
;; > If the attribute ends with the string ThisDeviceOnly,
;; > the item can be restored to the same device that created a backup,
;; > The kSecAttrAccessible attribute enables you to control item availability
;; > relative to the lock state of the device.
;; > It also lets you specify eligibility for restoration to a new device.
;; > If the attribute ends with the string ThisDeviceOnly,
;; > the item can be restored to the same device that created a backup,
;; > but it isnt migrated when restoring another devices backup data.
;; > ...
;; > For extremely sensitive data
;; > THAT YOU NEVER WANT STORED IN iCloud,
;; > For extremely sensitive data
;; > THAT YOU NEVER WANT STORED IN iCloud,
;; > you might choose kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly.
;; That is exactly what we use there.
;; Note that the password won't be stored if the device isn't locked by a passcode.
@ -47,7 +48,7 @@
;; Stores the password for the address to the Keychain
(defn save-user-password [address password callback]
(if-not platform/ios?
(callback) ;; no-op on Androids (for now)
(callback true) ;; no-op on Androids (for now)
(-> (.setInternetCredentials rn/keychain address address password
(clj->js keychain-restricted-availability))
(.then callback))))
@ -55,12 +56,12 @@
(defn handle-callback [callback result]
(if result
(callback (.-password result))
(callback "")))
(callback nil)))
;; Gets the password for a specified address from the Keychain
(defn get-user-password [address callback]
(if-not platform/ios?
(callback "") ;; no-op on Androids (for now)
(callback) ;; no-op on Androids (for now)
(-> (.getInternetCredentials rn/keychain address)
(.then (partial handle-callback callback)))))
@ -68,12 +69,12 @@
;; (example of usage is logout or signing in w/o "save-password")
(defn clear-user-password [address callback]
(if-not platform/ios?
(callback)
(callback true)
(-> (.resetInternetCredentials rn/keychain address)
(.then callback))))
;; Resolves to `false` if the device doesn't have neither a passcode nor a biometry auth.
(defn can-save-user-password [callback]
(defn can-save-user-password? [callback]
(if-not platform/ios?
(callback false)
(-> (.canImplyAuthentication
@ -83,9 +84,9 @@
(enum-val "ACCESS_CONTROL" "BIOMETRY_ANY_OR_DEVICE_PASSCODE")}))
(.then callback))))
;; ********************************************************************************
;; ********************************************************************************
;; Storing / Retrieving the realm encryption key to/from the Keychain
;; ********************************************************************************
;; ********************************************************************************
;; Smoke test key to make sure is ok, we noticed some non-random keys on
@ -175,13 +176,18 @@
(re-frame/reg-fx
:clear-user-password
(fn [[address]]
(fn [address]
(clear-user-password
address
#(when-not %
(log/error (str "Error while clearing saved password."))))))
(re-frame/reg-fx
:can-save-user-password
(fn [[callback]]
(can-save-user-password callback)))
:can-save-user-password?
(fn [_]
(can-save-user-password? #(re-frame/dispatch [:callback/can-save-user-password? %]))))
(handlers/register-handler-fx
:callback/can-save-user-password?
(fn [{:keys [db]} [_ can-save-user-password?]]
{:db (assoc-in db [:accounts/login :can-save-password?] can-save-user-password?)}))

View File

@ -7,10 +7,14 @@
[status-im.models.account :as models.account]
[status-im.ui.components.list-selection :as list-selection]
[status-im.ui.components.react :as react]
[status-im.utils.handlers-macro :as handlers-macro]
[cljs.spec.alpha :as spec]
[status-im.ui.screens.navigation :as navigation]
[status-im.ui.screens.add-new.new-chat.db :as new-chat.db]))
;; TODO(yenda) investigate why `handle-universal-link` event is
;; dispatched 7 times for the same link
(def public-chat-regex #".*/chat/public/(.*)$")
(def profile-regex #".*/user/(.*)$")
(def browse-regex #".*/browse/(.*)$")
@ -49,12 +53,6 @@
(defn handle-not-found [full-url]
(log/info "universal-links: no handler for " full-url))
(defn stored-url-event
"Return an event description for processing a url if in the database"
[{:keys [db]}]
(when-let [url (:universal-links/url db)]
[:handle-universal-link url]))
(defn dispatch-url
"Dispatch url so we can get access to re-frame/db"
[url]
@ -62,16 +60,6 @@
(re-frame/dispatch [:handle-universal-link url])
(log/debug "universal-links: no url")))
(defn store-url-for-later
"Store the url in the db to be processed on login"
[url {:keys [db]}]
(assoc-in {:db db} [:db :universal-links/url] url))
(defn clear-url
"Remove a url from the db"
[{:keys [db]}]
(update {:db db} :db dissoc :universal-links/url))
(defn route-url
"Match a url against a list of routes and handle accordingly"
[url cofx]
@ -87,15 +75,28 @@
:else (handle-not-found url)))
(defn store-url-for-later
"Store the url in the db to be processed on login"
[url {:keys [db]}]
(log/info :store-url-for-later)
{:db (assoc db :universal-links/url url)})
(defn handle-url
"Store url in the database if the user is not logged in, to be processed
on login, otherwise just handle it"
[url cofx]
(if (models.account/logged-in? cofx)
(do
(clear-url cofx)
(route-url url cofx))
(store-url-for-later url cofx)))
(when config/universal-links-enabled?
(if (models.account/logged-in? cofx)
(route-url url cofx)
(store-url-for-later url cofx))))
(defn process-stored-event
"Return an event description for processing a url if in the database"
[{:keys [db] :as cofx}]
(when-let [url (:universal-links/url db)]
(handlers-macro/merge-fx cofx
{:db (dissoc db :universal-links/url)}
(handle-url url))))
(defn unwrap-js-url [e]
(-> e

View File

@ -2,8 +2,9 @@
(:require [cljs.test :refer-macros [deftest is testing]]
[status-im.init.core :as init]))
(deftest initialize-db
(deftest initialize-account-db
(testing "it preserves universal-links/url"
(is (= "some-url" (get-in (init/initialize-db {:db
{:universal-links/url "some-url"}})
(is (= "some-url" (get-in (init/initialize-account-db
"address"
{:db {:universal-links/url "some-url"}})
[:db :universal-links/url])))))

View File

@ -22,10 +22,9 @@
:name "Bob"
:public-key "0x04d2e59a7501a7bc5bc8bf973a0ab95d06225e2b0f53d5f6be719d857c579bdc1b809bfbe0e8393343f9a5b63a9a90a1a58329c6d1c286d6929f01aaa5472ca9c2"}}
:current-public-key nil
:push-notifications/initial? true
:push-notifications/stored {"0x04d2e59a7501a7bc5bc8bf973a0ab95d06225e2b0f53d5f6be719d857c579bdc1b809bfbe0e8393343f9a5b63a9a90a1a58329c6d1c286d6929f01aaa5472ca9c2"
"0x045db1fdb16c4721ddf32e892c5156d9c7a7445482b84ccd41131eb7970f9d623629d86763c5c2a542ae372815125c27eb73535d583d3285bdbfa16ba37f42e2de"}}
:dispatch [:open-login "bd36cd64e2621b054a3b7464ff1b3c4c304880e7" "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAMAAAC7IEhfAAADAFBMVEX" "Bob"]}
:dispatch [:ui/open-login "bd36cd64e2621b054a3b7464ff1b3c4c304880e7" "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAMAAAC7IEhfAAADAFBMVEX" "Bob"]}
(notifications/handle-push-notification {:db {:accounts/accounts {"bd36cd64e2621b054a3b7464ff1b3c4c304880e7" {:address "bd36cd64e2621b054a3b7464ff1b3c4c304880e7"
:photo-path "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAMAAAC7IEhfAAADAFBMVEX"
:name "Bob"
@ -33,5 +32,4 @@
:current-public-key nil
:push-notifications/stored {}}}
[:push-notification-opened {:from "0x045db1fdb16c4721ddf32e892c5156d9c7a7445482b84ccd41131eb7970f9d623629d86763c5c2a542ae372815125c27eb73535d583d3285bdbfa16ba37f42e2de"
:to "0x04d2e59a7501a7bc5bc8bf973a0ab95d06225e2b0f53d5f6be719d857c579bdc1b809bfbe0e8393343f9a5b63a9a90a1a58329c6d1c286d6929f01aaa5472ca9c2"
:initial? true}])))))
:to "0x04d2e59a7501a7bc5bc8bf973a0ab95d06225e2b0f53d5f6be719d857c579bdc1b809bfbe0e8393343f9a5b63a9a90a1a58329c6d1c286d6929f01aaa5472ca9c2"}])))))

View File

@ -3,150 +3,114 @@
[status-im.utils.config :as config]
[status-im.ui.screens.accounts.login.models :as models]))
(deftest login-account-internal
(let [initial-db {:db {:node/after-start "something"}}]
(defn test-func [save-password]
(testing (str "login-account-interal test, save password: " save-password)
(let [actual (models/login-account-internal "address" "password" save-password initial-db)]
(testing "it resets :node/after-start"
(is (= (get-in actual [:db :node/after-start]) nil))
(testing "it causes :login effect and preserves save-password flag"
(is (= ["address" "password" save-password] (get-in actual [:login]))))))))
(map test-func [true false])))
#_(deftest login-account
(let [mainnet-account {:network "mainnet_rpc"
:networks {"mainnet_rpc" {:config {:NetworkId 1}}}}
testnet-account {:network "testnet_rpc"
:networks {"testnet_rpc" {:config {:NetworkId 3}}}}
accounts {"mainnet" mainnet-account
"testnet" testnet-account}
initial-db {:db {:network "mainnet_rpc"
:accounts/accounts accounts}}]
(deftest login-account
(let [mainnet-account {:network "mainnet_rpc"
:networks {"mainnet_rpc" {:config {:NetworkId 1}}}}
testnet-account {:network "testnet_rpc"
:networks {"testnet_rpc" {:config {:NetworkId 3}}}}
accounts {"mainnet" mainnet-account
"testnet" testnet-account}
initial-db {:db {:network "mainnet_rpc"
:accounts/accounts accounts}}]
(testing "save password"
(testing "save password: status-go not started"
(let [actual (models/login-account "testnet" "password" true initial-db)]
(testing "status-go has not started"
(let [actual (models/user-login "testnet" "password" initial-db)]
(testing "it starts status-node if it has not started"
(is (= {:NetworkId 3}
(:init/initialize-geth
actual))))
(testing "it logins the user after the node started"
(is (= [:login-account-internal "testnet" "password" true] (get-in actual [:db :node/after-start]))))))
(is (= [:login-account-internal "testnet" "password"] (get-in actual [:db :node/after-start]))))))
(testing "status-go has started & the user is on mainnet"
(let [db (assoc-in initial-db [:db :status-node-started?] true)
actual (models/login-account "mainnet" "password" false db)]
actual (models/user-login "mainnet" "password" db)]
(testing "it does not start status-node if it has already started"
(is (not (:init/initialize-geth actual))))
(testing "it logs in the user"
(is (= ["mainnet" "password" false] (:login actual)))))))
(is (= ["mainnet" "password"] (:login actual))))))
(testing "the user has selected a different network"
(testing "status-go has started"
(let [db (assoc-in initial-db [:db :status-node-started?] true)
actual (models/login-account "testnet" "password" false db)]
(testing "it dispatches start-node"
(is (get-in actual [:db :node/after-stop] [:start-node "testnet" "password" true])))
(testing "it stops status-node"
(is (contains? actual :stop-node))))))
(testing "the user has selected a different network"
(testing "status-go has started"
(let [db (assoc-in initial-db [:db :status-node-started?] true)
actual (models/user-login "testnet" "password" db)]
(testing "it dispatches start-node"
(is (get-in actual [:db :node/after-stop] [:start-node "testnet" "password"])))
(testing "it stops status-node"
(is (contains? actual :stop-node)))))
(testing "status-go has not started"
(let [actual (models/login-account "testnet" "password" false initial-db)]
(testing "it starts status-node"
(is (= {:NetworkId 3} (:init/initialize-geth actual))))
(testing "it logins the user after the node started"
(is (= [:login-account-internal "testnet" "password" false] (get-in actual [:db :node/after-start]))))))
(testing "status-go has not started"
(let [actual (models/user-login "testnet" "password" initial-db)]
(testing "it starts status-node"
(is (= {:NetworkId 3} (:init/initialize-geth actual))))
(testing "it logins the user after the node started"
(is (= [:login-account-internal "testnet" "password"] (get-in actual [:db :node/after-start])))))))
(testing "status-go has started & the user is on mainnet"
(let [db (assoc-in initial-db [:db :status-node-started?] true)
actual (models/login-account "mainnet" "password" false db)]
(testing "it does not start status-node if it has already started"
(is (not (:init/initialize-geth actual))))
(testing "it logs in the user"
(is (= ["mainnet" "password" false] (:login actual))))))
(testing "custom bootnodes"
(let [custom-bootnodes {"a" {:id "a"
:name "name-a"
:address "address-a"}
"b" {:id "b"
:name "name-b"
:address "address-b"}}
bootnodes-db (assoc-in
initial-db
[:db :accounts/accounts "mainnet" :bootnodes]
{"mainnet_rpc" custom-bootnodes})]
(testing "the user has selected a different network"
(testing "status-go has started"
(let [db (assoc-in initial-db [:db :status-node-started?] true)
actual (models/login-account "testnet" "password" false db)]
(testing "it dispatches start-node"
(is (get-in actual [:db :node/after-stop] [:start-node "testnet" "password" false])))
(testing "it stops status-node"
(is (contains? actual :stop-node)))))
(testing "custom bootnodes enabled"
(let [bootnodes-enabled-db (assoc-in
bootnodes-db
[:db :accounts/accounts "mainnet" :settings]
{:bootnodes {"mainnet_rpc" true}})
actual (models/user-login "mainnet" "password" bootnodes-enabled-db)]
(testing "status-node has started"
(let [db (assoc-in bootnodes-enabled-db [:db :status-node-started?] true)
actual (models/user-login "mainnet" "password" db)]
(testing "it dispatches start-node"
(is (get-in actual [:db :node/after-stop] [:start-node "testnet" "password"])))
(testing "it stops status-node"
(is (contains? actual :stop-node)))))
(testing "status-node has not started"
(let [actual (models/user-login "mainnet" "password" bootnodes-enabled-db)]
(testing "it adds bootnodes to the config"
(is (= {:ClusterConfig {:Enabled true
:BootNodes ["address-a" "address-b"]}
:NetworkId 1} (:init/initialize-geth actual))))
(testing "it logins the user after the node started"
(is (= [:login-account-internal "mainnet" "password"] (get-in actual [:db :node/after-start]))))))))
(testing "status-go has not started"
(let [actual (models/login-account "testnet" "password" false initial-db)]
(testing "it starts status-node"
(is (= {:NetworkId 3} (:init/initialize-geth actual))))
(testing "it logins the user after the node started"
(is (= [:login-account-internal "testnet" "password" false] (get-in actual [:db :node/after-start])))))))
(testing "custom bootnodes"
(let [custom-bootnodes {"a" {:id "a"
:name "name-a"
:address "address-a"}
"b" {:id "b"
:name "name-b"
:address "address-b"}}
bootnodes-db (assoc-in
initial-db
[:db :accounts/accounts "mainnet" :bootnodes]
{"mainnet_rpc" custom-bootnodes})]
(testing "custom bootnodes enabled"
(let [bootnodes-enabled-db (assoc-in
bootnodes-db
[:db :accounts/accounts "mainnet" :settings]
{:bootnodes {"mainnet_rpc" true}})
actual (models/login-account "mainnet" "password" false bootnodes-enabled-db)]
(testing "custom bootnodes not enabled"
(testing "status-node has started"
(let [db (assoc-in bootnodes-enabled-db [:db :status-node-started?] true)
actual (models/login-account "mainnet" "password" false db)]
(testing "it dispatches start-node"
(is (get-in actual [:db :node/after-stop] [:start-node "testnet" "password" false])))
(testing "it stops status-node"
(is (contains? actual :stop-node)))))
(let [db (assoc-in bootnodes-db [:db :status-node-started?] true)
actual (models/user-login "mainnet" "password" db)]
(testing "it does not start status-node if it has already started"
(is (not (:init/initialize-geth actual))))
(testing "it logs in the user"
(is (= ["mainnet" "password"] (:login actual))))))
(testing "status-node has not started"
(let [actual (models/login-account "mainnet" "password" false bootnodes-enabled-db)]
(testing "it adds bootnodes to the config"
(is (= {:ClusterConfig {:Enabled true
:BootNodes ["address-a" "address-b"]}
:NetworkId 1} (:init/initialize-geth actual))))
(let [actual (models/user-login "mainnet" "password" bootnodes-db)]
(testing "it starts status-node without custom bootnodes"
(is (= {:NetworkId 1} (:init/initialize-geth actual))))
(testing "it logins the user after the node started"
(is (= [:login-account-internal "mainnet" "password" false] (get-in actual [:db :node/after-start]))))))))
(is (= [:login-account-internal "mainnet" "password"] (get-in actual [:db :node/after-start])))))))))))
(testing "custom bootnodes not enabled"
(testing "status-node has started"
(let [db (assoc-in bootnodes-db [:db :status-node-started?] true)
actual (models/login-account "mainnet" "password" false db)]
(testing "it does not start status-node if it has already started"
(is (not (:init/initialize-geth actual))))
(testing "it logs in the user"
(is (= ["mainnet" "password" false] (:login actual))))))
(testing "status-node has not started"
(let [actual (models/login-account "mainnet" "password" false bootnodes-db)]
(testing "it starts status-node without custom bootnodes"
(is (= {:NetworkId 1} (:init/initialize-geth actual))))
(testing "it logins the user after the node started"
(is (= [:login-account-internal "mainnet" "password" false] (get-in actual [:db :node/after-start])))))))))))
(deftest restart-node?
(testing "custom bootnodes is toggled off"
(with-redefs [config/bootnodes-settings-enabled? false]
(testing "it returns true when the network is different"
(is (models/restart-node? "mainnet_rpc" "mainnet" true)))
(testing "it returns false when the network is the same"
(is (not (models/restart-node? "mainnet" "mainnet" true))))))
(testing "custom bootnodes is toggled on"
(with-redefs [config/bootnodes-settings-enabled? true]
(testing "the user is not using custom bootnodes"
#_(deftest restart-node?
(testing "custom bootnodes is toggled off"
(with-redefs [config/bootnodes-settings-enabled? false]
(testing "it returns true when the network is different"
(is (models/restart-node? "mainnet_rpc" "mainnet" false)))
(is (models/restart-node? "mainnet_rpc" "mainnet" true)))
(testing "it returns false when the network is the same"
(is (not (models/restart-node? "mainnet" "mainnet" false)))))
(testing "the user is using custom bootnodes"
(testing "it returns true when the network is different"
(is (models/restart-node? "mainnet" "mainnet" true)))
(testing "it returns true when the network is the same"
(is (models/restart-node? "mainnet_rpc" "mainnet" true)))))))
(is (not (models/restart-node? "mainnet" "mainnet" true))))))
(testing "custom bootnodes is toggled on"
(with-redefs [config/bootnodes-settings-enabled? true]
(testing "the user is not using custom bootnodes"
(testing "it returns true when the network is different"
(is (models/restart-node? "mainnet_rpc" "mainnet" false)))
(testing "it returns false when the network is the same"
(is (not (models/restart-node? "mainnet" "mainnet" false)))))
(testing "the user is using custom bootnodes"
(testing "it returns true when the network is different"
(is (models/restart-node? "mainnet" "mainnet" true)))
(testing "it returns true when the network is the same"
(is (models/restart-node? "mainnet_rpc" "mainnet" true)))))))

View File

@ -79,12 +79,12 @@
(testing "it returns false"
(is (not (links/universal-link? "https://not.status.im/blah"))))))
(deftest stored-url-event
(deftest process-stored-event
(testing "the url is in the database"
(testing "it returns the event"
(= [:handle-universal-link "some-url"]
(links/stored-url-event {:db {:universal-links/url "some-url"}}))))
(= "some-url"
(links/process-stored-event {:db {:universal-links/url "some-url"}}))))
(testing "the url is not in the database"
(testing "it returns nil"
(= nil
(links/stored-url-event {:db {}})))))
(links/process-stored-event {:db {}})))))