fix #1571 Feature/profile refactoring

Second step of refactoring guidelines to fix #1571

- use fx and cofx
- specs
- simplify event chain
This commit is contained in:
Eric Dvorsak 2017-08-13 00:19:13 +02:00 committed by Roman Volosovskyi
parent d63d411c53
commit cb48195383
8 changed files with 132 additions and 112 deletions

View File

@ -34,12 +34,6 @@
(defn open-drawer [] (.openDrawer @drawer-atom)) (defn open-drawer [] (.openDrawer @drawer-atom))
(defn close-drawer [] (.closeDrawer @drawer-atom)) (defn close-drawer [] (.closeDrawer @drawer-atom))
(defn- update-status [new-status]
(when-not (str/blank? new-status)
(rf/dispatch [:check-status-change new-status])
(rf/dispatch [:account-update {:status new-status}])
(rf/dispatch [:set-in [:profile-edit :status] new-status])))
(defview profile-picture [] (defview profile-picture []
[account [:get-current-account]] [account [:get-current-account]]
[touchable-opacity {:on-press #(rf/dispatch [:navigate-to :my-profile]) [touchable-opacity {:on-press #(rf/dispatch [:navigate-to :my-profile])
@ -49,27 +43,25 @@
(defview name-input [] (defview name-input []
[account [:get-current-account] [account [:get-current-account]
new-name [:get-in [:profile-edit :name]]] name-text (r/atom nil)]
(let [current-name (:name account) (let [previous-name (:name account)]
public-key (:public-key account)] [view st/name-input-wrapper
[view {:style st/name-input-wrapper}
[text-input [text-input
{:placeholder (gfycat/generate-gfy public-key) {:placeholder (gfycat/generate-gfy public-key)
:style (st/name-input-text (s/valid? ::profile.db/name (or new-name current-name))) :style (st/name-input-text (s/valid? ::profile.db/name @name-text))
:font :medium :font :medium
:default-value (or new-name current-name) :default-value (or @name-text previous-name)
:on-change-text #(rf/dispatch [:set-in [:profile-edit :name] %]) :on-change-text #(reset! name-text %)
:on-end-editing #(do :on-end-editing #(if (s/valid? ::profile.db/name @name-text)
(rf/dispatch [:set-in [:profile-edit :name] nil]) (rf/dispatch [:account-update {:name (utils/clean-text @name-text)}])
(when (s/valid? ::profile.db/name new-name) (reset! name-text previous-name))}]]))
(rf/dispatch [:account-update {:name (utils/clean-text new-name)}])))}]]))
(defview status-input [] (defview status-input []
[account [:get-current-account] [account [:get-current-account]
status-edit? (r/atom false) status-edit? (r/atom false)
status-text (r/atom nil)] status-text (r/atom nil)]
(let [status (:status account) (let [placeholder (i18n/label :t/update-status)
placeholder (i18n/label :t/update-status)] previous-status (:status account)]
[view st/status-container [view st/status-container
(if @status-edit? (if @status-edit?
[text-input {:style st/status-input-view [text-input {:style st/status-input-view
@ -79,21 +71,21 @@
:max-length 140 :max-length 140
:accessibility-label id/drawer-status-input :accessibility-label id/drawer-status-input
:placeholder placeholder :placeholder placeholder
:default-value status :default-value previous-status
:on-blur #(do
(reset! status-edit? false)
(update-status @status-text))
:on-change-text #(let [new-status (utils/clean-text %)] :on-change-text #(let [new-status (utils/clean-text %)]
(reset! status-text new-status) (reset! status-text new-status)
(if (str/includes? % "\n") (when (str/includes? % "\n")
(do
(reset! status-edit? false) (reset! status-edit? false)
(update-status new-status)) (rf/dispatch [:my-profile/update-status new-status])))
(rf/dispatch [:set-in [:profile-edit :status] new-status])))}] :on-blur #(do (reset! status-edit? false)
[status-view/status-view {:style (st/status-view (str/blank? status)) (rf/dispatch [:my-profile/update-status @status-text]))}]
[status-view/status-view {:style (st/status-view (str/blank? previous-status))
:on-press #(reset! status-edit? true) :on-press #(reset! status-edit? true)
:number-of-lines 3 :number-of-lines 3
:status (if (str/blank? status) placeholder status)}])])) :status (if (str/blank? previous-status)
placeholder
previous-status)}])]))
(defview transaction-list-item [{:keys [to value timestamp] :as transaction}] (defview transaction-list-item [{:keys [to value timestamp] :as transaction}]
[recipient [:contact-by-address to]] [recipient [:contact-by-address to]]
@ -152,7 +144,6 @@
[touchable-highlight [touchable-highlight
{:on-press (fn [] {:on-press (fn []
(close-drawer) (close-drawer)
(rf/dispatch [:set-in [:profile-edit :name] nil])
(rf/dispatch [:navigate-to :my-profile]))} (rf/dispatch [:navigate-to :my-profile]))}
[view [icon :options_gray]]]]) [view [icon :options_gray]]]])
@ -161,7 +152,6 @@
[touchable-highlight [touchable-highlight
{:on-press (fn [] {:on-press (fn []
(close-drawer) (close-drawer)
(rf/dispatch [:set-in [:profile-edit :name] nil])
(rf/dispatch [:navigate-to :accounts]))} (rf/dispatch [:navigate-to :accounts]))}
[view [view
[text {:style st/switch-account-text [text {:style st/switch-account-text

View File

@ -27,7 +27,7 @@
:current-chat-id console-chat-id :current-chat-id console-chat-id
:loading-allowed true :loading-allowed true
:selected-participants #{} :selected-participants #{}
:profile-edit {:edit? false :my-profile/edit {:edit? false
:name nil :name nil
:email nil :email nil
:status nil :status nil
@ -97,7 +97,8 @@
:group/contact-group-id :group/contact-group-id
:group/group-type :group/group-type
:group/selected-contacts :group/selected-contacts
:group/groups-order] :group/groups-order
:my-profile/edit]
:opt-un :opt-un
[::current-public-key [::current-public-key
::modal ::modal
@ -158,7 +159,6 @@
:chat/raw-unviewed-messages :chat/raw-unviewed-messages
:chat/bot-db :chat/bot-db
:chat/geolocation :chat/geolocation
:profile/profile-edit
:transactions/transactions :transactions/transactions
:transactions/transactions-queue :transactions/transactions-queue
:transactions/selected-transaction :transactions/selected-transaction

View File

@ -1,4 +1,5 @@
(ns status-im.ui.screens.profile.db (ns status-im.ui.screens.profile.db
(:require-macros [status-im.utils.db :refer [allowed-keys]])
(:require [cljs.spec.alpha :as spec] (:require [cljs.spec.alpha :as spec]
[clojure.string :as string] [clojure.string :as string]
[status-im.chat.constants :as chat.constants] [status-im.chat.constants :as chat.constants]
@ -19,10 +20,26 @@
(or (string/blank? email) (or (string/blank? email)
(and (string? email) (re-matches pattern email))))) (and (string? email) (re-matches pattern email)))))
(spec/def ::name correct-name?) (defn base64-encoded-image-path? [photo-path]
(spec/def ::email correct-email?) (or (string/starts-with? photo-path "data:image/jpeg;base64,")
(string/starts-with? photo-path "data:image/png;base64,")))
(spec/def :profile/name (spec/nilable correct-name?))
(spec/def ::profile (spec/keys :req-un [:profile/name]))
(spec/def ::name (spec/or :name correct-name?
:empty-string string/blank?))
(spec/def ::email (spec/nilable correct-email?))
(spec/def ::edit? boolean?)
(spec/def ::status (spec/nilable string?))
(spec/def ::photo-path (spec/nilable base64-encoded-image-path?))
(spec/def ::edit-status? boolean?)
(spec/def ::profile (spec/keys :req-un [::name]))
;; EDIT PROFILE ;; EDIT PROFILE
(spec/def :profile/profile-edit (spec/nilable map?)) (spec/def :my-profile/edit (allowed-keys
:req-un [::email ::edit? ::name ::status ::photo-path]
:opt-un [::edit-status?]))

View File

@ -24,15 +24,15 @@
:actions [{:image :blank}]}]) :actions [{:image :blank}]}])
(defview profile-name-input [] (defview profile-name-input []
(letsubs [new-profile-name [:get-in [:profile-edit :name]]] (letsubs [new-profile-name [:get-in [:my-profile/edit :name]]]
[react/view [react/view
[text-input-with-label {:label (label :t/name) [text-input-with-label {:label (label :t/name)
:default-value new-profile-name :default-value new-profile-name
:on-change-text #(dispatch [:set-in [:profile-edit :name] %])}]])) :on-change-text #(dispatch [:set-in [:my-profile/edit :name] %])}]]))
(def profile-icon-options (def profile-icon-options
[{:text (label :t/image-source-gallery) [{:text (label :t/image-source-gallery)
:value #(dispatch [:open-image-picker])} :value #(dispatch [:my-profile/update-picture])}
{:text (label :t/image-source-make-photo) {:text (label :t/image-source-make-photo)
:value (fn [] :value (fn []
(dispatch [:request-permissions (dispatch [:request-permissions
@ -43,8 +43,8 @@
(utils/show-popup (label :t/error) (utils/show-popup (label :t/error)
(label :t/camera-access-error)))))]))}]) (label :t/camera-access-error)))))]))}])
(defn edit-profile-bage [contact] (defn edit-profile-badge [contact]
[react/view styles/edit-profile-bage [react/view styles/edit-profile-badge
[react/view styles/edit-profile-icon-container [react/view styles/edit-profile-icon-container
[context-menu [context-menu
[my-profile-icon {:account contact [my-profile-icon {:account contact
@ -66,12 +66,12 @@
:max-length 140 :max-length 140
:placeholder (label :t/status) :placeholder (label :t/status)
:style styles/profile-status-input :style styles/profile-status-input
:on-change-text #(dispatch [:set-in [:profile-edit :status] (clean-text %)]) :on-change-text #(dispatch [:set-in [:my-profile/edit :status] (clean-text %)])
:on-blur #(dispatch [:set-in [:profile-edit :edit-status?] false]) :on-blur #(dispatch [:set-in [:my-profile/edit :edit-status?] false])
:blur-on-submit true :blur-on-submit true
:on-submit-editing #(.blur @input-ref) :on-submit-editing #(.blur @input-ref)
:default-value status}] :default-value status}]
[react/touchable-highlight {:on-press #(dispatch [:set-in [:profile-edit :edit-status?] true])} [react/touchable-highlight {:on-press #(dispatch [:set-in [:my-profile/edit :edit-status?] true])}
[react/view [react/view
(if (string/blank? status) (if (string/blank? status)
[react/text {:style styles/add-a-status} [react/text {:style styles/add-a-status}
@ -87,8 +87,8 @@
(defview edit-my-profile [] (defview edit-my-profile []
(letsubs [current-account [:get-current-account] (letsubs [current-account [:get-current-account]
changed-account [:get :profile-edit]] changed-account [:get :my-profile/edit]]
{:component-will-unmount #(dispatch [:set-in [:profile-edit :edit-status?] false])} {:component-will-unmount #(dispatch [:set-in [:my-profile/edit :edit-status?] false])}
(let [profile-edit-data-valid? (spec/valid? ::db/profile changed-account) (let [profile-edit-data-valid? (spec/valid? ::db/profile changed-account)
profile-edit-data-changed? (or (not= (:name current-account) (:name changed-account)) profile-edit-data-changed? (or (not= (:name current-account) (:name changed-account))
(not= (:status current-account) (:status changed-account)) (not= (:status current-account) (:status changed-account))
@ -97,10 +97,8 @@
[status-bar] [status-bar]
[edit-my-profile-toolbar] [edit-my-profile-toolbar]
[react/view styles/edit-my-profile-form [react/view styles/edit-my-profile-form
[edit-profile-bage changed-account] [edit-profile-badge changed-account]
[edit-profile-status changed-account] [edit-profile-status changed-account]
[status-prompt changed-account]] [status-prompt changed-account]]
(when (and profile-edit-data-changed? profile-edit-data-valid?) (when (and profile-edit-data-changed? profile-edit-data-valid?)
[sticky-button (label :t/save) #(do [sticky-button (label :t/save) #(dispatch [:my-profile/save-changes])])])))
(dispatch [:check-status-change (:status changed-account)])
(dispatch [:account-update changed-account]))])])))

View File

@ -1,5 +1,6 @@
(ns status-im.ui.screens.profile.events (ns status-im.ui.screens.profile.events
(:require [re-frame.core :as re-frame :refer [reg-fx]] (:require [clojure.string :as string]
[re-frame.core :as re-frame :refer [reg-fx]]
[status-im.components.react :refer [show-image-picker]] [status-im.components.react :refer [show-image-picker]]
[status-im.constants :refer [console-chat-id]] [status-im.constants :refer [console-chat-id]]
[status-im.ui.screens.profile.navigation] [status-im.ui.screens.profile.navigation]
@ -7,52 +8,71 @@
[status-im.utils.image-processing :refer [img->base64]] [status-im.utils.image-processing :refer [img->base64]]
[taoensso.timbre :as log])) [taoensso.timbre :as log]))
(defn message-user [identity] (reg-fx
(when identity
(re-frame/dispatch [:navigation-replace :chat identity])))
(handlers/register-handler
:open-image-picker :open-image-picker
(handlers/side-effect! ;; the image picker is only used here for now, this effect can be use in other scenarios as well
(fn [_ _] (fn [callback-event]
(show-image-picker (show-image-picker
(fn [image] (fn [image]
(let [path (get (js->clj image) "path") (let [path (get (js->clj image) "path")
_ (log/debug path) _ (log/debug path)
on-success (fn [base64] on-success (fn [base64]
(re-frame/dispatch [:set-in [:profile-edit :photo-path] (str "data:image/jpeg;base64," base64)])) (re-frame/dispatch [callback-event base64]))
on-error (fn [type error] on-error (fn [type error]
(.log js/console type error))] (.log js/console type error))]
(img->base64 path on-success on-error))))))) (img->base64 path on-success on-error))))))
(handlers/register-handler (handlers/register-handler-fx
:phone-number-change-requested :profile/send-transaction
(fn [_ [_ chat-id]]
{:dispatch-n [[:clear-seq-arguments]
[:navigate-to :chat chat-id]
;; TODO https://github.com/status-im/status-react/issues/1621
[:select-chat-input-command {:name "send"}]]}))
(handlers/register-handler-fx
:profile/send-message
(fn [_ [_ identity]]
(when identity
{:dispatch [:navigation-replace :chat identity]})))
(handlers/register-handler-fx
:my-profile/edit
(fn [{:keys [db]} [_ edit-type edit-value]]
(let [current-account-id (:current-account-id db)
current-account (select-keys (get-in db [:accounts current-account-id])
[:name :photo-path :status])
new-db (-> db
(update-in [:my-profile/edit] merge current-account)
(assoc-in [:my-profile/edit :edit-status?] (= edit-type :status true)))]
{:db new-db
:dispatch [:navigate-to :edit-my-profile]})))
(handlers/register-handler-fx
:my-profile/update-status
(fn [_ [_ new-status]]
(when-not (string/blank? new-status)
{:dispatch-n [[:check-status-change new-status]
[:account-update {:status new-status}]]})))
(handlers/register-handler-fx
:my-profile/update-phone-number
;; Switch user to the console issuing the !phone command automatically to let him change his phone number. ;; Switch user to the console issuing the !phone command automatically to let him change his phone number.
;; We allow to change phone number only from console because this requires entering SMS verification code. ;; We allow to change phone number only from console because this requires entering SMS verification code.
(handlers/side-effect! (fn [_ _]
(fn [db _] {:dispatch-n [[:navigate-to :chat console-chat-id]
(re-frame/dispatch [:navigate-to :chat console-chat-id]) [:select-chat-input-command {:name "phone"}]]}))
(js/setTimeout #(re-frame/dispatch [:select-chat-input-command {:name "phone"}]) 500))))
(handlers/register-handler (handlers/register-handler-fx
:open-chat-with-the-send-transaction :my-profile/update-picture
(handlers/side-effect! (fn [{:keys [db]} [this-event base64-image]]
(fn [db [_ chat-id]] (if base64-image
(re-frame/dispatch [:clear-seq-arguments]) {:db (assoc-in db [:my-profile/edit :photo-path] (str "data:image/jpeg;base64," base64-image))}
(re-frame/dispatch [:navigate-to :chat chat-id]) {:open-image-picker this-event})))
(js/setTimeout #(re-frame/dispatch [:select-chat-input-command {:name "send"}]) 500))))
(defn prepare-edit-profile (handlers/register-handler-fx
[{:keys [current-account-id] :as db} _] :my-profile/save-changes
(let [current-account (select-keys (get-in db [:accounts current-account-id]) (fn [{:keys [db]} _]
[:name :photo-path :status])] (let [{:keys [:my-profile/edit]} db]
(update-in db [:profile-edit] merge current-account))) {:dispatch-n [[:check-status-change (:status edit)]
[:account-update edit]]})))
(defn open-edit-profile [_ _]
(re-frame/dispatch [:navigate-to :edit-my-profile]))
(handlers/register-handler
:open-edit-my-profile
(handlers/handlers->
prepare-edit-profile
open-edit-profile))

View File

@ -18,7 +18,7 @@
_ (log/debug "Captured image: " path) _ (log/debug "Captured image: " path)
on-success (fn [base64] on-success (fn [base64]
(log/debug "Captured success: " base64) (log/debug "Captured success: " base64)
(dispatch [:set-in [:profile-edit :photo-path] (str "data:image/jpeg;base64," base64)]) (dispatch [:set-in [:my-profile/edit :photo-path] (str "data:image/jpeg;base64," base64)])
(dispatch [:navigate-back])) (dispatch [:navigate-back]))
on-error (fn [type error] on-error (fn [type error]
(log/debug type error))] (log/debug type error))]

View File

@ -50,10 +50,10 @@
:padding 16 :padding 16
:max-height 114}) :max-height 114})
(def profile-bage (def profile-badge
{:flex-direction :row}) {:flex-direction :row})
(def edit-profile-bage (def edit-profile-badge
{:flex-direction :row {:flex-direction :row
:padding-left 24}) :padding-left 24})

View File

@ -17,14 +17,13 @@
[status-im.components.toolbar-new.actions :as actions] [status-im.components.toolbar-new.actions :as actions]
[status-im.components.toolbar-new.view :refer [toolbar]] [status-im.components.toolbar-new.view :refer [toolbar]]
[status-im.i18n :refer [label]] [status-im.i18n :refer [label]]
[status-im.ui.screens.profile.events :as profile.event :refer [message-user]]
[status-im.ui.screens.profile.styles :as styles] [status-im.ui.screens.profile.styles :as styles]
[status-im.utils.datetime :as time] [status-im.utils.datetime :as time]
[status-im.utils.utils :refer [hash-tag?]]) [status-im.utils.utils :refer [hash-tag?]])
(:require-macros [status-im.utils.views :refer [defview]])) (:require-macros [status-im.utils.views :refer [defview]]))
(defn my-profile-toolbar [] (defn my-profile-toolbar []
[toolbar {:actions [(actions/opts [{:value #(dispatch [:open-edit-my-profile]) [toolbar {:actions [(actions/opts [{:value #(dispatch [:my-profile/edit])
:text (label :t/edit)}])]}]) :text (label :t/edit)}])]}])
(defn profile-toolbar [contact] (defn profile-toolbar [contact]
@ -43,7 +42,7 @@
(label :t/active-unknown)))) (label :t/active-unknown))))
(defn profile-badge [{:keys [name last-online] :as contact}] (defn profile-badge [{:keys [name last-online] :as contact}]
[react/view styles/profile-bage [react/view styles/profile-badge
[my-profile-icon {:account contact [my-profile-icon {:account contact
:edit? false}] :edit? false}]
[react/view styles/profile-badge-name-container [react/view styles/profile-badge-name-container
@ -66,13 +65,13 @@
[action-separator] [action-separator]
[action-button (label :t/start-conversation) [action-button (label :t/start-conversation)
:chats_blue :chats_blue
#(message-user whisper-identity)] #(dispatch [:profile/send-message whisper-identity])]
(when-not dapp? (when-not dapp?
[react/view [react/view
[action-separator] [action-separator]
[action-button (label :t/send-transaction) [action-button (label :t/send-transaction)
:arrow_right_blue :arrow_right_blue
#(dispatch [:open-chat-with-the-send-transaction chat-id])]])]) #(dispatch [:profile/send-transaction chat-id])]])])
(defn profile-info-item [{:keys [label value options text-mode empty-value?]}] (defn profile-info-item [{:keys [label value options text-mode empty-value?]}]
[react/view styles/profile-setting-item [react/view styles/profile-setting-item
@ -160,22 +159,18 @@
[info-item-separator] [info-item-separator]
[profile-info-phone-item [profile-info-phone-item
phone phone
[{:value #(dispatch [:phone-number-change-requested]) [{:value #(dispatch [:my-profile/change-phone-number])
:text (label :t/edit)}]]]) :text (label :t/edit)}]]])
(defn- profile-status-on-press []
(dispatch [:set-in [:profile-edit :edit-status?] true])
(dispatch [:open-edit-my-profile]))
(defn profile-status [status & [edit?]] (defn profile-status [status & [edit?]]
[react/view styles/profile-status-container [react/view styles/profile-status-container
(if (or (nil? status) (string/blank? status)) (if (or (nil? status) (string/blank? status))
[react/touchable-highlight {:on-press profile-status-on-press} [react/touchable-highlight {:on-press #(dispatch [:my-profile/edit :status])}
[react/view [react/view
[react/text {:style styles/add-a-status} [react/text {:style styles/add-a-status}
(label :t/add-a-status)]]] (label :t/add-a-status)]]]
[react/scroll-view [react/scroll-view
[react/touchable-highlight {:on-press (when edit? profile-status-on-press)} [react/touchable-highlight {:on-press (when edit? #(dispatch [:my-profile/edit :status]))}
[react/view [react/view
[react/text {:style styles/profile-status-text} [react/text {:style styles/profile-status-text}
(colorize-status-hashtags status)]]]])]) (colorize-status-hashtags status)]]]])])