Onboarding UI fixes

Signed-off-by: yenda <eric@status.im>
This commit is contained in:
Vitaliy Vlasov 2019-09-03 17:19:23 +03:00 committed by yenda
parent e509e8fcb0
commit 5fd269fde8
No known key found for this signature in database
GPG Key ID: 0095623C0069DCE6
37 changed files with 781 additions and 693 deletions

View File

@ -5,10 +5,10 @@
:empty-recent (js/require "./resources/images/ui/empty-recent.png")
:analytics-image (js/require "./resources/images/ui/analytics-image.png")
:welcome-image (js/require "./resources/images/ui/welcome-image.png")
:intro1 (js/require "./resources/images/ui/intro1.png")
:intro2 (js/require "./resources/images/ui/intro2.png")
:intro3 (js/require "./resources/images/ui/intro3.png")
:sample-key (js/require "./resources/images/ui/sample-key.png")
:intro1 (js/require "./resources/images/ui/intro1.jpg")
:intro2 (js/require "./resources/images/ui/intro2.jpg")
:intro3 (js/require "./resources/images/ui/intro3.jpg")
:sample-key (js/require "./resources/images/ui/sample-key.jpg")
:lock (js/require "./resources/images/ui/lock.png")
:tribute-to-talk (js/require "./resources/images/ui/tribute-to-talk.png")
:wallet-welcome (js/require "./resources/images/ui/wallet-welcome.png")

View File

@ -22,6 +22,7 @@ var TopLevel = {
"AsyncStorage" : function () {},
"at" : function () {},
"back" : function () {},
"BACK" : function () {},
"BackHandler" : function () {},
"balanceOf" : function () {},
"bezier" : function () {},
@ -162,6 +163,7 @@ var TopLevel = {
"getNodesFromContract" : function () {},
"getPublicKey" : function () {},
"getSecurityLevel" : function () {},
"getStateForAction" : function () {},
"getString" : function () {},
"getSymKey" : function () {},
"getSyncing" : function () {},
@ -403,6 +405,7 @@ var TopLevel = {
"rightPad" : function () {},
"round" : function () {},
"routeName" : function () {},
"router" : function () {},
"routes" : function () {},
"saveAccountAndLogin" : function () {},
"saveAccountAndLoginWithKeycard" : function () {},

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

View File

@ -3,6 +3,9 @@
[re-frame.core :as re-frame]
[status-im.constants :as constants]
[status-im.ethereum.core :as ethereum]
[taoensso.timbre :as log]
[status-im.i18n :as i18n]
[status-im.multiaccounts.db :as db]
[status-im.native-module.core :as status]
[status-im.node.core :as node]
[status-im.ui.components.colors :as colors]
@ -25,10 +28,9 @@
:select-key-storage 3
:create-code 4
:confirm-code 5
:enable-fingerprint 6
:enable-notifications 7})
:enable-notifications 6})
(defn dec-step [step]
(defn decrement-step [step]
(let [inverted (map-invert step-kw-to-num)]
(if (and (= step :create-code)
(not platform/android?))
@ -58,35 +60,62 @@
(fx/defn intro-wizard
{:events [:multiaccounts.create.ui/intro-wizard]}
[{:keys [db] :as cofx} first-time-setup?]
(fx/merge {:db (assoc db :intro-wizard {:step :generate-key
(fx/merge cofx
{:db (assoc db :intro-wizard {:step :generate-key
:weak-password? true
:back-action :intro-wizard/navigate-back
:forward-action :intro-wizard/step-forward-pressed
:encrypt-with-password? true
:first-time-setup? first-time-setup?})}
(navigation/navigate-to-cofx :intro-wizard nil)))
:first-time-setup? first-time-setup?})
::navigation/add-wizard-back-event [:intro-wizard/step-back-pressed]}
(navigation/navigate-to-cofx :create-multiaccount-generate-key nil)))
(fx/defn intro-step-back
{:events [:intro-wizard/step-back-pressed]}
(fx/defn dec-step
{:events [:intro-wizard/dec-step]}
[{:keys [db] :as cofx}]
(let [step (get-in db [:intro-wizard :step])
first-time-setup? (get-in db [:intro-wizard :first-time-setup?])]
(if (not= :generate-key step)
(fx/merge {:db (cond-> (assoc-in db [:intro-wizard :step] (dec-step step))
(let [step (get-in db [:intro-wizard :step])]
(fx/merge cofx
(when-not (= :generate-key step)
{:db (cond-> (assoc-in db [:intro-wizard :step] (decrement-step step))
(#{:create-code :confirm-code} step)
(update :intro-wizard assoc :weak-password? true :key-code nil)
(= step :confirm-code)
(assoc-in [:intro-wizard :confirm-failure?] false))}
(navigation/navigate-to-cofx :intro-wizard nil))
(assoc-in [:intro-wizard :confirm-failure?] false))})
(when (= :generate-key-step)
{:db (dissoc db :intro-wizard)
::navigation/remove-wizard-back-event nil}))))
(fx/merge {:db (dissoc db :intro-wizard)}
(navigation/navigate-back)))))
(fx/defn navigate-back
{:events [:intro-wizard/navigate-back]}
[cofx]
{::navigation/navigate-back nil})
(fx/defn intro-step-back
{:events [:intro-wizard/step-back-pressed]}
[{:keys [db] :as cofx} skip-alert?]
(let [step (get-in db [:intro-wizard :step])]
;; Cannot go back after account has been created
;; and we're on "Enable notifications" step
(when-not (= :enable-notifications step)
(if (and (= step :choose-key) (not skip-alert?))
(utils/show-question
(i18n/label :t/are-you-sure-to-cancel)
(i18n/label :t/you-will-start-from-scratch)
#(re-frame/dispatch [:intro-wizard/step-back-pressed true])
#(re-frame/dispatch [:navigation/reset-processing-flag]))
(fx/merge cofx
dec-step
navigation/navigate-back)))))
(fx/defn exit-wizard [{:keys [db] :as cofx}]
(fx/merge {:db (dissoc db :intro-wizard)}
(fx/merge cofx
{:db (dissoc db :intro-wizard)
::navigation/remove-wizard-back-event nil}
(navigation/navigate-to-cofx :home nil)))
(fx/defn init-key-generation
[{:keys [db] :as cofx}]
{:db (assoc-in db [:intro-wizard :generating-keys?] true)
{:db (assoc-in db [:intro-wizard :processing?] true)
:intro-wizard/start-onboarding nil})
(fx/defn on-confirm-failure [{:keys [db] :as cofx}]
@ -105,8 +134,7 @@
(let [key-code (get-in db [:intro-wizard :key-code])]
{:db (update db :intro-wizard
assoc :stored-key-code key-code
:key-code nil
:step :confirm-code)}))
:key-code nil)}))
(fx/defn intro-step-forward
{:events [:intro-wizard/step-forward-pressed]}
@ -126,9 +154,6 @@
(= step :generate-key)
(init-key-generation cofx)
(= step :create-code)
(store-key-code cofx)
(and (= step :confirm-code)
(not (:multiaccounts/login db))
(not processing?))
@ -140,9 +165,18 @@
(= :advanced selected-storage-type))
{:dispatch [:keycard/start-onboarding-flow]}
:else {:db (update db :intro-wizard
assoc :processing? false
:step (inc-step step))})))
:else (let [next-step (inc-step step)]
(fx/merge cofx
{:db (update db :intro-wizard
assoc :processing? false
:step next-step)}
(when (= step :create-code)
store-key-code)
(when (= next-step :enable-notifications)
(navigation/navigate-reset {:index 0
:actions [{:routeName :create-multiaccount-enable-notifications}]}))
(when (not= next-step :enable-notifications)
(navigation/navigate-to-cofx (->> next-step name (str "create-multiaccount-") keyword) nil)))))))
(defn prepare-accounts-data
[multiaccount]
@ -248,12 +282,12 @@
{:db (update db :intro-wizard
(fn [data]
(-> data
(dissoc :generating-keys?)
(dissoc :processing?)
(assoc :multiaccounts result
:selected-storage-type :default
:selected-id (-> result first :id)
:step :choose-key))))}
(navigation/navigate-to-cofx :intro-wizard nil)))
(navigation/navigate-to-cofx :create-multiaccount-choose-key nil)))
(fx/defn on-key-selected
{:events [:intro-wizard/on-key-selected]}
@ -290,7 +324,7 @@
(let [encrypt-with-password? (get-in db [:intro-wizard :encrypt-with-password?])]
{:db (update db :intro-wizard assoc :key-code new-key-code
:confirm-failure? false
:weak-password? (< (count new-key-code) 6))}))
:weak-password? (not (db/valid-length? new-key-code)))}))
(re-frame/reg-cofx
::get-signing-phrase

View File

@ -187,9 +187,6 @@
(when platform/desktop?
(chat-model/update-dock-badge-label)))))
(defn- recovering-multiaccount? [cofx]
(boolean (get-in cofx [:db :multiaccounts/recover])))
(fx/defn create-only-events
[{:keys [db] :as cofx} address password]
(let [{:keys [multiaccount :networks/networks :networks/current-network]} db]
@ -221,7 +218,6 @@
:mailserver-ranges {}
:mailserver-topics {}
:default-mailserver true})
(mobile-network/on-network-status-change)
(chaos-mode/check-chaos-mode)
(when-not platform/desktop?
(initialize-wallet)))))
@ -232,8 +228,7 @@
(fx/defn multiaccount-login-success
[{:keys [db] :as cofx}]
(let [{:keys [address password save-password? name photo-path creating?]} (:multiaccounts/login db)
step (get-in db [:intro-wizard :step])
recovering? (recovering-multiaccount? cofx)
recovering? (get-in db [:intro-wizard :recovering?])
login-only? (not (or creating?
recovering?
(keycard-setup? cofx)))

View File

@ -13,17 +13,8 @@
[status-im.utils.fx :as fx]
[status-im.utils.security :as security]
[status-im.utils.types :as types]
[status-im.utils.platform :as platform]))
(defn check-password-errors [password]
(cond (string/blank? password) :required-field
(not (db/valid-length? password)) :recover-password-too-short))
(defn check-phrase-errors [recovery-phrase]
(cond (string/blank? recovery-phrase) :required-field
(not (mnemonic/valid-words? recovery-phrase)) :recovery-phrase-invalid
(not (mnemonic/valid-length? recovery-phrase)) :recovery-phrase-wrong-length
(not (mnemonic/status-generated-phrase? recovery-phrase)) :recovery-phrase-unknown-words))
[status-im.utils.platform :as platform]
[status-im.utils.utils :as utils]))
(defn check-phrase-warnings [recovery-phrase]
(cond (string/blank? recovery-phrase) :required-field
@ -31,51 +22,28 @@
(not (mnemonic/status-generated-phrase? recovery-phrase)) :recovery-phrase-unknown-words))
(fx/defn set-phrase
{:events [::passphrase-input-changed]}
{:events [:multiaccounts.recover/passphrase-input-changed]}
[{:keys [db]} masked-recovery-phrase]
(let [recovery-phrase (security/safe-unmask-data masked-recovery-phrase)]
(fx/merge
{:db (update db :multiaccounts/recover assoc
{:db (update db :intro-wizard assoc
:passphrase (string/lower-case recovery-phrase)
:passphrase-error nil
:next-button-disabled? (or (empty? recovery-phrase)
(not (mnemonic/valid-length? recovery-phrase))))})))
(fx/defn validate-phrase
{:events [::passphrase-input-blured]}
[{:keys [db]}]
(let [recovery-phrase (get-in db [:multiaccounts/recover :passphrase])]
{:db (update db :multiaccounts/recover assoc
:passphrase-error (check-phrase-errors recovery-phrase))}))
(fx/defn validate-phrase-for-warnings
[{:keys [db]}]
(let [recovery-phrase (get-in db [:multiaccounts/recover :passphrase])]
{:db (update db :multiaccounts/recover assoc
(let [recovery-phrase (get-in db [:intro-wizard :passphrase])]
{:db (update db :intro-wizard assoc
:passphrase-error (check-phrase-warnings recovery-phrase))}))
(fx/defn set-password
{:events [::password-input-changed
::enter-password-input-changed]}
[{:keys [db]} masked-password]
(let [password (security/safe-unmask-data masked-password)]
{:db (update db :multiaccounts/recover assoc
:password password
:password-error nil
:password-valid? (not (check-password-errors password)))}))
(fx/defn validate-password
{:events [::password-input-blured]}
[{:keys [db]}]
(let [password (get-in db [:multiaccounts/recover :password])]
{:db (assoc-in db [:multiaccounts/recover :password-error] (check-password-errors password))}))
(fx/defn on-store-multiaccount-success
{:events [::store-multiaccount-success]
:interceptors [(re-frame/inject-cofx :random-guid-generator)
(re-frame/inject-cofx ::multiaccounts.create/get-signing-phrase)]}
[{:keys [db] :as cofx} password]
(let [multiaccount (get-in db [:multiaccounts/recover :root-key])
(let [multiaccount (get-in db [:intro-wizard :root-key])
multiaccount-address (-> (:address multiaccount)
(string/lower-case)
(string/replace-first "0x" ""))
@ -83,12 +51,12 @@
(if keycard-multiaccount?
;; trying to recover multiaccount created with keycard
{:db (-> db
(update :multiaccounts/recover assoc
(update :intro-wizard assoc
:processing? false
:passphrase-error :recover-keycard-multiaccount-not-supported)
(update :multiaccounts/recover dissoc
(update :intro-wizard dissoc
:passphrase-valid?))}
(let [multiaccount (assoc multiaccount :derived (get-in db [:multiaccounts/recover :derived]))]
(let [multiaccount (assoc multiaccount :derived (get-in db [:intro-wizard :derived]))]
(multiaccounts.create/on-multiaccount-created cofx
multiaccount
password
@ -97,17 +65,18 @@
(fx/defn store-multiaccount
{:events [::recover-multiaccount-confirmed]}
[{:keys [db] :as cofx}]
(let [{:keys [password passphrase root-key]} (:multiaccounts/recover db)
(let [password (get-in db [:intro-wizard :key-code])
{:keys [passphrase root-key]} (:intro-wizard db)
{:keys [id address]} root-key
callback #(re-frame/dispatch [::store-multiaccount-success password])
hashed-password (ethereum/sha3 (security/safe-unmask-data password))]
{:db (assoc-in db [:multiaccounts/recover :processing?] true)
{:db (assoc-in db [:intro-wizard :processing?] true)
::multiaccounts.create/store-multiaccount [id address hashed-password callback]}))
(fx/defn recover-multiaccount-with-checks
{:events [::sign-in-button-pressed]}
[{:keys [db] :as cofx}]
(let [{:keys [passphrase processing?]} (:multiaccounts/recover db)]
(let [{:keys [passphrase processing?]} (:intro-wizard db)]
(when-not processing?
(if (mnemonic/status-generated-phrase? passphrase)
(store-multiaccount cofx)
@ -140,23 +109,34 @@
{:events [::import-multiaccount-success]}
[{:keys [db] :as cofx} root-data derived-data]
(fx/merge cofx
{:db (-> db
(assoc-in [:multiaccounts/recover :root-key] root-data)
(assoc-in [:multiaccounts/recover :derived] derived-data))}
{:db (update db :intro-wizard
assoc :root-key root-data
:derived derived-data
:step :recovery-success
:forward-action :multiaccounts.recover/re-encrypt-pressed)}
(navigation/navigate-to-cofx :recover-multiaccount-success nil)))
(fx/defn enter-phrase-pressed
{:events [::enter-phrase-pressed]}
[{:keys [db] :as cofx}]
(fx/merge cofx
{:db (assoc db :multiaccounts/recover {:next-button-disabled? true})
{:db (assoc db
:intro-wizard {:step :enter-phrase
:recovering? true
:next-button-disabled? true
:weak-password? true
:encrypt-with-password? true
:first-time-setup? false
:back-action :intro-wizard/navigate-back
:forward-action :multiaccounts.recover/enter-phrase-next-pressed})
::navigation/add-wizard-back-event [:multiaccounts.recover/cancel-pressed]
:dispatch [:bottom-sheet/hide-sheet]}
(navigation/navigate-to-cofx :recover-multiaccount-enter-phrase nil)))
(fx/defn proceed-to-import-mnemonic
{:events [::enter-phrase-next-pressed]}
{:events [:multiaccounts.recover/enter-phrase-next-pressed]}
[{:keys [db] :as cofx}]
(let [{:keys [password passphrase]} (:multiaccounts/recover db)]
(let [{:keys [password passphrase]} (:intro-wizard db)]
(if (check-phrase-warnings passphrase)
(popover/show-popover cofx {:view :custom-seed-phrase})
(when (mnemonic/valid-length? passphrase)
@ -172,83 +152,106 @@
:password password}}
(popover/hide-popover))))
(fx/defn cancel-pressed
{:events [::cancel-pressed]}
(fx/defn dec-step
{:events [:multiaccounts.recover/dec-step]}
[{:keys [db] :as cofx}]
(let [step (get-in db [:intro-wizard :step])]
(if (= step :enter-phrase)
{:db (dissoc db :intro-wizard)
::navigation/remove-wizard-back-event nil}
{:db (update db :intro-wizard assoc :step
(case step
:recovery-success :enter-phrase
:select-key-storage :recovery-success
:create-code :select-key-storage
:confirm-code :create-code)
:confirm-failure? false
:key-code nil
:weak-password? true)})))
(fx/defn cancel-pressed
{:events [:multiaccounts.recover/cancel-pressed]}
[{:keys [db] :as cofx} skip-alert?]
;; Workaround for multiple Cancel button clicks
;; that can break navigation tree
(when-not (#{:multiaccounts :login} (:view-id db))
(navigation/navigate-back cofx)))
(let [step (get-in db [:intro-wizard :step])]
(when-not (#{:multiaccounts :login} (:view-id db))
(if (and (= step :select-key-storage) (not skip-alert?))
(utils/show-question
(i18n/label :t/are-you-sure-to-cancel)
(i18n/label :t/you-will-start-from-scratch)
#(re-frame/dispatch [:multiaccounts.recover/cancel-pressed true])
#(re-frame/dispatch [:navigation/reset-processing-flag]))
(fx/merge cofx
dec-step
navigation/navigate-back)))))
(fx/defn select-storage-next-pressed
{:events [::select-storage-next-pressed]
{:events [:multiaccounts.recover/select-storage-next-pressed]
:interceptors [(re-frame/inject-cofx :random-guid-generator)]}
[{:keys [db] :as cofx}]
(let [storage-type (get-in db [:intro-wizard :selected-storage-type])]
(if (= storage-type :advanced)
;;TODO: fix circular dependency to remove dispatch here
{:dispatch [:recovery.ui/keycard-option-pressed]}
(navigation/navigate-to-cofx cofx :recover-multiaccount-enter-password nil))))
{:dispatch [:recovery.ui/keycard-option-pressed]})
(fx/merge cofx
{:db (update db :intro-wizard assoc :step :create-code
:forward-action :multiaccounts.recover/enter-password-next-pressed)}
(navigation/navigate-to-cofx :recover-multiaccount-enter-password nil))))
(fx/defn re-encrypt-pressed
{:events [::re-encrypt-pressed]}
{:events [:multiaccounts.recover/re-encrypt-pressed]}
[{:keys [db] :as cofx}]
(fx/merge cofx
{:db (assoc-in db [:intro-wizard :selected-storage-type] :default)}
{:db (update db :intro-wizard
assoc :step :select-key-storage
:forward-action :multiaccounts.recover/select-storage-next-pressed
:selected-storage-type :default)}
(if platform/android?
(navigation/navigate-to-cofx :recover-multiaccount-select-storage nil)
(select-storage-next-pressed))))
(fx/defn proceed-to-password-confirm
[{:keys [db] :as cofx}]
(when (nil? (get-in db [:multiaccounts/recover :password-error]))
(navigation/navigate-to-cofx cofx :recover-multiaccount-confirm-password nil)))
(fx/merge cofx
{:db (update db :intro-wizard assoc :step :confirm-code
:forward-action :multiaccounts.recover/confirm-password-next-pressed)}
(navigation/navigate-to-cofx :recover-multiaccount-confirm-password nil)))
(fx/defn enter-password-next-button-pressed
{:events [::enter-password-input-submitted
::enter-password-next-pressed]}
{:events [:multiaccounts.recover/enter-password-next-pressed]}
[{:keys [db] :as cofx}]
(fx/merge cofx
(validate-password)
{:db (assoc-in db [:intro-wizard :stored-key-code] (get-in db [:intro-wizard :key-code]))}
(proceed-to-password-confirm)))
(fx/defn confirm-password-next-button-pressed
{:events [::confirm-password-input-submitted
::confirm-password-next-pressed]
{:events [:multiaccounts.recover/confirm-password-next-pressed]
:interceptors [(re-frame/inject-cofx :random-guid-generator)]}
[{:keys [db] :as cofx}]
(let [{:keys [password password-confirmation]} (:multiaccounts/recover db)]
(if (= password password-confirmation)
(let [{:keys [key-code stored-key-code]} (:intro-wizard db)]
(if (= key-code stored-key-code)
(fx/merge cofx
{:db (assoc db :intro-wizard nil)}
(store-multiaccount)
(navigation/navigate-to-cofx :keycard-welcome nil))
{:db (assoc-in db [:multiaccounts/recover :password-error] :password_error1)})))
(store-multiaccount))
{:db (assoc-in db [:intro-wizard :confirm-failure?] true)})))
(fx/defn count-words
[{:keys [db]}]
(let [passphrase (get-in db [:multiaccounts/recover :passphrase])]
{:db (assoc-in db [:multiaccounts/recover :words-count]
(let [passphrase (get-in db [:intro-wizard :passphrase])]
{:db (assoc-in db [:intro-wizard :passphrase-word-count]
(mnemonic/words-count passphrase))}))
(fx/defn run-validation
[{:keys [db] :as cofx}]
(let [passphrase (get-in db [:multiaccounts/recover :passphrase])]
(let [passphrase (get-in db [:intro-wizard :passphrase])]
(when (= (last passphrase) " ")
(fx/merge cofx
(validate-phrase-for-warnings)))))
(fx/defn enter-phrase-input-changed
{:events [::enter-phrase-input-changed]}
{:events [:multiaccounts.recover/enter-phrase-input-changed]}
[cofx input]
(fx/merge cofx
(set-phrase input)
(count-words)
(run-validation)))
(fx/defn confirm-password-input-changed
{:events [::confirm-password-input-changed]}
[{:keys [db]} input]
{:db (-> db
(assoc-in [:multiaccounts/recover :password-confirmation] input)
(assoc-in [:multiaccounts/recover :password-error] nil))})

View File

@ -1,6 +1,7 @@
(ns status-im.subs
(:require [cljs.spec.alpha :as spec]
[clojure.string :as string]
[taoensso.timbre :as log]
[re-frame.core :as re-frame]
[status-im.browser.core :as browser]
[status-im.chat.commands.core :as commands]
@ -176,7 +177,7 @@
(reg-root-key-sub :signing/edit-fee :signing/edit-fee)
;;intro-wizard
(reg-root-key-sub :intro-wizard :intro-wizard)
(reg-root-key-sub :intro-wizard-state :intro-wizard)
(reg-root-key-sub :popover/popover :popover/popover)
(reg-root-key-sub :generate-account :generate-account)
@ -193,6 +194,72 @@
(fn [desktop _]
(get desktop :debug-metrics)))
;; Intro wizard
(re-frame/reg-sub
:intro-wizard
:<- [:intro-wizard-state]
:<- [:dimensions/window]
(fn [[wizard-state
{:keys [width height] :as dimensions}
view-id]]
(assoc wizard-state
:view-height height :view-width width)))
(re-frame/reg-sub
:intro-wizard/generate-key
:<- [:intro-wizard]
(fn [wizard-state]
(select-keys wizard-state [:processing? :view-height])))
(re-frame/reg-sub
:intro-wizard/choose-key
:<- [:intro-wizard]
(fn [wizard-state]
(select-keys wizard-state [:multiaccounts :selected-id :view-height])))
(re-frame/reg-sub
:intro-wizard/select-key-storage
:<- [:intro-wizard]
(fn [wizard-state]
(merge (select-keys wizard-state [:selected-storage-type :view-height :recovering?])
(if (:recovering? wizard-state)
{:forward-action :multiaccounts.recover/select-storage-next-pressed}
{:forward-action :intro-wizard/step-forward-pressed}))))
(re-frame/reg-sub
:intro-wizard/create-code
:<- [:intro-wizard]
(fn [wizard-state]
(merge (select-keys wizard-state [:confirm-failure? :encrypt-with-password? :weak-password? :view-width])
(if (:recovering? wizard-state)
{:forward-action :multiaccounts.recover/enter-password-next-pressed}
{:forward-action :intro-wizard/step-forward-pressed}))))
(re-frame/reg-sub
:intro-wizard/confirm-code
:<- [:intro-wizard]
(fn [wizard-state]
(merge (select-keys wizard-state [:confirm-failure? :encrypt-with-password? :processing? :view-width])
(if (:recovering? wizard-state)
{:forward-action :multiaccounts.recover/confirm-password-next-pressed}
{:forward-action :intro-wizard/step-forward-pressed}))))
(re-frame/reg-sub
:intro-wizard/enter-phrase
:<- [:intro-wizard]
(fn [wizard-state]
(select-keys wizard-state [:processing?
:passphrase-word-count
:next-button-disabled?
:passphrase-error])))
(re-frame/reg-sub
:intro-wizard/recovery-success
:<- [:intro-wizard]
(fn [wizard-state]
{:pubkey (get-in wizard-state [:derived constants/path-whisper-keyword :publicKey])
:processing? (:processing? wizard-state)}))
(re-frame/reg-sub
:settings/logging-enabled
:<- [:desktop/desktop]

View File

@ -21,11 +21,11 @@
(styles/def input
{:padding 0
:text-align-vertical :top
:text-align-vertical :center
:desktop {:height 52}})
(defn error [label?]
{:bottom-value (if label? 20 0)
(defn error [bottom-value]
{:bottom-value bottom-value
:container-style {:shadow-offset {:width 0 :height 1}
:shadow-radius 6
:shadow-opacity 1

View File

@ -15,10 +15,11 @@
merged-styles)))
(defn text-input-with-label
[{:keys [label content error style height container text editable keyboard-type]
[{:keys [label content error style height container
parent-container bottom-value text editable keyboard-type]
:as props
:or {editable true}}]
[react/view
[react/view {:style parent-container}
(when label
[react/text {:style (styles/label editable)}
label])
@ -39,4 +40,7 @@
{:value text}))]
(when content content)]
(when error
[tooltip/tooltip error (styles/error label)])])
[tooltip/tooltip error (styles/error
(cond bottom-value bottom-value
label 20
:else 0))])])

View File

@ -4,11 +4,13 @@
[status-im.i18n :as i18n]
[status-im.ui.components.colors :as colors]
[status-im.ui.components.copyable-text :as copyable-text]
[status-im.ui.components.icons.vector-icons :as vector-icons]
[status-im.ui.components.list.views :as list]
[status-im.ui.components.list-item.views :as list-item]
[status-im.ui.components.react :as react]
[status-im.ui.components.status-bar.view :as status-bar]
[status-im.ui.components.toolbar.view :as toolbar]))
[status-im.ui.components.toolbar.view :as toolbar]
[status-im.ui.screens.about-app.styles :as styles]))
(defn- data [app-version node-version]
[{:type :small
@ -62,3 +64,16 @@
{:data (data app-version node-version)
:key-fn (fn [_ i] (str i))
:render-fn list/flat-list-generic-render-fn}]]))
(views/defview learn-more-sheet []
(views/letsubs [{:keys [title content]} [:bottom-sheet/options]]
[react/view {:style {:padding-left 16 :padding-top 16
:padding-right 34 :padding-bottom 0}}
[react/view {:style {:align-items :center :flex-direction :row :margin-bottom 16}}
[vector-icons/icon :main-icons/info {:color colors/blue
:container-style {:margin-right 13}}]
[react/text {:style styles/learn-more-title} title]]
[react/text {:style styles/learn-more-text} content]]))
(def learn-more
{:content learn-more-sheet})

View File

@ -12,6 +12,7 @@
[status-im.chat.models :as chat]
[status-im.hardwallet.core :as hardwallet]
[status-im.mailserver.core :as mailserver]
[status-im.multiaccounts.recover.core :as recovery]
[status-im.native-module.core :as status]
[status-im.ui.components.permissions :as permissions]
[status-im.utils.dimensions :as dimensions]

View File

@ -4,7 +4,6 @@
(def intro-view
{:flex 1
:justify-content :flex-end
:padding-horizontal 30
:margin-bottom 12})
(def intro-logo-container
@ -58,7 +57,7 @@
:padding-left 16
:padding-right 10
:background-color (if selected? colors/blue-light colors/white)
:padding-vertical 10})
:padding-vertical 12})
(def multiaccount-image
{:width 40
@ -81,8 +80,8 @@
:width width})
(def buttons-container
{:align-items :center
:margin-top 32})
{:align-items :center
:padding-horizontal 32})
(def bottom-button
{:padding-horizontal 24
@ -100,5 +99,4 @@
:align-self :stretch
:padding-top 16
:border-top-width 1
:border-top-color colors/gray-lighter
:margin-right 20})
:border-top-color colors/gray-lighter})

View File

@ -9,6 +9,7 @@
[status-im.ui.components.icons.vector-icons :as vector-icons]
[status-im.utils.identicon :as identicon]
[status-im.ui.components.radio :as radio]
[status-im.ui.components.text-input.view :as text-input]
[taoensso.timbre :as log]
[status-im.utils.gfycat.core :as gfy]
[status-im.ui.components.colors :as colors]
@ -17,6 +18,8 @@
[status-im.ui.components.common.common :as components.common]
[status-im.ui.screens.intro.styles :as styles]
[status-im.ui.components.toolbar.view :as toolbar]
[status-im.utils.platform :as platform]
[status-im.utils.security :as security]
[status-im.i18n :as i18n]
[status-im.ui.components.status-bar.view :as status-bar]
[status-im.constants :as constants]
@ -30,16 +33,19 @@
^{:key i}
[react/view {:style (styles/dot color (selected i))}]))])
(defn intro-viewer [slides window-width]
(let [margin 24
view-width (- window-width (* 2 margin))
scroll-x (r/atom 0)
(defn intro-viewer [slides window-height]
(let [scroll-x (r/atom 0)
scroll-view-ref (atom nil)
max-width (* view-width (dec (count slides)))]
width (r/atom 0)
height (r/atom 0)
bottom-margin (if (> window-height 600) 32 16)]
(fn []
[react/view {:style {:margin-horizontal 32
:align-items :center
:justify-content :flex-end}}
[react/view {:style {:align-items :center
:flex 1
:margin-bottom bottom-margin
:justify-content :flex-end}
:on-layout (fn [e]
(reset! width (-> e .-nativeEvent .-layout .-width)))}
[react/scroll-view {:horizontal true
:paging-enabled true
:ref #(reset! scroll-view-ref %)
@ -48,25 +54,34 @@
:pinch-gesture-enabled false
:on-scroll #(let [x (.-nativeEvent.contentOffset.x %)]
(reset! scroll-x x))
:style {:width view-width
:margin-vertical 32}}
(for [s slides]
^{:key (:title s)}
[react/view {:style {:width view-width
:padding-horizontal 16}}
[react/view {:style styles/intro-logo-container}
[components.common/image-contain
{:container-style {}}
{:image (:image s) :width view-width :height view-width}]]
[react/i18n-text {:style styles/wizard-title :key (:title s)}]
[react/i18n-text {:style styles/wizard-text
:key (:text s)}]])]
(let [selected (hash-set (/ @scroll-x view-width))]
:style {;:width @width
:margin-bottom bottom-margin}}
(doall
(for [s slides]
^{:key (:title s)}
[react/view {:style {:flex 1
:width @width
:justify-content :flex-end
:align-items :center
:padding-horizontal 32}}
(let [margin 32
size (min @width @height) #_(- (min @width @height) #_(* 2 margin))]
[react/view {:style {:flex 1}
:on-layout (fn [e]
(reset! height (-> e .-nativeEvent .-layout .-height)))}
[react/image {:source (:image s)
:resize-mode :contain
:style {:width size
:height size}}]])
[react/i18n-text {:style styles/wizard-title :key (:title s)}]
[react/i18n-text {:style styles/wizard-text
:key (:text s)}]]))]
(let [selected (hash-set (quot (int @scroll-x) (int @width)))]
[dots-selector {:selected selected :n (count slides)
:color colors/blue}])])))
(defview intro []
(letsubs [window-width [:dimensions/window-width]]
(letsubs [{window-height :height} [:dimensions/window]]
[react/view {:style styles/intro-view}
[status-bar/status-bar {:flat? true}]
[intro-viewer [{:image (:intro1 resources/ui)
@ -77,7 +92,7 @@
:text :intro-text2}
{:image (:intro3 resources/ui)
:title :intro-title3
:text :intro-text3}] window-width]
:text :intro-text3}] window-height]
[react/view styles/buttons-container
[components.common/button {:button-style (assoc styles/bottom-button :margin-bottom 16)
:on-press #(re-frame/dispatch [:multiaccounts.create.ui/intro-wizard true])
@ -94,25 +109,35 @@
(i18n/label :t/intro-privacy-policy-note2)]]]]))
(defn generate-key []
[components.common/image-contain
{:container-style {:margin-horizontal 80}}
{:image (resources/get-image :sample-key)
:width 154 :height 140}])
(let [dimensions (r/atom {})]
(fn []
[react/view {:on-layout (fn [e]
(reset! dimensions (js->clj (-> e .-nativeEvent .-layout) :keywordize-keys true)))
:style {:align-items :center
:justify-content :center
:flex 1}}
(let [padding 40
image-size (- (min (:width @dimensions) (:height @dimensions)) padding)]
[react/image {:source (resources/get-image :sample-key)
:resize-mode :contain
:style {:width image-size :height image-size}}])])))
(defn choose-key [{:keys [multiaccounts selected-id] :as wizard-state} view-height]
(defn choose-key [{:keys [multiaccounts selected-id view-height]}]
[react/scroll-view {:content-container-style {:flex 1
:justify-content :flex-end
:justify-content (if (< view-height 600) :flex-start :flex-end)
:margin-top (if (< view-height 600) 20 0)
;; We have to align top multiaccount entry
;; with top key storage entry on the next screen
:margin-bottom (if (< view-height 600)
-20
0
(/ view-height 12))}}
(for [acc multiaccounts]
(for [[acc accessibility-n] (map vector multiaccounts (range (count multiaccounts)))]
(let [selected? (= (:id acc) selected-id)
public-key (get-in acc [:derived constants/path-whisper-keyword :publicKey])]
^{:key public-key}
[react/touchable-highlight
{:on-press #(re-frame/dispatch [:intro-wizard/on-key-selected (:id acc)])}
{:accessibility-label (keyword (str "select-account-button-" accessibility-n))
:on-press #(re-frame/dispatch [:intro-wizard/on-key-selected (:id acc)])}
[react/view {:style (styles/list-item selected?)}
[react/image {:source {:uri (identicon/identicon public-key)}
@ -140,11 +165,12 @@
[react/text {:style (assoc styles/wizard-text :text-align :left :margin-left 16)}
(i18n/label type)]]
[react/touchable-highlight
{:on-press #(re-frame/dispatch [:intro-wizard/on-key-storage-selected (if (and config/hardwallet-enabled?
{:accessibility-label (keyword (str "select-storage-" type))
:on-press #(re-frame/dispatch [:intro-wizard/on-key-storage-selected (if (and config/hardwallet-enabled?
platform/android?) type :default)])}
[react/view (assoc (styles/list-item selected?)
:align-items :flex-start
:padding-top 20
:padding-top 16
:padding-bottom 12)
(if image
[react/image
@ -160,7 +186,7 @@
(i18n/label desc)]]
[radio/radio selected?]]]]))
(defn select-key-storage [{:keys [selected-storage-type] :as wizard-state} view-height]
(defn select-key-storage [{:keys [selected-storage-type view-height]}]
(let [storage-types [{:type :default
:icon :main-icons/mobile
:icon-width 24
@ -175,12 +201,12 @@
:title :keycard
:desc :keycard-desc}]]
[react/view {:style {:flex 1
:justify-content :flex-end
:justify-content (if (< view-height 600) :flex-start :flex-end)
;; We have to align top storage entry
;; with top multiaccount entry on the previous screen
:margin-bottom (+ (- 300 232) (if (< view-height 600)
-20
(/ view-height 12)))}}
:margin-bottom (if (< view-height 600)
0
(+ (- 322 226) (/ view-height 12)))}}
[storage-entry (first storage-types) selected-storage-type]
[react/view {:style {:min-height 16 :max-height 16}}]
[storage-entry (second storage-types) selected-storage-type]]))
@ -197,16 +223,17 @@
[react/text-input {:secure-text-entry true
:auto-focus true
:accessibility-label :password-input
:text-align :center
:placeholder ""
:style (styles/password-text-input (- view-width (* 2 horizontal-margin)))
:on-change-text #(re-frame/dispatch [:intro-wizard/code-symbol-pressed %])}]]
[react/text {:style (assoc styles/wizard-text :margin-bottom 16)} (i18n/label :t/password-description)]]))
(defn create-code [{:keys [confirm-failure?] :as wizard-state} view-width]
(defn create-code [{:keys [confirm-failure? view-width]}]
[password-container confirm-failure? view-width])
(defn confirm-code [{:keys [confirm-failure? processing?] :as wizard-state} view-width]
(defn confirm-code [{:keys [confirm-failure? processing? view-width]}]
(if processing?
[react/view {:style {:justify-content :center
:align-items :center}}
@ -217,63 +244,71 @@
(i18n/label :t/processing)]]
[password-container confirm-failure? view-width]))
(defn enable-fingerprint []
[vector-icons/icon :main-icons/fingerprint
{:container-style {:align-items :center
:justify-content :center}
:width 76 :height 84}])
(defn enable-notifications []
[vector-icons/icon :main-icons/bell {:container-style {:align-items :center
:justify-content :center}
:width 66 :height 64}])
(defn bottom-bar [{:keys [step generating-keys? weak-password? encrypt-with-password?
(defn bottom-bar [{:keys [step weak-password? encrypt-with-password?
forward-action
next-button-disabled?
processing?] :as wizard-state}]
[react/view {:style {:margin-bottom (if (or (#{:choose-key :select-key-storage} step)
[react/view {:style {:margin-bottom (if (or (#{:choose-key :select-key-storage
:enter-phrase :recovery-success} step)
(and (#{:create-code :confirm-code} step)
encrypt-with-password?))
20
32)
:align-items :center}}
(cond generating-keys?
[react/activity-indicator {:animating true
:size :large}]
(#{:generate-key :enable-fingerprint :enable-notifications} step)
(cond (and (#{:generate-key :recovery-success} step) processing?)
[react/view {:min-height 46 :max-height 46 :align-self :stretch}
[react/activity-indicator {:animating true
:size :large}]]
(#{:generate-key :recovery-success :enable-notifications} step)
(let [label-kw (case step
:generate-key :generate-a-key
:enable-fingerprint :intro-wizard-title6
:enable-notifications :intro-wizard-title7)]
[components.common/button {:button-style styles/bottom-button
:on-press #(re-frame/dispatch
[:intro-wizard/step-forward-pressed])
:label (i18n/label label-kw)}])
:recovery-success :re-encrypt-key
:enable-notifications :intro-wizard-title6)]
[react/view {:min-height 46 :max-height 46}
[components.common/button {:button-style styles/bottom-button
:on-press #(re-frame/dispatch
[forward-action])
:accessibility-label :onboarding-next-button
:label (i18n/label label-kw)}]])
(and (#{:create-code :confirm-code} step)
(not encrypt-with-password?))
[components.common/button {:button-style styles/bottom-button
:label (i18n/label :t/encrypt-with-password)
:accessibility-label :encrypt-with-password-button
:on-press #(re-frame/dispatch [:intro-wizard/on-encrypt-with-password-pressed])
:background? false}]
:else
[react/view {:style styles/bottom-arrow}
[components.common/bottom-button {:on-press #(re-frame/dispatch
[:intro-wizard/step-forward-pressed])
:disabled? (or processing?
(and (= step :create-code) weak-password?))
:forward? true}]])
(when (#{:enable-fingerprint :enable-notifications} step)
[react/view {:style {:margin-right 10}}
[components.common/bottom-button {:on-press #(re-frame/dispatch [forward-action])
:accessibility-label :onboarding-next-button
:disabled? (or processing?
(and (= step :create-code) weak-password?)
(and (= step :enter-phrase) next-button-disabled?))
:forward? true}]]])
(when (= :enable-notifications step)
[components.common/button {:button-style (assoc styles/bottom-button :margin-top 20)
:label (i18n/label :t/maybe-later)
:on-press #(re-frame/dispatch [:intro-wizard/step-forward-pressed {:skip? true}])
:accessibility-label :skip-notifications-button
:on-press #(re-frame/dispatch [forward-action {:skip? true}])
:background? false}])
(when (= :generate-key step)
(when (or (= :generate-key step) (and processing? (= :recovery-success step)))
[react/text {:style (assoc styles/wizard-text :margin-top 20)}
(i18n/label (if generating-keys? :t/generating-keys
:t/this-will-take-few-seconds))])])
(i18n/label (cond (= :recovery-success step)
:t/processing
processing? :t/generating-keys
:else :t/this-will-take-few-seconds))])])
(defn top-bar [{:keys [step encrypt-with-password?]}]
(let [hide-subtitle? (or (= step :confirm-code)
(= step :enter-phrase)
(and (#{:create-code :confirm-code} step) encrypt-with-password?))]
[react/view {:style {:margin-top 16
:margin-horizontal 32}}
@ -281,8 +316,14 @@
[react/text {:style (cond-> styles/wizard-title
hide-subtitle?
(assoc :margin-bottom 0))}
(i18n/label (keyword (str "intro-wizard-title" (when (and (#{:create-code :confirm-code} step) encrypt-with-password?)
"-alt") (step-kw-to-num step))))]
(i18n/label
(cond (= step :enter-phrase)
:t/multiaccounts-recover-enter-phrase-title
(= step :recovery-success)
:t/keycard-recovery-success-header
:else (keyword (str "intro-wizard-title"
(when (and (#{:create-code :confirm-code} step) encrypt-with-password?)
"-alt") (step-kw-to-num step)))))]
(cond (#{:choose-key :select-key-storage} step)
; Use nested text for the "Learn more" link
[react/nested-text {:style styles/wizard-text}
@ -290,35 +331,260 @@
[{:on-press #(re-frame/dispatch [:bottom-sheet/show-sheet :learn-more
{:title (i18n/label (if (= step :choose-key) :t/about-names-title :t/about-key-storage-title))
:content (i18n/label (if (= step :choose-key) :t/about-names-content :t/about-key-storage-content))}])
:style {:color colors/blue}}
:style {:color colors/blue}
:accessibility-label :learn-more}
(i18n/label :learn-more)]]
(not hide-subtitle?)
[react/text {:style styles/wizard-text}
(i18n/label (keyword (str "intro-wizard-text" (step-kw-to-num step))))]
(i18n/label (cond (= step :recovery-success)
:t/recovery-success-text
:else (keyword (str "intro-wizard-text"
(step-kw-to-num step)))))]
:else nil)]))
(defview wizard []
(letsubs [{:keys [step generating-keys?] :as wizard-state} [:intro-wizard]
{view-height :height view-width :width} [:dimensions/window]]
[react/keyboard-avoiding-view {:style {:flex 1}}
(defn enter-phrase [{:keys [processing?
passphrase-word-count
next-button-disabled?
passphrase-error] :as wizard-state}]
[react/keyboard-avoiding-view {:flex 1
:justify-content :flex-start
:background-color colors/white}
[text-input/text-input-with-label
{:on-change-text #(re-frame/dispatch [:multiaccounts.recover/enter-phrase-input-changed (security/mask-data %)])
:auto-focus true
:error (when passphrase-error (i18n/label passphrase-error))
:accessibility-label :passphrase-input
:placeholder nil
:bottom-value 40
:multiline true
:auto-correct false
:keyboard-type "visible-password"
:parent-container {:flex 1
:align-self :stretch
:justify-content :center
:align-items :center}
:container {:background-color :white
:flex 1
:justify-content :center
:align-items :center}
:style (merge {:background-color :white
:text-align :center
:text-align-vertical :center
:min-width 40
:font-size 16
:font-weight "700"}
(when platform/android?
{:flex 1}))}]
[react/view {:align-items :center}
(if passphrase-word-count
[react/view {:flex-direction :row
:margin-bottom 4
:min-height 24
:max-height 24
:align-items :center}
[react/nested-text {:style {:font-size 14
:padding-right 4
:text-align :center
:color colors/gray}}
(str (i18n/label :t/word-count) ": ")
[{:style {:font-weight "500"
:color colors/black}}
(i18n/label-pluralize passphrase-word-count :t/words-n)]]
(when-not next-button-disabled?
[react/view {:style {:background-color colors/green-transparent-10
:border-radius 12
:width 24
:justify-content :center
:align-items :center
:height 24}}
[vector-icons/tiny-icon :tiny-icons/tiny-check {:color colors/green}]])]
[react/view {:align-self :stretch :margin-bottom 4
:max-height 24 :min-height 24}])
[react/text {:style {:color colors/gray
:font-size 14
:margin-bottom 8
:text-align :center}}
(i18n/label :t/multiaccounts-recover-enter-phrase-text)]]
(when processing?
[react/view {:flex 1 :align-items :center}
[react/activity-indicator {:size :large
:animating true}]
[react/text {:style {:color colors/gray
:margin-top 8}}
(i18n/label :t/processing)]])])
(defn recovery-success [pubkey]
[react/view {:flex 1
:justify-content :space-between
:background-color colors/white}
[react/view {:flex 1
:justify-content :space-between
:align-items :center}
[react/view {:flex-direction :column
:flex 1
:justify-content :center
:align-items :center}
[react/view {:margin-horizontal 16
:flex-direction :column}
[react/view {:justify-content :center
:align-items :center
:margin-bottom 11}
[react/image {:source {:uri (identicon/identicon pubkey)}
:style {:width 61
:height 61
:border-radius 30
:border-width 1
:border-color colors/black-transparent}}]]
[react/text {:style {:text-align :center
:color colors/black
:font-weight "500"}
:number-of-lines 1
:ellipsize-mode :middle}
(gfy/generate-gfy pubkey)]
[react/text {:style {:text-align :center
:margin-top 4
:color colors/gray
:font-family "monospace"}
:number-of-lines 1
:ellipsize-mode :middle}
(utils/get-shortened-address pubkey)]]]]])
(defview wizard-generate-key []
(letsubs [wizard-state [:intro-wizard/generate-key]]
[react/view {:style {:flex 1}}
[toolbar/toolbar
{:style {:border-bottom-width 0
:margin-top 0}}
(when-not (#{:enable-fingerprint :enable-notifications} step)
(toolbar/nav-button
(actions/back #(re-frame/dispatch
[:intro-wizard/step-back-pressed]))))
:margin-top 16}}
(toolbar/nav-button
(actions/back #(re-frame/dispatch [:intro-wizard/navigate-back])))
nil]
[react/view {:style {:flex 1
:justify-content :space-between}}
[top-bar wizard-state]
(case step
:generate-key [generate-key]
:choose-key [choose-key wizard-state view-height]
:select-key-storage [select-key-storage wizard-state view-height]
:create-code [create-code wizard-state view-width]
:confirm-code [confirm-code wizard-state view-width]
:enable-fingerprint [enable-fingerprint]
:enable-notifications [enable-notifications]
nil nil)
[bottom-bar wizard-state]]]))
[top-bar {:step :generate-key}]
[generate-key]
[bottom-bar {:step :generate-key
:forward-action :intro-wizard/step-forward-pressed
:processing? (:processing? wizard-state)}]]]))
(defview wizard-choose-key []
(letsubs [wizard-state [:intro-wizard/choose-key]]
[react/view {:style {:flex 1}}
[toolbar/toolbar
{:style {:border-bottom-width 0
:margin-left 16
:margin-top 16}}
(toolbar/nav-text
{:handler #(re-frame/dispatch [:intro-wizard/navigate-back])}
(i18n/label :t/cancel))
nil]
[react/view {:style {:flex 1
:justify-content :space-between}}
[top-bar {:step :choose-key}]
[choose-key wizard-state]
[bottom-bar {:step :choose-key
:forward-action :intro-wizard/step-forward-pressed}]]]))
(defview wizard-select-key-storage []
(letsubs [wizard-state [:intro-wizard/select-key-storage]]
[react/view {:style {:flex 1}}
[toolbar/toolbar
{:style (merge {:border-bottom-width 0
:margin-top 16}
(when (:recovering? wizard-state)
{:margin-left 16}))}
(if (:recovering? wizard-state)
(toolbar/nav-text
{:handler #(re-frame/dispatch [:intro-wizard/navigate-back])}
(i18n/label :t/cancel))
(toolbar/nav-button
(actions/back #(re-frame/dispatch [:intro-wizard/navigate-back]))))
nil]
[react/view {:style {:flex 1
:justify-content :space-between}}
[top-bar {:step :select-key-storage}]
[select-key-storage wizard-state]
[bottom-bar {:step :select-key-storage
:forward-action (:forward-action wizard-state)}]]]))
(defview wizard-create-code []
(letsubs [wizard-state [:intro-wizard/create-code]]
[react/keyboard-avoiding-view {:style {:flex 1}}
[toolbar/toolbar
{:style {:border-bottom-width 0
:margin-top 16}}
(toolbar/nav-button
(actions/back #(re-frame/dispatch [:intro-wizard/navigate-back])))
nil]
[react/view {:style {:flex 1
:justify-content :space-between}}
[top-bar {:step :create-code :encrypt-with-password? (:encrypt-with-password? wizard-state)}]
[create-code wizard-state]
[bottom-bar (merge {:step :create-code
:forward-action (:forward-action wizard-state)}
wizard-state)]]]))
(defview wizard-confirm-code []
(letsubs [wizard-state [:intro-wizard/confirm-code]]
[react/keyboard-avoiding-view {:style {:flex 1}}
[toolbar/toolbar
{:style {:border-bottom-width 0
:margin-top 16}}
(toolbar/nav-button
(actions/back #(re-frame/dispatch [:intro-wizard/navigate-back])))
nil]
[react/view {:style {:flex 1
:justify-content :space-between}}
[top-bar {:step :confirm-code :encrypt-with-password? (:encrypt-with-password? wizard-state)}]
[confirm-code wizard-state]
[bottom-bar (merge {:step :confirm-code
:forward-action (:forward-action wizard-state)}
wizard-state)]]]))
(defn wizard-enable-notifications []
[react/view {:style {:flex 1}}
[toolbar/toolbar
{:style {:border-bottom-width 0
:margin-top 16}}
nil
nil]
[react/view {:style {:flex 1
:justify-content :space-between}}
[top-bar {:step :enable-notifications}]
[enable-notifications]
[bottom-bar {:step :enable-notifications
:forward-action :intro-wizard/step-forward-pressed}]]])
(defview wizard-enter-phrase []
(letsubs [wizard-state [:intro-wizard/enter-phrase]]
[react/keyboard-avoiding-view {:style {:flex 1}}
[toolbar/toolbar
{:style {:border-bottom-width 0
:margin-top 16}}
(toolbar/nav-button
(actions/back #(re-frame/dispatch [:intro-wizard/navigate-back])))
nil]
[react/view {:style {:flex 1
:justify-content :space-between}}
[top-bar {:step :enter-phrase}]
[enter-phrase wizard-state]
[bottom-bar (merge {:step :enter-phrase
:forward-action :multiaccounts.recover/enter-phrase-next-pressed}
wizard-state)]]]))
(defview wizard-recovery-success []
(letsubs [{:keys [pubkey processing?]} [:intro-wizard/recovery-success]]
[react/view {:style {:flex 1}}
[toolbar/toolbar
{:style {:border-bottom-width 0
:margin-top 16}}
(toolbar/nav-button
(actions/back #(re-frame/dispatch [:intro-wizard/navigate-back])))
nil]
[react/view {:style {:flex 1
:justify-content :space-between}}
[top-bar {:step :recovery-success}]
[recovery-success pubkey]
[bottom-bar {:step :recovery-success
:forward-action :multiaccounts.recover/re-encrypt-pressed
:processing? processing?}]]]))

View File

@ -25,8 +25,7 @@
{:transparent? true
:style {:margin-top 32}}
(toolbar/nav-button
(actions/back #(re-frame/dispatch
[::multiaccounts.recover/cancel-pressed])))
(actions/back #(re-frame/dispatch [:navigate-back])))
nil]
[react/view {:flex 1
:flex-direction :column

View File

@ -89,320 +89,3 @@
(def bottom-sheet
{:content bottom-sheet-view
:content-height (if platform/android? 130 65)})
(defview enter-phrase []
(letsubs [{:keys [processing?
passphrase-error
words-count
next-button-disabled?]} [:get-recover-multiaccount]]
[react/keyboard-avoiding-view {:flex 1
:justify-content :space-between
:background-color colors/white}
[toolbar/toolbar
{:transparent? true
:style {:margin-top 32}}
[toolbar/nav-text
{:handler #(re-frame/dispatch [::multiaccounts.recover/cancel-pressed])
:style {:padding-left 21}}
(i18n/label :t/cancel)]
[react/text {:style {:color colors/gray}}
(i18n/label :t/step-i-of-n {:step "1"
:number "2"})]]
[react/view {:flex 1
:flex-direction :column
:justify-content :space-between
:align-items :center}
[react/view {:flex-direction :column
:align-items :center}
[react/view {:margin-top 16}
[react/text {:style {:typography :header
:text-align :center}}
(i18n/label :t/multiaccounts-recover-enter-phrase-title)]]
[react/view {:margin-top 16}
[text-input/text-input-with-label
{:on-change-text #(re-frame/dispatch [::multiaccounts.recover/enter-phrase-input-changed (security/mask-data %)])
:auto-focus true
:error (when passphrase-error (i18n/label passphrase-error))
:placeholder nil
:height 120
:multiline true
:auto-correct false
:keyboard-type "visible-password"
:container {:background-color :white
:min-width "50%"}
:style {:background-color :white
:text-align :center
:font-size 16
:font-weight "700"}}]]
[react/view {:align-items :center}
(when words-count
[react/view {:flex-direction :row
:height 14
:align-items :center}
(when-not next-button-disabled?
[vector-icons/tiny-icon :tiny-icons/tiny-check])
[react/text {:style {:font-size 14
:padding-left 4
:text-align :center
:color colors/black}}
(i18n/label-pluralize words-count :t/words-n)]])]
(when next-button-disabled?
[react/view {:margin-top 17
:align-items :center}
[react/text {:style {:color colors/black
:font-size 14
:text-align :center}}
(i18n/label :t/multiaccounts-recover-enter-phrase-text)]])]
(when processing?
[react/view
[react/activity-indicator {:size :large
:animating true}]
[react/text {:style {:color colors/gray
:margin-top 8}}
(i18n/label :t/processing)]])
[react/view {:flex-direction :row
:justify-content :space-between
:align-items :center
:width "100%"
:height 86}
(when-not processing?
[react/view])
(when-not processing?
[react/view {:margin-right 20}
[components.common/bottom-button
{:on-press #(re-frame/dispatch [::multiaccounts.recover/enter-phrase-next-pressed])
:label (i18n/label :t/next)
:disabled? next-button-disabled?
:forward? true}]])]]]))
(defview success []
(letsubs [multiaccount [:get-recover-multiaccount]]
(let [pubkey (get-in multiaccount [:derived constants/path-whisper-keyword :publicKey])]
[react/view {:flex 1
:justify-content :space-between
:background-color colors/white}
[toolbar/toolbar
{:transparent? true
:style {:margin-top 32}}
nil
nil]
[react/view {:flex 1
:flex-direction :column
:justify-content :space-between
:align-items :center}
[react/view {:flex-direction :column
:align-items :center}
[react/view {:margin-top 16}
[react/text {:style {:typography :header
:text-align :center}}
(i18n/label :t/keycard-recovery-success-header)]]
[react/view {:margin-top 16
:width "85%"
:align-items :center}
[react/text {:style {:color colors/gray
:text-align :center}}
(i18n/label :t/recovery-success-text)]]]
[react/view {:flex-direction :column
:flex 1
:justify-content :center
:align-items :center}
[react/view {:margin-horizontal 16
:flex-direction :column}
[react/view {:justify-content :center
:align-items :center
:margin-bottom 11}
[react/image {:source {:uri (identicon/identicon pubkey)}
:style {:width 61
:height 61
:border-radius 30
:border-width 1
:border-color colors/black-transparent}}]]
[react/text {:style {:text-align :center
:color colors/black
:font-weight "500"}
:number-of-lines 1
:ellipsize-mode :middle}
(gfy/generate-gfy pubkey)]
[react/text {:style {:text-align :center
:margin-top 4
:color colors/gray
:font-family "monospace"}
:number-of-lines 1
:ellipsize-mode :middle}
(utils/get-shortened-address pubkey)]]]
[react/view {:margin-bottom 50}
[react/touchable-highlight
{:on-press #(re-frame/dispatch [::multiaccounts.recover/re-encrypt-pressed])}
[react/view {:background-color colors/blue-light
:align-items :center
:justify-content :center
:flex-direction :row
:width 193
:height 44
:border-radius 10}
[react/text {:style {:color colors/blue}}
(i18n/label :t/re-encrypt-key)]]]]]])))
(defview select-storage []
(letsubs [{:keys [selected-storage-type]} [:intro-wizard]
{view-height :height} [:dimensions/window]]
[react/view {:flex 1
:justify-content :space-between
:background-color colors/white}
[toolbar/toolbar
{:transparent? true
:style {:margin-top 32}}
[toolbar/nav-text
{:handler #(re-frame/dispatch [::multiaccounts.recover/cancel-pressed])
:style {:padding-left 21}}
(i18n/label :t/cancel)]
nil]
[react/view {:flex 1
:justify-content :space-between}
[react/view {:flex-direction :column
:align-items :center}
[react/view {:margin-top 16}
[react/text {:style {:typography :header
:text-align :center}}
(i18n/label :t/intro-wizard-title3)]]
[react/view {:margin-top 16
:width "85%"
:align-items :center}
[react/text {:style {:color colors/gray
:text-align :center}}
(i18n/label :t/intro-wizard-text3)]]]
[intro.views/select-key-storage {:selected-storage-type (if config/hardwallet-enabled? selected-storage-type :default)} view-height]
[react/view {:flex-direction :row
:justify-content :space-between
:align-items :center
:width "100%"
:height 86}
[react/view components.styles/flex]
[react/view {:margin-right 20}
[components.common/bottom-button
{:on-press #(re-frame/dispatch [::multiaccounts.recover/select-storage-next-pressed])
:forward? true}]]]]]))
(defview enter-password []
(letsubs [{:keys [password password-error]} [:get-recover-multiaccount]]
[react/keyboard-avoiding-view {:flex 1
:justify-content :space-between
:background-color colors/white}
[toolbar/toolbar
{:transparent? true
:style {:margin-top 32}}
[toolbar/nav-text
{:handler #(re-frame/dispatch [::multiaccounts.recover/cancel-pressed])
:style {:padding-left 21}}
(i18n/label :t/cancel)]
[react/text {:style {:color colors/gray}}
(i18n/label :t/step-i-of-n {:step "1"
:number "2"})]]
[react/view {:flex 1
:flex-direction :column
:justify-content :space-between
:align-items :center}
[react/view {:flex-direction :column
:align-items :center}
[react/view {:margin-top 16}
[react/text {:style {:typography :header
:text-align :center}}
(i18n/label :t/intro-wizard-title-alt4)]]
[react/view {:margin-top 16
:width "85%"
:align-items :center}
[react/text {:style {:color colors/gray
:text-align :center}}
(i18n/label :t/password-description)]]
[react/view {:margin-top 16}
[text-input/text-input-with-label
{:on-change-text #(re-frame/dispatch [::multiaccounts.recover/enter-password-input-changed (security/mask-data %)])
:auto-focus true
:on-submit-editing #(re-frame/dispatch [::multiaccounts.recover/enter-password-input-submitted])
:secure-text-entry true
:error (when password-error (i18n/label password-error))
:placeholder nil
:height 125
:multiline false
:auto-correct false
:container {:background-color :white
:min-width "50%"}
:style {:background-color :white
:width 200
:text-align :center
:font-size 20
:font-weight "700"}}]]]
[react/view {:flex-direction :row
:justify-content :space-between
:align-items :center
:width "100%"
:height 86}
[react/view]
[react/view {:margin-right 20}
[components.common/bottom-button
{:on-press #(re-frame/dispatch [::multiaccounts.recover/enter-password-next-pressed])
:label (i18n/label :t/next)
:disabled? (empty? password)
:forward? true}]]]]]))
(defview confirm-password []
(letsubs [{:keys [password-confirmation password-error]} [:get-recover-multiaccount]]
[react/keyboard-avoiding-view {:flex 1
:justify-content :space-between
:background-color colors/white}
[toolbar/toolbar
{:transparent? true
:style {:margin-top 32}}
[toolbar/nav-text
{:handler #(re-frame/dispatch [::multiaccounts.recover/cancel-pressed])
:style {:padding-left 21}}
(i18n/label :t/cancel)]
[react/text {:style {:color colors/gray}}
(i18n/label :t/step-i-of-n {:step "1"
:number "2"})]]
[react/view {:flex 1
:flex-direction :column
:justify-content :space-between
:align-items :center}
[react/view {:flex-direction :column
:align-items :center}
[react/view {:margin-top 16}
[react/text {:style {:typography :header
:text-align :center}}
(i18n/label :t/intro-wizard-title-alt5)]]
[react/view {:margin-top 16
:width "85%"
:align-items :center}
[react/text {:style {:color colors/gray
:text-align :center}}
(i18n/label :t/password-description)]]
[react/view {:margin-top 16}
[text-input/text-input-with-label
{:on-change-text #(re-frame/dispatch [::multiaccounts.recover/confirm-password-input-changed %])
:auto-focus true
:on-submit-editing #(re-frame/dispatch [::multiaccounts.recover/confirm-password-input-submitted])
:error (when password-error (i18n/label password-error))
:secure-text-entry true
:placeholder nil
:height 125
:multiline false
:auto-correct false
:container {:background-color :white
:min-width "50%"}
:style {:background-color :white
:width 200
:text-align :center
:font-size 20
:font-weight "700"}}]]]
[react/view {:flex-direction :row
:justify-content :space-between
:align-items :center
:width "100%"
:height 86}
[react/view]
[react/view {:margin-right 20}
[components.common/bottom-button
{:on-press #(re-frame/dispatch [::multiaccounts.recover/confirm-password-next-pressed])
:label (i18n/label :t/next)
:disabled? (empty? password-confirmation)
:forward? true}]]]]]))

View File

@ -1,7 +1,9 @@
(ns status-im.ui.screens.navigation
(:require [re-frame.core :as re-frame]
[status-im.react-native.js-dependencies :as js-dependencies]
[status-im.utils.handlers :as handlers]
[status-im.utils.navigation :as navigation]
[status-im.utils.platform :as platform]
[taoensso.timbre :as log]
[status-im.utils.fx :as fx]))
@ -135,3 +137,37 @@
(assoc :prev-tab-view-id (:view-id db))
(assoc :prev-view-id (:view-id db)))}
(navigate-to-cofx view-id {}))))
;; This atom stores event vector
;; to be dispatched when a react-navigation's BACK
;; actions is invoked
(def wizard-back-event (atom nil))
;; This atom exists in order to avoid
;; endless loop when processing NavigationActions/BACK
;; in react-navigation's getStateForAction fn
(def processing-back-event? (atom false))
(fx/defn reset-processing-flag
{:events [:navigation/reset-processing-flag]}
[{:keys [db] :as cofx}]
{::reset-processing-flag nil})
(re-frame/reg-fx
::reset-processing-flag
(fn []
(reset! processing-back-event? false)))
;; Below two effects are added when we need
;; to override default react-navigation's BACK action
;; processing
(re-frame/reg-fx
::add-wizard-back-event
(fn [event]
(reset! wizard-back-event event)))
(re-frame/reg-fx
::remove-wizard-back-event
(fn [event]
(reset! processing-back-event? false)
(reset! wizard-back-event nil)))

View File

@ -1,6 +1,7 @@
(ns status-im.ui.screens.routing.core
(:require
[status-im.ui.components.react :as react]
[status-im.ui.screens.navigation :as screens.navigation]
[status-im.ui.components.styles :as common-styles]
[status-im.utils.navigation :as navigation]
[cljs-react-navigation.reagent :as nav-reagent]
@ -25,7 +26,7 @@
(defn navigation-events [current-view-id modal? screen-focused?]
[:> navigation/navigation-events
{:on-will-focus
(fn []
(fn [payload]
(reset! screen-focused? true)
(when (not= @view-id current-view-id)
(reset! view-id current-view-id))
@ -38,8 +39,9 @@
(when-not modal?
(status-bar/set-status-bar current-view-id)))
:on-will-blur
(fn []
(fn [payload]
(reset! screen-focused? false)
(log/debug :on-will-blur current-view-id)
;; Reset currently mounted text inputs to their default values
;; on navigating away; this is a privacy measure
(doseq [[text-input default-value] @react/text-input-refs]
@ -117,27 +119,45 @@
(utils/update-if-present :mode name)))
(defn stack-navigator [routes config]
(nav-reagent/stack-navigator
routes
(merge {:headerMode "none"
:cardStyle {:backgroundColor :white}
#_:transitionConfig
#_(fn []
#js {:transitionSpec #js{:duration 10}})
:onTransitionStart (fn [n]
(let [idx (.. n
-navigation
-state
-index)
routes (.. n
-navigation
-state
-routes)]
(when (and (array? routes) (int? idx))
(let [route (aget routes idx)
route-name (keyword (.-routeName route))]
(tabbar/minimize-bar route-name)))))}
(prepare-config config))))
(let [res (nav-reagent/stack-navigator
routes
(merge {:headerMode "none"
:cardStyle {:backgroundColor :white}
#_:transitionConfig
#_(fn []
#js {:transitionSpec #js{:duration 10}})
:onTransitionStart (fn [n]
(let [idx (.. n
-navigation
-state
-index)
routes (.. n
-navigation
-state
-routes)]
(when (and (array? routes) (int? idx))
(let [route (aget routes idx)
route-name (keyword (.-routeName route))]
(tabbar/minimize-bar route-name)))))}
(prepare-config config)))
default-get-state-for-action (.-getStateForAction (.-router res))
new-get-state-for-action (fn [action state]
;; Override default getStateForAction on this stack navigator
;; if we have a custom event set in wizard-back-event atom
(if (and (= (.-type action) (.-BACK navigation/navigation-actions))
@screens.navigation/wizard-back-event)
(if @screens.navigation/processing-back-event?
(do
(reset! screens.navigation/processing-back-event? false)
(default-get-state-for-action action state))
(do
(reset! screens.navigation/processing-back-event? true)
(re-frame/dispatch @screens.navigation/wizard-back-event)
;; Return nil so that BACK event processing ends here
nil))
(default-get-state-for-action action state)))]
(set! (-> res .-router .-getStateForAction) new-get-state-for-action)
res))
(defn twopane-navigator [routes config]
(navigation/twopane-navigator
@ -176,7 +196,7 @@
- keyword, which points to some specific route
- vector of [:modal :screen-key] type when screen should be wrapped as modal
- map with `name`, `screens`, `config` keys, where `screens` is a vector
of children and `config` is `stack-navigator` configuration"
of children and `config` is `stack-navigator` configuration"
(let [[screen-name screen-config]
(cond (keyword? screen)
[screen (screens/get-screen screen)]
@ -198,6 +218,12 @@
:else
(nav-reagent/stack-screen (wrap screen-name screen-config)))]
[screen-name (cond-> {:screen res}
;; TODO issue #8947
;; replace this hack with configuration
(#{:create-multiaccount-choose-key
:recover-multiaccount-select-storage}
screen-name)
(assoc :navigationOptions {:gesturesEnabled false})
(:navigation screen-config)
(assoc :navigationOptions
(:navigation screen-config)))])))

View File

@ -5,6 +5,12 @@
#{:login
:progress
:create-multiaccount
:create-multiaccount-generate-key
:create-multiaccount-choose-key
:create-multiaccount-select-key-storage
:create-multiaccount-create-code
:create-multiaccount-confirm-code
:create-multiaccount-enable-notifications
:recover-multiaccount-enter-phrase
:recover-multiaccount-select-storage
:recover-multiaccount-enter-password
@ -52,6 +58,12 @@
:progress
:keycard-recovery-intro
:create-multiaccount
:create-multiaccount-generate-key
:create-multiaccount-choose-key
:create-multiaccount-select-key-storage
:create-multiaccount-create-code
:create-multiaccount-confirm-code
:create-multiaccount-enable-notifications
:recover-multiaccount-enter-phrase
:recover-multiaccount-select-storage
:recover-multiaccount-enter-password

View File

@ -73,14 +73,19 @@
(def all-screens
{:login login/login
:progress progress/progress
:recover-multiaccount-enter-phrase multiaccounts.recover/enter-phrase
:recover-multiaccount-select-storage multiaccounts.recover/select-storage
:recover-multiaccount-enter-password multiaccounts.recover/enter-password
:recover-multiaccount-confirm-password multiaccounts.recover/confirm-password
:recover-multiaccount-success multiaccounts.recover/success
:create-multiaccount-generate-key intro/wizard-generate-key
:create-multiaccount-choose-key intro/wizard-choose-key
:create-multiaccount-select-key-storage intro/wizard-select-key-storage
:create-multiaccount-create-code intro/wizard-create-code
:create-multiaccount-confirm-code intro/wizard-confirm-code
:create-multiaccount-enable-notifications intro/wizard-enable-notifications
:recover-multiaccount-enter-phrase intro/wizard-enter-phrase
:recover-multiaccount-success intro/wizard-recovery-success
:recover-multiaccount-select-storage intro/wizard-select-key-storage
:recover-multiaccount-enter-password intro/wizard-create-code
:recover-multiaccount-confirm-password intro/wizard-confirm-code
:multiaccounts multiaccounts/multiaccounts
:intro intro/intro
:intro-wizard intro/wizard
:hardwallet-authentication-method hardwallet.authentication/hardwallet-authentication-method
:hardwallet-connect hardwallet.connect/hardwallet-connect
:hardwallet-connect-settings hardwallet.connect/hardwallet-connect

View File

@ -30,6 +30,20 @@
(defonce initial-view-id (atom nil))
(defn bottom-sheet-comp [opts height-atom]
;; We compute bottom sheet height dynamically by rendering it
;; on an invisible view; then, if height is already available
;; (either because it is statically provided or computed),
;; we render the sheet itself
(if (or (not @height-atom) (= 0 @height-atom))
[react/view {:style {:position :absolute :opacity 0}
:on-layout (fn [e]
(let [h (-> e .-nativeEvent .-layout .-height)]
(reset! height-atom h)))}
(when (:content opts)
[(:content opts)])]
[bottom-sheet/bottom-sheet (assoc opts :content-height @height-atom)]))
(views/defview bottom-sheet []
(views/letsubs [{:keys [show? view]} [:bottom-sheet]]
(let [opts (cond-> {:show? show?
@ -53,6 +67,9 @@
(= view :keycard.login/more)
(merge keycard/more-sheet)
(= view :learn-more)
(merge about-app/learn-more)
(= view :private-chat-actions)
(merge home.sheet/private-chat-actions)
@ -60,9 +77,9 @@
(merge home.sheet/group-chat-actions)
(= view :recover-sheet)
(merge recover.views/bottom-sheet))]
[bottom-sheet/bottom-sheet opts])))
(merge recover.views/bottom-sheet))
height-atom (reagent/atom (if (:content-height opts) (:content-height opts) nil))]
[bottom-sheet-comp opts height-atom])))
(defn reset-component-on-mount [view-id component two-pane?]
(when (and @initial-view-id

View File

@ -189,7 +189,6 @@ class SignInView(BaseView):
self.confirm_your_password_input.set_value(password)
self.next_button.click()
self.maybe_later_button.click()
self.maybe_later_button.click()
return self.get_home_view()
def recover_access(self, passphrase: str, password: str = common_password):

View File

@ -547,7 +547,6 @@
:intro-wizard-text3
:intro-wizard-text4
:intro-wizard-text6
:intro-wizard-text7
:intro-wizard-title-alt4
:intro-wizard-title-alt5
:intro-wizard-title1
@ -556,7 +555,6 @@
:intro-wizard-title4
:intro-wizard-title5
:intro-wizard-title6
:intro-wizard-title7
:invalid-extension
:invalid-format
:invalid-key-confirm

View File

@ -8,32 +8,6 @@
;;;; helpers
(deftest check-password-errors
(is (= :required-field (models/check-password-errors nil)))
(is (= :required-field (models/check-password-errors " ")))
(is (= :required-field (models/check-password-errors " \t\n ")))
(is (= :recover-password-too-short) (models/check-password-errors "a"))
(is (= :recover-password-too-short) (models/check-password-errors "abc"))
(is (= :recover-password-too-short) (models/check-password-errors "12345"))
(is (nil? (models/check-password-errors "123456")))
(is (nil? (models/check-password-errors "thisisapasswoord"))))
(deftest check-phrase-errors
(is (= :required-field (models/check-phrase-errors nil)))
(is (= :required-field (models/check-phrase-errors " ")))
(is (= :required-field (models/check-phrase-errors " \t\n ")))
(is (= :recovery-phrase-wrong-length (models/check-phrase-errors "phrase with four words")))
(is (= :recovery-phrase-wrong-length (models/check-phrase-errors "phrase with five cool words")))
(is (nil? (models/check-phrase-errors "monkey monkey monkey monkey monkey monkey monkey monkey monkey monkey monkey monkey")))
(is (nil? (models/check-phrase-errors (string/join " " (repeat 15 "monkey")))))
(is (nil? (models/check-phrase-errors (string/join " " (repeat 18 "monkey")))))
(is (nil? (models/check-phrase-errors (string/join " " (repeat 24 "monkey")))))
(is (= :recovery-phrase-wrong-length (models/check-phrase-errors (string/join " " (repeat 14 "monkey")))))
(is (= :recovery-phrase-wrong-length (models/check-phrase-errors (string/join " " (repeat 11 "monkey")))))
(is (= :recovery-phrase-wrong-length (models/check-phrase-errors (string/join " " (repeat 19 "monkey")))))
(is (= :recovery-phrase-invalid (models/check-phrase-errors "monkey monkey monkey 12345 monkey adf+123 monkey monkey monkey monkey monkey monkey")))
;;NOTE(goranjovic): the following check should be ok because we sanitize extra whitespace
(is (nil? (models/check-phrase-errors " monkey monkey monkey\t monkey monkey monkey monkey monkey monkey monkey monkey monkey \t "))))
(deftest check-phrase-warnings
(is (nil? (models/check-phrase-warnings "monkey monkey monkey monkey monkey monkey monkey monkey monkey monkey monkey monkey")))
@ -43,75 +17,39 @@
;;;; handlers
(deftest set-phrase
(is (= {:db {:multiaccounts/recover {:passphrase "game buzz method pretty olympic fat quit display velvet unveil marine crater"
:passphrase-error nil
:next-button-disabled? false}}}
(is (= {:db {:intro-wizard {:passphrase "game buzz method pretty olympic fat quit display velvet unveil marine crater"
:passphrase-error nil
:next-button-disabled? false}}}
(models/set-phrase {:db {}} (security/mask-data "game buzz method pretty olympic fat quit display velvet unveil marine crater"))))
(is (= {:db {:multiaccounts/recover {:passphrase "game buzz method pretty olympic fat quit display velvet unveil marine crater"
:passphrase-error nil
:next-button-disabled? false}}}
(is (= {:db {:intro-wizard {:passphrase "game buzz method pretty olympic fat quit display velvet unveil marine crater"
:passphrase-error nil
:next-button-disabled? false}}}
(models/set-phrase {:db {}} (security/mask-data "Game buzz method pretty Olympic fat quit DISPLAY velvet unveil marine crater"))))
(is (= {:db {:multiaccounts/recover {:passphrase "game buzz method pretty zeus fat quit display velvet unveil marine crater"
:passphrase-error nil
:next-button-disabled? false}}}
(is (= {:db {:intro-wizard {:passphrase "game buzz method pretty zeus fat quit display velvet unveil marine crater"
:passphrase-error nil
:next-button-disabled? false}}}
(models/set-phrase {:db {}} (security/mask-data "game buzz method pretty zeus fat quit display velvet unveil marine crater"))))
(is (= {:db {:multiaccounts/recover {:passphrase " game\t buzz method pretty olympic fat quit\t display velvet unveil marine crater "
:passphrase-error nil
:next-button-disabled? false}}}
(is (= {:db {:intro-wizard {:passphrase " game\t buzz method pretty olympic fat quit\t display velvet unveil marine crater "
:passphrase-error nil
:next-button-disabled? false}}}
(models/set-phrase {:db {}} (security/mask-data " game\t buzz method pretty olympic fat quit\t display velvet unveil marine crater "))))
(is (= {:db {:multiaccounts/recover {:passphrase "game buzz method pretty 1234 fat quit display velvet unveil marine crater"
:passphrase-error nil
:next-button-disabled? false}}}
(is (= {:db {:intro-wizard {:passphrase "game buzz method pretty 1234 fat quit display velvet unveil marine crater"
:passphrase-error nil
:next-button-disabled? false}}}
(models/set-phrase {:db {}} (security/mask-data "game buzz method pretty 1234 fat quit display velvet unveil marine crater")))))
(deftest validate-phrase
(is (= {:db {:multiaccounts/recover {:passphrase-error nil
:passphrase "game buzz method pretty olympic fat quit display velvet unveil marine crater"}}}
(models/validate-phrase {:db {:multiaccounts/recover {:passphrase "game buzz method pretty olympic fat quit display velvet unveil marine crater"}}})))
(is (= {:db {:multiaccounts/recover {:passphrase-error :recovery-phrase-unknown-words
:passphrase "game buzz method pretty zeus fat quit display velvet unveil marine crater"}}}
(models/validate-phrase {:db {:multiaccounts/recover {:passphrase "game buzz method pretty zeus fat quit display velvet unveil marine crater"}}})))
(is (= {:db {:multiaccounts/recover {:passphrase-error :recovery-phrase-invalid
:passphrase "game buzz method pretty 1234 fat quit display velvet unveil marine crater"}}}
(models/validate-phrase {:db {:multiaccounts/recover {:passphrase "game buzz method pretty 1234 fat quit display velvet unveil marine crater"}}}))))
(deftest set-password
(is (= {:db {:multiaccounts/recover {:password " "
:password-error nil
:password-valid? false}}}
(models/set-password {:db {}} (security/mask-data " "))))
(is (= {:db {:multiaccounts/recover {:password "abc"
:password-error nil
:password-valid? false}}}
(models/set-password {:db {}} (security/mask-data "abc"))))
(is (= {:db {:multiaccounts/recover {:password "thisisapaswoord"
:password-error nil
:password-valid? true}}}
(models/set-password {:db {}} (security/mask-data "thisisapaswoord")))))
(deftest validate-password
(is (= {:db {:multiaccounts/recover {:password " "
:password-error :required-field}}}
(models/validate-password {:db {:multiaccounts/recover {:password " "}}})))
(is (= {:db {:multiaccounts/recover {:password "abc"
:password-error :recover-password-too-short}}}
(models/validate-password {:db {:multiaccounts/recover {:password "abc"}}})))
(is (= {:db {:multiaccounts/recover {:password "thisisapaswoord"
:password-error nil}}}
(models/validate-password {:db {:multiaccounts/recover {:password "thisisapaswoord"}}}))))
(deftest store-multiaccount
(let [new-cofx (models/store-multiaccount {:db {:multiaccounts/recover
(let [new-cofx (models/store-multiaccount {:db {:intro-wizard
{:passphrase "game buzz method pretty zeus fat quit display velvet unveil marine crater"
:password "thisisapaswoord"}}})]
(is (::multiaccounts.create/store-multiaccount new-cofx))))
(deftest recover-multiaccount-with-checks
(let [new-cofx (models/recover-multiaccount-with-checks {:db {:multiaccounts/recover
(let [new-cofx (models/recover-multiaccount-with-checks {:db {:intro-wizard
{:passphrase "game buzz method pretty olympic fat quit display velvet unveil marine crater"
:password "thisisapaswoord"}}})]
(is (::multiaccounts.create/store-multiaccount new-cofx)))
(let [new-cofx (models/recover-multiaccount-with-checks {:db {:multiaccounts/recover
(let [new-cofx (models/recover-multiaccount-with-checks {:db {:intro-wizard
{:passphrase "game buzz method pretty zeus fat quit display velvet unveil marine crater"
:password "thisisapaswoord"}}})]
(is (= (i18n/label :recovery-typo-dialog-title) (-> new-cofx :ui/show-confirmation :title)))

View File

@ -527,8 +527,7 @@
"intro-wizard-text2": "This name is your identity in Status. It cant be changed once you choose one. ",
"intro-wizard-text3": "Your key is stored locally. There is no copy. Only you have access.",
"intro-wizard-text4": "Secure and encrypt your key",
"intro-wizard-text6": "Make it easy to sign and send transactions by enabling fingerprint signing",
"intro-wizard-text7": "Status will notify you about new messages. You can edit your notification preferences later in settings",
"intro-wizard-text6": "Status will notify you about new messages. You can edit your notification preferences later in settings",
"intro-wizard-title-alt4": "Create a password",
"intro-wizard-title-alt5": "Confirm your password",
"intro-wizard-title1": "Get yourself a key first",
@ -536,8 +535,9 @@
"intro-wizard-title3": "Select key storage",
"intro-wizard-title4": "Create a 6-digit passcode",
"intro-wizard-title5": "Confirm the passcode",
"intro-wizard-title6": "Enable fingerprint",
"intro-wizard-title7": "Enable notifications",
"intro-wizard-title6": "Enable notifications",
"are-you-sure-to-cancel": "Are you sure you want to cancel?",
"you-will-start-from-scratch": "You will start from scratch with a new set of keys",
"invalid-extension": "Invalid extension URI",
"invalid-format": "Invalid format\nMust be {{format}}",
"invalid-key-confirm": "Apply",
@ -670,7 +670,7 @@
"mobile-syncing-sheet-details": "Status tends to use a lot of data when syncing chats. You can choose not to sync when on mobile network",
"mobile-syncing-sheet-title": "Sync using Mobile data",
"more": "more",
"multiaccounts-recover-enter-phrase-text": "Enter 12, 15, 18, 21 or 24 words.\nSeperate words by a single space.",
"multiaccounts-recover-enter-phrase-text": "Enter 12, 15, 18, 21 or 24 words.\nSeparate words by a single space.",
"multiaccounts-recover-enter-phrase-title": "Enter your seed phrase",
"name": "Name",
"name-description": "Change this anytime in your Profile.",
@ -1069,6 +1069,7 @@
"welcome-screen-text": "Set up your wallet, invite friends to chat\n and browse popular dapps!",
"welcome-to-status": "Welcome to Status",
"welcome-to-status-description": "Here you can chat with people in a secure private chat, browse and interact with DApps.",
"word-count": "Word count",
"word-n": "Word #{{number}}",
"word-n-description": "In order to check if you have backed up your seed phrase correctly, enter the word #{{number}} above.",
"words-n": {

View File

@ -514,8 +514,7 @@
"intro-wizard-text2": "Ce nom est votre identité dans Status. Il ne peut pas être changé une fois que vous en choisissez un.",
"intro-wizard-text3": "Votre clé est stockée localement. Il n'y a pas de copie. Vous êtes le seul à y avoir accès.",
"intro-wizard-text4": "Sécurisez et chiffrez votre clé",
"intro-wizard-text6": "Facilitez la signature et l'envoi de transactions en activant la signature par empreinte digitale.",
"intro-wizard-text7": "Status vous informera des nouveaux messages. Vous pouvez modifier vos préférences de notification ultérieurement dans les paramètres.",
"intro-wizard-text6": "Status vous informera des nouveaux messages. Vous pouvez modifier vos préférences de notification ultérieurement dans les paramètres.",
"intro-wizard-title-alt4": "Créer un mot de passe",
"intro-wizard-title-alt5": "Confirmer votre mot de passe",
"intro-wizard-title1": "Procurez-vous une clé en premier",
@ -523,8 +522,7 @@
"intro-wizard-title3": "Sélectionner le stockage des clés",
"intro-wizard-title4": "Créer un code à 6 chiffres",
"intro-wizard-title5": "Confirmer le code",
"intro-wizard-title6": "Activer l'empreinte digitale",
"intro-wizard-title7": "Activer les notifications",
"intro-wizard-title6": "Activer les notifications",
"invalid-extension": "Extension URI invalide",
"invalid-format": "Format invalide \nDoit être {{format}}",
"invalid-key-confirm": "Appliquer",

View File

@ -512,8 +512,7 @@
"intro-wizard-text2": "この名前はStatusにおけるあなたの身分情報です。1度選択すれば変更できません。",
"intro-wizard-text3": "あなたの鍵はローカルに保存されています。コピーはありません。アクセスできるのはあなただけです。",
"intro-wizard-text4": "鍵を暗号化し保護する",
"intro-wizard-text6": "指紋を使った署名を有効化してトランザクションの送信と署名を簡単にする",
"intro-wizard-text7": "Statusは新しいメッセージを通知します。通知設定は設定でいつでも変更できます。",
"intro-wizard-text6": "Statusは新しいメッセージを通知します。通知設定は設定でいつでも変更できます。",
"intro-wizard-title-alt4": "パスワードを作成",
"intro-wizard-title-alt5": "パスワードを確認",
"intro-wizard-title1": "まず鍵を取得する",
@ -521,8 +520,7 @@
"intro-wizard-title3": "鍵の保管場所を選択",
"intro-wizard-title4": "6桁のコードを作成する",
"intro-wizard-title5": "コードを確認",
"intro-wizard-title6": "指紋を有効にする",
"intro-wizard-title7": "通知を有効にする",
"intro-wizard-title6": "通知を有効にする",
"invalid-extension": "拡張機能のURLが無効です",
"invalid-format": "無効な形式\n {{format}}の形式である必要があります",
"invalid-key-confirm": "適用する",

View File

@ -524,8 +524,7 @@
"intro-wizard-text2": "아래에서 스테이터스 닉네임을 선택합니다. 닉네임은 변경할 수 없습니다.",
"intro-wizard-text3": "키는 기기에 저장되며 사본을 생성하지 않습니다. 사용자만이 키에 접근 할 수 있습니다.",
"intro-wizard-text4": "키 보안 및 암호화",
"intro-wizard-text6": "지문 인식을 통해 트랜잭션을 안전하게 서명하고 진행할 수 있습니다",
"intro-wizard-text7": "새 메시지에 대한 알림을 수신합니다. 알림 설정은 프로필 설정에서 변경할 수 있습니다.",
"intro-wizard-text6": "새 메시지에 대한 알림을 수신합니다. 알림 설정은 프로필 설정에서 변경할 수 있습니다.",
"intro-wizard-title-alt4": "비밀번호 만들기",
"intro-wizard-title-alt5": "비밀번호 확인",
"intro-wizard-title1": "키 생성하기",
@ -533,8 +532,7 @@
"intro-wizard-title3": "키 스토리지 선택",
"intro-wizard-title4": "6자리 패스코드 만들기",
"intro-wizard-title5": "패스코드 확인",
"intro-wizard-title6": "지문 사용",
"intro-wizard-title7": "알림 사용",
"intro-wizard-title6": "알림 사용",
"invalid-extension": "잘못된 확장 URI",
"invalid-format": "잘못된 형식\n{{format}} 이어야 합니다",
"invalid-key-confirm": "적용",

View File

@ -541,8 +541,7 @@
"intro-wizard-text2": "Это имя - ваша личность в Status. Его нельзя изменить после первоначального выбора. ",
"intro-wizard-text3": "Ваш ключ хранится локально. Не существует никакой копии. Только вы имеете доступ.",
"intro-wizard-text4": "Защитите и зашифруйте свой ключ",
"intro-wizard-text6": "Упростите подписание и отправку транзакций, включив подпись по отпечатку пальца",
"intro-wizard-text7": "Status будет уведомлять вас о новых сообщениях. Вы можете изменить настройки уведомлений позже в настройках",
"intro-wizard-text6": "Status будет уведомлять вас о новых сообщениях. Вы можете изменить настройки уведомлений позже в настройках",
"intro-wizard-title-alt4": "Создать пароль",
"intro-wizard-title-alt5": "Подтвердить пароль",
"intro-wizard-title1": "Получить ключ",
@ -550,8 +549,7 @@
"intro-wizard-title3": "Выбрать хранилище ключей",
"intro-wizard-title4": "Создать 6-значный код",
"intro-wizard-title5": "Подтвердить код",
"intro-wizard-title6": "Включить отпечаток пальца",
"intro-wizard-title7": "Включить уведомления",
"intro-wizard-title6": "Включить уведомления",
"invalid-extension": "Неверный URI расширения",
"invalid-format": "Недопустимый формат \n Ожидается {{format}}",
"invalid-key-confirm": "Применить",

View File

@ -523,8 +523,7 @@
"intro-wizard-text2": "此名称是您在Status中的身份。一旦选择就无法更改。",
"intro-wizard-text3": "您的密钥存储在本地。没有副本。只有你有权访问。",
"intro-wizard-text4": "保护并加密您的密钥",
"intro-wizard-text6": "通过启用指纹签名,可以轻松签名和发送交易",
"intro-wizard-text7": "有新消息Status将通知您。您可以稍后在设置中编辑通知偏好设置",
"intro-wizard-text6": "有新消息Status将通知您。您可以稍后在设置中编辑通知偏好设置",
"intro-wizard-title-alt4": "创建密码",
"intro-wizard-title-alt5": "确认你的密码",
"intro-wizard-title1": "先获取一个秘钥",
@ -532,8 +531,7 @@
"intro-wizard-title3": "选择密钥存储",
"intro-wizard-title4": "创建一个6位数字密码",
"intro-wizard-title5": "确认密码",
"intro-wizard-title6": "启用指纹",
"intro-wizard-title7": "启用通知",
"intro-wizard-title6": "启用通知",
"invalid-extension": "无效的扩展网址",
"invalid-format": "无效的格式\n必须是{{format}}",
"invalid-key-confirm": "应用",

View File

@ -487,7 +487,6 @@
"intro-wizard-text3": "",
"intro-wizard-text4": "",
"intro-wizard-text6": "",
"intro-wizard-text7": "",
"intro-wizard-title-alt4": "",
"intro-wizard-title-alt5": "",
"intro-wizard-title1": "",
@ -496,7 +495,6 @@
"intro-wizard-title4": "",
"intro-wizard-title5": "",
"intro-wizard-title6": "",
"intro-wizard-title7": "",
"invalid-extension": "无效的扩展网址",
"invalid-format": "无效的格式\n必须是{{format}}",
"invalid-key-confirm": "应用",