[#10452] Keycard: updated PUK flow

This commit is contained in:
Roman Volosovskyi 2020-05-14 17:35:43 +03:00
parent 4021e957ea
commit a1027cdfcc
No known key found for this signature in database
GPG Key ID: 0238A4B5ECEE70DE
28 changed files with 950 additions and 335 deletions

View File

@ -348,7 +348,8 @@
response]))
:on-failure
(fn [response]
(log/info "[keycard response fail] get-keys")
(log/info "[keycard response fail] get-keys"
(error-object->map response))
(re-frame/dispatch [:hardwallet.callback/on-get-keys-error
(error-object->map response)]))})))

View File

@ -4,24 +4,27 @@
[status-im.hardwallet.onboarding :as onboarding]
[status-im.utils.fx :as fx]
[taoensso.timbre :as log]
[status-im.hardwallet.common :as common]))
[status-im.hardwallet.common :as common]
[status-im.hardwallet.login :as hardwallet.login]))
(fx/defn change-pin-pressed
{:events [:keycard-settings.ui/change-pin-pressed]}
[{:keys [db] :as cofx}]
(let [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
(assoc-in db [:hardwallet :pin] {:enter-step enter-step
:current []
:puk []
:original []
:confirmation []
:status nil
:error-label nil
:on-verified :hardwallet/proceed-to-change-pin})}
(common/navigate-to-enter-pin-screen))))
(if (= enter-step :puk)
(hardwallet.login/reset-pin cofx)
(fx/merge cofx
{:db
(assoc-in db [:hardwallet :pin] {:enter-step enter-step
:current []
:puk []
:original []
:confirmation []
:status nil
:error-label nil
:on-verified :hardwallet/proceed-to-change-pin})}
(common/navigate-to-enter-pin-screen)))))
(fx/defn proceed-to-change-pin
{:events [:hardwallet/proceed-to-change-pin]}

View File

@ -204,27 +204,30 @@
(fx/defn clear-pin
[{:keys [db] :as cofx}]
(fx/merge cofx
{:db (update-in db
[:hardwallet :pin]
merge
{:status nil
:login (get-in db [:hardwallet :pin :original])
:export-key []
:sign []
:puk []
:current []
:original []
:confirmation []
:error-label nil})}))
(fx/merge
cofx
{:db (assoc-in db
[:hardwallet :pin]
{:status nil
:login (get-in db [:hardwallet :pin :original])
:export-key []
:sign []
:puk []
:current []
:original []
:confirmation []
:error-label nil
:on-verified (get-in db [:hardwallet :pin :on-verified])
:on-verified-failure (get-in db [:hardwallet :pin :on-verified])})}))
(fx/defn cancel-sheet-confirm
{:events [::cancel-sheet-confirm
:hardwallet/back-button-pressed]}
[cofx]
(fx/merge cofx
(hide-connection-sheet)
(clear-pin)))
[{:keys [db] :as cofx}]
(when-not (get-in db [:hardwallet :card-connected?])
(fx/merge cofx
(hide-connection-sheet)
(clear-pin))))
(fx/defn cancel-sheet
{:events [::cancel-sheet]}
@ -293,6 +296,9 @@
(let [key-uid (get-in db [:multiaccounts/login :key-uid])
pairing (get-in db [:multiaccounts/multiaccounts key-uid :keycard-pairing])
pin (string/join (get-in db [:hardwallet :pin :login]))]
(log/debug "[keycard] get-keys-from-keycard"
"not nil pairing:" (boolean pairing)
", not empty pin:" (boolean (seq pin)))
(when (and pairing
(seq pin))
{:db (assoc-in db [:hardwallet :pin :status] :verifying)
@ -344,15 +350,20 @@
(if tag-was-lost?
{:db (assoc-in db [:hardwallet :pin :status] nil)}
(if (re-matches pin-mismatch-error (:error error))
(fx/merge cofx
{:hardwallet/get-application-info {:pairing (get-pairing db key-uid)}
:db (update-in db [:hardwallet :pin] merge {:status :error
:login []
:import-multiaccount []
:error-label :t/pin-mismatch})}
(hide-connection-sheet)
(when (= flow :import)
(navigation/navigate-to-cofx :keycard-recovery-pin nil)))
(fx/merge
cofx
{:hardwallet/get-application-info
{:pairing (get-pairing db key-uid)}
:db
(update-in db [:hardwallet :pin] merge
{:status :error
:login []
:import-multiaccount []
:error-label :t/pin-mismatch})}
(hide-connection-sheet)
(when (= flow :import)
(navigation/navigate-to-cofx :keycard-recovery-pin nil)))
(show-wrong-keycard-alert true)))))
;; Get application info
@ -360,44 +371,57 @@
(fx/defn get-application-info
{:events [:hardwallet/get-application-info]}
[{:keys [db]} pairing on-card-read]
(let [key-uid (get-in db [:hardwallet :application-info :key-uid])
(let [key-uid (get-in
db [:hardwallet :application-info :key-uid]
(get-in db [:multiaccounts/login :key-uid]))
pairing' (or pairing (some->> key-uid (get-pairing db)))]
(log/debug "[hardwallet] get-application-info"
"pairing" pairing')
{:hardwallet/get-application-info {:pairing pairing'
:on-success on-card-read}}))
(fx/defn frozen-keycard-popup
[{:keys [db] :as cofx}]
(if (:multiaccounts/login db)
(fx/merge
cofx
{:db (assoc-in db [:hardwallet :pin :status] :frozen-card)}
hide-connection-sheet)
{:db (assoc db :popover/popover {:view :frozen-card})}))
(fx/defn on-get-application-info-success
{:events [:hardwallet.callback/on-get-application-info-success]}
[{:keys [db] :as cofx} info on-success]
(let [info' (-> info
(js->clj :keywordize-keys true)
(update :key-uid ethereum/normalized-hex))
{:keys [pin-retry-counter puk-retry-counter]} info'
(let [{:keys [pin-retry-counter puk-retry-counter]} info
view-id (:view-id db)
{:keys [on-card-read]} (:hardwallet db)
on-success' (or on-success on-card-read)
enter-step (if (zero? pin-retry-counter)
:puk
(get-in db [:hardwallet :pin :enter-step]))]
enter-step (get-in db [:hardwallet :pin :enter-step])]
(log/debug "[hardwallet] on-get-application-info-success"
"on-success" on-success')
(fx/merge cofx
{:db (-> db
(assoc-in [:hardwallet :pin :enter-step] enter-step)
(update-in [:hardwallet :pin :error-label] #(if (= :puk enter-step)
:t/enter-puk-code-description
%))
(assoc-in [:hardwallet :application-info] info')
(assoc-in [:hardwallet :application-info :applet-installed?] true)
(assoc-in [:hardwallet :application-info-error] nil))}
(stash-on-card-read)
(if (zero? puk-retry-counter)
{:utils/show-popup {:title (i18n/label :t/error)
:content (i18n/label :t/keycard-blocked)}}
(when on-success'
(dispatch-event on-success'))))))
"on-success" on-success'
"pin-retry-counter" pin-retry-counter
"puk-retry-counter" puk-retry-counter)
(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))}
(stash-on-card-read)
(when (and (zero? pin-retry-counter)
(pos? puk-retry-counter)
(not= enter-step :puk))
(frozen-keycard-popup))
(fn [{:keys [db] :as cofx}]
(if (zero? puk-retry-counter)
(fx/merge
cofx
{:db (assoc-in db [:hardwallet :pin :status] :blocked-card)}
hide-connection-sheet)
(when on-success'
(dispatch-event cofx on-success')))))))
(fx/defn on-get-application-info-error
{:events [:hardwallet.callback/on-get-application-info-error]}
@ -484,3 +508,12 @@
{:db (assoc-in db [:hardwallet :pin :status] :verifying)
:hardwallet/verify-pin {:pin pin
:pairing pairing}})))}))))
(fx/defn navigete-to-keycard-settings
{:events [::navigate-to-keycard-settings]}
[cofx]
(navigation/navigate-reset
cofx
{:index 1
:routes [{:name :my-profile}
{:name :keycard-settings}]}))

View File

@ -120,22 +120,35 @@
(defn- proceed-to-pin-confirmation [fx]
(assoc-in fx [:db :hardwallet :pin :enter-step] :confirmation))
(defn- proceed-to-pin-reset-confirmation [fx]
(-> fx
(update-in [:db :hardwallet :pin] dissoc :reset-confirmation)
(assoc-in [:db :hardwallet :pin :enter-step] :reset-confirmation)))
(defn- proceed-to-puk-confirmation [fx]
(assoc-in fx [:db :hardwallet :pin :enter-step] :puk))
(fx/defn on-unblock-pin-success
{:events [:hardwallet.callback/on-unblock-pin-success]}
[{:keys [db] :as cofx}]
(let [pairing (common/get-pairing db)]
(let [pairing (common/get-pairing db)
reset-pin (get-in db [:hardwallet :pin :reset])]
(fx/merge cofx
{:hardwallet/get-application-info {:pairing pairing}
:db (-> db
(update-in [:hardwallet :pin] merge {:status nil
:enter-step :original
:current [0 0 0 0 0 0]
:confirmation []
:puk []
:puk-restore? true
:error-label nil}))}
{:hardwallet/get-application-info
{:pairing pairing}
:db
(update-in db [:hardwallet :pin] merge
{:status :after-unblocking
:enter-step :login
:login reset-pin
:confirmation []
:puk []
:puk-restore? true
:error-label nil})}
(common/hide-connection-sheet)
(navigation/navigate-to-cofx :enter-pin-settings nil))))
(common/clear-on-card-connected)
(common/clear-on-card-read))))
(fx/defn on-unblock-pin-error
{:events [:hardwallet.callback/on-unblock-pin-error]}
@ -145,11 +158,15 @@
(log/debug "[hardwallet] unblock pin error" error)
(when-not tag-was-lost?
(fx/merge cofx
{:hardwallet/get-application-info {:pairing pairing}
:db (update-in db [:hardwallet :pin] merge {:status :error
:error-label :t/puk-mismatch
:enter-step :puk
:puk []})}
{:hardwallet/get-application-info
{:pairing pairing}
:db
(update-in db [:hardwallet :pin] merge
{:status :error
:error-label :t/puk-mismatch
:enter-step :puk
:puk []})}
(common/hide-connection-sheet)))))
(fx/defn clear-on-verify-handlers
@ -215,7 +232,7 @@
(fn [_] {:utils/dispatch-later
[{:dispatch [on-verified-failure]
:ms 200}]}))
(clear-on-verify-handlers))
#_(clear-on-verify-handlers))
(fx/merge cofx
(common/hide-connection-sheet)
@ -231,12 +248,13 @@
:handler
(fn [{:keys [db]}]
(let [puk (common/vector->string (get-in db [:hardwallet :pin :puk]))
pin (common/vector->string (get-in db [:hardwallet :pin :reset]))
key-uid (get-in db [:hardwallet :application-info :key-uid])
pairing (common/get-pairing db key-uid)]
{:db (assoc-in db [:hardwallet :pin :status] :verifying)
:hardwallet/unblock-pin
{:puk puk
:new-pin common/default-pin
:new-pin pin
:pairing pairing}}))}))
(def pin-code-length 6)
@ -252,12 +270,15 @@
(fx/defn update-pin
{:events [:hardwallet.ui/pin-numpad-button-pressed]}
[{:keys [db] :as cofx} number enter-step]
(log/debug "update-pin" enter-step)
(let [numbers-entered (count (get-in db [:hardwallet :pin enter-step]))
need-update? (if (= enter-step :puk)
(< numbers-entered puk-code-length)
(< numbers-entered pin-code-length))]
(fx/merge cofx
{:db (cond-> (assoc-in db [:hardwallet :pin :status] nil)
{:db (cond-> (-> db
(assoc-in [:hardwallet :pin :enter-step] enter-step)
(assoc-in [:hardwallet :pin :status] nil))
need-update? (update-in [:hardwallet :pin enter-step] (fnil conj []) number))}
(when need-update?
(handle-pin-input enter-step)))))
@ -269,6 +290,13 @@
:original []
:confirmation []}))
(defn- pin-reset-error [fx error-label]
(update-in fx [:db :hardwallet :pin] merge {:status :error
:error-label error-label
:enter-step :reset
:reset []
:reset-confirmation []}))
; PIN enter steps:
; login - PIN is used to login
; sign - PIN for transaction sign
@ -309,7 +337,7 @@
(and (= enter-step :export-key)
(= pin-code-length numbers-entered))
(wallet/hide-pin-sheet)
(wallet/verify-pin-with-delay)
(and (= enter-step :sign)
(= pin-code-length numbers-entered))
@ -328,7 +356,21 @@
(= pin-code-length numbers-entered)
(not= (get-in db [:hardwallet :pin :original])
(get-in db [:hardwallet :pin :confirmation])))
(pin-enter-error :t/pin-mismatch))))
(pin-enter-error :t/pin-mismatch)
(= enter-step :reset)
(proceed-to-pin-reset-confirmation)
(and (= enter-step :reset-confirmation)
(= (get-in db [:hardwallet :pin :reset])
(get-in db [:hardwallet :pin :reset-confirmation])))
(proceed-to-puk-confirmation)
(and (= enter-step :reset-confirmation)
(= pin-code-length numbers-entered)
(not= (get-in db [:hardwallet :pin :reset])
(get-in db [:hardwallet :pin :reset-confirmation])))
(pin-reset-error :t/pin-mismatch))))
(fx/defn set-multiaccount-pairing
[cofx _ pairing paired-on]

View File

@ -1,6 +1,7 @@
(ns status-im.hardwallet.export-key
(:require [status-im.utils.fx :as fx]
[taoensso.timbre :as log]
[status-im.hardwallet.wallet :as wallet]
[status-im.hardwallet.common :as common]))
(fx/defn on-export-key-error
@ -39,5 +40,6 @@
(let [callback-fn (get-in db [:hardwallet :on-export-success])]
(fx/merge cofx
{:dispatch (callback-fn pubkey)}
(wallet/hide-pin-sheet)
(common/clear-pin)
(common/hide-connection-sheet))))

View File

@ -8,7 +8,8 @@
[status-im.hardwallet.recovery :as recovery]
[status-im.hardwallet.onboarding :as onboarding]
status-im.hardwallet.fx
[status-im.ui.components.bottom-sheet.core :as bottom-sheet]))
[status-im.ui.components.bottom-sheet.core :as bottom-sheet]
[status-im.signing.core :as signing.core]))
(fx/defn login-got-it-pressed
{:events [:keycard.login.pin.ui/got-it-pressed
@ -49,16 +50,56 @@
{:db (assoc-in db [:hardwallet :flow] :login)}
(navigation/navigate-to-cofx :keycard-recovery-pair nil)))
(fx/defn frozen-keycard-popup
[{:keys [db]}]
{:db (assoc db :popover/popover {:view :frozen-card})})
(fx/defn reset-pin
{:events [::reset-pin]}
[{:keys [db] :as cofx}]
(fx/merge
cofx
{:db (assoc db :hardwallet/new-account-sheet? false)}
(signing.core/discard)
(fn [{:keys [db]}]
{:db (-> db
(dissoc :popover/popover)
(update-in [:hardwallet :pin] dissoc
:reset :puk)
(update-in [:hardwallet :pin] assoc
:enter-step :reset
:error nil
:status nil))})
(when-not (:multiaccounts/login db)
(navigation/navigate-to-cofx
:profile-stack
{:screen :keycard-pin}))))
(fx/defn dismiss-frozen-keycard-popover
{:events [::frozen-keycard-popover-dismissed]}
[{:keys [db]}]
{:db (-> db
(dissoc :popover/popover)
(update :hardwallet dissoc :setup-step))})
(fx/defn login-with-keycard
{:events [:hardwallet/login-with-keycard]}
[{:keys [db] :as cofx}]
(let [application-info (get-in db [:hardwallet :application-info])
(let [{:keys [:pin-retry-counter :puk-retry-counter]
:as application-info}
(get-in db [:hardwallet :application-info])
key-uid (get-in db [:hardwallet :application-info :key-uid])
multiaccount (get-in db [:multiaccounts/multiaccounts (get-in db [:multiaccounts/login :key-uid])])
multiaccount-key-uid (get multiaccount :key-uid)
multiaccount-mismatch? (or (nil? multiaccount)
(not= multiaccount-key-uid key-uid))
pairing (:keycard-pairing multiaccount)]
(log/debug "[keycard] login-with-keycard"
"empty application info" (empty? application-info)
"no key-uid" (empty? key-uid)
"multiaccount-mismatch?" multiaccount-mismatch?
"no pairing" (empty? pairing))
(cond
(empty? application-info)
(fx/merge cofx
@ -80,10 +121,16 @@
(common/hide-connection-sheet)
(navigation/navigate-to-cofx :keycard-unpaired nil))
(and (zero? pin-retry-counter)
(or (nil? puk-retry-counter)
(= 5 puk-retry-counter)))
nil #_(frozen-keycard-popup cofx)
:else
(common/get-keys-from-keycard cofx))))
(fx/defn proceed-to-login
{:events [::login-after-reset]}
[cofx]
(log/debug "[hardwallet] proceed-to-login")
(common/show-connection-sheet

View File

@ -3,6 +3,7 @@
["react-native" :as rn]
[status-im.utils.types :as types]
[status-im.native-module.core :as status]
[status-im.ethereum.core :as ethereum]
[status-im.hardwallet.keycard :as keycard]))
(defonce event-emitter (.-DeviceEventEmitter rn))
@ -57,9 +58,20 @@
(defn get-application-info
[{:keys [pairing on-success on-failure]}]
;; NOTE: if the card fails to get application info in the middle of the call
;; it doesn't returns a Tar was lost. error properly
;; https://github.com/status-im/react-native-status-keycard/blob/master/android/src/main/java/im/status/ethereum/keycard/SmartCard.java#L235
(.. status-keycard
(getApplicationInfo (str pairing))
(then on-success)
(then (fn [response]
(let [info (-> response
(js->clj :keywordize-keys true)
(update :key-uid ethereum/normalized-hex))]
(if (and pairing (nil? (:pin-retry-counter info)))
(on-failure (clj->js {:message "Tag was lost."
:code "android.nfc.TagLostException"}))
(on-success info)))))
(catch on-failure)))
(defn install-applet [{:keys [on-success on-failure]}]

View File

@ -197,17 +197,21 @@
{:events [:hardwallet.callback/on-sign-error]}
[{:keys [db] :as cofx} error]
(log/debug "[hardwallet] sign error: " error)
(let [tag-was-lost? (common/tag-lost? (:error error))]
(let [tag-was-lost? (common/tag-lost? (:error error))
pin-retries (get-in db [:hardwallet :application-info :pin-retry-counter])]
(when-not tag-was-lost?
(if (re-matches common/pin-mismatch-error (:error error))
(fx/merge cofx
{:db (-> db
(assoc-in [:hardwallet :application-info :pin-retry-counter] (dec pin-retries))
(update-in [:hardwallet :pin] merge {:status :error
:sign []
:error-label :t/pin-mismatch})
(assoc-in [:signing/sign :keycard-step] :pin))}
(common/hide-connection-sheet)
(common/get-application-info (common/get-pairing db) nil))
(common/get-application-info (common/get-pairing db) nil)
(when (zero? (dec pin-retries))
(common/frozen-keycard-popup)))
(fx/merge cofx
(common/hide-connection-sheet)
(common/show-wrong-keycard-alert true))))))

View File

@ -31,6 +31,8 @@
:paired? true
:has-master-key? true
:initialized? true
:pin-retry-counter 3
:puk-retry-counter 5
:key-uid (get-in @re-frame.db/app-db [:multiaccounts/login :key-uid])})
(connect-card))
@ -84,7 +86,7 @@
(defn install-cash-applet [_])
(def kk1-password "000000")
(def default-puk "000000000000")
(defn init-card [{:keys [pin on-success]}]
(swap! state assoc :application-info
{:free-pairing-slots 5
@ -98,7 +100,7 @@
(swap! state assoc :pin pin)
(later
#(on-success {:password kk1-password
:puk "000000000000"
:puk default-puk
:pin pin})))
(defn install-applet-and-init-card [_])
@ -141,12 +143,34 @@
password
#(on-success response)))))
(defn unblock-pin [_])
(defn unblock-pin
[{:keys [puk on-success on-failure]}]
(if (= puk default-puk)
(do
(swap! state update :application-info assoc
:pin-retry-counter 3
:puk-retry-counter 5)
(later #(on-success true)))
(do
(swap! state update-in
[:application-info :puk-retry-counter]
(fnil dec 5))
(later
#(on-failure
#js {:code "EUNSPECIFIED"
:message "Unexpected error SW, 0x63C2"})))))
(defn verify-pin [{:keys [pin pairing on-success]}]
(when (and (= pairing kk1-pair)
(= pin (get @state :pin)))
(later #(on-success 3))))
(defn verify-pin [{:keys [pin pairing on-success on-failure]}]
(if (and (= pairing kk1-pair)
(= pin (get @state :pin)))
(later #(on-success 3))
(do
(swap! state update-in
[:application-info :pin-retry-counter]
(fnil dec 3))
(later #(on-failure
#js {:code "EUNSPECIFIED"
:message "Unexpected error SW, 0x63C2"})))))
(defn change-pin [args]
(log/warn "change-pin not implemented" args))
@ -199,16 +223,42 @@
(on-success publicKey)))))))))))))))
(defn unpair-and-delete [_])
(defn get-keys [{:keys [on-success pin]}]
(swap! state assoc :pin pin)
;;TODO(rasom): verify password before callback
(later
#(on-success
{:key-uid (get-in @state [:application-info :key-uid])
:encryption-public-key (ethereum/sha3 pin)})))
(defn sign [{:keys [on-success]}]
(on-success "123"))
;; It is a bit complicated to verify password before we have multiaccs main
;; wallet address, so we just define a set of "allowed" pins
(def allowed-pins
#{"121212" "111111" "222222" "123123"})
(defn get-keys [{:keys [on-success on-failure pin]}]
(if (contains? allowed-pins pin)
(do
(swap! state assoc :pin pin)
(later
#(on-success
{:key-uid (get-in @state [:application-info :key-uid])
:encryption-public-key (ethereum/sha3 pin)})))
(do
(log/debug "Incorrect PIN" pin)
(swap! state update-in
[:application-info :pin-retry-counter]
(fnil dec 3))
(later
#(on-failure
#js {:code "EUNSPECIFIED"
:message "Unexpected error SW, 0x63C2"})))))
(defn sign [{:keys [pin on-success on-failure]}]
(if (= pin (get @state :pin))
(later
#(on-success "123"))
(do
(swap! state update-in
[:application-info :pin-retry-counter]
(fnil dec 3))
(later
#(on-failure
#js {:code "EUNSPECIFIED"
:message "Unexpected error SW, 0x63C2"})))))
(defn sign-typed-data [args]
(log/warn "sign-typed-data not implemented" args))

View File

@ -1,11 +1,9 @@
(ns status-im.hardwallet.wallet
(:require [status-im.ethereum.core :as ethereum]
[status-im.utils.fx :as fx]
[status-im.ui.screens.wallet.add-new.views :as add-new.views]
[status-im.hardwallet.common :as common]
[status-im.constants :as constants]
[status-im.ethereum.eip55 :as eip55]
[status-im.ui.components.bottom-sheet.core :as bottom-sheet]
[status-im.utils.hex :as utils.hex]))
(fx/defn show-pin-sheet
@ -15,21 +13,20 @@
cofx
{:db (-> db
(assoc-in [:hardwallet :pin :enter-step] :export-key)
(update-in [:hardwallet :pin] dissoc :export-key))}
(bottom-sheet/show-bottom-sheet
{:view {:content add-new.views/pin
:height 256}})))
(update-in [:hardwallet :pin] dissoc :export-key)
(assoc :hardwallet/new-account-sheet? true))}))
(fx/defn verify-pin-with-delay
[cofx]
{:utils/dispatch-later
;; We need to give previous sheet some time to be fully hidden
[{:ms 200
:dispatch [:wallet.accounts/verify-pin]}]})
(fx/defn hide-pin-sheet
{:events [:hardwallet/hide-new-account-pin-sheet]}
[cofx]
(fx/merge
cofx
{:utils/dispatch-later
;; We need to give previous sheet some time to be fully hidden
[{:ms 200
:dispatch [:wallet.accounts/verify-pin]}]}
(bottom-sheet/hide-bottom-sheet)))
{:events [:hardwallet/new-account-pin-sheet-hide]}
[{:keys [db]}]
{:db (assoc db :hardwallet/new-account-sheet? false)})
(fx/defn generate-new-keycard-account
{:events [:wallet.accounts/generate-new-keycard-account]}
@ -59,6 +56,6 @@
(common/verify-pin
cofx
{:pin-step :export-key
:on-card-connected :wallet.accounts/generate-new-keycard-account
:on-card-connected :wallet.accounts/verify-pin
:on-success :wallet.accounts/generate-new-keycard-account
:on-failure :hardwallet/new-account-pin-sheet}))

View File

@ -716,7 +716,6 @@
:pin-changed
:pin-code
:pin-mismatch
:pin-retries-left
:preview-privacy
:privacy
:privacy-and-security

View File

@ -7,6 +7,7 @@
[status-im.ethereum.core :as ethereum]
[status-im.ethereum.eip55 :as eip55]
[status-im.ethereum.json-rpc :as json-rpc]
[status-im.hardwallet.common :as hardwallet.common]
[status-im.fleet.core :as fleet]
[status-im.i18n :as i18n]
[status-im.multiaccounts.biometric.core :as biometric]
@ -367,16 +368,18 @@
(log/debug "[login] get-auth-method-success"
"auth-method" auth-method
"keycard-multiacc?" keycard-multiaccount?)
(fx/merge cofx
{:db (assoc db :auth-method auth-method)}
#(cond
(= auth-method
keychain/auth-method-biometric)
(biometric/biometric-auth %)
(= auth-method
keychain/auth-method-password)
(get-credentials % key-uid))
(open-login-callback nil))))
(fx/merge
cofx
{:db (assoc db :auth-method auth-method)}
#(cond
(= auth-method keychain/auth-method-biometric)
(biometric/biometric-auth %)
(= auth-method keychain/auth-method-password)
(get-credentials % key-uid)
(and keycard-multiaccount?
(get-in db [:hardwallet :card-connected?]))
(hardwallet.common/get-application-info % nil nil))
(open-login-callback nil))))
(fx/defn biometric-auth-done
{:events [:biometric-auth-done]}

View File

@ -60,6 +60,7 @@
(navigate-to-cofx cofx view-id params))
(fx/defn navigate-replace
{:events [:navigate-replace]}
[{:keys [db]} go-to-view-id screen-params]
(let [db (cond-> (assoc db :view-id go-to-view-id)
(seq screen-params)

View File

@ -188,6 +188,9 @@
(reg-root-key-sub ::message-lists :message-lists)
(reg-root-key-sub ::pagination-info :pagination-info)
;; keycard
(reg-root-key-sub :hardwallet/new-account-sheet? :hardwallet/new-account-sheet?)
;;GENERAL ==============================================================================================================
(re-frame/reg-sub

View File

@ -49,7 +49,28 @@
update? (atom nil)
current-obj (reagent/atom nil)]
(reagent/create-class
{:component-will-update (fn [_ [_ obj _ _]]
{:component-will-mount (fn [args]
(let [[_ obj _ _] (.-argv (.-props args))]
(when @clear-timeout (js/clearTimeout @clear-timeout))
(when (or (not= obj @current-obj) @update?)
(cond
@update?
(do (reset! update? false)
(show-panel-anim bottom-anim-value alpha-value))
(and @current-obj obj)
(do (reset! update? true)
(js/setTimeout #(reset! current-obj obj) 600)
(hide-panel-anim bottom-anim-value alpha-value (- window-height)))
obj
(do (reset! current-obj obj)
(show-panel-anim bottom-anim-value alpha-value))
:else
(do (reset! clear-timeout (js/setTimeout #(reset! current-obj nil) 600))
(hide-panel-anim bottom-anim-value alpha-value (- window-height)))))))
:component-will-update (fn [_ [_ obj _ _]]
(when @clear-timeout (js/clearTimeout @clear-timeout))
(when (or (not= obj @current-obj) @update?)
(cond

View File

@ -0,0 +1,44 @@
(ns status-im.ui.screens.hardwallet.frozen-card.view
(:require-macros [status-im.utils.views :as views])
(:require [status-im.ui.components.react :as react]
[status-im.ui.components.colors :as colors]
[status-im.ui.components.icons.vector-icons :as icons]
[status-im.ui.components.common.common :as components.common]
[status-im.hardwallet.login :as login]
[status-im.i18n :as i18n]
[re-frame.core :as re-frame]))
(views/defview frozen-card
[{:keys [show-dismiss-button?]
:or {show-dismiss-button? true}}]
[react/view {:style (when-not show-dismiss-button?
{:flex 1})}
[react/view {:margin-top 24
:margin-horizontal 24
:align-items :center}
[react/view {:background-color colors/blue-light
:width 32 :height 32
:border-radius 16
:align-items :center
:justify-content :center}
[icons/icon :main-icons/warning {:color colors/blue}]]
[react/text {:style {:typography :title-bold
:margin-top 16
:margin-bottom 8}}
(i18n/label :t/keycard-is-frozen-title)]
[react/text {:style {:color colors/gray
:text-align :center}}
(i18n/label :t/keycard-is-frozen-details)]]
[react/view {:margin-bottom 24
:margin-horizontal 24
:align-items :center}
[components.common/button
{:on-press #(re-frame/dispatch [::login/reset-pin])
:button-style {:margin-top 24}
:label (i18n/label :t/keycard-is-frozen-reset)}]
(when show-dismiss-button?
[components.common/button
{:on-press #(re-frame/dispatch [::login/frozen-keycard-popover-dismissed])
:button-style {:margin-top 24}
:background? false
:label (i18n/label :t/dismiss)}])]])

View File

@ -1,22 +1,41 @@
(ns status-im.ui.screens.hardwallet.pin.styles
(:require [status-im.ui.components.colors :as colors]
[status-im.utils.styles :as styles]))
(:require [status-im.ui.components.colors :as colors]))
(styles/def pin-container
(def pin-container
{:flex 1
:flex-direction :column
:justify-content :space-between})
(defn error-container [small-screen?]
{:height (when small-screen? 18)
:margin-top (if small-screen? 14 10)
:margin-bottom (if small-screen? 10 0)})
(defn info-container [small-screen?]
{:height 44
:width "100%"
:justify-content :center
:margin-top (if small-screen? 14 10)})
(defn error-container [y-translation opacity]
{:left 0
:right 0
:align-items :center
:position :absolute
:transform [{:translateY y-translation}]
:opacity opacity
:justify-content :center})
(defn error-text [small-screen?]
{:color colors/red
{:position :absolute
:color colors/red
:font-size (if small-screen? 12 15)
:text-align :center})
(defn retry-container [y-translation opacity]
{:left 0
:right 0
:align-items :center
:position :absolute
:transform [{:translateY y-translation}]
:opacity opacity
:justify-content :center})
(defn center-container [title]
{:flex-direction :column
:align-items :center
@ -34,16 +53,18 @@
(def pin-indicator-container
{:flex-direction :row
:justify-content :space-between
:margin-top 16})
:align-items :center
:height 22
:margin-top 5})
(def pin-indicator-group-container
{:flex-direction :row
:justify-content :space-between})
(defn pin-indicator [pressed? status]
(defn pin-indicator [pressed? error?]
{:width 8
:height 8
:background-color (if (= status :error)
:background-color (if error?
colors/red
(if pressed?
colors/blue
@ -51,6 +72,15 @@
:border-radius 50
:margin-horizontal 5})
(defn puk-indicator [error?]
{:width 8
:height 8
:background-color (if error?
colors/red
colors/black-transparent)
:border-radius 50
:margin-horizontal 5})
(def waiting-indicator-container
{:margin-top 26})

View File

@ -19,7 +19,7 @@
(re-frame/reg-sub
:hardwallet/pin-enter-step
(fn [db]
(get-in db [:hardwallet :pin :enter-step] :original)))
(get-in db [:hardwallet :pin :enter-step])))
(re-frame/reg-sub
:hardwallet/pin-operation
@ -47,3 +47,10 @@
:hardwallet/pin-error-label
(fn [db]
(get-in db [:hardwallet :pin :error-label])))
(re-frame/reg-sub
:hardwallet/frozen-card?
(fn [db]
(let [{:keys [pin-retry-counter]}
(get-in db [:hardwallet :application-info])]
(zero? pin-retry-counter))))

View File

@ -1,6 +1,8 @@
(ns status-im.ui.screens.hardwallet.pin.views
(:require-macros [status-im.utils.views :refer [defview letsubs]])
(:require [re-frame.core :as re-frame]
[reagent.core :as reagent]
[status-im.ui.components.animation :as animation]
[status-im.i18n :as i18n]
[status-im.ui.components.colors :as colors]
[status-im.ui.components.icons.vector-icons :as vector-icons]
@ -8,9 +10,11 @@
[status-im.ui.screens.hardwallet.pin.styles :as styles]
[status-im.ui.components.checkbox.view :as checkbox]
[status-im.utils.platform :as platform]
[status-im.utils.utils :as utils]
[status-im.ui.components.topbar :as topbar]))
(def default-pin-retries-number 3)
(def default-puk-retries-number 5)
(defn numpad-button [n step enabled? small-screen?]
[react/touchable-highlight
@ -40,32 +44,36 @@
[react/view (styles/numpad-delete-button small-screen?)
[vector-icons/icon :main-icons/backspace {:color colors/blue}]]]]])
(defn pin-indicator [pressed? status]
[react/view (styles/pin-indicator pressed? status)])
(defn pin-indicators [pin status style]
[react/view (merge styles/pin-indicator-container style)
(defn pin-indicators [pin error?]
[react/view styles/pin-indicator-container
(map-indexed
(fn [i group]
^{:key i}
[react/view styles/pin-indicator-group-container
group])
(partition 3
(map-indexed
(fn [i n]
^{:key i}
[pin-indicator (number? n) status])
(concat pin
(repeat (- 6 (count pin))
nil)))))])
(fn [i n]
(let [pressed? (number? n)]
^{:key i} [react/view (styles/pin-indicator pressed? error?)]))
(concat pin (repeat (- 6 (count pin)) nil)))])
(defn puk-indicators [puk status]
[react/view {:margin-top 28}
(defn puk-indicators [puk error?]
[react/view {:margin-top 28
:flex-direction :row
:justify-content :space-between}
(map-indexed
(fn [i puk-group]
^{:key i}
[pin-indicators puk-group status {:margin-top 8}])
(partition 6
[react/view (merge styles/pin-indicator-container
{:margin-top 8
:margin 12})
(map-indexed
(fn [j n]
(if (number? n)
^{:key j} [react/text {:style {:font-size 20
:width 18
:color (if error?
colors/red
colors/black)}}
n]
^{:key j} [react/view (styles/puk-indicator error?)]))
puk-group)])
(partition 4
(concat puk
(repeat (- 12 (count puk))
nil))))])
@ -82,38 +90,142 @@
:on-value-change #(re-frame/dispatch [:multiaccounts/save-password %])}]
[react/text (i18n/label :t/hardwallet-dont-ask-card)]])))
(defn bezier-easing []
(.bezier ^js animation/easing 0.77, 0.000, 0.175, 1))
(defn animate-info-in
"animation that makes the error message appear for a few seconds, then
replaces it with the number of attempts left"
[error-y-translation error-opacity retries-y-translation retries-opacity]
(animation/start
(animation/anim-sequence
[(animation/parallel
[(animation/timing error-opacity
{:toValue 1
:easing (bezier-easing)
:duration 400
:useNativeDriver true})
(animation/timing error-y-translation
{:toValue 0
:easing (bezier-easing)
:duration 400
:useNativeDriver true})])
(animation/anim-delay 2200)
(animation/parallel
[(animation/timing error-opacity
{:toValue 0
:easing (bezier-easing)
:duration 400
:useNativeDriver true})
(animation/timing error-y-translation
{:toValue 8
:easing (bezier-easing)
:duration 400
:useNativeDriver true})
(animation/timing retries-opacity
{:toValue 1
:easing (bezier-easing)
:duration 400
:useNativeDriver true})
(animation/timing retries-y-translation
{:toValue 0
:easing (bezier-easing)
:duration 400
:useNativeDriver true})])])))
(defn animate-info-out
[retries-y-translation retries-opacity]
(animation/start
(animation/parallel
[(animation/timing retries-opacity
{:toValue 0
:easing (bezier-easing)
:duration 400
:useNativeDriver true})
(animation/timing retries-y-translation
{:toValue -8
:easing (bezier-easing)
:duration 400
:useNativeDriver true})])))
(defn pin-view
[{:keys [pin title-label description-label step status error-label
retry-counter small-screen? save-password-checkbox?]}]
(let [enabled? (not= status :verifying)]
[react/scroll-view
[react/view styles/pin-container
[react/view (styles/center-container title-label)
(when title-label
[react/text {:style styles/center-title-text}
(i18n/label title-label)])
(when description-label
[react/text {:style styles/create-pin-text
:number-of-lines 2}
(i18n/label description-label)])
[react/view {:flex 1}
(case status
:verifying [react/view styles/waiting-indicator-container
[react/activity-indicator {:animating true
:size :small}]]
:error [react/view (styles/error-container small-screen?)
[react/text {:style (styles/error-text small-screen?)}
(i18n/label error-label)]]
(when (and retry-counter (< retry-counter default-pin-retries-number))
[react/view {:margin-top (if (= step :puk) 24 8)}
[react/text {:style {:text-align :center}}
(i18n/label :t/pin-retries-left {:number retry-counter})]]))]
(when save-password-checkbox?
[save-password])
(if (= step :puk)
[puk-indicators pin status]
[pin-indicators pin status nil])
[numpad step enabled? small-screen?]]]]))
[{:keys [retry-counter]}]
(let [error-y-translation (animation/create-value -8)
error-opacity (animation/create-value 0)
retries-y-translation (animation/create-value (if retry-counter 8 0))
retries-opacity (animation/create-value (if retry-counter 0 1))
!error? (reagent/atom false)]
(reagent/create-class
{:component-did-update
(fn [this [_ previous-props]]
(let [[_ props] (.-argv (.-props ^js this))
previous-status (:status previous-props)
new-status (:status props)]
(case new-status
:error (when (or (nil? previous-status)
(= :verifying previous-status))
(utils/vibrate)
(reset! !error? true)
(animate-info-in error-y-translation
error-opacity
retries-y-translation
retries-opacity)
(js/setTimeout (fn [] (reset! !error? false)) 3000))
:verifying (do
(animation/set-value error-y-translation -8)
(animate-info-out retries-y-translation
retries-opacity))
nil)))
:reagent-render
(fn [{:keys [pin title-label description-label step error-label status
retry-counter small-screen? save-password-checkbox?]}]
(let [enabled? (and (not= status :verifying)
(not @!error?))
puk? (= step :puk)]
[react/scroll-view
[react/view styles/pin-container
[react/view (styles/center-container title-label)
(when title-label
[react/text {:style styles/center-title-text}
(i18n/label title-label)])
(when description-label
[react/text {:style styles/create-pin-text
:number-of-lines 2}
(i18n/label description-label)])
(when save-password-checkbox?
[save-password])
[react/view {:style (styles/info-container small-screen?)}
(when error-label
[react/animated-view {:style (styles/error-container error-y-translation error-opacity)}
[react/text {:style (styles/error-text small-screen?)}
(i18n/label error-label)]])
[react/animated-view {:style (styles/retry-container retries-y-translation retries-opacity)}
(cond
(and retry-counter (= retry-counter 1))
[react/nested-text {:style {:text-align :center
:color colors/gray}}
(i18n/label (if puk?
:t/pin-one-attempt-blocked-before
:t/pin-one-attempt-frozen-before))
[{:style {:color colors/black
:font-weight "700"}}
(i18n/label :t/pin-one-attempt)]
(i18n/label (if puk?
:t/pin-one-attempt-blocked-after
:t/pin-one-attempt-frozen-after))]
(and retry-counter (< retry-counter (if puk?
default-puk-retries-number
default-pin-retries-number)))
[react/text {:style {:text-align :center
:color colors/gray}}
(i18n/label :t/pin-retries-left {:number retry-counter})]
:else
nil)]]
(if puk?
[puk-indicators pin @!error?]
[pin-indicators pin @!error?])
[numpad step enabled? small-screen?]]]]))})))
(def pin-retries 3)
(def puk-retries 5)
@ -125,32 +237,37 @@
pin-retry-counter [:hardwallet/pin-retry-counter]
puk-retry-counter [:hardwallet/puk-retry-counter]
error-label [:hardwallet/pin-error-label]]
[react/view {:flex 1
:background-color colors/white}
[topbar/topbar {}]
(if (zero? pin-retry-counter)
[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-view {:pin pin
:retry-counter (when (< pin-retry-counter pin-retries) pin-retry-counter)
:title-label (case step
:current :t/current-pin
:login :t/current-pin
:import-multiaccount :t/current-pin
:original :t/create-a-pin
:confirmation :t/repeat-pin
:t/current-pin)
:description-label (case step
:current :t/current-pin-description
:sign :t/current-pin-description
:import-multiaccount :t/current-pin-description
:login :t/login-pin-description
:t/new-pin-description)
:step step
:status status
:error-label error-label}])]))
(let [;; TODO(rasom): retarded hack to prevent state mess on opening pin
;; sheet on another tab and returning back to this screen. Should be
;; properly rewritten so that different instances of pin-view do not
;; mess with state unrelated to them.
step (or step :current)]
[react/view {:flex 1
:background-color colors/white}
[topbar/topbar {}]
(if (zero? pin-retry-counter)
[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-view {:pin pin
:retry-counter (when (< pin-retry-counter pin-retries) pin-retry-counter)
:title-label (case step
:current :t/current-pin
:login :t/current-pin
:import-multiaccount :t/current-pin
:original :t/create-a-pin
:confirmation :t/repeat-pin
:t/current-pin)
:description-label (case step
:current :t/current-pin-description
:sign :t/current-pin-description
:import-multiaccount :t/current-pin-description
:login :t/login-pin-description
:t/new-pin-description)
:step step
:status status
:error-label error-label}])])))

View File

@ -8,7 +8,9 @@
[status-im.ui.components.colors :as colors]
[status-im.ui.components.common.common :as components.common]
[status-im.ui.components.topbar :as topbar]
[status-im.constants :as constants]))
[status-im.constants :as constants]
[status-im.ui.screens.keycard.views :as keycard.views]
[status-im.hardwallet.common :as hardwallet.common]))
(defn- action-row [{:keys [icon label on-press color-theme]}]
[react/touchable-highlight
@ -131,3 +133,10 @@
:color-theme :red
:label :t/reset-card
:on-press #(re-frame/dispatch [:keycard-settings.ui/reset-card-pressed])}]])]]))
(defn reset-pin []
[keycard.views/login-pin
{:back-button-handler
::hardwallet.common/navigate-to-keycard-settings
:hide-login-actions? true
:default-enter-step :reset}])

View File

@ -15,8 +15,9 @@
[status-im.ui.screens.keycard.styles :as styles]
[status-im.utils.core :as utils.core]
[status-im.utils.gfycat.core :as gfy]
[status-im.utils.identicon :as identicon]
[status-im.constants :as constants])
[status-im.constants :as constants]
[status-im.ui.screens.keycard.views :as keycard.views]
[status-im.utils.identicon :as identicon])
(:require-macros [status-im.utils.views :refer [defview letsubs]]))
(defn intro []
@ -83,26 +84,34 @@
{:handler #(re-frame/dispatch [::hardwallet.recovery/cancel-pressed])
:style {:padding-left 21}}
(i18n/label :t/cancel)]
[react/text {:style {:color colors/gray}}
(i18n/label :t/step-i-of-n {:number 2
:step 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/enter-your-code)]]]
[pin.views/pin-view
{:pin pin
:retry-counter retry-counter
:small-screen? small-screen?
:status status
:error-label error-label
:step :import-multiaccount}]]]))
(when-not (#{:frozen-card :blocked-card} status)
[react/text {:style {:color colors/gray}}
(i18n/label :t/step-i-of-n {:number 2
:step 2})])]
(case status
:frozen-card
[keycard.views/frozen-card]
:blocked-card
[keycard.views/blocked-card]
[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/enter-your-code)]]]
[pin.views/pin-view
{:pin pin
:retry-counter retry-counter
:small-screen? small-screen?
:status status
:error-label error-label
:step :import-multiaccount}]])]))
(defview pair []
(letsubs [pair-code [:hardwallet-pair-code]

View File

@ -12,7 +12,10 @@
[status-im.ui.screens.chat.photos :as photos]
[status-im.ui.screens.hardwallet.pin.views :as pin.views]
[status-im.ui.screens.keycard.styles :as styles]
[status-im.constants :as constants])
[status-im.constants :as constants]
[status-im.ui.components.button :as button]
[status-im.hardwallet.login :as hardwallet.login]
[status-im.ui.screens.hardwallet.frozen-card.view :as frozen-card.view])
(:require-macros [status-im.utils.views :refer [defview letsubs]]))
;; NOTE(Ferossgp): Seems like it should be in popover
@ -208,74 +211,199 @@
[photos/photo (multiaccounts/displayed-photo account)
{:size (if small-screen? 45 61)}])}))
(defview login-pin []
(defn access-is-reset [{:keys [hide-login-actions?]}]
[react/view
{:style {:flex 1
:align-items :center}}
[react/view
{:style {:flex 1
:align-items :center
:justify-content :center}}
[react/view
{:style
{:background-color colors/green-transparent-10
:margin-bottom 32
:width 40
:height 40
:align-items :center
:justify-content :center
:border-radius 20}}
[vector-icons/icon
:main-icons/check
{:color colors/green}]]
[react/text {:style {:typography :header}}
(i18n/label :t/keycard-access-reset)]
[react/text (i18n/label :t/keycard-can-use-with-new-passcode)]]
(when-not hide-login-actions?
[react/view
{:style {:width 160
:margin-bottom 15}}
[button/button
{:type :main
:style {:align-self :stretch}
:container-style {:height 52}
:label (i18n/label :t/open)
:on-press #(re-frame/dispatch
[::hardwallet.login/login-after-reset])}]])])
(defn frozen-card []
[frozen-card.view/frozen-card
{:show-dismiss-button? false}])
(defn blocked-card []
[react/view {:style {:flex 1
:align-items :center}}
[react/view {:margin-top 24
:margin-horizontal 24
:align-items :center}
[react/view {:background-color colors/red-transparent-10
:width 32
:height 32
:border-radius 16
:align-items :center
:justify-content :center}
[vector-icons/icon
:main-icons/cancel
{:color colors/red
:width 20
:height 20}]]
[react/text {:style {:typography :title-bold
:margin-top 16
:margin-bottom 8}}
(i18n/label :t/keycard-is-blocked-title)]
[react/text {:style {:color colors/gray
:text-align :center}}
(i18n/label :t/keycard-is-blocked-details)]
[react/text "\n"]
[react/nested-text
{:style {:color colors/gray
:text-align :center}}
(i18n/label :t/keycard-is-blocked-instructions)
[{} " "]
[{:style {:color colors/blue}
:on-press #(.openURL ^js react/linking "https://status.im/faq/#keycard")}
(i18n/label :t/learn-more)]]]])
(defn- step-view [step]
[react/view
{:style {:flex 1
:justify-content :center
:align-items :center}}
[react/text {:style {:typography :title-bold :text-align :center}}
(i18n/label :t/keycard-reset-passcode)]
[react/text {:style {:color colors/gray}}
(i18n/label :t/keycard-enter-new-passcode {:step step})]])
(defview login-pin [{:keys [back-button-handler
hide-login-actions?
default-enter-step]
:or {default-enter-step :login}}]
(letsubs [pin [:hardwallet/pin]
enter-step [:hardwallet/pin-enter-step]
status [:hardwallet/pin-status]
error-label [:hardwallet/pin-error-label]
{:keys [name] :as account} [:multiaccounts/login]
login-multiaccount [:multiaccounts/login]
multiaccount [:multiaccount]
small-screen? [:dimensions/small-screen?]
retry-counter [:hardwallet/retry-counter]]
[react/view styles/container
[topbar/topbar
{:accessories [{:icon :main-icons/more
:handler #(re-frame/dispatch [:keycard.login.pin.ui/more-icon-pressed])}]
:navigation
{:icon :main-icons/back
:accessibility-label :back-button
:handler #(re-frame/dispatch [:keycard.login.pin.ui/cancel-pressed])}}]
[react/view {:flex 1
:flex-direction :column
:justify-content :space-between
:align-items :center}
[react/view {:flex-direction :column
:justify-content :center
:align-items :center
:height 140}
[react/view {:margin-horizontal 16
:flex-direction :column}
[react/view {:justify-content :center
(let [{:keys [name] :as account} (or login-multiaccount multiaccount)
;; TODO(rasom): this hack fixes state mess when more then two
;; pin-view instances are used at the same time. Should be properly
;; refactored instead
enter-step (or enter-step default-enter-step)]
[react/view styles/container
[topbar/topbar
{:accessories [(when-not hide-login-actions?
{:icon :main-icons/more
:handler #(re-frame/dispatch [:keycard.login.pin.ui/more-icon-pressed])})]
:content (cond
(= :reset enter-step)
[step-view 1]
(= :reset-confirmation enter-step)
[step-view 2]
(and (= :puk enter-step)
(not= :blocked-card status))
[react/view
{:style {:flex 1
:justify-content :center
:align-items :center}}
[react/text {:style {:color colors/gray}}
(i18n/label :t/enter-puk-code)]])
:navigation
{:icon :main-icons/back
:accessibility-label :back-button
:handler #(re-frame/dispatch
[(or back-button-handler
:keycard.login.pin.ui/cancel-pressed)])}}]
[react/view {:flex 1
:flex-direction :column
:justify-content :space-between
:align-items :center}
[react/view {:flex-direction :column
:justify-content :center
:align-items :center
:flex-direction :row}
[react/view {:width (if small-screen? 50 69)
:height (if small-screen? 50 69)
:justify-content :center
:align-items :center}
[photo account small-screen?]
[react/view {:justify-content :center
:align-items :center
:width (if small-screen? 18 24)
:height (if small-screen? 18 24)
:border-radius (if small-screen? 18 24)
:position :absolute
:right 0
:bottom 0
:background-color colors/white
:border-width 1
:border-color colors/black-transparent}
[react/image {:source (resources/get-image :keycard-key)
:style {:width (if small-screen? 6 8)
:height (if small-screen? 11 14)}}]]]]
[react/text {:style {:text-align :center
:margin-top (if small-screen? 8 12)
:color colors/black
:font-weight "500"}
:number-of-lines 1
:ellipsize-mode :middle}
name]]]
[pin.views/pin-view
{:pin pin
:retry-counter retry-counter
:small-screen? small-screen?
:status status
:error-label error-label
:step enter-step
:save-password-checkbox? true}]
[react/view {:margin-bottom (if small-screen? 25 32)}
[react/touchable-highlight
{:on-press #(re-frame/dispatch [:multiaccounts.recover.ui/recover-multiaccount-button-pressed])}
[react/text {:style {:color colors/blue}}
(i18n/label :t/recover-key)]]]]]))
:height 140}
[react/view {:margin-horizontal 16
:flex-direction :column}
[react/view {:justify-content :center
:align-items :center
:flex-direction :row}
[react/view {:width (if small-screen? 50 69)
:height (if small-screen? 50 69)
:justify-content :center
:align-items :center}
[photo account small-screen?]
[react/view {:justify-content :center
:align-items :center
:width (if small-screen? 18 24)
:height (if small-screen? 18 24)
:border-radius (if small-screen? 18 24)
:position :absolute
:right 0
:bottom 0
:background-color colors/white
:border-width 1
:border-color colors/black-transparent}
[react/image {:source (resources/get-image :keycard-key)
:style {:width (if small-screen? 6 8)
:height (if small-screen? 11 14)}}]]]]
[react/text {:style {:text-align :center
:margin-top (if small-screen? 8 12)
:color colors/black
:font-weight "500"}
:number-of-lines 1
:ellipsize-mode :middle}
name]]]
(cond
(= :after-unblocking status)
[access-is-reset
{:hide-login-actions? hide-login-actions?}]
(= :frozen-card status)
[frozen-card]
(= :blocked-card status)
[blocked-card]
:else
[pin.views/pin-view
{:pin pin
:retry-counter retry-counter
:small-screen? small-screen?
:status status
:error-label error-label
:step enter-step
:save-password-checkbox? (not (contains?
#{:reset :reset-confirmation :puk}
enter-step))}])
(when-not hide-login-actions?
[react/view {:margin-bottom (if small-screen? 25 32)}
[react/touchable-highlight
{:on-press #(re-frame/dispatch [:multiaccounts.recover.ui/recover-multiaccount-button-pressed])}
[react/text {:style {:color colors/blue}}
(i18n/label :t/recover-key)]]])]])))
(defn- more-sheet-content []
[react/view {:flex 1}

View File

@ -12,7 +12,8 @@
[status-im.ui.screens.multiaccounts.recover.views :as multiaccounts.recover]
[status-im.ui.screens.signing.views :as signing]
[status-im.ui.screens.biometric.views :as biometric]
[status-im.ui.components.colors :as colors]))
[status-im.ui.components.colors :as colors]
[status-im.ui.screens.hardwallet.frozen-card.view :as frozen-card]))
(defn hide-panel-anim
[bottom-anim-value alpha-value window-height]
@ -137,6 +138,9 @@
(= :transaction-data view)
[signing/transaction-data]
(= :frozen-card view)
[frozen-card/frozen-card]
:else
[view])]]]]])))})))

View File

@ -126,5 +126,7 @@
:component hardwallet.settings/keycard-settings}
{:name :reset-card
:component hardwallet.settings/reset-card}
{:name :keycard-pin
:component hardwallet.settings/reset-pin}
{:name :enter-pin-settings
:component hardwallet.pin/enter-pin}]])

View File

@ -96,14 +96,15 @@
enter-step [:hardwallet/pin-enter-step]
status [:hardwallet/pin-status]
retry-counter [:hardwallet/retry-counter]]
[react/view
[pin.views/pin-view
{:pin pin
:retry-counter retry-counter
:step enter-step
:small-screen? small-screen?
:status status
:error-label error-label}]]))
(let [enter-step (or enter-step :sign)]
[react/view
[pin.views/pin-view
{:pin pin
:retry-counter retry-counter
:step enter-step
:small-screen? small-screen?
:status status
:error-label error-label}]])))
(defn sign-with-keycard-button
[amount-error gas-error]

View File

@ -12,7 +12,8 @@
[status-im.ui.screens.wallet.accounts.sheets :as sheets]
[status-im.ui.screens.wallet.accounts.styles :as styles]
[status-im.utils.utils :as utils.utils]
[status-im.wallet.utils :as wallet.utils])
[status-im.wallet.utils :as wallet.utils]
[status-im.hardwallet.login :as hardwallet.login])
(:require-macros [status-im.utils.views :as views]))
(views/defview account-card [{:keys [name color address type] :as account}]
@ -130,14 +131,21 @@
(views/letsubs [currency [:wallet/currency]
portfolio-value [:portfolio-value]
empty-balances? [:empty-balances?]
frozen-card? [:hardwallet/frozen-card?]
{:keys [mnemonic]} [:multiaccount]]
[reanimated/view {:style (styles/container {:minimized minimized})}
(when (and mnemonic minimized (not empty-balances?))
(when (or
(and frozen-card? minimized)
(and mnemonic minimized (not empty-balances?)))
[reanimated/view {:style (styles/accounts-mnemonic {:animation animation})}
[react/touchable-highlight
{:on-press #(re-frame/dispatch [:navigate-to :profile-stack {:screen :backup-seed
:initial false}])}
[react/view {:flex-direction :row :align-items :center}
{:on-press #(re-frame/dispatch
(if frozen-card?
[::hardwallet.login/reset-pin]
[:navigate-to :profile-stack {:screen :backup-seed
:initial false}]))}
[react/view {:flex-direction :row
:align-items :center}
[react/view {:width 14
:height 14
:background-color colors/gray
@ -151,7 +159,9 @@
"!"]]
[react/text {:style {:color colors/gray}
:accessibility-label :back-up-your-seed-phrase-warning}
(i18n/label :t/back-up-your-seed-phrase)]]]])
(if frozen-card?
(i18n/label :t/your-card-is-frozen)
(i18n/label :t/back-up-your-seed-phrase))]]]])
[reanimated/view {:style (styles/value-container {:minimized minimized
:animation animation})

View File

@ -17,7 +17,8 @@
[status-im.ethereum.core :as ethereum]
[status-im.utils.security :as security]
[clojure.string :as string]
[status-im.utils.platform :as platform]))
[status-im.utils.platform :as platform]
[status-im.ui.components.bottom-panel.views :as bottom-panel]))
(defn- request-camera-permissions []
(let [options {:handler :wallet.add-new/qr-scanner-result}]
@ -127,23 +128,40 @@
(re-frame/dispatch [:set-in [:add-account :private-key] (security/mask-data %)]))}])])
(defview pin []
(letsubs [pin [:hardwallet/pin]
status [:hardwallet/pin-status]
error-label [:hardwallet/pin-error-label]]
(letsubs [pin [:hardwallet/pin]
status [:hardwallet/pin-status]
error-label [:hardwallet/pin-error-label]
retry-counter [:hardwallet/retry-counter]]
[react/keyboard-avoiding-view {:style {:flex 1}}
[topbar/topbar
{:navigation :none
:accessories
[{:label :t/cancel
:handler #(re-frame/dispatch [:bottom-sheet/hide])}]}]
:handler #(re-frame/dispatch [:hardwallet/new-account-pin-sheet-hide])}]}]
[pin.views/pin-view
{:pin pin
:status status
:retry-counter retry-counter
:title-label :t/current-pin
:description-label :t/current-pin-description
:error-label error-label
:step :export-key}]]))
(defn pin-sheet []
(let [show-sheet? @(re-frame/subscribe [:hardwallet/new-account-sheet?])
{window-height :height} @(re-frame/subscribe [:dimensions/window])]
[bottom-panel/bottom-panel
show-sheet?
(fn [_]
[react/view {:style
{:background-color colors/white
:border-top-right-radius 16
:border-top-left-radius 16
:padding-bottom 40
:flex 1}}
[pin]])
window-height]))
(defview add-account []
(letsubs [{:keys [type account] :as add-account} [:add-account]
add-account-disabled? [:add-account-disabled?]
@ -164,7 +182,8 @@
:label :t/add-account
:accessibility-label :add-account-add-account-button
:on-press
(if keycard?
(if (and keycard?
(not= type :watch))
#(re-frame/dispatch [:hardwallet/new-account-pin-sheet
{:view {:content pin
:height 256}}])
@ -177,5 +196,6 @@
(and
(not keycard?)
(not (spec/valid? ::multiaccounts.db/password
@entered-password)))))}}]]))
@entered-password)))))}}]
[pin-sheet]]))

View File

@ -543,11 +543,15 @@
"joined-group-chat-description": "You've joined {{group-name}} from invitation by {{username}}",
"key": "Key",
"keycard": "Keycard",
"keycard-access-reset": "Keycard access is reset",
"keycard-can-use-with-new-passcode": "You can use this card with your new passcode",
"keycard-applet-install-instructions": "To install the applet please follow the instructions on https://github.com/status-im/keycard-cli#keycard-applet-installation",
"keycard-blocked": "Keycard has been blocked.\nYou need to reset card to continue using it.",
"keycard-cancel-setup-text": "This will cancel keycard setup. It's highly recommended to finish the setup in order to use keycard. Do you really want to cancel?",
"keycard-cancel-setup-title": "Dangerous operation",
"keycard-desc": "Own a Keycard? Store your keys on it; youll need it for transactions",
"keycard-reset-passcode": "Reset passcode",
"keycard-enter-new-passcode": "Enter new passcode {{step}}/2",
"keycard-has-multiaccount-on-it": "This card is full. Each card can hold one main keypair",
"keycard-onboarding-finishing-header": "Finishing up",
"keycard-onboarding-intro-header": "Store your keys on Keycard",
@ -576,6 +580,13 @@
"keycard-recovery-phrase-confirmation-title": "Written the seed phrase down?",
"keycard-recovery-success-header": "Your keys have been\n successfully recovered",
"keycard-unauthorized-operation": "You're unauthorized to perform this operation.\n Please tap valid card and try again.",
"keycard-is-frozen-title": "Keycard is frozen",
"keycard-is-frozen-details": "To protect your assets, your card is frozen. Reset card access to unlock keys and send transactions. Create a new passcode and enter your PUK to access your account(s) on this card",
"keycard-is-frozen-reset": "Reset card access",
"your-card-is-frozen": "Your Keycard is frozen. Reset card access",
"keycard-is-blocked-title": "Keycard is blocked",
"keycard-is-blocked-details": "You can no longer use this card to access or sign for this account. There have been too many failed passcode and PUK attempts.",
"keycard-is-blocked-instructions": "To access your account reinstall Status and use a new Keycard, use a different wallet or reset Keycard manually.",
"language": "Language",
"learn-more": "Learn more",
"learn-more-about-keycard": "Learn more about Keycard",
@ -772,7 +783,12 @@
"pin-changed": "6-digit passcode has been changed",
"pin-code": "6-digit passcode",
"pin-mismatch": "Wrong passcode",
"pin-retries-left": "You have {{number}} retries left",
"pin-retries-left": "{{number}} attemps left",
"pin-one-attempt-blocked-before": "Be careful, you have only",
"pin-one-attempt-frozen-before": "Be careful, you have only",
"pin-one-attempt": " one attempt ",
"pin-one-attempt-blocked-after": "before your Keycard gets blocked",
"pin-one-attempt-frozen-after": "before your Keycard gets frozen",
"preview-privacy": "Preview privacy mode",
"privacy": "Privacy",
"privacy-and-security": "Privacy and security",
@ -789,7 +805,7 @@
"puk-and-pairing-codes-displayed": "PUK and pairing codes displayed",
"puk-code": "PUK code",
"puk-code-explanation": "If you forget your 6-digit passcode or enter it incorrectly 3 times, you'll need this code to unlock your card.",
"puk-mismatch": "PUK code does not match",
"puk-mismatch": "Wrong PUK code",
"quiet-days": "{{quiet-days}} days",
"quiet-hours": "{{quiet-hours}} hours",
"re-encrypt-key": "Re-encrypt your keys",