Skip to content

Commit

Permalink
Change password inside new settings (#19474)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
clauxx committed May 7, 2024
1 parent 245b3fc commit 161ba27
Show file tree
Hide file tree
Showing 26 changed files with 673 additions and 41 deletions.
21 changes: 15 additions & 6 deletions src/native_module/core.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -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]))
Expand Down Expand Up @@ -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]
Expand Down
23 changes: 23 additions & 0 deletions src/native_module/utils.cljs
Original file line number Diff line number Diff line change
@@ -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)))))
6 changes: 6 additions & 0 deletions src/quo/components/password/password_tips/style.cljs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
(ns quo.components.password.password-tips.style)

(def password-tips
{:flex-direction :row
:margin-horizontal 20
:justify-content :space-between})
30 changes: 30 additions & 0 deletions src/quo/components/password/password_tips/view.cljs
Original file line number Diff line number Diff line change
@@ -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))
17 changes: 8 additions & 9 deletions src/quo/components/text_combinations/page_top/view.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -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])]))
Expand Down
2 changes: 2 additions & 0 deletions src/quo/core.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion src/status_im/common/keychain/events.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
9 changes: 9 additions & 0 deletions src/status_im/constants.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
18 changes: 6 additions & 12 deletions src/status_im/contexts/onboarding/create_password/view.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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?}
Expand All @@ -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
[]
Expand Down
5 changes: 4 additions & 1 deletion src/status_im/contexts/preview/quo/main.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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
Expand Down
29 changes: 29 additions & 0 deletions src/status_im/contexts/preview/quo/password/password_tips.cljs
Original file line number Diff line number Diff line change
@@ -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]])))
2 changes: 1 addition & 1 deletion src/status_im/contexts/profile/settings/list_items.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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)))))
Original file line number Diff line number Diff line change
@@ -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)}))
Original file line number Diff line number Diff line change
@@ -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)]])
Loading

0 comments on commit 161ba27

Please sign in to comment.