From bae935d3e47eacee77d8b843ceff834663e202ab Mon Sep 17 00:00:00 2001 From: Dmitry Novotochinov Date: Fri, 8 Feb 2019 16:30:44 +0300 Subject: [PATCH] [#7217] check card state before installation Signed-off-by: Dmitry Novotochinov --- mobile_files/package.json.orig | 2 +- mobile_files/yarn.lock | 6 +- resources/icons/main/keycard.svg | 8 +- src/status_im/events.cljs | 61 ++-- src/status_im/hardwallet/card.cljs | 18 +- src/status_im/hardwallet/core.cljs | 326 +++++++++++++----- src/status_im/hardwallet/fx.cljs | 35 +- src/status_im/init/core.cljs | 30 +- .../ui/screens/hardwallet/components.cljs | 47 ++- .../ui/screens/hardwallet/pin/views.cljs | 16 +- .../ui/screens/hardwallet/settings/views.cljs | 13 +- .../ui/screens/hardwallet/setup/styles.cljs | 17 +- .../ui/screens/hardwallet/setup/subs.cljs | 7 +- .../ui/screens/hardwallet/setup/views.cljs | 82 +++-- translations/en.json | 15 +- 15 files changed, 419 insertions(+), 264 deletions(-) diff --git a/mobile_files/package.json.orig b/mobile_files/package.json.orig index 4fc787a287..c8896e107f 100644 --- a/mobile_files/package.json.orig +++ b/mobile_files/package.json.orig @@ -55,7 +55,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.4", + "react-native-status-keycard": "git+https://github.com/status-im/react-native-status-keycard.git#v2.3.7", "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", diff --git a/mobile_files/yarn.lock b/mobile_files/yarn.lock index b8cf449352..19656e3615 100644 --- a/mobile_files/yarn.lock +++ b/mobile_files/yarn.lock @@ -6010,9 +6010,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.4": - version "2.3.4" - resolved "git+https://github.com/status-im/react-native-status-keycard.git#04b0fb6fcbe588bd7fa076e1b0379e661bf1bddc" +"react-native-status-keycard@git+https://github.com/status-im/react-native-status-keycard.git#v2.3.7": + version "2.3.7" + resolved "git+https://github.com/status-im/react-native-status-keycard.git#9e6df66bfd9288584031cff5455445230059720b" react-native-svg@6.5.2: version "6.5.2" diff --git a/resources/icons/main/keycard.svg b/resources/icons/main/keycard.svg index 58d9676bf0..970cc53bd5 100644 --- a/resources/icons/main/keycard.svg +++ b/resources/icons/main/keycard.svg @@ -1,7 +1,3 @@ - - - - - - + + \ No newline at end of file diff --git a/src/status_im/events.cljs b/src/status_im/events.cljs index 87a886ff08..530431d33c 100644 --- a/src/status_im/events.cljs +++ b/src/status_im/events.cljs @@ -893,22 +893,16 @@ ;; hardwallet module -(handlers/register-handler-fx - :hardwallet.ui/get-application-info - (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 (fn [cofx [_ listeners]] (hardwallet/on-register-card-events cofx listeners))) +(handlers/register-handler-fx + :hardwallet/get-application-info + (fn [_ _] + {:hardwallet/get-application-info nil})) + (handlers/register-handler-fx :hardwallet.callback/on-get-application-info-success (fn [cofx [_ info on-success]] @@ -939,6 +933,16 @@ (fn [cofx [_ data]] (hardwallet/on-card-disconnected cofx data))) +(handlers/register-handler-fx + :hardwallet.callback/on-init-card-success + (fn [cofx [_ secrets]] + (hardwallet/on-init-card-success cofx secrets))) + +(handlers/register-handler-fx + :hardwallet.callback/on-init-card-error + (fn [cofx [_ error]] + (hardwallet/on-init-card-error cofx error))) + (handlers/register-handler-fx :hardwallet.callback/on-install-applet-and-init-card-success (fn [cofx [_ secrets]] @@ -1047,6 +1051,11 @@ (fn [cofx _] (hardwallet/login-with-keycard cofx false))) +(handlers/register-handler-fx + :hardwallet/check-card-state + (fn [cofx _] + (hardwallet/check-card-state cofx))) + (handlers/register-handler-fx :hardwallet.callback/on-get-keys-error (fn [cofx [_ error]] @@ -1067,32 +1076,15 @@ (fn [_ _] {:hardwallet/open-nfc-settings nil})) -(handlers/register-handler-fx - :hardwallet.ui/hold-card-button-pressed - (fn [{:keys [db] :as cofx} _] - (fx/merge cofx - {:db (assoc-in db [:hardwallet :setup-step] :begin)} - (navigation/navigate-to-cofx :hardwallet-setup nil)))) - (handlers/register-handler-fx :hardwallet.ui/begin-setup-button-pressed - (fn [_ _] - {:ui/show-confirmation {:title "" - :content (i18n/label :t/begin-keycard-setup-confirmation-text) - :confirm-button-text (i18n/label :t/yes) - :cancel-button-text (i18n/label :t/no) - :on-accept #(re-frame/dispatch [:hardwallet.ui/begin-setup-confirm-button-pressed]) - :on-cancel #()}})) + (fn [cofx _] + (hardwallet/begin-setup-button-pressed cofx))) (handlers/register-handler-fx - :hardwallet.ui/begin-setup-confirm-button-pressed + :hardwallet/start-installation (fn [cofx _] - (hardwallet/load-preparing-screen cofx))) - -(handlers/register-handler-fx - :hardwallet/install-applet-and-init-card - (fn [cofx _] - (hardwallet/install-applet-and-init-card cofx))) + (hardwallet/start-installation cofx))) (handlers/register-handler-fx :hardwallet.ui/pair-card-button-pressed @@ -1102,11 +1094,12 @@ (handlers/register-handler-fx :hardwallet.ui/pair-code-input-changed (fn [{:keys [db]} [_ pair-code]] - {:db (assoc-in db [:hardwallet :pair-code] pair-code)})) + {:db (assoc-in db [:hardwallet :secrets :password] pair-code)})) (handlers/register-handler-fx :hardwallet.ui/pair-code-next-button-pressed - (fn [{:keys [db]} _])) + (fn [cofx] + (hardwallet/pair cofx))) (handlers/register-handler-fx :hardwallet.ui/recovery-phrase-next-button-pressed diff --git a/src/status_im/hardwallet/card.cljs b/src/status_im/hardwallet/card.cljs index 5d3570ba7b..449d9bc6a4 100644 --- a/src/status_im/hardwallet/card.cljs +++ b/src/status_im/hardwallet/card.cljs @@ -56,10 +56,24 @@ (then #(re-frame/dispatch [:hardwallet.callback/on-get-application-info-success % on-success])) (catch #(re-frame/dispatch [:hardwallet.callback/on-get-application-info-error (error-object->map %)])))) -(defn install-applet-and-init-card [] +(defn install-applet [] (when config/hardwallet-enabled? (.. keycard - installAppletAndInitCard + installApplet + (then #(re-frame/dispatch [:hardwallet.callback/on-install-applet-success %])) + (catch #(re-frame/dispatch [:hardwallet.callback/on-install-applet-error (error-object->map %)]))))) + +(defn init-card [pin] + (when config/hardwallet-enabled? + (.. keycard + (init pin) + (then #(re-frame/dispatch [:hardwallet.callback/on-init-card-success %])) + (catch #(re-frame/dispatch [:hardwallet.callback/on-init-card-error (error-object->map %)]))))) + +(defn install-applet-and-init-card [pin] + (when config/hardwallet-enabled? + (.. keycard + (installAppletAndInitCard pin) (then #(re-frame/dispatch [:hardwallet.callback/on-install-applet-and-init-card-success %])) (catch #(re-frame/dispatch [:hardwallet.callback/on-install-applet-and-init-card-error (error-object->map %)]))))) diff --git a/src/status_im/hardwallet/core.cljs b/src/status_im/hardwallet/core.cljs index 365f871ad8..fca08fba9d 100644 --- a/src/status_im/hardwallet/core.cljs +++ b/src/status_im/hardwallet/core.cljs @@ -16,20 +16,34 @@ (def default-pin "000000") -(defn get-pairing [db] - (or - (get-in db [:hardwallet :secrets :pairing]) - (get-in db [:account/account :pairing]))) +(defn- find-account-by-keycard-instance-uid + [db keycard-instance-uid] + (->> (:accounts/accounts db) + vals + (filter #(= keycard-instance-uid (:keycard-instance-uid %))) + first)) + +(defn get-pairing + ([db] + (get-pairing db nil)) + ([db instance-uid] + (or + (get-in db [:account/account :keycard-pairing]) + (get-in db [:hardwallet :secrets :pairing]) + (when instance-uid + (:keycard-pairing + (find-account-by-keycard-instance-uid db instance-uid)))))) (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)]})) + [{:keys [db]} {:keys [remove-instance-uid?]}] + (let [account (cond-> (:account/account db) + true (assoc :keycard-pairing nil + :keycard-paired-on nil) + remove-instance-uid? (assoc :keycard-instance-uid nil))] + {:db (-> db + (assoc :account/account account) + (assoc-in [:accounts/accounts (:address account)] account)) + :data-store/base-tx [(accounts-store/save-account-tx account)]})) (defn hardwallet-supported? [{:keys [db]}] (and config/hardwallet-enabled? @@ -44,6 +58,74 @@ :content (i18n/label :t/keycard-unauthorized-operation)}} (navigation/navigate-to-cofx :keycard-settings nil))) +(fx/defn show-no-keycard-applet-alert [_] + {:utils/show-confirmation {:title (i18n/label :t/no-keycard-applet-on-card) + :content (i18n/label :t/keycard-applet-will-be-installed) + :cancel-button-text "" + :confirm-button-text :t/next}}) + +(fx/defn show-keycard-has-account-alert + [{:keys [db] :as cofx}] + (fx/merge cofx + {:utils/show-confirmation {:title nil + :content (i18n/label :t/keycard-has-account-on-it) + :cancel-button-text "" + :confirm-button-text :t/okay}} + (if (empty? (:accounts/accounts db)) + (navigation/navigate-to-cofx :intro nil) + (navigation/navigate-to-cofx :accounts nil)))) + +(defn- card-state->setup-step [state] + (case state + :not-paired :pair + :no-pairing-slots :no-slots + :begin)) + +(defn- get-card-state + [{:keys [has-master-key? + applet-installed? + initialized? + free-pairing-slots + paired?]}] + (cond + + (and (not paired?) + (zero? free-pairing-slots)) + :no-pairing-slots + + (and (not paired?) + has-master-key? + (pos? free-pairing-slots)) + :not-paired + + (not applet-installed?) + :blank + + (not initialized?) + :pre-init + + (not has-master-key?) + :init + + has-master-key? + :account)) + +(fx/defn set-setup-step + [{:keys [db]} card-state] + {:db (assoc-in db [:hardwallet :setup-step] (card-state->setup-step card-state))}) + +(fx/defn check-card-state + [{:keys [db] :as cofx}] + (let [app-info (get-in db [:hardwallet :application-info]) + card-state (get-card-state app-info)] + (fx/merge cofx + {:db (assoc-in db [:hardwallet :card-state] card-state)} + (when (= card-state :blank) + (show-no-keycard-applet-alert)) + (if (= card-state :account) + (show-keycard-has-account-alert) + (set-setup-step card-state))))) + (fx/defn navigate-to-keycard-settings [{:keys [db] :as cofx}] (fx/merge cofx @@ -104,15 +186,19 @@ [{:keys [db]}] {:db (assoc-in db [:hardwallet :on-card-read] nil)}) +(fx/defn dispatch-event + [_ event] + {:dispatch [event]}) + (fx/defn on-get-application-info-success - [{:keys [db]} info on-success] + [{:keys [db] :as cofx} info on-success] (let [info' (js->clj info :keywordize-keys true) {:keys [pin-retry-counter puk-retry-counter]} info' connect-screen? (= (:view-id db) :hardwallet-connect) enter-step (if (zero? pin-retry-counter) :puk (get-in db [:hardwallet :pin :enter-step]))] - (fx/merge {} + (fx/merge cofx {:db (-> db (assoc-in [:hardwallet :pin :enter-step] enter-step) (assoc-in [:hardwallet :application-info] info') @@ -123,22 +209,26 @@ (if (zero? puk-retry-counter) (navigation/navigate-to-cofx :keycard-settings nil) (when on-success - #(assoc % :dispatch [on-success])))))) + (dispatch-event on-success)))))) (fx/defn on-get-application-info-error [{:keys [db] :as cofx} error] (log/debug "[hardwallet] application info error " error) (let [on-card-read (get-in db [:hardwallet :on-card-read]) + connect-screen? (= (:view-id db) :hardwallet-connect) login? (= on-card-read :hardwallet/login-with-keycard)] (if login? (fx/merge cofx - {:db (assoc-in db [:hardwallet :on-card-read] nil) - :utils/show-popup {:title (i18n/label :t/wrong-card) + {:utils/show-popup {:title (i18n/label :t/wrong-card) :content (i18n/label :t/wrong-card-text)}} + (clear-on-card-read) (navigation/navigate-to-cofx :accounts nil)) - {:db (-> db - (assoc-in [:hardwallet :application-info-error] error) - (assoc-in [:hardwallet :application-info :applet-installed?] false))}))) + (fx/merge cofx + {:db (assoc-in db [:hardwallet :application-info-error] error)} + (when-not connect-screen? + (clear-on-card-read)) + (when on-card-read + (dispatch-event on-card-read)))))) (fx/defn set-nfc-support [{:keys [db]} supported?] @@ -155,7 +245,7 @@ :db (-> db (assoc-in [:hardwallet :setup-step] :begin) (assoc-in [:hardwallet :on-card-connected] nil) - (assoc-in [:hardwallet :on-card-read] nil) + (assoc-in [:hardwallet :on-card-read] :hardwallet/check-card-state) (assoc-in [:hardwallet :pin :on-verified] nil))} (navigation/navigate-to-cofx :hardwallet-connect nil))) @@ -246,7 +336,7 @@ :on-verified nil})) :utils/show-popup {:title "" :content (i18n/label :t/card-reseted)}} - (remove-pairing-from-account) + (remove-pairing-from-account {:remove-instance-uid? true}) (navigation/navigate-to-cofx :keycard-settings nil))) (fx/defn on-delete-error @@ -294,25 +384,20 @@ (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/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)] @@ -336,16 +421,24 @@ (defn- proceed-to-pin-confirmation [fx] (assoc-in fx [:db :hardwallet :pin :enter-step] :confirmation)) +(fx/defn load-preparing-screen + [{:keys [db]}] + {:db (assoc-in db [:hardwallet :setup-step] :preparing) + :dispatch [:hardwallet/start-installation]}) + (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}}))) + current-pin (vector->string (get-in db [:hardwallet :pin :current])) + setup-step (get-in db [:hardwallet :setup-step])] + (if (= setup-step :pin) + (load-preparing-screen fx) + (fx/merge fx + {:db (assoc-in db [:hardwallet :pin :status] :verifying) + :hardwallet/change-pin {:new-pin new-pin + :current-pin current-pin + :pairing pairing}})))) (fx/defn dispatch-on-verified-event [{:keys [db]} event] @@ -385,7 +478,7 @@ {:db (-> db (update-in [:hardwallet :pin] merge {:status nil :error-label nil}))} - (when (not= on-verified :hardwallet/unpair) + (when-not (contains? #{:hardwallet/unpair :hardwallet/unpair-and-delete} on-verified) (get-application-info pairing)) (when on-verified (dispatch-on-verified-event on-verified))))) @@ -428,15 +521,15 @@ (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) + {: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 nil) (navigation/navigate-to-cofx :keycard-settings nil))) (fx/defn on-unpair-error @@ -501,7 +594,8 @@ (fx/defn get-keys-from-keycard [{:keys [db]}] - (let [{:keys [pairing]} (get-in db [:hardwallet :secrets]) + (let [account-address (get-in db [:accounts/login :address]) + pairing (get-in db [:accounts/accounts account-address :keycard-pairing]) pin (string/join (get-in db [:hardwallet :pin :login]))] (when (and pairing (not (empty? pin))) @@ -576,23 +670,17 @@ (let [{:keys [pairing]} (get-in cofx [:db :hardwallet :secrets])] {:hardwallet/generate-mnemonic {:pairing pairing}})) -(fx/defn dispatch-on-card-connected-event - [_ event] - {:dispatch [event]}) - (defn login-with-keycard [{:keys [db] :as cofx} auto-login?] - (let [pairing (get-pairing db) - account-login-address (get-in db [:accounts/login :address]) + (let [account-login-address (get-in db [:accounts/login :address]) account-was-manually-selected? account-login-address account-instance-uid (get-in db [:accounts/accounts account-login-address :keycard-instance-uid]) keycard-instance-uid (get-in db [:hardwallet :application-info :instance-uid]) + account (find-account-by-keycard-instance-uid db keycard-instance-uid) account-mismatch? (if account-was-manually-selected? (not= account-instance-uid keycard-instance-uid) - (->> (:accounts/accounts db) - vals - (filter #(= keycard-instance-uid (:keycard-instance-uid %))) - empty?))] + (nil? account)) + pairing (:keycard-pairing account)] (cond (empty? keycard-instance-uid) @@ -613,7 +701,9 @@ :content (i18n/label :t/no-pairing-on-device)}} auto-login? - (navigation/navigate-to-cofx cofx :enter-pin nil) + (fx/merge cofx + {:db (assoc db :accounts/login (select-keys account [:address :name :photo-path]))} + (navigation/navigate-to-cofx :enter-pin nil)) :else (get-keys-from-keycard cofx)))) @@ -622,27 +712,31 @@ [{: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-step (get-in db [:hardwallet :setup-step]) + setup-running? (boolean setup-step) pin-enter-step (get-in db [:hardwallet :pin :enter-step]) login? (= :login pin-enter-step) accounts-screen? (= :accounts (:view-id db)) auto-login? accounts-screen? + instance-uid (get-in db [:hardwallet :application-info :instance-uid]) + should-read-instance-uid? (nil? instance-uid) on-card-connected (get-in db [:hardwallet :on-card-connected]) - on-card-read (if auto-login? - :hardwallet/auto-login - (get-in db [:hardwallet :on-card-read])) - pairing (get-pairing db)] + on-card-read (cond + auto-login? :hardwallet/auto-login + should-read-instance-uid? :hardwallet/get-application-info + :else (get-in db [:hardwallet :on-card-read])) + pairing (get-pairing db instance-uid)] (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 :card-read-in-progress?] (boolean on-card-read)) true (assoc-in [:hardwallet :return-to-step] nil)) - :hardwallet/get-application-info {:pairing pairing + :hardwallet/get-application-info {:pairing pairing :on-success on-card-read}} (when (and on-card-connected (not login?)) - (dispatch-on-card-connected-event on-card-connected)) + (dispatch-event on-card-connected)) (when setup-running? (navigation/navigate-to-cofx :hardwallet-setup nil))))) @@ -659,14 +753,33 @@ on-card-connected) (navigation/navigate-to-cofx :hardwallet-connect nil))))) -(fx/defn load-preparing-screen +(fx/defn begin-setup-button-pressed [{:keys [db]}] - {:db (assoc-in db [:hardwallet :setup-step] :preparing) - :dispatch [:hardwallet/install-applet-and-init-card]}) + {:db (-> db + (assoc-in [:hardwallet :setup-step] :pin) + (assoc-in [:hardwallet :pin :enter-step] :original) + (assoc-in [:hardwallet :pin :original] []) + (assoc-in [:hardwallet :pin :confirmation] []))}) -(fx/defn install-applet-and-init-card +(fx/defn start-installation [{:keys [db]}] - {:hardwallet/install-applet-and-init-card nil}) + (let [card-state (get-in db [:hardwallet :card-state]) + pin (vector->string (get-in db [:hardwallet :pin :original]))] + (case card-state + + :blank + {:hardwallet/install-applet-and-init-card pin} + + :pre-init + {:hardwallet/init-card pin} + + :init + {:hardwallet/install-applet-and-init-card pin} + + (do + (log/debug (str "Cannot start keycard installation from state: " card-state)) + {:utils/show-popup {:title (i18n/label :t/error) + :content (i18n/label :t/something-went-wrong)}})))) (fx/defn on-install-applet-and-init-card-success [{:keys [db]} secrets] @@ -675,6 +788,8 @@ (assoc-in [:hardwallet :setup-step] :secret-keys) (assoc-in [:hardwallet :secrets] secrets'))})) +(def on-init-card-success on-install-applet-and-init-card-success) + (defn- tag-lost-exception? [code] (= code "android.nfc.TagLostException")) @@ -692,23 +807,49 @@ (assoc-in [:hardwallet :setup-error] error))} (process-error code))) +(def on-init-card-error on-install-applet-and-init-card-error) + +(fx/defn set-account-pairing + [{:keys [db]} {:keys [address] :as account} pairing paired-on] + (let [account' (assoc account :keycard-pairing pairing + :keycard-paired-on paired-on)] + {:db (-> db + (assoc db :accounts/account account') + (assoc-in [:accounts/accounts address] account')) + :data-store/base-tx [(accounts-store/save-account-tx account')]})) + (fx/defn on-pairing-success - [{:keys [db]} 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)))}) + [{:keys [db] :as cofx} pairing] + (let [setup-step (get-in db [:hardwallet :setup-step]) + instance-uid (get-in db [:hardwallet :application-info :instance-uid]) + account (find-account-by-keycard-instance-uid db instance-uid) + paired-on (utils.datetime/timestamp) + next-step (if (= setup-step :enter-pair-code) + :begin + :card-ready)] + (fx/merge cofx + {:db (-> db + (assoc-in [:hardwallet :application-info :paired?] true) + (assoc-in [:hardwallet :setup-step] next-step) + (assoc-in [:hardwallet :secrets :pairing] pairing) + (assoc-in [:hardwallet :secrets :paired-on] paired-on))} + (when account + (set-account-pairing account pairing paired-on)) + (when (= next-step :begin) + (check-card-state))))) (fx/defn on-pairing-error [{:keys [db] :as cofx} {:keys [error code]}] (log/debug "[hardwallet] pairing error: " error) - (fx/merge cofx - {:db (-> db - (assoc-in [:hardwallet :return-to-step] :secret-keys) - (assoc-in [:hardwallet :setup-error] error))} - (process-error code))) + (let [setup-step (get-in db [:hardwallet :setup-step]) + next-step (if (= setup-step :enter-pair-code) + :enter-pair-code + :secret-keys)] + (fx/merge cofx + {:db (-> db + (assoc-in [:hardwallet :return-to-step] next-step) + (assoc-in [:hardwallet :setup-error] error))} + (process-error code)))) (fx/defn on-generate-mnemonic-success [{:keys [db]} mnemonic] @@ -766,7 +907,8 @@ (fx/defn generate-and-load-key [{:keys [db] :as cofx}] - (let [{:keys [mnemonic pairing pin]} (get-in db [:hardwallet :secrets])] + (let [{:keys [mnemonic pairing]} (get-in db [:hardwallet :secrets]) + pin (vector->string (get-in db [:hardwallet :pin :original]))] (fx/merge cofx {:hardwallet/generate-and-load-key {:mnemonic mnemonic :pairing pairing diff --git a/src/status_im/hardwallet/fx.cljs b/src/status_im/hardwallet/fx.cljs index ce75ef6986..fcd4618c2c 100644 --- a/src/status_im/hardwallet/fx.cljs +++ b/src/status_im/hardwallet/fx.cljs @@ -25,6 +25,10 @@ :hardwallet/install-applet-and-init-card card/install-applet-and-init-card) +(re-frame/reg-fx + :hardwallet/init-card + card/init-card) + (re-frame/reg-fx :hardwallet/register-card-events card/register-card-events) @@ -73,37 +77,6 @@ :hardwallet/get-keys card/get-keys) -;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")))) - (re-frame/reg-fx :hardwallet/login-with-keycard statusgo/login-with-keycard) diff --git a/src/status_im/init/core.cljs b/src/status_im/init/core.cljs index d707f9b669..1067b5caba 100644 --- a/src/status_im/init/core.cljs +++ b/src/status_im/init/core.cljs @@ -91,22 +91,20 @@ push-notifications/stored network/type] :node/keys [status] :or {network (get app-db :network)}} :db}] - ;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 []) - :node/status status - :network network - :network/type type - :hardwallet hardwallet - :device-UUID device-UUID - :view-id view-id - :push-notifications/stored stored)}) + {: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 []) + :node/status status + :network network + :network/type type + :hardwallet hardwallet + :device-UUID device-UUID + :view-id view-id + :push-notifications/stored stored)}) (fx/defn initialize-app [cofx encryption-key] diff --git a/src/status_im/ui/screens/hardwallet/components.cljs b/src/status_im/ui/screens/hardwallet/components.cljs index 2e1bdc75b5..79d5135989 100644 --- a/src/status_im/ui/screens/hardwallet/components.cljs +++ b/src/status_im/ui/screens/hardwallet/components.cljs @@ -36,28 +36,31 @@ "Close window"]]]]])) (def step-name - {:preparing {:label :t/initialization + {:pin {:label :t/pin-code :number 1 + :next-step :preparing} + :preparing {:label :t/initialization + :number 2 :next-step :secret-keys} :secret-keys {:label :t/puk-and-pair-codes - :number 2 + :number 3 :next-step :pairing} :pairing {:label :t/pairing - :number 3 + :number 4 :next-step :recovery-phrase} :card-ready {:label :t/pairing - :number 3 + :number 5 :next-step :recovery-phrase} :generating-mnemonic {:label :t/recovery-phrase - :number 4} + :number 5} :recovery-phrase-confirm-word1 {:label :t/recovery-phrase - :number 4} + :number 5} :recovery-phrase-confirm-word2 {:label :t/recovery-phrase - :number 4} + :number 5} :loading-keys {:label :t/recovery-phrase - :number 4} + :number 5} :recovery-phrase {:label :t/recovery-phrase - :number 4}}) + :number 5}}) (defn- setup-steps [step] (let [current-step (step-name step) @@ -96,21 +99,17 @@ (animation/start))) :display-name "maintain-card" :reagent-render (fn [step] [react/view styles/maintain-card-container - [react/touchable-highlight - {:on-press #(do - (re-frame/dispatch [:hardwallet.ui/get-application-info]) - (reset! modal-visible? true))} - [react/view styles/hardwallet-icon-container - [vector-icons/icon :main-icons/keycard {:color colors/blue}] - [vector-icons/icon :icons/indicator-small {:color colors/blue - :container-style (styles/hardwallet-icon-indicator-small-container - (interpolate-fn [0 0.5 1 0.5 0]))}] - [vector-icons/icon :icons/indicator-middle {:color colors/blue - :container-style (styles/hardwallet-icon-indicator-middle-container - (interpolate-fn [1 0.4 0 0.4 0.8]))}] - [vector-icons/icon :icons/indicator-big {:color colors/blue - :container-style (styles/hardwallet-icon-indicator-big-container - (interpolate-fn [0.5 0.8 0.5 0.8 0.4]))}]]] + [react/view styles/hardwallet-icon-container + [vector-icons/icon :main-icons/keycard {:color colors/blue}] + [vector-icons/icon :icons/indicator-small {:color colors/blue + :container-style (styles/hardwallet-icon-indicator-small-container + (interpolate-fn [0 0.5 1 0.5 0]))}] + [vector-icons/icon :icons/indicator-middle {:color colors/blue + :container-style (styles/hardwallet-icon-indicator-middle-container + (interpolate-fn [1 0.4 0 0.4 0.8]))}] + [vector-icons/icon :icons/indicator-big {:color colors/blue + :container-style (styles/hardwallet-icon-indicator-big-container + (interpolate-fn [0.5 0.8 0.5 0.8 0.4]))}]] [setup-steps step] [application-info modal-visible?]])}))) diff --git a/src/status_im/ui/screens/hardwallet/pin/views.cljs b/src/status_im/ui/screens/hardwallet/pin/views.cljs index 38efaf00c5..a8cc8fc739 100644 --- a/src/status_im/ui/screens/hardwallet/pin/views.cljs +++ b/src/status_im/ui/screens/hardwallet/pin/views.cljs @@ -104,6 +104,20 @@ (def pin-retries 3) (def puk-retries 5) +(defview create-pin [] + (letsubs [pin [:hardwallet/pin] + step [:hardwallet/pin-enter-step] + status [:hardwallet/pin-status] + error-label [:hardwallet/pin-error-label]] + [pin-view {:pin pin + :title-label (case step + :confirmation :t/repeat-pin + :t/create-a-pin) + :description-label :t/create-pin-description + :step step + :status status + :error-label error-label}])) + (defview enter-pin [] (letsubs [pin [:hardwallet/pin] step [:hardwallet/pin-enter-step] @@ -128,7 +142,7 @@ :title-label (case step :current :t/current-pin :login :t/current-pin - :original :t/create-pin + :original :t/create-a-pin :confirmation :t/repeat-pin :t/current-pin) :description-label (case step diff --git a/src/status_im/ui/screens/hardwallet/settings/views.cljs b/src/status_im/ui/screens/hardwallet/settings/views.cljs index 9442b4d029..b0f8bb4584 100644 --- a/src/status_im/ui/screens/hardwallet/settings/views.cljs +++ b/src/status_im/ui/screens/hardwallet/settings/views.cljs @@ -130,9 +130,10 @@ [action-row {:icon :main-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 :main-icons/logout - :color-theme :red - :label :t/reset-card - :on-press #(re-frame/dispatch [:keycard-settings.ui/reset-card-pressed])}]]]])) + (when pairing + [react/view {:margin-bottom 20 + :margin-left 16} + [action-row {:icon :main-icons/logout + :color-theme :red + :label :t/reset-card + :on-press #(re-frame/dispatch [:keycard-settings.ui/reset-card-pressed])}]])]])) diff --git a/src/status_im/ui/screens/hardwallet/setup/styles.cljs b/src/status_im/ui/screens/hardwallet/setup/styles.cljs index f852d8b6c1..06ea209298 100644 --- a/src/status_im/ui/screens/hardwallet/setup/styles.cljs +++ b/src/status_im/ui/screens/hardwallet/setup/styles.cljs @@ -115,7 +115,8 @@ :color colors/black}) (def bottom-container - {:height 60 + {:height 52 + :width "100%" :justify-content :center :align-items :center :border-top-width 1 @@ -128,8 +129,7 @@ :flex-direction :row :width 160 :height 44 - :border-radius 10 - :margin-bottom 14}) + :border-radius 10}) (def begin-button-container {:background-color colors/gray-background @@ -143,8 +143,7 @@ (def bottom-button-text {:font-size 15 - :color colors/blue - :line-height 20}) + :color colors/blue}) (def next-button-container {:flex-direction :row @@ -313,9 +312,11 @@ :color colors/black}) (def enter-pair-code-explanation-text - {:font-size 15 - :padding-top 5 - :color colors/gray}) + {:font-size 15 + :padding-top 5 + :text-align :center + :padding-horizontal 60 + :color colors/gray}) (def card-is-empty-text {:color colors/black diff --git a/src/status_im/ui/screens/hardwallet/setup/subs.cljs b/src/status_im/ui/screens/hardwallet/setup/subs.cljs index 71ac4ddcfa..4b72534ca2 100644 --- a/src/status_im/ui/screens/hardwallet/setup/subs.cljs +++ b/src/status_im/ui/screens/hardwallet/setup/subs.cljs @@ -6,10 +6,15 @@ (fn [db] (get-in db [:hardwallet :setup-step]))) +(re-frame/reg-sub + :hardwallet-card-state + (fn [db] + (get-in db [:hardwallet :card-state]))) + (re-frame/reg-sub :hardwallet-pair-code (fn [db] - (get-in db [:hardwallet :pair-code]))) + (get-in db [:hardwallet :secrets :password]))) (re-frame/reg-sub :hardwallet-recovery-phrase-word diff --git a/src/status_im/ui/screens/hardwallet/setup/views.cljs b/src/status_im/ui/screens/hardwallet/setup/views.cljs index 78ddf0b741..b005efbe1e 100644 --- a/src/status_im/ui/screens/hardwallet/setup/views.cljs +++ b/src/status_im/ui/screens/hardwallet/setup/views.cljs @@ -187,7 +187,7 @@ :forward? true}]]]])) (defn- card-with-button-view - [{:keys [text-label button-label button-container-style on-press]}] + [{:keys [text-label button-label button-container-style on-press show-icon?]}] "Generic view with centered card image and button at the bottom. Used by 'Prepare', 'Pair', 'No slots', 'Card is linked' screens" [react/view styles/card-with-button-view-container @@ -195,13 +195,17 @@ [react/image {:source (:hardwallet-card resources/ui) :style styles/hardwallet-card-image}] [react/view styles/center-text-container - [react/text {:style styles/center-text} + [react/text {:style (assoc styles/center-text :padding-horizontal 60)} (i18n/label text-label)]]] - [react/touchable-highlight - {:on-press on-press} - [react/view (merge styles/bottom-button-container button-container-style) - [react/text {:style styles/bottom-button-text} - (i18n/label button-label)]]]]) + [react/view styles/bottom-container + [react/touchable-highlight + {:on-press on-press} + [react/view (merge styles/bottom-button-container button-container-style) + [react/text {:style styles/bottom-button-text} + (i18n/label button-label)] + (when show-icon? + [vector-icons/icon :icons/link {:color colors/blue + :container-style {:margin-left 5}}])]]]]) (defn begin [] [react/view styles/card-blank-container @@ -219,10 +223,11 @@ [react/text {:style styles/remaining-steps-text} (i18n/label :t/remaining-steps)] [react/view {:margin-bottom 25} - (for [[number text] [["1" (i18n/label :t/initialization-of-the-card)] - ["2" (i18n/label :t/puk-and-pairing-codes-displayed)] - ["3" (i18n/label :t/device-pairing)] - ["4" (i18n/label :t/recovery-phrase)]]] + (for [[number text] [["1" (i18n/label :t/create-pin)] + ["2" (i18n/label :t/initialization-of-the-card)] + ["3" (i18n/label :t/puk-and-pairing-codes-displayed)] + ["4" (i18n/label :t/device-pairing)] + ["5" (i18n/label :t/recovery-phrase)]]] ^{:key number} [react/view styles/remaining-step-row [react/view styles/remaining-step-row-text [react/text {:style {:color colors/black}} @@ -238,21 +243,23 @@ (i18n/label :t/begin-set-up)]]]]]]) (defn pair [] - [card-with-button-view {:text-label :t/pair-card-question - :button-label :t/pair-card - :on-press-event #(re-frame/dispatch [:hardwallet.ui/pair-card-button-pressed])}]) + [card-with-button-view {:text-label :t/pair-card-question + :button-label :t/pair-card + :on-press #(re-frame/dispatch [:hardwallet.ui/pair-card-button-pressed])}]) (defn no-slots [] [card-with-button-view {:text-label :t/no-pairing-slots-available - :button-label :t/help + :button-label :t/help-capitalized + :show-icon? true :button-container-style {:background-color colors/white} - :on-press-event (.openURL react/linking "https://hardwallet.status.im")}]) + :on-press #(.openURL react/linking "https://hardwallet.status.im")}]) (defn card-already-linked [] [card-with-button-view {:text-label :t/card-already-linked - :button-label :t/help + :button-label :t/help-capitalized + :show-icon? true :button-container-style {:background-color colors/white} - :on-press-event (.openURL react/linking "https://hardwallet.status.im")}]) + :on-press #(.openURL react/linking "https://hardwallet.status.im")}]) (defview error [] (letsubs [error [:hardwallet-setup-error]] @@ -263,15 +270,20 @@ [react/view styles/center-text-container [react/text {:style styles/center-text} (i18n/label :t/something-went-wrong)] - [react/text {:style styles/center-text} - (str (:code error) "\n" (:error error))]]] - [react/touchable-highlight - {:on-press #(re-frame/dispatch [:hardwallet.ui/error-button-pressed])} - [react/view styles/bottom-button-container - [react/text {:style styles/bottom-button-text - :font :medium - :uppercase? false} - (i18n/label :t/try-again)]]]])) + [react/view {:padding-horizontal 20 + :margin-top 20} + [react/text {:style styles/center-text} + (if (map? error) + (str (:code error) "\n" (:error error)) + (str error))]]]] + [react/view styles/bottom-container + [react/touchable-highlight + {:on-press #(re-frame/dispatch [:hardwallet.ui/error-button-pressed])} + [react/view styles/bottom-button-container + [react/text {:style styles/bottom-button-text + :font :medium + :uppercase? false} + (i18n/label :t/try-again)]]]]])) (defn- loading-view [{:keys [title-label text-label estimated-time-seconds step-number]}] "Generic view with waiting time estimate and loading indicator. @@ -293,7 +305,8 @@ (defview preparing [] (letsubs [progress-bar (atom nil) - listener (atom nil)] + listener (atom nil) + card-state [:hardwallet-card-state]] {:component-will-mount (fn [] (when @listener (.removeListener @listener))) @@ -315,10 +328,13 @@ [react/text {:style styles/estimated-time-text} (i18n/label :t/taking-long-hold-phone-connected)]] [react/view styles/progress-bar-container - [react/progress-bar {:styleAttr "Horizontal" - :indeterminate false - :progress 0 - :ref #(reset! progress-bar %)}]]])) + (if (contains? #{:blank :init} card-state) + [react/progress-bar {:styleAttr "Horizontal" + :indeterminate false + :progress 0 + :ref #(reset! progress-bar %)}] + [react/activity-indicator {:animating true + :size :large}])]])) (defn- generating-mnemonic [] [react/view styles/loading-view-container @@ -378,7 +394,7 @@ :no-slots [no-slots] :card-already-linked [card-already-linked] :pairing [pairing] - :pin [pin.views/enter-pin] + :pin [pin.views/create-pin] :recovery-phrase [recovery-phrase] :recovery-phrase-confirm-word1 [recovery-phrase-confirm-word step] :recovery-phrase-confirm-word2 [recovery-phrase-confirm-word step] diff --git a/translations/en.json b/translations/en.json index 5836ba6e6d..8f19cbc28c 100644 --- a/translations/en.json +++ b/translations/en.json @@ -797,6 +797,8 @@ "card-is-blank": "This card is blank", "card-setup-prepare-text": "The whole process will take a few minutes.", "card-is-paired": "Card is paired", + "no-keycard-applet-on-card": "No Keycard applet on card", + "keycard-applet-will-be-installed": "Keycard applet will automatically be installed", "begin-set-up": "Begin setup", "maintain-card-to-phone-contact": "Maintain card-to-phone contact during process.", "preparing-card": "Preparing card", @@ -842,7 +844,8 @@ "change-pin": "Change PIN", "enter-pin": "Enter PIN", "enter-pin-description": "Enter your PIN code to login\n to your account", - "create-pin": "Create a PIN", + "create-pin": "Create PIN", + "create-a-pin": "Create a PIN", "create-pin-description": "You'll need your card + this PIN to log in and to confirm transactions", "repeat-pin": "Repeat new PIN", "puk-mismatch": "PUK code does not match", @@ -858,11 +861,12 @@ "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?", + "pair-card": "Pair to this device", + "pair-card-question": "Card is ready to pair", "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", + "enter-pair-code-description": "SECURITY NOTE: Use only the code you wrote down during card setup.", + "no-pairing-slots-available": "This card is already paired to 5 devices and cannot pair to this one. Please use one of the paired devices, log in with this card and free up pairing slots on the card", + "keycard-has-account-on-it": "This card has already an account on it. If you wish to change it, login first and reset your card.", "card-already-linked": "Card is already linked to another account", "keycard-unauthorized-operation": "You're unauthorized to perform this operation.", "no-account-on-card": "No account on this card", @@ -871,7 +875,6 @@ "wrong-card-text": "You have tapped a card that does not correspond to the account you selected. Please try again.", "account-not-listed": "Account not listed", "account-not-listed-text": "The account on this card is not listed on your phone. Would you like to login with this new account?", - "wrong-card-text": "You have tapped a card that does not correspond to the account you selected. Please try again.", "no-pairing-on-device": "Card is not paired to this device", "help": "help", "help-capitalized": "Help",