diff --git a/externs.js b/externs.js index ac0c940a31..37eea15934 100644 --- a/externs.js +++ b/externs.js @@ -19,8 +19,10 @@ var TopLevel = { "argv" : function () {}, "Array" : function () {}, "array" : function () {}, + "AsyncStorage" : function () {}, "at" : function () {}, "back" : function () {}, + "BackHandler" : function () {}, "balanceOf" : function () {}, "bezier" : function () {}, "BigNumber" : function () {}, @@ -151,6 +153,7 @@ var TopLevel = { "getInitialNotification" : function () {}, "getInitialURL" : function () {}, "getInternetCredentials" : function () {}, + "getItem" : function () {}, "getKeys" : function () {}, "getLayout" : function () {}, "getNetwork" : function () {}, @@ -401,6 +404,7 @@ var TopLevel = { "routeName" : function () {}, "routes" : function () {}, "saveAccountAndLogin" : function () {}, + "saveAccountAndLoginWithKeycard" : function () {}, "schemaVersion" : function () {}, "scrollTo" : function () {}, "scrollToEnd" : function () {}, @@ -432,6 +436,7 @@ var TopLevel = { "setHidden" : function () {}, "setInternetCredentials" : function () {}, "setInterval" : function () {}, + "setItem" : function () {}, "setNativeProps" : function () {}, "setNetworkActivityIndicatorVisible" : function () {}, "setPriority" : function () {}, diff --git a/mobile/js_files/package.json b/mobile/js_files/package.json index 193cf67ad2..fa6d8430a8 100644 --- a/mobile/js_files/package.json +++ b/mobile/js_files/package.json @@ -37,7 +37,7 @@ "react-native-screens": "1.0.0-alpha.22", "react-native-shake": "^3.3.1", "react-native-splash-screen": "^3.2.0", - "react-native-status-keycard": "^2.5.7", + "react-native-status-keycard": "^2.5.11", "react-native-svg": "9.8.4", "react-native-touch-id": "^4.4.1", "react-native-webview": "6.11.1", diff --git a/mobile/js_files/yarn.lock b/mobile/js_files/yarn.lock index 9b2a668598..d2e425530b 100644 --- a/mobile/js_files/yarn.lock +++ b/mobile/js_files/yarn.lock @@ -4898,7 +4898,7 @@ react-native-splash-screen@^3.2.0: resolved "https://registry.yarnpkg.com/react-native-splash-screen/-/react-native-splash-screen-3.2.0.tgz#d47ec8557b1ba988ee3ea98d01463081b60fff45" integrity sha512-Ls9qiNZzW/OLFoI25wfjjAcrf2DZ975hn2vr6U9gyuxi2nooVbzQeFoQS5vQcbCt9QX5NY8ASEEAtlLdIa6KVg== -react-native-status-keycard@^2.5.7: +react-native-status-keycard@^2.5.11: version "2.5.11" resolved "https://registry.yarnpkg.com/react-native-status-keycard/-/react-native-status-keycard-2.5.11.tgz#ba7d6b417ca3de345b7da27997d799ece6de6b68" integrity sha512-yc1Jkr+mKJpyd8QRJuYFchzkZ8ANDSC2CPYVKJJN2i93RReft3o2cHAi+Sn5LxI5Ex5ZpLaEzdL5ezG2EWbrjA== diff --git a/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusModule.java b/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusModule.java index 6d1f569fb7..30a9a30dde 100644 --- a/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusModule.java +++ b/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusModule.java @@ -292,6 +292,20 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL } } + @ReactMethod + public void saveAccountAndLoginWithKeycard(final String accountData, final String password , final String config, final String chatKey) { + Log.d(TAG, "saveAccountAndLoginWithKeycard"); + String finalConfig = prepareDirAndUpdateConfig(config); + String result = Statusgo.saveAccountAndLoginWithKeycard(accountData, password, finalConfig, chatKey); + if (result.startsWith("{\"error\":\"\"")) { + Log.d(TAG, "StartNode result: " + result); + Log.d(TAG, "Geth node started"); + } + else { + Log.e(TAG, "StartNode failed: " + result); + } + } + @ReactMethod public void login(final String accountData, final String password) { Log.d(TAG, "login"); @@ -432,22 +446,15 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL } @ReactMethod - public void loginWithKeycard(final String whisperPrivateKey, final String encryptionPublicKey, final String configJSON, final Callback callback) { + public void loginWithKeycard(final String accountData, final String password, final String chatKey) { Log.d(TAG, "loginWithKeycard"); - if (!checkAvailability()) { - callback.invoke(false); - return; + String result = Statusgo.loginWithKeycard(accountData, password, chatKey); + if (result.startsWith("{\"error\":\"\"")) { + Log.d(TAG, "LoginWithKeycard result: " + result); + } + else { + Log.e(TAG, "LoginWithKeycard failed: " + result); } - - Runnable r = new Runnable() { - @Override - public void run() { - String result = Statusgo.loginWithKeycard(whisperPrivateKey, encryptionPublicKey, configJSON); - callback.invoke(result); - } - }; - - StatusThreadPoolExecutor.getInstance().execute(r); } @ReactMethod diff --git a/modules/react-native-status/ios/RCTStatus/RCTStatus.m b/modules/react-native-status/ios/RCTStatus/RCTStatus.m index 6a3a587b8d..9fb1b395f9 100644 --- a/modules/react-native-status/ios/RCTStatus/RCTStatus.m +++ b/modules/react-native-status/ios/RCTStatus/RCTStatus.m @@ -403,6 +403,18 @@ RCT_EXPORT_METHOD(saveAccountAndLogin:(NSString *)accountData NSLog(@"%@", result); } +//////////////////////////////////////////////////////////////////// saveAccountAndLoginWithKeycard +RCT_EXPORT_METHOD(saveAccountAndLogin:(NSString *)accountData + password:(NSString *)password + config:(NSString *)config + chatKey:(NSString *)chatKey) { +#if DEBUG + NSLog(@"SaveAccountAndLoginWithKeycard() method called"); +#endif + NSString *finalConfig = [self prepareDirAndUpdateConfig:config]; + NSString *result = StatusgoSaveAccountAndLoginWithKeycard(accountData, password, finalConfig, chatKey); + NSLog(@"%@", result); +} //////////////////////////////////////////////////////////////////// login RCT_EXPORT_METHOD(login:(NSString *)accountData diff --git a/react-native/src/mobile/status_im/react_native/js_dependencies.cljs b/react-native/src/mobile/status_im/react_native/js_dependencies.cljs index f49804c295..89a486949f 100644 --- a/react-native/src/mobile/status_im/react_native/js_dependencies.cljs +++ b/react-native/src/mobile/status_im/react_native/js_dependencies.cljs @@ -35,6 +35,8 @@ (def net-info (js/require "@react-native-community/netinfo")) (def mail-class (js/require "react-native-mail")) (def react-native-mail (.-default mail-class)) +(def async-storage (.-AsyncStorage react-native)) +(def back-handler (.-BackHandler react-native)) (def desktop-linking #js {:addEventListener (fn [])}) (def desktop-menu #js {:addEventListener (fn [])}) (def desktop-config #js {:addEventListener (fn [])}) diff --git a/src/status_im/events.cljs b/src/status_im/events.cljs index 06c05f854c..f8c730dba3 100644 --- a/src/status_im/events.cljs +++ b/src/status_im/events.cljs @@ -758,11 +758,6 @@ (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]] - (hardwallet/on-install-applet-and-init-card-success cofx secrets))) - (handlers/register-handler-fx :hardwallet.callback/on-install-applet-and-init-card-error (fn [cofx [_ error]] @@ -1025,11 +1020,6 @@ (fn [cofx _] (hardwallet/generate-mnemonic cofx))) -(handlers/register-handler-fx - :hardwallet/generate-and-load-key - (fn [cofx _] - (hardwallet/generate-and-load-key cofx))) - (handlers/register-handler-fx :hardwallet.ui/create-pin-button-pressed (fn [{:keys [db]} _] diff --git a/src/status_im/hardwallet/core.cljs b/src/status_im/hardwallet/core.cljs index 2fa0c7e8af..143fa9a642 100644 --- a/src/status_im/hardwallet/core.cljs +++ b/src/status_im/hardwallet/core.cljs @@ -56,6 +56,25 @@ (:keycard-pairing (find-multiaccount-by-keycard-instance-uid db instance-uid)))))) +(fx/defn listen-to-hardware-back-button + [{:keys [db]}] + (when-not (get-in db [:hardwallet :back-button-listener]) + {:hardwallet/listen-to-hardware-back-button nil})) + +(fx/defn remove-listener-to-hardware-back-button + [{:keys [db]}] + (when-let [listener (get-in db [:hardwallet :back-button-listener])] + {:hardwallet/remove-listener-to-hardware-back-button listener})) + +(fx/defn on-add-listener-to-hardware-back-button + "Adds listener to hardware back button on Android. + During keycard setup we show user a warning that setup will be cancelled + when back button pressed. This prevents user from going back during setup + flow as some of the actions changing keycard step could not be repeated." + {:events [:hardwallet/add-listener-to-hardware-back-button]} + [{:keys [db]} listener] + {:db (assoc-in db [:hardwallet :back-button-listener] listener)}) + (fx/defn hardwallet-connect-navigate-back-button-clicked [{:keys [db] :as cofx}] (fx/merge cofx @@ -183,6 +202,7 @@ (fx/merge cofx {:db (-> db (assoc-in [:hardwallet :setup-step] :pair))} + (listen-to-hardware-back-button) (navigation/navigate-to-cofx :keycard-recovery-pair nil))) (fx/defn load-pairing-screen @@ -212,6 +232,8 @@ (fx/defn cancel-setup-pressed {:events [:keycard.onboarding.ui/cancel-pressed + :hardwallet/back-button-pressed + :keycard.onboarding.recovery-phrase.ui/cancel-pressed :keycard.onboarding.connection-lost-setup.ui/cancel-setup-pressed]} [_] {:ui/show-confirmation {:title (i18n/label :t/keycard-cancel-setup-title) @@ -223,10 +245,12 @@ (fx/defn cancel-setup-confirm-pressed {:events [:keycard.onboarding.ui/cancel-confirm-pressed]} - [cofx] + [{:keys [db] :as cofx}] (fx/merge cofx - {} - (navigation/navigate-to-cofx :keycard-onboarding-intro nil))) + (remove-listener-to-hardware-back-button) + (navigation/navigate-reset {:index 0 + :actions [{:routeName (if (:multiaccounts/multiaccounts db) + :multiaccounts :intro)}]}))) (fx/defn load-finishing-screen {:events [:keycard.onboarding.recovery-phrase-confirm-word2.ui/next-pressed @@ -248,10 +272,6 @@ [_] (.openURL react/linking "https://keycard.status.im")) -(fx/defn recovery-phrase-cancel-pressed - {:events [:keycard.onboarding.recovery-phrase.ui/cancel-pressed]} - [_]) - (fx/defn recovery-phrase-next-pressed {:events [:keycard.onboarding.recovery-phrase.ui/next-pressed]} [_] @@ -262,18 +282,21 @@ :on-accept #(re-frame/dispatch [:keycard.onboarding.recovery-phrase.ui/confirm-pressed]) :on-cancel #()}}) -(fx/defn recovery-phrase-start-confirmation [{:keys [db]}] +(fx/defn recovery-phrase-start-confirmation + [{:keys [db] :as cofx}] (let [mnemonic (get-in db [:hardwallet :secrets :mnemonic]) [word1 word2] (shuffle (map-indexed vector (clojure.string/split mnemonic #" "))) word1 (zipmap [:idx :word] word1) word2 (zipmap [:idx :word] word2)] - {:db (-> db - (assoc-in [:hardwallet :setup-step] :recovery-phrase-confirm-word1) - (assoc-in [:hardwallet :recovery-phrase :step] :word1) - (assoc-in [:hardwallet :recovery-phrase :confirm-error] nil) - (assoc-in [:hardwallet :recovery-phrase :input-word] nil) - (assoc-in [:hardwallet :recovery-phrase :word1] word1) - (assoc-in [:hardwallet :recovery-phrase :word2] word2))})) + (fx/merge cofx + {:db (-> db + (assoc-in [:hardwallet :setup-step] :recovery-phrase-confirm-word1) + (assoc-in [:hardwallet :recovery-phrase :step] :word1) + (assoc-in [:hardwallet :recovery-phrase :confirm-error] nil) + (assoc-in [:hardwallet :recovery-phrase :input-word] nil) + (assoc-in [:hardwallet :recovery-phrase :word1] word1) + (assoc-in [:hardwallet :recovery-phrase :word2] word2))} + (remove-listener-to-hardware-back-button)))) (fx/defn recovery-phrase-confirm-pressed {:events [:keycard.onboarding.recovery-phrase.ui/confirm-pressed]} @@ -338,32 +361,81 @@ [{:keys [db] :as cofx}] (fx/merge cofx {:db (-> db - (assoc-in [:hardwallet :pin] {:enter-step :import-multiaccount + (assoc-in [:hardwallet :pin] {:enter-step :import-multiaccount :import-multiaccount [] - :current []}))} + :current []}))} + (listen-to-hardware-back-button) (navigation/navigate-to-cofx :keycard-recovery-pin nil))) +(fx/defn generate-mnemonic + [cofx] + (let [{:keys [pairing]} (get-in cofx [:db :hardwallet :secrets])] + {:hardwallet/generate-mnemonic {:pairing pairing + :words (string/join "\n" mnemonic/dictionary)}})) + +(fx/defn proceed-with-generating-mnemonic + [{:keys [db] :as cofx}] + (let [pin (or (get-in db [:hardwallet :secrets :pin]) + (vector->string (get-in db [:hardwallet :pin :current])))] + (if (empty? pin) + (fx/merge cofx + {:db (assoc-in db [:hardwallet :pin] {:enter-step :current + :on-verified :hardwallet/generate-mnemonic + :current []})} + (navigation/navigate-to-cofx :keycard-onboarding-pin nil)) + (generate-mnemonic cofx)))) + +(fx/defn proceed-setup-with-initialized-card + [{:keys [db] :as cofx} flow instance-uid] + (if (= flow :import) + (navigation/navigate-to-cofx cofx :keycard-recovery-no-key nil) + (let [pairing-data (get-in db [:hardwallet :pairings (keyword instance-uid)])] + (if pairing-data + (fx/merge cofx + {:db (update-in db [:hardwallet :secrets] merge pairing-data)} + (listen-to-hardware-back-button) + (when (= flow :create) + (proceed-with-generating-mnemonic)) + (when (= flow :recovery) + (proceed-with-generating-key))) + (load-pair-screen cofx))))) + +(fx/defn show-existing-multiaccount-alert + [{:keys [db] :as cofx}] + (fx/merge cofx + {:utils/show-confirmation {:title nil + :content (i18n/label :t/keycard-existing-multiaccount) + :cancel-button-text "" + :confirm-button-text :t/okay}} + (navigation/navigate-back))) + (fx/defn check-card-state {:events [:hardwallet/check-card-state]} [{:keys [db] :as cofx}] (let [app-info (get-in db [:hardwallet :application-info]) flow (get-in db [:hardwallet :flow]) - instance-uid (:instance-uid app-info) + {:keys [instance-uid key-uid]} app-info pairing (get-pairing db instance-uid) app-info' (if pairing (assoc app-info :paired? true) app-info) card-state (get-card-state app-info')] (fx/merge cofx {:db (assoc-in db [:hardwallet :card-state] card-state)} (set-setup-step card-state) + (when (and flow + (= card-state :init)) + (proceed-setup-with-initialized-card flow instance-uid)) (when (= card-state :pre-init) (if (= flow :import) (navigation/navigate-to-cofx :keycard-recovery-no-key nil) (load-pin-screen))) (when (and (= card-state :multiaccount) (= flow :import)) - (if pairing - (load-recovery-pin-screen) - (load-pair-screen))) + (let [existing-multiaccount (find-multiaccount-by-keycard-key-uid db key-uid)] + (if existing-multiaccount + (show-existing-multiaccount-alert) + (if pairing + (load-recovery-pin-screen) + (load-pair-screen))))) (when (= card-state :blank) (if (= flow :import) (navigation/navigate-to-cofx :keycard-recovery-no-key nil) @@ -383,11 +455,11 @@ (fx/defn navigate-to-enter-pin-screen [{:keys [db] :as cofx}] - (let [keycard-instance-uid (get-in db [:hardwallet :application-info :instance-uid]) - multiaccount-instance-uid (get-in db [:multiaccount :keycard-instance-uid])] - (if (or (nil? multiaccount-instance-uid) - (and keycard-instance-uid - (= keycard-instance-uid multiaccount-instance-uid))) + (let [keycard-key-uid (get-in db [:hardwallet :application-info :key-uid]) + multiaccount-key-uid (get-in db [:multiaccount :keycard-key-uid])] + (if (or (nil? multiaccount-key-uid) + (and keycard-key-uid + (= keycard-key-uid multiaccount-key-uid))) (fx/merge cofx {:db (assoc-in db [:hardwallet :pin :current] [])} (navigation/navigate-to-cofx :enter-pin-settings nil)) @@ -479,14 +551,10 @@ [{:keys [db] :as cofx}] (let [application-info (get-in db [:hardwallet :application-info]) keycard-key-uid (get-in db [:hardwallet :application-info :key-uid]) - keycard-instance-uid (get-in db [:hardwallet :application-info :instance-uid]) multiaccount (get-in db [:multiaccounts/multiaccounts (get-in db [:multiaccounts/login :address])]) multiaccount-key-uid (get multiaccount :keycard-key-uid) - multiaccount-instance-uid (get multiaccount :keycard-instance-uid) multiaccount-mismatch? (or (nil? multiaccount) - (if (:keycard-key-uid multiaccount) - (not= multiaccount-key-uid keycard-key-uid) - (not= multiaccount-instance-uid keycard-instance-uid))) + (not= multiaccount-key-uid keycard-key-uid)) pairing (:keycard-pairing multiaccount)] (cond (empty? application-info) @@ -584,8 +652,11 @@ (fx/defn keycard-connection-lost-cancel-pressed {:events [:keycard.connection-lost.ui/cancel-pressed]} - [cofx] - (navigation/navigate-back cofx)) + [{:keys [db] :as cofx}] + (if (contains? (set (take 3 (:navigation-stack db))) + :keycard-login-pin) + (navigation/navigate-to-cofx cofx :multiaccounts nil) + (navigation/navigate-back cofx))) (fx/defn start-onboarding-flow {:events [:keycard.recovery.no-key.ui/generate-key-pressed @@ -773,23 +844,26 @@ :pairing pairing}} (fx/merge cofx {:db (assoc-in db [:hardwallet :on-card-connected] :hardwallet/remove-key-with-unpair)} - (navigation/navigate-to-cofx :hardwallet-connect-settings nil))))) + (navigation/navigate-to-cofx :keycard-connection-lost nil))))) (fx/defn on-remove-key-success [{:keys [db] :as cofx}] (let [multiaccount-address (get-in db [:multiaccount :address]) - pairing (get-in db [:multiaccount :keycard-pairing])] + instance-uid (get-in db [:hardwallet :application-info :instance-uid]) + pairings (get-in db [:hardwallet :pairings])] (fx/merge cofx {:db (-> db (update :multiaccounts/multiaccounts dissoc multiaccount-address) + (assoc-in [:hardwallet :secrets] nil) + (update-in [:hardwallet :pairings] dissoc (keyword instance-uid)) (assoc-in [:hardwallet :whisper-public-key] nil) (assoc-in [:hardwallet :wallet-address] nil) - (assoc-in [:hardwallet :secrets] {:pairing pairing}) (assoc-in [:hardwallet :application-info] nil) (assoc-in [:hardwallet :on-card-connected] nil) (assoc-in [:hardwallet :pin] {:status nil :error-label nil :on-verified nil})) + :hardwallet/persist-pairings (dissoc pairings (keyword instance-uid)) ;;FIXME delete multiaccount :utils/show-popup {:title "" :content (i18n/label :t/card-reseted)}} @@ -807,7 +881,7 @@ (assoc-in [:hardwallet :pin :status] nil)) :utils/show-popup {:title (i18n/label :t/error) :content (i18n/label :t/cannot-read-card)}} - (navigation/navigate-to-cofx :hardwallet-connect-settings nil)) + (navigation/navigate-to-cofx :keycard-connection-lost nil)) (show-wrong-keycard-alert cofx true))))) (fx/defn on-delete-success @@ -848,11 +922,11 @@ (fx/defn delete-card [{:keys [db] :as cofx}] - (let [keycard-instance-uid (get-in db [:hardwallet :application-info :instance-uid]) - multiaccount-instance-uid (get-in db [:multiaccount :keycard-instance-uid])] - (if (or (nil? multiaccount-instance-uid) - (and keycard-instance-uid - (= keycard-instance-uid multiaccount-instance-uid))) + (let [keycard-key-uid (get-in db [:hardwallet :application-info :key-uid]) + multiaccount-key-uid (get-in db [:multiaccount :keycard-key-uid])] + (if (or (nil? multiaccount-key-uid) + (and keycard-key-uid + (= keycard-key-uid multiaccount-key-uid))) {:hardwallet/delete nil} (unauthorized-operation cofx)))) @@ -1019,8 +1093,8 @@ :utils/show-popup {:title (i18n/label :t/error) :content (i18n/label :t/cannot-read-card)}} (navigation/navigate-to-cofx (if setup? - :hardwallet-connect - :hardwallet-connect-settings) nil)) + :keycard-connection-lost-setup + :keycard-connection-lost) nil)) (if (re-matches pin-mismatch-error (:error error)) (fx/merge cofx {:db (update-in db [:hardwallet :pin] merge {:status :error @@ -1077,17 +1151,21 @@ (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-multiaccount nil) - (navigation/navigate-to-cofx :keycard-settings nil))) + (let [instance-uid (get-in db [:hardwallet :application-info :instance-uid]) + pairings (get-in db [:hardwallet :pairings])] + (fx/merge cofx + {:db (-> db + (assoc-in [:hardwallet :secrets] nil) + (update-in [:hardwallet :pairings] dissoc (keyword instance-uid)) + (assoc-in [:hardwallet :on-card-connected] nil) + (assoc-in [:hardwallet :pin] {:status nil + :error-label nil + :on-verified nil})) + :hardwallet/persist-pairings (dissoc pairings (keyword instance-uid)) + :utils/show-popup {:title "" + :content (i18n/label :t/card-unpaired)}} + (remove-pairing-from-multiaccount nil) + (navigation/navigate-to-cofx :keycard-settings nil)))) (fx/defn on-unpair-error [{:keys [db] :as cofx} error] @@ -1116,8 +1194,8 @@ (fx/merge cofx {:db (assoc-in db [:hardwallet :on-card-connected] :hardwallet/verify-pin)} (navigation/navigate-to-cofx (if setup? - :hardwallet-connect - :hardwallet-connect-settings) nil))))) + :keycard-connection-lost-setup + :keycard-connection-lost) nil))))) (defn unblock-pin [{:keys [db] :as cofx}] @@ -1132,7 +1210,7 @@ :pairing pairing}} (fx/merge cofx {:db (assoc-in db [:hardwallet :on-card-connected] :hardwallet/unblock-pin)} - (navigation/navigate-to-cofx :hardwallet-connect-settings nil))))) + (navigation/navigate-to-cofx :keycard-connection-lost nil))))) (def pin-code-length 6) (def puk-code-length 12) @@ -1316,12 +1394,6 @@ (dispatch-event :hardwallet/generate-mnemonic) (navigation/navigate-to-cofx :hardwallet-connect nil))))) -(fx/defn generate-mnemonic - [cofx] - (let [{:keys [pairing]} (get-in cofx [:db :hardwallet :secrets])] - {:hardwallet/generate-mnemonic {:pairing pairing - :words (string/join "\n" mnemonic/dictionary)}})) - (fx/defn on-card-connected [{:keys [db] :as cofx} _] (log/debug "[hardwallet] card connected") @@ -1383,6 +1455,7 @@ (navigation/navigate-to-cofx :hardwallet-authentication-method nil)))))) (fx/defn on-install-applet-and-init-card-success + {:events [:hardwallet.callback/on-install-applet-and-init-card-success]} [{:keys [db] :as cofx} secrets] (let [secrets' (js->clj secrets :keywordize-keys true)] (fx/merge cofx @@ -1392,6 +1465,7 @@ (assoc-in [:hardwallet :on-card-connected] nil) (assoc-in [:hardwallet :setup-step] :secret-keys) (update-in [:hardwallet :secrets] merge secrets'))} + (listen-to-hardware-back-button) (navigation/navigate-to-cofx :keycard-onboarding-puk-code nil)))) (def on-init-card-success on-install-applet-and-init-card-success) @@ -1419,6 +1493,11 @@ :keycard-paired-on paired-on} {})) +(fx/defn on-retrieve-pairings-success + {:events [:hardwallet.callback/on-retrieve-pairings-success]} + [{:keys [db]} pairings] + {:db (assoc-in db [:hardwallet :pairings] pairings)}) + (fx/defn on-pair-success [{:keys [db] :as cofx} pairing] (let [setup-step (get-in db [:hardwallet :setup-step]) @@ -1426,16 +1505,20 @@ instance-uid (get-in db [:hardwallet :application-info :instance-uid]) multiaccount (find-multiaccount-by-keycard-instance-uid db instance-uid) paired-on (utils.datetime/timestamp) + pairings (get-in db [:hardwallet :pairings]) + instance-uid (get-in db [:hardwallet :application-info :instance-uid]) next-step (if (= setup-step :pair) :begin :card-ready)] (fx/merge cofx - {:db (-> db - (assoc-in [:hardwallet :application-info :paired?] true) - (assoc-in [:hardwallet :on-card-connected] nil) - (assoc-in [:hardwallet :setup-step] next-step) - (assoc-in [:hardwallet :secrets :pairing] pairing) - (assoc-in [:hardwallet :secrets :paired-on] paired-on))} + {:hardwallet/persist-pairings (assoc pairings instance-uid {:pairing pairing + :paired-on paired-on}) + :db (-> db + (assoc-in [:hardwallet :application-info :paired?] true) + (assoc-in [:hardwallet :on-card-connected] nil) + (assoc-in [:hardwallet :setup-step] next-step) + (assoc-in [:hardwallet :secrets :pairing] pairing) + (assoc-in [:hardwallet :secrets :paired-on] paired-on))} (when multiaccount (set-multiaccount-pairing multiaccount pairing paired-on)) (when (= flow :login) @@ -1543,6 +1626,7 @@ (navigation/navigate-to-cofx :enter-pin-login nil))) (fx/defn generate-and-load-key + {:events [:hardwallet/generate-and-load-key]} [{:keys [db] :as cofx}] (let [{:keys [mnemonic pairing pin]} (get-in db [:hardwallet :secrets]) {:keys [selected-id multiaccounts]} (:intro-wizard db) @@ -1550,7 +1634,8 @@ (filter #(= (:id %) selected-id)) first :mnemonic) - mnemonic' (or user-selected-mnemonic mnemonic) + recovery-mnemonic (get-in db [:multiaccounts/recover :passphrase]) + mnemonic' (or user-selected-mnemonic mnemonic recovery-mnemonic) pin' (or pin (vector->string (get-in db [:hardwallet :pin :current])))] (fx/merge cofx {:hardwallet/generate-and-load-key {:mnemonic mnemonic' @@ -1561,40 +1646,51 @@ (fx/defn create-keycard-multiaccount [{:keys [db] :as cofx}] (let [{{:keys [multiaccount secrets flow]} :hardwallet} db - {:keys [whisper-public-key + {:keys [address + public-key + whisper-public-key wallet-public-key whisper-address wallet-address + whisper-private-key encryption-public-key instance-uid key-uid]} multiaccount {:keys [pairing paired-on]} secrets] (fx/merge cofx - {:db (assoc-in db [:hardwallet :setup-step] nil)} + {:db (-> db + (assoc-in [:hardwallet :setup-step] nil) + (assoc :intro-wizard nil))} (multiaccounts.create/on-multiaccount-created {:derived {constants/path-whisper-keyword {:publicKey whisper-public-key :address whisper-address} constants/path-default-wallet-keyword {:publicKey wallet-public-key :address wallet-address}} :mnemonic "" + :address address + :publicKey public-key :keycard-instance-uid instance-uid :keycard-key-uid key-uid :keycard-pairing pairing - :keycard-paired-on paired-on} + :keycard-paired-on paired-on + :chat-key whisper-private-key} encryption-public-key {:seed-backed-up? true :login? true}) (if (= flow :import) (navigation/navigate-to-cofx :keycard-recovery-success nil) - (navigation/navigate-to-cofx :keycard-welcome nil))))) + (navigation/navigate-to-cofx :home nil))))) (fx/defn on-generate-and-load-key-success [{:keys [db random-guid-generator] :as cofx} data] - (let [account-data (js->clj data :keywordize-keys true) - flow (get-in db [:hardwallet :flow])] + (let [account-data (js->clj data :keywordize-keys true)] (fx/merge cofx {:db (-> db (assoc-in [:hardwallet :multiaccount] (-> account-data + (update :address #(str "0x" %)) + (update :whisper-address #(str "0x" %)) + (update :wallet-address #(str "0x" %)) + (update :public-key #(str "0x" %)) (update :whisper-public-key #(str "0x" %)) (update :wallet-public-key #(str "0x" %)) (update :instance-uid #(get-in db [:hardwallet :multiaccount :instance-uid] %)))) @@ -1606,9 +1702,8 @@ (update-in [:hardwallet :secrets] dissoc :pin :puk :password) (assoc :multiaccounts/new-installation-id (random-guid-generator)) (update-in [:hardwallet :secrets] dissoc :mnemonic))} - (create-keycard-multiaccount) - (when-not (= flow :import) - (navigation/navigate-to-cofx :keycard-welcome nil))))) + (remove-listener-to-hardware-back-button) + (create-keycard-multiaccount)))) (fx/defn on-generate-and-load-key-error [{:keys [db] :as cofx} {:keys [error code]}] @@ -1619,11 +1714,18 @@ (assoc-in [:hardwallet :setup-error] error))} (process-error code error))) +(fx/defn on-login-success + {:events [:keycard.login.callback/login-success]} + [cofx result] + (log/debug "loginWithKeycard success: " result)) + (fx/defn on-get-keys-success [{:keys [db] :as cofx} data] - (let [{:keys [wallet-address whisper-address encryption-public-key whisper-private-key] :as account-data} (js->clj data :keywordize-keys true) - {:keys [photo-path name]} (get-in db [:multiaccounts/multiaccounts whisper-address]) - instance-uid (get-in db [:hardwallet :application-info :instance-uid])] + (let [{:keys [address whisper-address encryption-public-key whisper-private-key] :as account-data} (js->clj data :keywordize-keys true) + address (str "0x" address) + {:keys [photo-path name]} (get-in db [:multiaccounts/multiaccounts address]) + instance-uid (get-in db [:hardwallet :application-info :instance-uid]) + multiaccount-data (types/clj->json {:name name :address address :photo-path photo-path})] (fx/merge cofx {:db (-> db (assoc-in [:hardwallet :pin :status] nil) @@ -1632,13 +1734,13 @@ (assoc-in [:hardwallet :flow] nil) (update :multiaccounts/login assoc :password encryption-public-key - :address whisper-address + :address address :photo-path photo-path :name name)) - :hardwallet/get-application-info {:pairing (get-pairing db instance-uid)} - :hardwallet/login-with-keycard {:whisper-private-key whisper-private-key - :encryption-public-key encryption-public-key - :on-result #(re-frame/dispatch [:multiaccounts.login.callback/login-success %])}}))) + :hardwallet/get-application-info {:pairing (get-pairing db instance-uid)} + :hardwallet/login-with-keycard {:multiaccount-data multiaccount-data + :password encryption-public-key + :chat-key whisper-private-key}}))) (fx/defn on-get-keys-error [{:keys [db] :as cofx} error] diff --git a/src/status_im/hardwallet/fx.cljs b/src/status_im/hardwallet/fx.cljs index 74358524f5..d907a85b0a 100644 --- a/src/status_im/hardwallet/fx.cljs +++ b/src/status_im/hardwallet/fx.cljs @@ -1,7 +1,10 @@ (ns status-im.hardwallet.fx (:require [re-frame.core :as re-frame] + [status-im.utils.types :as types] [status-im.hardwallet.card :as card] - [status-im.native-module.core :as status])) + [status-im.native-module.core :as status] + [status-im.react-native.js-dependencies :as js-dependencies] + [status-im.utils.platform :as platform])) (re-frame/reg-fx :hardwallet/get-application-info @@ -95,3 +98,34 @@ :send-transaction-with-signature (fn [{:keys [transaction signature on-completed]}] (status/send-transaction-with-signature transaction signature on-completed))) + +(re-frame/reg-fx + :hardwallet/persist-pairings + (fn [pairings] + (.. js-dependencies/async-storage + (setItem "status-keycard-pairings" (types/serialize pairings))))) + +(re-frame/reg-fx + :hardwallet/retrieve-pairings + (fn [] + (when platform/android? + (.. js-dependencies/async-storage + (getItem "status-keycard-pairings") + (then #(re-frame/dispatch [:hardwallet.callback/on-retrieve-pairings-success + (types/deserialize %)])))))) + +(re-frame/reg-fx + :hardwallet/listen-to-hardware-back-button + ;;NOTE: not done in view because effect should happen under different conditions and is not dependent on + ;;particular screen to be loaded. An fx is easier to re-use and test. + (fn [] + (re-frame/dispatch [:hardwallet/add-listener-to-hardware-back-button + (.addEventListener js-dependencies/back-handler "hardwareBackPress" + (fn [] + (re-frame/dispatch [:hardwallet/back-button-pressed]) + true))]))) + +(re-frame/reg-fx + :hardwallet/remove-listener-to-hardware-back-button + (fn [listener] + (.remove listener))) diff --git a/src/status_im/init/core.cljs b/src/status_im/init/core.cljs index f4df3ceb0f..3b924ad33e 100644 --- a/src/status_im/init/core.cljs +++ b/src/status_im/init/core.cljs @@ -9,7 +9,8 @@ [status-im.ui.screens.db :refer [app-db]] [status-im.ui.screens.navigation :as navigation] [status-im.utils.fx :as fx] - [status-im.utils.platform :as platform])) + [status-im.utils.platform :as platform] + [clojure.string :as string])) (defn restore-native-settings! [] (when platform/desktop? @@ -65,8 +66,12 @@ (fx/defn initialize-multiaccounts {:events [::initialize-multiaccounts]} [{:keys [db] :as cofx} all-multiaccounts] - (let [multiaccounts (reduce (fn [acc {:keys [address] :as multiaccount}] - (assoc acc address multiaccount)) + (let [multiaccounts (reduce (fn [acc {:keys [address keycard-key-uid keycard-pairing] :as multiaccount}] + (-> (assoc acc address multiaccount) + (assoc-in [address :keycard-key-uid] (when-not (string/blank? keycard-key-uid) + keycard-key-uid)) + (assoc-in [address :keycard-pairing] (when-not (string/blank? keycard-pairing) + keycard-pairing)))) {} all-multiaccounts)] (fx/merge cofx @@ -75,17 +80,18 @@ (fx/defn start-app [cofx] (fx/merge cofx - {::get-device-UUID nil - ::get-supported-biometric-auth nil - ::init-keystore nil - ::restore-native-settings nil - ::open-multiaccounts #(re-frame/dispatch [::initialize-multiaccounts %]) + {::get-device-UUID nil + ::get-supported-biometric-auth nil + ::init-keystore nil + ::restore-native-settings nil + ::open-multiaccounts #(re-frame/dispatch [::initialize-multiaccounts %]) :ui/listen-to-window-dimensions-change nil :notifications/init nil ::network/listen-to-network-info nil :hardwallet/register-card-events nil - :hardwallet/check-nfc-support nil - :hardwallet/check-nfc-enabled nil} + :hardwallet/check-nfc-support nil + :hardwallet/check-nfc-enabled nil + :hardwallet/retrieve-pairings nil} (initialize-app-db))) (re-frame/reg-fx diff --git a/src/status_im/multiaccounts/create/core.cljs b/src/status_im/multiaccounts/create/core.cljs index fd98e43a6e..104774ca15 100644 --- a/src/status_im/multiaccounts/create/core.cljs +++ b/src/status_im/multiaccounts/create/core.cljs @@ -161,27 +161,46 @@ :path constants/path-whisper :chat true})]) +(fx/defn save-account-and-login-with-keycard + [_ multiaccount-data password node-config chat-key] + {::save-account-and-login-with-keycard [(types/clj->json multiaccount-data) + password + node-config + chat-key]}) + +(fx/defn save-account-and-login + [_ multiaccount-data password node-config accounts-data] + {::save-account-and-login [(types/clj->json multiaccount-data) + password + node-config + (types/clj->json accounts-data)]}) + (fx/defn on-multiaccount-created [{:keys [signing-phrase random-guid-generator db] :as cofx} - {:keys [address publicKey keycard-instance-uid keycard-key-uid keycard-pairing keycard-paired-on mnemonic] :as multiaccount} + {:keys [address chat-key keycard-instance-uid keycard-key-uid keycard-pairing keycard-paired-on mnemonic] :as multiaccount} password {:keys [seed-backed-up? login?] :or {login? true}}] (let [[wallet-account {:keys [publicKey]} :as accounts-data] (prepare-accounts-data multiaccount) name (gfycat/generate-gfy publicKey) photo-path (identicon/identicon publicKey) multiaccount-data {:name name :address address :photo-path photo-path} - new-multiaccount {:address address - :name name - :photo-path photo-path - :public-key publicKey + new-multiaccount (cond-> {:address address + :name name + :photo-path photo-path + :public-key publicKey - :latest-derived-path 0 - :accounts [wallet-account] - :signing-phrase signing-phrase + :latest-derived-path 0 + :accounts [wallet-account] + :signing-phrase signing-phrase - :installation-id (random-guid-generator) - :mnemonic mnemonic - :settings constants/default-multiaccount-settings} + :installation-id (random-guid-generator) + :mnemonic mnemonic + :settings constants/default-multiaccount-settings} + + keycard-key-uid (assoc :keycard-instance-uid keycard-instance-uid + :keycard-key-uid keycard-key-uid + :keycard-pairing keycard-pairing + :keycard-paired-on keycard-paired-on)) db (assoc db :multiaccounts/login {:address address :name name @@ -195,11 +214,16 @@ (fx/merge cofx {:db (cond-> db seed-backed-up? - (assoc-in [:multiaccount :seed-backed-up?] true)) - ::save-account-and-login [(types/clj->json multiaccount-data) - (ethereum/sha3 (security/safe-unmask-data password)) - (node/get-new-config db) - (types/clj->json accounts-data)]} + (assoc-in [:multiaccount :seed-backed-up?] true))} + (if keycard-key-uid + (save-account-and-login-with-keycard new-multiaccount + password + (node/get-new-config db) + chat-key) + (save-account-and-login multiaccount-data + (ethereum/sha3 (security/safe-unmask-data password)) + (node/get-new-config db) + accounts-data)) (when (:intro-wizard db) (intro-step-forward {}))))) @@ -300,3 +324,10 @@ hashed-password config accounts-data))) +(re-frame/reg-fx + ::save-account-and-login-with-keycard + (fn [[multiaccount-data password config chat-key]] + (status/save-account-and-login-with-keycard multiaccount-data + (security/safe-unmask-data password) + config + chat-key))) diff --git a/src/status_im/multiaccounts/login/core.cljs b/src/status_im/multiaccounts/login/core.cljs index 04a273ae9a..52d75ff04d 100644 --- a/src/status_im/multiaccounts/login/core.cljs +++ b/src/status_im/multiaccounts/login/core.cljs @@ -271,7 +271,7 @@ (fx/defn open-login [{:keys [db] :as cofx} address photo-path name public-key] - (let [keycard-multiaccount? (get-in db [:multiaccounts/multiaccounts address :keycard-instance-uid])] + (let [keycard-multiaccount? (get-in db [:multiaccounts/multiaccounts address :keycard-key-uid])] (fx/merge cofx {:db (-> db (update :multiaccounts/login assoc diff --git a/src/status_im/multiaccounts/recover/core.cljs b/src/status_im/multiaccounts/recover/core.cljs index 90221bba52..5be62d4397 100644 --- a/src/status_im/multiaccounts/recover/core.cljs +++ b/src/status_im/multiaccounts/recover/core.cljs @@ -78,7 +78,7 @@ multiaccount-address (-> (:address multiaccount) (string/lower-case) (string/replace-first "0x" "")) - keycard-multiaccount? (boolean (get-in db [:multiaccounts/multiaccounts multiaccount-address :keycard-instance-uid]))] + keycard-multiaccount? (boolean (get-in db [:multiaccounts/multiaccounts multiaccount-address :keycard-key-uid]))] (if keycard-multiaccount? ;; trying to recover multiaccount created with keycard {:db (-> db @@ -171,8 +171,9 @@ [{:keys [db] :as cofx}] (let [storage-type (get-in db [:intro-wizard :selected-storage-type])] (if (= storage-type :advanced) - {:dispatch [:recovery.ui/keycard-option-pressed]}) - (navigation/navigate-to-cofx cofx :recover-multiaccount-enter-password nil))) + ;;TODO: fix circular dependency to remove dispatch here + {:dispatch [:recovery.ui/keycard-option-pressed]} + (navigation/navigate-to-cofx cofx :recover-multiaccount-enter-password nil)))) (fx/defn re-encrypt-pressed {:events [::re-encrypt-pressed]} diff --git a/src/status_im/native_module/core.cljs b/src/status_im/native_module/core.cljs index 48d9c2330a..4f9fd8ef1d 100644 --- a/src/status_im/native_module/core.cljs +++ b/src/status_im/native_module/core.cljs @@ -35,6 +35,11 @@ (clear-web-data) (.saveAccountAndLogin (status) multiaccount-data hashed-password config accounts-data)) +(defn save-account-and-login-with-keycard + "NOTE: chat-key is a whisper private key sent from keycard" + [multiaccount-data password config chat-key] + (.saveAccountAndLoginWithKeycard (status) multiaccount-data password config chat-key)) + (defn login "NOTE: beware, the password has to be sha3 hashed" [account-data hashed-password] @@ -133,9 +138,9 @@ (.verify (status) address hashed-password callback)) (defn login-with-keycard - [{:keys [whisper-private-key encryption-public-key on-result]}] + [{:keys [multiaccount-data password chat-key]}] (clear-web-data) - (.loginWithKeycard (status) whisper-private-key encryption-public-key on-result)) + (.loginWithKeycard (status) multiaccount-data password chat-key)) (defn set-soft-input-mode [mode] (.setSoftInputMode (status) mode)) diff --git a/src/status_im/signing/core.cljs b/src/status_im/signing/core.cljs index e73abbf0b2..da84838a54 100644 --- a/src/status_im/signing/core.cljs +++ b/src/status_im/signing/core.cljs @@ -171,7 +171,7 @@ (fx/defn show-sign [{:keys [db] :as cofx}] (let [{:signing/keys [queue]} db {{:keys [gas gasPrice] :as tx-obj} :tx-obj {:keys [data typed?] :as message} :message :as tx} (last queue) - keycard-multiaccount? (boolean (get-in db [:multiaccount :keycard-instance-uid])) + keycard-multiaccount? (boolean (get-in db [:multiaccount :keycard-key-uid])) wallet-set-up-passed? (get-in db [:multiaccount :wallet-set-up-passed?]) updated-db (if wallet-set-up-passed? db (assoc db :popover/popover {:view :signing-phrase}))] (if message diff --git a/src/status_im/subs.cljs b/src/status_im/subs.cljs index 4b2ab82c45..c7330fe2bf 100644 --- a/src/status_im/subs.cljs +++ b/src/status_im/subs.cljs @@ -180,6 +180,8 @@ (reg-root-key-sub :popover/popover :popover/popover) (reg-root-key-sub :generate-account :generate-account) +(reg-root-key-sub :keycard :hardwallet) + ;;GENERAL ============================================================================================================== (re-frame/reg-sub diff --git a/src/status_im/ui/screens/hardwallet/pin/styles.cljs b/src/status_im/ui/screens/hardwallet/pin/styles.cljs index 303ea61c66..3fdaa5ce9b 100644 --- a/src/status_im/ui/screens/hardwallet/pin/styles.cljs +++ b/src/status_im/ui/screens/hardwallet/pin/styles.cljs @@ -10,22 +10,21 @@ {:flex 1 :flex-direction :column :justify-content :space-between - :padding-bottom 10 - :android {:margin-top 40} - :ios {:margin-top 30}}) + :android {:margin-top 10} + :ios {:margin-top 10}}) (defstyle error-container - {:android {:margin-top 25} - :ios {:margin-top 28}}) + {:height 22}) (def error-text {:color colors/red + :font-size 15 :text-align :center}) (defn center-container [title] {:flex-direction :column :align-items :center - :margin-top (if title 28 5)}) + :margin-top (if title 20 5)}) (def center-title-text {:typography :header}) @@ -39,18 +38,20 @@ (def pin-indicator-container {:flex-direction :row :justify-content :space-between - :margin-top 30}) + :margin-top 16}) (def pin-indicator-group-container {:flex-direction :row :justify-content :space-between}) -(defn pin-indicator [pressed?] +(defn pin-indicator [pressed? status] {:width 8 :height 8 - :background-color (if pressed? - colors/blue - colors/black-transparent) + :background-color (if (= status :error) + colors/red + (if pressed? + colors/blue + colors/black-transparent)) :border-radius 50 :margin-horizontal 5}) diff --git a/src/status_im/ui/screens/hardwallet/pin/views.cljs b/src/status_im/ui/screens/hardwallet/pin/views.cljs index b9cf430998..75802613da 100644 --- a/src/status_im/ui/screens/hardwallet/pin/views.cljs +++ b/src/status_im/ui/screens/hardwallet/pin/views.cljs @@ -40,10 +40,10 @@ [react/view styles/numpad-delete-button [vector-icons/icon :main-icons/backspace {:color colors/blue}]]]]]) -(defn pin-indicator [pressed?] - [react/view (styles/pin-indicator pressed?)]) +(defn pin-indicator [pressed? status] + [react/view (styles/pin-indicator pressed? status)]) -(defn pin-indicators [pin style] +(defn pin-indicators [pin status style] [react/view (merge styles/pin-indicator-container style) (map-indexed (fn [i group] @@ -54,17 +54,17 @@ (map-indexed (fn [i n] ^{:key i} - [pin-indicator (number? n)]) + [pin-indicator (number? n) status]) (concat pin (repeat (- 6 (count pin)) nil)))))]) -(defn puk-indicators [puk] +(defn puk-indicators [puk status] [react/view (map-indexed (fn [i puk-group] ^{:key i} - [pin-indicators puk-group {:margin-top 15}]) + [pin-indicators puk-group status {:margin-top 15}]) (partition 6 (concat puk (repeat (- 12 (count puk)) @@ -83,21 +83,23 @@ [react/text {:style styles/create-pin-text :number-of-lines 2} (i18n/label description-label)]) - (when retry-counter - [react/text {:style {:font-weight "700" - :padding-top 10 - :color colors/red}} - (i18n/label :t/pin-retries-left {:number retry-counter})]) - (case status - :verifying [react/view styles/waiting-indicator-container - [react/activity-indicator {:animating true - :size :small}]] - :error [react/view styles/error-container - [react/text {:style styles/error-text} - (i18n/label error-label)]] - (if (= step :puk) - [puk-indicators pin] - [pin-indicators pin])) + [react/view {:height 10} + (when retry-counter + [react/text {:style {:font-weight "700" + :color colors/red}} + (i18n/label :t/pin-retries-left {:number retry-counter})])] + [react/view {:height 22} + (case status + :verifying [react/view styles/waiting-indicator-container + [react/activity-indicator {:animating true + :size :small}]] + :error [react/view styles/error-container + [react/text {:style styles/error-text} + (i18n/label error-label)]] + nil)] + (if (= step :puk) + [puk-indicators pin status] + [pin-indicators pin status nil]) [numpad step enabled?]]]])) (def pin-retries 3) diff --git a/src/status_im/ui/screens/hardwallet/settings/subs.cljs b/src/status_im/ui/screens/hardwallet/settings/subs.cljs index 9cd55ad7b4..15730cb00e 100644 --- a/src/status_im/ui/screens/hardwallet/settings/subs.cljs +++ b/src/status_im/ui/screens/hardwallet/settings/subs.cljs @@ -38,4 +38,4 @@ :keycard-multiaccount? (fn [db] (boolean - (get-in db [:multiaccount :keycard-instance-uid])))) + (get-in db [:multiaccount :keycard-key-uid])))) diff --git a/src/status_im/ui/screens/hardwallet/settings/views.cljs b/src/status_im/ui/screens/hardwallet/settings/views.cljs index 67e413f5ab..6f6561a0a2 100644 --- a/src/status_im/ui/screens/hardwallet/settings/views.cljs +++ b/src/status_im/ui/screens/hardwallet/settings/views.cljs @@ -113,7 +113,7 @@ (if (zero? puk-retry-counter) [card-blocked] [react/view - [action-row {:icon :main-icons/info + [action-row {:icon :main-icons/help :label :t/help-capitalized :on-press #(.openURL react/linking "https://hardwallet.status.im")}] (when pairing @@ -124,10 +124,11 @@ [action-row {:icon :main-icons/close :label :t/unpair-card :on-press #(re-frame/dispatch [:keycard-settings.ui/unpair-card-pressed])}]])])] - (when pairing - [react/view {:margin-bottom 35 - :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])}]])]])) + ; NOTE: Reset card is hidden until multiaccount removal will be implemented + #_(when pairing + [react/view {:margin-bottom 35 + :margin-left 16} + [action-row {:icon :main-icons/warning + :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/keycard/onboarding/views.cljs b/src/status_im/ui/screens/keycard/onboarding/views.cljs index ae61dcf516..4108539e71 100644 --- a/src/status_im/ui/screens/keycard/onboarding/views.cljs +++ b/src/status_im/ui/screens/keycard/onboarding/views.cljs @@ -261,12 +261,25 @@ :text-align :center}} (i18n/label (if (= :original enter-step) :t/intro-wizard-title4 - :t/intro-wizard-title5))]]] + :t/intro-wizard-title5))]] + [react/view {:margin-top 16 + :height 22} + (when (= :original enter-step) + [react/text {:style {:color colors/gray}} + (i18n/label :t/intro-wizard-text4)])]] [pin.views/pin-view {:pin pin :status status :error-label error-label - :step enter-step}]]])) + :step enter-step}] + [react/view {:align-items :center + :flex-direction :column + :justify-content :center + :margin-bottom 15} + [react/text {:style {:color colors/gray + :padding-horizontal 40 + :text-align :center}} + (i18n/label :t/you-will-need-this-code)]]]])) (defview recovery-phrase [] (letsubs [mnemonic [:hardwallet-mnemonic]] diff --git a/src/status_im/ui/screens/keycard/views.cljs b/src/status_im/ui/screens/keycard/views.cljs index 855cf98af2..9b1f5523c9 100644 --- a/src/status_im/ui/screens/keycard/views.cljs +++ b/src/status_im/ui/screens/keycard/views.cljs @@ -12,39 +12,45 @@ [status-im.utils.core :as utils.core] [status-im.utils.gfycat.core :as gfy] [status-im.utils.identicon :as identicon] - [status-im.ui.components.list-item.views :as list-item])) + [status-im.ui.components.list-item.views :as list-item] + [status-im.ui.screens.chat.photos :as photos])) -(defn connection-lost [] - [react/view {:flex 1 - :justify-content :center - :align-items :center - :background-color colors/gray-transparent-40} - [react/view {:background-color colors/white - :height 478 - :width "85%" - :border-radius 16 - :flex-direction :column - :justify-content :space-between - :align-items :center} - [react/view {:margin-top 32} - [react/text {:style {:typography :title-bold - :text-align :center}} - (i18n/label :t/connection-with-the-card-lost)] - [react/view {:margin-top 16} - [react/text {:style {:color colors/gray - :text-align :center}} - (i18n/label :t/connection-with-the-card-lost-text)]]] - [react/view {:margin-top 16} - [react/image {:source (resources/get-image :keycard-connection) - :resize-mode :center - :style {:width 200 - :height 200}}]] - [react/view {:margin-bottom 43} - [react/touchable-highlight - {:on-press #(re-frame/dispatch [:keycard.connection-lost.ui/cancel-pressed])} - [react/text {:style {:color colors/red - :text-align :center}} - (i18n/label :t/cancel)]]]]]) +(defview connection-lost [] + (letsubs [{:keys [card-connected?]} [:keycard]] + [react/view {:flex 1 + :justify-content :center + :align-items :center + :background-color colors/gray-transparent-40} + [react/view {:background-color colors/white + :height 478 + :width "85%" + :border-radius 16 + :flex-direction :column + :justify-content :space-between + :align-items :center} + [react/view {:margin-top 32} + [react/text {:style {:typography :title-bold + :text-align :center}} + (i18n/label :t/connection-with-the-card-lost)] + [react/view {:margin-top 16} + [react/text {:style {:color colors/gray + :padding-horizontal 50 + :text-align :center}} + (i18n/label :t/connection-with-the-card-lost-text)]]] + [react/view {:margin-top 16} + (if card-connected? + [react/activity-indicator {:size :large + :animating true}] + [react/image {:source (resources/get-image :keycard-connection) + :resize-mode :center + :style {:width 200 + :height 200}}])] + [react/view {:margin-bottom 43} + [react/touchable-highlight + {:on-press #(re-frame/dispatch [:keycard.connection-lost.ui/cancel-pressed])} + [react/text {:style {:color colors/red + :text-align :center}} + (i18n/label :t/cancel)]]]]])) (defn connection-lost-setup [] [react/view {:flex 1 @@ -356,92 +362,85 @@ enter-step [:hardwallet/pin-enter-step] status [:hardwallet/pin-status] error-label [:hardwallet/pin-error-label] - {:keys [address public-key]} [:multiaccounts/login]] - (let [address (str "0x" address)] - [react/view styles/container - [toolbar/toolbar - {:transparent? true - :style {:margin-top 32}} - [toolbar/nav-text - {:handler #(re-frame/dispatch [:keycard.login.pin.ui/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 1})] - [react/view {:margin-right 20} - [react/touchable-highlight - {:on-press #(re-frame/dispatch [:keycard.login.pin.ui/more-icon-pressed])} - [vector-icons/icon :main-icons/more {:color colors/black - :container-style {:margin-left 5}}]]]] - [react/view {:flex 1 - :flex-direction :column - :justify-content :space-between - :align-items :center - :margin-top 60} - [react/view {:flex-direction :column - :flex 1 - :justify-content :center - :align-items :center} - [react/view {:margin-horizontal 16 - :flex-direction :column} - [react/view {:justify-content :center - :align-items :center - :flex-direction :row} - [react/view {:width 69 - :height 69 - :justify-content :center - :align-items :center} - [react/image {:source {:uri (identicon/identicon public-key)} - :style {:width 61 - :height 61 - :border-radius 30 - :border-width 1 - :border-color colors/black-transparent}}] - [react/view {:justify-content :center - :align-items :center - :width 24 - :height 24 - :border-radius 24 - :position :absolute - :right 0 - :bottom 0 - :background-color :white - :border-width 1 - :border-color colors/black-transparent} - [react/image {:source (resources/get-image :keycard-key) - :style {:width 8 - :height 14}}]]]] - [react/text {:style {:text-align :center - :margin-top 12 - :color colors/black - :font-weight "500"} - :number-of-lines 1 - :ellipsize-mode :middle} - (gfy/generate-gfy public-key)] - [react/text {:style {:text-align :center - :margin-top 4 - :color colors/gray - :font-family "monospace"} - :number-of-lines 1 - :ellipsize-mode :middle} - (utils.core/truncate-str address 14 true)]]] - [pin.views/pin-view - {:pin pin - :status status - :error-label error-label - :step enter-step}] - [react/view {:margin-bottom 32} - [react/touchable-highlight - {:on-press #(re-frame/dispatch [:keycard.login.ui/recover-key-pressed])} - [react/text {:style {:color colors/blue}} - (i18n/label :t/recover-key)]]]]]))) + {:keys [address name photo-path]} [:multiaccounts/login]] + [react/view styles/container + [toolbar/toolbar + {:transparent? true + :style {:margin-top 32}} + [toolbar/nav-text + {:handler #(re-frame/dispatch [:keycard.login.pin.ui/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 1})] + [react/view {:margin-right 20} + [react/touchable-highlight + {:on-press #(re-frame/dispatch [:keycard.login.pin.ui/more-icon-pressed])} + [vector-icons/icon :main-icons/more {:color colors/black + :container-style {:margin-left 5}}]]]] + [react/view {:flex 1 + :flex-direction :column + :justify-content :space-between + :align-items :center + :margin-top 60} + [react/view {:flex-direction :column + :flex 1 + :justify-content :center + :align-items :center} + [react/view {:margin-horizontal 16 + :flex-direction :column} + [react/view {:justify-content :center + :align-items :center + :flex-direction :row} + [react/view {:width 69 + :height 69 + :justify-content :center + :align-items :center} + [photos/photo photo-path {:size 61}] + [react/view {:justify-content :center + :align-items :center + :width 24 + :height 24 + :border-radius 24 + :position :absolute + :right 0 + :bottom 0 + :background-color :white + :border-width 1 + :border-color colors/black-transparent} + [react/image {:source (resources/get-image :keycard-key) + :style {:width 8 + :height 14}}]]]] + [react/text {:style {:text-align :center + :margin-top 12 + :color colors/black + :font-weight "500"} + :number-of-lines 1 + :ellipsize-mode :middle} + name] + [react/text {:style {:text-align :center + :margin-top 4 + :color colors/gray + :font-family "monospace"} + :number-of-lines 1 + :ellipsize-mode :middle} + (utils.core/truncate-str address 14 true)]]] + [pin.views/pin-view + {:pin pin + :status status + :error-label error-label + :step enter-step}] + [react/view {:margin-bottom 32} + [react/touchable-highlight + {:on-press #(re-frame/dispatch [:keycard.login.ui/recover-key-pressed])} + [react/text {:style {:color colors/blue}} + (i18n/label :t/recover-key)]]]]])) (defview login-connect-card [] (letsubs [status [:hardwallet/pin-status] - {:keys [address public-key]} [:multiaccounts/login]] - (let [address (str "0x" address) - in-progress? (= status :verifying)] + {:keys [address name photo-path]} [:multiaccounts/login]] + (let [in-progress? (= status :verifying)] [react/view styles/container [toolbar/toolbar {:transparent? true @@ -472,12 +471,7 @@ :height 69 :justify-content :center :align-items :center} - [react/image {:source {:uri (identicon/identicon public-key)} - :style {:width 61 - :height 61 - :border-radius 30 - :border-width 1 - :border-color colors/black-transparent}}] + [photos/photo photo-path {:size 61}] [react/view {:justify-content :center :align-items :center :width 24 @@ -498,7 +492,7 @@ :font-weight "500"} :number-of-lines 1 :ellipsize-mode :middle} - (gfy/generate-gfy public-key)] + name] [react/text {:style {:text-align :center :margin-top 4 :color colors/gray diff --git a/src/status_im/ui/screens/multiaccounts/views.cljs b/src/status_im/ui/screens/multiaccounts/views.cljs index 746e3f13da..9ad1cdf20b 100644 --- a/src/status_im/ui/screens/multiaccounts/views.cljs +++ b/src/status_im/ui/screens/multiaccounts/views.cljs @@ -16,7 +16,7 @@ [status-im.ui.screens.privacy-policy.views :as privacy-policy] [status-im.react-native.resources :as resources])) -(defn multiaccount-view [{:keys [address photo-path name public-key keycard-instance-uid]}] +(defn multiaccount-view [{:keys [address photo-path name public-key keycard-key-uid]}] [react/touchable-highlight {:on-press #(re-frame/dispatch [:multiaccounts.login.ui/multiaccount-selected address photo-path name public-key])} [react/view styles/multiaccount-view [photos/photo photo-path {:size styles/multiaccount-image-size}] @@ -29,7 +29,7 @@ [react/text {:style styles/multiaccount-badge-pub-key-text} (utils/get-shortened-address public-key)]] [react/view {:flex 1}] - (when keycard-instance-uid + (when keycard-key-uid [react/view {:justify-content :center :align-items :center :margin-right 7 diff --git a/src/status_im/ui/screens/profile/user/views.cljs b/src/status_im/ui/screens/profile/user/views.cljs index 68c96931c4..ac8e5edf41 100644 --- a/src/status_im/ui/screens/profile/user/views.cljs +++ b/src/status_im/ui/screens/profile/user/views.cljs @@ -22,6 +22,7 @@ [status-im.ui.screens.profile.user.styles :as styles] [status-im.utils.identicon :as identicon] [status-im.utils.platform :as platform] + [status-im.utils.config :as config] [status-im.utils.universal-links.core :as universal-links]) (:require-macros [status-im.utils.views :as views])) @@ -159,6 +160,13 @@ :accessibility-label :sync-settings-button :accessories [:chevron] :on-press #(re-frame/dispatch [:navigate-to :sync-settings])} + (when (and platform/android? + config/hardwallet-enabled?) + {:icon :main-icons/keycard + :title :t/keycard + :accessibility-label :keycard-button + :accessories [:chevron] + :on-press #(re-frame/dispatch [:navigate-to :keycard-settings])}) {:icon :main-icons/settings-advanced :title :t/advanced :accessibility-label :advanced-button diff --git a/src/status_im/ui/screens/routing/profile_stack.cljs b/src/status_im/ui/screens/routing/profile_stack.cljs index 67afbc8e47..92ee28e6af 100644 --- a/src/status_im/ui/screens/routing/profile_stack.cljs +++ b/src/status_im/ui/screens/routing/profile_stack.cljs @@ -39,6 +39,6 @@ config/hardwallet-enabled? (concat [:keycard-settings :reset-card - :hardwallet-connect-settings + :keycard-connection-lost :enter-pin-settings])) :config {:initialRouteName :my-profile}}) diff --git a/src/status_im/ui/screens/signing/views.cljs b/src/status_im/ui/screens/signing/views.cljs index e06732f4dd..5e723f1fb4 100644 --- a/src/status_im/ui/screens/signing/views.cljs +++ b/src/status_im/ui/screens/signing/views.cljs @@ -154,7 +154,7 @@ [amount-error gas-error] [button/button {:on-press #(re-frame/dispatch [:signing.ui/sign-with-keycard-pressed]) :disabled? (or amount-error gas-error) - :label :t/sign-with}]) + :label :t/sign-with-keycard}]) (defn- signing-phrase-view [phrase] [react/view {:align-items :center} @@ -163,7 +163,7 @@ (defn- keycard-view [{:keys [keycard-step]} phrase] - [react/view {:height 450} + [react/view {:height 500} [signing-phrase-view phrase] (case keycard-step :pin [keycard-pin-view] diff --git a/status-go-version.json b/status-go-version.json index b80d694cdd..96727b562a 100644 --- a/status-go-version.json +++ b/status-go-version.json @@ -3,6 +3,6 @@ "owner": "status-im", "repo": "status-go", "version": "develop", - "commit-sha1": "76d184b4c9e666383ba54303b3c4a438ac4519de", - "src-sha256": "141glfhal3chlykz5m3jbmzrv10rprly9yk4xf1ss4a3hd8hj90c" + "commit-sha1": "d263be10c46aa8788bc776069225ab31c52c6c51", + "src-sha256": "0r7hp9a946s1sbh5dbl8s2p0z0yrzyi7pjhbwy3hqm25bbprrql0" } diff --git a/test/cljs/status_im/test/sign_in/data.cljs b/test/cljs/status_im/test/sign_in/data.cljs index 48f7879b41..c9634765c1 100644 --- a/test/cljs/status_im/test/sign_in/data.cljs +++ b/test/cljs/status_im/test/sign_in/data.cljs @@ -147,7 +147,7 @@ :network "mainnet_rpc" :wallet-set-up-passed? false :public-key "0x04173f7cdea0076a7998abb674cc79fe61337c42db77043c01d5b0f3e3ac1e5a45bca0c93bb9f3c3d38b7cc9a7337cd64f9f9b2114fe4bbdfe1ae2633ba14d8c9c" - :keycard-instance-uid nil + :keycard-key-uid nil :installation-id "618ec020-13c8-5505-8aa6-9c5444317e7f"}) (def multiaccounts diff --git a/translations/en.json b/translations/en.json index 5901b9eafc..7cf46855a4 100644 --- a/translations/en.json +++ b/translations/en.json @@ -465,6 +465,7 @@ "generate-new-key": "Generate a new key", "generating-codes-for-pairing": "> Downloading product software to card\n > Generating unlocking & pairing codes", "generating-keys": "Generating keys...", + "you-will-need-this-code": "You'll need this code to open Status and sign transactions", "generating-mnemonic": "Generating mnemonic phrase", "get-started": "Get started", "get-status-at": "Get Status at http://status.im", @@ -555,6 +556,7 @@ "keycard-cancel-setup-title": "Dangerous operation", "keycard-desc": "Android only. You will need to get a Keycard first", "keycard-has-multiaccount-on-it": "This card has already an multiaccount on it. If you wish to change it, login first and reset your card. If you want to import keycard multiaccount, please use \"Add existing multiaccount\"", + "keycard-existing-multiaccount": "You can't recover this multiaccount because it's already on your phone", "keycard-onboarding-finishing-header": "Finishing up", "keycard-onboarding-intro-header": "Store your key on Keycard", "keycard-onboarding-intro-text": "Get ready, this might take a few minutes, but it's important to secure your account",