From 0522120c666d35f4d7d46b94f69c79e6e4bc9ef3 Mon Sep 17 00:00:00 2001 From: Siddarth Kumar Date: Thu, 28 Sep 2023 16:53:15 +0530 Subject: [PATCH] Standardized in-app authentication (#16916) * chore: move password input to connected component --------- Co-authored-by: Jamie Caprani Co-authored-by: frank --- .../buttons/slide_button/animations.cljs | 2 +- .../buttons/slide_button/component_spec.cljs | 9 +- .../buttons/slide_button/style.cljs | 12 +- .../buttons/slide_button/utils.cljs | 16 ++- .../components/buttons/slide_button/view.cljs | 42 ++++--- src/react_native/touch_id.cljs | 2 +- src/status_im2/common/bottom_sheet/view.cljs | 1 + .../enter_password/style.cljs | 20 ++++ .../enter_password/view.cljs | 53 +++++++++ .../forgot_password_doc/style.cljs | 6 + .../forgot_password_doc/view.cljs | 52 +++++++++ .../password_input/style.cljs | 6 + .../password_input/view.cljs | 70 ++++++++++++ .../standard_auth/view.cljs | 79 +++++++++++++ .../contexts/profile/login/events.cljs | 26 +++++ .../contexts/profile/profiles/style.cljs | 9 -- .../contexts/profile/profiles/view.cljs | 105 ++---------------- .../quo_preview/buttons/slide_button.cljs | 12 +- src/status_im2/contexts/syncing/events.cljs | 26 +++-- .../contexts/syncing/setup_syncing/style.cljs | 31 +++--- .../contexts/syncing/setup_syncing/view.cljs | 95 ++++++++-------- .../syncing/sheets/enter_password/view.cljs | 42 ------- translations/en.json | 3 +- 23 files changed, 451 insertions(+), 268 deletions(-) create mode 100644 src/status_im2/common/standard_authentication/enter_password/style.cljs create mode 100644 src/status_im2/common/standard_authentication/enter_password/view.cljs create mode 100644 src/status_im2/common/standard_authentication/forgot_password_doc/style.cljs create mode 100644 src/status_im2/common/standard_authentication/forgot_password_doc/view.cljs create mode 100644 src/status_im2/common/standard_authentication/password_input/style.cljs create mode 100644 src/status_im2/common/standard_authentication/password_input/view.cljs create mode 100644 src/status_im2/common/standard_authentication/standard_auth/view.cljs delete mode 100644 src/status_im2/contexts/syncing/sheets/enter_password/view.cljs diff --git a/src/quo2/components/buttons/slide_button/animations.cljs b/src/quo2/components/buttons/slide_button/animations.cljs index 88273471e8..bb32f0808f 100644 --- a/src/quo2/components/buttons/slide_button/animations.cljs +++ b/src/quo2/components/buttons/slide_button/animations.cljs @@ -70,7 +70,7 @@ [sliding-complete?] (reset! sliding-complete? true)) -(defn- reset-track-position +(defn reset-track-position [x-pos] (animate-spring x-pos 0)) diff --git a/src/quo2/components/buttons/slide_button/component_spec.cljs b/src/quo2/components/buttons/slide_button/component_spec.cljs index 16180f9336..754a0764c9 100644 --- a/src/quo2/components/buttons/slide_button/component_spec.cljs +++ b/src/quo2/components/buttons/slide_button/component_spec.cljs @@ -1,7 +1,6 @@ (ns quo2.components.buttons.slide-button.component-spec (:require [quo2.components.buttons.slide-button.view :as slide-button] [quo2.components.buttons.slide-button.constants :as constants] - [quo2.components.buttons.slide-button.utils :as utils] ["@testing-library/react-native" :as rtl] ["react-native-gesture-handler/jest-utils" :as gestures-jest] [reagent.core :as r] @@ -82,17 +81,11 @@ (h/has-style track-mock {:opacity constants/disable-opacity}))) (h/test "render the small button" - (h/render [slide-button/view (assoc default-props :size :small)]) + (h/render [slide-button/view (assoc default-props :size :size/s-40)]) (let [mock (h/get-by-test-id :slide-button-track) small-height (:track-height constants/small-dimensions)] (h/has-style mock {:height small-height}))) - (h/test "render with the correct customization-color" - (h/render [slide-button/view (assoc default-props :customization-color :purple)]) - (let [track-mock (h/get-by-test-id :slide-button-track) - purple-color (utils/slider-color :track :purple)] - (h/has-style track-mock {:backgroundColor purple-color}))) - (h/test "calls on-complete when dragged" (let [props (merge default-props {:on-complete (h/mock-fn)}) diff --git a/src/quo2/components/buttons/slide_button/style.cljs b/src/quo2/components/buttons/slide_button/style.cljs index 6aade4aea8..837f5ca03d 100644 --- a/src/quo2/components/buttons/slide_button/style.cljs +++ b/src/quo2/components/buttons/slide_button/style.cljs @@ -13,10 +13,10 @@ :right 0}) (defn thumb-container - [interpolate-track thumb-size customization-color] + [{:keys [interpolate-track thumb-size customization-color theme]}] (reanimated/apply-animations-to-style {:transform [{:translate-x (interpolate-track :track-clamp)}]} - {:background-color (utils/slider-color :main customization-color) + {:background-color (utils/slider-color :main customization-color theme) :border-radius 12 :height thumb-size :width thumb-size @@ -46,7 +46,7 @@ :justify-content :space-around})) (defn track - [disabled? customization-color height] + [{:keys [disabled? customization-color height theme]}] {:align-items :flex-start :justify-content :center :border-radius 14 @@ -54,7 +54,7 @@ :align-self :stretch :padding constants/track-padding :opacity (if disabled? 0.3 1) - :background-color (utils/slider-color :track customization-color)}) + :background-color (utils/slider-color :track customization-color theme)}) (defn track-cover [interpolate-track] @@ -74,7 +74,7 @@ :width track-width}) (defn track-text - [customization-color] + [customization-color theme] (-> typography/paragraph-1 (merge typography/font-medium) - (assoc :color (utils/slider-color :main customization-color)))) + (assoc :color (utils/slider-color :main customization-color theme)))) diff --git a/src/quo2/components/buttons/slide_button/utils.cljs b/src/quo2/components/buttons/slide_button/utils.cljs index 9da58cf740..deaac47fd8 100644 --- a/src/quo2/components/buttons/slide_button/utils.cljs +++ b/src/quo2/components/buttons/slide_button/utils.cljs @@ -6,9 +6,15 @@ (defn slider-color "- `color-key` `:main`/`:track` - `customization-color` Customization color" - [color-key customization-color] - (let [colors-by-key {:main (colors/custom-color-by-theme customization-color 50 60) - :track (colors/custom-color-by-theme customization-color 50 60 10 10)}] + [color-key customization-color theme] + (let [colors-by-key {:main (colors/theme-colors + (colors/custom-color customization-color 50) + (colors/custom-color customization-color 60) + theme) + :track (colors/theme-colors + (colors/custom-color customization-color 50 10) + (colors/custom-color customization-color 60 10) + theme)}] (color-key colors-by-key))) (defn clamp-value @@ -28,8 +34,8 @@ (defn get-dimensions [track-width size dimension-key] (let [default-dimensions (case size - :small constants/small-dimensions - :large constants/large-dimensions + :size/s-40 constants/small-dimensions + :size/s-48 constants/large-dimensions constants/large-dimensions)] (-> default-dimensions (merge {:usable-track (calc-usable-track diff --git a/src/quo2/components/buttons/slide_button/view.cljs b/src/quo2/components/buttons/slide_button/view.cljs index c8146cb163..73995c8a2d 100644 --- a/src/quo2/components/buttons/slide_button/view.cljs +++ b/src/quo2/components/buttons/slide_button/view.cljs @@ -10,7 +10,8 @@ [reagent.core :as reagent] [oops.core :as oops] [react-native.reanimated :as reanimated] - [quo2.components.buttons.slide-button.constants :as constants])) + [quo2.components.buttons.slide-button.constants :as constants] + [quo2.theme :as quo.theme])) (defn- f-slider [{:keys [disabled?]}] @@ -20,14 +21,15 @@ on-track-layout (fn [evt] (let [width (oops/oget evt "nativeEvent.layout.width")] (reset! track-width width)))] - - (fn [{:keys [on-complete + (fn [{:keys [on-reset + on-complete track-text track-icon disabled? customization-color size - container-style]}] + container-style + theme]}] (let [x-pos (reanimated/use-shared-value 0) dimensions (partial utils/get-dimensions (or @track-width constants/default-width) @@ -36,12 +38,19 @@ x-pos (dimensions :usable-track) (dimensions :thumb))] - (rn/use-effect (fn [] (when @sliding-complete? (on-complete))) [@sliding-complete?]) + (rn/use-effect (fn [] + (when on-reset + (reset! sliding-complete? false) + (reset! gestures-disabled? false) + (animations/reset-track-position x-pos) + (on-reset))) + [on-reset]) + [gesture/gesture-detector {:gesture (animations/drag-gesture x-pos gestures-disabled? @@ -50,21 +59,25 @@ sliding-complete?)} [reanimated/view {:test-ID :slide-button-track - :style (merge (style/track disabled? customization-color (dimensions :track-height)) + :style (merge (style/track {:disabled? disabled? + :customization-color customization-color + :height (dimensions :track-height) + :theme theme}) container-style) :on-layout (when-not (some? @track-width) on-track-layout)} [reanimated/view {:style (style/track-cover interpolate-track)} [rn/view {:style (style/track-cover-text-container @track-width)} [icon/icon track-icon - {:color (utils/slider-color :main customization-color) + {:color (utils/slider-color :main customization-color theme) :size 20}] [rn/view {:width 4}] - [rn/text {:style (style/track-text customization-color)} track-text]]] + [rn/text {:style (style/track-text customization-color theme)} track-text]]] [reanimated/view - {:style (style/thumb-container interpolate-track - (dimensions :thumb) - customization-color)} + {:style (style/thumb-container {:interpolate-track interpolate-track + :thumb-size (dimensions :thumb) + :customization-color customization-color + :theme theme})} [reanimated/view {:style (style/arrow-icon-container interpolate-track)} [icon/icon :arrow-right {:color colors/white @@ -76,16 +89,19 @@ {:color colors/white :size 20}]]]]])))) -(defn view +(defn- view-internal "Options - `on-complete` Callback called when the sliding is complete - `disabled?` Boolean that disables the button (_and gestures_) - - `size` `:small`/`:large` + - `size` :size/s-40`/`:size/s-48` - `track-text` Text that is shown on the track - `track-icon` Key of the icon shown on the track (e.g. `:face-id`) - `customization-color` Customization color + - `on-reset` A callback which can be used to reset the component and run required functionality " [props] [:f> f-slider props]) + +(def view (quo.theme/with-theme view-internal)) diff --git a/src/react_native/touch_id.cljs b/src/react_native/touch_id.cljs index 22dfe24069..ef7055a16b 100644 --- a/src/react_native/touch_id.cljs +++ b/src/react_native/touch_id.cljs @@ -15,5 +15,5 @@ (defn authenticate [{:keys [on-success on-fail reason options]}] (-> (.authenticate ^js touchid reason (clj->js options)) - (.then #(when on-success (on-success))) + (.then #(when on-success (on-success %))) (.catch #(when on-fail (on-fail (aget % "code")))))) diff --git a/src/status_im2/common/bottom_sheet/view.cljs b/src/status_im2/common/bottom_sheet/view.cljs index b950e721e2..2bec83c241 100644 --- a/src/status_im2/common/bottom_sheet/view.cljs +++ b/src/status_im2/common/bottom_sheet/view.cljs @@ -17,6 +17,7 @@ (defn hide [translate-y bg-opacity window-height on-close] + (rf/dispatch [:dismiss-keyboard]) (when (fn? on-close) (on-close)) ;; it will be better to use animation callback, but it doesn't work diff --git a/src/status_im2/common/standard_authentication/enter_password/style.cljs b/src/status_im2/common/standard_authentication/enter_password/style.cljs new file mode 100644 index 0000000000..d7668897cc --- /dev/null +++ b/src/status_im2/common/standard_authentication/enter_password/style.cljs @@ -0,0 +1,20 @@ +(ns status-im2.common.standard-authentication.enter-password.style) + +(def enter-password-container + {:margin-horizontal 20 + :border-top-left-radius 12 + :border-top-right-radius 12}) + +(def error-message + {:margin-top 8 + :flex-direction :row + :align-items :center}) + +(def enter-password-button + {:margin-top 45 + :margin-bottom 12}) + +(def context-tag + {:flex-direction :row + :margin-bottom 20 + :margin-top 8}) diff --git a/src/status_im2/common/standard_authentication/enter_password/view.cljs b/src/status_im2/common/standard_authentication/enter_password/view.cljs new file mode 100644 index 0000000000..7f31a6c5e4 --- /dev/null +++ b/src/status_im2/common/standard_authentication/enter_password/view.cljs @@ -0,0 +1,53 @@ +(ns status-im2.common.standard-authentication.enter-password.view + (:require [utils.i18n :as i18n] + [quo2.core :as quo] + [react-native.core :as rn] + [utils.re-frame :as rf] + [status-im2.common.standard-authentication.enter-password.style :as style] + [status-im2.common.standard-authentication.password-input.view :as password-input] + [status-im.multiaccounts.core :as multiaccounts])) + +(defn view + [{:keys [on-enter-password button-label]}] + (let [{:keys [key-uid display-name + customization-color] + :as account} (rf/sub [:profile/multiaccount]) + {:keys [error processing password]} (rf/sub [:profile/login]) + sign-in-enabled? (rf/sub [:sign-in-enabled?]) + profile-picture (multiaccounts/displayed-photo account)] + [:<> + [rn/view {:style style/enter-password-container} + [rn/view + [quo/text + {:accessibility-label :sync-code-generated + :weight :bold + :size :heading-1 + :style {:margin-bottom 4}} + (i18n/label :t/enter-password)] + [rn/view + {:style style/context-tag} + [quo/context-tag + {:type :default + :blur? true + :profile-picture profile-picture + :full-name display-name + :customization-color customization-color + :size 24}]] + [password-input/view + {:processing processing + :error error + :default-password password + :sign-in-enabled? sign-in-enabled?}] + [rn/view {:style style/enter-password-button} + [quo/button + {:size 40 + :type :primary + :customization-color (or customization-color :primary) + :accessibility-label :login-button + :icon-left :i/reveal + :disabled? (or (not sign-in-enabled?) processing) + :on-press (fn [] + (rf/dispatch [:set-in [:profile/login :key-uid] key-uid]) + (rf/dispatch [:profile.login/verify-database-password password + #(on-enter-password password)]))} + button-label]]]]])) diff --git a/src/status_im2/common/standard_authentication/forgot_password_doc/style.cljs b/src/status_im2/common/standard_authentication/forgot_password_doc/style.cljs new file mode 100644 index 0000000000..1a8ca24dd9 --- /dev/null +++ b/src/status_im2/common/standard_authentication/forgot_password_doc/style.cljs @@ -0,0 +1,6 @@ +(ns status-im2.common.standard-authentication.forgot-password-doc.style) + +(def container {:margin-right 16}) +(def step-container {:flex-direction :row :margin-top 14}) +(def step-content {:margin-left 10}) +(def step-title {:flex-direction :row}) diff --git a/src/status_im2/common/standard_authentication/forgot_password_doc/view.cljs b/src/status_im2/common/standard_authentication/forgot_password_doc/view.cljs new file mode 100644 index 0000000000..281ca8a059 --- /dev/null +++ b/src/status_im2/common/standard_authentication/forgot_password_doc/view.cljs @@ -0,0 +1,52 @@ +(ns status-im2.common.standard-authentication.forgot-password-doc.view + (:require [status-im2.common.standard-authentication.forgot-password-doc.style :as style] + [quo2.core :as quo] + [react-native.core :as rn] + [utils.i18n :as i18n])) + +(defn view + [{:keys [shell?]}] + [quo/documentation-drawers + {:title (i18n/label :t/forgot-your-password-info-title) + :shell? shell?} + [rn/view + {:style style/container} + [quo/text {:size :paragraph-2} (i18n/label :t/forgot-your-password-info-description)] + + [rn/view {:style style/step-container} + [quo/step {:in-blur-view? shell?} 1] + [rn/view + {:style style/step-content} + [quo/text {:size :paragraph-2 :weight :semi-bold} + (i18n/label :t/forgot-your-password-info-remove-app)] + [quo/text {:size :paragraph-2} (i18n/label :t/forgot-your-password-info-remove-app-description)]]] + + [rn/view {:style style/step-container} + [quo/step {:in-blur-view? shell?} 2] + [rn/view + {:style style/step-content} + [quo/text {:size :paragraph-2 :weight :semi-bold} + (i18n/label :t/forgot-your-password-info-reinstall-app)] + [quo/text {:size :paragraph-2} + (i18n/label :t/forgot-your-password-info-reinstall-app-description)]]] + + [rn/view {:style style/step-container} + [quo/step {:in-blur-view? shell?} 3] + [rn/view + {:style style/step-content} + [rn/view + {:style style/step-title} + [quo/text {:size :paragraph-2} (str (i18n/label :t/sign-up) " ")] + [quo/text {:size :paragraph-2 :weight :semi-bold} + (i18n/label :t/forgot-your-password-info-signup-with-key)]] + [quo/text {:size :paragraph-2} + (i18n/label :t/forgot-your-password-info-signup-with-key-description)]]] + + [rn/view {:style style/step-container} + [quo/step {:in-blur-view? shell?} 4] + [rn/view + {:style style/step-content} + [quo/text {:size :paragraph-2 :weight :semi-bold} + (i18n/label :t/forgot-your-password-info-create-new-password)] + [quo/text {:size :paragraph-2} + (i18n/label :t/forgot-your-password-info-create-new-password-description)]]]]]) diff --git a/src/status_im2/common/standard_authentication/password_input/style.cljs b/src/status_im2/common/standard_authentication/password_input/style.cljs new file mode 100644 index 0000000000..b15a6ed222 --- /dev/null +++ b/src/status_im2/common/standard_authentication/password_input/style.cljs @@ -0,0 +1,6 @@ +(ns status-im2.common.standard-authentication.password-input.style) + +(def error-message + {:margin-top 8 + :flex-direction :row + :align-items :center}) diff --git a/src/status_im2/common/standard_authentication/password_input/view.cljs b/src/status_im2/common/standard_authentication/password_input/view.cljs new file mode 100644 index 0000000000..05fd228450 --- /dev/null +++ b/src/status_im2/common/standard_authentication/password_input/view.cljs @@ -0,0 +1,70 @@ +(ns status-im2.common.standard-authentication.password-input.view + (:require [quo2.core :as quo] + [quo2.foundations.colors :as colors] + [react-native.core :as rn] + [utils.i18n :as i18n] + [utils.re-frame :as rf] + [utils.security.core :as security] + [status-im2.common.standard-authentication.password-input.style :as style] + [status-im2.common.standard-authentication.forgot-password-doc.view :as forgot-password-doc] + [quo2.theme :as quo.theme] + [clojure.string :as string])) + +(defn get-error-message + [error] + (if (and (some? error) + (or (= error "file is not a database") + (string/starts-with? error "failed to set ") + (string/starts-with? error "Failed"))) + (i18n/label :t/oops-wrong-password) + error)) + +(defn- on-change-password + [entered-password] + (rf/dispatch [:set-in [:profile/login :password] + (security/mask-data entered-password)]) + (rf/dispatch [:set-in [:profile/login :error] ""])) + +(defn- view-internal + [{:keys [default-password theme shell?]}] + (let [{:keys [error processing]} (rf/sub [:profile/login]) + error-message (get-error-message error) + error? (boolean (seq error-message))] + [:<> + [quo/input + {:type :password + :blur? true + :disabled? processing + :placeholder (i18n/label :t/type-your-password) + :auto-focus true + :error? error? + :label (i18n/label :t/profile-password) + :on-change-text on-change-password + :default-value (security/safe-unmask-data default-password)}] + (when error? + [rn/view {:style style/error-message} + [quo/info-message + {:type :error + :size :default + :icon :i/info} + error-message] + [rn/pressable + {:hit-slop {:top 6 :bottom 20 :left 0 :right 0} + :disabled processing + :on-press (fn [] + (rn/dismiss-keyboard!) + (rf/dispatch [:show-bottom-sheet + {:content #(forgot-password-doc/view {:shell? shell?}) + :theme theme + :shell? shell?}]))} + [rn/text + {:style {:text-decoration-line :underline + :color (colors/theme-colors + (colors/custom-color :danger 50) + (colors/custom-color :danger 60) + theme)} + :size :paragraph-2 + :suppress-highlighting true} + (i18n/label :t/forgot-password)]]])])) + +(def view (quo.theme/with-theme view-internal)) diff --git a/src/status_im2/common/standard_authentication/standard_auth/view.cljs b/src/status_im2/common/standard_authentication/standard_auth/view.cljs new file mode 100644 index 0000000000..cbf78c0245 --- /dev/null +++ b/src/status_im2/common/standard_authentication/standard_auth/view.cljs @@ -0,0 +1,79 @@ +(ns status-im2.common.standard-authentication.standard-auth.view + (:require + [quo2.core :as quo] + [quo2.theme :as quo.theme] + [reagent.core :as reagent] + [utils.re-frame :as rf] + [react-native.touch-id :as biometric] + [utils.i18n :as i18n] + [taoensso.timbre :as log] + [status-im2.common.standard-authentication.enter-password.view :as enter-password] + [react-native.core :as rn])) + +(defn reset-password + [] + (rf/dispatch [:set-in [:profile/login :password] nil]) + (rf/dispatch [:set-in [:profile/login :error] ""])) + +(defn authorize + [{:keys [on-enter-password biometric-auth? on-auth-success on-auth-fail on-close + fallback-button-label theme blur?]}] + (biometric/get-supported-type + (fn [biometric-type] + (if (and biometric-auth? biometric-type) + (biometric/authenticate + {:reason (i18n/label :t/biometric-auth-confirm-message) + :on-success (fn [response] + (when on-auth-success (on-auth-success response)) + (log/info "response" response)) + :on-fail (fn [error] + (log/error "Authentication Failed. Error:" error) + (when on-auth-fail (on-auth-fail error)) + (rf/dispatch [:show-bottom-sheet + {:theme theme + :shell? blur? + :content (fn [] + [enter-password/view + {:on-enter-password on-enter-password}])}]))}) + (do + (reset-password) + (rf/dispatch [:show-bottom-sheet + {:on-close on-close + :theme theme + :shell? blur? + :content (fn [] + [enter-password/view + {:on-enter-password on-enter-password + :button-label fallback-button-label}])}])))))) + +(defn- view-internal + [_] + (let [reset-slider? (reagent/atom false) + on-close #(reset! reset-slider? true)] + (fn [{:keys [biometric-auth? + track-text + customization-color + fallback-button-label + on-enter-password + on-auth-success + on-auth-fail + size + theme + blur?]}] + [rn/view {:style {:flex 1}} + [quo/slide-button + {:size size + :customization-color customization-color + :on-reset (when @reset-slider? #(reset! reset-slider? false)) + :on-complete #(authorize {:on-close on-close + :theme theme + :blur? blur? + :on-enter-password on-enter-password + :biometric-auth? biometric-auth? + :on-auth-success on-auth-success + :on-auth-fail on-auth-fail + :fallback-button-label fallback-button-label}) + :track-icon :i/face-id + :track-text track-text}]]))) + +(def view (quo.theme/with-theme view-internal)) diff --git a/src/status_im2/contexts/profile/login/events.cljs b/src/status_im2/contexts/profile/login/events.cljs index fa6f3b9ca5..86b3cfddb6 100644 --- a/src/status_im2/contexts/profile/login/events.cljs +++ b/src/status_im2/contexts/profile/login/events.cljs @@ -181,3 +181,29 @@ (rf/merge cofx (navigation/init-root :profiles) (biometric/show-message code))) + +(rf/defn verify-database-password + {:events [:profile.login/verify-database-password]} + [_ entered-password cb] + (let [hashed-password (-> entered-password + security/safe-unmask-data + native-module/sha3)] + {:json-rpc/call [{:method "accounts_verifyPassword" + :params [hashed-password] + :on-success #(do (rf/dispatch [:profile.login/verified-database-password %]) (cb)) + :on-error #(log/error "accounts_verifyPassword error" %)}]})) + +(rf/defn verify-database-password-success + {:events [:profile.login/verified-database-password]} + [{:keys [db] :as cofx} valid?] + (if valid? + (do + {:db (update db + :profile/login + dissoc + :processing :error)}) + {:db (update db + :profile/login + #(-> % + (dissoc :processing) + (assoc :error "Invalid password")))})) diff --git a/src/status_im2/contexts/profile/profiles/style.cljs b/src/status_im2/contexts/profile/profiles/style.cljs index 458cadcfc0..008f0e2019 100644 --- a/src/status_im2/contexts/profile/profiles/style.cljs +++ b/src/status_im2/contexts/profile/profiles/style.cljs @@ -53,12 +53,3 @@ (def login-profile-card {:margin-bottom 20}) -(def error-message - {:margin-top 8 - :flex-direction :row - :align-items :center}) - -(def forget-password-doc-container {:margin-right 16}) -(def forget-password-step-container {:flex-direction :row :margin-top 14}) -(def forget-password-step-content {:margin-left 10}) -(def forget-password-step-title {:flex-direction :row}) diff --git a/src/status_im2/contexts/profile/profiles/view.cljs b/src/status_im2/contexts/profile/profiles/view.cljs index 64275ab83c..77c2c6485d 100644 --- a/src/status_im2/contexts/profile/profiles/view.cljs +++ b/src/status_im2/contexts/profile/profiles/view.cljs @@ -1,8 +1,6 @@ (ns status-im2.contexts.profile.profiles.view - (:require [clojure.string :as string] - [native-module.core :as native-module] + (:require [native-module.core :as native-module] [quo2.core :as quo] - [quo2.foundations.colors :as colors] [react-native.core :as rn] [react-native.reanimated :as reanimated] [react-native.safe-area :as safe-area] @@ -15,8 +13,8 @@ [taoensso.timbre :as log] [utils.i18n :as i18n] [utils.re-frame :as rf] - [utils.security.core :as security] - [utils.transforms :as transforms])) + [utils.transforms :as transforms] + [status-im2.common.standard-authentication.password-input.view :as password-input])) (defonce push-animation-fn-atom (atom nil)) (defonce pop-animation-fn-atom (atom nil)) @@ -166,69 +164,12 @@ [props] [:f> f-profiles-section props]) -(defn forget-password-doc - [] - [quo/documentation-drawers - {:title (i18n/label :t/forgot-your-password-info-title) - :shell? true} - [rn/view - {:style style/forget-password-doc-container} - [quo/text {:size :paragraph-2} (i18n/label :t/forgot-your-password-info-description)] - - [rn/view {:style style/forget-password-step-container} - [quo/step {:in-blur-view? true} 1] - [rn/view - {:style style/forget-password-step-content} - [quo/text {:size :paragraph-2 :weight :semi-bold} - (i18n/label :t/forgot-your-password-info-remove-app)] - [quo/text {:size :paragraph-2} (i18n/label :t/forgot-your-password-info-remove-app-description)]]] - - [rn/view {:style style/forget-password-step-container} - [quo/step {:in-blur-view? true} 2] - [rn/view - {:style style/forget-password-step-content} - [quo/text {:size :paragraph-2 :weight :semi-bold} - (i18n/label :t/forgot-your-password-info-reinstall-app)] - [quo/text {:size :paragraph-2} - (i18n/label :t/forgot-your-password-info-reinstall-app-description)]]] - - [rn/view {:style style/forget-password-step-container} - [quo/step {:in-blur-view? true} 3] - [rn/view - {:style style/forget-password-step-content} - [rn/view - {:style style/forget-password-step-title} - [quo/text {:size :paragraph-2} (str (i18n/label :t/sign-up) " ")] - [quo/text {:size :paragraph-2 :weight :semi-bold} - (i18n/label :t/forgot-your-password-info-signup-with-key)]] - [quo/text {:size :paragraph-2} - (i18n/label :t/forgot-your-password-info-signup-with-key-description)]]] - - [rn/view {:style style/forget-password-step-container} - [quo/step {:in-blur-view? true} 4] - [rn/view - {:style style/forget-password-step-content} - [quo/text {:size :paragraph-2 :weight :semi-bold} - (i18n/label :t/forgot-your-password-info-create-new-password)] - [quo/text {:size :paragraph-2} - (i18n/label :t/forgot-your-password-info-create-new-password-description)]]]]]) - -(defn- get-error-message - [error] - (if (and (some? error) - (or (= error "file is not a database") - (string/starts-with? error "failed to set ") - (string/starts-with? error "Failed"))) - (i18n/label :t/oops-wrong-password) - error)) - (defn login-section [{:keys [set-show-profiles]}] - (let [{:keys [error processing password]} (rf/sub [:profile/login]) + (let [{:keys [processing password]} (rf/sub [:profile/login]) {:keys [key-uid name 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]) - error (get-error-message error) login-multiaccount #(rf/dispatch [:profile.login/login])] [rn/keyboard-avoiding-view {:style style/login-container @@ -263,41 +204,9 @@ :customization-color (or customization-color :primary) :profile-picture profile-picture :card-style style/login-profile-card}] - [quo/input - {:type :password - :blur? true - :disabled? processing - :placeholder (i18n/label :t/type-your-password) - :auto-focus true - :error? (seq error) - :label (i18n/label :t/profile-password) - :on-change-text (fn [password] - (rf/dispatch [:set-in [:profile/login :password] - (security/mask-data password)]) - (rf/dispatch [:set-in [:profile/login :error] ""])) - :default-value (security/safe-unmask-data password) - :on-submit-editing (when sign-in-enabled? login-multiaccount)}] - (when (seq error) - [rn/view {:style style/error-message} - [quo/info-message - {:type :error - :size :default - :icon :i/info} - error] - [rn/touchable-opacity - {:hit-slop {:top 6 :bottom 20 :left 0 :right 0} - :disabled processing - :active-opacity 1 - :on-press (fn [] - (rn/dismiss-keyboard!) - (rf/dispatch [:show-bottom-sheet - {:content forget-password-doc :shell? true}]))} - [rn/text - {:style {:text-decoration-line :underline - :color colors/danger-60} - :size :paragraph-2 - :suppress-highlighting true} - (i18n/label :t/forgot-password)]]])] + [password-input/view + {:shell? true + :default-password password}]] [quo/button {:size 40 :type :primary diff --git a/src/status_im2/contexts/quo_preview/buttons/slide_button.cljs b/src/status_im2/contexts/quo_preview/buttons/slide_button.cljs index e79399cda4..a26a047168 100644 --- a/src/status_im2/contexts/quo_preview/buttons/slide_button.cljs +++ b/src/status_im2/contexts/quo_preview/buttons/slide_button.cljs @@ -6,8 +6,8 @@ (def descriptor [{:key :size :type :select - :options [{:key :large} - {:key :small}]} + :options [{:key :size/s-48} + {:key :size/s-40}]} {:key :disabled? :type :boolean} (preview/customization-color-option {:key :color})]) @@ -16,10 +16,8 @@ [] (let [state (reagent/atom {:disabled? false :color :blue - :size :large}) + :size :size/s-48}) color (reagent/cursor state [:color]) - disabled? (reagent/cursor state [:disabled?]) - size (reagent/cursor state [:size]) complete? (reagent/atom false)] (fn [] [preview/preview-container @@ -31,8 +29,8 @@ {:track-text "We gotta slide" :track-icon :face-id :customization-color @color - :size @size - :disabled? @disabled? + :size (:size @state) + :disabled? (:disabled? @state) :on-complete (fn [] (js/setTimeout (fn [] (reset! complete? true)) 1000) diff --git a/src/status_im2/contexts/syncing/events.cljs b/src/status_im2/contexts/syncing/events.cljs index 03fe82019c..40d960f2fe 100644 --- a/src/status_im2/contexts/syncing/events.cljs +++ b/src/status_im2/contexts/syncing/events.cljs @@ -1,6 +1,7 @@ (ns status-im2.contexts.syncing.events (:require [native-module.core :as native-module] [re-frame.core :as re-frame] + [clojure.string :as string] [status-im.data-store.settings :as data-store.settings] [status-im.node.core :as node] [status-im2.config :as config] @@ -9,6 +10,7 @@ [utils.re-frame :as rf] [utils.security.core :as security] [utils.transforms :as transforms] + [status-im2.contexts.syncing.utils :as sync-utils] [react-native.platform :as platform])) (rf/defn local-pairing-update-role @@ -73,16 +75,17 @@ (native-module/prepare-dir-and-update-config "" default-node-config-string callback))) (rf/defn preparations-for-connection-string - {:events [:syncing/get-connection-string-for-bootstrapping-another-device]} - [{:keys [db]} entered-password set-code] - (let [valid-password? (>= (count entered-password) constants/min-password-length) - show-sheet (fn [connection-string] - (set-code connection-string) - (rf/dispatch [:syncing/update-role constants/local-pairing-role-sender]) - (rf/dispatch [:bottom-sheet/hide]))] - (if valid-password? - (let [sha3-pwd (native-module/sha3 (str (security/safe-unmask-data entered-password))) - key-uid (get-in db [:profile/profile :key-uid]) + {:events [:syncing/get-connection-string]} + [{:keys [db] :as cofx} entered-password on-valid-connection-string] + (let [error (get-in db [:profile/login :error]) + handle-connection (fn [response] + (when (sync-utils/valid-connection-string? response) + (on-valid-connection-string response) + (rf/dispatch [:syncing/update-role constants/local-pairing-role-sender]) + (rf/dispatch [:hide-bottom-sheet])))] + (when-not (and error (string/blank? error)) + (let [key-uid (get-in db [:profile/login :key-uid]) + sha3-pwd (native-module/sha3 (str (security/safe-unmask-data entered-password))) config-map (.stringify js/JSON (clj->js {:senderConfig {:keyUID key-uid :keystorePath "" @@ -91,5 +94,4 @@ :serverConfig {:timeout 0}}))] (native-module/get-connection-string-for-bootstrapping-another-device config-map - #(show-sheet %))) - (show-sheet "")))) + handle-connection))))) diff --git a/src/status_im2/contexts/syncing/setup_syncing/style.cljs b/src/status_im2/contexts/syncing/setup_syncing/style.cljs index 5cdabc83e2..8118a900d3 100644 --- a/src/status_im2/contexts/syncing/setup_syncing/style.cljs +++ b/src/status_im2/contexts/syncing/setup_syncing/style.cljs @@ -6,7 +6,8 @@ :flex 1}) (def page-container - {:margin-horizontal 20}) + {:margin-top 14 + :margin-horizontal 20}) (def title-container {:flex-direction :row @@ -17,17 +18,18 @@ {:height 56}) (def sync-code - {:margin-top 36}) + {:margin-top 20}) -(defn qr-container - [valid-code?] - (merge {:margin-top 12 - :background-color colors/white-opa-5 - :border-radius 20 - :padding 12} - (if valid-code? - {:flex 1} - {:aspect-ratio 1}))) +(def standard-auth + {:margin-top 12 + :flex 1}) + +(def qr-container + {:margin-top 12 + :background-color colors/white-opa-5 + :border-radius 20 + :flex 1 + :padding 12}) (def sub-text-container {:margin-bottom 8 @@ -39,10 +41,3 @@ {:flex 1 :margin 12}) -(def generate-button - {:position :absolute - :top "50%" - :bottom 0 - :left 0 - :right 0 - :margin-horizontal 60}) diff --git a/src/status_im2/contexts/syncing/setup_syncing/view.cljs b/src/status_im2/contexts/syncing/setup_syncing/view.cljs index 6339c5be99..277645a65b 100644 --- a/src/status_im2/contexts/syncing/setup_syncing/view.cljs +++ b/src/status_im2/contexts/syncing/setup_syncing/view.cljs @@ -3,12 +3,12 @@ [quo2.foundations.colors :as colors] [react-native.clipboard :as clipboard] [react-native.core :as rn] - [react-native.hooks :as hooks] - [reagent.core :as reagent] [status-im2.common.qr-code-viewer.view :as qr-code-viewer] + [reagent.core :as reagent] [status-im2.common.resources :as resources] + [status-im2.common.standard-authentication.standard-auth.view :as standard-auth] + [react-native.hooks :as hooks] [status-im2.contexts.syncing.setup-syncing.style :as style] - [status-im2.contexts.syncing.sheets.enter-password.view :as enter-password] [status-im2.contexts.syncing.utils :as sync-utils] [utils.datetime :as datetime] [utils.i18n :as i18n] @@ -24,31 +24,37 @@ (defn view [] - (let [profile-color (rf/sub [:profile/customization-color]) - valid-for-ms (reagent/atom code-valid-for-ms) - code (reagent/atom nil) - delay-ms (reagent/atom nil) - timestamp (reagent/atom nil) - set-code (fn [connection-string] - (when (sync-utils/valid-connection-string? connection-string) - (reset! timestamp (* 1000 (js/Math.ceil (/ (datetime/timestamp) 1000)))) - (reset! delay-ms 1000) - (reset! code connection-string))) - clock (fn [] - (if (pos? (- code-valid-for-ms - (- (* 1000 (js/Math.ceil (/ (datetime/timestamp) 1000))) - @timestamp))) - (swap! valid-for-ms (fn [_] - (- code-valid-for-ms - (- (* 1000 - (js/Math.ceil (/ (datetime/timestamp) 1000))) - @timestamp)))) - (reset! delay-ms nil))) - cleanup-clock (fn [] - (reset! code nil) - (reset! timestamp nil) - (reset! valid-for-ms code-valid-for-ms))] - + (let [{:keys [customization-color]} (rf/sub [:profile/multiaccount]) + valid-for-ms (reagent/atom code-valid-for-ms) + code (reagent/atom nil) + delay-ms (reagent/atom nil) + timestamp (reagent/atom nil) + set-code (fn [connection-string] + (when (sync-utils/valid-connection-string? connection-string) + (reset! timestamp (* 1000 + (js/Math.ceil (/ (datetime/timestamp) + 1000)))) + (reset! delay-ms 1000) + (reset! code connection-string))) + clock (fn [] + (if (pos? (- code-valid-for-ms + (- (* 1000 + (js/Math.ceil (/ (datetime/timestamp) 1000))) + @timestamp))) + (swap! valid-for-ms (fn [_] + (- code-valid-for-ms + (- (* 1000 + (js/Math.ceil + (/ (datetime/timestamp) 1000))) + @timestamp)))) + (reset! delay-ms nil))) + cleanup-clock (fn [] + (reset! code nil) + (reset! timestamp nil) + (reset! valid-for-ms code-valid-for-ms)) + on-enter-password (fn [entered-password] + (rf/dispatch [:syncing/get-connection-string entered-password + set-code]))] (fn [] [rn/view {:style style/container-main} [:f> f-use-interval clock cleanup-clock @delay-ms] @@ -68,29 +74,14 @@ :weight :semi-bold :style {:color colors/white}} (i18n/label :t/setup-syncing)]] - [rn/view {:style (style/qr-container (sync-utils/valid-connection-string? @code))} + [rn/view {:style style/qr-container} (if (sync-utils/valid-connection-string? @code) - [qr-code-viewer/qr-code-view 331 @code] + [rn/view {:style {:margin-horizontal 12}} + [qr-code-viewer/qr-code-view 311 @code]] [quo/qr-code {:source (resources/get-image :qr-code) :height 220 :width "100%"}]) - (when-not (sync-utils/valid-connection-string? @code) - [quo/button - {:on-press (fn [] - ;TODO https://github.com/status-im/status-mobile/issues/15570 - ;remove old bottom sheet when Authentication process design is - ;created. - (rf/dispatch [:bottom-sheet/hide-old]) - (rf/dispatch [:bottom-sheet/show-sheet-old - {:content (fn [] - [enter-password/sheet set-code])}])) - :type :primary - :customization-color profile-color - :size 40 - :container-style style/generate-button - :icon-left :i/reveal} - (i18n/label :t/reveal-sync-code)]) (when (sync-utils/valid-connection-string? @code) [rn/view {:style style/valid-cs-container} @@ -122,7 +113,17 @@ :type :grey :container-style {:margin-top 12} :icon-left :i/copy} - (i18n/label :t/copy-qr)]])]] + (i18n/label :t/copy-qr)]]) + (when-not (sync-utils/valid-connection-string? @code) + [rn/view {:style style/standard-auth} + [standard-auth/view + {:blur? true + :size :size/s-40 + :track-text (i18n/label :t/slide-to-reveal-code) + :customization-color customization-color + :on-enter-password on-enter-password + :biometric-auth? false + :fallback-button-label (i18n/label :t/reveal-sync-code)}]])]] [rn/view {:style style/sync-code} [quo/divider-label {:tight? false} (i18n/label :t/have-a-sync-code?)] [quo/action-drawer diff --git a/src/status_im2/contexts/syncing/sheets/enter_password/view.cljs b/src/status_im2/contexts/syncing/sheets/enter_password/view.cljs deleted file mode 100644 index 9286895eee..0000000000 --- a/src/status_im2/contexts/syncing/sheets/enter_password/view.cljs +++ /dev/null @@ -1,42 +0,0 @@ -(ns status-im2.contexts.syncing.sheets.enter-password.view - (:require [utils.i18n :as i18n] - [quo.core :as quo-old] - [quo2.core :as quo] - [quo2.foundations.colors :as colors] - [react-native.core :as rn] - [utils.re-frame :as rf])) - -;;TODO : this file is temporary and will be removed for new design auth method -(defn sheet - [set-code] - (let [entered-password (atom "")] - [:<> - [rn/view {:margin 20} - [rn/view - [quo/text - {:accessibility-label :sync-code-generated - :weight :bold - :size :heading-1 - :style {:color colors/neutral-100 - :margin 20}} - (i18n/label :t/enter-your-password)] - [rn/view {:flex-direction :row :align-items :center} - [rn/view {:flex 1} - [quo-old/text-input - {:placeholder (i18n/label :t/enter-your-password) - :auto-focus true - :accessibility-label :password-input - :show-cancel false - :on-change-text #(reset! entered-password %) - :secure-text-entry true}]]] - [rn/view - {:padding-horizontal 18 - :margin-top 20} - [quo/button - {:on-press (fn [] - ;TODO https://github.com/status-im/status-mobile/issues/15570 - ;remove old bottom sheet when Authentication process design is created. - (rf/dispatch [:bottom-sheet/hide-old]) - (rf/dispatch [:syncing/get-connection-string-for-bootstrapping-another-device - @entered-password set-code]))} - (i18n/label :t/generate-scan-sync-code)]]]]])) diff --git a/translations/en.json b/translations/en.json index f2c434216c..b70c249aa7 100644 --- a/translations/en.json +++ b/translations/en.json @@ -1892,6 +1892,7 @@ "swap": "Swap", "select-token-to-swap": "Select token to Swap", "select-token-to-receive": "Select token to receive", + "slide-to-reveal-code": "Slide to reveal code", "minimum-received": "Minimum received", "powered-by-paraswap": "Powered by Paraswap", "priority": "Priority", @@ -2050,7 +2051,7 @@ "local-pairing-experimental-mode": "Local Pairing Mode (alpha)", "syncing": "Syncing", "synced-devices": "Synced Devices", - "setup-syncing": "Setup Syncing", + "setup-syncing": "Pair devices to sync", "sync-code": "Sync Code", "sync-code-generated": "Sync code generated", "generate-scan-sync-code": "Generate Scan Sync Code",