diff --git a/resources/images/ui2/keycard-buy.png b/resources/images/ui2/keycard-buy.png new file mode 100644 index 0000000000..447aba8758 Binary files /dev/null and b/resources/images/ui2/keycard-buy.png differ diff --git a/resources/images/ui2/keycard-migration-failed.png b/resources/images/ui2/keycard-migration-failed.png new file mode 100644 index 0000000000..437a516d07 Binary files /dev/null and b/resources/images/ui2/keycard-migration-failed.png differ diff --git a/resources/images/ui2/keycard-migration-succeeded.png b/resources/images/ui2/keycard-migration-succeeded.png new file mode 100644 index 0000000000..2e6e8c7c02 Binary files /dev/null and b/resources/images/ui2/keycard-migration-succeeded.png differ diff --git a/resources/images/ui2/keycard-migration.png b/resources/images/ui2/keycard-migration.png new file mode 100644 index 0000000000..0bd5ac2e40 Binary files /dev/null and b/resources/images/ui2/keycard-migration.png differ diff --git a/resources/images/ui2/not-keycard.png b/resources/images/ui2/not-keycard.png new file mode 100644 index 0000000000..a02ae5329c Binary files /dev/null and b/resources/images/ui2/not-keycard.png differ diff --git a/src/keycard/keycard.cljs b/src/keycard/keycard.cljs index 7ea3692fe5..9820200093 100644 --- a/src/keycard/keycard.cljs +++ b/src/keycard/keycard.cljs @@ -3,8 +3,7 @@ ["react-native" :as rn] ["react-native-status-keycard" :default status-keycard] [react-native.platform :as platform] - [taoensso.timbre :as log] - [utils.address :as address])) + [taoensso.timbre :as log])) (defonce event-emitter (if platform/ios? @@ -94,22 +93,14 @@ [{:keys [on-success on-failure]}] (.. status-keycard (getApplicationInfo) - (then (fn [response] - (let [info (-> response - (js->clj :keywordize-keys true) - (update :key-uid address/normalized-hex))] - (on-success info)))) + (then on-success) (catch on-failure))) (defn factory-reset [{:keys [on-success on-failure]}] (.. status-keycard (factoryReset) - (then (fn [response] - (let [info (-> response - (js->clj :keywordize-keys true) - (update :key-uid address/normalized-hex))] - (on-success info)))) + (then on-success) (catch on-failure))) (defn install-applet @@ -155,6 +146,13 @@ (then on-success) (catch on-failure))) +(defn save-mnemonic + [{:keys [mnemonic pin on-success on-failure]}] + (.. status-keycard + (saveMnemonic mnemonic pin) + (then on-success) + (catch on-failure))) + (defn unblock-pin [{:keys [puk new-pin on-success on-failure]}] (when (and new-pin puk) diff --git a/src/native_module/core.cljs b/src/native_module/core.cljs index a279d1bcee..eb5be90c6d 100644 --- a/src/native_module/core.cljs +++ b/src/native_module/core.cljs @@ -508,17 +508,16 @@ new-password-hashed callback)))) -(defn convert-to-keycard-account - [{:keys [key-uid] :as multiaccount-data} settings current-password# new-password callback] - (log/debug "[native-module] convert-to-keycard-account") +(defn convert-to-keycard-profile + [{:keys [key-uid] :as profile} settings password new-password callback] (.convertToKeycardAccount ^js (encryption) key-uid - (types/clj->json multiaccount-data) + (types/clj->json profile) (types/clj->json settings) - "" - current-password# + (:keycard-instance-uid settings) + password new-password - callback)) + #(when callback (callback (types/json->clj %))))) (defn backup-disabled-data-dir [] diff --git a/src/quo/components/keycard/view.cljs b/src/quo/components/keycard/view.cljs index 11d801175d..4759830604 100644 --- a/src/quo/components/keycard/view.cljs +++ b/src/quo/components/keycard/view.cljs @@ -16,7 +16,7 @@ " [{:keys [holder-name locked?]}] (let [theme (quo.theme/use-theme) - label (if (boolean holder-name) + label (if holder-name (i18n/label :t/user-keycard {:name holder-name}) (i18n/label :t/empty-keycard))] [rn/view {:style (style/card-container locked? theme)} diff --git a/src/quo/components/markdown/list/style.cljs b/src/quo/components/markdown/list/style.cljs index 5680007380..aa3f645902 100644 --- a/src/quo/components/markdown/list/style.cljs +++ b/src/quo/components/markdown/list/style.cljs @@ -3,8 +3,10 @@ (defn container [container-style] (merge container-style - {:flex-direction :row - :align-items :flex-start})) + {:padding-vertical 7 + :padding-horizontal 20 + :flex-direction :row + :align-items :flex-start})) (def index {:margin-left 5}) diff --git a/src/quo/components/markdown/list/view.cljs b/src/quo/components/markdown/list/view.cljs index f86c434acf..f823cd8b2b 100644 --- a/src/quo/components/markdown/list/view.cljs +++ b/src/quo/components/markdown/list/view.cljs @@ -45,7 +45,7 @@ :or {type :bullet}}] (let [theme (quo.theme/use-theme)] [rn/view {:style (style/container container-style)} - [rn/view {:style style/index} + [rn/view (case type :step [step/view diff --git a/src/quo/components/text_combinations/page_top/style.cljs b/src/quo/components/text_combinations/page_top/style.cljs index 483a325fdd..87e4ece774 100644 --- a/src/quo/components/text_combinations/page_top/style.cljs +++ b/src/quo/components/text_combinations/page_top/style.cljs @@ -8,8 +8,7 @@ (def header {:flex-direction :row - :justify-content :space-between - :height 32}) + :justify-content :space-between}) (def header-title {:flex 1 diff --git a/src/quo/components/text_combinations/standard_title/view.cljs b/src/quo/components/text_combinations/standard_title/view.cljs index e9a13d796a..16e831d04b 100644 --- a/src/quo/components/text_combinations/standard_title/view.cljs +++ b/src/quo/components/text_combinations/standard_title/view.cljs @@ -59,7 +59,6 @@ [rn/view {:style (merge style/container container-style)} [text/text {:size :heading-1 - :number-of-lines 1 :weight :semi-bold :style style/text :accessibility-label accessibility-label} diff --git a/src/status_im/common/metrics_confirmation_modal/style.cljs b/src/status_im/common/metrics_confirmation_modal/style.cljs index 73843c0ba3..d2a07b5e72 100644 --- a/src/status_im/common/metrics_confirmation_modal/style.cljs +++ b/src/status_im/common/metrics_confirmation_modal/style.cljs @@ -7,8 +7,7 @@ :margin-bottom 8}) (def item-text - {:margin-top 10 - :margin-left -4}) + {:margin-left -24}) (def info-text {:margin-top -5}) diff --git a/src/status_im/common/resources.cljs b/src/status_im/common/resources.cljs index ec7ad142e0..a5969babae 100644 --- a/src/status_im/common/resources.cljs +++ b/src/status_im/common/resources.cljs @@ -22,6 +22,11 @@ :qr-code (js/require "../resources/images/ui2/qr-code.png") :keycard-logo (js/require "../resources/images/ui2/keycard-logo.png") :keycard-watermark (js/require "../resources/images/ui2/keycard-watermark.png") + :keycard-buy (js/require "../resources/images/ui2/keycard-buy.png") + :keycard-migration (js/require "../resources/images/ui2/keycard-migration.png") + :keycard-migration-failed (js/require "../resources/images/ui2/keycard-migration-failed.png") + :keycard-migration-succeeded (js/require "../resources/images/ui2/keycard-migration-succeeded.png") + :not-keycard (js/require "../resources/images/ui2/not-keycard.png") :discover (js/require "../resources/images/ui2/discover.png") :invite-friends (js/require "../resources/images/ui2/invite-friends.png") :transaction-progress (js/require "../resources/images/ui2/transaction-progress.png") diff --git a/src/status_im/common/standard_authentication/enter_password/view.cljs b/src/status_im/common/standard_authentication/enter_password/view.cljs index 398bf78a9a..ec3360c72e 100644 --- a/src/status_im/common/standard_authentication/enter_password/view.cljs +++ b/src/status_im/common/standard_authentication/enter_password/view.cljs @@ -1,9 +1,10 @@ (ns status-im.common.standard-authentication.enter-password.view (:require + [clojure.string :as string] [quo.core :as quo] [react-native.core :as rn] + [status-im.common.standard-authentication.core :as standard-authentication] [status-im.common.standard-authentication.enter-password.style :as style] - [status-im.common.standard-authentication.password-input.view :as password-input] [status-im.contexts.profile.utils :as profile.utils] [utils.i18n :as i18n] [utils.re-frame :as rf])) @@ -31,11 +32,12 @@ :full-name (profile.utils/displayed-name profile) :customization-color customization-color :size 24}]] - [password-input/view + [standard-authentication/password-input {:on-press-biometrics on-press-biometrics :blur? true :processing processing - :error error + :error {:error? (not (string/blank? error)) + :error-message error} :default-password password :sign-in-enabled? sign-in-enabled?}] [quo/button diff --git a/src/status_im/common/standard_authentication/password_input/view.cljs b/src/status_im/common/standard_authentication/password_input/view.cljs index 3f39d93616..1c9595d186 100644 --- a/src/status_im/common/standard_authentication/password_input/view.cljs +++ b/src/status_im/common/standard_authentication/password_input/view.cljs @@ -1,6 +1,5 @@ (ns status-im.common.standard-authentication.password-input.view (:require - [clojure.string :as string] [quo.core :as quo] [quo.foundations.colors :as colors] [quo.theme :as quo.theme] @@ -12,14 +11,6 @@ [utils.re-frame :as rf] [utils.security.core :as security])) -(defn- get-error-message - [error] - (if (and (some? error) - (or (= error "file is not a database") - (string/starts-with? (string/lower-case error) "failed"))) - (i18n/label :t/oops-wrong-password) - error)) - (defn- error-info [error-message processing shell?] (let [theme (quo.theme/use-theme) @@ -49,21 +40,21 @@ (i18n/label :t/forgot-password)]]])) (defn view - [{:keys [shell? on-press-biometrics blur?]}] - (let [{:keys [error processing]} (rf/sub [:profile/login]) - error-message (rn/use-memo #(get-error-message error) [error]) - error? (boolean (seq error-message)) - default-value (rn/use-ref-atom "") ;;bug on Android - ;;https://github.com/status-im/status-mobile/issues/19004 - theme (quo.theme/use-theme) - on-change-password (rn/use-callback - (fn [entered-password] - (reset! default-value entered-password) - (debounce/debounce-and-dispatch [:profile/on-password-input-changed - {:password (security/mask-data - entered-password) - :error ""}] - 100)))] + [{:keys [shell? on-press-biometrics blur? processing error]}] + (let [{:keys [error? + error-message]} error + default-value (rn/use-ref-atom "") ;;bug on Android + ;;https://github.com/status-im/status-mobile/issues/19004 + theme (quo.theme/use-theme) + on-change-password (rn/use-callback + (fn [entered-password] + (reset! default-value entered-password) + (debounce/debounce-and-dispatch + [:profile/on-password-input-changed + {:password (security/mask-data + entered-password) + :error ""}] + 100)))] [:<> [rn/view {:style {:flex-direction :row}} [quo/input diff --git a/src/status_im/constants.cljs b/src/status_im/constants.cljs index 3aa0e4afdf..f69b2bdbc7 100644 --- a/src/status_im/constants.cljs +++ b/src/status_im/constants.cljs @@ -580,13 +580,14 @@ (def ^:const sheet-screen-handle-height 20) (def ^:const status-hostname "status.app") +(def ^:const get-keycard-url "https://get.keycard.tech/") (def ^:const community-joined-notification-type "communityJoined") (def ^:const default-telemetry-server-url "https://telemetry.status.im") (def ^:const contact-item-height 56) - +(def ^:const page-nav-height 56) (def ^:const currency-item-height 64) (def ^:const slippages [0.1 0.5 1]) diff --git a/src/status_im/contexts/keycard/authorise/view.cljs b/src/status_im/contexts/keycard/authorise/view.cljs index 8c91198f9f..a5fa3d4f14 100644 --- a/src/status_im/contexts/keycard/authorise/view.cljs +++ b/src/status_im/contexts/keycard/authorise/view.cljs @@ -4,20 +4,18 @@ [status-im.common.events-helper :as events-helper] [status-im.common.standard-authentication.core :as standard-auth] [utils.i18n :as i18n] - [utils.re-frame :as rf] - [utils.security.core :as security])) + [utils.re-frame :as rf])) (defn view [] - (let [profile-name (rf/sub [:profile/name]) - profile-picture (rf/sub [:profile/image]) - customization-color (rf/sub [:profile/customization-color])] + (let [profile-name (rf/sub [:profile/name]) + profile-picture (rf/sub [:profile/image]) + customization-color (rf/sub [:profile/customization-color]) + {:keys [on-success]} (rf/sub [:get-screen-params])] [:<> [quo/page-nav - {:key :header - :background :blur - :icon-name :i/close - :on-press events-helper/navigate-back}] + {:icon-name :i/close + :on-press events-helper/navigate-back}] [quo/page-top {:title (i18n/label :t/authorise-with-password) :description :context-tag @@ -31,5 +29,5 @@ :container-style {} :customization-color customization-color :track-text (i18n/label :t/slide-to-authorise) - :on-auth-success #(println "TBD" (security/safe-unmask-data %)) + :on-auth-success #(when on-success (on-success %)) :auth-button-label (i18n/label :t/confirm)}]]])) diff --git a/src/status_im/contexts/keycard/check/view.cljs b/src/status_im/contexts/keycard/check/view.cljs index 07d387a5f0..a69fdc2b35 100644 --- a/src/status_im/contexts/keycard/check/view.cljs +++ b/src/status_im/contexts/keycard/check/view.cljs @@ -10,10 +10,8 @@ [] [:<> [quo/page-nav - {:key :header - :background :blur - :icon-name :i/arrow-left - :on-press events-helper/navigate-back}] + {:icon-name :i/arrow-left + :on-press events-helper/navigate-back}] [quo/page-top {:title (i18n/label :t/check-keycard) :description :text @@ -23,14 +21,9 @@ {:resize-mode :contain :source (resources/get-image :check-your-keycard)}]] [quo/divider-label (i18n/label :t/tips-scan-keycard)] - [rn/view {:style {:padding-horizontal 10}} - [quo/markdown-list - {:container-style {:padding-vertical 10} - :description (i18n/label :t/remove-phone-case)}] - [quo/markdown-list - {:container-style {:padding-bottom 25} - :description (i18n/label :t/keep-card-steady)}]] - [quo/button - {:on-press #(rf/dispatch [:keycard/connect]) - :container-style {:margin-horizontal 20}} - (i18n/label :t/ready-to-scan)]]) + [quo/markdown-list {:description (i18n/label :t/remove-phone-case)}] + [quo/markdown-list {:description (i18n/label :t/keep-card-steady)}] + [quo/bottom-actions + {:actions :one-action + :button-one-label (i18n/label :t/ready-to-scan) + :button-one-props {:on-press #(rf/dispatch [:keycard/migration.check-empty-card])}}]]) diff --git a/src/status_im/contexts/keycard/effects.cljs b/src/status_im/contexts/keycard/effects.cljs index 04fe3858ec..05bebd4777 100644 --- a/src/status_im/contexts/keycard/effects.cljs +++ b/src/status_im/contexts/keycard/effects.cljs @@ -3,10 +3,10 @@ [native-module.core :as native-module] [react-native.async-storage :as async-storage] [react-native.platform :as platform] + status-im.contexts.keycard.nfc.effects + [status-im.contexts.keycard.utils :as keycard.utils] [status-im.contexts.profile.config :as profile.config] - [taoensso.timbre :as log] - [utils.re-frame :as rf] - [utils.transforms :as transforms])) + [utils.re-frame :as rf])) (defonce ^:private active-listeners (atom [])) @@ -32,106 +32,45 @@ (rf/reg-fx :effects.keycard/register-card-events register-card-events) (rf/reg-fx :effects.keycard/unregister-card-events unregister-card-events) -(defn check-nfc-enabled - [] - (log/debug "[keycard] check-nfc-enabled") - (keycard/check-nfc-enabled - {:on-success - (fn [response] - (log/debug "[keycard response] check-nfc-enabled") - (rf/dispatch [:keycard/on-check-nfc-enabled-success response]))})) -(rf/reg-fx :effects.keycard/check-nfc-enabled check-nfc-enabled) +(rf/reg-fx :effects.keycard/get-application-info + (fn [args] + (keycard/get-application-info (keycard.utils/wrap-handlers args)))) + +(rf/reg-fx :effects.keycard/get-keys + (fn [args] + (keycard/get-keys (keycard.utils/wrap-handlers args)))) + +(rf/reg-fx :effects.keycard/sign + (fn [args] + (keycard/sign (keycard.utils/wrap-handlers args)))) + +(rf/reg-fx :keycard/init-card + (fn [args] + (keycard/init-card (keycard.utils/wrap-handlers args)))) + +(rf/reg-fx :effects.keycard/generate-and-load-key + (fn [args] + (keycard/generate-and-load-key (keycard.utils/wrap-handlers args)))) + +(rf/reg-fx :effects.keycard/login-with-keycard + (fn [{:keys [key-uid password whisper-private-key]}] + (native-module/login-account + (assoc (profile.config/login) + :keyUid key-uid + :password password + :keycardWhisperPrivateKey whisper-private-key)))) + +(rf/reg-fx :effects.keycard/set-pairing-to-keycard + (fn [pairings] + (keycard/set-pairings pairings))) (rf/reg-fx - :effects.keycard.ios/start-nfc - (fn [args] - (log/debug "fx start-nfc") - (keycard/start-nfc args))) + :keycard/persist-pairings + (fn [pairings] + (async-storage/set-item! "status-keycard-pairings" pairings))) -(rf/reg-fx - :effects.keycard.ios/stop-nfc - (fn [args] - (log/debug "fx stop-nfc") - (keycard/stop-nfc args))) - -(defn- error-object->map - [^js object] - {:code (.-code object) - :error (.-message object)}) - -(defn get-application-info - [{:keys [on-success on-failure] :as args}] - (log/debug "[keycard] get-application-info") - (keycard/get-application-info - (assoc - args - :on-success - (fn [response] - (log/debug "[keycard response succ] get-application-info") - (when on-success - (on-success response))) - :on-failure - (fn [response] - (log/error "[keycard response fail] get-application-info") - (when on-failure - (on-failure (error-object->map response))))))) -(rf/reg-fx :effects.keycard/get-application-info get-application-info) - -(defn get-keys - [{:keys [on-success on-failure] :as args}] - (log/debug "[keycard] get-keys") - (keycard/get-keys - (assoc - args - :on-success - (fn [response] - (log/debug "[keycard response succ] get-keys") - (when on-success - (on-success (transforms/js->clj response)))) - :on-failure - (fn [response] - (log/warn "[keycard response fail] get-keys" - (error-object->map response)) - (when on-failure - (on-failure (error-object->map response))))))) -(rf/reg-fx :effects.keycard/get-keys get-keys) - -(defn login - [{:keys [key-uid password whisper-private-key]}] - (native-module/login-account - (assoc (profile.config/login) - :keyUid key-uid - :password password - :keycardWhisperPrivateKey whisper-private-key))) -(rf/reg-fx :effects.keycard/login-with-keycard login) - -(defn retrieve-pairings - [] - (async-storage/get-item - "status-keycard-pairings" - #(rf/dispatch [:keycard/on-retrieve-pairings-success %]))) -(rf/reg-fx :effects.keycard/retrieve-pairings retrieve-pairings) - -(defn set-pairing-to-keycard - [pairings] - (keycard/set-pairings pairings)) -(rf/reg-fx :effects.keycard/set-pairing-to-keycard set-pairing-to-keycard) - -(defn sign - [{:keys [on-success on-failure] :as args}] - (log/debug "[keycard] sign") - (keycard/sign - (assoc - args - :on-success - (fn [response] - (log/debug "[keycard response succ] sign") - (when on-success - (on-success (transforms/js->clj response)))) - :on-failure - (fn [response] - (log/warn "[keycard response fail] sign" - (error-object->map response)) - (when on-failure - (on-failure (error-object->map response))))))) -(rf/reg-fx :effects.keycard/sign sign) +(rf/reg-fx :effects.keycard/retrieve-pairings + (fn [] + (async-storage/get-item + "status-keycard-pairings" + #(rf/dispatch [:keycard/on-retrieve-pairings-success %])))) diff --git a/src/status_im/contexts/keycard/empty/view.cljs b/src/status_im/contexts/keycard/empty/view.cljs index 6155fbff03..500d2ed162 100644 --- a/src/status_im/contexts/keycard/empty/view.cljs +++ b/src/status_im/contexts/keycard/empty/view.cljs @@ -3,7 +3,7 @@ [react-native.core :as rn] [status-im.common.events-helper :as events-helper] [status-im.common.resources :as resources] - [status-im.contexts.keycard.sheets.migrate.view :as sheets.migrate] + [status-im.contexts.keycard.migrate.sheets.view :as sheets.migrate] [utils.i18n :as i18n] [utils.re-frame :as rf])) @@ -11,10 +11,8 @@ [] [:<> [quo/page-nav - {:key :header - :background :blur - :icon-name :i/arrow-left - :on-press events-helper/navigate-back}] + {:icon-name :i/close + :on-press events-helper/navigate-back}] [quo/page-top {:title (i18n/label :t/keycard-empty) :description :text @@ -26,10 +24,15 @@ :subtitle (i18n/label :t/use-keycard-login-sign) :button-label (i18n/label :t/import-profile-key-pair) :accessibility-label :get-keycard - :image (resources/get-image :generate-keys) - :on-press #(rf/dispatch [:show-bottom-sheet - {:theme :dark - :content (fn [] [sheets.migrate/view])}])}]] + :image (resources/get-image :keycard-buy) + :on-press (fn [] + (rf/dispatch + [:show-bottom-sheet + {:theme :dark + :content (fn [] + [sheets.migrate/view + {:on-continue #(rf/dispatch + [:keycard/migration.get-phrase])}])}]))}]] [quo/information-box {:type :default :style {:margin-top 32 :margin-horizontal 28}} diff --git a/src/status_im/contexts/keycard/error/view.cljs b/src/status_im/contexts/keycard/error/view.cljs index 3f023cdcf7..2da99270cb 100644 --- a/src/status_im/contexts/keycard/error/view.cljs +++ b/src/status_im/contexts/keycard/error/view.cljs @@ -1,42 +1,37 @@ (ns status-im.contexts.keycard.error.view (:require [quo.core :as quo] [react-native.core :as rn] - [react-native.safe-area :as safe-area] [status-im.common.events-helper :as events-helper] + [utils.i18n :as i18n] [utils.re-frame :as rf])) (def titles - {:keycard/error.keycard-wrong {:title "Keycard is not empty" - :description "You can’t use it to store new keys right now"} - :keycard/error.keycard-unpaired {:title "Keycard is full" - :description "All pairing slots are occupied"} - :keycard/error.keycard-frozen {:title "Keycard is locked" - :description "You can’t use it right now"} - :keycard/error.keycard-locked {:title "Keycard is locked" - :description "You can’t use it right now"}}) + {:keycard/error.keycard-blank {:title (i18n/label :t/keycard-empty) + :description (i18n/label :t/no-key-pair-keycard)} + :keycard/error.keycard-wrong-profile {:title (i18n/label :t/keycard-not-empty) + :description (i18n/label :t/cant-store-new-keys)} + :keycard/error.keycard-unpaired {:title (i18n/label :t/keycard-full) + :description (i18n/label :t/pairing-slots-occupied)} + :keycard/error.keycard-frozen {:title (i18n/label :t/keycard-locked) + :description (i18n/label :t/cant-use-right-now)} + :keycard/error.keycard-locked {:title (i18n/label :t/keycard-locked) + :description (i18n/label :t/cant-use-right-now)}}) (defn view [] - (let [{:keys [top bottom]} (safe-area/get-insets) - error (rf/sub [:keycard/application-info-error]) + (let [error (rf/sub [:keycard/application-info-error]) {:keys [title description]} (get titles error)] - [quo/overlay - {:type :shell - :container-style {:padding-top top - :padding-bottom bottom}} + [:<> [quo/page-nav - {:key :header - :background :blur - :icon-name :i/arrow-left - :on-press events-helper/navigate-back}] + {:icon-name :i/close + :on-press events-helper/navigate-back}] [quo/page-top {:title title :description :text :description-text description}] - [rn/view {:height 226}] - [rn/view {:padding-horizontal 20} - [quo/info-message - {:container-style {:padding-top 15} - :icon :i/info - :size :default} - "To unlock or factory reset the Keycard, please use the Status desktop app. If you'd like this features on mobile, feel free to upvote them and discuss in the Status community."]]])) + [rn/view {:style {:margin-horizontal 20}} + [quo/keycard {:holder-name ""}] + [quo/information-box + {:type :default + :style {:margin-top 20}} + (i18n/label :t/unlock-reset-instructions)]]])) diff --git a/src/status_im/contexts/keycard/events.cljs b/src/status_im/contexts/keycard/events.cljs index f6be5b6dc9..19add51959 100644 --- a/src/status_im/contexts/keycard/events.cljs +++ b/src/status_im/contexts/keycard/events.cljs @@ -1,116 +1,117 @@ (ns status-im.contexts.keycard.events (:require [re-frame.core :as rf] status-im.contexts.keycard.login.events - status-im.contexts.keycard.nfc-sheet.events + status-im.contexts.keycard.migrate.events + status-im.contexts.keycard.migrate.re-encrypting.events + status-im.contexts.keycard.nfc.events + status-im.contexts.keycard.nfc.sheets.events status-im.contexts.keycard.pin.events status-im.contexts.keycard.sign.events [status-im.contexts.keycard.utils :as keycard.utils] - [taoensso.timbre :as log])) - -(rf/reg-event-fx :keycard/on-check-nfc-enabled-success - (fn [{:keys [db]} [nfc-enabled?]] - {:db (assoc-in db [:keycard :nfc-enabled?] nfc-enabled?)})) - -(rf/reg-event-fx :keycard.ios/on-nfc-user-cancelled - (fn [{:keys [db]}] - (log/debug "[keycard] nfc user cancelled") - {:db (-> db - (assoc-in [:keycard :pin :status] nil) - (assoc-in [:keycard :on-nfc-cancelled-event-vector] nil)) - :fx [(when-let [on-nfc-cancelled-event-vector (get-in db [:keycard :on-nfc-cancelled-event-vector])] - [:dispatch on-nfc-cancelled-event-vector])]})) + utils.datetime)) (rf/reg-event-fx :keycard/on-card-connected (fn [{:keys [db]} _] - (log/debug "[keycard] card globally connected") {:db (assoc-in db [:keycard :card-connected?] true) :fx [(when-let [event (get-in db [:keycard :on-card-connected-event-vector])] [:dispatch event])]})) (rf/reg-event-fx :keycard/on-card-disconnected (fn [{:keys [db]} _] - (log/debug "[keycard] card disconnected") {:db (assoc-in db [:keycard :card-connected?] false) :fx [(when-let [event (get-in db [:keycard :on-card-disconnected-event-vector])] [:dispatch event])]})) -(rf/reg-event-fx :keycard.ios/start-nfc - (fn [_] - {:effects.keycard.ios/start-nfc nil})) - -(rf/reg-event-fx :keycard.ios/on-nfc-timeout - (fn [{:keys [db]} _] - (log/debug "[keycard] nfc timeout") - {:db (assoc-in db [:keycard :card-connected?] false) - :fx [[:dispatch-later [{:ms 500 :dispatch [:keycard.ios/start-nfc]}]]]})) - (rf/reg-event-fx :keycard/on-retrieve-pairings-success (fn [{:keys [db]} [pairings]] {:db (assoc-in db [:keycard :pairings] pairings) :fx [[:effects.keycard/set-pairing-to-keycard pairings]]})) -(rf/reg-event-fx :keycard.ios/on-start-nfc-success - (fn [{:keys [db]} [{:keys [on-cancel-event-vector]}]] - (log/debug "[keycard] nfc started success") - {:db (assoc-in db [:keycard :on-nfc-cancelled-event-vector] on-cancel-event-vector)})) +(rf/reg-event-fx :keycard/update-pairings + (fn [{:keys [db]} [instance-uid pairing]] + (let [pairings (get-in db [:keycard :pairings]) + new-pairings (assoc pairings + instance-uid + {:pairing pairing + :paired-on (utils.datetime/timestamp)})] + {:db (assoc-in db [:keycard :pairings] new-pairings) + :keycard/persist-pairings new-pairings}))) (rf/reg-event-fx :keycard/on-action-with-pin-error (fn [{:keys [db]} [error]] - (log/debug "[keycard] on-action-with-pin-error: " error) (let [tag-was-lost? (keycard.utils/tag-lost? (:error error)) pin-retries-count (keycard.utils/pin-retries (:error error))] - (if tag-was-lost? + (if (or tag-was-lost? (nil? pin-retries-count)) {:db (assoc-in db [:keycard :pin :status] nil)} - (if (nil? pin-retries-count) - {:effects.utils/show-popup {:title "wrong-keycard"}} - {:db (-> db - (assoc-in [:keycard :application-info :pin-retry-counter] pin-retries-count) - (assoc-in [:keycard :pin :status] :error)) - :fx [[:dispatch [:keycard/disconnect]] - (when (zero? pin-retries-count) - [:effects.utils/show-popup {:title "frozen-keycard"}])]}))))) - -(rf/reg-event-fx :keycard/on-get-application-info-success - (fn [{:keys [db]} [application-info {:keys [key-uid on-success-fx]}]] - (if-let [error (keycard.utils/validate-application-info key-uid application-info)] - (case error - :keycard/error.not-keycard - {:fx [[:dispatch [:keycard/disconnect]] - [:dispatch [:open-modal :screen/keycard.not-keycard]]]} - :keycard/error.keycard-blank - {:fx [[:dispatch [:keycard/disconnect]] - [:dispatch [:open-modal :screen/keycard.empty]]]} - {:db (assoc-in db [:keycard :application-info-error] error) + {:db (-> db + (assoc-in [:keycard :application-info :pin-retry-counter] pin-retries-count) + (assoc-in [:keycard :pin :status] :error)) :fx [[:dispatch [:keycard/disconnect]] - [:dispatch [:open-modal :screen/keycard.error]]]}) - {:db (-> db - (assoc-in [:keycard :application-info] application-info) - (assoc-in [:keycard :pin :status] :verifying)) - :fx on-success-fx}))) + (when (zero? pin-retries-count) + [:dispatch + [:keycard/on-application-info-error + :keycard/error.keycard-locked]])]})))) -(rf/reg-event-fx :keycard/get-application-info - (fn [_ [{:keys [on-success on-failure]}]] - {:effects.keycard/get-application-info {:on-success on-success :on-failure on-failure}})) +(rf/reg-event-fx :keycard/get-keys + (fn [_ [data]] + {:effects.keycard/get-keys data})) (rf/reg-event-fx :keycard/cancel-connection (fn [{:keys [db]}] - {:db (-> db - (assoc-in [:keycard :on-card-connected-event-vector] nil) - (assoc-in [:keycard :on-nfc-cancelled-event-vector] nil))})) + {:db (update db :keycard dissoc :on-card-connected-event-vector :on-nfc-cancelled-event-vector)})) (rf/reg-event-fx :keycard/disconnect (fn [_ _] {:fx [[:dispatch [:keycard/cancel-connection]] [:dispatch [:keycard/hide-connection-sheet]]]})) +(rf/reg-event-fx :keycard/on-application-info-error + (fn [{:keys [db]} [error]] + {:db (assoc-in db [:keycard :application-info-error] error) + :fx [[:dispatch [:keycard/disconnect]] + [:dispatch + [:open-modal + (if (= :keycard/error.not-keycard error) + :screen/keycard.not-keycard + :screen/keycard.error)]]]})) + +(rf/reg-event-fx :keycard/update-application-info + (fn [{:keys [db]} [app-info]] + {:db (update db + :keycard + #(-> % + (assoc :application-info app-info) + (dissoc :application-info-error)))})) + +(rf/reg-event-fx :keycard/get-application-info + (fn [_ [{:keys [key-uid on-success on-error]}]] + {:effects.keycard/get-application-info + {:on-success (fn [{:keys [instance-uid new-pairing] :as app-info}] + (rf/dispatch [:keycard/update-application-info app-info]) + (when (and instance-uid new-pairing) + (rf/dispatch [:keycard/update-pairings instance-uid new-pairing])) + (if-let [error (keycard.utils/validate-application-info key-uid app-info)] + (if on-error + (on-error error) + (rf/dispatch [:keycard/on-application-info-error error])) + (when on-success (on-success app-info)))) + :on-error (fn [] + (if on-error + (on-error :keycard/error.not-keycard) + (rf/dispatch [:keycard/on-application-info-error + :keycard/error.not-keycard])))}})) + (rf/reg-event-fx :keycard/connect - (fn [{:keys [db]} [args]] - (let [connected? (get-in db [:keycard :card-connected?]) - event-vector [:keycard/get-application-info - {:on-success #(rf/dispatch [:keycard/on-get-application-info-success % args])}]] + (fn [{:keys [db]} [{:keys [key-uid on-success on-error on-connect-event-vector]}]] + (let [event-vector + (or on-connect-event-vector + [:keycard/get-application-info + {:key-uid key-uid + :on-success on-success + :on-error on-error}])] {:db (assoc-in db [:keycard :on-card-connected-event-vector] event-vector) :fx [[:dispatch [:keycard/show-connection-sheet {:on-cancel-event-vector [:keycard/cancel-connection]}]] - (when connected? + (when (get-in db [:keycard :card-connected?]) [:dispatch event-vector])]}))) diff --git a/src/status_im/contexts/keycard/login/events.cljs b/src/status_im/contexts/keycard/login/events.cljs index e5cc657a1c..bb6026bd00 100644 --- a/src/status_im/contexts/keycard/login/events.cljs +++ b/src/status_im/contexts/keycard/login/events.cljs @@ -5,7 +5,6 @@ (fn [{:keys [db]} [data]] (let [{:keys [key-uid encryption-public-key whisper-private-key]} data - key-uid (str "0x" key-uid) profile (get-in db [:profile/profiles-overview key-uid])] {:db (-> db diff --git a/src/status_im/contexts/keycard/migrate/events.cljs b/src/status_im/contexts/keycard/migrate/events.cljs new file mode 100644 index 0000000000..29583cead3 --- /dev/null +++ b/src/status_im/contexts/keycard/migrate/events.cljs @@ -0,0 +1,132 @@ +(ns status-im.contexts.keycard.migrate.events + (:require [clojure.string :as string] + [status-im.contexts.keycard.pin.view :as keycard.pin] + [utils.re-frame :as rf] + [utils.security.core :as security])) + +(rf/reg-event-fx :keycard/migration.check-empty-card + (fn [{:keys [db]}] + {:fx [[:dispatch + [:keycard/connect + {:key-uid (get-in db [:profile/profile :key-uid]) + :on-success + (fn [] + ;;TODO keys already on the keycard, new flow needs to be implemented + ;; https://github.com/status-im/status-mobile/issues/21446 + (rf/dispatch [:keycard/disconnect]) + (rf/dispatch [:open-modal :screen/keycard.authorise + {:on-success + #(rf/dispatch [:keycard/migration.authorisation-success %])}])) + :on-error + (fn [error] + (if (= error :keycard/error.keycard-blank) + (do + (rf/dispatch [:keycard/disconnect]) + (rf/dispatch [:open-modal :screen/keycard.empty])) + (rf/dispatch [:keycard/on-application-info-error error])))}]]]})) + +(defn get-application-info-and-continue + [key-uid] + (rf/dispatch [:keycard/get-application-info + {:key-uid key-uid + :on-success #(rf/dispatch [:keycard/migration.continue]) + :on-error + (fn [error] + (if (= error :keycard/error.keycard-blank) + (rf/dispatch [:keycard/migration.continue]) + (rf/dispatch [:keycard/on-application-info-error error])))}])) + +(rf/reg-event-fx :keycard/migration.continue + (fn [{:keys [db]}] + (let [key-uid (get-in db [:profile/profile :key-uid]) + {:keys [initialized? has-master-key?]} (get-in db [:keycard :application-info]) + {:keys [masked-phrase pin]} (get-in db [:keycard :migration]) + on-failure (fn [] + (rf/dispatch [:keycard/disconnect]) + (rf/dispatch [:navigate-to + :screen/keycard.migrate.fail]))] + (cond + + (not initialized?) + {:fx [[:keycard/init-card + {:pin pin + :on-success #(get-application-info-and-continue key-uid) + :on-failure on-failure}]]} + + (not has-master-key?) + {:fx [[:effects.keycard/generate-and-load-key + {:mnemonic (security/safe-unmask-data masked-phrase) + :pin pin + :on-success #(get-application-info-and-continue key-uid) + :on-failure on-failure}]]} + + :else + {:fx [[:effects.keycard/get-keys + {:pin pin + :on-success #(rf/dispatch [:keycard/migration.convert-to-keycard-profile %]) + :on-failure on-failure}]]})))) + +(rf/reg-event-fx :keycard/migration.start + (fn [{:keys [db]}] + {:fx [[:dispatch + [:keycard/connect + {:key-uid (get-in db [:profile/profile :key-uid]) + :on-success #(rf/dispatch [:keycard/migration.continue]) + :on-error + (fn [error] + (if (= error :keycard/error.keycard-blank) + (rf/dispatch [:keycard/migration.continue]) + (rf/dispatch [:keycard/on-application-info-error error])))}]]]})) + +(rf/reg-event-fx :keycard/migration.get-phrase + (fn [{:keys [db]}] + {:db (assoc-in db [:keycard :migration] nil) + :fx [[:dispatch [:navigate-back]] + (if (string/blank? (get-in db [:profile/profile :mnemonic])) + [:dispatch + [:open-modal :screen/use-recovery-phrase + {:on-success #(rf/dispatch [:keycard/migration.phrase-entered %])}]] + [:dispatch + [:open-modal :screen/backup-recovery-phrase + {:on-success #(rf/dispatch [:keycard/migration.phrase-backed-up %])}]])]})) + +(rf/reg-event-fx :keycard/migration.phrase-entered + (fn [{:keys [db]} [{:keys [phrase]}]] + {:db (assoc-in db [:keycard :migration :masked-phrase] (security/mask-data phrase)) + :fx [[:dispatch [:navigate-back]] + [:dispatch + [:open-modal :screen/keycard.authorise + {:on-success #(rf/dispatch [:keycard/migration.authorisation-success %])}]]]})) + +(rf/reg-event-fx :keycard/migration.phrase-backed-up + (fn [{:keys [db]}] + {:db (assoc-in db + [:keycard :migration :masked-phrase] + (security/mask-data (get-in db [:profile/profile :mnemonic]))) + :fx [[:dispatch [:profile.settings/profile-update :mnemonic nil]] + [:dispatch [:navigate-back]] + [:dispatch + [:open-modal :screen/keycard.authorise + {:on-success #(rf/dispatch [:keycard/migration.authorisation-success %])}]]]})) + +(rf/reg-event-fx :keycard/migration.authorisation-success + (fn [{:keys [db]} [masked-password]] + (let [{:keys [initialized?]} (get-in db [:keycard :application-info])] + {:db (assoc-in db [:keycard :migration :masked-password] masked-password) + :fx [[:dispatch [:navigate-back]] + (if initialized? + [:dispatch + [:show-bottom-sheet + {:content (fn [] + [keycard.pin/auth + {:on-complete #(rf/dispatch [:keycard/migration.pin-created %])}])}]] + [:dispatch + [:open-modal :screen/keycard.pin.create + {:on-complete (fn [new-pin] + (rf/dispatch [:navigate-back]) + (rf/dispatch [:keycard/migration.pin-created new-pin]))}]])]}))) + +(rf/reg-event-fx :keycard/migration.pin-created + (fn [{:keys [db]} [pin]] + {:db (assoc-in db [:keycard :migration :pin] pin) + :fx [[:dispatch [:open-modal :screen/keycard.migrate]]]})) diff --git a/src/status_im/contexts/keycard/migrate/fail/view.cljs b/src/status_im/contexts/keycard/migrate/fail/view.cljs new file mode 100644 index 0000000000..b594dbbee3 --- /dev/null +++ b/src/status_im/contexts/keycard/migrate/fail/view.cljs @@ -0,0 +1,32 @@ +(ns status-im.contexts.keycard.migrate.fail.view + (:require [quo.core :as quo] + [react-native.core :as rn] + [status-im.common.resources :as resources] + [status-im.constants :as constants] + [utils.i18n :as i18n] + [utils.re-frame :as rf])) + +(defn view + [] + (let [profile-name (rf/sub [:profile/name]) + profile-picture (rf/sub [:profile/image]) + customization-color (rf/sub [:profile/customization-color])] + [:<> + [quo/page-top + {:title (i18n/label :t/failed-to-migrate-key-pair) + :description :context-tag + :context-tag {:full-name profile-name + :profile-picture profile-picture + :customization-color customization-color} + :container-style {:margin-top constants/page-nav-height}}] + [rn/view {:style {:flex 1 :align-items :center :justify-content :center}} + [rn/image + {:resize-mode :contain + :source (resources/get-image :keycard-migration-failed)}]] + [quo/divider-label (i18n/label :t/what-you-can-do)] + [quo/markdown-list {:description (i18n/label :t/log-out-remove-profile)}] + [quo/markdown-list {:description (i18n/label :t/recover-status-profile)}] + [quo/bottom-actions + {:actions :one-action + :button-one-label (i18n/label :t/log-out-remove) + :button-one-props {:on-press #(rf/dispatch [:logout])}}]])) diff --git a/src/status_im/contexts/keycard/migrate/re_encrypting/events.cljs b/src/status_im/contexts/keycard/migrate/re_encrypting/events.cljs new file mode 100644 index 0000000000..69a8df5a6d --- /dev/null +++ b/src/status_im/contexts/keycard/migrate/re_encrypting/events.cljs @@ -0,0 +1,31 @@ +(ns status-im.contexts.keycard.migrate.re-encrypting.events + (:require [clojure.string :as string] + [utils.re-frame :as rf] + [utils.security.core :as security])) + +(rf/reg-event-fx :keycard/migration.convert-to-keycard-profile + (fn [{:keys [db]} [{:keys [key-uid instance-uid encryption-public-key]}]] + (let [{:keys [masked-password]} (get-in db [:keycard :migration]) + {:keys [pairing paired-on]} (get-in db [:keycard :pairings instance-uid]) + {:keys [kdfIterations]} (:profile/profile db)] + {:fx [[:dispatch [:keycard/disconnect]] + [:dispatch [:navigate-back]] + [:dispatch [:open-modal :screen/keycard.re-encrypting]] + [:effects.profile/convert-to-keycard-profile + {:profile + {:key-uid key-uid + :keycard-pairing pairing + :kdfIterations kdfIterations} + :settings + {:keycard-instance-uid instance-uid + :keycard-paired-on paired-on + :keycard-pairing pairing} + :password + (security/safe-unmask-data masked-password) + :new-password + encryption-public-key + :callback + (fn [{:keys [error]}] + (if (string/blank? error) + (rf/dispatch [:navigate-to :screen/keycard.migrate.success]) + (rf/dispatch [:navigate-to :screen/keycard.migrate.fail])))}]]}))) diff --git a/src/status_im/contexts/keycard/migrate/re_encrypting/view.cljs b/src/status_im/contexts/keycard/migrate/re_encrypting/view.cljs new file mode 100644 index 0000000000..bea83c3fd3 --- /dev/null +++ b/src/status_im/contexts/keycard/migrate/re_encrypting/view.cljs @@ -0,0 +1,27 @@ +(ns status-im.contexts.keycard.migrate.re-encrypting.view + (:require [quo.core :as quo] + [react-native.core :as rn] + [status-im.common.resources :as resources] + [status-im.constants :as constants] + [utils.i18n :as i18n] + [utils.re-frame :as rf])) + +(defn view + [] + (let [profile-name (rf/sub [:profile/name]) + profile-picture (rf/sub [:profile/image]) + customization-color (rf/sub [:profile/customization-color])] + [:<> + [quo/page-top + {:title (i18n/label :t/re-encrypting-data) + :description :context-tag + :context-tag {:full-name profile-name + :profile-picture profile-picture + :customization-color customization-color} + :container-style {:margin-top constants/page-nav-height}}] + [quo/text {:style {:padding-horizontal 20}} + (i18n/label :t/do-not-quit)] + [rn/view {:style {:flex 1 :align-items :center :justify-content :center}} + [rn/image + {:resize-mode :contain + :source (resources/get-image :keycard-migration)}]]])) diff --git a/src/status_im/contexts/keycard/sheets/migrate/view.cljs b/src/status_im/contexts/keycard/migrate/sheets/view.cljs similarity index 58% rename from src/status_im/contexts/keycard/sheets/migrate/view.cljs rename to src/status_im/contexts/keycard/migrate/sheets/view.cljs index 60b038df3b..886aef4654 100644 --- a/src/status_im/contexts/keycard/sheets/migrate/view.cljs +++ b/src/status_im/contexts/keycard/migrate/sheets/view.cljs @@ -1,15 +1,14 @@ -(ns status-im.contexts.keycard.sheets.migrate.view +(ns status-im.contexts.keycard.migrate.sheets.view (:require [quo.core :as quo] [react-native.core :as rn] [utils.i18n :as i18n] [utils.re-frame :as rf])) (defn view - [] - (let [profile-name (rf/sub [:profile/name]) - profile-picture (rf/sub [:profile/image]) - customization-color (rf/sub [:profile/customization-color]) - recovery-phrase-backed-up? (rf/sub [:profile/recovery-phrase-backed-up?])] + [{:keys [on-continue]}] + (let [profile-name (rf/sub [:profile/name]) + profile-picture (rf/sub [:profile/image]) + customization-color (rf/sub [:profile/customization-color])] [:<> [quo/drawer-top {:type :context-tag @@ -28,11 +27,7 @@ [quo/bottom-actions {:actions :two-actions :button-one-label (i18n/label :t/continue) - :button-one-props {:on-press #(if recovery-phrase-backed-up? - (rf/dispatch [:open-modal :screen/use-recovery-phrase - {:on-success (fn [])}]) - (rf/dispatch [:open-modal :screen/backup-recovery-phrase - {:on-success (fn [])}]))} + :button-one-props {:on-press on-continue} :button-two-label (i18n/label :t/cancel) :button-two-props {:type :grey :on-press #(rf/dispatch [:hide-bottom-sheet])}}]])) diff --git a/src/status_im/contexts/keycard/migrate/success/view.cljs b/src/status_im/contexts/keycard/migrate/success/view.cljs new file mode 100644 index 0000000000..b5f05dac24 --- /dev/null +++ b/src/status_im/contexts/keycard/migrate/success/view.cljs @@ -0,0 +1,31 @@ +(ns status-im.contexts.keycard.migrate.success.view + (:require [quo.core :as quo] + [react-native.core :as rn] + [status-im.common.resources :as resources] + [status-im.constants :as constants] + [utils.i18n :as i18n] + [utils.re-frame :as rf])) + +(defn view + [] + (let [profile-name (rf/sub [:profile/name]) + profile-picture (rf/sub [:profile/image]) + customization-color (rf/sub [:profile/customization-color])] + [:<> + [quo/page-top + {:title (i18n/label :t/key-pair-migrated-successfully) + :description :context-tag + :context-tag {:full-name profile-name + :profile-picture profile-picture + :customization-color customization-color} + :container-style {:margin-top constants/page-nav-height}}] + [quo/text {:style {:padding-horizontal 20}} + (i18n/label :t/use-keycard-for-status)] + [rn/view {:style {:flex 1 :align-items :center :justify-content :center}} + [rn/image + {:resize-mode :contain + :source (resources/get-image :keycard-migration-succeeded)}]] + [quo/button + {:on-press #(rf/dispatch [:logout]) + :container-style {:margin-horizontal 20}} + (i18n/label :t/logout)]])) diff --git a/src/status_im/contexts/keycard/migrate/view.cljs b/src/status_im/contexts/keycard/migrate/view.cljs new file mode 100644 index 0000000000..8ed7058572 --- /dev/null +++ b/src/status_im/contexts/keycard/migrate/view.cljs @@ -0,0 +1,34 @@ +(ns status-im.contexts.keycard.migrate.view + (:require [quo.core :as quo] + [react-native.core :as rn] + [status-im.common.events-helper :as events-helper] + [status-im.common.resources :as resources] + [utils.i18n :as i18n] + [utils.re-frame :as rf])) + +(defn view + [] + (let [profile-name (rf/sub [:profile/name]) + profile-picture (rf/sub [:profile/image]) + customization-color (rf/sub [:profile/customization-color])] + [:<> + [quo/page-nav + {:icon-name :i/close + :on-press events-helper/navigate-back}] + [quo/page-top + {:title (i18n/label :t/ready-to-migrate-key-pair) + :description :context-tag + :context-tag {:full-name profile-name + :profile-picture profile-picture + :customization-color customization-color}}] + [rn/view {:style {:flex 1 :align-items :center :justify-content :center}} + [rn/image + {:resize-mode :contain + :source (resources/get-image :keycard-migration)}]] + [quo/divider-label (i18n/label :t/tips-scan-keycard)] + [quo/markdown-list {:description (i18n/label :t/remove-phone-case)}] + [quo/markdown-list {:description (i18n/label :t/keep-card-steady)}] + [quo/bottom-actions + {:actions :one-action + :button-one-label (i18n/label :t/scan-keycard) + :button-one-props {:on-press #(rf/dispatch [:keycard/migration.start])}}]])) diff --git a/src/status_im/contexts/keycard/nfc/effects.cljs b/src/status_im/contexts/keycard/nfc/effects.cljs new file mode 100644 index 0000000000..87d25ca116 --- /dev/null +++ b/src/status_im/contexts/keycard/nfc/effects.cljs @@ -0,0 +1,11 @@ +(ns status-im.contexts.keycard.nfc.effects + (:require [keycard.keycard :as keycard] + [utils.re-frame :as rf])) + +(rf/reg-fx :effects.keycard/check-nfc-enabled + (fn [] + (keycard/check-nfc-enabled + {:on-success #(rf/dispatch [:keycard/on-check-nfc-enabled-success %])}))) + +(rf/reg-fx :effects.keycard.ios/start-nfc keycard/start-nfc) +(rf/reg-fx :effects.keycard.ios/stop-nfc keycard/stop-nfc) diff --git a/src/status_im/contexts/keycard/nfc/events.cljs b/src/status_im/contexts/keycard/nfc/events.cljs new file mode 100644 index 0000000000..b2cb461f43 --- /dev/null +++ b/src/status_im/contexts/keycard/nfc/events.cljs @@ -0,0 +1,27 @@ +(ns status-im.contexts.keycard.nfc.events + (:require [utils.re-frame :as rf])) + +(rf/reg-event-fx :keycard.ios/start-nfc + (fn [_] + {:fx [[:effects.keycard.ios/start-nfc]]})) + +(rf/reg-event-fx :keycard.ios/on-start-nfc-success + (fn [{:keys [db]} [{:keys [on-cancel-event-vector]}]] + {:db (assoc-in db [:keycard :on-nfc-cancelled-event-vector] on-cancel-event-vector)})) + +(rf/reg-event-fx :keycard.ios/on-nfc-timeout + (fn [{:keys [db]} _] + {:db (assoc-in db [:keycard :card-connected?] false) + :fx [[:dispatch-later [{:ms 500 :dispatch [:keycard.ios/start-nfc]}]]]})) + +(rf/reg-event-fx :keycard/on-check-nfc-enabled-success + (fn [{:keys [db]} [nfc-enabled?]] + {:db (assoc-in db [:keycard :nfc-enabled?] nfc-enabled?)})) + +(rf/reg-event-fx :keycard.ios/on-nfc-user-cancelled + (fn [{:keys [db]}] + {:db (-> db + (assoc-in [:keycard :pin :status] nil) + (assoc-in [:keycard :on-nfc-cancelled-event-vector] nil)) + :fx [(when-let [on-nfc-cancelled-event-vector (get-in db [:keycard :on-nfc-cancelled-event-vector])] + [:dispatch on-nfc-cancelled-event-vector])]})) diff --git a/src/status_im/contexts/keycard/nfc_sheet/events.cljs b/src/status_im/contexts/keycard/nfc/sheets/events.cljs similarity index 95% rename from src/status_im/contexts/keycard/nfc_sheet/events.cljs rename to src/status_im/contexts/keycard/nfc/sheets/events.cljs index 195d185611..4d767f10bb 100644 --- a/src/status_im/contexts/keycard/nfc_sheet/events.cljs +++ b/src/status_im/contexts/keycard/nfc/sheets/events.cljs @@ -1,4 +1,4 @@ -(ns status-im.contexts.keycard.nfc-sheet.events +(ns status-im.contexts.keycard.nfc.sheets.events (:require [re-frame.core :as rf] [react-native.platform :as platform] [taoensso.timbre :as log])) diff --git a/src/status_im/contexts/keycard/nfc_sheet/view.cljs b/src/status_im/contexts/keycard/nfc/sheets/view.cljs similarity index 96% rename from src/status_im/contexts/keycard/nfc_sheet/view.cljs rename to src/status_im/contexts/keycard/nfc/sheets/view.cljs index 8ef8f91fab..68d157b6db 100644 --- a/src/status_im/contexts/keycard/nfc_sheet/view.cljs +++ b/src/status_im/contexts/keycard/nfc/sheets/view.cljs @@ -1,4 +1,4 @@ -(ns status-im.contexts.keycard.nfc-sheet.view +(ns status-im.contexts.keycard.nfc.sheets.view (:require [quo.foundations.colors :as colors] quo.theme [react-native.core :as rn] diff --git a/src/status_im/contexts/keycard/not_keycard/view.cljs b/src/status_im/contexts/keycard/not_keycard/view.cljs index 742aae1545..abdb634812 100644 --- a/src/status_im/contexts/keycard/not_keycard/view.cljs +++ b/src/status_im/contexts/keycard/not_keycard/view.cljs @@ -1,28 +1,24 @@ (ns status-im.contexts.keycard.not-keycard.view (:require [quo.core :as quo] [react-native.core :as rn] - [react-native.safe-area :as safe-area] [status-im.common.events-helper :as events-helper] - [utils.i18n :as i18n] - [utils.re-frame :as rf])) + [status-im.common.resources :as resources] + [utils.i18n :as i18n])) (defn view [] - (let [{:keys [top bottom]} (safe-area/get-insets)] - [quo/overlay - {:type :shell - :container-style {:padding-top top - :padding-bottom bottom}} - [quo/page-nav - {:key :header - :background :blur - :icon-name :i/arrow-left - :on-press events-helper/navigate-back}] - [quo/page-top - {:title (i18n/label :t/oops-not-keycard) - :description :text - :description-text (i18n/label :t/make-sure-keycard)}] - [rn/view {:flex 1}] - [rn/view {:padding-horizontal 20} - [quo/button {:on-press #(rf/dispatch [:keycard/connect])} - (i18n/label :t/try-again)]]])) + [:<> + [quo/page-nav + {:icon-name :i/close + :on-press events-helper/navigate-back}] + [quo/page-top + {:title (i18n/label :t/oops-not-keycard) + :description :text + :description-text (i18n/label :t/make-sure-keycard)}] + [rn/view {:style {:flex 1 :align-items :center :justify-content :center}} + [rn/image + {:resize-mode :contain + :source (resources/get-image :not-keycard)}]] + [rn/view {:padding-horizontal 20} + [quo/button {:on-press events-helper/navigate-back} + (i18n/label :t/try-again)]]]) diff --git a/src/status_im/contexts/keycard/pin/create/view.cljs b/src/status_im/contexts/keycard/pin/create/view.cljs index a952e528e8..e54a81ec24 100644 --- a/src/status_im/contexts/keycard/pin/create/view.cljs +++ b/src/status_im/contexts/keycard/pin/create/view.cljs @@ -2,12 +2,15 @@ (:require [clojure.string :as string] [quo.core :as quo] [react-native.core :as rn] + [status-im.common.events-helper :as events-helper] [status-im.constants :as constants] - [utils.i18n :as i18n])) + [utils.i18n :as i18n] + [utils.re-frame :as rf])) (defn view - [{:keys [on-complete]}] - (let [[pin set-pin] (rn/use-state "") + [] + (let [{:keys [on-complete]} (rf/sub [:get-screen-params]) + [pin set-pin] (rn/use-state "") [first-pin set-first-pin] (rn/use-state "") [error? set-error] (rn/use-state false) [stage set-stage] (rn/use-state :create) @@ -38,10 +41,15 @@ (set-stage :repeat))))))) [pin stage first-pin])] [rn/view {:style {:padding-bottom 12 :flex 1}} - [quo/drawer-top - {:title (if (= :create stage) - (i18n/label :t/create-keycard-pin) - (i18n/label :t/repeat-keycard-pin))}] + [quo/page-nav + {:icon-name :i/close + :on-press events-helper/navigate-back}] + [quo/page-top + {:title (if (= :create stage) + (i18n/label :t/create-keycard-pin) + (i18n/label :t/repeat-keycard-pin)) + :description :text + :description-text "You’ll need this PIN to login and sign transactions"}] [rn/view {:style {:flex 1 :justify-content :center :align-items :center :padding-vertical 34}} [quo/pin-input {:blur? false diff --git a/src/status_im/contexts/keycard/pin/events.cljs b/src/status_im/contexts/keycard/pin/events.cljs index 66ee928dbf..d96eb522cc 100644 --- a/src/status_im/contexts/keycard/pin/events.cljs +++ b/src/status_im/contexts/keycard/pin/events.cljs @@ -23,3 +23,7 @@ (assoc-in [:keycard :pin :status] nil)) :fx [(when (and on-complete (= (dec max-numbers) (count pin))) [:effects.keycard.pin/dispatch-on-complete [on-complete new-pin]])]})))) + +(rf/reg-event-fx :keycard.pin/clear + (fn [{:keys [db]}] + {:db (assoc-in db [:keycard :pin] nil)})) diff --git a/src/status_im/contexts/keycard/pin/view.cljs b/src/status_im/contexts/keycard/pin/view.cljs index 09c1951854..27c2732abe 100644 --- a/src/status_im/contexts/keycard/pin/view.cljs +++ b/src/status_im/contexts/keycard/pin/view.cljs @@ -6,10 +6,12 @@ [utils.re-frame :as rf])) (defn auth - [{:keys [on-complete]}] - (let [{:keys [text status]} (rf/sub [:keycard/pin]) - pin-retry-counter (rf/sub [:keycard/pin-retry-counter]) - error? (= status :error)] + [{:keys [on-complete error]}] + (let [{:keys [error? error-message]} error + {:keys [text status]} (rf/sub [:keycard/pin]) + pin-retry-counter (rf/sub [:keycard/pin-retry-counter]) + error? (or error? (= status :error))] + (rn/use-unmount #(rf/dispatch [:keycard.pin/clear])) [rn/view {:padding-bottom 12 :flex 1} [rn/view {:flex 1 :justify-content :center :align-items :center :padding 34} [quo/pin-input @@ -18,7 +20,9 @@ :number-of-filled-pins (count text) :error? error? :info (when error? - (i18n/label :t/pin-retries-left {:number pin-retry-counter}))}]] + (if error-message + error-message + (i18n/label :t/pin-retries-left {:number pin-retry-counter})))}]] [quo/numbered-keyboard {:delete-key? true :on-delete #(rf/dispatch [:keycard.pin/delete-pressed]) diff --git a/src/status_im/contexts/keycard/sign/events.cljs b/src/status_im/contexts/keycard/sign/events.cljs index f7b9adba7e..1c1499d4db 100644 --- a/src/status_im/contexts/keycard/sign/events.cljs +++ b/src/status_im/contexts/keycard/sign/events.cljs @@ -8,6 +8,10 @@ :s (subs signature 64 128) :v (str (js/parseInt (subs signature 128 130) 16))}}) +(rf/reg-event-fx :keycard/sign + (fn [_ [data]] + {:effects.keycard/sign data})) + (rf/reg-event-fx :keycard/sign-hash (fn [{:keys [db]} [pin-text tx-hash]] (let [current-address (get-in db [:wallet :current-viewing-account-address]) @@ -15,16 +19,16 @@ key-uid (get-in db [:profile/profile :key-uid])] {:fx [[:dispatch [:keycard/connect - {:key-uid key-uid - :on-success-fx [[:effects.keycard/sign - {:pin pin-text - :path path - :hash (utils.address/naked-address tx-hash) - :on-success - #(do - (rf/dispatch [:keycard/disconnect]) - (rf/dispatch - [:wallet/proceed-with-transactions-signatures - (get-signature-map tx-hash %)])) - :on-failure #(rf/dispatch [:keycard/on-action-with-pin-error - %])}]]}]]]}))) + {:key-uid key-uid + :on-success + (fn [] + (rf/dispatch + [:keycard/sign + {:pin pin-text + :path path + :hash (utils.address/naked-address tx-hash) + :on-success (fn [signature] + (rf/dispatch [:keycard/disconnect]) + (rf/dispatch [:wallet/proceed-with-transactions-signatures + (get-signature-map tx-hash signature)])) + :on-failure #(rf/dispatch [:keycard/on-action-with-pin-error %])}]))}]]]}))) diff --git a/src/status_im/contexts/keycard/utils.cljs b/src/status_im/contexts/keycard/utils.cljs index 44d414beb8..6abbf84d11 100644 --- a/src/status_im/contexts/keycard/utils.cljs +++ b/src/status_im/contexts/keycard/utils.cljs @@ -1,5 +1,7 @@ (ns status-im.contexts.keycard.utils - (:require [taoensso.timbre :as log])) + (:require [clojure.string :as string] + [utils.address :as address] + [utils.transforms :as transforms])) (def pin-mismatch-error #"Unexpected error SW, 0x63C(\d+)|wrongPIN\(retryCounter: (\d+)\)") @@ -16,33 +18,55 @@ (re-matches #".*NFCError:100.*" error))) (defn validate-application-info - [profile-key-uid {:keys [key-uid paired? pin-retry-counter puk-retry-counter] :as application-info}] - (let [profile-mismatch? (or (nil? profile-key-uid) (not= profile-key-uid key-uid))] - (log/debug "[keycard]" "login-with-keycard" - "empty application info" (empty? application-info) - "no key-uid" (empty? key-uid) - "profile-mismatch?" profile-mismatch? - "no pairing" paired?) - (cond - (empty? application-info) - :keycard/error.not-keycard + [profile-key-uid + {:keys [key-uid has-master-key? paired? pin-retry-counter puk-retry-counter] :as application-info}] - (empty? (:key-uid application-info)) - :keycard/error.keycard-blank + (cond + (empty? application-info) + :keycard/error.not-keycard - profile-mismatch? - :keycard/error.keycard-wrong + (not has-master-key?) + :keycard/error.keycard-blank - (not paired?) - :keycard/error.keycard-unpaired + (not= profile-key-uid key-uid) + :keycard/error.keycard-wrong-profile - (and (zero? pin-retry-counter) - (or (nil? puk-retry-counter) - (pos? puk-retry-counter))) - :keycard/error.keycard-frozen + (not paired?) + :keycard/error.keycard-unpaired - (zero? puk-retry-counter) - :keycard/error.keycard-locked + (and (zero? pin-retry-counter) + (or (nil? puk-retry-counter) + (pos? puk-retry-counter))) + :keycard/error.keycard-frozen - :else - nil))) + (zero? puk-retry-counter) + :keycard/error.keycard-locked + + :else + nil)) + +(defn- error-object->map + [^js object] + {:code (.-code object) + :error (.-message object)}) + +(defn normalize-key-uid + [{:keys [key-uid] :as data}] + (if (string/blank? key-uid) + data + (update data :key-uid address/normalized-hex))) + +(defn get-on-success + [{:keys [on-success]}] + #(when on-success (on-success (normalize-key-uid (transforms/js->clj %))))) + +(defn get-on-failure + [{:keys [on-failure]}] + #(when on-failure (on-failure (error-object->map %)))) + +(defn wrap-handlers + [args] + (assoc + args + :on-success (get-on-success args) + :on-failure (get-on-failure args))) diff --git a/src/status_im/contexts/profile/login/effects.cljs b/src/status_im/contexts/profile/login/effects.cljs index e115ed5bc2..9e6daf3005 100644 --- a/src/status_im/contexts/profile/login/effects.cljs +++ b/src/status_im/contexts/profile/login/effects.cljs @@ -13,3 +13,7 @@ (rf/reg-fx :effects.profile/enable-local-notifications (fn [] (native-module/start-local-notifications))) + +(rf/reg-fx :effects.profile/convert-to-keycard-profile + (fn [{:keys [profile settings password new-password callback]}] + (native-module/convert-to-keycard-profile profile settings password new-password callback))) diff --git a/src/status_im/contexts/profile/profiles/view.cljs b/src/status_im/contexts/profile/profiles/view.cljs index c8942f0ecc..8f56a33f96 100644 --- a/src/status_im/contexts/profile/profiles/view.cljs +++ b/src/status_im/contexts/profile/profiles/view.cljs @@ -1,5 +1,6 @@ (ns status-im.contexts.profile.profiles.view (:require + [clojure.string :as string] [native-module.core :as native-module] [quo.core :as quo] [react-native.core :as rn] @@ -182,7 +183,7 @@ :render-fn profile-card}]])) (defn password-input - [] + [processing error] (let [auth-method (rf/sub [:auth-method]) on-press-biometrics (fn [] (rf/dispatch [:biometric/authenticate @@ -193,18 +194,31 @@ %])}]))] [standard-authentication/password-input - {:shell? true + {:processing processing + :error error + :shell? true :blur? true :on-press-biometrics (when (= auth-method constants/auth-method-biometric) on-press-biometrics)}])) +(defn- get-error-message + [error] + (if (and (some? error) + (or (= error "file is not a database") + (string/starts-with? (string/lower-case error) "failed"))) + (i18n/label :t/oops-wrong-password) + error)) + (defn login-section [{:keys [show-profiles]}] - (let [processing (rf/sub [:profile/login-processing]) - {:keys [key-uid name keycard-pairing + (let [{:keys [key-uid name keycard-pairing customization-color]} (rf/sub [:profile/login-profile]) sign-in-enabled? (rf/sub [:sign-in-enabled?]) profile-picture (rf/sub [:profile/login-profiles-picture key-uid]) - login-multiaccount (rn/use-callback #(rf/dispatch [:profile.login/login]))] + {:keys [error processing]} (rf/sub [:profile/login]) + error-message (rn/use-memo #(get-error-message error) [error]) + error {:error? (boolean (seq error-message)) + :error-message error-message} + login-profile (rn/use-callback #(rf/dispatch [:profile.login/login]))] [rn/keyboard-avoiding-view {:style style/login-container :keyboard-vertical-offset (- (safe-area/get-bottom))} @@ -239,17 +253,20 @@ :card-style style/login-profile-card}] (if keycard-pairing [keycard.pin/auth - {:on-complete - (fn [pin-text] + {:error error + :on-complete + (fn [pin] (rf/dispatch [:keycard/connect - {:key-uid key-uid - :on-success-fx [[:effects.keycard/get-keys - {:pin pin-text - :on-success #(rf/dispatch [:keycard.login/on-get-keys-success %]) - :on-failure #(rf/dispatch [:keycard/on-action-with-pin-error - %])}]]}]))}] - [password-input])] + {:key-uid key-uid + :on-success + (fn [] + (rf/dispatch + [:keycard/get-keys + {:pin pin + :on-success #(rf/dispatch [:keycard.login/on-get-keys-success %]) + :on-failure #(rf/dispatch [:keycard/on-action-with-pin-error %])}]))}]))}] + [password-input processing error])] (when-not keycard-pairing [quo/button {:size 40 @@ -258,7 +275,7 @@ :accessibility-label :login-button :icon-left :i/unlocked :disabled? (or (not sign-in-enabled?) processing) - :on-press login-multiaccount + :on-press login-profile :container-style {:margin-bottom (+ (safe-area/get-bottom) 12)}} (i18n/label :t/log-in)])])) diff --git a/src/status_im/contexts/settings/keycard/view.cljs b/src/status_im/contexts/settings/keycard/view.cljs index cdb1a654b5..b6e274304d 100644 --- a/src/status_im/contexts/settings/keycard/view.cljs +++ b/src/status_im/contexts/settings/keycard/view.cljs @@ -4,6 +4,7 @@ [react-native.core :as rn] [status-im.common.events-helper :as events-helper] [status-im.common.resources :as resources] + [status-im.constants :as constants] [utils.i18n :as i18n] [utils.re-frame :as rf])) @@ -27,11 +28,11 @@ :subtitle (i18n/label :t/secure-wallet-card) :button-label (i18n/label :t/buy-keycard) :accessibility-label :get-keycard - :image (resources/get-image :generate-keys) - :on-press #()}] + :image (resources/get-image :keycard-buy) + :on-press #(rf/dispatch [:browser.ui/open-url constants/get-keycard-url])}] [rn/view {:style {:margin-top 24}} [quo/text - {:style {:margin-bottom 12 + {:style {:margin-bottom 1 :color colors/white-opa-70} :size :paragraph-2 :weight :medium} diff --git a/src/status_im/contexts/settings/privacy_and_security/share_usage/view.cljs b/src/status_im/contexts/settings/privacy_and_security/share_usage/view.cljs index 12918e5ff7..4d07ca9a8e 100644 --- a/src/status_im/contexts/settings/privacy_and_security/share_usage/view.cljs +++ b/src/status_im/contexts/settings/privacy_and_security/share_usage/view.cljs @@ -41,10 +41,9 @@ (for [label points] ^{:key label} [quo/markdown-list - {:description (i18n/label label) - :blur? true - :type (when lock? :lock) - :container-style {:padding-top 8}}])]}) + {:description (i18n/label label) + :blur? true + :type (when lock? :lock)}])]}) (defn- on-privacy-policy-press [] diff --git a/src/status_im/navigation/options.cljs b/src/status_im/navigation/options.cljs index 3697c83b11..1d7f3faee9 100644 --- a/src/status_im/navigation/options.cljs +++ b/src/status_im/navigation/options.cljs @@ -69,12 +69,6 @@ transitions/new-to-status-modal-animations transitions/push-animations-for-transparent-background)})) -(def keycard-modal-screen-options - (assoc transparent-modal-screen-options - :layout nil - :theme :dark - :insets {:top? true :bottom? true})) - (def sheet-options {:layout {:componentBackgroundColor :transparent :orientation ["portrait"] diff --git a/src/status_im/navigation/screens.cljs b/src/status_im/navigation/screens.cljs index 13f36ad1b1..ede4b2e818 100644 --- a/src/status_im/navigation/screens.cljs +++ b/src/status_im/navigation/screens.cljs @@ -32,7 +32,12 @@ [status-im.contexts.keycard.check.view :as keycard.check] [status-im.contexts.keycard.empty.view :as keycard.empty] [status-im.contexts.keycard.error.view :as keycard.error] + [status-im.contexts.keycard.migrate.fail.view :as keycard.migrate.fail] + [status-im.contexts.keycard.migrate.re-encrypting.view :as keycard.re-encrypting] + [status-im.contexts.keycard.migrate.success.view :as keycard.migrate.success] + [status-im.contexts.keycard.migrate.view :as keycard.migrate] [status-im.contexts.keycard.not-keycard.view :as keycard.not-keycard] + [status-im.contexts.keycard.pin.create.view :as pin.create] [status-im.contexts.onboarding.create-or-sync-profile.view :as create-or-sync-profile] [status-im.contexts.onboarding.create-password.view :as create-password] [status-im.contexts.onboarding.create-profile.view :as create-profile] @@ -310,8 +315,9 @@ :component settings/view} {:name :screen/settings.keycard - :metrics {:track? :true} - :options options/keycard-modal-screen-options + :metrics {:track? :true + :alias-id :settings.keycard} + :options {:insets {:top? true :bottom? true}} :component settings.keycard/view} {:name :edit-profile @@ -877,29 +883,73 @@ (def keycard-screens [{:name :screen/keycard.check - :metrics {:track? true} - :options options/keycard-modal-screen-options + :metrics {:track? :true + :alias-id :keycard.check} + :options {:insets {:top? true :bottom? true}} :component keycard.check/view} {:name :screen/keycard.empty - :metrics {:track? true} - :options options/keycard-modal-screen-options + :metrics {:track? :true + :alias-id :keycard.empty} + :options {:insets {:top? true :bottom? true}} :component keycard.empty/view} {:name :screen/keycard.error - :metrics {:track? true} - :options options/keycard-modal-screen-options + :metrics {:track? :true + :alias-id :keycard.error} + :options {:insets {:top? true :bottom? true}} :component keycard.error/view} {:name :screen/keycard.not-keycard - :metrics {:track? true} - :options options/keycard-modal-screen-options + :metrics {:track? :true + :alias-id :keycard.not-keycard} + :options {:insets {:top? true :bottom? true}} :component keycard.not-keycard/view} {:name :screen/keycard.authorise - :metrics {:track? true} - :options options/keycard-modal-screen-options - :component keycard.authorise/view}]) + :metrics {:track? :true + :alias-id :keycard.authorise} + :options {:insets {:top? true :bottom? true}} + :component keycard.authorise/view} + + {:name :screen/keycard.migrate + :metrics {:track? :true + :alias-id :keycard.migrate} + :options {:insets {:top? true :bottom? true}} + :component keycard.migrate/view} + + {:name :screen/keycard.re-encrypting + :metrics {:track? :true + :alias-id :keycard.re-encrypting} + :options {:insets {:top? true :bottom? true} + :popGesture false + :hardwareBackButton {:dismissModalOnPress false + :popStackOnPress false}} + :component keycard.re-encrypting/view} + + {:name :screen/keycard.migrate.success + :metrics {:track? :true + :alias-id :keycard.migrate.success} + :options {:insets {:top? true :bottom? true} + :popGesture false + :hardwareBackButton {:dismissModalOnPress false + :popStackOnPress false}} + :component keycard.migrate.success/view} + + {:name :screen/keycard.migrate.fail + :metrics {:track? :true + :alias-id :keycard.migrate.fail} + :options {:insets {:top? true :bottom? true} + :popGesture false + :hardwareBackButton {:dismissModalOnPress false + :popStackOnPress false}} + :component keycard.migrate.fail/view} + + {:name :screen/keycard.pin.create + :metrics {:track? :true + :alias-id :keycard.pin.create} + :options {:insets {:top? true :bottom? true}} + :component pin.create/view}]) (defn screens [] diff --git a/src/status_im/navigation/view.cljs b/src/status_im/navigation/view.cljs index eaff875400..4a63e8543b 100644 --- a/src/status_im/navigation/view.cljs +++ b/src/status_im/navigation/view.cljs @@ -11,7 +11,7 @@ [status-im.common.bottom-sheet-screen.view :as bottom-sheet-screen] [status-im.common.bottom-sheet.view :as bottom-sheet] [status-im.common.toasts.view :as toasts] - [status-im.contexts.keycard.nfc-sheet.view :as keycard.sheet] + [status-im.contexts.keycard.nfc.sheets.view :as keycard.sheet] [status-im.navigation.screens :as screens] [status-im.setup.hot-reload :as reloader] [utils.re-frame :as rf])) diff --git a/translations/en.json b/translations/en.json index caa55062ec..d761b19954 100644 --- a/translations/en.json +++ b/translations/en.json @@ -293,6 +293,8 @@ "cant-fetch-info": "Can't fetch info", "cant-open-public-chat": "Can't open public chat", "cant-report-bug": "Can't report a bug", + "cant-store-new-keys": "You can’t use it to store new keys right now", + "cant-use-right-now": "You can’t use it right now", "card-is-blank": "This card is blank", "card-reseted": "Card has been reseted", "card-unpaired": "Card has been unpaired from current device", @@ -782,6 +784,8 @@ "display-collectibles": "Display collectibles", "do-not-cheat": "Don't try to cheat", "do-not-cheat-description": "These 12 words give access to all of your funds so it is important that you write them in the correct order, take it seriously.", + "do-not-quit": "Do not quit the application or turn off your device. Doing so will lead to data corruption, loss of your Status profile and the inability to use Status.", + "do-not-share": "Do not share", "done": "Done", "dont-ask": "Don't ask me again", @@ -1010,6 +1014,7 @@ "failed": "Failed", "failed-on": "Failed on", "failed-to-fetch-community": "Failed to fetch community", + "failed-to-migrate-key-pair": "Failed to migrate key pair", "faq": "Frequently asked questions", "fast": "Fast", "favorite-communities": "Your favourite communities", @@ -1317,6 +1322,7 @@ "key-name-error-too-short": "Key pair name must be at least {{count}} characters", "key-on-device": "Private key is saved on this device", "key-pair-imported-successfully": "{{name}} key pair imported successfully", + "key-pair-migrated-successfully": "Profile key pair successfully migrated", "key-pair-name-updated": "Key pair name updated", "key-pair-removed": "Key pair and derived accounts has been removed", "key-pairs-successfully-imported": "{{count}} key pairs successfully imported", @@ -1344,6 +1350,7 @@ "keycard-factory-reset-text": "Performing this will delete any mnemonic phrase stored on the card. Make sure you have a backup of the mnemonic phrase you've been using with this Keycard.", "keycard-factory-reset-title": "Are you sure you want to perform a factory reset?", "keycard-free-pairing-slots": "Keycard has {{n}} free pairing slots", + "keycard-full": "Keycard is full", "keycard-has-multiaccount-on-it": "This card is full. Each card can hold one main key pair", "keycard-init-description": "Put the card to the back of your phone to continue", "keycard-init-title": "Looking for cards...", @@ -1354,6 +1361,8 @@ "keycard-is-frozen-factory-reset": "Reset with mnemonic", "keycard-is-frozen-reset": "Reset with PUK", "keycard-is-frozen-title": "Keycard is frozen", + "keycard-locked": "Keycard is locked", + "keycard-not-empty": "Keycard is not empty", "keycard-onboarding-finishing-header": "Finishing up", "keycard-onboarding-intro-header": "Store your keys on Keycard", "keycard-onboarding-intro-text": "Get ready, this might take a few minutes, but it's important to secure your account", @@ -1450,6 +1459,8 @@ "log-in": "Log in", "log-level": "Log level", "log-level-settings": "Log level settings", + "log-out-remove": "Log out & Remove profile", + "log-out-remove-profile": "Log out and remove profile from this device", "logged-in": "Logged in", "logging": "Logging", "logging-enabled": "Logging enabled?", @@ -1718,6 +1729,7 @@ "no-fees": "No fees", "no-group-chats": "No group chats", "no-group-chats-description": "Much fun. Have friends. Wow!", + "no-key-pair-keycard": "There is no key pair on this Keycard", "no-keycard-applet-on-card": "No Keycard applet on card", "no-messages": "No messages", "no-messages-description": "Here’s a cat in a box instead", @@ -1876,6 +1888,7 @@ "pairing-new-installation-detected-title": "New device detected", "pairing-no-info": "No info", "pairing-please-set-a-name": "Please set a name for your device.", + "pairing-slots-occupied": "All pairing slots are occupied", "paraswap-error": "Paraswap error: {{paraswap-error}}", "participate-in-the-metaverse": "Participate in the truly free metaverse", "passphrase": "Passphrase", @@ -2024,9 +2037,11 @@ "re-encrypt": "Re-encrypt", "re-encrypt-data": "Re-encrypt your data", "re-encrypt-key": "Re-encrypt your keys", + "re-encrypting-data": "Re-encrypting data", "read": "Read", "read-more": "Read more", "ready-keycard": "Get your Keycard ready", + "ready-to-migrate-key-pair": "Ready to migrate profile key pair to the Keycard", "ready-to-scan": "Ready to Scan", "rearrange-categories": "Rearrange Categories", "receive": "Receive", @@ -2045,6 +2060,7 @@ "recover": "Recover", "recover-key": "Access existing keys", "recover-keycard-multiaccount-not-supported": "Keys for this account already exist and can't be added again. If you've lost your password, passcode or Keycard, uninstall the app, reinstall and access your keys by entering your seed phrase", + "recover-status-profile": "Recover your Status profile with recovery phrase", "recover-with-keycard": "Recover with Keycard", "recover-with-seed-phrase": "Recover with seed phrase", "recovering-key": "Accessing keys...", @@ -2170,6 +2186,7 @@ "scan-an-account-qr-code": "Scan an account QR code", "scan-an-address-qr-code": "Scan an address QR code", "scan-key-pairs-qr-code": "Scan key pairs QR code", + "scan-keycard": "Scan Keycard", "scan-or-enter-a-sync-code": "Scan or enter a sync code", "scan-or-enter-sync-code": "Scan or enter sync code", "scan-or-enter-sync-code-seen-on-this-device": "Scan or enter sync code seen on this device", @@ -2622,6 +2639,7 @@ "unknown-status-go-error": "Unknown status-go error", "unlimited": "Unlimited", "unlock": "Unlock", + "unlock-reset-instructions": "To unlock or factory reset the Keycard, please use the Status desktop app. If you'd like this feature on mobile, feel free to upvote them and discuss in the Status community.", "unmute": "Unmute", "unmute-channel": "Unmute channel", "unmute-chat": "Unmute chat", @@ -2660,6 +2678,7 @@ "use-as-profile-picture": "Use as profile picture", "use-biometrics": "Use biometrics to fill in your password", "use-keycard": "Use Keycard", + "use-keycard-for-status": "From now on, use this Keycard to login to Status and transact with derived accounts on all synced devices. To start using it, logout and log back in with this Keycard.", "use-keycard-login-sign": "Use this Keycard to login and sign transactions", "use-keycard-subtitle": "Keys will be stored on your Keycard", "use-photo": "Use Photo", @@ -2809,6 +2828,7 @@ "what-to-do": "What you would like to do?", "what-we-will-receive": "What we will receive:", "what-we-wont-receive": "What we won't receive:", + "what-you-can-do": "What you can do:", "whats-on-your-mind": "What’s on your mind…", "which-connection-to-use": "Which connection to use for syncing?", "who-are-you-looking-for": "Who are you looking for ?",