Costmetic refactoring according to refactoring guidelines

This commit is contained in:
Eric Dvorsak 2017-08-09 12:48:28 +02:00 committed by Roman Volosovskyi
parent 6dafd137dc
commit 69a84c8315
24 changed files with 624 additions and 658 deletions

View File

@ -22,7 +22,7 @@
dismiss-keyboard!]] dismiss-keyboard!]]
[status-im.components.status-view.view :as status-view] [status-im.components.status-view.view :as status-view]
[status-im.i18n :as i18n] [status-im.i18n :as i18n]
[status-im.profile.validations :as v] [status-im.ui.screens.profile.db :as profile.db]
[status-im.utils.datetime :as time] [status-im.utils.datetime :as time]
[status-im.utils.gfycat.core :as gfycat] [status-im.utils.gfycat.core :as gfycat]
[status-im.utils.listview :as lw] [status-im.utils.listview :as lw]
@ -54,13 +54,13 @@
[view {:style st/name-input-wrapper} [view {:style st/name-input-wrapper}
[text-input [text-input
{:placeholder placeholder {:placeholder placeholder
:style (st/name-input-text (s/valid? ::v/name (or new-name current-name))) :style (st/name-input-text (s/valid? ::profile.db/name (or new-name current-name)))
:font :medium :font :medium
:default-value (or new-name current-name) :default-value (or new-name current-name)
:on-change-text #(rf/dispatch [:set-in [:profile-edit :name] %]) :on-change-text #(rf/dispatch [:set-in [:profile-edit :name] %])
:on-end-editing #(do :on-end-editing #(do
(rf/dispatch [:set-in [:profile-edit :name] nil]) (rf/dispatch [:set-in [:profile-edit :name] nil])
(when (s/valid? ::v/name new-name) (when (s/valid? ::profile.db/name new-name)
(rf/dispatch [:account-update {:name (utils/clean-text new-name)}])))}]])) (rf/dispatch [:account-update {:name (utils/clean-text new-name)}])))}]]))
(defview status-input [] (defview status-input []

View File

@ -1,113 +0,0 @@
(ns status-im.profile.edit.screen
(:require-macros [status-im.utils.views :refer [defview]])
(:require [cljs.spec.alpha :as s]
[clojure.string :as str]
[reagent.core :as r]
[re-frame.core :refer [dispatch]]
[status-im.profile.styles :as st]
[status-im.components.text-input-with-label.view :refer [text-input-with-label]]
[status-im.components.styles :refer [color-blue color-gray5]]
[status-im.components.status-bar :refer [status-bar]]
[status-im.components.toolbar-new.view :refer [toolbar]]
[status-im.components.toolbar-new.actions :as act]
[status-im.i18n :refer [label]]
[status-im.profile.screen :refer [colorize-status-hashtags]]
[status-im.components.sticky-button :refer [sticky-button]]
[status-im.components.camera :as camera]
[status-im.components.chat-icon.screen :refer [my-profile-icon]]
[status-im.components.context-menu :refer [context-menu]]
[status-im.profile.validations :as v]
[status-im.components.react :refer [view
scroll-view
keyboard-avoiding-view
text
touchable-highlight
text-input]]
[status-im.utils.utils :as utils :refer [clean-text]]
[status-im.utils.platform :refer [ios?]]))
(defn edit-my-profile-toolbartoolbar []
[toolbar {:title (label :t/edit-profile)
:actions [{:image :blank}]}])
(defview profile-name-input []
[new-profile-name [:get-in [:profile-edit :name]]]
[view
[text-input-with-label {:label (label :t/name)
:default-value new-profile-name
:on-change-text #(dispatch [:set-in [:profile-edit :name] %])}]])
(def profile-icon-options
[{:text (label :t/image-source-gallery)
:value #(dispatch [:open-image-picker])}
{:text (label :t/image-source-make-photo)
:value (fn []
(dispatch [:request-permissions
[:camera :write-external-storage]
(fn []
(camera/request-access
#(if % (dispatch [:navigate-to :profile-photo-capture])
(utils/show-popup (label :t/error)
(label :t/camera-access-error)))))]))}])
(defn edit-profile-bage [contact]
[view st/edit-profile-bage
[view st/edit-profile-icon-container
[context-menu
[my-profile-icon {:account contact
:edit? true}]
profile-icon-options
st/context-menu-custom-styles]]
[view st/edit-profile-name-container
[profile-name-input]]])
(defn edit-profile-status [{:keys [status edit-status?]}]
(let [input-ref (r/atom nil)]
[view st/edit-profile-status
[scroll-view
(if edit-status?
[text-input
{:ref #(reset! input-ref %)
:auto-focus edit-status?
:multiline true
:max-length 140
:placeholder (label :t/status)
:style st/profile-status-input
:on-change-text #(dispatch [:set-in [:profile-edit :status] (clean-text %)])
:on-blur #(dispatch [:set-in [:profile-edit :edit-status?] false])
:blur-on-submit true
:on-submit-editing #(.blur @input-ref)
:default-value status}]
[touchable-highlight {:on-press #(dispatch [:set-in [:profile-edit :edit-status?] true])}
[view
(if (str/blank? status)
[text {:style st/add-a-status}
(label :t/status)]
[text {:style st/profile-status-text}
(colorize-status-hashtags status)])]])]]))
(defn status-prompt [{:keys [status]}]
(when (or (nil? status) (str/blank? status))
[view st/status-prompt
[text {:style st/status-prompt-text}
(colorize-status-hashtags (label :t/status-prompt))]]))
(defview edit-my-profile []
[current-account [:get-current-account]
changed-account [:get :profile-edit]]
{:component-will-unmount #(dispatch [:set-in [:profile-edit :edit-status?] false])}
(let [profile-edit-data-valid? (s/valid? ::v/profile changed-account)
profile-edit-data-changed? (or (not= (:name current-account) (:name changed-account))
(not= (:status current-account) (:status changed-account))
(not= (:photo-path current-account) (:photo-path changed-account)))]
[keyboard-avoiding-view {:style st/profile}
[status-bar]
[edit-my-profile-toolbartoolbar]
[view st/edit-my-profile-form
[edit-profile-bage changed-account]
[edit-profile-status changed-account]
[status-prompt changed-account]]
(when (and profile-edit-data-changed? profile-edit-data-valid?)
[sticky-button (label :t/save) #(do
(dispatch [:check-status-change (:status changed-account)])
(dispatch [:account-update changed-account]))])]))

View File

@ -1,62 +0,0 @@
(ns status-im.profile.handlers
(:require [re-frame.core :refer [subscribe dispatch after]]
[status-im.utils.handlers :refer [register-handler get-hashtags] :as u]
[status-im.components.react :refer [show-image-picker]]
[status-im.utils.image-processing :refer [img->base64]]
[status-im.i18n :refer [label]]
[taoensso.timbre :as log]
[status-im.constants :refer [console-chat-id]]
[status-im.ui.screens.navigation :as nav]))
(defn message-user [identity]
(when identity
(dispatch [:navigation-replace :chat identity])))
(register-handler :open-image-picker
(u/side-effect!
(fn [_ _]
(show-image-picker
(fn [image]
(let [path (get (js->clj image) "path")
_ (log/debug path)
on-success (fn [base64]
(dispatch [:set-in [:profile-edit :photo-path] (str "data:image/jpeg;base64," base64)]))
on-error (fn [type error]
(.log js/console type error))]
(img->base64 path on-success on-error)))))))
(register-handler :phone-number-change-requested
;; 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.
(u/side-effect!
(fn [db _]
(dispatch [:navigate-to :chat console-chat-id])
(js/setTimeout #(dispatch [:select-chat-input-command {:name "phone"}]) 500))))
(register-handler :open-chat-with-the-send-transaction
(u/side-effect!
(fn [db [_ chat-id]]
(dispatch [:clear-seq-arguments])
(dispatch [:navigate-to :chat chat-id])
(js/setTimeout #(dispatch [:select-chat-input-command {:name "send"}]) 500))))
(defn prepare-edit-profile
[{:keys [current-account-id] :as db} _]
(let [current-account (select-keys (get-in db [:accounts current-account-id])
[:name :photo-path :status])]
(update-in db [:profile-edit] merge current-account)))
(defn open-edit-profile [_ _]
(dispatch [:navigate-to :edit-my-profile]))
(register-handler :open-edit-my-profile
(u/handlers->
prepare-edit-profile
open-edit-profile))
(defmethod nav/preload-data! :qr-code-view
[{:keys [current-account-id] :as db} [_ _ {:keys [contact qr-source amount?]}]]
(assoc db :qr-modal {:contact (or contact
(get-in db [:accounts current-account-id]))
:qr-source qr-source
:amount? amount?}))

View File

@ -1,5 +0,0 @@
(ns status-im.profile.photo-capture.styles)
(def container
{:flex 1
:background-color :white})

View File

@ -1,58 +0,0 @@
(ns status-im.profile.qr-code.screen
(:require-macros [status-im.utils.views :refer [defview]])
(:require [status-im.components.react :refer [view
text
image
icon
touchable-highlight
get-dimensions]]
[status-im.components.status-bar :refer [status-bar]]
[status-im.components.styles :refer [icon-close]]
[status-im.components.qr-code :refer [qr-code]]
[re-frame.core :refer [dispatch subscribe]]
[status-im.profile.qr-code.styles :as st]
[status-im.i18n :refer [label]]
[clojure.string :as s]))
(defview qr-code-view []
[{:keys [photo-path address name] :as contact} [:get-in [:qr-modal :contact]]
{:keys [qr-source amount? dimensions]} [:get :qr-modal]
{:keys [amount]} [:get :contacts/click-params]]
[view st/wallet-qr-code
[status-bar {:type :modal}]
[view st/account-toolbar
[view st/wallet-account-container
[view st/qr-photo-container
[view st/account-photo-container
[image {:source {:uri (if (s/blank? photo-path) :avatar photo-path)}
:style st/photo-image}]]]
[view st/name-container
[text {:style st/name-text
:number-of-lines 1} name]]
[view st/online-container
[touchable-highlight {:onPress #(dispatch [:navigate-back])}
[view st/online-image-container
[icon :close_white]]]]]]
[view {:style st/qr-code
:on-layout #(let [layout (.. % -nativeEvent -layout)]
(dispatch [:set-in [:qr-modal :dimensions] {:width (.-width layout)
:height (.-height layout)}]))}
(when (:width dimensions)
[view {:style (st/qr-code-container dimensions)}
[qr-code {:value (if amount?
(prn-str {:address (get contact qr-source)
:amount amount})
(str "ethereum:" (get contact qr-source)))
:size (- (min (:width dimensions)
(:height dimensions))
80)}]])]
[view st/footer
[view st/wallet-info
[text {:style st/wallet-name-text} (label :t/main-wallet)]
[text {:style st/wallet-address-text} address]]
[touchable-highlight {:onPress #(dispatch [:navigate-back])}
[view st/done-button
[text {:style st/done-button-text} (label :t/done)]]]]])

View File

@ -1,232 +0,0 @@
(ns status-im.profile.screen
(:require-macros [status-im.utils.views :refer [defview]])
(:require [re-frame.core :refer [dispatch]]
[clojure.string :as str]
[reagent.core :as r]
[status-im.ui.screens.contacts.styles :as cst]
[status-im.components.common.common :refer [separator
form-spacer
top-shadow
bottom-shadow]]
[status-im.components.styles :refer [color-blue color-gray5]]
[status-im.components.context-menu :refer [context-menu]]
[status-im.components.action-button.action-button :refer [action-button
action-button-disabled
action-separator]]
[status-im.components.action-button.styles :refer [actions-list]]
[status-im.components.react :refer [view
text
text-input
image
icon
scroll-view
touchable-highlight]]
[status-im.components.chat-icon.screen :refer [my-profile-icon]]
[status-im.components.status-bar :refer [status-bar]]
[status-im.components.toolbar-new.view :refer [toolbar]]
[status-im.components.toolbar-new.actions :as act]
[status-im.components.list-selection :refer [share-options]]
[status-im.utils.platform :refer [platform-specific android?]]
[status-im.profile.handlers :refer [message-user]]
[status-im.profile.styles :as st]
[status-im.i18n :refer [label]]
[status-im.utils.datetime :as time]
[status-im.utils.utils :refer [hash-tag?]]))
(defn my-profile-toolbar []
[toolbar {:actions [(act/opts [{:value #(dispatch [:open-edit-my-profile])
:text (label :t/edit)}])]}])
(defn profile-toolbar [contact]
[toolbar
(when (and (not (:pending? contact))
(not (:unremovable? contact)))
{:actions [(act/opts [{:value #(dispatch [:hide-contact contact])
:text (label :t/remove-from-contacts)}])]})])
(defn online-text [last-online]
(let [last-online-date (time/to-date last-online)
now-date (time/now)]
(if (and (pos? last-online)
(<= last-online-date now-date))
(time/time-ago last-online-date)
(label :t/active-unknown))))
(defn profile-badge [{:keys [name last-online] :as contact}]
[view st/profile-bage
[my-profile-icon {:account contact
:edit? false}]
[view st/profile-badge-name-container
[text {:style st/profile-name-text
:number-of-lines 1}
name]
(when-not (nil? last-online)
[view st/profile-activity-status-container
[text {:style st/profile-activity-status-text}
(online-text last-online)]])]])
(defn profile-actions [{:keys [pending? whisper-identity dapp?]} chat-id]
[view actions-list
(if pending?
[action-button (label :t/add-to-contacts)
:add_blue
#(dispatch [:add-pending-contact chat-id])]
[action-button-disabled (label :t/in-contacts)
:ok_dark])
[action-separator]
[action-button (label :t/start-conversation)
:chats_blue
#(message-user whisper-identity)]
(when-not dapp?
[view
[action-separator]
[action-button (label :t/send-transaction)
:arrow_right_blue
#(dispatch [:open-chat-with-the-send-transaction chat-id])]])])
(defn profile-info-item [{:keys [label value options text-mode empty-value?]}]
[view st/profile-setting-item
[view (st/profile-info-text-container options)
[text {:style st/profile-setting-title}
label]
[view st/profile-setting-spacing]
[text {:style (if empty-value?
st/profile-setting-text-empty
st/profile-setting-text)
:number-of-lines 1
:ellipsizeMode text-mode}
value]]
(when options
[context-menu
[icon :options_gray]
options
nil
st/profile-info-item-button])])
(defn show-qr [contact qr-source]
#(dispatch [:navigate-to-modal :qr-code-view {:contact contact
:qr-source qr-source}]))
(defn profile-options [contact k text]
(into []
(concat [{:value (show-qr contact k)
:text (label :t/show-qr)}]
(when text
(share-options text)))))
(defn profile-info-address-item [{:keys [address] :as contact}]
[profile-info-item
{:label (label :t/address)
:value address
:options (profile-options contact :address address)
:text-mode :middle}])
(defn profile-info-public-key-item [public-key contact]
[profile-info-item
{:label (label :t/public-key)
:value public-key
:options (profile-options contact :public-key public-key)
:text-mode :middle}])
(defn info-item-separator []
[separator st/info-item-separator])
(defn tag-view [tag]
[text {:style {:color color-blue}
:font :medium}
(str tag " ")])
(defn colorize-status-hashtags [status]
(for [[i status] (map-indexed vector (str/split status #" "))]
(if (hash-tag? status)
^{:key (str "item-" i)}
[tag-view status]
^{:key (str "item-" i)}
(str status " "))))
(defn profile-info-phone-item [phone & [options]]
(let [phone-empty? (or (nil? phone) (str/blank? phone))
phone-text (if phone-empty?
(label :t/not-specified)
phone)]
[profile-info-item {:label (label :t/phone-number)
:value phone-text
:options options
:empty-value? phone-empty?}]))
(defn profile-info [{:keys [whisper-identity status phone] :as contact}]
[view
[profile-info-address-item contact]
[info-item-separator]
[profile-info-public-key-item whisper-identity contact]
[info-item-separator]
[profile-info-phone-item phone]])
(defn my-profile-info [{:keys [public-key status phone] :as contact}]
[view
[profile-info-address-item contact]
[info-item-separator]
[profile-info-public-key-item public-key contact]
[info-item-separator]
[profile-info-phone-item
phone
[{:value #(dispatch [:phone-number-change-requested])
: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?]]
[view st/profile-status-container
(if (or (nil? status) (str/blank? status))
[touchable-highlight {:on-press profile-status-on-press}
[view
[text {:style st/add-a-status}
(label :t/add-a-status)]]]
[scroll-view
[touchable-highlight {:on-press (when edit? profile-status-on-press)}
[view
[text {:style st/profile-status-text}
(colorize-status-hashtags status)]]]])])
(defview my-profile []
[{:keys [status] :as current-account} [:get-current-account]]
[view st/profile
[status-bar]
[my-profile-toolbar]
[scroll-view
[view st/profile-form
[profile-badge current-account]
[profile-status status true]]
[form-spacer]
[view actions-list
[action-button (label :t/show-qr)
:q_r_blue
(show-qr current-account :public-key)]]
[form-spacer]
[view st/profile-info-container
[my-profile-info current-account]
[bottom-shadow]]]])
(defview profile []
[{:keys [pending?
status
whisper-identity]
:as contact} [:contact]
chat-id [:get :current-chat-id]]
[view st/profile
[status-bar]
[profile-toolbar contact]
[scroll-view
[view st/profile-form
[profile-badge contact]
(when (and (not (nil? status)) (not (str/blank? status)))
[profile-status status])]
[form-spacer]
[profile-actions contact chat-id]
[form-spacer]
[view st/profile-info-container
[profile-info contact]
[bottom-shadow]]]])

View File

@ -1,5 +0,0 @@
(ns status-im.profile.specs
(:require [cljs.spec.alpha :as s]))
;EDIT PROFILE
(s/def :profile/profile-edit (s/nilable map?))

View File

@ -1,25 +0,0 @@
(ns status-im.profile.validations
(:require [cljs.spec.alpha :as s]
[status-im.constants :refer [console-chat-id wallet-chat-id]]
[status-im.chat.constants :as chat-consts]
[clojure.string :as str]
[status-im.utils.homoglyph :as h]))
(defn correct-name? [username]
(when-let [username (some-> username (str/trim))]
(every? false?
[(str/blank? username)
(h/matches username console-chat-id)
(h/matches username wallet-chat-id)
(str/includes? username chat-consts/command-char)
(str/includes? username chat-consts/bot-char)])))
(defn correct-email? [email]
(let [pattern #"[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?"]
(or (str/blank? email)
(and (string? email) (re-matches pattern email)))))
(s/def ::name correct-name?)
(s/def ::email correct-email?)
(s/def ::profile (s/keys :req-un [::name]))

View File

@ -9,7 +9,7 @@
status-im.ui.screens.group.db status-im.ui.screens.group.db
status-im.chat.specs status-im.chat.specs
status-im.chat.new-public-chat.db status-im.chat.new-public-chat.db
status-im.profile.specs status-im.ui.screens.profile.db
status-im.transactions.specs status-im.transactions.specs
status-im.ui.screens.discover.db)) status-im.ui.screens.discover.db))
@ -38,7 +38,6 @@
:sync-state :done :sync-state :done
:network :testnet}) :network :testnet})
;;;;GLOBAL ;;;;GLOBAL
;;public key of current logged in account ;;public key of current logged in account

View File

@ -1,39 +1,37 @@
(ns status-im.ui.screens.events (ns status-im.ui.screens.events
(:require (:require status-im.bots.handlers
status-im.chat.handlers status-im.chat.handlers
status-im.ui.screens.group.chat-settings.events
status-im.ui.screens.navigation
status-im.ui.screens.contacts.events
status-im.ui.screens.discover.events
status-im.ui.screens.group.events
status-im.profile.handlers
status-im.commands.handlers.loading
status-im.commands.handlers.jail status-im.commands.handlers.jail
status-im.ui.screens.qr-scanner.events status-im.commands.handlers.loading
status-im.ui.screens.accounts.events status-im.debug.handlers
status-im.network.handlers
status-im.protocol.handlers status-im.protocol.handlers
status-im.transactions.handlers status-im.transactions.handlers
status-im.network.handlers status-im.ui.screens.accounts.events
status-im.debug.handlers status-im.ui.screens.contacts.events
status-im.bots.handlers status-im.ui.screens.discover.events
status-im.ui.screens.group.chat-settings.events
status-im.ui.screens.group.events
status-im.ui.screens.navigation
status-im.ui.screens.profile.events
status-im.ui.screens.qr-scanner.events
[re-frame.core :refer [dispatch reg-fx]] [re-frame.core :refer [dispatch reg-fx]]
[status-im.utils.handlers :refer [register-handler-db register-handler-fx]]
[status-im.ui.screens.db :refer [app-db]]
[status-im.data-store.core :as data-store]
[taoensso.timbre :as log]
[status-im.utils.crypt :as crypt]
[status-im.components.status :as status] [status-im.components.status :as status]
[status-im.components.permissions :as permissions] [status-im.components.permissions :as permissions]
[status-im.utils.types :as types]
[status-im.constants :refer [console-chat-id]] [status-im.constants :refer [console-chat-id]]
[status-im.data-store.core :as data-store]
[status-im.i18n :as i18n]
[status-im.js-dependencies :as dependencies]
[status-im.ui.screens.db :refer [app-db]]
[status-im.utils.config :as config]
[status-im.utils.crypt :as crypt]
[status-im.utils.handlers :refer [register-handler-db register-handler-fx]]
[status-im.utils.instabug :as inst] [status-im.utils.instabug :as inst]
[status-im.utils.platform :as platform] [status-im.utils.platform :as platform]
[status-im.js-dependencies :as dependencies] [status-im.utils.types :as types]
[status-im.utils.utils :as utils] [status-im.utils.utils :as utils]
[status-im.utils.config :as config] [taoensso.timbre :as log]))
[status-im.i18n :as i18n]))
;;;; COFX ;;;; COFX

View File

@ -0,0 +1,28 @@
(ns status-im.ui.screens.profile.db
(:require [cljs.spec.alpha :as spec]
[clojure.string :as string]
[status-im.chat.constants :as chat.constants]
[status-im.constants :as constants]
[status-im.utils.homoglyph :as homoglyph]))
(defn correct-name? [username]
(when-let [username (some-> username (string/trim))]
(every? false?
[(string/blank? username)
(homoglyph/matches username constants/console-chat-id)
(homoglyph/matches username constants/wallet-chat-id)
(string/includes? username chat.constants/command-char)
(string/includes? username chat.constants/bot-char)])))
(defn correct-email? [email]
(let [pattern #"[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?"]
(or (string/blank? email)
(and (string? email) (re-matches pattern email)))))
(spec/def ::name correct-name?)
(spec/def ::email correct-email?)
(spec/def ::profile (spec/keys :req-un [::name]))
;; EDIT PROFILE
(spec/def :profile/profile-edit (spec/nilable map?))

View File

@ -0,0 +1,106 @@
(ns status-im.ui.screens.profile.edit.views
(:require [cljs.spec.alpha :as spec]
[clojure.string :as string]
[re-frame.core :refer [dispatch]]
[reagent.core :as reagent]
[status-im.components.camera :as camera]
[status-im.components.chat-icon.screen :refer [my-profile-icon]]
[status-im.components.context-menu :refer [context-menu]]
[status-im.components.react :as react]
[status-im.components.status-bar :refer [status-bar]]
[status-im.components.sticky-button :refer [sticky-button]]
[status-im.components.text-input-with-label.view :refer [text-input-with-label]]
[status-im.components.toolbar-new.view :refer [toolbar]]
[status-im.i18n :refer [label]]
[status-im.ui.screens.profile.db :as db]
[status-im.ui.screens.profile.events :as profile.events]
[status-im.ui.screens.profile.styles :as styles]
[status-im.ui.screens.profile.views :refer [colorize-status-hashtags]]
[status-im.utils.utils :as utils :refer [clean-text]])
(:require-macros [status-im.utils.views :refer [defview letsubs]]))
(defn edit-my-profile-toolbar []
[toolbar {:title (label :t/edit-profile)
:actions [{:image :blank}]}])
(defview profile-name-input []
(letsubs [new-profile-name [:get-in [:profile-edit :name]]]
[react/view
[text-input-with-label {:label (label :t/name)
:default-value new-profile-name
:on-change-text #(dispatch [:set-in [:profile-edit :name] %])}]]))
(def profile-icon-options
[{:text (label :t/image-source-gallery)
:value #(dispatch [:open-image-picker])}
{:text (label :t/image-source-make-photo)
:value (fn []
(dispatch [:request-permissions
[:camera :write-external-storage]
(fn []
(camera/request-access
#(if % (dispatch [:navigate-to :profile-photo-capture])
(utils/show-popup (label :t/error)
(label :t/camera-access-error)))))]))}])
(defn edit-profile-bage [contact]
[react/view styles/edit-profile-bage
[react/view styles/edit-profile-icon-container
[context-menu
[my-profile-icon {:account contact
:edit? true}]
profile-icon-options
styles/context-menu-custom-styles]]
[react/view styles/edit-profile-name-container
[profile-name-input]]])
(defn edit-profile-status [{:keys [status edit-status?]}]
(let [input-ref (reagent/atom nil)]
[react/view styles/edit-profile-status
[react/scroll-view
(if edit-status?
[react/text-input
{:ref #(reset! input-ref %)
:auto-focus edit-status?
:multiline true
:max-length 140
:placeholder (label :t/status)
:style styles/profile-status-input
:on-change-text #(dispatch [:set-in [:profile-edit :status] (clean-text %)])
:on-blur #(dispatch [:set-in [:profile-edit :edit-status?] false])
:blur-on-submit true
:on-submit-editing #(.blur @input-ref)
:default-value status}]
[react/touchable-highlight {:on-press #(dispatch [:set-in [:profile-edit :edit-status?] true])}
[react/view
(if (string/blank? status)
[react/text {:style styles/add-a-status}
(label :t/status)]
[react/text {:style styles/profile-status-text}
(colorize-status-hashtags status)])]])]]))
(defn status-prompt [{:keys [status]}]
(when (or (nil? status) (string/blank? status))
[react/view styles/status-prompt
[react/text {:style styles/status-prompt-text}
(colorize-status-hashtags (label :t/status-prompt))]]))
(defview edit-my-profile []
(letsubs [current-account [:get-current-account]
changed-account [:get :profile-edit]]
{:component-will-unmount #(dispatch [:set-in [:profile-edit :edit-status?] false])}
(let [profile-edit-data-valid? (spec/valid? ::db/profile changed-account)
profile-edit-data-changed? (or (not= (:name current-account) (:name changed-account))
(not= (:status current-account) (:status changed-account))
(not= (:photo-path current-account) (:photo-path changed-account)))]
[react/keyboard-avoiding-view {:style styles/profile}
[status-bar]
[edit-my-profile-toolbar]
[react/view styles/edit-my-profile-form
[edit-profile-bage changed-account]
[edit-profile-status changed-account]
[status-prompt changed-account]]
(when (and profile-edit-data-changed? profile-edit-data-valid?)
[sticky-button (label :t/save) #(do
(dispatch [:check-status-change (:status changed-account)])
(dispatch [:account-update changed-account]))])])))

View File

@ -0,0 +1,58 @@
(ns status-im.ui.screens.profile.events
(:require [re-frame.core :as re-frame :refer [reg-fx]]
[status-im.components.react :refer [show-image-picker]]
[status-im.constants :refer [console-chat-id]]
[status-im.ui.screens.profile.navigation]
[status-im.utils.handlers :as handlers]
[status-im.utils.image-processing :refer [img->base64]]
[taoensso.timbre :as log]))
(defn message-user [identity]
(when identity
(re-frame/dispatch [:navigation-replace :chat identity])))
(handlers/register-handler
:open-image-picker
(handlers/side-effect!
(fn [_ _]
(show-image-picker
(fn [image]
(let [path (get (js->clj image) "path")
_ (log/debug path)
on-success (fn [base64]
(re-frame/dispatch [:set-in [:profile-edit :photo-path] (str "data:image/jpeg;base64," base64)]))
on-error (fn [type error]
(.log js/console type error))]
(img->base64 path on-success on-error)))))))
(handlers/register-handler
:phone-number-change-requested
;; 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.
(handlers/side-effect!
(fn [db _]
(re-frame/dispatch [:navigate-to :chat console-chat-id])
(js/setTimeout #(re-frame/dispatch [:select-chat-input-command {:name "phone"}]) 500))))
(handlers/register-handler
:open-chat-with-the-send-transaction
(handlers/side-effect!
(fn [db [_ chat-id]]
(re-frame/dispatch [:clear-seq-arguments])
(re-frame/dispatch [:navigate-to :chat chat-id])
(js/setTimeout #(re-frame/dispatch [:select-chat-input-command {:name "send"}]) 500))))
(defn prepare-edit-profile
[{:keys [current-account-id] :as db} _]
(let [current-account (select-keys (get-in db [:accounts current-account-id])
[:name :photo-path :status])]
(update-in db [:profile-edit] merge current-account)))
(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

@ -0,0 +1,9 @@
(ns status-im.ui.screens.profile.navigation
(:require [status-im.ui.screens.navigation :as navigation]))
(defmethod navigation/preload-data! :qr-code-view
[{:keys [current-account-id] :as db} [_ _ {:keys [contact qr-source amount?]}]]
(assoc db :qr-modal {:contact (or contact
(get-in db [:accounts current-account-id]))
:qr-source qr-source
:amount? amount?}))

View File

@ -0,0 +1,5 @@
(ns status-im.ui.screens.profile.photo-capture.styles)
(def container
{:flex 1
:background-color :white})

View File

@ -1,23 +1,16 @@
(ns status-im.profile.photo-capture.screen (ns status-im.ui.screens.profile.photo-capture.views
(:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] (:require [re-frame.core :refer [dispatch]]
[clojure.walk :refer [keywordize-keys]]
[status-im.components.react :refer [view
text
image
touchable-highlight]]
[status-im.components.camera :refer [camera
aspects
capture-targets]]
[status-im.components.styles :refer [icon-back]]
[status-im.components.icons.custom-icons :refer [ion-icon]]
[status-im.components.status-bar :refer [status-bar]]
[status-im.components.toolbar.view :refer [toolbar]]
[status-im.components.toolbar.actions :as act]
[status-im.components.toolbar.styles :refer [toolbar-background1]]
[status-im.utils.image-processing :refer [img->base64]]
[status-im.profile.photo-capture.styles :as st]
[status-im.i18n :refer [label]]
[reagent.core :as r] [reagent.core :as r]
[status-im.components.camera :refer [aspects camera capture-targets]]
[status-im.components.icons.custom-icons :refer [ion-icon]]
[status-im.components.react :as react]
[status-im.components.status-bar :refer [status-bar]]
[status-im.components.toolbar.actions :as actions]
[status-im.components.toolbar.styles :refer [toolbar-background1]]
[status-im.components.toolbar.view :refer [toolbar]]
[status-im.i18n :refer [label]]
[status-im.ui.screens.profile.photo-capture.styles :as styles]
[status-im.utils.image-processing :refer [img->base64]]
[taoensso.timbre :as log])) [taoensso.timbre :as log]))
(defn image-captured [data] (defn image-captured [data]
@ -33,10 +26,10 @@
(defn profile-photo-capture [] (defn profile-photo-capture []
(let [camera-ref (r/atom nil)] (let [camera-ref (r/atom nil)]
[view st/container [react/view styles/container
[status-bar] [status-bar]
[toolbar {:title (label :t/image-source-title) [toolbar {:title (label :t/image-source-title)
:nav-action (act/back #(dispatch [:navigate-back])) :nav-action (actions/back #(dispatch [:navigate-back]))
:background-color toolbar-background1}] :background-color toolbar-background1}]
[camera {:style {:flex 1} [camera {:style {:flex 1}
:aspect (:fill aspects) :aspect (:fill aspects)
@ -44,14 +37,14 @@
:captureTarget (:disk capture-targets) :captureTarget (:disk capture-targets)
:type "front" :type "front"
:ref #(reset! camera-ref %)}] :ref #(reset! camera-ref %)}]
[view {:style {:padding 10 [react/view {:style {:padding 10
:background-color toolbar-background1}} :background-color toolbar-background1}}
[touchable-highlight {:style {:align-self "center"} [react/touchable-highlight {:style {:align-self "center"}
:on-press (fn [] :on-press (fn []
(let [camera @camera-ref] (let [camera @camera-ref]
(-> (.capture camera) (-> (.capture camera)
(.then image-captured) (.then image-captured)
(.catch #(log/debug "Error capturing image: " %)))))} (.catch #(log/debug "Error capturing image: " %)))))}
[view [react/view
[ion-icon {:name :md-camera [ion-icon {:name :md-camera
:style {:font-size 36}}]]]]])) :style {:font-size 36}}]]]]]))

View File

@ -1,6 +1,5 @@
(ns status-im.profile.qr-code.styles (ns status-im.ui.screens.profile.qr-code.styles
(:require [status-im.components.styles :refer [color-white]] (:require [status-im.components.styles :refer [color-white]]))
[status-im.components.react :as r]))
(def photo-container (def photo-container
{:flex 0.2 {:flex 0.2

View File

@ -0,0 +1,50 @@
(ns status-im.ui.screens.profile.qr-code.views
(:require [clojure.string :as string]
[re-frame.core :refer [dispatch]]
[status-im.components.qr-code :refer [qr-code]]
[status-im.components.react :as react]
[status-im.components.status-bar :refer [status-bar]]
[status-im.i18n :refer [label]]
[status-im.ui.screens.profile.qr-code.styles :as styles])
(:require-macros [status-im.utils.views :refer [defview letsubs]]))
(defview qr-code-view []
(letsubs [{:keys [photo-path address name] :as contact} [:get-in [:qr-modal :contact]]
{:keys [qr-source amount? dimensions]} [:get :qr-modal]
{:keys [amount]} [:get :contacts/click-params]]
[react/view styles/wallet-qr-code
[status-bar {:type :modal}]
[react/view styles/account-toolbar
[react/view styles/wallet-account-container
[react/view styles/qr-photo-container
[react/view styles/account-photo-container
[react/image {:source {:uri (if (string/blank? photo-path) :avatar photo-path)}
:style styles/photo-image}]]]
[react/view styles/name-container
[react/text {:style styles/name-text
:number-of-lines 1} name]]
[react/view styles/online-container
[react/touchable-highlight {:onPress #(dispatch [:navigate-back])}
[react/view styles/online-image-container
[react/icon :close_white]]]]]]
[react/view {:style styles/qr-code
:on-layout #(let [layout (.. % -nativeEvent -layout)]
(dispatch [:set-in [:qr-modal :dimensions] {:width (.-width layout)
:height (.-height layout)}]))}
(when (:width dimensions)
[react/view {:style (styles/qr-code-container dimensions)}
[qr-code {:value (if amount?
(prn-str {:address (get contact qr-source)
:amount amount})
(str "ethereum:" (get contact qr-source)))
:size (- (min (:width dimensions)
(:height dimensions))
80)}]])]
[react/view styles/footer
[react/view styles/wallet-info
[react/text {:style styles/wallet-name-text} (label :t/main-wallet)]
[react/text {:style styles/wallet-address-text} address]]
[react/touchable-highlight {:onPress #(dispatch [:navigate-back])}
[react/view styles/done-button
[react/text {:style styles/done-button-text} (label :t/done)]]]]]))

View File

@ -1,14 +1,15 @@
(ns status-im.profile.styles (ns status-im.ui.screens.profile.styles
(:require-macros [status-im.utils.styles :refer [defstyle]]) (:require [status-im.components.styles
(:require [status-im.components.styles :refer [color-white :refer
color-black [color-black
color-gray4 color-gray4
color-gray5 color-gray5
color-light-gray
color-light-blue color-light-blue
color-light-blue-transparent color-light-gray
color-white
text1-color]] text1-color]]
[status-im.utils.platform :as p])) [status-im.utils.platform :as platform])
(:require-macros [status-im.utils.styles :refer [defstyle]]))
(def profile (def profile
{:flex 1 {:flex 1
@ -129,7 +130,7 @@
{:margin-left 16}) {:margin-left 16})
(def edit-line-color (def edit-line-color
(if p/ios? (if platform/ios?
(str color-gray5 "80") (str color-gray5 "80")
color-gray5)) color-gray5))
@ -137,7 +138,7 @@
color-light-blue) color-light-blue)
(def profile-focus-line-height (def profile-focus-line-height
(get-in p/platform-specific [:component-styles :text-field-focus-line-height])) (get-in platform/platform-specific [:component-styles :text-field-focus-line-height]))
(defstyle profile-name-input (defstyle profile-name-input
{:color text1-color {:color text1-color
@ -189,4 +190,3 @@
(def add-a-status (def add-a-status
(merge profile-status-text (merge profile-status-text
{:color color-gray4})) {:color color-gray4}))

View File

@ -0,0 +1,221 @@
(ns status-im.ui.screens.profile.views
(:require [clojure.string :as string]
[re-frame.core :refer [dispatch]]
[status-im.components.action-button.action-button
:refer
[action-button action-button-disabled action-separator]]
[status-im.components.action-button.styles :refer [actions-list]]
[status-im.components.chat-icon.screen :refer [my-profile-icon]]
[status-im.components.common.common
:refer
[bottom-shadow form-spacer separator]]
[status-im.components.context-menu :refer [context-menu]]
[status-im.components.list-selection :refer [share-options]]
[status-im.components.react :as react]
[status-im.components.status-bar :refer [status-bar]]
[status-im.components.styles :refer [color-blue]]
[status-im.components.toolbar-new.actions :as actions]
[status-im.components.toolbar-new.view :refer [toolbar]]
[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.utils.datetime :as time]
[status-im.utils.utils :refer [hash-tag?]])
(:require-macros [status-im.utils.views :refer [defview]]))
(defn my-profile-toolbar []
[toolbar {:actions [(actions/opts [{:value #(dispatch [:open-edit-my-profile])
:text (label :t/edit)}])]}])
(defn profile-toolbar [contact]
[toolbar
(when (and (not (:pending? contact))
(not (:unremovable? contact)))
{:actions [(actions/opts [{:value #(dispatch [:hide-contact contact])
:text (label :t/remove-from-contacts)}])]})])
(defn online-text [last-online]
(let [last-online-date (time/to-date last-online)
now-date (time/now)]
(if (and (pos? last-online)
(<= last-online-date now-date))
(time/time-ago last-online-date)
(label :t/active-unknown))))
(defn profile-badge [{:keys [name last-online] :as contact}]
[react/view styles/profile-bage
[my-profile-icon {:account contact
:edit? false}]
[react/view styles/profile-badge-name-container
[react/text {:style styles/profile-name-text
:number-of-lines 1}
name]
(when-not (nil? last-online)
[react/view styles/profile-activity-status-container
[react/text {:style styles/profile-activity-status-text}
(online-text last-online)]])]])
(defn profile-actions [{:keys [pending? whisper-identity dapp?]} chat-id]
[react/view actions-list
(if pending?
[action-button (label :t/add-to-contacts)
:add_blue
#(dispatch [:add-pending-contact chat-id])]
[action-button-disabled (label :t/in-contacts)
:ok_dark])
[action-separator]
[action-button (label :t/start-conversation)
:chats_blue
#(message-user whisper-identity)]
(when-not dapp?
[react/view
[action-separator]
[action-button (label :t/send-transaction)
:arrow_right_blue
#(dispatch [:open-chat-with-the-send-transaction chat-id])]])])
(defn profile-info-item [{:keys [label value options text-mode empty-value?]}]
[react/view styles/profile-setting-item
[react/view (styles/profile-info-text-container options)
[react/text {:style styles/profile-setting-title}
label]
[react/view styles/profile-setting-spacing]
[react/text {:style (if empty-value?
styles/profile-setting-text-empty
styles/profile-setting-text)
:number-of-lines 1
:ellipsizeMode text-mode}
value]]
(when options
[context-menu
[react/icon :options_gray]
options
nil
styles/profile-info-item-button])])
(defn show-qr [contact qr-source]
#(dispatch [:navigate-to-modal :qr-code-view {:contact contact
:qr-source qr-source}]))
(defn profile-options [contact k text]
(into []
(concat [{:value (show-qr contact k)
:text (label :t/show-qr)}]
(when text
(share-options text)))))
(defn profile-info-address-item [{:keys [address] :as contact}]
[profile-info-item
{:label (label :t/address)
:value address
:options (profile-options contact :address address)
:text-mode :middle}])
(defn profile-info-public-key-item [public-key contact]
[profile-info-item
{:label (label :t/public-key)
:value public-key
:options (profile-options contact :public-key public-key)
:text-mode :middle}])
(defn info-item-separator []
[separator styles/info-item-separator])
(defn tag-view [tag]
[react/text {:style {:color color-blue}
:font :medium}
(str tag " ")])
(defn colorize-status-hashtags [status]
(for [[i status] (map-indexed vector (string/split status #" "))]
(if (hash-tag? status)
^{:key (str "item-" i)}
[tag-view status]
^{:key (str "item-" i)}
(str status " "))))
(defn profile-info-phone-item [phone & [options]]
(let [phone-empty? (or (nil? phone) (string/blank? phone))
phone-text (if phone-empty?
(label :t/not-specified)
phone)]
[profile-info-item {:label (label :t/phone-number)
:value phone-text
:options options
:empty-value? phone-empty?}]))
(defn profile-info [{:keys [whisper-identity status phone] :as contact}]
[react/view
[profile-info-address-item contact]
[info-item-separator]
[profile-info-public-key-item whisper-identity contact]
[info-item-separator]
[profile-info-phone-item phone]])
(defn my-profile-info [{:keys [public-key status phone] :as contact}]
[react/view
[profile-info-address-item contact]
[info-item-separator]
[profile-info-public-key-item public-key contact]
[info-item-separator]
[profile-info-phone-item
phone
[{:value #(dispatch [:phone-number-change-requested])
: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?]]
[react/view styles/profile-status-container
(if (or (nil? status) (string/blank? status))
[react/touchable-highlight {:on-press profile-status-on-press}
[react/view
[react/text {:style styles/add-a-status}
(label :t/add-a-status)]]]
[react/scroll-view
[react/touchable-highlight {:on-press (when edit? profile-status-on-press)}
[react/view
[react/text {:style styles/profile-status-text}
(colorize-status-hashtags status)]]]])])
(defview my-profile []
(letsubs [{:keys [status] :as current-account} [:get-current-account]]
[react/view styles/profile
[status-bar]
[my-profile-toolbar]
[react/scroll-view
[react/view styles/profile-form
[profile-badge current-account]
[profile-status status true]]
[form-spacer]
[react/view actions-list
[action-button (label :t/show-qr)
:q_r_blue
(show-qr current-account :public-key)]]
[form-spacer]
[react/view styles/profile-info-container
[my-profile-info current-account]
[bottom-shadow]]]]))
(defview profile []
(letsubs [{:keys [pending?
status
whisper-identity]
:as contact} [:contact]
chat-id [:get :current-chat-id]]
[react/view styles/profile
[status-bar]
[profile-toolbar contact]
[react/scroll-view
[react/view styles/profile-form
[profile-badge contact]
(when (and (not (nil? status)) (not (string/blank? status)))
[profile-status status])]
[form-spacer]
[profile-actions contact chat-id]
[form-spacer]
[react/view styles/profile-info-container
[profile-info contact]
[bottom-shadow]]]]))

View File

@ -37,10 +37,10 @@
add-participants-toggle-list]] add-participants-toggle-list]]
[status-im.ui.screens.group.reorder.views :refer [reorder-groups]] [status-im.ui.screens.group.reorder.views :refer [reorder-groups]]
[status-im.profile.screen :refer [profile my-profile]] [status-im.ui.screens.profile.views :refer [profile my-profile]]
[status-im.profile.edit.screen :refer [edit-my-profile]] [status-im.ui.screens.profile.edit.views :refer [edit-my-profile]]
[status-im.profile.photo-capture.screen :refer [profile-photo-capture]] [status-im.ui.screens.profile.photo-capture.views :refer [profile-photo-capture]]
[status-im.profile.qr-code.screen :refer [qr-code-view]] [status-im.ui.screens.profile.qr-code.views :refer [qr-code-view]]
[status-im.ui.screens.wallet.send.views :refer [send-transaction]])) [status-im.ui.screens.wallet.send.views :refer [send-transaction]]))
;;[status-im.ui.screens.wallet.receive.views :refer [receive-transaction]] ;;[status-im.ui.screens.wallet.receive.views :refer [receive-transaction]]

View File

@ -1,9 +1,9 @@
(ns status-im.utils.handlers (ns status-im.utils.handlers
(:require [re-frame.core :refer [reg-event-db reg-event-fx]] (:require [cljs.spec.alpha :as spec]
[clojure.string :as string]
[re-frame.core :refer [reg-event-db reg-event-fx]]
[re-frame.interceptor :refer [->interceptor get-coeffect get-effect]] [re-frame.interceptor :refer [->interceptor get-coeffect get-effect]]
[clojure.string :as str] [taoensso.timbre :as log])
[taoensso.timbre :as log]
[cljs.spec.alpha :as s])
(:require-macros status-im.utils.handlers)) (:require-macros status-im.utils.handlers))
(defn side-effect! (defn side-effect!
@ -31,8 +31,8 @@
[context] [context]
(let [new-db (get-effect context :db) (let [new-db (get-effect context :db)
v (get-coeffect context :event)] v (get-coeffect context :event)]
(when (and new-db (not (s/valid? :status-im.ui.screens.db/db new-db))) (when (and new-db (not (spec/valid? :status-im.ui.screens.db/db new-db)))
(throw (ex-info (str "spec check failed on: " (first v) "\n " (s/explain-str :status-im.ui.screens.db/db new-db)) {}))) (throw (ex-info (str "spec check failed on: " (first v) "\n " (spec/explain-str :status-im.ui.screens.db/db new-db)) {})))
context)))) context))))
(defn register-handler (defn register-handler
@ -52,7 +52,7 @@
(defn get-hashtags [status] (defn get-hashtags [status]
(if status (if status
(let [hashtags (map #(str/lower-case (subs % 1)) (let [hashtags (map #(string/lower-case (subs % 1))
(re-seq #"#[^ !?,;:.]+" status))] (re-seq #"#[^ !?,;:.]+" status))]
(set (or hashtags []))) (set (or hashtags [])))
#{})) #{}))