From b4d27d287a5383f515a1835e647f2c5670adc319 Mon Sep 17 00:00:00 2001 From: Mohsen Date: Mon, 8 Jan 2024 18:27:07 +0300 Subject: [PATCH] feat: implement edit profile and change name (#18270) --- src/quo/components/inputs/input/view.cljs | 4 +- src/quo/components/overlay/view.cljs | 6 +- .../settings/category/settings/view.cljs | 4 +- .../settings/settings_item/style.cljs | 7 +- .../settings/settings_item/view.cljs | 2 +- src/status_im/common/validation/profile.cljs | 41 +++++++++++ .../common/validation/profile_test.cljs | 52 ++++++++++++++ .../onboarding/create_profile/view.cljs | 48 +++---------- .../contexts/profile/edit/header/view.cljs | 33 +++++++++ .../contexts/profile/edit/list_items.cljs | 62 ++++++++++++++++ .../contexts/profile/edit/name/events.cljs | 18 +++++ .../profile/edit/name/events_test.cljs | 15 ++++ .../contexts/profile/edit/name/style.cljs | 17 +++++ .../contexts/profile/edit/name/view.cljs | 72 +++++++++++++++++++ .../contexts/profile/edit/style.cljs | 25 +++++++ src/status_im/contexts/profile/edit/view.cljs | 48 +++++++++++++ src/status_im/contexts/profile/events.cljs | 1 + .../contexts/profile/settings/list_items.cljs | 2 +- .../integration_test/profile_test.cljs | 22 ++++++ src/status_im/navigation/screens.cljs | 10 +++ translations/en.json | 8 +++ 21 files changed, 448 insertions(+), 49 deletions(-) create mode 100644 src/status_im/common/validation/profile.cljs create mode 100644 src/status_im/common/validation/profile_test.cljs create mode 100644 src/status_im/contexts/profile/edit/header/view.cljs create mode 100644 src/status_im/contexts/profile/edit/list_items.cljs create mode 100644 src/status_im/contexts/profile/edit/name/events.cljs create mode 100644 src/status_im/contexts/profile/edit/name/events_test.cljs create mode 100644 src/status_im/contexts/profile/edit/name/style.cljs create mode 100644 src/status_im/contexts/profile/edit/name/view.cljs create mode 100644 src/status_im/contexts/profile/edit/style.cljs create mode 100644 src/status_im/contexts/profile/edit/view.cljs create mode 100644 src/status_im/integration_test/profile_test.cljs diff --git a/src/quo/components/inputs/input/view.cljs b/src/quo/components/inputs/input/view.cljs index 8e7fd6bd9f..5d792bd079 100644 --- a/src/quo/components/inputs/input/view.cljs +++ b/src/quo/components/inputs/input/view.cljs @@ -61,7 +61,7 @@ :container-style]) (defn- base-input - [{:keys [on-change-text on-char-limit-reach weight]}] + [{:keys [on-change-text on-char-limit-reach weight default-value]}] (let [status (reagent/atom :default) internal-on-focus #(reset! status :focus) internal-on-blur #(reset! status :default) @@ -72,7 +72,7 @@ (if (> height min-height) (reset! multiple-lines? true) (reset! multiple-lines? false))) - char-count (reagent/atom 0) + char-count (reagent/atom (count default-value)) update-char-limit! (fn [new-text char-limit] (when on-change-text (on-change-text new-text)) (let [amount-chars (count new-text)] diff --git a/src/quo/components/overlay/view.cljs b/src/quo/components/overlay/view.cljs index 7b35f87a93..8855ec0e36 100644 --- a/src/quo/components/overlay/view.cljs +++ b/src/quo/components/overlay/view.cljs @@ -5,7 +5,7 @@ [react-native.core :as rn])) (defn view - [{:keys [type]} & children] + [{:keys [type container-style]} & children] [rn/view {:style (style/overlay-background type)} (if (= type :shell) [blur/view @@ -14,7 +14,7 @@ :blur-type :transparent :overlay-color :transparent :style style/container} - [rn/view {:style style/blur-container} + [rn/view {:style (merge style/blur-container container-style)} children]] - [rn/view {:style style/container} + [rn/view {:style (merge style/container container-style)} children])]) diff --git a/src/quo/components/settings/category/settings/view.cljs b/src/quo/components/settings/category/settings/view.cljs index 980c4ed6b3..7b9d64a288 100644 --- a/src/quo/components/settings/category/settings/view.cljs +++ b/src/quo/components/settings/category/settings/view.cljs @@ -7,8 +7,8 @@ [react-native.core :as rn])) (defn- category-internal - [{:keys [label data] :as props}] - [rn/view {:style (style/container label)} + [{:keys [label data container-style] :as props}] + [rn/view {:style (merge (style/container label) container-style)} (when label [text/text {:weight :medium diff --git a/src/quo/components/settings/settings_item/style.cljs b/src/quo/components/settings/settings_item/style.cljs index 5f3ffac613..e4ba8c679b 100644 --- a/src/quo/components/settings/settings_item/style.cljs +++ b/src/quo/components/settings/settings_item/style.cljs @@ -19,10 +19,12 @@ (defn sub-container [align-action] {:flex-direction :row + :padding-right 0.5 :align-items (or align-action :center)}) -(def left-container - {:margin-horizontal 12 +(defn left-container + [image?] + {:margin-horizontal (if image? 12 0) :flex 1 :height "100%" :justify-content :flex-start}) @@ -57,6 +59,7 @@ {:width 15 :height 15 :border-radius 12 + :margin-right 4 :background-color background-color}) (def status-tag-container diff --git a/src/quo/components/settings/settings_item/view.cljs b/src/quo/components/settings/settings_item/view.cljs index 5029909fbd..a660c16e2f 100644 --- a/src/quo/components/settings/settings_item/view.cljs +++ b/src/quo/components/settings/settings_item/view.cljs @@ -109,7 +109,7 @@ :accessibility-label accessibility-label} [rn/view {:style (style/left-sub-container props)} [image-component props] - [rn/view {:style style/left-container} + [rn/view {:style (style/left-container (:image props))} [text/text {:weight :medium :style {:color (when blur? colors/white)}} title] diff --git a/src/status_im/common/validation/profile.cljs b/src/status_im/common/validation/profile.cljs new file mode 100644 index 0000000000..13f03cf41c --- /dev/null +++ b/src/status_im/common/validation/profile.cljs @@ -0,0 +1,41 @@ +(ns status-im.common.validation.profile + (:require [clojure.string :as string] + [utils.i18n :as i18n])) + +;; NOTE - validation should match with Desktop +;; https://github.com/status-im/status-desktop/blob/2ba96803168461088346bf5030df750cb226df4c/ui/imports/utils/Constants.qml#L468 +(def min-length 5) +(def max-length 24) + +(def emoji-regex + #"(\u00a9|\u00ae|[\u2000-\u3300]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff])") + +(def status-regex #"^[a-zA-Z0-9\-_ ]+$") + +(def common-names ["Ethereum" "Bitcoin"]) + +(defn has-emojis? [s] (boolean (re-find emoji-regex s))) + +(defn has-common-names? [s] (pos? (count (filter #(string/includes? s %) common-names)))) + +(defn has-special-characters? [s] (not (re-find status-regex s))) + +(defn name-too-short? [s] (< (count (string/trim (str s))) min-length)) + +(defn name-too-long? [s] (> (count (string/trim (str s))) max-length)) + +(defn validation-name + [s] + (cond + (or (= s nil) (= s "")) nil + (string/ends-with? s "-eth") (i18n/label :t/ending-not-allowed {:ending "-eth"}) + (string/ends-with? s "_eth") (i18n/label :t/ending-not-allowed {:ending "_eth"}) + (string/ends-with? s ".eth") (i18n/label :t/ending-not-allowed {:ending ".eth"}) + (string/starts-with? s " ") (i18n/label :t/start-with-space) + (string/ends-with? s " ") (i18n/label :t/ends-with-space) + (has-common-names? s) (i18n/label :t/are-not-allowed {:check (i18n/label :t/common-names)}) + (has-emojis? s) (i18n/label :t/are-not-allowed {:check (i18n/label :t/emojis)}) + (has-special-characters? s) (i18n/label :t/are-not-allowed + {:check (i18n/label :t/special-characters)}) + (name-too-short? s) (i18n/label :t/minimum-characters {:min-chars min-length}) + (name-too-long? s) (i18n/label :t/profile-name-is-too-long))) diff --git a/src/status_im/common/validation/profile_test.cljs b/src/status_im/common/validation/profile_test.cljs new file mode 100644 index 0000000000..b12fe7a231 --- /dev/null +++ b/src/status_im/common/validation/profile_test.cljs @@ -0,0 +1,52 @@ +(ns status-im.common.validation.profile-test + (:require + [cljs.test :refer-macros [deftest are]] + [status-im.common.validation.profile :as profile-validator] + [utils.i18n :as i18n])) + +(deftest has-emojis-test + (are [arg expected] + (expected (profile-validator/has-emojis? arg)) + "Hello 😊" true? + "Hello" false?)) + +(deftest has-common-names-test + (are [arg expected] + (expected (profile-validator/has-common-names? arg)) + "Ethereum" true? + "Hello" false?)) + +(deftest has-special-characters-test + (are [arg expected] + (expected (profile-validator/has-special-characters? arg)) + "@name" true? + "name" false?)) + +(deftest name-too-short-test + (are [arg expected] + (expected (profile-validator/name-too-short? arg)) + "abc" true? + "abcdef" false?)) + +(deftest name-too-long-test + (are [arg expected] + (expected (profile-validator/name-too-long? arg)) + (apply str (repeat 25 "a")) true? + "abcdef" false?)) + +(deftest validation-name-test + (are [arg expected] + (= (profile-validator/validation-name arg) expected) + nil nil + "" nil + "@name" (i18n/label :t/are-not-allowed + {:check (i18n/label :t/special-characters)}) + "name-eth" (i18n/label :t/ending-not-allowed {:ending "-eth"}) + "name_eth" (i18n/label :t/ending-not-allowed {:ending "_eth"}) + "name.eth" (i18n/label :t/ending-not-allowed {:ending ".eth"}) + " name" (i18n/label :t/start-with-space) + "name " (i18n/label :t/ends-with-space) + "Ethereum" (i18n/label :t/are-not-allowed {:check (i18n/label :t/common-names)}) + "Hello 😊" (i18n/label :t/are-not-allowed {:check (i18n/label :t/emojis)}) + "abc" (i18n/label :t/minimum-characters {:min-chars 5}) + (apply str (repeat 25 "a")) (i18n/label :t/profile-name-is-too-long))) diff --git a/src/status_im/contexts/onboarding/create_profile/view.cljs b/src/status_im/contexts/onboarding/create_profile/view.cljs index e18a6cb15c..97ffa57cb0 100644 --- a/src/status_im/contexts/onboarding/create_profile/view.cljs +++ b/src/status_im/contexts/onboarding/create_profile/view.cljs @@ -10,6 +10,7 @@ [react-native.platform :as platform] [react-native.safe-area :as safe-area] [reagent.core :as reagent] + [status-im.common.validation.profile :as profile-validator] [status-im.constants :as c] [status-im.contexts.onboarding.create-profile.style :as style] [status-im.contexts.onboarding.select-photo.method-menu.view :as method-menu] @@ -17,40 +18,9 @@ [utils.re-frame :as rf] [utils.responsiveness :as responsiveness])) -;; NOTE - validation should match with Desktop -;; https://github.com/status-im/status-desktop/blob/2ba96803168461088346bf5030df750cb226df4c/ui/imports/utils/Constants.qml#L468 -;; -(def emoji-regex - (new - js/RegExp - #"(\u00a9|\u00ae|[\u2000-\u3300]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff])" - "i")) -(defn has-emojis [s] (re-find emoji-regex s)) -(def common-names ["Ethereum" "Bitcoin"]) -(defn has-common-names [s] (pos? (count (filter #(string/includes? s %) common-names)))) -(def status-regex (new js/RegExp #"^[a-zA-Z0-9\-_ ]+$")) -(defn has-special-characters [s] (not (re-find status-regex s))) -(def min-length 5) -(defn length-not-valid [s] (< (count (string/trim (str s))) min-length)) (def scroll-view-height (reagent/atom 0)) (def content-container-height (reagent/atom 0)) -(defn validation-message - [s] - (cond - (or (= s nil) (= s "")) nil - (has-special-characters s) (i18n/label :t/are-not-allowed - {:check (i18n/label :t/special-characters)}) - (string/ends-with? s "-eth") (i18n/label :t/ending-not-allowed {:ending "-eth"}) - (string/ends-with? s "_eth") (i18n/label :t/ending-not-allowed {:ending "_eth"}) - (string/ends-with? s ".eth") (i18n/label :t/ending-not-allowed {:ending ".eth"}) - (string/starts-with? s " ") (i18n/label :t/start-with-space) - (string/ends-with? s " ") (i18n/label :t/ends-with-space) - (has-common-names s) (i18n/label :t/are-not-allowed {:check (i18n/label :t/common-names)}) - (has-emojis s) (i18n/label :t/are-not-allowed {:check (i18n/label :t/emojis)}) - :else nil)) - - (defn show-button-background [keyboard-height keyboard-shown content-scroll-y] (let [button-container-height 64 @@ -66,7 +36,6 @@ :else false)))) - (defn button-container [show-keyboard? keyboard-shown show-background? keyboard-height children] (let [height (reagent/atom 0)] @@ -109,23 +78,26 @@ #(reset! show-keyboard? false)) {:keys [image-path display-name color]} onboarding-profile-data full-name (reagent/atom display-name) - validation-msg (reagent/atom (validation-message - @full-name)) + validation-msg (reagent/atom + (profile-validator/validation-name + @full-name)) on-change-text (fn [s] - (reset! validation-msg (validation-message - s)) + (reset! validation-msg + (profile-validator/validation-name + s)) (reset! full-name (string/trim s))) custom-color (reagent/atom (or color c/profile-default-color)) profile-pic (reagent/atom image-path) on-change-profile-pic #(reset! profile-pic %) on-change #(reset! custom-color %)] - (let [name-too-short? (length-not-valid @full-name) + (let [name-too-short? (profile-validator/name-too-short? @full-name) valid-name? (and (not @validation-msg) (not name-too-short?)) info-message (if @validation-msg @validation-msg (i18n/label :t/minimum-characters - {:min-chars min-length})) + {:min-chars + profile-validator/min-length})) info-type (cond @validation-msg :error name-too-short? :default :else :success) diff --git a/src/status_im/contexts/profile/edit/header/view.cljs b/src/status_im/contexts/profile/edit/header/view.cljs new file mode 100644 index 0000000000..0f7143eb16 --- /dev/null +++ b/src/status_im/contexts/profile/edit/header/view.cljs @@ -0,0 +1,33 @@ +(ns status-im.contexts.profile.edit.header.view + (:require [quo.core :as quo] + [react-native.core :as rn] + [status-im.common.not-implemented :as not-implemented] + [status-im.contexts.profile.edit.style :as style] + [status-im.contexts.profile.utils :as profile.utils] + [utils.i18n :as i18n] + [utils.re-frame :as rf])) + +(defn view + [] + (let [profile (rf/sub [:profile/profile-with-image]) + full-name (profile.utils/displayed-name profile) + profile-picture (profile.utils/photo profile)] + [rn/view + {:key :edit-profile + :style style/screen-container} + [quo/text-combinations {:title (i18n/label :t/edit-profile)}] + [rn/view style/avatar-wrapper + [quo/user-avatar + {:full-name full-name + :profile-picture profile-picture + :status-indicator? false + :ring? true + :size :big}] + [quo/button + {:on-press not-implemented/alert + :container-style style/camera-button + :icon-only? true + :type :grey + :background :photo + :size 32} + :i/camera]]])) diff --git a/src/status_im/contexts/profile/edit/list_items.cljs b/src/status_im/contexts/profile/edit/list_items.cljs new file mode 100644 index 0000000000..da20a58e4f --- /dev/null +++ b/src/status_im/contexts/profile/edit/list_items.cljs @@ -0,0 +1,62 @@ +(ns status-im.contexts.profile.edit.list-items + (:require [quo.foundations.colors :as colors] + [status-im.common.not-implemented :as not-implemented] + [status-im.contexts.profile.edit.style :as style] + [status-im.contexts.profile.utils :as profile.utils] + [utils.i18n :as i18n] + [utils.re-frame :as rf])) + +(defn items + [theme] + (let [profile (rf/sub [:profile/profile-with-image]) + customization-color (rf/sub [:profile/customization-color]) + full-name (profile.utils/displayed-name profile)] + [{:label (i18n/label :t/profile) + :items [{:title (i18n/label :t/name) + :on-press #(rf/dispatch [:open-modal :edit-name]) + :blur? true + :label :text + :label-props full-name + :action :arrow + :container-style style/item-container} + {:title (i18n/label :t/bio) + :on-press not-implemented/alert + :blur? true + :action :arrow + :container-style style/item-container} + {:title (i18n/label :t/accent-colour) + :on-press not-implemented/alert + :label :color + :label-props (colors/resolve-color customization-color theme) + :blur? true + :action :arrow + :container-style style/item-container}]} + + {:label (i18n/label :t/showcase) + :items [{:title (i18n/label :t/communities) + :on-press not-implemented/alert + :blur? true + :action :arrow + :container-style style/item-container} + {:title (i18n/label :t/accounts) + :on-press not-implemented/alert + :blur? true + :action :arrow + :container-style style/item-container} + {:title (i18n/label :t/collectibles) + :on-press not-implemented/alert + :blur? true + :action :arrow + :container-style style/item-container} + {:title (i18n/label :t/assets) + :on-press not-implemented/alert + :blur? true + :action :arrow + :container-style style/item-container}]} + + {:label (i18n/label :t/on-the-web) + :items [{:title (i18n/label :t/links) + :on-press not-implemented/alert + :blur? true + :action :arrow + :container-style style/item-container}]}])) diff --git a/src/status_im/contexts/profile/edit/name/events.cljs b/src/status_im/contexts/profile/edit/name/events.cljs new file mode 100644 index 0000000000..3e1feda2f2 --- /dev/null +++ b/src/status_im/contexts/profile/edit/name/events.cljs @@ -0,0 +1,18 @@ +(ns status-im.contexts.profile.edit.name.events + (:require [utils.i18n :as i18n] + [utils.re-frame :as rf])) + +(defn edit-profile-name + [{:keys [db]} [name]] + {:db (assoc-in db [:profile/profile :display-name] name) + :fx [[:json-rpc/call + [{:method "wakuext_setDisplayName" + :params [name] + :on-success (fn [] + (rf/dispatch [:navigate-back]) + (rf/dispatch [:toasts/upsert + {:type :positive + :theme :dark + :text (i18n/label :t/name-updated)}]))}]]]}) + +(rf/reg-event-fx :profile/edit-name edit-profile-name) diff --git a/src/status_im/contexts/profile/edit/name/events_test.cljs b/src/status_im/contexts/profile/edit/name/events_test.cljs new file mode 100644 index 0000000000..efba9bd8f9 --- /dev/null +++ b/src/status_im/contexts/profile/edit/name/events_test.cljs @@ -0,0 +1,15 @@ +(ns status-im.contexts.profile.edit.name.events-test + (:require [cljs.test :refer [deftest is]] + matcher-combinators.test + [status-im.contexts.profile.edit.name.events :as sut])) + +(deftest edit-name-test + (let [new-name "John Doe" + cofx {:db {:profile/profile {:display-name "Old name"}}} + expected {:db {:profile/profile {:display-name new-name}} + :fx [[:json-rpc/call + [{:method "wakuext_setDisplayName" + :params [name] + :on-success fn?}]]]}] + (is (match? expected + (sut/edit-profile-name cofx [new-name]))))) diff --git a/src/status_im/contexts/profile/edit/name/style.cljs b/src/status_im/contexts/profile/edit/name/style.cljs new file mode 100644 index 0000000000..3a94cbe9a4 --- /dev/null +++ b/src/status_im/contexts/profile/edit/name/style.cljs @@ -0,0 +1,17 @@ +(ns status-im.contexts.profile.edit.name.style) + +(defn page-wrapper + [insets] + {:padding-top (:top insets) + :padding-bottom (:bottom insets) + :padding-horizontal 1 + :flex 1}) + +(def screen-container + {:flex 1 + :padding-top 14 + :padding-horizontal 20 + :justify-content :space-between}) + +(def button-wrapper + {:margin-vertical 12}) diff --git a/src/status_im/contexts/profile/edit/name/view.cljs b/src/status_im/contexts/profile/edit/name/view.cljs new file mode 100644 index 0000000000..6d9ca85975 --- /dev/null +++ b/src/status_im/contexts/profile/edit/name/view.cljs @@ -0,0 +1,72 @@ +(ns status-im.contexts.profile.edit.name.view + (:require [clojure.string :as string] + [quo.core :as quo] + [react-native.core :as rn] + [react-native.safe-area :as safe-area] + [reagent.core :as reagent] + [status-im.common.validation.profile :as profile-validator] + [status-im.constants :as constants] + [status-im.contexts.profile.edit.name.style :as style] + [status-im.contexts.profile.utils :as profile.utils] + [utils.debounce :as debounce] + [utils.i18n :as i18n] + [utils.re-frame :as rf])) + +(defn view + [] + (let [insets (safe-area/get-insets) + profile (rf/sub [:profile/profile-with-image]) + customization-color (rf/sub [:profile/customization-color]) + display-name (profile.utils/displayed-name profile) + full-name (reagent/atom display-name) + error-msg (reagent/atom nil) + typing? (reagent/atom false) + validate-name (debounce/debounce (fn [name] + (reset! error-msg + (profile-validator/validation-name name)) + (reset! typing? false)) + 300) + on-change-text (fn [s] + (reset! typing? true) + (reset! full-name s) + (validate-name s))] + (fn [] + [quo/overlay + {:type :shell + :container-style (style/page-wrapper insets)} + [quo/page-nav + {:key :header + :background :blur + :icon-name :i/arrow-left + :on-press #(rf/dispatch [:navigate-back])}] + [rn/keyboard-avoiding-view + {:key :content + :style style/screen-container} + [rn/view {:style {:gap 22}} + [quo/text-combinations {:title (i18n/label :t/name)}] + [quo/input + {:theme :dark + :blur? true + :error? (not (string/blank? @error-msg)) + :container-style {:margin-bottom -11} + :default-value @full-name + :auto-focus true + :char-limit constants/profile-name-max-length + :label (i18n/label :t/profile-name) + :on-change-text on-change-text}] + (when-not (string/blank? @error-msg) + [quo/info-message + {:type :error + :size :default + :icon :i/info} + @error-msg])] + [rn/view {:style style/button-wrapper} + [quo/button + {:type :primary + :customization-color customization-color + :on-press (fn [] + (rf/dispatch [:profile/edit-name @full-name])) + :disabled? (boolean (or @typing? + (string/blank? @full-name) + (not (string/blank? @error-msg))))} + (i18n/label :t/save-name)]]]]))) diff --git a/src/status_im/contexts/profile/edit/style.cljs b/src/status_im/contexts/profile/edit/style.cljs new file mode 100644 index 0000000000..e96cc249dc --- /dev/null +++ b/src/status_im/contexts/profile/edit/style.cljs @@ -0,0 +1,25 @@ +(ns status-im.contexts.profile.edit.style) + +(defn page-wrapper + [inset] + {:padding-top inset + :padding-horizontal 1}) + +(def screen-container + {:padding-top 14 + :padding-horizontal 20}) + +(def avatar-wrapper + {:width 88 + :margin-top 22 + :margin-bottom 12}) + +(def camera-button + {:position :absolute + :border-radius 16 + :overflow :hidden + :right 0 + :bottom 0}) + +(def item-container + {:padding-top 14}) diff --git a/src/status_im/contexts/profile/edit/view.cljs b/src/status_im/contexts/profile/edit/view.cljs new file mode 100644 index 0000000000..d66fe27d44 --- /dev/null +++ b/src/status_im/contexts/profile/edit/view.cljs @@ -0,0 +1,48 @@ +(ns status-im.contexts.profile.edit.view + (:require [quo.core :as quo] + [quo.theme :as quo.theme] + [react-native.core :as rn] + [react-native.safe-area :as safe-area] + [status-im.common.not-implemented :as not-implemented] + [status-im.contexts.profile.edit.header.view :as header] + [status-im.contexts.profile.edit.list-items :as edit.items] + [status-im.contexts.profile.edit.style :as style] + [utils.re-frame :as rf])) + +(defn- item-view + [data] + [quo/category + {:container-style {:padding-bottom 9.5} + :list-type :settings + :blur? true + :label (:label data) + :data (:items data)}]) + +(defn- get-item-layout + [_ index] + #js {:length 100 :offset (* 100 index) :index index}) + +(defn internal-view + [theme] + (let [insets (safe-area/get-insets)] + [quo/overlay + {:type :shell + :container-style (style/page-wrapper (:top insets))} + [quo/page-nav + {:key :header + :background :blur + :icon-name :i/arrow-left + :on-press #(rf/dispatch [:navigate-back]) + :right-side [{:icon-name :i/reveal :on-press not-implemented/alert}]}] + [rn/flat-list + {:key :list + :header [header/view] + :data (edit.items/items theme) + :key-fn :label + :get-item-layout get-item-layout + :initial-num-to-render 3 + :max-to-render-per-batch 3 + :shows-vertical-scroll-indicator false + :render-fn item-view}]])) + +(def view (quo.theme/with-theme internal-view)) diff --git a/src/status_im/contexts/profile/events.cljs b/src/status_im/contexts/profile/events.cljs index c1eef3b577..4826b27b47 100644 --- a/src/status_im/contexts/profile/events.cljs +++ b/src/status_im/contexts/profile/events.cljs @@ -2,6 +2,7 @@ (:require [native-module.core :as native-module] [re-frame.core :as re-frame] + [status-im.contexts.profile.edit.name.events] [status-im.contexts.profile.login.events :as profile.login] [status-im.contexts.profile.rpc :as profile.rpc] [status-im.navigation.events :as navigation] diff --git a/src/status_im/contexts/profile/settings/list_items.cljs b/src/status_im/contexts/profile/settings/list_items.cljs index 3e86178328..4f6301db16 100644 --- a/src/status_im/contexts/profile/settings/list_items.cljs +++ b/src/status_im/contexts/profile/settings/list_items.cljs @@ -5,7 +5,7 @@ (def items [[{:title (i18n/label :t/edit-profile) - :on-press not-implemented/alert + :on-press #(rf/dispatch [:open-modal :edit-profile]) :image-props :i/edit :image :icon :blur? true diff --git a/src/status_im/integration_test/profile_test.cljs b/src/status_im/integration_test/profile_test.cljs new file mode 100644 index 0000000000..645c12ab8b --- /dev/null +++ b/src/status_im/integration_test/profile_test.cljs @@ -0,0 +1,22 @@ +(ns status-im.integration-test.profile-test + (:require + [cljs.test :refer [deftest is]] + [day8.re-frame.test :as rf-test] + [status-im.contexts.profile.utils :as profile.utils] + [test-helpers.integration :as h] + [utils.re-frame :as rf])) + +(deftest edit-profile-name-test + (h/log-headline :edit-profile-name-test) + (let [new-name "John Doe"] + (rf-test/run-test-async + (h/with-app-initialized + (h/with-account + (rf/dispatch [:profile/edit-name new-name]) + (rf-test/wait-for + [:navigate-back] + (rf-test/wait-for + [:toasts/upsert] + (let [profile (rf/sub [:profile/profile]) + display-name (profile.utils/displayed-name profile)] + (is (= new-name display-name)))))))))) diff --git a/src/status_im/navigation/screens.cljs b/src/status_im/navigation/screens.cljs index fd7b7cfd76..8ef658d759 100644 --- a/src/status_im/navigation/screens.cljs +++ b/src/status_im/navigation/screens.cljs @@ -34,6 +34,8 @@ [status-im.contexts.preview.quo.component-preview.view :as component-preview] [status-im.contexts.preview.quo.main :as quo.preview] [status-im.contexts.preview.status-im.main :as status-im-preview] + [status-im.contexts.profile.edit.name.view :as edit-name] + [status-im.contexts.profile.edit.view :as edit-profile] [status-im.contexts.profile.profiles.view :as profiles] [status-im.contexts.profile.settings.view :as settings] [status-im.contexts.shell.activity-center.view :as activity-center] @@ -165,6 +167,14 @@ :on-focus [:onboarding/overlay-dismiss] :component profiles/view} + {:name :edit-profile + :options options/transparent-screen-options + :component edit-profile/view} + + {:name :edit-name + :options options/transparent-screen-options + :component edit-name/view} + {:name :new-to-status :options {:theme :dark :layout options/onboarding-transparent-layout diff --git a/translations/en.json b/translations/en.json index cc5b19510e..c86c84a434 100644 --- a/translations/en.json +++ b/translations/en.json @@ -65,6 +65,7 @@ "back-up-your-seed-phrase": "Back up your seed phrase", "balance": "Balance", "begin-set-up": "Begin setup", + "bio": "Bio", "biometric-auth-android-sensor-desc": "Touch sensor", "biometric-auth-android-sensor-error-desc": "Failed", "biometric-auth-android-title": "Authentication Required", @@ -874,6 +875,7 @@ "left": "left", "lets-go": "Let's go!", "les-ulc": "LES/ULC", + "links": "Links", "linked-on": "Linked on {{date}}", "load-messages-before": "before {{date}}", "load-more-messages": "↓ Fetch more messages", @@ -972,6 +974,7 @@ "multiaccounts-recover-enter-phrase-title": "Enter your seed phrase", "multichain": "Multichain", "name": "Name", + "name-updated": "Name updated", "name-of-token": "The name of your token", "need-help": "Need help?", "new-to-status": "I’m new to Status", @@ -1090,6 +1093,7 @@ "ok-got-it": "Okay, got it", "okay": "Okay", "on": "On", + "on-the-web": "On the web", "only-mentions": "Only @mentions", "open": "Open", "open-home": "Open...", @@ -1176,6 +1180,8 @@ "product-information": "Product Information", "profile": "Profile", "profile-details": "Profile details", + "profile-name": "Profile name", + "profile-name-is-too-long": "Profile name is too long", "public-chat": "Public chat", "public-chats": "Public chats", "public-group-status": "Public", @@ -1236,6 +1242,7 @@ "revoke-access": "Revoke access", "rpc-url": "RPC URL", "save": "Save", + "save-name": "Save name", "save-password": "Save password", "save-password-unavailable": "Set device passcode to save password", "save-password-unavailable-android": "Save password is unavailable: your device may be rooted or lacks necessary security features.", @@ -1287,6 +1294,7 @@ "sharing-copy-to-clipboard": "Copy", "share-logs": "Share logs", "sharing-share": "Share", + "showcase": "Showcase", "show-less": "Show less", "show-more": "Show more", "show-qr": "Show QR code",