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:
parent
dacfe97a58
commit
bb339dc39b
|
@ -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)))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)))
|
|
@ -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)))
|
|
@ -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)))))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 ?
|
||||
|
|
|
@ -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)))
|
||||
|
|
|
@ -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)))
|
||||
|
|
|
@ -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))
|
|
@ -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)}]])]))
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))))
|
||||
|
||||
|
|
|
@ -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 isn’t migrated when restoring another device’s 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?)}))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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])))))
|
||||
|
|
|
@ -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"}])))))
|
||||
|
|
|
@ -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)))))))
|
||||
|
|
|
@ -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 {}})))))
|
||||
|
|
Loading…
Reference in New Issue