Change password inside new settings (#19474)

* feat: added change-password screen

Signed-off-by: Lungu Cristian <lungucristian95@gmail.com>

* fix: pw verification error not shown

Signed-off-by: Lungu Cristian <lungucristian95@gmail.com>

* feat: added changing password with confirmation and loading

Signed-off-by: Lungu Cristian <lungucristian95@gmail.com>

* fix: adjusted flow

Signed-off-by: Lungu Cristian <lungucristian95@gmail.com>

* fix: added minimum waiting time when loading

Signed-off-by: Lungu Cristian <lungucristian95@gmail.com>

* ref: moved events to change-password

Signed-off-by: Lungu Cristian <lungucristian95@gmail.com>

* fix: added styles where missing

Signed-off-by: Lungu Cristian <lungucristian95@gmail.com>

* ref: moved header out

Signed-off-by: Lungu Cristian <lungucristian95@gmail.com>

* ref: moved forms into separate namespaces

Signed-off-by: Lungu Cristian <lungucristian95@gmail.com>

* fix: linter promesa alias issue

Signed-off-by: Lungu Cristian <lungucristian95@gmail.com>

* feat: added password-tips quo-component

Signed-off-by: Lungu Cristian <lungucristian95@gmail.com>

* fix: validation message

Signed-off-by: Lungu Cristian <lungucristian95@gmail.com>

* fix: removed bottom-sheet event

Signed-off-by: Lungu Cristian <lungucristian95@gmail.com>

* removed temp file

Signed-off-by: Lungu Cristian <lungucristian95@gmail.com>

* fix: addressed review comments

Signed-off-by: Lungu Cristian <lungucristian95@gmail.com>

* fix: addressed @seanstrom's review comments

Signed-off-by: Lungu Cristian <lungucristian95@gmail.com>

* fix: address @ilmotta's review comments

Signed-off-by: Lungu Cristian <lungucristian95@gmail.com>

* fix: addressed @vkjr's review comments

Signed-off-by: Lungu Cristian <lungucristian95@gmail.com>

* fix: buttom button alignment with keyboard

Signed-off-by: Lungu Cristian <lungucristian95@gmail.com>

* fix: addressed review comments

Signed-off-by: Lungu Cristian <lungucristian95@gmail.com>

* fix: keyboard behavior

Signed-off-by: Lungu Cristian <lungucristian95@gmail.com>

* fix: navigation to loader

Signed-off-by: Lungu Cristian <lungucristian95@gmail.com>

* fix: use-theme usage

* fix: button alignment due to alert banner

---------

Signed-off-by: Lungu Cristian <lungucristian95@gmail.com>
This commit is contained in:
Lungu Cristian 2024-05-07 17:38:16 +03:00 committed by GitHub
parent 245b3fcf57
commit 161ba2713f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
26 changed files with 673 additions and 41 deletions

View File

@ -2,6 +2,7 @@
(:require
["react-native" :as react-native]
[clojure.string :as string]
[native-module.utils :as native-utils]
[react-native.platform :as platform]
[taoensso.timbre :as log]
[utils.transforms :as types]))
@ -543,13 +544,21 @@
(when platform/android?
(.resetKeyboardInputCursor ^js (ui-helper) input selection)))
;; passwords are hashed
(defn reset-password
[key-uid current-password# new-password# callback]
(log/debug "[native-module] change-database-password")
(init-keystore
key-uid
#(.reEncryptDbAndKeystore ^js (encryption) key-uid current-password# new-password# callback)))
([key-uid current-password-hashed new-password-hashed]
(native-utils/promisify-native-module-call reset-password
key-uid
current-password-hashed
new-password-hashed))
([key-uid current-password-hashed new-password-hashed callback]
(log/debug "[native-module] change-database-password")
(init-keystore
key-uid
#(.reEncryptDbAndKeystore ^js (encryption)
key-uid
current-password-hashed
new-password-hashed
callback))))
(defn convert-to-keycard-account
[{:keys [key-uid] :as multiaccount-data} settings current-password# new-password callback]

View File

@ -0,0 +1,23 @@
(ns native-module.utils
(:require
[clojure.string :as string]
[promesa.core :as promesa]
[utils.transforms :as types]))
(defn- promisify-callback
[res rej]
(fn [result]
(let [native-error (let [{:keys [error]} (types/json->clj result)]
(when-not (string/blank? error)
error))]
(if native-error
(rej (ex-info "Native module call error" {:error native-error}))
(res result)))))
(defn promisify-native-module-call
[f & args]
(promesa/create
(fn [res rej]
(->> [(promisify-callback res rej)]
(concat args)
(apply f)))))

View File

@ -0,0 +1,6 @@
(ns quo.components.password.password-tips.style)
(def password-tips
{:flex-direction :row
:margin-horizontal 20
:justify-content :space-between})

View File

@ -0,0 +1,30 @@
(ns quo.components.password.password-tips.view
(:require [quo.components.password.password-tips.style :as style]
[quo.components.password.tips.view :as tips]
[react-native.core :as rn]
[schema.core :as schema]
[utils.i18n :as i18n]))
(def ?schema
[:=>
[:cat
[:map {:closed true}
[:lower-case? :boolean]
[:upper-case? :boolean]
[:numbers? :boolean]
[:symbols? :boolean]]
[:any]]])
(defn- view-internal
[{:keys [lower-case? upper-case? numbers? symbols?]}]
[rn/view {:style style/password-tips}
[tips/view {:completed? lower-case?}
(i18n/label :t/password-creation-tips-1)]
[tips/view {:completed? upper-case?}
(i18n/label :t/password-creation-tips-2)]
[tips/view {:completed? numbers?}
(i18n/label :t/password-creation-tips-3)]
[tips/view {:completed? symbols?}
(i18n/label :t/password-creation-tips-4)]])
(def view (schema/instrument #'view-internal ?schema))

View File

@ -37,18 +37,17 @@
[{:keys [title title-accessibility-label input counter-top counter-bottom
title-right title-right-props]
avatar-props :avatar}]
(let [avatar-props (when avatar-props
(assoc avatar-props :size :size-32))
title-props (assoc title-right-props
:title title
:right title-right
:accessibility-label title-accessibility-label)]
(let [title-props (assoc title-right-props
:title title
:right title-right
:accessibility-label title-accessibility-label)]
[rn/view {:style style/header}
[rn/view {:style style/header-title}
(when avatar-props
(if (:group? avatar-props)
[group-avatar/view avatar-props]
[channel-avatar/view avatar-props]))
(let [avatar-props (assoc avatar-props :size :size-32)]
(if (:group? avatar-props)
[group-avatar/view avatar-props]
[channel-avatar/view avatar-props])))
[standard-title/view title-props]]
(when (= input :recovery-phrase)
[header-counter counter-top counter-bottom])]))

View File

@ -111,6 +111,7 @@
quo.components.numbered-keyboard.numbered-keyboard.view
quo.components.onboarding.small-option-card.view
quo.components.overlay.view
quo.components.password.password-tips.view
quo.components.password.tips.view
quo.components.profile.collectible-list-item.view
quo.components.profile.collectible.view
@ -356,6 +357,7 @@
;;;; Password
(def tips quo.components.password.tips.view/view)
(def password-tips quo.components.password.password-tips.view/view)
;;;; Profile
(def collectible quo.components.profile.collectible.view/collectible)

View File

@ -80,7 +80,7 @@
(defn save-user-password!
[key-uid password]
(keychain/save-credentials key-uid key-uid (security/safe-unmask-data password) #()))
(keychain/save-credentials key-uid key-uid (security/safe-unmask-data password)))
(defn get-user-password!
[key-uid callback]

View File

@ -123,11 +123,20 @@
(def ^:const profile-pictures-visibility-none 3)
(def ^:const min-password-length 6)
(def ^:const new-password-min-length 10)
(def ^:const max-group-chat-participants 20)
(def ^:const max-group-chat-name-length 24)
(def ^:const default-number-of-messages 20)
(def ^:const default-number-of-pin-messages 3)
(def ^:const password-tips [:lower-case? :upper-case? :numbers? :symbols?])
(def ^:const strength-status
{1 :very-weak
2 :weak
3 :okay
4 :strong
5 :very-strong})
(def ^:const mailserver-password "status-offline-inbox")
(def ^:const send-transaction-failed-parse-response 1)

View File

@ -6,6 +6,7 @@
[react-native.core :as rn]
[react-native.safe-area :as safe-area]
[reagent.core :as reagent]
[status-im.constants :as constants]
[status-im.contexts.onboarding.create-password.style :as style]
[utils.i18n :as i18n]
[utils.re-frame :as rf]
@ -80,18 +81,11 @@
:on-focus on-input-focus
:on-blur on-blur-repeat-password}]]))
(def strength-status
{1 :very-weak
2 :weak
3 :okay
4 :strong
5 :very-strong})
(defn help
[{{:keys [lower-case? upper-case? numbers? symbols?]} :validations
password-strength :password-strength}]
[rn/view
[quo/strength-divider {:type (strength-status password-strength :info)}
[quo/strength-divider {:type (constants/strength-status password-strength :info)}
(i18n/label :t/password-creation-tips-title)]
[rn/view {:style style/password-tips}
[quo/tips {:completed? lower-case?}
@ -109,16 +103,16 @@
utils.string/has-upper-case?
utils.string/has-numbers?
utils.string/has-symbols?
#(utils.string/at-least-n-chars? % 10))]
#(utils.string/at-least-n-chars? % constants/new-password-min-length))]
(->> password
(validations)
(zipmap [:lower-case? :upper-case? :numbers? :symbols? :long-enough?]))))
validations
(zipmap (conj constants/password-tips :long-enough?)))))
(defn calc-password-strength
[validations]
(->> (vals validations)
(filter true?)
(count)))
count))
(defn password-form
[]

View File

@ -136,6 +136,7 @@
numbered-keyboard]
[status-im.contexts.preview.quo.onboarding.small-option-card :as
small-option-card]
[status-im.contexts.preview.quo.password.password-tips :as password-tips]
[status-im.contexts.preview.quo.password.tips :as tips]
[status-im.contexts.preview.quo.profile.collectible :as collectible]
[status-im.contexts.preview.quo.profile.collectible-list-item :as collectible-list-item]
@ -425,7 +426,9 @@
:onboarding [{:name :small-option-card
:component small-option-card/view}]
:password [{:name :tips
:component tips/view}]
:component tips/view}
{:name :password-tips
:component password-tips/view}]
:profile [{:name :collectible
:component collectible/view}
{:name :collectible-list-item

View File

@ -0,0 +1,29 @@
(ns status-im.contexts.preview.quo.password.password-tips
(:require
[quo.core :as quo]
[quo.foundations.colors :as colors]
[reagent.core :as reagent]
[status-im.constants :as constant]
[status-im.contexts.preview.quo.preview :as preview]))
(def init-state
(reduce (fn [acc tip] (assoc acc tip false)) {} constant/password-tips))
(defn- make-tip-descriptor
[tip]
{:key tip
:type :boolean})
(def descriptor
(map make-tip-descriptor constant/password-tips))
(defn view
[]
(let [state (reagent/atom init-state)]
(fn []
[preview/preview-container
{:state state
:descriptor descriptor
:component-container-style {:padding 20
:background-color colors/neutral-95}}
[quo/password-tips @state]])))

View File

@ -23,7 +23,7 @@
:blur? true
:action :arrow}
{:title (i18n/label :t/password)
:on-press #(rf/dispatch [:open-modal :settings-password])
:on-press #(rf/dispatch [:open-modal :screen/settings-password])
:image-props :i/password
:image :icon
:blur? true

View File

@ -0,0 +1,17 @@
(ns status-im.contexts.profile.settings.screens.password.change-password.effects
(:require [native-module.core :as native-module]
[promesa.core :as promesa]
[status-im.common.keychain.events :as keychain]
[utils.re-frame :as rf]
[utils.security.core :as security]))
(rf/reg-fx :effects.change-password/change-password
(fn [{:keys [key-uid old-password new-password on-success on-fail]}]
(let [hash-masked-password (fn [password]
(-> password security/hash-masked-password security/safe-unmask-data))
old-password-hashed (hash-masked-password old-password)
new-password-hashed (hash-masked-password new-password)]
(-> (native-module/reset-password key-uid old-password-hashed new-password-hashed)
(promesa/then #(keychain/save-user-password! key-uid new-password-hashed))
(promesa/then on-success)
(promesa/catch on-fail)))))

View File

@ -0,0 +1,68 @@
(ns status-im.contexts.profile.settings.screens.password.change-password.events
(:require [status-im.contexts.profile.settings.screens.password.change-password.effects]
[taoensso.timbre :as log]
[utils.re-frame :as rf]
[utils.security.core :as security]))
(rf/reg-event-fx :change-password/verify-old-password
(fn [_ [entered-password]]
(let [hashed-password (-> entered-password
security/hash-masked-password
security/safe-unmask-data)]
{:json-rpc/call [{:method "accounts_verifyPassword"
:params [hashed-password]
:on-success (fn [valid?]
(rf/dispatch [:change-password/password-verify-success valid?
entered-password]))
:on-error (fn [error]
(log/error "accounts_verifyPassword error"
{:error error
:event :password-settings/change-password}))}]})))
(rf/reg-event-fx :change-password/password-verify-success
(fn [{:keys [db]} [valid? old-password]]
{:db (if valid?
(-> db
(assoc-in [:settings/change-password :old-password] old-password)
(assoc-in [:settings/change-password :current-step] :new-password))
(assoc-in db [:settings/change-password :verify-error] true))}))
(rf/reg-event-fx :change-password/reset-error
(fn [{:keys [db]}]
{:db (assoc-in db [:settings/change-password :verify-error] false)}))
(rf/reg-event-fx :change-password/reset
(fn [{:keys [db]}]
{:db (assoc db :settings/change-password {})}))
(rf/reg-event-fx :change-password/confirm-new-password
(fn [{:keys [db]} [new-password]]
{:db (assoc-in db [:settings/change-password :new-password] new-password)
:fx [[:dispatch [:change-password/submit]]]}))
(rf/reg-event-fx :change-password/submit
(fn [{:keys [db]}]
(let [key-uid (get-in db [:profile/profile :key-uid])
{:keys [new-password old-password]} (get db :settings/change-password)]
{:db (assoc-in db [:settings/change-password :loading?] true)
:fx [[:dispatch [:dismiss-keyboard]]
[:dispatch [:open-modal :screen/change-password-loading]]
[:effects.change-password/change-password
{:key-uid key-uid
:old-password old-password
:new-password new-password
:on-success (fn []
(rf/dispatch [:change-password/submit-success]))
:on-fail (fn [error]
(rf/dispatch [:change-password/submit-fail error]))}]]})))
(rf/reg-event-fx :change-password/submit-fail
(fn [_ [error]]
(log/error "failed to change the password"
{:error error
:event :change-password/submit})
{:fx [[:dispatch [:change-password/reset]]
[:dispatch [:navigate-back]]]}))
(rf/reg-event-fx :change-password/submit-success
(fn [{:keys [db]}]
{:db (assoc-in db [:settings/change-password :loading?] false)}))

View File

@ -0,0 +1,19 @@
(ns status-im.contexts.profile.settings.screens.password.change-password.header
(:require [quo.core :as quo]
[react-native.core :as rn]
[status-im.contexts.profile.settings.screens.password.change-password.style :as style]
[utils.i18n :as i18n]))
(defn view
[]
[rn/view {:style style/heading}
[quo/text
{:style style/heading-title
:weight :semi-bold
:size :heading-1}
(i18n/label :t/change-password)]
[quo/text
{:style style/heading-subtitle
:weight :regular
:size :paragraph-1}
(i18n/label :t/change-password-description)]])

View File

@ -0,0 +1,54 @@
(ns status-im.contexts.profile.settings.screens.password.change-password.loading
(:require [quo.core :as quo]
[react-native.core :as rn]
[react-native.safe-area :as safe-area]
[status-im.contexts.profile.settings.screens.password.change-password.style :as style]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
;; NOTE: The duration of the password change depends on the device hardware, so to avoid
;; quick changes in UI when mounted, we're waiting for a bit.
(def ^:private minimum-loading-time 5000)
(defn- handle-logout
[]
(rf/dispatch [:multiaccounts.logout.ui/logout-pressed])
(rf/dispatch [:change-password/reset]))
(defn view
[]
(let [insets (safe-area/get-insets)
[minimum-loading-timeout-done?
set-minimum-loading-timeout-done] (rn/use-state false)
loading? (rf/sub [:settings/change-password-loading])
done? (and (not loading?) minimum-loading-timeout-done?)]
(rn/use-mount (fn []
(js/setTimeout
#(set-minimum-loading-timeout-done true)
minimum-loading-time)))
[quo/overlay {:type :shell}
[rn/view
{:key :change-password-loading
:style (style/loading-container insets)}
[quo/page-nav]
[quo/page-top
{:description :text
:title (if done?
(i18n/label :t/change-password-done-header)
(i18n/label :t/change-password-loading-header))
:description-text (if done?
(i18n/label :t/change-password-done-description)
(i18n/label :t/change-password-loading-description))}]
[rn/view
{:style style/loading-content}
(when-not done?
[quo/information-box
{:type :error
:style {:margin-top 12}
:icon :i/info}
(i18n/label :t/change-password-loading-warning)])]
(when done?
[quo/logout-button
{:container-style style/logout-container
:on-press handle-logout}])]]))

View File

@ -0,0 +1,157 @@
(ns status-im.contexts.profile.settings.screens.password.change-password.new-password-form
(:require
[clojure.string :as string]
[quo.core :as quo]
[quo.foundations.colors :as colors]
[react-native.core :as rn]
[status-im.constants :as constant]
[status-im.contexts.profile.settings.screens.password.change-password.events]
[status-im.contexts.profile.settings.screens.password.change-password.header :as header]
[status-im.contexts.profile.settings.screens.password.change-password.style :as style]
[utils.i18n :as i18n]
[utils.re-frame :as rf]
[utils.security.core :as security]
[utils.string :as utils.string]))
(defn- password-with-hint
[{{:keys [text status shown?]} :hint :as input-props}]
[:<>
[quo/input
(-> input-props
(dissoc :hint)
(assoc :type :password
:blur? true))]
[rn/view {:style style/info-message}
(when shown?
[quo/info-message
(cond-> {:type status
:size :default}
(not= :success status) (assoc :icon :i/info)
(= :success status) (assoc :icon :i/positive-state)
(= :default status) (assoc :text-color colors/white-70-blur
:icon-color colors/white-70-blur))
text])]])
(defn- calc-password-strength
[validations]
(->> (vals validations)
(filter true?)
count))
(defn- help
[{:keys [validations]}]
(let [{:keys [lower-case? upper-case? numbers? symbols?]} validations
password-strength (calc-password-strength validations)]
[rn/view
[quo/strength-divider {:type (constant/strength-status password-strength :info)}
(i18n/label :t/password-creation-tips-title)]
[rn/view {:style style/password-tips}
[quo/tips {:completed? lower-case?}
(i18n/label :t/password-creation-tips-1)]
[quo/tips {:completed? upper-case?}
(i18n/label :t/password-creation-tips-2)]
[quo/tips {:completed? numbers?}
(i18n/label :t/password-creation-tips-3)]
[quo/tips {:completed? symbols?}
(i18n/label :t/password-creation-tips-4)]]]))
(defn- password-validations
[password]
{:lower-case? (utils.string/has-lower-case? password)
:upper-case? (utils.string/has-upper-case? password)
:numbers? (utils.string/has-numbers? password)
:symbols? (utils.string/has-symbols? password)})
(defn view
[]
(let [customization-color (rf/sub [:profile/customization-color])
[password set-password] (rn/use-state "")
[repeat-password set-repeat-password] (rn/use-state "")
[disclaimer-accepted? set-disclaimer-accepted] (rn/use-state false)
[focused? set-focused] (rn/use-state false)
[show-validation? set-show-validation] (rn/use-state false)
;; validations
not-blank? (complement string/blank?)
validations (password-validations password)
long-enough? (utils.string/at-least-n-chars?
password
constant/new-password-min-length)
empty-password? (string/blank? password)
same-passwords? (and (not empty-password?)
(= password repeat-password))
meet-requirements? (and (not empty-password?)
long-enough?
same-passwords?
disclaimer-accepted?)
error? (and show-validation?
(not same-passwords?)
(not empty-password?))
;; handlers
on-change-password (fn [new-value]
(set-password new-value)
(when (and (not-blank? new-value)
(= (count new-value)
(count repeat-password)))
(set-show-validation true)))
on-change-repeat-password (fn [new-value]
(set-repeat-password new-value)
(when (and (not-blank? new-value)
(= (count new-value)
(count password)))
(set-show-validation true)))
on-blur-repeat-password (fn []
(if empty-password?
(set-show-validation false)
(set-show-validation true)))
on-input-focus (fn [] (set-focused true))
on-disclaimer-change (fn []
(set-disclaimer-accepted
(not disclaimer-accepted?)))
on-submit (fn []
(rf/dispatch
[:change-password/confirm-new-password
(security/mask-data password)]))]
[:<>
[rn/scroll-view {:style style/form-container}
[header/view]
[password-with-hint
{:hint {:text (i18n/label :t/password-creation-hint)
:status (if long-enough? :success :default)
:shown? true}
:placeholder (i18n/label :t/change-password-new-password-placeholder)
:label (i18n/label :t/change-password-new-password-label)
:on-change-text on-change-password
:on-focus on-input-focus
:auto-focus true}]
[rn/view {:style style/space-between-inputs}]
[password-with-hint
{:hint {:text (if same-passwords?
(i18n/label :t/password-creation-match)
(i18n/label :t/password-creation-dont-match))
:status (if same-passwords? :success :error)
:shown? (and (not empty-password?)
show-validation?)}
:error? error?
:placeholder (i18n/label :t/change-password-repeat-password-placeholder)
:on-change-text on-change-repeat-password
:on-focus on-input-focus
:on-blur on-blur-repeat-password}]]
[rn/view {:style style/bottom-part}
(when same-passwords?
[rn/view {:style style/disclaimer-container}
[quo/disclaimer
{:blur? true
:on-change on-disclaimer-change
:checked? disclaimer-accepted?}
(i18n/label :t/password-creation-disclaimer)]])
(when (and focused? (not same-passwords?))
[help
{:validations validations}])
[rn/view {:style style/button-container}
[quo/button
{:disabled? (not meet-requirements?)
:customization-color customization-color
:on-press on-submit}
(i18n/label :t/password-creation-confirm)]]]]))

View File

@ -0,0 +1,63 @@
(ns status-im.contexts.profile.settings.screens.password.change-password.old-password-form
(:require
[clojure.string :as string]
[quo.core :as quo]
[react-native.core :as rn]
[status-im.constants :as constant]
[status-im.contexts.profile.settings.screens.password.change-password.events]
[status-im.contexts.profile.settings.screens.password.change-password.header :as header]
[status-im.contexts.profile.settings.screens.password.change-password.style :as style]
[utils.i18n :as i18n]
[utils.re-frame :as rf]
[utils.security.core :as security]
[utils.string :as utils.string]))
(defn view
[]
(let [error (rf/sub [:settings/change-password-error])
customization-color (rf/sub [:profile/customization-color])
[password set-password] (rn/use-state "")
on-change-password (fn [new-value]
(when error
(rf/dispatch [:change-password/reset-error]))
(set-password new-value))
meet-requirements? (and ((complement string/blank?) password)
(utils.string/at-least-n-chars? password
constant/min-password-length))
on-submit (fn []
(when meet-requirements?
(rf/dispatch
[:change-password/verify-old-password
(security/mask-data password)])))]
[:<>
[rn/scroll-view {:content-container-style style/form-container}
[header/view]
[quo/input
{:placeholder (i18n/label :t/change-password-old-password-placeholder)
:label (i18n/label :t/change-password-old-password-label)
:on-change-text on-change-password
:auto-focus true
:type :password
:blur? true}]
[rn/view
{:style style/error-container}
(when error
[quo/info-message
{:type :error
:size :default
:icon :i/info}
(i18n/label :t/oops-wrong-password)])]
[quo/information-box
{:type :error
:style style/warning-container
:icon :i/info}
(i18n/label :t/change-password-confirm-warning)]]
[rn/view
{:style (assoc style/bottom-part
:margin-horizontal 20
:margin-top 12)}
[quo/button
{:disabled? (not meet-requirements?)
:customization-color customization-color
:on-press on-submit}
(i18n/label :t/continue)]]]))

View File

@ -0,0 +1,60 @@
(ns status-im.contexts.profile.settings.screens.password.change-password.style
(:require
[quo.foundations.colors :as colors]
[react-native.safe-area :as safe-area]))
(def form-container
{:margin-horizontal 20})
(def heading
{:margin-bottom 20
:margin-top 12})
(def heading-subtitle {:color colors/white})
(def heading-title (assoc heading-subtitle :margin-bottom 8))
(def info-message
{:margin-top 8})
(def space-between-inputs {:height 16})
(def error-container
{:margin-top 8
:flex-direction :row
:align-items :center})
(def warning-container
{:margin-top 12})
(defn loading-container
[{:keys [top bottom]}]
{:flex 1
:justify-content :space-between
:padding-top top
:padding-bottom bottom})
(def loading-content
{:flex 1
:padding-horizontal 20})
(def logout-container
{:margin-vertical 12
:margin-horizontal 20})
(def password-tips
{:flex-direction :row
:margin-horizontal 20
:justify-content :space-between})
(def bottom-part
{:margin-bottom (- (safe-area/get-bottom) 12)
:justify-content :flex-end})
(def disclaimer-container
{:margin-horizontal 20
:margin-vertical 4})
(def button-container
{:margin-top 12
:margin-horizontal 20})

View File

@ -0,0 +1,40 @@
(ns status-im.contexts.profile.settings.screens.password.change-password.view
(:require
[quo.core :as quo]
[react-native.core :as rn]
[react-native.platform :as platform]
[react-native.safe-area :as safe-area]
[status-im.contexts.profile.settings.screens.password.change-password.events]
[status-im.contexts.profile.settings.screens.password.change-password.new-password-form :as
new-password-form]
[status-im.contexts.profile.settings.screens.password.change-password.old-password-form :as
old-password-form]
[utils.re-frame :as rf]))
(defn- navigate-back
[]
(rf/dispatch [:navigate-back]))
(defn view
[]
(let [{:keys [top]} (safe-area/get-insets)
alert-banners-top-margin (rf/sub [:alert-banners/top-margin])
current-step (rf/sub [:settings/change-password-current-step])]
(rn/use-unmount #(rf/dispatch [:change-password/reset]))
[quo/overlay {:type :shell}
[rn/pressable
{:key :change-password
:on-press rn/dismiss-keyboard!
:accessible false
:style {:flex 1}}
[quo/page-nav
{:margin-top top
:background :blur
:icon-name :i/arrow-left
:on-press navigate-back}]
[rn/keyboard-avoiding-view
{:style {:flex 1}
:keyboardVerticalOffset (if platform/ios? alert-banners-top-margin 0)}
(condp = current-step
:old-password [old-password-form/view]
:new-password [new-password-form/view])]]]))

View File

@ -2,8 +2,6 @@
(:require [quo.core :as quo]
[quo.theme :as quo.theme]
[status-im.common.biometric.utils :as biometric]
[status-im.common.not-implemented :as not-implemented]
[status-im.config :as config]
[status-im.constants :as constants]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
@ -47,13 +45,12 @@
(defn- get-change-password-item
[]
(when config/show-not-implemented-features?
{:title (i18n/label :t/change-password)
:on-press not-implemented/alert
:blur? true
:image :icon
:image-props :i/password
:action :arrow}))
{:title (i18n/label :t/change-password)
:on-press #(rf/dispatch [:open-modal :screen/change-password])
:blur? true
:image :icon
:image-props :i/password
:action :arrow})
(defn- navigate-back
[]

View File

@ -40,6 +40,7 @@
:registry {}
:visibility-status-updates {}
:stickers/packs-pending #{}
:settings/change-password {}
:keycard {:nfc-enabled? false
:pin {:original []
:confirmation []

View File

@ -52,6 +52,9 @@
[status-im.contexts.profile.settings.screens.messages.blocked-users.view :as
settings.blocked-users]
[status-im.contexts.profile.settings.screens.messages.view :as settings.messages]
[status-im.contexts.profile.settings.screens.password.change-password.loading :as
change-password-loading]
[status-im.contexts.profile.settings.screens.password.change-password.view :as change-password]
[status-im.contexts.profile.settings.screens.password.view :as settings-password]
[status-im.contexts.profile.settings.view :as settings]
[status-im.contexts.settings.wallet.saved-addresses.view :as saved-addresses-settings]
@ -485,7 +488,7 @@
;; Settings
{:name :settings-password
{:name :screen/settings-password
:options options/transparent-modal-screen-options
:component settings-password/view}
@ -503,7 +506,20 @@
{:name :screen/settings-blocked-users
:options options/transparent-modal-screen-options
:component settings.blocked-users/view}]
:component settings.blocked-users/view}
{:name :screen/change-password
:options (assoc options/transparent-modal-screen-options :theme :dark)
:component change-password/view}
{:name :screen/change-password-loading
:options (assoc
options/transparent-modal-screen-options
:theme :dark
:popGesture false
:hardwareBackButton {:dismissModalOnPress false
:popStackOnPress false})
:component change-password-loading/view}]
[{:name :shell
:options {:theme :dark}}]

View File

@ -13,6 +13,7 @@
status-im.subs.onboarding
status-im.subs.pairing
status-im.subs.profile
status-im.subs.settings
status-im.subs.shell
status-im.subs.wallet.activities
status-im.subs.wallet.collectibles
@ -167,6 +168,9 @@
;;biometrics
(reg-root-key-sub :biometrics :biometrics)
;;settings
(reg-root-key-sub :settings/change-password :settings/change-password)
;;debug
(when js/goog.DEBUG
(reg-root-key-sub :dev/previewed-component :dev/previewed-component))

View File

@ -0,0 +1,17 @@
(ns status-im.subs.settings
(:require [re-frame.core :as rf]))
(rf/reg-sub :settings/change-password-current-step
:<- [:settings/change-password]
(fn [change-password]
(or (get change-password :current-step) :old-password)))
(rf/reg-sub :settings/change-password-loading
:<- [:settings/change-password]
(fn [change-password]
(get change-password :loading?)))
(rf/reg-sub :settings/change-password-error
:<- [:settings/change-password]
(fn [change-password]
(get change-password :verify-error)))

View File

@ -146,6 +146,19 @@
"change-logging-enabled": "Are you sure you want to {{enable}} logging?",
"change-passcode": "Change Passcode",
"change-password": "Change password",
"change-password-confirm-description": "Your data must now be re-encrypted with your new password. Please do not quit the app or turn off your device.",
"change-password-confirm-warning": "Re-encrypting data after changing password may take up to 3 min. Closing the app or locking phone while re-encryption is in progress will lead to data corruption and loss of your Status profile.",
"change-password-description": "Change password used to log in to Status and sign transactions",
"change-password-loading-header": "Keep app open on screen!",
"change-password-loading-description": "Data is now being re-encrypted with your new password, this may take up to 3min.",
"change-password-done-header": "Re-encryption complete!",
"change-password-done-description": "Please log out Status and log in using your new password",
"change-password-loading-warning": "Do not quit the app or turn off your device while in-progress.\n \nClosing the app or locking your phone while re-encryption is taking place will lead to data corruption and the loss of your Status profile.",
"change-password-new-password-label": "New password",
"change-password-new-password-placeholder": "Enter new password",
"change-password-old-password-label": "Current password",
"change-password-old-password-placeholder": "Enter current password",
"change-password-repeat-password-placeholder": "Repeat new password",
"change-pin": "Change 6-digit passcode",
"change-puk": "Change 12-digit PUK",
"change-pairing": "Change pairing code",
@ -1211,7 +1224,9 @@
"puk-mismatch": "Wrong PUK code",
"quiet-days": "{{quiet-days}} days",
"quiet-hours": "{{quiet-hours}} hours",
"re-encrypt": "Re-encrypt",
"re-encrypt-key": "Re-encrypt your keys",
"re-encrypt-data": "Re-encrypt your data",
"receive": "Receive",
"receive-transaction": "Receive transaction",
"recent": "Recent",