Add modal screen to update the profile

This commit is contained in:
Ulises M 2024-12-20 17:08:59 -06:00
parent e9a064ad71
commit 9de74191f6
No known key found for this signature in database
GPG Key ID: 5A15782EB758534E
3 changed files with 377 additions and 0 deletions

View File

@ -0,0 +1,51 @@
(ns status-im.contexts.profile.edit.modal.events
(:require [status-im.contexts.profile.edit.introduce-yourself.view :as introduce-yourself]
[utils.re-frame :as rf]))
(rf/reg-event-fx
:profile/edit-profile
(fn [_ [{:keys [display-name picture color on-success]}]]
{:fx (cond-> []
display-name
(conj [:dispatch
[:profile/edit-name
{:display-name display-name
:navigate-back? false
:show-toast? false}]])
color
(conj [:dispatch
[:profile/edit-accent-colour
{:color color
:navigate-back? false
:show-toast? false}]])
picture
(conj [:dispatch
[:profile/edit-picture
{:picture picture
:show-toast? false}]])
(nil? picture)
(conj [:dispatch [:profile/delete-picture {:show-toast? false}]])
:always
(conj [:dispatch-n [[:navigate-back] on-success]]))}))
(rf/reg-event-fx
:profile/ask-profile-update
(fn [_ [pending-event]]
{:fx [[:effects.async-storage/set {:update-profile-asked? true}]
[:dispatch
[:show-bottom-sheet
{:content (fn []
[introduce-yourself/sheet {:pending-event pending-event}])}]]]}))
(rf/reg-event-fx
:profile/check-profile-update-prompt
(fn [_ [pending-event]]
{:fx [[:effects.async-storage/get
{:keys [:update-profile-asked?]
:cb (fn [{:keys [update-profile-asked?]}]
(rf/dispatch (if update-profile-asked?
pending-event
[:profile/ask-profile-update pending-event])))}]]}))

View File

@ -0,0 +1,60 @@
(ns status-im.contexts.profile.edit.modal.style
(:require
[quo.foundations.colors :as colors]
[react-native.platform :as platform]))
(def continue-button
{:width "100%"
:margin-left :auto
:margin-top (if platform/android? :auto 0)
:margin-right :auto})
(def button-container
{:width "100%"
:padding-left 20
:padding-right 20
:padding-top 12
:align-self :flex-end
:height 64})
(defn view-button-container
[keyboard-shown?]
(merge button-container
(if platform/ios?
{:margin-bottom (if keyboard-shown? 0 34)}
{:margin-bottom (if keyboard-shown? 12 34)})))
(def blur-button-container
(merge button-container
(when platform/android? {:padding-bottom 12})))
(def page-container
{:position :absolute
:top 0
:bottom 0
:left 0
:right 0
:z-index 100})
(def info-message
{:margin-top 8})
(def title
{:color colors/white
:margin-top 12
:margin-bottom 18})
(def color-title
{:color colors/white-70-blur
:margin-top 20
:margin-bottom 16})
(def content-container
{:padding-horizontal 20})
(def input-container
{:align-items :flex-start})
(def profile-input-container
{:flex-direction :row
:justify-content :center})

View File

@ -0,0 +1,266 @@
(ns status-im.contexts.profile.edit.modal.view
(:require
[clojure.string :as string]
[oops.core :as oops]
[quo.core :as quo]
[quo.foundations.colors :as colors]
[react-native.core :as rn]
[react-native.hooks :as hooks]
[react-native.platform :as platform]
[react-native.safe-area :as safe-area]
[reagent.core :as reagent]
[status-im.common.avatar-picture-picker.view :as profile-picture-picker]
[status-im.common.events-helper :as events-helper]
[status-im.common.validation.profile :as profile-validator]
[status-im.constants :as c]
[status-im.contexts.profile.edit.modal.events]
[status-im.contexts.profile.edit.modal.style :as style]
[utils.i18n :as i18n]
[utils.re-frame :as rf]
[utils.responsiveness :as responsiveness]))
(def scroll-view-height (reagent/atom 0))
(def content-container-height (reagent/atom 0))
(def set-scroll-view-height #(reset! scroll-view-height %))
(defn set-content-container-height
[e]
(reset! content-container-height (oops/oget e "nativeEvent.layout.height")))
(defn show-button-background?
[keyboard-height keyboard-shown content-scroll-y]
(let [button-container-height 64
keyboard-view-height (+ keyboard-height button-container-height)]
(when keyboard-shown
(cond
platform/android?
(< (- @scroll-view-height button-container-height) @content-container-height)
platform/ios?
(< (- @scroll-view-height keyboard-view-height) (- @content-container-height content-scroll-y))
:else
false))))
(defn button-container
[show-keyboard? keyboard-shown show-background? keyboard-height children]
(let [height (reagent/atom 0)]
(reset! height (if show-keyboard? (if keyboard-shown keyboard-height 0) 0))
[rn/view {:style {:margin-top :auto}}
(cond
(and (> @height 0) show-background?)
[quo/blur
(when keyboard-shown
{:blur-amount 34
:blur-type :transparent
:overlay-color :transparent
:background-color (if platform/android?
colors/neutral-100
colors/neutral-80-opa-1-blur)
:style style/blur-button-container})
children]
(and (> @height 0) (not show-background?))
[rn/view {:style (style/view-button-container true)}
children]
(not show-keyboard?)
[rn/view {:style (style/view-button-container false)}
children])]))
(defn- add-keyboard-listener
[e callback]
(oops/ocall rn/keyboard "addListener" e callback))
(defn- on-keyboard-show
[f]
(add-keyboard-listener (if platform/android? "keyboardDidShow" "keyboardWillShow") f))
(defn- on-keyboard-hide
[f]
(add-keyboard-listener (if platform/android? "keyboardDidHide" "keyboardWillHide") f))
(defn floating-button
[{:keys [custom-color scroll-y on-submit disabled?]}]
(reagent/with-let [show-keyboard? (reagent/atom false)
show-listener (on-keyboard-show #(reset! show-keyboard? true))
hide-listener (on-keyboard-hide #(reset! show-keyboard? false))]
(let [{:keys [keyboard-shown keyboard-height]} (hooks/use-keyboard)
show-background? (show-button-background? keyboard-height
keyboard-shown
scroll-y)]
[rn/keyboard-avoiding-view
{:style {:position :absolute
:top 0
:bottom 0
:left 0
:right 0}
:pointer-events :box-none}
[button-container @show-keyboard? keyboard-shown show-background? keyboard-height
[quo/button
{:accessibility-label :submit-create-profile-button
:type :primary
:customization-color custom-color
:on-press on-submit
:container-style style/continue-button
:disabled? disabled?}
(i18n/label :t/continue)]]])
(finally
(oops/ocall show-listener "remove")
(oops/ocall hide-listener "remove"))))
(defn- content
[{:keys [set-display-name set-validation-msg picture-picker]}]
(let [open-picture-picker (fn []
(rf/dispatch [:dismiss-keyboard])
(rf/dispatch [:show-bottom-sheet
{:content picture-picker
:shell? true
:theme :dark}]))
on-change-text (fn [s]
(set-validation-msg s)
(set-display-name s))]
(fn [{:keys [profile-image custom-color display-name validation-msg valid-name?
name-too-short? initial-display-name set-scroll set-scroll-height
set-custom-color]}]
(let [{window-width :width} (rn/get-window)
info-type (cond
validation-msg :error
name-too-short? :default
:else :success)
info-message (or validation-msg
(i18n/label :t/minimum-characters
{:min-chars profile-validator/min-length}))
full-name (if (seq display-name)
display-name
initial-display-name)]
[rn/scroll-view
{:on-layout set-scroll-height
:on-scroll set-scroll
:scroll-event-throttle 64
:content-container-style {:flex-grow 1}}
[rn/view {:on-layout set-content-container-height}
[rn/view {:style style/content-container}
[quo/text
{:style style/title
:size :heading-1
:weight :semi-bold}
(i18n/label :t/edit-profile)]
[rn/view {:style style/input-container}
[rn/view {:style style/profile-input-container}
[quo/profile-input
{:customization-color custom-color
:placeholder initial-display-name
:on-press open-picture-picker
:image-picker-props {:profile-picture profile-image
:full-name full-name
:customization-color custom-color}
:title-input-props {:default-value ""
:auto-focus true
:max-length c/profile-name-max-length
:on-change-text on-change-text}}]]
[quo/info-message
{:status info-type
:size :default
:icon (if valid-name? :i/positive-state :i/info)
:color (when (= :default info-type) colors/white-70-blur)
:container-style style/info-message}
info-message]
[quo/text
{:size :paragraph-2
:weight :medium
:style style/color-title}
(i18n/label :t/colour)]]]
[quo/color-picker
{:blur? true
:default-selected custom-color
:on-change set-custom-color
:window-width window-width
:container-style {:padding-left (responsiveness/iphone-11-Pro-20-pixel-from-width
window-width)}}]]]))))
(defn- submittable-state?
[{:keys [new-color? new-picture? new-name? valid-name?]}]
(let [acceptable-name? (or (not new-name?) valid-name?)]
(or (and new-color? acceptable-name?)
(and new-picture? acceptable-name?)
(and new-name? valid-name?))))
(defn view
[]
(let [{:keys [pending-event]} (rf/sub [:get-screen-params])
{initial-display-name :display-name
initial-color :customization-color
[initial-image] :images} (rf/sub [:profile/profile])
top (safe-area/get-top)
scroll-y (reagent/atom 0)
display-name (reagent/atom "")
validation-msg (reagent/atom (profile-validator/validation-name @display-name))
custom-color (reagent/atom (or initial-color c/profile-default-color))
profile-image (reagent/atom initial-image)
set-display-name #(reset! validation-msg (profile-validator/validation-name %))
set-validation-msg #(reset! display-name (string/trim %))
set-custom-color #(reset! custom-color %)
set-scroll (fn [e]
(reset! scroll-y (oops/oget e "nativeEvent.contentOffset.y")))
set-scroll-height (fn [e]
(reset! scroll-y 0)
(-> e
(oops/oget "nativeEvent.layout.height")
set-scroll-view-height))
picture-picker (fn []
[profile-picture-picker/view
{:on-result #(reset! profile-image %)
:has-picture? (some? @profile-image)}])
update-profile (fn [] ;; TODO: check image not updated in communities and
;; profile
(rf/dispatch
[:profile/edit-profile
(cond-> {:on-success pending-event}
(and (seq @display-name)
(not= initial-display-name @display-name))
(assoc :display-name @display-name)
(and (not= initial-image @profile-image))
(assoc :picture @profile-image)
(not= initial-color @custom-color)
(assoc :color @custom-color))]))
on-close (fn []
(events-helper/navigate-back)
(rf/dispatch pending-event))]
(fn []
(let [name-too-short? (profile-validator/name-too-short? @display-name)
valid-name? (and (not @validation-msg) (not name-too-short?))
disabled? (not
(submittable-state?
{:new-color? (not= initial-color @custom-color)
:new-picture? (not= @profile-image initial-image)
:new-name? (and (not= initial-display-name @display-name)
(seq @display-name))
:valid-name? valid-name?}))]
[rn/view {:style style/page-container}
[quo/page-nav
{:margin-top top
:background :blur
:icon-name :i/close
:on-press on-close}]
[content
{:profile-image @profile-image
:custom-color @custom-color
:display-name @display-name
:validation-msg @validation-msg
:valid-name? valid-name?
:name-too-short? name-too-short?
:picture-picker picture-picker
:initial-display-name initial-display-name
:set-scroll set-scroll
:set-scroll-height set-scroll-height
:set-custom-color set-custom-color
:set-display-name set-display-name
:set-validation-msg set-validation-msg}]
[floating-button
{:custom-color @custom-color
:scroll-y @scroll-y
:on-submit update-profile
:disabled? disabled?}]]))))