feature #3011 - reworked group chat profile and refactored common profile elements

Signed-off-by: Goran Jovic <goranjovic@gmail.com>
This commit is contained in:
Goran Jovic 2018-02-18 11:55:50 +01:00
parent 80b1edfff6
commit b5b3ad204d
No known key found for this signature in database
GPG Key ID: A778DE3CD7D2D10D
36 changed files with 778 additions and 934 deletions

View File

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path fill="#000000" d="M7.00001034,5 C7.004297,3.34182361 8.34093668,2 10,2 L14,2 C15.6555111,2 16.9973018,3.34290405 16.9999959,5 L18.5454545,5 C19.9047426,5 21,6.12948418 21,7.5 C21,8.87051582 19.9047426,10 18.5454545,10 L5.45454545,10 C4.09525738,10 3,8.87051582 3,7.5 C3,6.12948418 4.09525738,5 5.45454545,5 L7.0000096,5 Z M14.9999879,5 C14.9973085,4.44655807 14.5500233,4 14,4 L10,4 C9.44811686,4 9.00424986,4.44383154 9.00003034,5 L14.9999888,5 Z M12,13.5857864 L13.2928932,12.2928932 C13.6834175,11.9023689 14.3165825,11.9023689 14.7071068,12.2928932 C15.0976311,12.6834175 15.0976311,13.3165825 14.7071068,13.7071068 L13.4142136,15 L14.7071068,16.2928932 C15.0976311,16.6834175 15.0976311,17.3165825 14.7071068,17.7071068 C14.3165825,18.0976311 13.6834175,18.0976311 13.2928932,17.7071068 L12,16.4142136 L10.7071068,17.7071068 C10.3165825,18.0976311 9.68341751,18.0976311 9.29289322,17.7071068 C8.90236893,17.3165825 8.90236893,16.6834175 9.29289322,16.2928932 L10.5857864,15 L9.29289322,13.7071068 C8.90236893,13.3165825 8.90236893,12.6834175 9.29289322,12.2928932 C9.68341751,11.9023689 10.3165825,11.9023689 10.7071068,12.2928932 L12,13.5857864 Z M17,12 C17,11.4477153 17.4477153,11 18,11 C18.5522847,11 19,11.4477153 19,12 L19,19.998 C19,21.604 17.953,23 16.4985,23 L7.5,23 C6.047,23 5,21.604 5,20 L5,12 C5,11.4477153 5.44771525,11 6,11 C6.55228475,11 7,11.4477153 7,12 L7,20 C7,20.604 7.297,21 7.5,21 L16.4985,21 C16.703,21 17,20.604 17,19.998 L17,12 Z M18.5454545,8 C18.7861665,8 19,7.77948418 19,7.5 C19,7.22051582 18.7861665,7 18.5454545,7 L5.45454545,7 C5.21383352,7 5,7.22051582 5,7.5 C5,7.77948418 5.21383352,8 5.45454545,8 L18.5454545,8 Z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -21,6 +21,7 @@
:error "Error"
:unknown-status-go-error "Unknown status-go error"
:node-unavailable "No ethereum node running"
:add "Add"
:yes "Yes"
:no "No"
@ -212,12 +213,20 @@
:leave-confirmation "Leave?"
:clear "Clear"
:clear-history "Clear history"
:clear-history-title "Clear history?"
:clear-group-history-confirmation "Are you sure you want to clear this group's chat history?"
:clear-history-confirmation "Clear history?"
:clear-group-history-confirmation "Are you sure you want to clear history in this group chat?"
:clear-history-action "Clear"
:mute-notifications "Mute notifications"
:leave-group-action "Leave"
:leave-group-title "Leave?"
:leave-group-confirmation "Are you sure you want to leave this group?"
:leave-chat "Leave chat"
:leave-group-chat-confirmation "Are you sure you want to leave this group?"
:leave-group-chat "Leave group chat"
:leave-group "Leave group"
:remove-from-chat "Remove from chat"
:delete-chat-title "Delete chat?"
:leave-public-chat "Leave public chat"
:chat-settings "Chat settings"
:edit "Edit"

View File

@ -150,7 +150,7 @@
:online-dot-right styles/online-dot-right-profile
:size size
:chat-icon styles/chat-icon-profile
:default-chat-icon (styles/default-chat-icon-profile color)
:default-chat-icon (styles/default-chat-icon-profile color size)
:default-chat-icon-text styles/default-chat-icon-text}]
[react/view (:container styles)
(when edit?

View File

@ -17,11 +17,11 @@
:height 40
:border-radius 20}))
(defn default-chat-icon-profile [color]
(defn default-chat-icon-profile [color size]
(merge (default-chat-icon color)
{:width 64
:height 64
:border-radius 32}))
{:width size
:height size
:border-radius (/ size 2)}))
(defn default-chat-icon-view-action [color]
(merge (default-chat-icon color)

View File

@ -9,8 +9,8 @@
(def gray "#939ba1") ;; Used as a background for a light foreground and as section header and secondary text color
(def gray-light "#e8ebec") ;; Used as divider color
(def gray-lighter "#eef2f5") ;; Used as a background or shadow
(def blue "#4360df") ;; Used as main wallet color, and ios home add button
(def blue-transparent "rgba(67, 96, 223, 0.10)") ;; Used as shadow on ios home add button
(def blue "#4360df") ;; Used as main wallet color
(def blue-transparent "rgba(67, 96, 223, 0.10)") ;;used as shadow for blue elements
(def red "#ff2d55") ;; Used to highlight errors or "dangerous" actions
(def text-light-gray "#212121") ;; Used for labels (home items)

View File

@ -25,7 +25,7 @@
[react/text {:style styles/info-text}
info])]]))
(defn contact-view [{:keys [style contact extended? on-press extend-options info show-forward?]}]
(defn contact-view [{:keys [style contact extended? on-press extend-options extend-title info show-forward?]}]
[react/touchable-highlight (when-not extended?
{:on-press (when on-press #(on-press contact))})
[react/view styles/contact-container
@ -35,7 +35,8 @@
[vector-icons/icon :icons/forward]])
(when (and extended? (not (empty? extend-options)))
[react/view styles/more-btn-container
[react/touchable-highlight {:on-press #(list-selection/show {:options extend-options})}
[react/touchable-highlight {:on-press #(list-selection/show {:options extend-options
:title extend-title})}
[react/view styles/more-btn
[vector-icons/icon :icons/options {:accessibility-label :options}]]]])]])

View File

@ -1,8 +1,8 @@
(ns status-im.ui.components.contact.styles
(:require-macros [status-im.utils.styles :refer [defstyle]])
(:require [status-im.ui.components.colors :as colors]
[status-im.ui.components.styles :as common]
[status-im.utils.platform]))
(:require-macros [status-im.utils.styles :refer [defstyle defnstyle]])
(:require [status-im.ui.components.styles :as common]
[status-im.utils.platform]
[status-im.ui.components.colors :as colors]))
(defstyle contact-inner-container
{:flex 1
@ -48,3 +48,13 @@
(def more-btn
{:padding 16})
(def toggle-container
{:width 56
:height 56
:alignItems :center
:justifyContent :center})
(def check-icon
{:width 16
:height 16})

View File

@ -47,6 +47,7 @@
:icons/browse (slurp/slurp-svg "./resources/icons/browse.svg")
:icons/close (slurp/slurp-svg "./resources/icons/close.svg")
:icons/copy-from (slurp/slurp-svg "./resources/icons/copy_from.svg")
:icons/delete (slurp/slurp-svg "./resources/icons/delete.svg")
:icons/dots-horizontal (slurp/slurp-svg "./resources/icons/dots_horizontal.svg")
:icons/dots-vertical (slurp/slurp-svg "./resources/icons/dots_vertical.svg")
:icons/exclamation_mark (slurp/slurp-svg "./resources/icons/exclamation_mark.svg")

View File

@ -44,7 +44,9 @@
(def icon-wrapper-size (+ icon-size (* 2 8)))
(def item-icon-wrapper
{:align-items :center
{:width icon-wrapper-size
:height icon-wrapper-size
:align-items :center
:justify-content :center})
(def item-icon
@ -95,6 +97,21 @@
:ios {:margin-top 10
:margin-bottom 2}})
(def action-list
{:background-color colors/blue})
(def action
{:background-color colors/white-transparent
:border-radius 50})
(def action-label
{:color colors/white})
(def action-separator
{:height 1
:background-color colors/white-light-transparent
:margin-left 70})
(def list-with-label-wrapper
{:margin-top 26
:margin-horizontal 16})

View File

@ -170,6 +170,28 @@
:renderSectionHeader (wrap-render-section-header-fn render-section-header-fn)}
(when platform/ios? {:SectionSeparatorComponent (fn [] (reagent/as-element section-separator))}))])
(defn- render-action [{:keys [label icon action]}
{:keys [action-style action-label-style icon-opts]}]
[react/touchable-highlight {:on-press action}
[react/view
[item
[item-icon {:icon icon
:style (merge styles/action action-style)
:icon-opts (merge {:color :white} icon-opts)}]
[item-primary-only {:style (merge styles/action-label action-label-style)}
label]
item-icon-forward]]])
(defn action-list [actions {:keys [container-style action-separator-style] :as styles}]
[react/view (merge styles/action-list container-style)
[flat-list
{:separator (when platform/ios?
[react/view (merge styles/action-separator
action-separator-style)])
:data actions
:render-fn #(render-action % styles)}]])
(defn list-with-label [{:keys [style]} label list]
[react/view (merge styles/list-with-label-wrapper style)
[react/text {:style styles/label}

View File

@ -74,10 +74,11 @@
;; Actions
(defn text-action [{:keys [style handler disabled?]} title]
[react/text {:style (merge styles/item styles/item-text style
(when disabled? styles/toolbar-text-action-disabled))
:on-press (when-not disabled? handler)}
(defn text-action [{:keys [style handler disabled? uppercase?]} title]
[react/text {:style (merge styles/item styles/item-text style
(when disabled? styles/toolbar-text-action-disabled))
:on-press (when-not disabled? handler)
:uppercase? uppercase?}
title])
(def blank-action [react/view {:style (merge styles/item styles/toolbar-action)}])

View File

@ -148,6 +148,8 @@
:my-profile/profile
:my-profile/default-name
:my-profile/editing?
:group-chat-profile/profile
:group-chat-profile/editing?
:networks/selected-network
:networks/networks
:node/after-start

View File

@ -8,7 +8,8 @@
[status-im.ui.components.status-bar.view :refer [status-bar]]
[status-im.ui.components.toolbar.view :as toolbar]
[status-im.ui.screens.group.styles :as styles]
[status-im.ui.screens.contacts.styles :as contacts.styles]))
[status-im.ui.screens.contacts.styles :as contacts.styles]
[status-im.ui.components.styles :as components.styles]))
(defn- on-toggle [checked? whisper-identity]
(let [action (if checked? :deselect-contact :select-contact)]
@ -29,7 +30,9 @@
toolbar/default-nav-back
[toolbar/content-title title]
(when (pos? count)
[toolbar/text-action {:handler handler}
[toolbar/text-action {:handler handler
:uppercase? components.styles/uppercase?
:style styles/toggle-list-action}
label])])
(defn toggle-list [contacts render-function]
@ -67,9 +70,23 @@
(:name group)]
[toggle-list contacts group-toggle-contact]]))
(defn toggle-participants-handler []
(re-frame/dispatch [:add-new-group-chat-participants])
(re-frame/dispatch [:navigate-back]))
(defn add-participants-toggle-list-toolbar [selected-contacts-count]
[toolbar/toolbar {}
toolbar/default-nav-back
[toolbar/content-title (i18n/label :t/add-members)]
(when (pos? selected-contacts-count)
[toolbar/text-action {:handler toggle-participants-handler
:uppercase? components.styles/uppercase?
:style styles/toggle-list-action}
(i18n/label :t/add)])])
(defview add-participants-toggle-list []
(letsubs [contacts [:all-new-contacts]
chat-name [:chat :name]
(letsubs [contacts [:all-new-contacts]
chat-name [:chat :name]
selected-contacts-count [:selected-participants-count]]
[react/keyboard-avoiding-view {:style styles/group-container}
[status-bar]
@ -77,6 +94,6 @@
:handler #(do
(re-frame/dispatch [:add-new-group-chat-participants])
(re-frame/dispatch [:navigate-back]))
:label (i18n/label :t/save)}
:label (i18n/label :t/add)}
chat-name]
[toggle-list contacts group-toggle-participant]]))

View File

@ -135,7 +135,7 @@
(fn [{db :db} [_ chat-id]]
{:db (assoc db :new-chat-name (get-in db [:chats chat-id :name])
:group/group-type :chat-group)
:dispatch [:navigate-to :chat-group-settings]}))
:dispatch [:navigate-to :group-chat-profile]}))
(handlers/register-handler-fx
:add-new-group-chat-participants
@ -162,8 +162,8 @@
(select-keys db [:current-chat-id :contacts/contacts]))}))
(handlers/register-handler-fx
:set-chat-name
(fn [{{:keys [current-chat-id new-chat-name] :as db} :db} _]
:set-group-chat-name
(fn [{{:keys [current-chat-id] :as db} :db} [_ new-chat-name]]
{:db (assoc-in db [:chats current-chat-id :name] new-chat-name)
::save-chat-property [current-chat-id :name new-chat-name]}))

View File

@ -1,65 +0,0 @@
(ns status-im.ui.screens.group.chat-settings.views
(:require-macros [status-im.utils.views :refer [defview letsubs]])
(:require [re-frame.core :refer [dispatch]]
[status-im.ui.components.contact.contact :refer [contact-view]]
[status-im.ui.components.common.common :as common]
[status-im.ui.components.react :refer [view scroll-view keyboard-avoiding-view]]
[status-im.ui.components.sticky-button :refer [sticky-button]]
[status-im.ui.screens.group.styles :as styles]
[status-im.ui.screens.group.views :refer [group-toolbar group-chat-settings-btns
group-name-view add-btn more-btn]]
[status-im.ui.screens.group.db :as v]
[status-im.i18n :refer [label]]
[cljs.spec.alpha :as spec]))
(def ^:const contacts-limit 3)
(defview chat-group-contacts-view [admin?]
(letsubs [contacts [:current-chat-contacts]]
(let [limited-contacts (take contacts-limit contacts)
contacts-count (count contacts)]
[view
(when (and admin? (pos? contacts-count))
[common/list-separator])
[view
(doall
(map (fn [row]
^{:key row}
[view
[contact-view
{:contact row
:extend-options [{:action #(dispatch [:remove-group-chat-participants #{(:whisper-identity row)}])
:label (label :t/remove)}]
:extended? admin?}]
(when-not (= row (last limited-contacts))
[common/list-separator])])
limited-contacts))]
(when (< contacts-limit contacts-count)
[more-btn contacts-limit contacts-count #(dispatch [:navigate-to :edit-chat-group-contact-list])])])))
(defview chat-group-members []
(letsubs [current-pk [:get :current-public-key]
group-admin [:chat :group-admin]]
(let [admin? (= current-pk group-admin)]
[view
(when admin?
[add-btn #(dispatch [:navigate-to :add-participants-toggle-list])])
[chat-group-contacts-view admin?]])))
(defview chat-group-settings []
(letsubs [new-chat-name [:get :new-chat-name]
chat-name [:chat :name]
type [:get-group-type]]
(let [save-btn-enabled? (and (spec/valid? ::v/name new-chat-name)
(not= new-chat-name chat-name))]
[keyboard-avoiding-view {:style styles/group-container}
[view {:flex 1}
[group-toolbar type true]
[scroll-view
[group-name-view]
[chat-group-members]
[view styles/separator]
[group-chat-settings-btns]]]
(when save-btn-enabled?
[sticky-button (label :t/save) #(dispatch [:set-chat-name])
true])])))

View File

@ -34,20 +34,6 @@
:accessibility-label :remove-button
:label (i18n/label :t/remove-from-group)}]))
(defview edit-chat-group-contact-list []
(letsubs [chat-name [:chat :name]
contacts [:current-chat-contacts]
current-pk [:get :current-public-key]
group-admin [:chat :group-admin]]
(let [admin? (= current-pk group-admin)]
[react/view styles/group-container
[status-bar]
[toolbar/simple-toolbar chat-name]
[contacts-list
contacts
admin?
chat-extended-options]])))
(defview contacts-list-view [group-id]
(letsubs [contacts [:all-added-group-contacts group-id]]
[contacts-list

View File

@ -170,3 +170,9 @@
(def list-view-container
{:flex 1
:margin-top 10})
(defstyle toggle-list-action
{:font-size 14
:android {:font-weight "500"}})

View File

@ -1,25 +1,17 @@
(ns status-im.ui.screens.group.views
(:require-macros [status-im.utils.views :refer [defview letsubs]])
(:require-macros [status-im.utils.views :as views])
(:require [cljs.spec.alpha :as spec]
[re-frame.core :as re-frame]
[status-im.i18n :as i18n]
[status-im.ui.screens.contacts.styles :as cstyles]
[status-im.ui.components.common.common :as common]
[status-im.ui.components.action-button.action-button :refer [action-button action-separator]]
[status-im.ui.components.react :as react]
[status-im.ui.components.icons.vector-icons :as vi]
[status-im.ui.components.list.views :as list]
[status-im.ui.components.text-input-with-label.view :refer [text-input-with-label]]
[status-im.ui.components.status-bar.view :as status-bar]
[status-im.ui.components.styles :as components.styles]
[status-im.ui.components.toolbar.view :as toolbar]
[status-im.utils.platform :as platform :refer [platform-specific ios?]]
[status-im.ui.components.sticky-button :refer [sticky-button]]
[status-im.ui.components.contact.contact :refer [contact-view]]
[status-im.utils.platform :as platform]
[status-im.ui.components.contact.contact :as contact]
[status-im.ui.screens.add-new.styles :as add-new.styles]
[status-im.ui.screens.group.styles :as styles]
[status-im.ui.screens.group.db :as v]
[status-im.utils.utils :as utils]))
[status-im.ui.screens.group.db :as v]))
(defn group-toolbar [group-type edit?]
[react/view
@ -30,8 +22,8 @@
(if edit? :t/edit-group :t/new-group)
(if edit? :t/chat-settings :t/group-chat)))]])
(defview group-name-view []
(letsubs [new-group-name [:get :new-chat-name]]
(views/defview group-name-view []
(views/letsubs [new-group-name [:get :new-chat-name]]
[react/view add-new.styles/input-container
[react/text-input
{:auto-focus true
@ -40,107 +32,8 @@
:placeholder (i18n/label :t/set-a-topic)
:style add-new.styles/input}]]))
(defn add-btn [on-press]
[action-button {:label (i18n/label :t/add-members)
:icon :icons/add
:icon-opts {:color :blue}
:on-press on-press}])
(defn delete-btn [on-press]
[react/view styles/settings-group-container
[react/touchable-highlight {:on-press on-press}
[react/view styles/settings-group-item
[react/view styles/delete-icon-container
[vi/icon :icons/close {:color :red}]]
[react/view styles/settings-group-text-container
[react/text {:style styles/delete-group-text}
(i18n/label :t/delete-group)]
[react/text {:style styles/delete-group-prompt-text}
(i18n/label :t/delete-group-prompt)]]]]])
(defn group-chat-settings-btns []
[react/view styles/settings-group-container
[react/view {:opacity 0.4}
[react/touchable-highlight {:on-press #()}
[react/view styles/settings-group-item
[react/view styles/settings-icon-container
[vi/icon :icons/speaker {:color :blue}]]
[react/view styles/settings-group-text-container
[react/text {:style styles/settings-group-text}
(i18n/label :t/mute-notifications)]]]]]
[action-separator]
[action-button {:label (i18n/label :t/clear-history)
:icon :icons/close
:icon-opts {:color :blue}
:on-press #(re-frame/dispatch [:clear-history])}]
[action-separator]
[react/touchable-highlight {:on-press #(re-frame/dispatch [:leave-group-chat])}
[react/view styles/settings-group-item
[react/view styles/delete-icon-container
[vi/icon :icons/arrow-right {:color :red}]]
[react/view styles/settings-group-text-container
[react/text {:style styles/delete-group-text}
(i18n/label :t/leave-chat)]]]]])
(defn more-btn [contacts-limit contacts-count on-press]
[react/view
[common/list-separator]
[react/view cstyles/show-all
[react/touchable-highlight {:on-press on-press}
[react/view
[react/text {:style cstyles/show-all-text
:uppercase? (components.styles/uppercase?)
:font (if ios? :default :medium)}
(str (- contacts-count contacts-limit) " " (i18n/label :t/more))]]]]])
(def ^:const contacts-limit 3)
(defview group-contacts-view [group]
(letsubs [contacts [:all-added-group-contacts-with-limit (:group-id group) contacts-limit]
contacts-count [:all-added-group-contacts-count (:group-id group)]]
[react/view
(when (pos? contacts-count)
[common/list-separator])
[react/view
(doall
(map (fn [row]
^{:key row}
[react/view
[contact-view
{:contact row
:extend-options [{:action #(re-frame/dispatch [:remove-contact-from-group
(:whisper-identity row)
(:group-id group)])
:label (i18n/label :t/remove-from-group)}]
:extended? true}]
(when-not (= row (last contacts))
[common/list-separator])])
contacts))]
(when (< contacts-limit contacts-count)
[more-btn contacts-limit contacts-count #(re-frame/dispatch [:navigate-to :edit-group-contact-list])])]))
(defview edit-contact-group []
(letsubs [{:keys [group group-type]} [:get-screen-params]]
(let [group-name (:name group)
save-btn-enabled? (and (spec/valid? ::v/name group-name)
(not= group-name (:name group)))]
[react/keyboard-avoiding-view {:style styles/group-container}
[group-toolbar group-type true]
[group-name-view]
[react/view styles/list-view-container
[add-btn #(re-frame/dispatch [:navigate-to :add-contacts-toggle-list])]
[group-contacts-view group]
[react/view styles/separator]
[delete-btn #(utils/show-confirmation
(str (i18n/label :t/delete-group) "?") (i18n/label :t/delete-group-confirmation) (i18n/label :t/delete)
(fn[]
(re-frame/dispatch [:delete-contact-group])
(re-frame/dispatch [:navigate-to-clean :contact-list])))]]
(when save-btn-enabled?
[sticky-button (i18n/label :t/save) #(re-frame/dispatch [:set-contact-group-name])])])))
(defn- render-contact [contact]
[contact-view {:contact contact :style styles/contact}])
[contact/contact-view {:contact contact :style styles/contact}])
(defn- toolbar [group-name save-btn-enabled?]
[toolbar/toolbar {}
@ -155,9 +48,9 @@
[toolbar/text-action {:handler handler}
(i18n/label :t/create)])))])
(defview new-group []
(letsubs [contacts [:selected-group-contacts]
group-name [:get :new-chat-name]]
(views/defview new-group []
(views/letsubs [contacts [:selected-group-contacts]
group-name [:get :new-chat-name]]
(let [save-btn-enabled? (and (spec/valid? ::v/name group-name) (pos? (count contacts)))]
[react/keyboard-avoiding-view (merge {:behavior :padding}
styles/group-container)

View File

@ -7,9 +7,9 @@
[status-im.ui.components.status-bar.view :as status-bar.view]
[status-im.ui.components.styles :as common.styles]
[status-im.ui.screens.home.views :as home]
[status-im.ui.screens.profile.views :as profile]
[status-im.ui.screens.wallet.views :as wallet]
[status-im.ui.screens.main-tabs.styles :as styles]))
[status-im.ui.screens.main-tabs.styles :as styles]
[status-im.ui.screens.profile.user.views :as profile.user]))
(def tabs-list-data
[{:view-id :home
@ -75,7 +75,7 @@
{:enabled? (= :my-profile view-id)
:preview [react/view {}]}
[react/navigation-wrapper
{:component profile/my-profile
{:component profile.user/my-profile
:views :my-profile
:current-view view-id}]]
[tabs view-id]]]))

View File

@ -0,0 +1,89 @@
(ns status-im.ui.screens.profile.components.styles
(:require-macros [status-im.utils.styles :refer [defstyle]])
(:require [status-im.ui.components.styles :as styles]
[status-im.ui.components.colors :as colors]))
;; profile header elements
(def profile-header-display
{:flex-direction :column
:justify-content :center
:align-items :center})
(def profile-header-edit
{:flex-direction :column
:justify-content :center})
(defstyle profile-name-text
{:padding-vertical 14
:font-size 15
:text-align :center
:ios {:letter-spacing -0.2}
:android {:color colors/black}})
(defstyle profile-name-input-text
{:font-size 15
:text-align :center
:flex 1
:ios {:letter-spacing -0.2
:height 46
:border-bottom-width 1
:border-bottom-color colors/gray-light}
:android {:color colors/black
:border-bottom-width 2
:border-bottom-color colors/blue}})
(def profile-header-name-container
{:flex 1
:justify-content :center})
;; settings items elements
(def settings-item-separator
{:margin-left 16})
(defstyle settings-item
{:padding-horizontal 16
:flex-direction :row
:align-items :center
:background-color colors/white
:height 52})
(defstyle settings-item-text
{:flex 1
:flex-wrap :nowrap
:font-size 15
:ios {:letter-spacing -0.2}
:android {:color colors/black}})
(def settings-item-value
{:flex 2
:flex-wrap :nowrap
:text-align :right
:padding-right 10
:font-size 15
:color colors/gray})
(defstyle settings-title
{:color colors/gray
:margin-left 16
:margin-top 18
:font-size 14
:ios {:letter-spacing -0.2}})
;; shared profile styles
(def modal-menu
{:align-items :center})
(def profile
{:flex 1
:background-color colors/white
:flex-direction :column})
(def profile-form
{:background-color colors/white
:padding 16})
(defstyle profile-info-container
{:background-color colors/white})

View File

@ -0,0 +1,77 @@
(ns status-im.ui.screens.profile.components.views
(:require [status-im.ui.components.react :as react]
[re-frame.core :as re-frame]
[status-im.ui.components.list-selection :as list-selection]
[status-im.i18n :as i18n]
[status-im.ui.components.chat-icon.screen :as chat-icon.screen]
[status-im.ui.screens.profile.components.styles :as styles]
[status-im.ui.components.common.common :as common]
[status-im.ui.components.styles :as components.styles]
[status-im.ui.components.icons.vector-icons :as vector-icons]
[status-im.ui.components.colors :as colors]
[clojure.string :as string]))
;; profile header elements
(defn profile-name-input [name on-change-text-event]
[react/view
[react/text-input
{:style styles/profile-name-input-text
:placeholder ""
:default-value name
:auto-focus true
:on-change-text #(when on-change-text-event
(re-frame/dispatch [on-change-text-event %]))}]])
(defn show-profile-icon-actions [options]
(when (seq options)
(list-selection/show {:title (i18n/label :t/image-source-title)
:options options})))
(defn profile-header-display [{:keys [name] :as contact}]
[react/view styles/profile-header-display
[chat-icon.screen/my-profile-icon {:account contact
:edit? false}]
[react/view styles/profile-header-name-container
[react/text {:style styles/profile-name-text
:number-of-lines 1}
name]]])
(defn profile-header-edit [{:keys [name] :as contact}
icon-options on-change-text-event allow-icon-change?]
[react/view styles/profile-header-edit
[react/touchable-highlight {:on-press #(show-profile-icon-actions icon-options)}
[react/view styles/modal-menu
[chat-icon.screen/my-profile-icon {:account contact
:edit? allow-icon-change?}]]]
[react/view styles/profile-header-name-container
[profile-name-input name on-change-text-event]]])
(defn profile-header [contact editing? allow-icon-change? options on-change-text-event]
(if editing?
[profile-header-edit contact options on-change-text-event allow-icon-change?]
[profile-header-display contact]))
;; settings items elements
(defn settings-item-separator []
[common/separator styles/settings-item-separator])
(defn settings-title [title]
[react/text {:style styles/settings-title}
title])
(defn settings-item [label-kw value action-fn active?]
[react/touchable-highlight
{:on-press action-fn
:disabled (not active?)}
[react/view styles/settings-item
[react/text {:style styles/settings-item-text}
(i18n/label label-kw)]
(when-not (string/blank? value)
[react/text {:style styles/settings-item-value
:number-of-lines 1
:uppercase? components.styles/uppercase?}
value])
(when active?
[vector-icons/icon :icons/forward {:color colors/gray}])]])

View File

@ -0,0 +1,40 @@
(ns status-im.ui.screens.profile.contact.styles
(:require-macros [status-im.utils.styles :refer [defstyle]])
(:require [status-im.ui.components.colors :as colors]))
(def network-info {:background-color :white})
(defstyle profile-setting-item
{:flex-direction :row
:align-items :center
:padding-left 16
:ios {:height 73}
:android {:height 72}})
(defn profile-info-text-container [options]
{:flex 1
:padding-right (if options 16 40)})
(defstyle profile-info-title
{:color colors/gray
:margin-left 16
:font-size 14
:ios {:letter-spacing -0.2}})
(defstyle profile-setting-spacing
{:ios {:height 10}
:android {:height 7}})
(defstyle profile-setting-text
{:ios {:font-size 17
:letter-spacing -0.2}
:android {:font-size 16
:color colors/black}})
(def profile-setting-text-empty
(merge profile-setting-text
{:color colors/gray}))
(def profile-info-item-button
{:padding 16})

View File

@ -0,0 +1,117 @@
(ns status-im.ui.screens.profile.contact.views
(:require-macros [status-im.utils.views :refer [defview letsubs]])
(:require [status-im.ui.screens.profile.contact.styles :as styles]
[status-im.ui.components.common.common :as common]
[status-im.ui.components.react :as react]
[status-im.ui.screens.profile.components.styles :as profile.components.styles]
[status-im.ui.screens.profile.components.views :as profile.components]
[status-im.ui.components.status-bar.view :as status-bar]
[status-im.ui.components.action-button.styles :as action-button.styles]
[status-im.ui.components.action-button.action-button :as action-button]
[status-im.i18n :as i18n]
[re-frame.core :as re-frame]
[status-im.ui.components.toolbar.view :as toolbar]
[status-im.ui.components.toolbar.actions :as actions]
[status-im.ui.components.list-selection :as list-selection]
[status-im.ui.components.icons.vector-icons :as vector-icons]))
(defn profile-contact-toolbar [contact]
[toolbar/toolbar {}
toolbar/default-nav-back
[toolbar/content-title ""]
[toolbar/actions
(when (and (not (:pending? contact))
(not (:unremovable? contact)))
[(actions/opts [{:action #(re-frame/dispatch [:hide-contact contact])
:label (i18n/label :t/remove-from-contacts)}])])]])
(defn network-info []
[react/view styles/network-info
[common/network-info]
[common/separator]])
(defn profile-actions [{:keys [pending? whisper-identity dapp?]} chat-id]
[react/view action-button.styles/actions-list
(if pending?
[action-button/action-button {:label (i18n/label :t/add-to-contacts)
:icon :icons/add
:icon-opts {:color :blue}
:on-press #(re-frame/dispatch [:add-pending-contact chat-id])}]
[action-button/action-button-disabled {:label (i18n/label :t/in-contacts) :icon :icons/ok}])
[action-button/action-separator]
[action-button/action-button {:label (i18n/label :t/start-conversation)
:icon :icons/chats
:icon-opts {:color :blue}
:on-press #(re-frame/dispatch [:start-chat whisper-identity {:navigation-replace? true}])}]
(when-not dapp?
[react/view
[action-button/action-separator]
[action-button/action-button {:label (i18n/label :t/send-transaction)
:icon :icons/arrow-right
:icon-opts {:color :blue}
:on-press #(re-frame/dispatch [:profile/send-transaction chat-id whisper-identity])}]])])
(defn profile-info-item [{:keys [label value options text-mode empty-value? accessibility-label]}]
[react/view styles/profile-setting-item
[react/view (styles/profile-info-text-container options)
[react/text {:style styles/profile-info-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
:accessibility-label accessibility-label}
value]]
(when options
[react/touchable-highlight {:on-press #(list-selection/show {:options options})}
[react/view profile.components.styles/modal-menu
[vector-icons/icon :icons/options {:container-style styles/profile-info-item-button}]]])])
(defn profile-options [text]
(into []
(when text
(list-selection/share-options text))))
(defn profile-info-address-item [address]
[profile-info-item
{:label (i18n/label :t/address)
:action address
:options (profile-options address)
:text-mode :middle
:accessibility-label :profile-address
:value address}])
(defn profile-info-public-key-item [whisper-identity]
[profile-info-item
{:label (i18n/label :t/public-key)
:action whisper-identity
:options (profile-options whisper-identity)
:text-mode :middle
:accessibility-label :profile-public-key
:value whisper-identity}])
(defn profile-info [{:keys [whisper-identity address]}]
[react/view
[profile-info-address-item address]
[profile.components/settings-item-separator]
[profile-info-public-key-item whisper-identity]])
(defview profile []
(letsubs [contact [:contact]
chat-id [:get :current-chat-id]]
[react/view profile.components.styles/profile
[status-bar/status-bar]
[profile-contact-toolbar contact]
[network-info]
[react/scroll-view
[react/view profile.components.styles/profile-form
[profile.components/profile-header contact false false nil nil]]
[common/form-spacer]
[profile-actions contact chat-id]
[common/form-spacer]
[react/view profile.components.styles/profile-info-container
[profile-info contact]
[common/bottom-shadow]]]]))

View File

@ -1,19 +1,14 @@
(ns status-im.ui.screens.profile.events
(:require [clojure.spec.alpha :as spec]
[clojure.string :as string]
[re-frame.core :as re-frame]
[status-im.ui.components.react :refer [show-image-picker]]
[status-im.constants :as const]
[status-im.chat.constants :as chat-const]
[status-im.ui.screens.profile.db :as db]
[status-im.ui.screens.profile.navigation]
[status-im.ui.screens.accounts.events :as accounts-events]
[status-im.chat.events :as chat-events]
[status-im.chat.events.input :as input-events]
[status-im.utils.gfycat.core :as gfycat]
[status-im.utils.handlers :as handlers]
[status-im.utils.image-processing :refer [img->base64]]
[status-im.utils.core :as utils]
[taoensso.timbre :as log]))
(re-frame/reg-fx
@ -42,56 +37,9 @@
(defn get-current-account [{:keys [:accounts/current-account-id] :as db}]
(get-in db [:accounts/accounts current-account-id]))
(handlers/register-handler-fx
:my-profile.drawer/edit-name
(fn [{:keys [db]} _]
(let [{:my-profile/keys [default-name]} db
{:keys [name public-key]} (get-current-account db)]
{:db (cond-> db
(not default-name) (assoc :my-profile/default-name (gfycat/generate-gfy public-key))
:always (assoc-in [:my-profile/drawer :name] name))})))
(handlers/register-handler-fx
:my-profile.drawer/edit-status
(fn [{:keys [db]} _]
(let [{:keys [status]} (get-current-account db)]
{:db (-> db
(assoc-in [:my-profile/drawer :status] status)
(assoc-in [:my-profile/drawer :edit-status?] true))})))
(handlers/register-handler-fx
:my-profile/edit-profile
(fn [{:keys [db]} [_ edit-status?]]
(let [new-db (-> db
(assoc-in [:my-profile/profile :edit-status?] edit-status?)
(update-in [:my-profile/profile]
#(merge (select-keys (get-current-account db) db/account-profile-keys) %)))]
{:db new-db})))
(defn valid-name? [name]
(spec/valid? :profile/name name))
(handlers/register-handler-fx
:my-profile.drawer/update-name
(fn [{:keys [db]} [_ name]]
{:db (-> db
(assoc-in [:my-profile/drawer :valid-name?] (valid-name? name))
(assoc-in [:my-profile/drawer :name] name))}))
(handlers/register-handler-fx
:my-profile.drawer/update-status
(fn [{:keys [db]} [_ status]]
(let [linebreak? (string/includes? status "\n")
new-db (if linebreak?
(-> db
(assoc-in [:my-profile/drawer :edit-status?] nil)
(assoc-in [:my-profile/drawer :status] (utils/clean-text status)))
(assoc-in db [:my-profile/drawer :status] status))]
(if linebreak?
{:db new-db
:dispatch [:my-profile.drawer/save-status]}
{:db new-db}))))
(handlers/register-handler-fx
:my-profile/update-name
(fn [{:keys [db]} [_ name]]
@ -99,15 +47,6 @@
(assoc-in [:my-profile/profile :valid-name?] (valid-name? name))
(assoc-in [:my-profile/profile :name] name))}))
(handlers/register-handler-fx
:my-profile/update-status
(fn [{:keys [db]} [_ status]]
{:db (if (string/includes? status "\n")
(-> db
(assoc-in [:my-profile/profile :edit-status?] nil)
(assoc-in [:my-profile/profile :status] (utils/clean-text status)))
(assoc-in db [:my-profile/profile :status] status))}))
(handlers/register-handler-fx
:my-profile/update-picture
(fn [{:keys [db]} [this-event base64-image]]
@ -132,29 +71,6 @@
(accounts-events/account-update {:name cleaned-name
:last-updated now})))))
(defn status-change
"Takes map containing old status and the updated one and if the status has changed
returns the effects neccessary for status broadcasting"
[fx {:keys [old-status status]}]
(if (and status (not= status old-status))
(assoc fx :dispatch-n [[:broadcast-status status]])
fx))
(handlers/register-handler-fx
:my-profile.drawer/save-status
(fn [{:keys [db now]} _]
(let [status (get-in db [:my-profile/drawer :status])
new-fx (clear-profile {:db db})
{:accounts/keys [accounts current-account-id]} db
{old-status :status} (get accounts current-account-id)]
(if (string/blank? status)
new-fx
(-> new-fx
(accounts-events/account-update {:status status
:last-updated now})
(status-change {:old-status old-status
:status status}))))))
(handlers/register-handler-fx
:my-profile/start-editing-profile
(fn [{:keys [db]} []]
@ -163,17 +79,23 @@
(handlers/register-handler-fx
:my-profile/save-profile
(fn [{:keys [db now]} _]
(let [{:accounts/keys [accounts current-account-id]} db
{old-status :status} (get accounts current-account-id)
{:keys [status photo-path]} (:my-profile/profile db)
(let [{:keys [photo-path]} (:my-profile/profile db)
cleaned-name (clean-name db :my-profile/profile)
cleaned-edit (merge {:name cleaned-name
:status status
:last-updated now}
(if photo-path
{:photo-path photo-path}))]
(-> (clear-profile {:db db})
(accounts-events/account-update cleaned-edit)
(status-change {:old-status old-status
:status status})
(update :dispatch-n concat [[:navigate-back]])))))
(handlers/register-handler-fx
:group-chat-profile/start-editing
(fn [{:keys [db]} _]
{:db (assoc db :group-chat-profile/editing? true)}))
(handlers/register-handler-fx
:group-chat-profile/save-profile
(fn [{:keys [db]} _]
(-> {:db db}
(update :db dissoc :group-chat-profile/editing?))))

View File

@ -0,0 +1,21 @@
(ns status-im.ui.screens.profile.group-chat.styles
(:require [status-im.ui.components.colors :as colors]))
(def action-container
{:background-color colors/white
:padding-top 24})
(def action
{:background-color colors/blue-transparent
:border-radius 50})
(def action-label
{:color colors/blue})
(def action-separator
{:height 1
:background-color colors/gray-light
:margin-left 50})
(def action-icon-opts
{:color colors/blue})

View File

@ -0,0 +1,109 @@
(ns status-im.ui.screens.profile.group-chat.views
(:require-macros [status-im.utils.views :refer [defview letsubs]])
(:require [status-im.ui.screens.profile.group-chat.styles :as styles]
[status-im.ui.components.react :as react]
[status-im.ui.screens.profile.components.styles :as profile.components.styles]
[status-im.ui.screens.profile.components.views :as profile.components]
[status-im.ui.components.contact.contact :as contact]
[status-im.ui.components.list.views :as list]
[status-im.ui.components.colors :as colors]
[status-im.ui.components.styles :as components.styles]
[status-im.ui.components.toolbar.view :as toolbar]
[re-frame.core :as re-frame]
[status-im.ui.components.common.styles :as common.styles]
[status-im.i18n :as i18n]
[status-im.utils.utils :as utils]))
(defn group-chat-profile-toolbar []
[toolbar/toolbar {}
toolbar/default-nav-back
[toolbar/content-title ""]
[react/touchable-highlight
{:on-press #(re-frame/dispatch [:group-chat-profile/start-editing])}
[react/view
[react/text {:style common.styles/label-action-text
:uppercase? components.styles/uppercase?} (i18n/label :t/edit)]]]])
(defn group-chat-profile-edit-toolbar []
[toolbar/toolbar {}
nil
[toolbar/content-title ""]
[toolbar/default-done {:handler #(re-frame/dispatch [:group-chat-profile/save-profile])
:icon :icons/ok
:icon-opts {:color colors/blue}}]])
(defn actions [admin? chat-id]
(concat
(when admin?
[{:label (i18n/label :add-members)
:icon :icons/add
:action #(re-frame/dispatch [:navigate-to :add-participants-toggle-list])}])
[{:label (i18n/label :t/clear-history)
:icon :icons/close
:action #(utils/show-confirmation (i18n/label :t/clear-history-title)
(i18n/label :t/clear-group-history-confirmation)
(i18n/label :t/clear-history-action)
(fn [] (re-frame/dispatch [:clear-history])))}
{:label (i18n/label :t/delete-chat)
:icon :icons/delete
:action #(utils/show-confirmation (i18n/label :t/delete-chat-title)
(i18n/label :t/delete-group-chat-confirmation)
(i18n/label :t/delete)
(fn [] ;; TODO(goranjovic) - fix double dispatch after rebase agains group chat actions
(re-frame/dispatch [:remove-chat chat-id])
(re-frame/dispatch [:navigation-replace :home])))}
{:label (i18n/label :t/leave-group)
:icon :icons/arrow-left
:action #(utils/show-confirmation (i18n/label :t/leave-group-title)
(i18n/label :t/leave-group-confirmation)
(i18n/label :t/leave-group-action)
(fn [] (re-frame/dispatch [:leave-group-chat])))}]))
(defn contact-actions [contact]
[{:action #(re-frame/dispatch [:show-profile (:whisper-identity contact)])
:label (i18n/label :t/view-profile)}
{:action #(re-frame/dispatch [:remove-group-chat-participants #{(:whisper-identity contact)}])
:label (i18n/label :t/remove-from-chat)}])
(defn render-contact [contact admin?]
[react/view
[contact/contact-view
{:contact contact
:extend-options (contact-actions contact)
:extend-title (:name contact)
:extended? admin?}]])
(defview chat-group-contacts-view [admin?]
(letsubs [contacts [:current-chat-contacts]]
[react/view
[list/flat-list {:data contacts
:separator list/default-separator
:render-fn #(render-contact % admin?)}]]))
(defn members-list [admin?]
[react/view
[profile.components/settings-title (i18n/label :t/members-title)]
[chat-group-contacts-view admin?]])
(defview group-chat-profile []
(letsubs [current-chat [:get-current-chat]
editing? [:get :group-chat-profile/editing?]
changed-chat [:get :group-chat-profile/profile]
current-pk [:get :current-public-key]
group-admin [:chat :group-admin]]
(let [shown-chat (merge current-chat changed-chat)
admin? (= current-pk group-admin)]
[react/view profile.components.styles/profile
(if editing?
[group-chat-profile-edit-toolbar]
[group-chat-profile-toolbar])
[react/scroll-view
[react/view profile.components.styles/profile-form
[profile.components/profile-header shown-chat editing? false nil :set-group-chat-name]
[list/action-list (actions admin? (:chat-id current-chat))
{:container-style styles/action-container
:action-style styles/action
:action-label-style styles/action-label
:action-separator-style styles/action-separator
:icon-opts styles/action-icon-opts}]
[members-list admin?]]]])))

View File

@ -1,181 +0,0 @@
(ns status-im.ui.screens.profile.styles
(:require [status-im.ui.components.styles :as styles]
[status-im.ui.components.colors :as colors])
(:require-macros [status-im.utils.styles :refer [defstyle]]))
(def profile
{:flex 1
:background-color colors/white
:flex-direction :column})
(def profile-form
{:background-color colors/white
:padding 16})
(defstyle profile-info-container
{:background-color colors/white})
(def profile-info-item-button
{:padding 16})
(def profile-badge
{:flex-direction :column
:justify-content :center
:align-items :center})
(def profile-badge-edit
{:flex-direction :column
:justify-content :center})
(def modal-menu
{:align-items :center})
(def edit-name-title
{:color colors/gray
:ios {:font-size 13
:letter-spacing -0.1}
:android {:font-size 12}})
(defstyle profile-name-text
{:padding-vertical 14
:font-size 15
:text-align :center
:ios {:letter-spacing -0.2}
:android {:color colors/black}})
(defstyle profile-name-input-text
{:font-size 15
:text-align :center
:flex 1
:ios {:letter-spacing -0.2
:height 46
:border-bottom-width 1
:border-bottom-color styles/color-light-gray3}
:android {:color colors/black
:border-bottom-width 2
:border-bottom-color styles/color-blue4}})
(def profile-badge-name-container
{:flex 1
:justify-content :center})
(def profile-activity-status-container
{:margin-top 4})
(defstyle profile-activity-status-text
{:color colors/gray
:ios {:font-size 14
:line-height 20
:letter-spacing -0.2}
:android {:font-size 15
:line-height 20}})
(defstyle profile-setting-item
{:flex-direction :row
:align-items :center
:padding-left 16
:ios {:height 73}
:android {:height 72}})
(defn profile-info-text-container [options]
{:flex 1
:padding-right (if options 16 40)})
(defstyle profile-settings-title
{:color colors/gray
:margin-left 16
:margin-top 18
:margin-bottom 20
:font-size 14
:ios {:letter-spacing -0.2}})
(defstyle profile-info-title
{:color colors/gray
:margin-left 16
:font-size 14
:ios {:letter-spacing -0.2}})
(defstyle profile-setting-text
{:ios {:font-size 17
:letter-spacing -0.2}
:android {:font-size 16
:color colors/black}})
(defstyle profile-setting-spacing
{:ios {:height 10}
:android {:height 7}})
(def profile-setting-text-empty
(merge profile-setting-text
{:color colors/gray}))
(def settings-item-separator
{:margin-left 16})
(defstyle settings-item
{:padding-horizontal 16
:flex-direction :row
:align-items :center
:background-color colors/white
:height 52})
(defstyle settings-item-text
{:flex 1
:flex-wrap :nowrap
:font-size 15
:ios {:letter-spacing -0.2}
:android {:color colors/black}})
(def settings-item-value
{:flex 2
:flex-wrap :nowrap
:text-align :right
:padding-right 10
:font-size 15
:color colors/gray})
(defstyle logout-text
(merge settings-item-text
{:color colors/red}))
(defstyle profile-name-input
{:color styles/text1-color
:ios {:font-size 17
:padding-bottom 0
:line-height 17
:letter-spacing -0.2}
:android {:font-size 16
:line-height 16
:padding-top 5
:height 30
:padding-bottom 0}})
(def network-info {:background-color :white})
(def share-contact-code
{:margin-horizontal 16
:flex-direction :row
:justify-content :space-between
:align-items :center
:height 42
:border-radius 8
:background-color styles/color-blue4-transparent})
(def share-contact-code-text-container
{:padding-left 16
:padding-bottom 1
:flex 0.9
:flex-direction :row
:justify-content :center
:align-items :center})
(def share-contact-code-text
{:color colors/blue
:font-size 15})
(def share-contact-icon-container
{:border-radius 50
:flex 0.1
:padding-right 5
:align-items :center
:justify-content :center})

View File

@ -1,51 +0,0 @@
(ns status-im.ui.screens.profile.subs
(:require [re-frame.core :refer [reg-sub subscribe]]
[clojure.string :as string]
[status-im.ui.screens.profile.db :refer [account-profile-keys]]))
(defn get-in-profile [profile current-account k]
(or (get profile k)
(get current-account k)))
(reg-sub :my-profile/get
:<- [:get-current-account]
:<- [:get :my-profile/profile]
(fn [[current-account profile] [_ k]]
(get-in-profile profile current-account k)))
(reg-sub :my-profile.drawer/get
:<- [:get-current-account]
:<- [:get :my-profile/drawer]
(fn [[current-account profile] [_ k]]
(get-in-profile profile current-account k)))
(reg-sub :my-profile/valid-name?
:<- [:get :my-profile/profile]
(fn [profile]
(get profile :valid-name? true)))
(reg-sub :my-profile.drawer/valid-name?
:<- [:get :my-profile/drawer]
(fn [profile]
(or (string/blank? (:name profile))
(get profile :valid-name? true))))
(reg-sub :my-profile/changed?
:<- [:get-current-account]
:<- [:get :my-profile/profile]
(fn [[current-account profile]]
(not= (select-keys current-account account-profile-keys)
(select-keys profile account-profile-keys))))
(reg-sub :my-profile.drawer/save-event
:<- [:get-current-account]
:<- [:get :my-profile/drawer]
(fn [[{:keys [name status]} {new-name :name new-status :status}]]
(cond
(and new-name
(not= name new-name))
:my-profile.drawer/save-name
(and status
(not= status new-status))
:my-profile.drawer/save-status)))

View File

@ -0,0 +1,37 @@
(ns status-im.ui.screens.profile.user.styles
(:require-macros [status-im.utils.styles :refer [defstyle]])
(:require [status-im.ui.components.colors :as colors]
[status-im.ui.screens.profile.components.styles :as profile.components.styles]
[status-im.ui.components.styles :as components.styles]))
(def share-contact-code
{:margin-horizontal 16
:flex-direction :row
:justify-content :space-between
:align-items :center
:height 42
:border-radius components.styles/border-radius
:background-color colors/blue-transparent})
(def share-contact-code-text-container
{:padding-left 16
:padding-bottom 1
:flex 0.9
:flex-direction :row
:justify-content :center
:align-items :center})
(def share-contact-code-text
{:color colors/blue
:font-size 15})
(def share-contact-icon-container
{:border-radius 50
:flex 0.1
:padding-right 5
:align-items :center
:justify-content :center})
(defstyle logout-text
(merge profile.components.styles/settings-item-text
{:color colors/red}))

View File

@ -0,0 +1,137 @@
(ns status-im.ui.screens.profile.user.views
(:require-macros [status-im.utils.views :refer [defview letsubs]])
(:require [status-im.ui.screens.profile.user.styles :as styles]
[status-im.ui.components.toolbar.view :as toolbar]
[status-im.ui.components.react :as react]
[re-frame.core :as re-frame]
[status-im.ui.components.common.styles :as common.styles]
[status-im.ui.components.styles :as components.styles]
[status-im.i18n :as i18n]
[status-im.ui.components.colors :as colors]
[status-im.utils.utils :as utils]
[status-im.ui.components.icons.vector-icons :as vector-icons]
[status-im.ui.components.list-selection :as list-selection]
[status-im.ui.components.status-bar.view :as status-bar]
[status-im.ui.components.qr-code-viewer.views :as qr-code-viewer]
[status-im.utils.config :as config]
[status-im.utils.platform :as platform]
[status-im.ui.screens.profile.components.views :as profile.components]
[status-im.ui.screens.profile.components.styles :as profile.components.styles]
[status-im.ui.components.action-button.styles :as action-button.styles]
[status-im.protocol.core :as protocol]))
(defn my-profile-toolbar []
[toolbar/toolbar {}
nil
[toolbar/content-title ""]
[react/touchable-highlight
{:on-press #(re-frame/dispatch [:my-profile/start-editing-profile])}
[react/view
[react/text {:style common.styles/label-action-text
:uppercase? components.styles/uppercase?} (i18n/label :t/edit)]]]])
(defn my-profile-edit-toolbar []
[toolbar/toolbar {}
nil
[toolbar/content-title ""]
[toolbar/default-done {:handler #(re-frame/dispatch [:my-profile/save-profile])
:icon :icons/ok
:icon-opts {:color colors/blue}}]])
(def profile-icon-options
[{:label (i18n/label :t/image-source-gallery)
:action #(re-frame/dispatch [:my-profile/update-picture])}
{:label (i18n/label :t/image-source-make-photo)
:action (fn []
(re-frame/dispatch [:request-permissions
[:camera :write-external-storage]
#(re-frame/dispatch [:navigate-to :profile-photo-capture])
#(utils/show-popup (i18n/label :t/error)
(i18n/label :t/camera-access-error))]))}])
(defn qr-viewer-toolbar [label value]
[toolbar/toolbar {}
[toolbar/default-done {:icon-opts {:color colors/black}}]
[toolbar/content-title label]
[toolbar/actions [{:icon :icons/share
:icon-opts {:color :black}
:handler #(list-selection/open-share {:message value})}]]])
(defview qr-viewer []
(letsubs [{:keys [value contact]} [:get :qr-modal]]
[react/view {:flex-grow 1
:flex-direction :column}
[status-bar/status-bar {:type :modal}]
[qr-viewer-toolbar (:name contact) value]
[qr-code-viewer/qr-code-viewer {}
value (i18n/label :t/qr-code-public-key-hint) (str value)]]))
(defn- show-qr [contact source value]
#(re-frame/dispatch [:navigate-to :profile-qr-viewer {:contact contact
:source source
:value value}]))
(defn share-contact-code [current-account public-key]
[react/touchable-highlight {:on-press (show-qr current-account :public-key public-key)}
[react/view styles/share-contact-code
[react/view styles/share-contact-code-text-container
[react/text {:style styles/share-contact-code-text
:uppercase? components.styles/uppercase?}
(i18n/label :t/share-contact-code)]]
[react/view styles/share-contact-icon-container
[vector-icons/icon :icons/qr {:color colors/blue}]]]])
(defn my-profile-settings [{:keys [network networks]}]
[react/view
[profile.components/settings-title (i18n/label :t/settings)]
[profile.components/settings-item :t/main-currency "USD" #() false]
[profile.components/settings-item-separator]
[profile.components/settings-item :t/notifications "" #() true]
[profile.components/settings-item-separator]
[profile.components/settings-item :t/network (get-in networks [network :name])
#(re-frame/dispatch [:navigate-to :network-settings]) true]
(when config/offline-inbox-enabled?
[profile.components/settings-item-separator])
(when config/offline-inbox-enabled?
[profile.components/settings-item :t/offline-messaging-settings ""
#(re-frame/dispatch [:navigate-to :offline-messaging-settings]) true])])
(defn navigate-to-accounts []
;; TODO(rasom): probably not the best place for this call
(protocol/stop-whisper!)
(re-frame/dispatch [:navigate-to :accounts]))
(defn handle-logout []
(utils/show-confirmation (i18n/label :t/logout-title)
(i18n/label :t/logout-are-you-sure)
(i18n/label :t/logout) navigate-to-accounts))
(defn logout []
[react/view {}
[react/touchable-highlight
{:on-press handle-logout}
[react/view profile.components.styles/settings-item
[react/text {:style styles/logout-text
:font (if platform/android? :medium :default)}
(i18n/label :t/logout)]]]])
(defview my-profile []
(letsubs [{:keys [public-key] :as current-account} [:get-current-account]
editing? [:get :my-profile/editing?]
changed-account [:get :my-profile/profile]]
(let [shown-account (merge current-account changed-account)]
[react/view profile.components.styles/profile
(if editing?
[my-profile-edit-toolbar]
[my-profile-toolbar])
[react/scroll-view
[react/view profile.components.styles/profile-form
[profile.components/profile-header shown-account editing? true profile-icon-options :my-profile/update-name]]
[react/view action-button.styles/actions-list
[share-contact-code current-account public-key]]
[react/view profile.components.styles/profile-info-container
[my-profile-settings current-account]]
[logout]]])))

View File

@ -1,315 +0,0 @@
(ns status-im.ui.screens.profile.views
(:require-macros [status-im.utils.views :refer [defview letsubs]])
(:require [clojure.string :as string]
[re-frame.core :as re-frame]
[status-im.i18n :as i18n]
[status-im.ui.components.action-button.action-button :as action-button]
[status-im.ui.components.action-button.styles :as action-button.styles]
[status-im.ui.components.chat-icon.screen :as chat-icon.screen]
[status-im.ui.components.common.common :as common]
[status-im.ui.components.common.styles :as common.styles]
[status-im.ui.components.list-selection :as list-selection]
[status-im.ui.components.qr-code-viewer.views :as qr-code-viewer]
[status-im.ui.components.react :as react]
[status-im.ui.components.icons.vector-icons :as vector-icons]
[status-im.ui.components.status-bar.view :as status-bar]
[status-im.ui.components.styles :as component.styles]
[status-im.ui.components.colors :as colors]
[status-im.ui.components.toolbar.actions :as actions]
[status-im.ui.components.toolbar.view :as toolbar]
[status-im.ui.screens.profile.styles :as styles]
[status-im.ui.components.colors :as colors]
[status-im.utils.utils :as utils]
[status-im.utils.core :refer [hash-tag?]]
[status-im.utils.datetime :as time]
[status-im.utils.config :as config]
[status-im.utils.platform :as platform]
[status-im.protocol.core :as protocol]))
(defn my-profile-toolbar []
[toolbar/toolbar {}
nil
[toolbar/content-title ""]
[react/touchable-highlight
{:on-press #(re-frame/dispatch [:my-profile/start-editing-profile])}
[react/view
[react/text {:style common.styles/label-action-text
:uppercase? component.styles/uppercase?} (i18n/label :t/edit)]]]])
(defn my-profile-edit-toolbar []
[toolbar/toolbar {}
nil
[toolbar/content-title ""]
[toolbar/default-done {:handler #(re-frame/dispatch [:my-profile/save-profile])
:icon :icons/ok
:icon-opts {:color colors/blue}}]])
(defn profile-toolbar [contact]
[toolbar/toolbar {}
toolbar/default-nav-back
[toolbar/content-title ""]
[toolbar/actions
(when (and (not (:pending? contact))
(not (:unremovable? contact)))
[(actions/opts [{:action #(re-frame/dispatch [:hide-contact contact])
:label (i18n/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)
(i18n/label :t/active-unknown))))
(defn profile-badge [{:keys [name] :as contact}]
[react/view styles/profile-badge
[chat-icon.screen/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]]])
(defn profile-name-input [name]
[react/view
[react/text-input
{:style styles/profile-name-input-text
:placeholder ""
:default-value name
:auto-focus true
:on-focus #(re-frame/dispatch [:my-profile/edit-profile])
:on-change-text #(re-frame/dispatch [:my-profile/update-name %])}]])
(def profile-icon-options
[{:label (i18n/label :t/image-source-gallery)
:action #(re-frame/dispatch [:my-profile/update-picture])}
{:label (i18n/label :t/image-source-make-photo)
:action (fn []
(re-frame/dispatch [:request-permissions
[:camera :write-external-storage]
#(re-frame/dispatch [:navigate-to :profile-photo-capture])
#(utils/show-popup (i18n/label :t/error)
(i18n/label :t/camera-access-error))]))}])
(defn profile-badge-edit [{:keys [name] :as account}]
[react/view styles/profile-badge-edit
[react/touchable-highlight {:on-press #(list-selection/show {:title (i18n/label :t/image-source-title)
:options profile-icon-options})}
[react/view styles/modal-menu
[chat-icon.screen/my-profile-icon {:account account
:edit? true}]]]
[react/view styles/profile-badge-name-container
[profile-name-input name]]])
(defn profile-actions [{:keys [pending? whisper-identity dapp?]} chat-id]
[react/view action-button.styles/actions-list
(if pending?
[action-button/action-button {:label (i18n/label :t/add-to-contacts)
:icon :icons/add
:icon-opts {:color :blue}
:on-press #(re-frame/dispatch [:add-pending-contact chat-id])}]
[action-button/action-button-disabled {:label (i18n/label :t/in-contacts) :icon :icons/ok}])
[action-button/action-separator]
[action-button/action-button {:label (i18n/label :t/start-conversation)
:icon :icons/chats
:icon-opts {:color :blue}
:on-press #(re-frame/dispatch [:start-chat whisper-identity {:navigation-replace? true}])}]
(when-not dapp?
[react/view
[action-button/action-separator]
[action-button/action-button {:label (i18n/label :t/send-transaction)
:icon :icons/arrow-right
:icon-opts {:color :blue}
:on-press #(re-frame/dispatch [:profile/send-transaction chat-id whisper-identity])}]])])
(defn profile-info-item [{:keys [label value options text-mode empty-value? accessibility-label]}]
[react/view styles/profile-setting-item
[react/view (styles/profile-info-text-container options)
[react/text {:style styles/profile-info-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
:accessibility-label accessibility-label}
value]]
(when options
[react/touchable-highlight {:on-press #(list-selection/show {:options options})}
[react/view styles/modal-menu
[vector-icons/icon :icons/options {:container-style styles/profile-info-item-button}]]])])
(defn- toolbar [label value]
[toolbar/toolbar {}
[toolbar/default-done {:icon-opts {:color colors/black}}]
[toolbar/content-title label]
[toolbar/actions [{:icon :icons/share
:icon-opts {:color :black}
:handler #(list-selection/open-share {:message value})}]]])
(defview qr-viewer []
(letsubs [{:keys [value contact]} [:get :qr-modal]]
[react/view {:flex-grow 1
:flex-direction :column}
[status-bar/status-bar {:type :modal}]
[toolbar (:name contact) value]
[qr-code-viewer/qr-code-viewer {}
value (i18n/label :t/qr-code-public-key-hint) (str value)]]))
(defn- show-qr [contact source value]
#(re-frame/dispatch [:navigate-to :profile-qr-viewer {:contact contact
:source source
:value value}]))
(defn profile-options [text]
(into []
(when text
(list-selection/share-options text))))
(defn profile-info-address-item [address]
[profile-info-item
{:label (i18n/label :t/address)
:action address
:options (profile-options address)
:text-mode :middle
:accessibility-label :profile-address
:value address}])
(defn profile-info-public-key-item [whisper-identity]
[profile-info-item
{:label (i18n/label :t/public-key)
:action whisper-identity
:options (profile-options whisper-identity)
:text-mode :middle
:accessibility-label :profile-public-key
:value whisper-identity}])
(defn settings-item-separator []
[common/separator styles/settings-item-separator])
(defn tag-view [tag]
[react/text {:style {:color colors/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 settings-title [title]
[react/text {:style styles/profile-settings-title}
title])
(defn settings-item [label-kw value action-fn active?]
[react/touchable-highlight
{:on-press action-fn
:disabled (not active?)}
[react/view styles/settings-item
[react/text {:style styles/settings-item-text}
(i18n/label label-kw)]
(when-not (string/blank? value)
[react/text {:style styles/settings-item-value
:number-of-lines 1
:uppercase? component.styles/uppercase?}
value])
(when active?
[vector-icons/icon :icons/forward {:color colors/gray}])]])
(defn profile-info [{:keys [whisper-identity address]}]
[react/view
[profile-info-address-item address]
[settings-item-separator]
[profile-info-public-key-item whisper-identity]])
(defn navigate-to-accounts []
;; TODO(rasom): probably not the best place for this call
(protocol/stop-whisper!)
(re-frame/dispatch [:navigate-to :accounts]))
(defn handle-logout []
(utils/show-confirmation (i18n/label :t/logout-title)
(i18n/label :t/logout-are-you-sure)
(i18n/label :t/logout) navigate-to-accounts))
(defn logout []
[react/view {}
[react/touchable-highlight
{:on-press handle-logout}
[react/view styles/settings-item
[react/text {:style styles/logout-text
:font (if platform/android? :medium :default)}
(i18n/label :t/logout)]]]])
(defn my-profile-settings [{:keys [network networks]}]
[react/view
[settings-title (i18n/label :t/settings)]
[settings-item :t/main-currency "USD" #() false]
[settings-item-separator]
[settings-item :t/notifications "" #() true]
[settings-item-separator]
[settings-item :t/network (get-in networks [network :name])
#(re-frame/dispatch [:navigate-to :network-settings]) true]
(when config/offline-inbox-enabled?
[settings-item-separator])
(when config/offline-inbox-enabled?
[settings-item :t/offline-messaging-settings ""
#(re-frame/dispatch [:navigate-to :offline-messaging-settings]) true])])
(defn network-info []
[react/view styles/network-info
[common/network-info]
[common/separator]])
(defn share-contact-code [current-account public-key]
[react/touchable-highlight {:on-press (show-qr current-account :public-key public-key)}
[react/view styles/share-contact-code
[react/view styles/share-contact-code-text-container
[react/text {:style styles/share-contact-code-text
:uppercase? component.styles/uppercase?}
(i18n/label :t/share-contact-code)]]
[react/view styles/share-contact-icon-container
[vector-icons/icon :icons/qr {:color colors/blue}]]]])
(defview my-profile []
(letsubs [{:keys [public-key] :as current-account} [:get-current-account]
editing? [:get :my-profile/editing?]
changed-account [:get :my-profile/profile]]
[react/view styles/profile
(if editing?
[my-profile-edit-toolbar]
[my-profile-toolbar])
[react/scroll-view
[react/view styles/profile-form
(if editing?
[profile-badge-edit (merge current-account changed-account)]
[profile-badge current-account])]
[react/view action-button.styles/actions-list
[share-contact-code current-account public-key]]
[react/view styles/profile-info-container
[my-profile-settings current-account]]
[logout]]]))
(defview profile []
(letsubs [contact [:contact]
chat-id [:get :current-chat-id]]
[react/view styles/profile
[status-bar/status-bar]
[profile-toolbar contact]
[network-info]
[react/scroll-view
[react/view styles/profile-form
[profile-badge contact]]
[common/form-spacer]
[profile-actions contact chat-id]
[common/form-spacer]
[react/view styles/profile-info-container
[profile-info contact]
[common/bottom-shadow]]]]))

View File

@ -8,7 +8,6 @@
status-im.ui.screens.discover.subs
status-im.ui.screens.contacts.subs
status-im.ui.screens.group.subs
status-im.ui.screens.profile.subs
status-im.ui.screens.wallet.subs
status-im.ui.screens.wallet.request.subs
status-im.ui.screens.wallet.send.subs

View File

@ -19,15 +19,15 @@
[status-im.ui.screens.qr-scanner.views :refer [qr-scanner]]
[status-im.ui.screens.group.views :refer [new-group edit-contact-group]]
[status-im.ui.screens.group.chat-settings.views :refer [chat-group-settings]]
[status-im.ui.screens.group.edit-contacts.views :refer [edit-contact-group-contact-list
edit-chat-group-contact-list]]
[status-im.ui.screens.group.views :refer [new-group]]
[status-im.ui.screens.group.edit-contacts.views :refer [edit-contact-group-contact-list]]
[status-im.ui.screens.group.add-contacts.views :refer [contact-toggle-list
add-contacts-toggle-list
add-participants-toggle-list]]
[status-im.ui.screens.profile.views :as profile]
[status-im.ui.screens.profile.user.views :as profile.user]
[status-im.ui.screens.profile.contact.views :as profile.contact]
[status-im.ui.screens.profile.group-chat.views :as profile.group-chat]
[status-im.ui.screens.profile.photo-capture.views :refer [profile-photo-capture]]
[status-im.ui.screens.wallet.send.views :refer [send-transaction send-transaction-modal]]
[status-im.ui.screens.wallet.choose-recipient.views :refer [choose-recipient]]
@ -144,18 +144,16 @@
:wallet-request-assets wallet.components/request-assets
:new add-new
:new-group new-group
:edit-contact-group edit-contact-group
:chat-group-settings chat-group-settings
:add-contacts-toggle-list add-contacts-toggle-list
:add-participants-toggle-list add-participants-toggle-list
:edit-group-contact-list edit-contact-group-contact-list
:edit-chat-group-contact-list edit-chat-group-contact-list
:new-public-chat new-public-chat
:contact-toggle-list contact-toggle-list
:new-chat new-chat
:qr-scanner qr-scanner
:chat chat
:profile profile/profile
:profile profile.contact/profile
:group-chat-profile profile.group-chat/group-chat-profile
:discover-all-recent discover-recent/discover-all-recent
:discover-all-popular-hashtags discover-popular/discover-all-popular-hashtags
:discover-search-results discover-search/discover-search-results
@ -173,7 +171,7 @@
:recent-recipients recent-recipients
:recipient-qr-code recipient-qr-code
:contact-code contact-code
:profile-qr-viewer profile/qr-viewer
:profile-qr-viewer profile.user/qr-viewer
(throw (str "Unknown view: " current-view)))
main-screen-view (create-main-screen-view current-view)]
[main-screen-view common-styles/flex

View File

@ -30,15 +30,6 @@
[react/text {:style styles/total-balance-currency} (i18n/label :t/usd-currency)]]
[react/text {:style styles/total-value} (i18n/label :t/wallet-total-value)]]])
(defn- render-action [{:keys [label icon action]}]
[react/touchable-highlight {:on-press action}
[react/view
[list/item
[list/item-icon {:icon icon :style styles/action :icon-opts {:color :white}}]
[list/item-primary-only {:style styles/action-label}
label]
list/item-icon-forward]]])
(def actions
[{:label (i18n/label :t/send-transaction)
:icon :icons/arrow-right
@ -50,13 +41,6 @@
:icon :icons/transaction-history
:action #(re-frame/dispatch [:navigate-to :transactions-history])}])
(defn- action-section []
[react/view styles/action-section
[list/flat-list
{:separator (when platform/ios? [react/view styles/action-separator])
:data actions
:render-fn render-action}]])
(defn- render-asset [{:keys [symbol icon decimals amount]}]
[react/view
[list/item
@ -97,5 +81,6 @@
[toolbar-view]
[react/view components.styles/flex
[total-section portfolio-value]
[action-section]
[list/action-list actions
{:container-style styles/action-section}]
[asset-section network balance visible-tokens prices-loading? balance-loading?]]]))

View File

@ -24,46 +24,3 @@
(rf/reg-fx ::events/init-store #())
(rf/reg-fx ::account-events/save-account #()))
(deftest profile-edit-events
(run-test-sync
(test-fixtures)
(let [accounts (rf/subscribe [:get-accounts])
address (:address new-account)]
(rf/dispatch [:initialize-db])
(rf/dispatch [:add-account new-account])
(rf/dispatch [:initialize-account-db address])
(testing "Setting status from edit profile screen"
(let [new-status "New edit profile status"]
(is (= (:status new-account) (-> @accounts
(get address)
:status)))
(rf/dispatch [:my-profile/edit-profile :edit-status])
(rf/dispatch [:my-profile/update-status new-status])
(rf/dispatch [:my-profile/save-profile])
(is (= new-status (-> @accounts
(get address)
:status)))))
(testing "Setting status from drawer"
(let [new-status "New drawer status"]
(rf/dispatch [:my-profile.drawer/edit-status])
(rf/dispatch [:my-profile.drawer/update-status new-status])
(rf/dispatch [:my-profile.drawer/save-status])
(is (= new-status (-> @accounts
(get address)
:status))))))))
(deftest test-status-change
(let [fx {:db {}}]
(is (= (profile-events/status-change fx {:old-status "this is old status"
:status "this is new and CHANGED status"})
{:db {}
:dispatch-n [[:broadcast-status "this is new and CHANGED status"]]}))
(is (= (profile-events/status-change fx {:old-status "this is old status"
:status "this is new and #changed status"})
{:db {}
:dispatch-n [[:broadcast-status "this is new and #changed status"]]}))
(is (= (profile-events/status-change fx {:old-status "this is old status"
:status "this is old status"})
{:db {}}))))