[#7132] add keycard settings

Signed-off-by: Dmitry Novotochinov <dmitry.novot@gmail.com>
This commit is contained in:
Dmitry Novotochinov 2018-12-25 17:57:19 +03:00
parent 7322626464
commit cee18d23b8
No known key found for this signature in database
GPG Key ID: 43D1DAF5AD39C927
32 changed files with 1111 additions and 160 deletions

View File

@ -54,7 +54,7 @@
"react-native-safe-area-view": "0.9.0",
"react-native-securerandom": "git+https://github.com/status-im/react-native-securerandom.git#0.1.1-2",
"react-native-splash-screen": "3.1.1",
"react-native-status-keycard": "git+https://github.com/status-im/react-native-status-keycard.git#v2.3.1",
"react-native-status-keycard": "git+https://github.com/status-im/react-native-status-keycard.git#v2.3.2",
"react-native-svg": "6.5.2",
"react-native-tcp": "git+https://github.com/status-im/react-native-tcp.git#v3.3.0-1-status",
"react-native-udp": "git+https://github.com/status-im/react-native-udp.git#2.3.1-1",

View File

@ -6007,9 +6007,9 @@ react-native-splash-screen@3.1.1:
resolved "https://registry.yarnpkg.com/react-native-splash-screen/-/react-native-splash-screen-3.1.1.tgz#1a4e46c9fdce53ff52af2a2cb4181788c4e30b30"
integrity sha512-PU2YocOSGbLjL9Vgcq/cwMNuHHKNjjuPpa1IPMuWo+6EB/fSZ5VOmxSa7+eucQe3631s3NhGuk3eHKahU03a4Q==
"react-native-status-keycard@git+https://github.com/status-im/react-native-status-keycard.git#v2.3.1":
version "2.3.1"
resolved "git+https://github.com/status-im/react-native-status-keycard.git#2dffe6aae4d2d29bc2649637f3c4a9157b2460c6"
"react-native-status-keycard@git+https://github.com/status-im/react-native-status-keycard.git#v2.3.2":
version "2.3.2"
resolved "git+https://github.com/status-im/react-native-status-keycard#4c2aada5dc3b9d106935ab1747ab33ad4e94547e"
react-native-svg@6.5.2:
version "6.5.2"

4
resources/icons/logout.svg Executable file
View File

@ -0,0 +1,4 @@
<svg width="18" height="19" viewBox="0 0 18 19" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9 0C8.44771 0 8 0.447716 8 1V10C8 10.5523 8.44771 11 9 11C9.55229 11 10 10.5523 10 10V1C10 0.447715 9.55229 0 9 0Z" fill="#FF2D55"/>
<path d="M4.05025 5.05025C4.44078 4.65972 4.44078 4.02656 4.05025 3.63603C3.65973 3.24551 3.02656 3.24551 2.63604 3.63603C1.00845 5.26362 0 7.51499 0 9.99999C0 14.9706 4.02944 19 9 19C13.9706 19 18 14.9706 18 9.99999C18 7.51499 16.9916 5.26363 15.364 3.63604C14.9734 3.24552 14.3403 3.24552 13.9498 3.63604C13.5592 4.02656 13.5592 4.65973 13.9498 5.05025C15.2176 6.3181 16 8.06672 16 9.99999C16 13.866 12.866 17 9 17C5.13401 17 2 13.866 2 9.99999C2 8.06672 2.78241 6.31809 4.05025 5.05025Z" fill="#FF2D55"/>
</svg>

After

Width:  |  Height:  |  Size: 754 B

BIN
resources/images/ui/hardwallet-card.png Normal file → Executable file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 616 KiB

After

Width:  |  Height:  |  Size: 247 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

View File

@ -61,7 +61,8 @@
[{:keys [signing-phrase
status
db] :as cofx}
{:keys [pubkey address mnemonic installation-id keycard-instance-uid]}
{:keys [pubkey address mnemonic installation-id
keycard-instance-uid keycard-pairing keycard-paired-on]}
password
{:keys [seed-backed-up? login?] :or {login? true}}]
(let [normalized-address (utils.hex/normalize-hex address)
@ -77,6 +78,8 @@
:seed-backed-up? seed-backed-up?
:mnemonic mnemonic
:keycard-instance-uid keycard-instance-uid
:keycard-pairing keycard-pairing
:keycard-paired-on keycard-paired-on
:settings (constants/default-account-settings)}]
(log/debug "account-created")
(when-not (string/blank? pubkey)

View File

@ -57,6 +57,9 @@
(spec/def :account/wallet-set-up-passed? (spec/nilable boolean?))
(spec/def :account/mainnet-warning-shown? (spec/nilable boolean?))
(spec/def :account/desktop-alpha-release-warning-shown? (spec/nilable boolean?))
(spec/def :account/keycard-instance-uid (spec/nilable string?))
(spec/def :account/keycard-pairing (spec/nilable string?))
(spec/def :account/keycard-paired-on (spec/nilable int?))
(spec/def :accounts/account (spec/keys :req-un [:account/name :account/address :account/public-key
:account/photo-path :account/signing-phrase
@ -69,7 +72,10 @@
:account/wallet-set-up-passed? :account/last-request
:account/bootnodes :account/extensions
:account/mainnet-warning-shown?
:account/desktop-alpha-release-warning-shown?]))
:account/desktop-alpha-release-warning-shown?
:account/keycard-instance-uid
:account/keycard-pairing
:account/keycard-paired-on]))
(spec/def :accounts/accounts (spec/nilable (spec/map-of :account/address :accounts/account)))

View File

@ -217,3 +217,8 @@
(def v16 (assoc-in v15
[:properties :keycard-instance-uid]
{:type :string :optional true}))
(def v17 (update v16 :properties merge {:keycard-pairing
{:type :string :optional true}
:keycard-paired-on
{:type :int :optional true}}))

View File

@ -76,6 +76,11 @@
(def v20 v19)
(def v21 [network/v1
bootnode/v4
extension/v12
account/v17])
;; put schemas ordered by version
(def schemas [{:schema v1
:schemaVersion 1
@ -136,4 +141,7 @@
:migration migrations/v19}
{:schema v20
:schemaVersion 20
:migration migrations/v20}])
:migration migrations/v20}
{:schema v21
:schemaVersion 21
:migration migrations/v21}])

View File

@ -196,3 +196,6 @@
;; - nightlies that had v18 as an empty transition will apply the empty transition
;; v19 again, and migrate Infura IDs as v20.
(migrate-infura-project-ids! new-realm))
(defn v21 [old-realm new-realm]
(log/debug "migrating base database v21: " old-realm new-realm))

View File

@ -863,8 +863,14 @@
(handlers/register-handler-fx
:hardwallet.ui/get-application-info
(fn [_ _]
{:hardwallet/get-application-info nil}))
(fn [{:keys [db]} _]
{:hardwallet/get-application-info (get-in db [:hardwallet :secrets :pairing])}))
(handlers/register-handler-fx
:hardwallet.callback/on-retrieve-pairing-success
(fn [{:keys [db]} [_ pairing-data]]
{:db (update-in db [:hardwallet :secrets] merge (select-keys pairing-data
[:pairing :paired-on]))}))
(handlers/register-handler-fx
:hardwallet.callback/on-register-card-events
@ -944,10 +950,60 @@
(fn [cofx [_ error]]
(hardwallet/on-generate-and-load-key-error cofx error)))
(handlers/register-handler-fx
:hardwallet.callback/on-unblock-pin-success
(fn [cofx _]
(hardwallet/on-unblock-pin-success cofx)))
(handlers/register-handler-fx
:hardwallet.callback/on-unblock-pin-error
(fn [cofx [_ error]]
(hardwallet/on-unblock-pin-error cofx error)))
(handlers/register-handler-fx
:hardwallet.callback/on-verify-pin-success
(fn [cofx _]
(hardwallet/on-verify-pin-success cofx)))
(handlers/register-handler-fx
:hardwallet.callback/on-verify-pin-error
(fn [cofx [_ error]]
(hardwallet/on-verify-pin-error cofx error)))
(handlers/register-handler-fx
:hardwallet.callback/on-change-pin-success
(fn [cofx _]
(hardwallet/on-change-pin-success cofx)))
(handlers/register-handler-fx
:hardwallet.callback/on-change-pin-error
(fn [cofx [_ error]]
(hardwallet/on-change-pin-error cofx error)))
(handlers/register-handler-fx
:hardwallet.callback/on-unpair-success
(fn [cofx _]
(hardwallet/on-unpair-success cofx)))
(handlers/register-handler-fx
:hardwallet.callback/on-unpair-error
(fn [cofx [_ error]]
(hardwallet/on-unpair-error cofx error)))
(handlers/register-handler-fx
:hardwallet.callback/on-delete-success
(fn [cofx _]
(hardwallet/on-delete-success cofx)))
(handlers/register-handler-fx
:hardwallet.callback/on-delete-error
(fn [cofx [_ error]]
(hardwallet/on-delete-error cofx error)))
(handlers/register-handler-fx
:hardwallet.ui/status-hardwallet-option-pressed
(fn [cofx _]
(hardwallet/navigate-to-connect-screen cofx)))
(hardwallet/status-hardwallet-option-pressed cofx)))
(handlers/register-handler-fx
:hardwallet.ui/password-option-pressed
@ -1072,7 +1128,12 @@
(handlers/register-handler-fx
:hardwallet.ui/pin-numpad-button-pressed
(fn [cofx [_ number step]]
(hardwallet/process-pin-input cofx number step)))
(hardwallet/update-pin cofx number step)))
(handlers/register-handler-fx
:hardwallet/process-pin-input
(fn [cofx _]
(hardwallet/process-pin-input cofx)))
(handlers/register-handler-fx
:hardwallet.ui/pin-numpad-delete-button-pressed
@ -1107,6 +1168,61 @@
(fn [cofx _]
(hardwallet/error-button-pressed cofx)))
(handlers/register-handler-fx
:keycard-settings.ui/change-pin-pressed
(fn [cofx _]
(hardwallet/change-pin-pressed cofx)))
(handlers/register-handler-fx
:hardwallet/proceed-to-change-pin
(fn [cofx _]
(hardwallet/proceed-to-change-pin cofx)))
(handlers/register-handler-fx
:keycard-settings.ui/unpair-card-pressed
(fn [cofx _]
(hardwallet/unpair-card-pressed cofx)))
(handlers/register-handler-fx
:keycard-settings.ui/unpair-card-confirmed
(fn [cofx _]
(hardwallet/unpair-card-confirmed cofx)))
(handlers/register-handler-fx
:hardwallet/unpair
(fn [cofx _]
(hardwallet/unpair cofx)))
(handlers/register-handler-fx
:keycard-settings.ui/reset-card-pressed
(fn [cofx _]
(hardwallet/reset-card-pressed cofx)))
(handlers/register-handler-fx
:keycard-settings.ui/reset-card-next-button-pressed
(fn [cofx _]
(hardwallet/reset-card-next-button-pressed cofx)))
(handlers/register-handler-fx
:hardwallet/proceed-to-reset-card
(fn [cofx _]
(hardwallet/proceed-to-reset-card cofx)))
(handlers/register-handler-fx
:hardwallet/unpair-and-delete
(fn [cofx _]
(hardwallet/unpair-and-delete cofx)))
(handlers/register-handler-fx
:hardwallet/navigate-to-enter-pin-screen
(fn [cofx _]
(hardwallet/navigate-to-enter-pin-screen cofx)))
(handlers/register-handler-fx
:hardwallet/navigate-to-reset-card-screen
(fn [cofx _]
(hardwallet/navigate-to-reset-card-screen cofx)))
;; browser module
(handlers/register-handler-fx
@ -1303,6 +1419,11 @@
(fn [cofx]
(browser/open-url cofx "names.statusnet.eth")))
(handlers/register-handler-fx
:profile.ui/keycard-settings-button-pressed
(fn [cofx]
(hardwallet/navigate-to-keycard-settings cofx)))
;; transport module
(handlers/register-handler-fx

View File

@ -13,14 +13,15 @@
:error (.-message object)})
(defn check-nfc-support []
(when config/hardwallet-enabled?
(when (and config/hardwallet-enabled?
platform/android?)
(.. keycard
nfcIsSupported
(then #(re-frame/dispatch [:hardwallet.callback/check-nfc-support-success %])))))
(defn check-nfc-enabled []
(when (and platform/android?
config/hardwallet-enabled?)
(when (and config/hardwallet-enabled?
platform/android?)
(.. keycard
nfcIsEnabled
(then #(re-frame/dispatch [:hardwallet.callback/check-nfc-enabled-success %])))))
@ -49,19 +50,12 @@
"keyCardOnDisconnected"
#(re-frame/dispatch [:hardwallet.callback/on-card-disconnected %]))}])))
(defn get-application-info []
(defn get-application-info [pairing]
(.. keycard
getApplicationInfo
(getApplicationInfo (str pairing))
(then #(re-frame/dispatch [:hardwallet.callback/on-get-application-info-success %]))
(catch #(re-frame/dispatch [:hardwallet.callback/on-get-application-info-error (error-object->map %)]))))
(defn start []
(when config/hardwallet-enabled?
(.. keycard
start
(then #(log/debug "[hardwallet] module started"))
(catch #(log/debug "[hardwallet] module not started " %)))))
(defn install-applet-and-init-card []
(when config/hardwallet-enabled?
(.. keycard
@ -76,6 +70,7 @@
(pair password)
(then #(re-frame/dispatch [:hardwallet.callback/on-pairing-success %]))
(catch #(re-frame/dispatch [:hardwallet.callback/on-pairing-error (error-object->map %)])))))
(defn generate-mnemonic
[{:keys [pairing]}]
(when pairing
@ -91,3 +86,50 @@
(generateAndLoadKey mnemonic pairing pin)
(then #(re-frame/dispatch [:hardwallet.callback/on-generate-and-load-key-success %]))
(catch #(re-frame/dispatch [:hardwallet.callback/on-generate-and-load-key-error (error-object->map %)])))))
(defn unblock-pin
[{:keys [puk new-pin pairing]}]
(when (and pairing new-pin puk)
(.. keycard
(unblockPin pairing puk new-pin)
(then #(re-frame/dispatch [:hardwallet.callback/on-unblock-pin-success %]))
(catch #(re-frame/dispatch [:hardwallet.callback/on-unblock-pin-error (error-object->map %)])))))
(defn verify-pin
[{:keys [pin pairing]}]
(when (and pairing pin)
(.. keycard
(verifyPin pairing pin)
(then #(re-frame/dispatch [:hardwallet.callback/on-verify-pin-success %]))
(catch #(re-frame/dispatch [:hardwallet.callback/on-verify-pin-error (error-object->map %)])))))
(defn change-pin
[{:keys [current-pin new-pin pairing]}]
(when (and pairing current-pin new-pin)
(.. keycard
(changePin pairing current-pin new-pin)
(then #(re-frame/dispatch [:hardwallet.callback/on-change-pin-success %]))
(catch #(re-frame/dispatch [:hardwallet.callback/on-change-pin-error (error-object->map %)])))))
(defn unpair
[{:keys [pin pairing]}]
(when (and pairing pin)
(.. keycard
(unpair pairing pin)
(then #(re-frame/dispatch [:hardwallet.callback/on-unpair-success %]))
(catch #(re-frame/dispatch [:hardwallet.callback/on-unpair-error (error-object->map %)])))))
(defn delete
[]
(.. keycard
(delete)
(then #(re-frame/dispatch [:hardwallet.callback/on-delete-success %]))
(catch #(re-frame/dispatch [:hardwallet.callback/on-delete-error (error-object->map %)]))))
(defn unpair-and-delete
[{:keys [pin pairing]}]
(when (and pairing pin)
(.. keycard
(unpairAndDelete pairing pin)
(then #(re-frame/dispatch [:hardwallet.callback/on-delete-success %]))
(catch #(re-frame/dispatch [:hardwallet.callback/on-delete-error (error-object->map %)])))))

View File

@ -8,31 +8,97 @@
[taoensso.timbre :as log]
[status-im.i18n :as i18n]
[status-im.accounts.create.core :as accounts.create]
[status-im.accounts.login.core :as accounts.login]
[status-im.node.core :as node]))
[status-im.node.core :as node]
[status-im.utils.datetime :as utils.datetime]
[status-im.data-store.accounts :as accounts-store]
[clojure.string :as string]))
(def default-pin "000000")
(defn get-pairing [db]
(or
(get-in db [:hardwallet :secrets :pairing])
(get-in db [:account/account :pairing])))
(fx/defn remove-pairing-from-account
[{:keys [db]}]
(let [account (-> db
(get :account/account)
(assoc :keycard-pairing nil)
(assoc :keycard-paired-on nil))]
;TODO remove remove-pairing when keycard login will be ready
{:hardwallet/remove-pairing nil
:data-store/base-tx [(accounts-store/save-account-tx account)]}))
(defn hardwallet-supported? [{:keys [db]}]
(and config/hardwallet-enabled?
platform/android?
(get-in db [:hardwallet :nfc-supported?])))
(fx/defn unauthorized-operation
[{:keys [db] :as cofx}]
(fx/merge cofx
{:db (assoc-in db [:hardwallet :on-card-connected] nil)
:utils/show-popup {:title ""
:content (i18n/label :t/keycard-unauthorized-operation)}}
(navigation/navigate-to-cofx :keycard-settings nil)))
(fx/defn navigate-to-keycard-settings
[{:keys [db] :as cofx}]
(fx/merge cofx
{:db (-> db
(assoc-in [:hardwallet :pin :on-verified] nil)
(assoc-in [:hardwallet :on-card-connected] nil)
(assoc-in [:hardwallet :setup-step] nil))}
(navigation/navigate-to-cofx :keycard-settings nil)))
(fx/defn navigate-to-enter-pin-screen
[{:keys [db] :as cofx}]
(let [keycard-instance-uid (get-in db [:hardwallet :application-info :instance-uid])
account-instance-uid (get-in db [:account/account :keycard-instance-uid])]
(if (or (nil? account-instance-uid)
(and keycard-instance-uid
(= keycard-instance-uid account-instance-uid)))
(fx/merge cofx
{:db (assoc-in db [:hardwallet :pin :current] [])}
(navigation/navigate-to-cofx :enter-pin nil))
(unauthorized-operation cofx))))
(fx/defn navigate-to-authentication-method
[cofx]
(if (hardwallet-supported? cofx)
(navigation/navigate-to-cofx cofx :hardwallet-authentication-method nil)
(accounts.create/navigate-to-create-account-screen cofx)))
(defn settings-screen-did-load
[{:keys [db]}]
{:db (-> db
(assoc-in [:hardwallet :pin :on-verified] nil)
(assoc-in [:hardwallet :on-card-connected] nil)
(assoc-in [:hardwallet :setup-step] nil))})
(defn reset-card-screen-did-load
[{:keys [db]}]
{:db (assoc-in db [:hardwallet :reset-card :disabled?] false)})
(fx/defn on-register-card-events
[{:keys [db]} listeners]
{:db (update-in db [:hardwallet :listeners] merge listeners)})
(fx/defn on-get-application-info-success
[{:keys [db]} info]
(let [info' (js->clj info :keywordize-keys true)]
{:db (-> db
(assoc-in [:hardwallet :application-info] info')
(assoc-in [:hardwallet :application-info :applet-installed?] true)
(assoc-in [:hardwallet :application-info-error] nil))}))
[{:keys [db] :as cofx} info]
(let [info' (js->clj info :keywordize-keys true)
{:keys [pin-retry-counter puk-retry-counter]} info'
enter-step (get-in db [:hardwallet :pin :enter-step])
enter-step' (if (zero? pin-retry-counter) :puk enter-step)]
(fx/merge cofx
{:db (-> db
(assoc-in [:hardwallet :pin :enter-step] enter-step')
(assoc-in [:hardwallet :application-info] info')
(assoc-in [:hardwallet :application-info :applet-installed?] true)
(assoc-in [:hardwallet :application-info-error] nil))}
(when (zero? puk-retry-counter)
(navigation/navigate-to-cofx :keycard-settings nil)))))
(fx/defn on-get-application-info-error
[{:keys [db]} error]
@ -49,17 +115,172 @@
[{:keys [db]} enabled?]
{:db (assoc-in db [:hardwallet :nfc-enabled?] enabled?)})
(fx/defn navigate-to-connect-screen [{:keys [db] :as cofx}]
(fx/defn status-hardwallet-option-pressed [{:keys [db] :as cofx}]
(fx/merge cofx
{:hardwallet/check-nfc-enabled nil
:hardwallet/register-card-events nil
:db (assoc-in db [:hardwallet :setup-step] :begin)}
:db (-> db
(assoc-in [:hardwallet :setup-step] :begin)
(assoc-in [:hardwallet :on-card-connected] nil)
(assoc-in [:hardwallet :pin :on-verified] nil))}
(navigation/navigate-to-cofx :hardwallet-connect nil)))
(fx/defn success-button-pressed [cofx]
;; login not implemented yet
)
(fx/defn change-pin-pressed
[{:keys [db] :as cofx}]
(let [card-connected? (get-in db [:hardwallet :card-connected?])
pin-retry-counter (get-in db [:hardwallet :application-info :pin-retry-counter])
enter-step (if (zero? pin-retry-counter) :puk :current)]
(fx/merge cofx
{:db (-> db
(assoc-in [:hardwallet :on-card-connected] :hardwallet/navigate-to-enter-pin-screen)
(assoc-in [:hardwallet :pin] {:enter-step enter-step
:current []
:puk []
:original []
:confirmation []
:status nil
:error-label nil
:on-verified :hardwallet/proceed-to-change-pin}))}
(if card-connected?
(navigate-to-enter-pin-screen)
(navigation/navigate-to-cofx :hardwallet-connect nil)))))
(fx/defn proceed-to-change-pin
[{:keys [db]}]
{:db (-> db
(assoc-in [:hardwallet :pin :enter-step] :original)
(assoc-in [:hardwallet :pin :status] nil))})
(fx/defn unpair-card-pressed
[_]
{:ui/show-confirmation {:title (i18n/label :t/unpair-card)
:content (i18n/label :t/unpair-card-confirmation)
:confirm-button-text (i18n/label :t/yes)
:cancel-button-text (i18n/label :t/no)
:on-accept #(re-frame/dispatch [:keycard-settings.ui/unpair-card-confirmed])
:on-cancel #()}})
(fx/defn unpair-card-confirmed
[{:keys [db] :as cofx}]
(let [card-connected? (get-in db [:hardwallet :card-connected?])
pin-retry-counter (get-in db [:hardwallet :application-info :pin-retry-counter])
enter-step (if (zero? pin-retry-counter) :puk :current)]
(fx/merge cofx
{:db (-> db
(assoc-in [:hardwallet :on-card-connected] :hardwallet/navigate-to-enter-pin-screen)
(assoc-in [:hardwallet :pin] {:enter-step enter-step
:current []
:puk []
:status nil
:error-label nil
:on-verified :hardwallet/unpair}))}
(if card-connected?
(navigate-to-enter-pin-screen)
(navigation/navigate-to-cofx :hardwallet-connect nil)))))
(defn- vector->string [v]
"Converts numbers stored in vector into string,
e.g. [1 2 3 4 5 6] -> \"123456\""
(apply str v))
(fx/defn unpair
[{:keys [db]}]
(let [pin (vector->string (get-in db [:hardwallet :pin :current]))
pairing (get-pairing db)]
{:hardwallet/unpair {:pin pin
:pairing pairing}}))
(fx/defn unpair-and-delete
[{:keys [db]}]
(let [pin (vector->string (get-in db [:hardwallet :pin :current]))
pairing (get-pairing db)]
{:hardwallet/unpair-and-delete {:pin pin
:pairing pairing}}))
(fx/defn on-delete-success
[{:keys [db] :as cofx}]
(fx/merge cofx
{:db (-> db
(assoc-in [:hardwallet :secrets] nil)
(assoc-in [:hardwallet :application-info] nil)
(assoc-in [:hardwallet :on-card-connected] nil)
(assoc-in [:hardwallet :pin] {:status nil
:error-label nil
:on-verified nil}))
:utils/show-popup {:title ""
:content (i18n/label :t/card-reseted)}}
(remove-pairing-from-account)
(navigation/navigate-to-cofx :keycard-settings nil)))
(fx/defn on-delete-error
[{:keys [db] :as cofx} error]
(log/debug "[hardwallet] delete error" error)
(fx/merge cofx
{:db (-> db
(assoc-in [:hardwallet :on-card-connected] nil)
(assoc-in [:hardwallet :pin] {:status nil
:error-label nil
:on-verified nil}))
:hardwallet/get-application-info nil
:utils/show-popup {:title ""
:content (i18n/label :t/something-went-wrong)}}
(navigation/navigate-to-cofx :keycard-settings nil)))
(fx/defn reset-card-pressed
[{:keys [db] :as cofx}]
(let [card-connected? (get-in db [:hardwallet :card-connected?])]
(if card-connected?
(navigation/navigate-to-cofx cofx :reset-card nil)
(fx/merge cofx
{:db (assoc-in db [:hardwallet :on-card-connected] :hardwallet/navigate-to-reset-card-screen)}
(navigation/navigate-to-cofx :hardwallet-connect nil)))))
(fx/defn delete-card
[{:keys [db] :as cofx}]
(let [keycard-instance-uid (get-in db [:hardwallet :application-info :instance-uid])
account-instance-uid (get-in db [:account/account :keycard-instance-uid])]
(if (or (nil? account-instance-uid)
(and keycard-instance-uid
(= keycard-instance-uid account-instance-uid)))
{:hardwallet/delete nil}
(unauthorized-operation cofx))))
(fx/defn navigate-to-reset-card-screen
[cofx]
(navigation/navigate-to-cofx cofx :reset-card nil))
(fx/defn reset-card-next-button-pressed
[{:keys [db]}]
{:db (assoc-in db [:hardwallet :reset-card :disabled?] true)
:dispatch [:hardwallet/proceed-to-reset-card]})
(fx/defn proceed-to-reset-card
[{:keys [db] :as cofx}]
(let [card-connected? (get-in db [:hardwallet :card-connected?])
puk-retry-counter (get-in db [:hardwallet :application-info :puk-retry-counter])
pin-retry-counter (get-in db [:hardwallet :application-info :pin-retry-counter])
pairing (get-pairing db)
enter-step (if (zero? pin-retry-counter) :puk :current)]
(if (or (zero? puk-retry-counter)
(empty? pairing))
(delete-card cofx)
(fx/merge cofx
{:db (-> db
(assoc-in [:hardwallet :on-card-connected] :hardwallet/navigate-to-enter-pin-screen)
(assoc-in [:hardwallet :pin] {:enter-step enter-step
:current []
:puk []
:status nil
:error-label nil
:on-verified :hardwallet/unpair-and-delete}))}
(if card-connected?
(navigate-to-enter-pin-screen)
(navigation/navigate-to-cofx :hardwallet-connect nil))))))
(fx/defn error-button-pressed [{:keys [db] :as cofx}]
(let [return-to-step (get-in db [:hardwallet :return-to-step] :begin)]
(fx/merge cofx
@ -82,35 +303,203 @@
(defn- proceed-to-pin-confirmation [fx]
(assoc-in fx [:db :hardwallet :pin :enter-step] :confirmation))
(defn- pin-match [fx]
(assoc-in fx [:db :hardwallet :pin :status] :validating))
(fx/defn pin-match
[{:keys [db] :as fx}]
(let [pairing (get-pairing db)
new-pin (vector->string (get-in db [:hardwallet :pin :original]))
current-pin (vector->string (get-in db [:hardwallet :pin :current]))]
(fx/merge fx
{:db (assoc-in db [:hardwallet :pin :status] :verifying)
:hardwallet/change-pin {:new-pin new-pin
:current-pin current-pin
:pairing pairing}})))
(defn- pin-mismatch [fx]
(assoc-in fx [:db :hardwallet :pin] {:status :error
:error :t/pin-mismatch
:original []
:confirmation []
:enter-step :original}))
(fx/defn dispatch-on-verified-event
[{:keys [db]} event]
{:dispatch [event]
:db (assoc-in db [:hardwallet :pin :on-verified] nil)})
(fx/defn on-unblock-pin-success
[{:keys [db] :as cofx}]
(let [pairing (get-pairing db)]
(fx/merge cofx
{:hardwallet/get-application-info pairing
:db (-> db
(update-in [:hardwallet :pin] merge {:status nil
:enter-step :original
:current (vec (string/split default-pin #""))
:puk []
:error-label nil}))}
(navigation/navigate-to-cofx :enter-pin nil))))
(defn on-unblock-pin-error
[{:keys [db]} error]
(let [pairing (get-pairing db)]
(log/debug "[hardwallet] unblock pin error" error)
{:hardwallet/get-application-info pairing
:db (update-in db [:hardwallet :pin] merge {:status :error
:error-label :t/puk-mismatch
:enter-step :puk
:puk []})}))
(fx/defn get-application-info [cofx pairing]
{:hardwallet/get-application-info pairing})
(fx/defn on-verify-pin-success
[{:keys [db] :as cofx}]
(let [on-verified (get-in db [:hardwallet :pin :on-verified])
pairing (get-pairing db)]
(fx/merge cofx
{:db (-> db
(update-in [:hardwallet :pin] merge {:status nil
:error-label nil}))}
(when (not= on-verified :hardwallet/unpair)
(get-application-info pairing))
(when on-verified
(dispatch-on-verified-event on-verified)))))
(defn on-verify-pin-error
[{:keys [db]} error]
(let [pairing (get-pairing db)]
(log/debug "[hardwallet] verify pin error" error)
{:hardwallet/get-application-info pairing
:db (update-in db [:hardwallet :pin] merge {:status :error
:error-label :t/pin-mismatch
:enter-step :current
:puk []
:current []
:original []
:confirmation []})}))
(fx/defn on-change-pin-success
[{:keys [db] :as cofx}]
(let [pin (vector->string (get-in db [:hardwallet :pin :original]))]
(fx/merge cofx
{:db (-> db
(assoc-in [:hardwallet :on-card-connected] nil)
(assoc-in [:hardwallet :pin] {:status nil
:error-label nil}))
:utils/show-popup {:title ""
:content (i18n/label :t/pin-changed {:pin pin})}}
(navigation/navigate-to-cofx :keycard-settings nil))))
(fx/defn on-change-pin-error
[{:keys [db]} error]
(log/debug "[hardwallet] change pin error" error)
{:db (update-in db [:hardwallet :pin] merge {:status :error
:error-label :t/pin-mismatch
:enter-step :original
:puk []
:confirmation []
:original []})})
(fx/defn on-unpair-success
[{:keys [db] :as cofx}]
(fx/merge cofx
{:db (-> db
(assoc-in [:hardwallet :secrets] nil)
(assoc-in [:hardwallet :on-card-connected] nil)
(assoc-in [:hardwallet :pin] {:status nil
:error-label nil
:on-verified nil}))
:utils/show-popup {:title ""
:content (i18n/label :t/card-unpaired)}}
(remove-pairing-from-account)
(navigation/navigate-to-cofx :keycard-settings nil)))
(fx/defn on-unpair-error
[{:keys [db] :as cofx} error]
(log/debug "[hardwallet] unpair error" error)
(fx/merge cofx
{:db (-> db
(assoc-in [:hardwallet :on-card-connected] nil)
(assoc-in [:hardwallet :pin] {:status nil
:error-label nil
:on-verified nil}))
:hardwallet/get-application-info nil
:utils/show-popup {:title ""
:content (i18n/label :t/something-went-wrong)}}
(navigation/navigate-to-cofx :keycard-settings nil)))
(defn- verify-pin
[{:keys [db] :as fx}]
(let [pin (vector->string (get-in fx [:db :hardwallet :pin :current]))
pairing (get-pairing db)]
{:db (assoc-in db [:hardwallet :pin :status] :verifying)
:hardwallet/verify-pin {:pin pin
:pairing pairing}}))
(defn- unblock-pin
[{:keys [db] :as fx}]
(let [puk (vector->string (get-in fx [:db :hardwallet :pin :puk]))
pairing (get-pairing db)]
{:db (assoc-in db [:hardwallet :pin :status] :verifying)
:hardwallet/unblock-pin {:puk puk
:new-pin default-pin
:pairing pairing}}))
(def pin-code-length 6)
(def puk-code-length 12)
(fx/defn handle-pin-input
[{:keys [db]} enter-step]
(let [numbers-entered (count (get-in db [:hardwallet :pin enter-step]))]
(when (or (= numbers-entered pin-code-length)
(= numbers-entered puk-code-length))
{:dispatch [:hardwallet/process-pin-input]})))
(fx/defn update-pin
[{:keys [db] :as cofx} number enter-step]
(fx/merge cofx
{:db (-> db
(update-in [:hardwallet :pin enter-step] (fnil conj []) number)
(assoc-in [:hardwallet :pin :status] nil))}
(handle-pin-input enter-step)))
(defn- pin-enter-error [fx error-label]
(update-in fx [:db :hardwallet :pin] merge {:status :error
:error-label error-label
:enter-step :original
:original []
:confirmation []}))
; PIN enter steps:
; current - current PIN to perform actions which require PIN auth
; original - new PIN when user changes it or creates new one
; confirmation - confirmation for new PIN
(fx/defn process-pin-input
[{:keys [db]} number enter-step]
(let [db' (update-in db [:hardwallet :pin enter-step] conj number)
numbers-entered (count (get-in db' [:hardwallet :pin enter-step]))]
(cond-> {:db (assoc-in db' [:hardwallet :pin :status] nil)}
[{:keys [db]}]
(let [enter-step (get-in db [:hardwallet :pin :enter-step])
pin (get-in db [:hardwallet :pin enter-step])
numbers-entered (count pin)]
(cond-> {:db (assoc-in db [:hardwallet :pin :status] nil)}
(and (= enter-step :original)
(= 6 numbers-entered))
(= pin-code-length numbers-entered))
(proceed-to-pin-confirmation)
(and (= enter-step :original)
(= pin-code-length numbers-entered)
(= default-pin (vector->string pin)))
(pin-enter-error :t/cannot-use-default-pin)
(and (= enter-step :current)
(= pin-code-length numbers-entered))
(verify-pin)
(and (= enter-step :puk)
(= puk-code-length numbers-entered))
(unblock-pin)
(and (= enter-step :confirmation)
(= (get-in db' [:hardwallet :pin :original])
(get-in db' [:hardwallet :pin :confirmation])))
(= (get-in db [:hardwallet :pin :original])
(get-in db [:hardwallet :pin :confirmation])))
(pin-match)
(and (= enter-step :confirmation)
(= 6 numbers-entered)
(not= (get-in db' [:hardwallet :pin :original])
(get-in db' [:hardwallet :pin :confirmation])))
(pin-mismatch))))
(= pin-code-length numbers-entered)
(not= (get-in db [:hardwallet :pin :original])
(get-in db [:hardwallet :pin :confirmation])))
(pin-enter-error :t/pin-mismatch))))
(fx/defn load-loading-keys-screen
[{:keys [db]}]
@ -127,27 +516,38 @@
(let [{:keys [pairing]} (get-in cofx [:db :hardwallet :secrets])]
{:hardwallet/generate-mnemonic {:pairing pairing}}))
(fx/defn dispatch-on-card-connected-event
[{:keys [db]} event]
{;:db (assoc-in db [:hardwallet :on-card-connected] nil)
:dispatch [event]})
(fx/defn on-card-connected
[{:keys [db] :as cofx} data]
(log/debug "[hardwallet] card connected " data)
(let [return-to-step (get-in db [:hardwallet :return-to-step])
setup-running? (get-in db [:hardwallet :setup-step])]
setup-running? (get-in db [:hardwallet :setup-step])
on-card-connected (get-in db [:hardwallet :on-card-connected])
pairing (get-pairing db)]
(fx/merge cofx
{:db (cond-> db
return-to-step (assoc-in [:hardwallet :setup-step] return-to-step)
true (assoc-in [:hardwallet :card-connected?] true)
true (assoc-in [:hardwallet :return-to-step] nil))
:hardwallet/get-application-info nil}
:hardwallet/get-application-info pairing}
(when on-card-connected
(dispatch-on-card-connected-event on-card-connected))
(when setup-running?
(navigation/navigate-to-cofx :hardwallet-setup nil)))))
(fx/defn on-card-disconnected
[{:keys [db] :as cofx} _]
(log/debug "[hardwallet] card disconnected ")
(let [setup-running? (get-in db [:hardwallet :setup-step])]
(let [setup-running? (get-in db [:hardwallet :setup-step])
on-card-connected (get-in db [:hardwallet :on-card-connected])]
(fx/merge cofx
{:db (assoc-in db [:hardwallet :card-connected?] false)}
(when setup-running?
(when (or setup-running?
on-card-connected)
(navigation/navigate-to-cofx :hardwallet-connect nil)))))
(fx/defn load-preparing-screen
@ -185,9 +585,12 @@
(fx/defn on-pairing-success
[{:keys [db]} pairing]
{:db (-> db
(assoc-in [:hardwallet :setup-step] :card-ready)
(assoc-in [:hardwallet :secrets :pairing] pairing))})
;TODO remove persistence to async storage when keycard login will be ready
{:hardwallet/persist-pairing pairing
:db (-> db
(assoc-in [:hardwallet :setup-step] :card-ready)
(assoc-in [:hardwallet :secrets :pairing] pairing)
(assoc-in [:hardwallet :secrets :paired-on] (utils.datetime/timestamp)))})
(fx/defn on-pairing-error
[{:keys [db] :as cofx} {:keys [error code]}]
@ -265,7 +668,9 @@
(let [{{:keys [whisper-public-key
wallet-address
encryption-public-key
keycard-instance-uid]} :hardwallet} db]
keycard-instance-uid
secrets]} :hardwallet} db
{:keys [pairing paired-on]} secrets]
(fx/merge (-> cofx
(accounts.create/get-signing-phrase)
(accounts.create/get-status))
@ -273,7 +678,9 @@
(accounts.create/on-account-created {:pubkey whisper-public-key
:address wallet-address
:mnemonic ""
:keycard-instance-uid keycard-instance-uid}
:keycard-instance-uid keycard-instance-uid
:keycard-pairing pairing
:keycard-paired-on paired-on}
encryption-public-key
{:seed-backed-up? true
:login? false})
@ -297,7 +704,8 @@
(assoc-in [:hardwallet :encryption-public-key] encryption-public-key)
(assoc-in [:hardwallet :keycard-instance-uid] keycard-instance-uid)
(assoc :node/on-ready :create-keycard-account)
(assoc :accounts/new-installation-id (random-guid-generator)))}
(assoc :accounts/new-installation-id (random-guid-generator))
(update-in [:hardwallet :secrets] dissoc :mnemonic))}
(node/initialize nil))))
(fx/defn on-generate-and-load-key-error

View File

@ -1,7 +1,8 @@
(ns status-im.hardwallet.fx
(:require [re-frame.core :as re-frame]
[status-im.hardwallet.card :as card]
[status-im.react-native.js-dependencies :as js-dependencies]))
[status-im.react-native.js-dependencies :as js-dependencies]
[status-im.utils.datetime :as utils.datetime]))
(re-frame/reg-fx
:hardwallet/get-application-info
@ -19,10 +20,6 @@
:hardwallet/open-nfc-settings
card/open-nfc-settings)
(re-frame/reg-fx
:hardwallet/start-module
card/start)
(re-frame/reg-fx
:hardwallet/install-applet-and-init-card
card/install-applet-and-init-card)
@ -46,3 +43,58 @@
(re-frame/reg-fx
:hardwallet/generate-and-load-key
card/generate-and-load-key)
(re-frame/reg-fx
:hardwallet/unblock-pin
card/unblock-pin)
(re-frame/reg-fx
:hardwallet/verify-pin
card/verify-pin)
(re-frame/reg-fx
:hardwallet/change-pin
card/change-pin)
(re-frame/reg-fx
:hardwallet/unpair
card/unpair)
(re-frame/reg-fx
:hardwallet/delete
card/delete)
(re-frame/reg-fx
:hardwallet/unpair-and-delete
card/unpair-and-delete)
;TODO remove when keycard login will be ready
(re-frame/reg-fx
:hardwallet/persist-pairing
(fn [pairing]
(.. js-dependencies/react-native
-AsyncStorage
(setItem "status-keycard-pairing"
(js/JSON.stringify
#js {"pairing" pairing
"paired-on" (utils.datetime/timestamp)})))))
;TODO remove when keycard login will be ready
(re-frame/reg-fx
:hardwallet/retrieve-pairing
(fn []
(.. js-dependencies/react-native
-AsyncStorage
(getItem "status-keycard-pairing")
(then #(re-frame/dispatch [:hardwallet.callback/on-retrieve-pairing-success
(-> %
(js/JSON.parse)
(js->clj :keywordize-keys true))])))))
;TODO remove when keycard login will be ready
(re-frame/reg-fx
:hardwallet/remove-pairing
(fn []
(.. js-dependencies/react-native
-AsyncStorage
(removeItem "status-keycard-pairing"))))

View File

@ -56,7 +56,7 @@
(then
#(re-frame/dispatch [:init.callback/keychain-reset]))))
(defn reset-data! []
(defn reset-data! []
(.. (realm/delete-realms)
(then reset-keychain!)
(catch reset-keychain!)))
@ -80,39 +80,40 @@
:notifications/init nil
:network/listen-to-network-status nil
:network/listen-to-connection-status nil
:hardwallet/check-nfc-support nil
:hardwallet/check-nfc-enabled nil
:hardwallet/start-module nil
:hardwallet/register-card-events nil}
(initialize-keychain)))
(fx/defn initialize-app-db
"Initialize db to initial state"
[{{:keys [status-module-initialized? view-id hardwallet
initial-props desktop/desktop
network-status network peers-count peers-summary device-UUID
push-notifications/stored]
[{{:keys [status-module-initialized? view-id hardwallet
initial-props desktop/desktop
network-status network peers-count peers-summary device-UUID
push-notifications/stored]
:node/keys [status]
:or {network (get app-db :network)}} :db}]
{:db (assoc app-db
:contacts/contacts {}
:initial-props initial-props
:desktop/desktop (merge desktop (:desktop/desktop app-db))
:network-status network-status
:peers-count (or peers-count 0)
:peers-summary (or peers-summary [])
:status-module-initialized? (or platform/ios? js/goog.DEBUG status-module-initialized?)
:node/status status
:network network
:hardwallet hardwallet
:device-UUID device-UUID
:view-id view-id
:push-notifications/stored stored)})
;TODO remove retrieve-pairing when keycard login will be ready
{:hardwallet/retrieve-pairing nil
:db (assoc app-db
:contacts/contacts {}
:initial-props initial-props
:desktop/desktop (merge desktop (:desktop/desktop app-db))
:network-status network-status
:peers-count (or peers-count 0)
:peers-summary (or peers-summary [])
:status-module-initialized? (or platform/ios? js/goog.DEBUG status-module-initialized?)
:node/status status
:network network
:hardwallet hardwallet
:device-UUID device-UUID
:view-id view-id
:push-notifications/stored stored)})
(fx/defn initialize-app
[cofx encryption-key]
(fx/merge cofx
{:init/init-store encryption-key}
{:init/init-store encryption-key
:hardwallet/check-nfc-support nil
:hardwallet/check-nfc-enabled nil}
(initialize-app-db)))
(fx/defn set-device-uuid
@ -166,12 +167,12 @@
(fx/defn initialize-account-db [{:keys [db web3]} address]
(let [{:universal-links/keys [url]
:keys [accounts/accounts accounts/create networks/networks network
network-status peers-count peers-summary view-id navigation-stack
desktop/desktop hardwallet
status-module-initialized? device-UUID semaphores accounts/login]
:node/keys [status on-ready]
:or {network (get app-db :network)}} db
:keys [accounts/accounts accounts/create networks/networks network
network-status peers-count peers-summary view-id navigation-stack
desktop/desktop hardwallet
status-module-initialized? device-UUID semaphores accounts/login]
:node/keys [status on-ready]
:or {network (get app-db :network)}} db
current-account (get accounts address)
account-network-id (get current-account :network network)
account-network (get-in current-account [:networks account-network-id])]

View File

@ -69,5 +69,6 @@
:secret-keys (js/require "./resources/images/ui/secret-keys.png")
:keycard-lock (js/require "./resources/images/ui/keycard-lock.png")
:hold-card-animation (js/require "./resources/images/ui/hold-card-animation.png")
:warning-sign (js/require "./resources/images/ui/warning-sign.png")
:phone-nfc-on (js/require "./resources/images/ui/phone-nfc-on.png")
:phone-nfc-off (js/require "./resources/images/ui/phone-nfc-off.png")})

View File

@ -37,6 +37,7 @@
;; RED
(def red "#ff2d55") ;; Used to highlight errors or "dangerous" actions
(def red-transparent-10 (alpha red 0.1))
;; GREEN
(def green "#44d058") ;; icon for successful inboud transaction

View File

@ -91,6 +91,7 @@
:icons/refresh (js/require "./resources/icons/refresh.svg")
:icons/newchat (js/require "./resources/icons/newchat.svg")
:icons/logo (js/require "./resources/icons/logo.svg")
:icons/logout (js/require "./resources/icons/logout.svg")
:icons/camera (js/require "./resources/icons/camera.svg")
:icons/check (js/require "./resources/icons/check.svg")
:icons/dots (js/require "./resources/icons/dots.svg")
@ -169,6 +170,7 @@
:icons/refresh (components.svg/slurp-svg "./resources/icons/refresh.svg")
:icons/newchat (components.svg/slurp-svg "./resources/icons/newchat.svg")
:icons/logo (components.svg/slurp-svg "./resources/icons/logo.svg")
:icons/logout (components.svg/slurp-svg "./resources/icons/logout.svg")
:icons/camera (components.svg/slurp-svg "./resources/icons/camera.svg")
:icons/check (components.svg/slurp-svg "./resources/icons/check.svg")
:icons/tiny-check (components.svg/slurp-svg "./resources/icons/tiny_check.svg")

View File

@ -67,6 +67,8 @@
:nfc-enabled? false
:pin {:original []
:confirmation []
:current []
:puk []
:enter-step :original}}
:chats/loading? true})

View File

@ -171,3 +171,13 @@
:update-window-dimensions
(fn [{:keys [db]} [_ dimensions]]
{:db (assoc db :dimensions/window (dimensions/window dimensions))}))
(handlers/register-handler-fx
:screens/on-will-focus
(fn [{:keys [db] :as cofx} [_ view-id]]
(fx/merge cofx
{:db (assoc db :view-id view-id)}
#(case view-id
:keycard-settings (hardwallet/settings-screen-did-load %)
:reset-card (hardwallet/reset-card-screen-did-load %)
nil))))

View File

@ -6,6 +6,7 @@
{:flex 1
:flex-direction :column
:justify-content :space-between
:padding-bottom 10
:ios {:margin-top 30}})
(defstyle error-container

View File

@ -2,7 +2,7 @@
(:require [re-frame.core :as re-frame]))
(re-frame/reg-sub
:hardwallet/pin
:hardwallet/original-pin
(fn [db]
(get-in db [:hardwallet :pin :original])))
@ -16,12 +16,29 @@
(fn [db]
(get-in db [:hardwallet :pin :enter-step] :original)))
(re-frame/reg-sub
:hardwallet/pin-operation
(fn [db]
(get-in db [:hardwallet :pin :operation])))
(re-frame/reg-sub
:hardwallet/pin-data
(fn [db]
(get-in db [:hardwallet :pin])))
(re-frame/reg-sub
:hardwallet/pin
:<- [:hardwallet/pin-data]
:<- [:hardwallet/pin-enter-step]
(fn [[pin-data step]]
(get pin-data step)))
(re-frame/reg-sub
:hardwallet/pin-status
(fn [db]
(get-in db [:hardwallet :pin :status])))
(re-frame/reg-sub
:hardwallet/pin-error
:hardwallet/pin-error-label
(fn [db]
(get-in db [:hardwallet :pin :error])))
(get-in db [:hardwallet :pin :error-label])))

View File

@ -40,8 +40,8 @@
(defn pin-indicator [pressed?]
[react/view (styles/pin-indicator pressed?)])
(defn pin-indicators [pin]
[react/view styles/pin-indicator-container
(defn pin-indicators [pin style]
[react/view (merge styles/pin-indicator-container style)
(map-indexed
(fn [i group]
^{:key i}
@ -56,42 +56,64 @@
(repeat (- 6 (count pin))
nil)))))])
(defn pin-view [{:keys [pin title step status error]}]
(let [enabled? (not= status :validating)]
[react/view styles/pin-container
[react/view styles/center-container
;[components/wizard-step 4]
[react/text {:style styles/center-title-text
:font :bold}
(i18n/label title)]
[react/text {:style styles/create-pin-text
:number-of-lines 2}
(i18n/label :t/create-pin-description)]
(case status
:validating [react/view styles/waiting-indicator-container
(defn puk-indicators [puk]
[react/view
(map-indexed
(fn [i puk-group]
^{:key i}
[pin-indicators puk-group {:margin-top 15}])
(partition 6
(concat puk
(repeat (- 12 (count puk))
nil))))])
(defn pin-view [{:keys [pin title-label description-label step status error-label
retry-counter]}]
(let [enabled? (not= status :verifying)]
[react/scroll-view
[react/view styles/pin-container
[react/view styles/center-container
[react/text {:style styles/center-title-text
:font :bold}
(i18n/label title-label)]
[react/text {:style styles/create-pin-text
:number-of-lines 2}
(i18n/label description-label)]
(when retry-counter
[react/text {:style {:font-weight :bold
:padding-top 10
:font-size 15
:color colors/red}}
(i18n/label :t/pin-retries-left {:number retry-counter})])
(case status
:verifying [react/view styles/waiting-indicator-container
[react/activity-indicator {:animating true
:size :small}]]
:error [react/view styles/error-container
[react/text {:style styles/error-text
:font :medium}
(i18n/label error)]]
[pin-indicators pin])
[numpad step enabled?]]]))
:error [react/view styles/error-container
[react/text {:style styles/error-text
:font :medium}
(i18n/label error-label)]]
(if (= step :puk)
[puk-indicators pin]
[pin-indicators pin]))
[numpad step enabled?]]]]))
(defview main []
(letsubs [original [:hardwallet/pin]
(letsubs [original [:hardwallet/original-pin]
confirmation [:hardwallet/pin-confirmation]
enter-step [:hardwallet/pin-enter-step]
status [:hardwallet/pin-status]
error [:hardwallet/pin-error]]
error-label [:hardwallet/pin-error-label]]
(case enter-step
:original [pin-view {:pin original
:title :t/create-pin
:step :original
:status status
:error error}]
:confirmation [pin-view {:pin confirmation
:title :t/repeat-pin
:step :confirmation
:status status
:error error}])))
:original [pin-view {:pin original
:title-label :t/create-pin
:description-label :t/create-pin-description
:step :original
:status status
:error-label error-label}]
:confirmation [pin-view {:pin confirmation
:title-label :t/repeat-pin
:description-label :t/create-pin-description
:step :confirmation
:status status
:error-label error-label}])))

View File

@ -0,0 +1,32 @@
(ns status-im.ui.screens.hardwallet.settings.subs
(:require [re-frame.core :as re-frame]
[status-im.hardwallet.core :as core]
[status-im.utils.datetime :as utils.datetime]))
(re-frame/reg-sub
:keycard-paired-on
(fn [db]
(some-> (or
(get-in db [:hardwallet :secrets :paired-on])
(get-in db [:account/account :keycard-paired-on]))
(utils.datetime/timestamp->year-month-day-date))))
(re-frame/reg-sub
:keycard-pairing
(fn [db]
(core/get-pairing db)))
(re-frame/reg-sub
:hardwallet/pin-retry-counter
(fn [db]
(get-in db [:hardwallet :application-info :pin-retry-counter])))
(re-frame/reg-sub
:hardwallet/puk-retry-counter
(fn [db]
(get-in db [:hardwallet :application-info :puk-retry-counter])))
(re-frame/reg-sub
:keycard-reset-card-disabled?
(fn [db]
(get-in db [:hardwallet :reset-card :disabled?] false)))

View File

@ -0,0 +1,178 @@
(ns status-im.ui.screens.hardwallet.settings.views
(:require-macros [status-im.utils.views :refer [defview letsubs]])
(:require [re-frame.core :as re-frame]
[status-im.i18n :as i18n]
[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.react-native.resources :as resources]
[status-im.ui.components.icons.vector-icons :as vector-icons]
[status-im.ui.components.colors :as colors]
[status-im.ui.screens.hardwallet.components :as components]
[status-im.ui.screens.hardwallet.pin.views :as pin.views]
[status-im.ui.components.common.common :as components.common]
[reagent.core :as reagent]))
(def pin-retries 3)
(def puk-retries 5)
(defview enter-pin []
(letsubs [pin [:hardwallet/pin]
step [:hardwallet/pin-enter-step]
status [:hardwallet/pin-status]
pin-retry-counter [:hardwallet/pin-retry-counter]
puk-retry-counter [:hardwallet/puk-retry-counter]
error-label [:hardwallet/pin-error-label]]
[react/keyboard-avoiding-view {:flex 1}
[react/view {:flex 1
:background-color colors/white}
[react/view {:flex-direction :column
:flex 1
:align-items :center
:justify-content :space-between}
[components/maintain-card nil]
(if (zero? pin-retry-counter)
[pin.views/pin-view {:pin pin
:retry-counter (when (< puk-retry-counter puk-retries) puk-retry-counter)
:title-label :t/enter-puk-code
:description-label :t/enter-puk-code-description
:step step
:status status
:error-label error-label}]
[pin.views/pin-view {:pin pin
:retry-counter (when (< pin-retry-counter pin-retries) pin-retry-counter)
:title-label (case step
:current :t/current-pin
:original :t/create-pin
:confirmation :t/repeat-pin
:t/current-pin)
:description-label (case step
:current :t/current-pin-description
:t/new-pin-description)
:step step
:status status
:error-label error-label}])]]]))
(defn- action-row [{:keys [icon label on-press color-theme]}]
[react/touchable-highlight
{:on-press on-press}
[react/view {:flex-direction :row
:margin-top 15}
[react/view {:background-color (case color-theme
:red colors/red-transparent-10
colors/blue-light)
:width 40
:height 40
:border-radius 50
:align-items :center
:justify-content :center}
[vector-icons/icon icon {:color (case color-theme
:red colors/red
colors/blue)}]]
[react/view {:align-items :center
:justify-content :center
:margin-left 16}
[react/text {:style {:font-size 17
:color (case color-theme
:red colors/red
colors/blue)}}
(i18n/label label)]]]])
(defn- activity-indicator [loading?]
(when loading?
[react/view {:margin-top 35}
[react/activity-indicator {:animating true
:size :large}]]))
(defn- reset-card-next-button [disabled?]
[react/view {:margin-right 18}
[components.common/bottom-button
{:on-press #(re-frame/dispatch [:keycard-settings.ui/reset-card-next-button-pressed])
:disabled? disabled?
:uppercase? false
:forward? true}]])
(defview reset-card []
(letsubs [disabled? [:keycard-reset-card-disabled?]]
[react/view {:flex 1}
[status-bar/status-bar]
[toolbar/simple-toolbar
(i18n/label :t/reset-card)]
[react/view {:flex 1
:background-color :white}
[react/view {:margin-top 71
:flex 1
:align-items :center}
[react/image {:source (:warning-sign resources/ui)
:style {:width 160
:height 160}}]]
[react/view {:flex 1
:padding-horizontal 30}
[react/text {:style {:font-weight :bold
:color colors/black
:font-size 22
:text-align :center}}
(i18n/label :t/reset-card-description)]
[activity-indicator disabled?]]
[react/view {:flex-direction :row
:justify-content :space-between
:align-items :center
:width "100%"
:height 52
:border-top-width 1
:border-color colors/gray-light}
[react/view {:flex 1}]
[reset-card-next-button disabled?]]]]))
(defn- card-blocked []
[react/view
[react/text {:style {:font-size 20
:text-align :center
:padding-horizontal 40
:color colors/black}}
(i18n/label :t/keycard-blocked)]])
(defview keycard-settings []
(letsubs [paired-on [:keycard-paired-on]
puk-retry-counter [:hardwallet/puk-retry-counter]
pairing [:keycard-pairing]]
[react/view {:flex 1}
[status-bar/status-bar]
[toolbar/simple-toolbar
(i18n/label :t/status-keycard)]
[react/view {:flex 1
:background-color :white}
[react/view {:margin-top 47
:flex 1
:align-items :center}
[react/image {:source (:hardwallet-card resources/ui)
:style {:width 255
:height 160}}]
(when paired-on
[react/view {:margin-top 27}
[react/text
(i18n/label :t/linked-on {:date paired-on})]])]
[react/view {:margin-left 16
:flex 1
:width "90%"
:flex-direction :column}
(if (zero? puk-retry-counter)
[card-blocked]
[react/view
[action-row {:icon :icons/info
:label :t/help-capitalized
:on-press #(.openURL react/linking "https://hardwallet.status.im")}]
(when pairing
[react/view
[action-row {:icon :icons/add
:label :t/change-pin
:on-press #(re-frame/dispatch [:keycard-settings.ui/change-pin-pressed])}]
[action-row {:icon :icons/close
:label :t/unpair-card
:on-press #(re-frame/dispatch [:keycard-settings.ui/unpair-card-pressed])}]])])]
[react/view {:margin-bottom 20
:margin-left 16}
[action-row {:icon :icons/logout
:color-theme :red
:label :t/reset-card
:on-press #(re-frame/dispatch [:keycard-settings.ui/reset-card-pressed])}]]]]))

View File

@ -27,7 +27,8 @@
(def maintain-card-text
{:font-size 12
:padding-horizontal 30
:flex 1
:padding-horizontal 20
:color colors/blue})
(def setup-steps-container

View File

@ -111,6 +111,10 @@
:action-fn #(re-frame/dispatch [:navigate-to :currency-settings])
:accessibility-label :currency-button}]
[profile.components/settings-item-separator]
(when config/hardwallet-enabled?
[profile.components/settings-item {:label-kw :t/status-keycard
:accessibility-label :keycard-button
:action-fn #(re-frame/dispatch [:profile.ui/keycard-settings-button-pressed])}])
[profile.components/settings-item {:label-kw :t/notifications
:accessibility-label :notifications-button
:action-fn #(.openURL react/linking "app-settings://notification/status-im")}]

View File

@ -15,6 +15,7 @@
status-im.ui.screens.wallet.send.subs
status-im.ui.screens.wallet.settings.subs
status-im.ui.screens.wallet.transactions.subs
status-im.ui.screens.hardwallet.settings.subs
status-im.ui.screens.network-settings.subs
status-im.ui.screens.log-level-settings.subs
status-im.ui.screens.fleet-settings.subs

View File

@ -52,6 +52,7 @@
[status-im.ui.screens.pairing.views :refer [installations]]
[status-im.ui.screens.bootnodes-settings.edit-bootnode.views :refer [edit-bootnode]]
[status-im.ui.screens.currency-settings.views :refer [currency-settings]]
[status-im.ui.screens.hardwallet.settings.views :refer [keycard-settings enter-pin reset-card]]
[status-im.ui.screens.help-center.views :refer [help-center]]
[status-im.ui.screens.browser.views :refer [browser]]
[status-im.ui.screens.add-new.open-dapp.views :refer [open-dapp dapp-description]]
@ -81,7 +82,7 @@
{:on-will-focus
(fn []
(log/debug :on-will-focus view-id)
(re-frame/dispatch [:set :view-id view-id]))}]])))
(re-frame/dispatch [:screens/on-will-focus view-id]))}]])))
(defn wrap-modal [modal-view component]
(fn []
@ -334,7 +335,10 @@
(assoc :hardwallet-authentication-method hardwallet-authentication-method
:hardwallet-connect hardwallet-connect
:hardwallet-setup hardwallet-setup
:hardwallet-success hardwallet-success)))
:hardwallet-success hardwallet-success
:keycard-settings keycard-settings
:reset-card reset-card
:enter-pin enter-pin)))
{:headerMode "none"
:initialRouteName "my-profile"})}
:profile-qr-viewer

View File

@ -145,6 +145,9 @@
(defn timestamp []
(inst-ms (js/Date.)))
(defn timestamp->year-month-day-date [ms]
(unparse (:year-month-day formatters) (to-date ms)))
(re-frame/reg-cofx
:now
(fn [coeffects _]

View File

@ -8,42 +8,38 @@
:confirmation []
:status nil
:enter-step :original}}}}
(hardwallet/process-pin-input {:db {:hardwallet {:pin {:original []
(hardwallet/process-pin-input {:db {:hardwallet {:pin {:original [1]
:confirmation []
:enter-step :original}}}}
1
:original))))
:enter-step :original}}}}))))
(testing "first 6 numbers entered"
(is (= {:db {:hardwallet {:pin {:original [1 2 3 4 5 6]
:confirmation []
:status nil
:enter-step :confirmation}}}}
(hardwallet/process-pin-input {:db {:hardwallet {:pin {:original [1 2 3 4 5]
:confirmation []
:enter-step :original}}}}
6
:original))))
(testing "confirmation entered"
(is (= {:db {:hardwallet {:pin {:original [1 2 3 4 5 6]
:confirmation [1 2 3 4 5 6]
:enter-step :confirmation
:status :validating}}}}
(hardwallet/process-pin-input {:db {:hardwallet {:pin {:original [1 2 3 4 5 6]
:confirmation [1 2 3 4 5]
:enter-step :confirmation}}}}
6
:confirmation))))
:confirmation []
:enter-step :original}}}}))))
(testing "confirmation entered"
(is (= {:db {:hardwallet {:pin {:original [1 2 3 4 5 6]
:confirmation [1 2 3 4 5 6]
:enter-step :confirmation
:status :verifying}}}
:hardwallet/change-pin {:new-pin "123456"
:current-pin ""
:pairing nil}}
(hardwallet/process-pin-input {:db {:hardwallet {:pin {:original [1 2 3 4 5 6]
:confirmation [1 2 3 4 5 6]
:enter-step :confirmation}}}}))))
(testing "confirmation doesn't match"
(is (= {:db {:hardwallet {:pin {:original []
:confirmation []
:enter-step :original
:error :t/pin-mismatch
:error-label :t/pin-mismatch
:status :error}}}}
(hardwallet/process-pin-input {:db {:hardwallet {:pin {:original [1 2 3 4 5 6]
:confirmation [1 2 3 4 5]
:enter-step :confirmation}}}}
7
:confirmation)))))
:confirmation [1 2 3 4 5 7]
:enter-step :confirmation}}}})))))
(deftest on-generate-and-load-key-success
(is (= (select-keys

View File

@ -775,10 +775,12 @@
"send-command-payment": "Send a payment",
"request-command-payment": "Request a payment",
"choose-authentication-method": "Choose an authentication method to protect your account",
"status-keycard": "Status Keycard",
"keycard": "Keycard",
"status-hardwallet-capitalized": "Status Hardwallet",
"status-hardwallet": "Status hardwallet",
"secure-your-assets": "secure your assets",
"linked-on": "Linked on {{date}}",
"link-card": "Link the card and use it to confirm your identity on the platform.",
"hold-card": "Hold card to the back\n of your phone",
"turn-nfc-on": "Turn NFC on to continue",
@ -821,17 +823,38 @@
"taking-long-hold-phone-connected": "This will take a few seconds.\n Hold card connected to the phone.",
"pin-code": "PIN code",
"pin-code-description": "Unlocks the card",
"enter-puk-code": "Enter PUK code",
"enter-puk-code-description": "PIN has been blocked.\n Please enter PUK code to unblock PIN.",
"pin-unblocked": "PIN has been unblocked",
"pin-unblocked-description": "Your new PIN {{pin}}",
"current-pin": "Enter PIN",
"current-pin-description": "Enter your PIN to proceed",
"new-pin-description": "Enter new PIN code",
"change-pin": "Change PIN",
"create-pin": "Create a PIN",
"create-pin-description": "You'll need your card + this PIN to log in and to confirm transactions",
"repeat-pin": "Repeate your PIN",
"repeat-pin": "Repeat new PIN",
"puk-mismatch": "PUK code does not match",
"pin-mismatch": "PIN does not match",
"cannot-use-default-pin": "PIN 000000 is not allowed.\nPlease use another number",
"pin-changed": "PIN has been changed to {{pin}}",
"pin-retries-left": "You have {{number}} retries left",
"keycard-blocked": "Keycard has been blocked.\nYou need to reset card to continue using it.",
"reset-card": "Reset card",
"reset-card-description": "This operation will reset card to initial state. It will erase all card data including private keys. Operation is not reversible.",
"unpair-card": "Unpair card",
"card-unpaired": "Card has been unpaired from current device",
"card-reseted": "Card has been reseted",
"unpair-card-confirmation": "This operation will unpair card from current device. Requires PIN authorization. Do you want to proceed?",
"pair-card": "pair card",
"pair-card-question": "Do you want to pair card to this device?",
"enter-pair-code": "Enter pair code",
"enter-pair-code-description": "Needed to link your card to this device",
"no-pairing-slots-available": "No pairing slots are available.\n You could unpair the device\n that already paired with the card",
"card-already-linked": "Card is already linked to another account",
"keycard-unauthorized-operation": "You're unauthorized to perform this operation.",
"help": "help",
"help-capitalized": "Help",
"pairing-card": "Pairing card",
"complete-exclamation": "Complete!",
"complete-hardwallet-setup": "This card is now an essential part your account security. Transactions can't be sent without it.",