Standardized in-app authentication (#16916)

* chore: move password input to connected component
---------

Co-authored-by: Jamie Caprani <jamiecaprani@gmail.com>
Co-authored-by: frank <lovefree103@gmail.com>
This commit is contained in:
Siddarth Kumar 2023-09-28 16:53:15 +05:30 committed by GitHub
parent 6b9b5fa300
commit 0522120c66
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 451 additions and 268 deletions

View File

@ -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))

View File

@ -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)})

View File

@ -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))))

View File

@ -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

View File

@ -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))

View File

@ -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"))))))

View File

@ -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

View File

@ -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})

View File

@ -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]]]]]))

View File

@ -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})

View File

@ -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)]]]]])

View File

@ -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})

View File

@ -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))

View File

@ -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))

View File

@ -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")))}))

View File

@ -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})

View File

@ -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

View File

@ -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)

View File

@ -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)))))

View File

@ -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})

View File

@ -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

View File

@ -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)]]]]]))

View File

@ -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",