From 5c3009ca031bb370d3a0ba55efd427ae95c46e4c Mon Sep 17 00:00:00 2001 From: virvar Date: Fri, 17 Jun 2016 13:13:27 +0300 Subject: [PATCH 01/14] Default user icon in contacts view --- .../components/chat_icon/screen.cljs | 27 ++++++++++++++++--- src/status_im/contacts/styles.cljs | 23 ++++------------ src/status_im/contacts/subs.cljs | 4 +++ .../contacts/views/contact_inner.cljs | 21 ++++----------- 4 files changed, 38 insertions(+), 37 deletions(-) diff --git a/src/status_im/components/chat_icon/screen.cljs b/src/status_im/components/chat_icon/screen.cljs index 0d93e38cbb..8b89575422 100644 --- a/src/status_im/components/chat_icon/screen.cljs +++ b/src/status_im/components/chat_icon/screen.cljs @@ -6,7 +6,7 @@ image icon]] [status-im.components.chat-icon.styles :as st] - [status-im.components.styles :refer [color-purple]] + [status-im.components.styles :refer [default-chat-color]] [clojure.string :as s])) (defn default-chat-icon [name styles] @@ -63,6 +63,27 @@ :default-chat-icon (st/default-chat-icon-menu-item color) :default-chat-icon-text st/default-chat-icon-text}]) +(defview contact-icon-view [identity styles] + [contact [:contact-by-identity identity]] + (let [photo-path (:photo-path contact) + ;; TODO stub data + online true] + [view (:container styles) + (if-not (s/blank? photo-path) + [chat-icon photo-path styles] + [default-chat-icon (:name contact) styles]) + [contact-online online styles]])) + +(defn contact-icon-contacts-tab [identity] + [contact-icon-view identity + {:container st/container-chat-list + :online-view st/online-view + :online-dot-left st/online-dot-left + :online-dot-right st/online-dot-right + :chat-icon st/chat-icon-chat-list + :default-chat-icon (st/default-chat-icon-chat-list default-chat-color) + :default-chat-icon-text st/default-chat-icon-text}]) + (defn profile-icon-view [photo-path name color online] (let [styles {:container st/container-profile :online-view st/online-view-profile @@ -81,7 +102,7 @@ [contact [:contact]] (let [;; TODO stub data online true - color color-purple] + color default-chat-color] [profile-icon-view (:photo-path contact) (:name contact) color online])) (defview my-profile-icon [] @@ -89,5 +110,5 @@ photo-path [:get :photo-path]] (let [;; TODO stub data online true - color color-purple] + color default-chat-color] [profile-icon-view photo-path name color online])) diff --git a/src/status_im/contacts/styles.cljs b/src/status_im/contacts/styles.cljs index 4b64148fc9..9c0a864cd6 100644 --- a/src/status_im/contacts/styles.cljs +++ b/src/status_im/contacts/styles.cljs @@ -16,7 +16,8 @@ {:backgroundColor :white}) (def contact-photo-container - {:borderRadius 50}) + {:marginTop 4 + :marginLeft 12}) (def photo-image {:borderRadius 50 @@ -34,20 +35,6 @@ :borderWidth 2 :borderColor color-white}) -(def online-dot - {:position :absolute - :top 6 - :width 4 - :height 4 - :borderRadius 50 - :backgroundColor color-white}) - -(def online-dot-left - (assoc online-dot :left 3)) - -(def online-dot-right - (assoc online-dot :left 9)) - (def contact-container {:flexDirection :row :height 56}) @@ -59,11 +46,11 @@ :height 44}) (def name-container - {:justifyContent :center}) + {:marginLeft 12 + :justifyContent :center}) (def name-text - {:marginLeft 16 - :fontSize 16 + {:fontSize 16 :fontFamily font :color text1-color}) diff --git a/src/status_im/contacts/subs.cljs b/src/status_im/contacts/subs.cljs index 76803f74dd..026bb029c0 100644 --- a/src/status_im/contacts/subs.cljs +++ b/src/status_im/contacts/subs.cljs @@ -33,6 +33,10 @@ (let [identity (:contact-identity @db)] (reaction (get-in @db [:contacts identity]))))) +(register-sub :contact-by-identity + (fn [db [_ identity]] + (reaction (get-in @db [:contacts identity])))) + (register-sub :all-new-contacts (fn [db _] (contacts-by-current-chat remove db))) diff --git a/src/status_im/contacts/views/contact_inner.cljs b/src/status_im/contacts/views/contact_inner.cljs index 05502d793b..d7fa63d8da 100644 --- a/src/status_im/contacts/views/contact_inner.cljs +++ b/src/status_im/contacts/views/contact_inner.cljs @@ -1,28 +1,17 @@ (ns status-im.contacts.views.contact-inner (:require [clojure.string :as s] [status-im.components.react :refer [view image text]] - [status-im.resources :as res] + [status-im.components.chat-icon.screen :refer [contact-icon-contacts-tab]] [status-im.contacts.styles :as st] [status-im.i18n :refer [label]])) -(defn contact-photo [{:keys [photo-path]}] +(defn contact-photo [{:keys [whisper-identity]}] [view st/contact-photo-container - [image {:source (if (s/blank? photo-path) - res/user-no-photo - {:uri photo-path}) - :style st/photo-image}]]) + [contact-icon-contacts-tab whisper-identity]]) -(defn contact-online [{:keys [online]}] - (when online - [view st/online-container - [view st/online-dot-left] - [view st/online-dot-right]])) - -(defn contact-inner-view [{:keys [name photo-path online]}] +(defn contact-inner-view [{:keys [name] :as contact}] [view st/contact-container - [view st/photo-container - [contact-photo {:photo-path photo-path}] - [contact-online {:online online}]] + [contact-photo contact] [view st/name-container [text {:style st/name-text} (if (pos? (count name)) From 9855d35d377fe874cf3618be3e9c74bcc2b28a2c Mon Sep 17 00:00:00 2001 From: virvar Date: Fri, 17 Jun 2016 14:41:56 +0300 Subject: [PATCH 02/14] Contacts' letters --- src/status_im/contacts/screen.cljs | 2 +- src/status_im/contacts/styles.cljs | 37 ++++++------------- src/status_im/contacts/subs.cljs | 17 +++++++++ .../contacts/views/contact_inner.cljs | 9 ++++- 4 files changed, 38 insertions(+), 27 deletions(-) diff --git a/src/status_im/contacts/screen.cljs b/src/status_im/contacts/screen.cljs index a763c2ea50..5ea7be8b15 100644 --- a/src/status_im/contacts/screen.cljs +++ b/src/status_im/contacts/screen.cljs @@ -36,7 +36,7 @@ :handler (fn [])}}]) (defview contact-list [] - [contacts [:get-contacts]] + [contacts [:contacts-with-letters]] [drawer-view [view st/contacts-list-container [contact-list-toolbar] diff --git a/src/status_im/contacts/styles.cljs b/src/status_im/contacts/styles.cljs index 9c0a864cd6..59fa3453e3 100644 --- a/src/status_im/contacts/styles.cljs +++ b/src/status_im/contacts/styles.cljs @@ -2,12 +2,11 @@ (:require [status-im.components.styles :refer [font title-font text1-color + text3-color color-white toolbar-background2 online-color]])) - - (def contacts-list-container {:flex 1 :backgroundColor :white}) @@ -15,38 +14,26 @@ (def contacts-list {:backgroundColor :white}) +(def letter-container + {:paddingTop 11 + :paddingLeft 20 + :width 56}) + +(def letter-text + {:fontSize 24 + :fontFamily font + :color text3-color}) + (def contact-photo-container {:marginTop 4 :marginLeft 12}) -(def photo-image - {:borderRadius 50 - :width 40 - :height 40}) - -(def online-container - {:position :absolute - :top 24 - :left 24 - :width 20 - :height 20 - :borderRadius 50 - :backgroundColor online-color - :borderWidth 2 - :borderColor color-white}) - (def contact-container {:flexDirection :row :height 56}) -(def photo-container - {:marginTop 8 - :marginLeft 16 - :width 44 - :height 44}) - (def name-container - {:marginLeft 12 + {:marginLeft 12 :justifyContent :center}) (def name-text diff --git a/src/status_im/contacts/subs.cljs b/src/status_im/contacts/subs.cljs index 026bb029c0..3f5764c9b5 100644 --- a/src/status_im/contacts/subs.cljs +++ b/src/status_im/contacts/subs.cljs @@ -12,6 +12,23 @@ (let [contacts (reaction (:contacts @db))] (reaction (sort-by :name (vals @contacts)))))) +(defn get-contact-letter [contact] + (when-let [letter (first (:name contact))] + (clojure.string/upper-case letter))) + +(register-sub :contacts-with-letters + (fn [db _] + (let [contacts (reaction (:contacts @db))] + (reaction + (let [ordered (sort-by :name (vals @contacts))] + (map (fn [prev cur] + (let [prev-letter (get-contact-letter prev) + cur-letter (get-contact-letter cur)] + (if (not= prev-letter cur-letter) + (assoc cur :letter cur-letter) + cur))) + (cons nil ordered) ordered)))))) + (defn contacts-by-chat [fn db chat-id] (let [chat (reaction (get-in @db [:chats chat-id])) contacts (reaction (:contacts @db))] diff --git a/src/status_im/contacts/views/contact_inner.cljs b/src/status_im/contacts/views/contact_inner.cljs index d7fa63d8da..9512ef3120 100644 --- a/src/status_im/contacts/views/contact_inner.cljs +++ b/src/status_im/contacts/views/contact_inner.cljs @@ -5,12 +5,19 @@ [status-im.contacts.styles :as st] [status-im.i18n :refer [label]])) +(defn letter-view [letter] + [view st/letter-container + (when letter + [text {:style st/letter-text} + letter])]) + (defn contact-photo [{:keys [whisper-identity]}] [view st/contact-photo-container [contact-icon-contacts-tab whisper-identity]]) -(defn contact-inner-view [{:keys [name] :as contact}] +(defn contact-inner-view [{:keys [name letter] :as contact}] [view st/contact-container + [letter-view letter] [contact-photo contact] [view st/name-container [text {:style st/name-text} From 9ed4cc03e3e6852f47688fcd9bac39df2d12274d Mon Sep 17 00:00:00 2001 From: virvar Date: Wed, 15 Jun 2016 16:41:08 +0300 Subject: [PATCH 03/14] Grey action bar. Hide navbar on chat list scroll. --- src/status_im/chats_list/screen.cljs | 39 ++++++++++++++++---- src/status_im/components/tabs/styles.cljs | 19 ++++------ src/status_im/components/tabs/tabs.cljs | 45 +++++++++++++++++++---- 3 files changed, 76 insertions(+), 27 deletions(-) diff --git a/src/status_im/chats_list/screen.cljs b/src/status_im/chats_list/screen.cljs index 572f0ac5a9..afd8047b39 100644 --- a/src/status_im/chats_list/screen.cljs +++ b/src/status_im/chats_list/screen.cljs @@ -1,4 +1,5 @@ (ns status-im.chats-list.screen + (:require-macros [status-im.utils.views :refer [defview]]) (:require [re-frame.core :refer [subscribe dispatch]] [status-im.components.react :refer [list-view list-item @@ -13,33 +14,55 @@ action-button-item]] [status-im.components.drawer.view :refer [drawer-view open-drawer]] [status-im.components.styles :refer [color-blue + toolbar-background1 toolbar-background2]] [status-im.components.toolbar :refer [toolbar]] [status-im.components.icons.ionicons :refer [icon]] [status-im.i18n :refer [label]] - [status-im.chats-list.styles :as st])) + [status-im.chats-list.styles :as st] + [status-im.components.tabs.styles :refer [tabs-height]])) -(defn chats-list-toolbar [] +(defview chats-list-toolbar [] + [chats-scrolled? [:get :chats-scrolled?]] [toolbar {:nav-action {:image {:source {:uri :icon_hamburger} :style st/hamburger-icon} :handler open-drawer} :title (label :t/chats) - :background-color toolbar-background2 + :background-color (if chats-scrolled? + toolbar-background1 + toolbar-background2) ;; TODO implement search :action {:image {:source {:uri :icon_search} :style st/search-icon} :handler (fn [])}}]) (defn chats-list [] - (let [chats (subscribe [:get :chats])] + (let [chats (subscribe [:get :chats]) + chat-scrolled? (subscribe [:get :chats-scrolled?]) + container-height (r/atom 0) + content-height (r/atom 0)] + (dispatch [:set :chats-scrolled? false]) (fn [] [drawer-view [view st/chats-container [chats-list-toolbar] - [list-view {:dataSource (to-datasource @chats) - :renderRow (fn [row _ _] - (list-item [chat-list-item row])) - :style st/list-container}] + [list-view {:dataSource (to-datasource @chats) + :renderRow (fn [row _ _] + (list-item [chat-list-item row])) + :style st/list-container + ;;; if "maximazing" chat list will make scroll to 0, + ;;; then disable maximazing + :onLayout (fn [event] + (when-not @chat-scrolled? + (let [height (.. event -nativeEvent -layout -height)] + (reset! container-height height)))) + :onContentSizeChange (fn [width height] + (reset! content-height height)) + :onScroll (fn [e] + (let [offset (.. e -nativeEvent -contentOffset -y) + min-content-height (+ @container-height tabs-height) + scrolled? (and (< 0 offset) (< min-content-height @content-height))] + (dispatch [:set :chats-scrolled? scrolled?])))}] [action-button {:buttonColor color-blue :offsetY 16 :offsetX 16} diff --git a/src/status_im/components/tabs/styles.cljs b/src/status_im/components/tabs/styles.cljs index 45af60e439..7dc57d6d24 100644 --- a/src/status_im/components/tabs/styles.cljs +++ b/src/status_im/components/tabs/styles.cljs @@ -10,21 +10,19 @@ text2-color toolbar-background1]])) +(def tabs-height 59) (def tab-height 56) -(def tabs - {:flex 1 - :position :absolute - :bottom 0 - :right 0 - :left 0 - }) +(defn tabs-container [offset-y] + {:height tabs-height + :backgroundColor color-white + :marginBottom offset-y}) (def top-gradient {:flexDirection :row :height 3}) -(def tabs-container +(def tabs-inner-container {:flexDirection :row :height tab-height :opacity 1 @@ -55,10 +53,9 @@ :alignItems :center}) (defn tab-view-container [offset-x] - {:flex 1 - :position :absolute + {:position :absolute :top 0 :left 0 :right 0 - :bottom tab-height + :bottom 0 :transform [{:translateX offset-x}]}) diff --git a/src/status_im/components/tabs/tabs.cljs b/src/status_im/components/tabs/tabs.cljs index bd12614f3f..bc326a7d43 100644 --- a/src/status_im/components/tabs/tabs.cljs +++ b/src/status_im/components/tabs/tabs.cljs @@ -2,6 +2,7 @@ (:require-macros [status-im.utils.views :refer [defview]]) (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] [status-im.components.react :refer [view + animated-view text-input text image @@ -9,7 +10,8 @@ linear-gradient]] [reagent.core :as r] [status-im.components.tabs.styles :as st] - [status-im.components.tabs.tab :refer [tab]])) + [status-im.components.tabs.tab :refer [tab]] + [status-im.components.animation :as anim])) (defn create-tab [index data selected-view-id] (let [data (merge data {:key index @@ -17,10 +19,37 @@ :selected-view-id selected-view-id})] [tab data])) -(defview tabs [{:keys [style tab-list selected-view-id]}] - (let [style (merge st/tabs style)] - [view {:style style} - [linear-gradient {:colors ["rgba(24, 52, 76, 0.01)" "rgba(24, 52, 76, 0.085)" "rgba(24, 52, 76, 0.165)"] - :style st/top-gradient}] - [view st/tabs-container - (doall (map-indexed #(create-tab %1 %2 selected-view-id) tab-list))]])) +(defn animation-logic [{:keys [hidden? val]}] + (fn [_] + (let [to-value (if @hidden? (- st/tab-height) 0)] + (anim/start + (anim/timing val {:toValue to-value + :duration 300}) + (fn [e] + (when-not (.-finished e) + nil)))))) + +(defn tabs-container [& children] + (let [chats-scrolled? (subscribe [:get :chats-scrolled?]) + anim-value (anim/create-value 0) + context {:hidden? chats-scrolled? + :val anim-value} + on-update (animation-logic context)] + (r/create-class + {:component-did-mount + on-update + :component-did-update + on-update + :reagent-render + (fn [& children] + @chats-scrolled? + (into [animated-view {:style (st/tabs-container anim-value) + :pointerEvents (if @chats-scrolled? :none :auto)}] + children))}))) + +(defn tabs [{:keys [tab-list selected-view-id]}] + [tabs-container + [linear-gradient {:colors ["rgba(24, 52, 76, 0.01)" "rgba(24, 52, 76, 0.085)" "rgba(24, 52, 76, 0.165)"] + :style st/top-gradient}] + [view st/tabs-inner-container + (doall (map-indexed #(create-tab %1 %2 selected-view-id) tab-list))]]) From 8622859c3a1fc256c63b437c10ba2e6ce0d93824 Mon Sep 17 00:00:00 2001 From: virvar Date: Thu, 16 Jun 2016 12:44:56 +0300 Subject: [PATCH 04/14] Improve tabs bar animation --- src/status_im/chats_list/screen.cljs | 42 +++++++++++++---------- src/status_im/chats_list/styles.cljs | 11 +++++- src/status_im/components/tabs/styles.cljs | 6 ++-- src/status_im/components/tabs/tabs.cljs | 30 +++++++++------- src/status_im/db.cljs | 3 +- 5 files changed, 58 insertions(+), 34 deletions(-) diff --git a/src/status_im/chats_list/screen.cljs b/src/status_im/chats_list/screen.cljs index afd8047b39..ad4624f798 100644 --- a/src/status_im/chats_list/screen.cljs +++ b/src/status_im/chats_list/screen.cljs @@ -4,6 +4,7 @@ [status-im.components.react :refer [list-view list-item view + animated-view text image touchable-highlight]] @@ -38,7 +39,9 @@ (defn chats-list [] (let [chats (subscribe [:get :chats]) - chat-scrolled? (subscribe [:get :chats-scrolled?]) + chats-scrolled? (subscribe [:get :chats-scrolled?]) + animation? (subscribe [:animations :tabs-bar-animation?]) + tabs-bar-value (subscribe [:animations :tabs-bar-value]) container-height (r/atom 0) content-height (r/atom 0)] (dispatch [:set :chats-scrolled? false]) @@ -53,7 +56,7 @@ ;;; if "maximazing" chat list will make scroll to 0, ;;; then disable maximazing :onLayout (fn [event] - (when-not @chat-scrolled? + (when-not @chats-scrolled? (let [height (.. event -nativeEvent -layout -height)] (reset! container-height height)))) :onContentSizeChange (fn [width height] @@ -62,19 +65,22 @@ (let [offset (.. e -nativeEvent -contentOffset -y) min-content-height (+ @container-height tabs-height) scrolled? (and (< 0 offset) (< min-content-height @content-height))] - (dispatch [:set :chats-scrolled? scrolled?])))}] - [action-button {:buttonColor color-blue - :offsetY 16 - :offsetX 16} - [action-button-item - {:title (label :t/new-chat) - :buttonColor :#9b59b6 - :onPress #(dispatch [:navigate-to :contact-list])} - [icon {:name :android-create - :style st/create-icon}]] - [action-button-item - {:title (label :t/new-group-chat) - :buttonColor :#1abc9c - :onPress #(dispatch [:show-group-new])} - [icon {:name :person-stalker - :style st/person-stalker-icon}]]]]]))) + (dispatch [:set :chats-scrolled? scrolled?]) + (dispatch [:set-animation :tabs-bar-animation? true])))}] + [animated-view {:style (st/action-buttons-container @animation? (or @tabs-bar-value 0)) + :pointerEvents :box-none} + [action-button {:buttonColor color-blue + :offsetY 16 + :offsetX 16} + [action-button-item + {:title (label :t/new-chat) + :buttonColor :#9b59b6 + :onPress #(dispatch [:navigate-to :contact-list])} + [icon {:name :android-create + :style st/create-icon}]] + [action-button-item + {:title (label :t/new-group-chat) + :buttonColor :#1abc9c + :onPress #(dispatch [:show-group-new])} + [icon {:name :person-stalker + :style st/person-stalker-icon}]]]]]]))) diff --git a/src/status_im/chats_list/styles.cljs b/src/status_im/chats_list/styles.cljs index ca72f89042..6621aedfce 100644 --- a/src/status_im/chats_list/styles.cljs +++ b/src/status_im/chats_list/styles.cljs @@ -6,7 +6,8 @@ online-color text1-color text2-color - new-messages-count-color]])) + new-messages-count-color]] + [status-im.components.tabs.styles :refer [tabs-height]])) (def chat-container {:flexDirection :row @@ -113,3 +114,11 @@ {:fontSize 20 :height 22 :color :white}) + +(defn action-buttons-container [animation? offset-y] + {:position :absolute + :left 0 + :right 0 + :top 0 + :bottom 0 + :transform [{:translateY (if animation? offset-y 1)}]}) diff --git a/src/status_im/components/tabs/styles.cljs b/src/status_im/components/tabs/styles.cljs index 7dc57d6d24..2d873e88cd 100644 --- a/src/status_im/components/tabs/styles.cljs +++ b/src/status_im/components/tabs/styles.cljs @@ -13,10 +13,12 @@ (def tabs-height 59) (def tab-height 56) -(defn tabs-container [offset-y] +(defn tabs-container [hidden? animation? offset-y] {:height tabs-height :backgroundColor color-white - :marginBottom offset-y}) + :marginBottom (if (or hidden? animation?) + (- tabs-height) 0) + :transform [{:translateY (if animation? offset-y 1)}]}) (def top-gradient {:flexDirection :row diff --git a/src/status_im/components/tabs/tabs.cljs b/src/status_im/components/tabs/tabs.cljs index bc326a7d43..a6d10d5d8d 100644 --- a/src/status_im/components/tabs/tabs.cljs +++ b/src/status_im/components/tabs/tabs.cljs @@ -20,21 +20,27 @@ [tab data])) (defn animation-logic [{:keys [hidden? val]}] - (fn [_] - (let [to-value (if @hidden? (- st/tab-height) 0)] - (anim/start - (anim/timing val {:toValue to-value - :duration 300}) - (fn [e] - (when-not (.-finished e) - nil)))))) + (let [was-hidden? (atom (not @hidden?))] + (fn [_] + (when (not= @was-hidden? @hidden?) + (let [to-value (if @hidden? 0 (- st/tabs-height))] + (swap! was-hidden? not) + (anim/start + (anim/timing val {:toValue to-value + :duration 300}) + (fn [e] + ;; if to-value was changed, then new animation has started + (when (= to-value (if @hidden? 0 (- st/tabs-height))) + (dispatch [:set-animation :tabs-bar-animation? false]))))))))) (defn tabs-container [& children] (let [chats-scrolled? (subscribe [:get :chats-scrolled?]) - anim-value (anim/create-value 0) - context {:hidden? chats-scrolled? - :val anim-value} + animation? (subscribe [:animations :tabs-bar-animation?]) + tabs-bar-value (subscribe [:animations :tabs-bar-value]) + context {:hidden? chats-scrolled? + :val @tabs-bar-value} on-update (animation-logic context)] + (anim/set-value @tabs-bar-value 0) (r/create-class {:component-did-mount on-update @@ -43,7 +49,7 @@ :reagent-render (fn [& children] @chats-scrolled? - (into [animated-view {:style (st/tabs-container anim-value) + (into [animated-view {:style (st/tabs-container @chats-scrolled? @animation? @tabs-bar-value) :pointerEvents (if @chats-scrolled? :none :auto)}] children))}))) diff --git a/src/status_im/db.cljs b/src/status_im/db.cljs index 2b3ad6f88f..25772a58fa 100644 --- a/src/status_im/db.cljs +++ b/src/status_im/db.cljs @@ -44,7 +44,8 @@ :message-input-buttons-scale 1 :messages-offset 0 :commands-input-is-switching? false - :response-resize? false}}) + :response-resize? false + :tabs-bar-value (anim/create-value 0)}}) (def protocol-initialized-path [:protocol-initialized]) (defn chat-input-text-path [chat-id] From b216d7624a3bacdfcb72c391891b89b1bcf67038 Mon Sep 17 00:00:00 2001 From: virvar Date: Wed, 15 Jun 2016 11:39:38 +0300 Subject: [PATCH 05/14] Keep the keyboard open after message sending --- src/status_im/chat/views/message_input.cljs | 17 ++++++----------- src/status_im/chat/views/plain_message.cljs | 12 +++--------- 2 files changed, 9 insertions(+), 20 deletions(-) diff --git a/src/status_im/chat/views/message_input.cljs b/src/status_im/chat/views/message_input.cljs index 4c26e15eb8..e40cfa4f8f 100644 --- a/src/status_im/chat/views/message_input.cljs +++ b/src/status_im/chat/views/message_input.cljs @@ -6,8 +6,7 @@ animated-view icon touchable-highlight - text-input - dismiss-keyboard!]] + text-input]] [status-im.components.animation :as anim] [status-im.chat.views.plain-message :as plain-message] [status-im.chat.views.command :as command] @@ -61,8 +60,7 @@ staged-commands [:get-chat-staged-commands] typing-command? [:typing-command?] commands-input-is-switching? [:animations :commands-input-is-switching?]] - (let [dismiss-keyboard (not (or command typing-command?)) - response? (and command to-msg-id) + (let [response? (and command to-msg-id) message-input? (or (not command) commands-input-is-switching?) animation? commands-input-is-switching?] [text-input (merge {:style (cond @@ -72,7 +70,7 @@ :ref (fn [input] (dispatch [:set-message-input input])) :autoFocus false - :blurOnSubmit dismiss-keyboard + :blurOnSubmit false :onChangeText (fn [text] (when-not animation? ((if message-input? @@ -82,8 +80,7 @@ :onSubmitEditing #(when-not animation? (if message-input? (plain-message/try-send staged-commands - input-message - dismiss-keyboard) + input-message) (command/try-send input-command validator)))} (when command {:accessibility-label :command-input}) @@ -100,8 +97,7 @@ staged-commands [:get-chat-staged-commands] typing-command? [:typing-command?] commands-input-is-switching? [:animations :commands-input-is-switching?]] - (let [dismiss-keyboard (not (or command typing-command?)) - response? (and command to-msg-id) + (let [response? (and command to-msg-id) message-input? (or (not command) commands-input-is-switching?)] [view st/input-container [view st/input-view @@ -117,8 +113,7 @@ (if message-input? (when (plain-message/message-valid? staged-commands input-message) [send-button {:on-press #(plain-message/try-send staged-commands - input-message - dismiss-keyboard) + input-message) :accessibility-label :send-message}]) (if (command/valid? input-command validator) [send-button {:on-press command/send-command diff --git a/src/status_im/chat/views/plain_message.cljs b/src/status_im/chat/views/plain_message.cljs index 7c637ebbd0..0284e0e25f 100644 --- a/src/status_im/chat/views/plain_message.cljs +++ b/src/status_im/chat/views/plain_message.cljs @@ -5,8 +5,7 @@ [status-im.components.react :refer [view animated-view icon - touchable-highlight - dismiss-keyboard!]] + touchable-highlight]] [status-im.components.animation :as anim] [status-im.chat.styles.plain-message :as st] [status-im.constants :refer [response-input-hiding-duration]])) @@ -14,19 +13,14 @@ (defn set-input-message [message] (dispatch [:set-chat-input-text message])) -(defn send [dismiss-keyboard] - (when dismiss-keyboard - (dismiss-keyboard!)) - (dispatch [:send-chat-msg])) - (defn message-valid? [staged-commands message] (or (and (pos? (count message)) (not= "!" message)) (pos? (count staged-commands)))) -(defn try-send [staged-commands message dismiss-keyboard] +(defn try-send [staged-commands message] (when (message-valid? staged-commands message) - (send dismiss-keyboard))) + (dispatch [:send-chat-msg]))) (defn prepare-message-input [message-input] (when message-input From e223fcbf9ed7ee1032d1d6568c980d38ed637eea Mon Sep 17 00:00:00 2001 From: virvar Date: Fri, 17 Jun 2016 18:38:47 +0300 Subject: [PATCH 06/14] Contacts tab view. Contacts group view. --- src/status_im/android/core.cljs | 3 +- src/status_im/components/styles.cljs | 1 + src/status_im/contacts/screen.cljs | 43 ++++++++++++----- src/status_im/contacts/styles.cljs | 48 ++++++++++++++++++- src/status_im/contacts/views/contact.cljs | 9 +++- .../contacts/views/contact_inner.cljs | 17 ++++++- .../contacts/views/contact_list.cljs | 48 +++++++++++++++++++ src/status_im/navigation/handlers.cljs | 6 +++ src/status_im/translations/en.cljs | 3 ++ 9 files changed, 161 insertions(+), 17 deletions(-) create mode 100644 src/status_im/contacts/views/contact_list.cljs diff --git a/src/status_im/android/core.cljs b/src/status_im/android/core.cljs index 154c7104ce..52c7cee85e 100644 --- a/src/status_im/android/core.cljs +++ b/src/status_im/android/core.cljs @@ -7,7 +7,7 @@ [status-im.subs] [status-im.components.react :refer [navigator app-registry]] [status-im.components.main-tabs :refer [main-tabs]] - [status-im.contacts.screen :refer [contact-list]] + [status-im.contacts.views.contact-list :refer [contact-list] ] [status-im.contacts.views.new-contact :refer [new-contact]] [status-im.qr-scanner.screen :refer [qr-scanner]] [status-im.discovery.screen :refer [discovery]] @@ -46,6 +46,7 @@ :new-group [new-group] :group-settings [group-settings] :contact-list [main-tabs] + :group-contacts [contact-list] :new-contact [new-contact] :qr-scanner [qr-scanner] :chat [chat] diff --git a/src/status_im/components/styles.cljs b/src/status_im/components/styles.cljs index a1176d46bf..4f697ad41e 100644 --- a/src/status_im/components/styles.cljs +++ b/src/status_im/components/styles.cljs @@ -20,6 +20,7 @@ (def text2-color color-gray) (def text3-color color-blue) (def text4-color color-white) +(def text5-color "#838c938f") (def online-color color-blue) (def new-messages-count-color color-blue-transparent) (def chat-background color-light-gray) diff --git a/src/status_im/contacts/screen.cljs b/src/status_im/contacts/screen.cljs index 5ea7be8b15..2be5a9c2cb 100644 --- a/src/status_im/contacts/screen.cljs +++ b/src/status_im/contacts/screen.cljs @@ -4,16 +4,17 @@ [status-im.components.react :refer [view text image touchable-highlight + scroll-view list-view list-item]] [status-im.components.action-button :refer [action-button action-button-item]] - [status-im.contacts.views.contact :refer [contact-view]] - [status-im.components.styles :refer [toolbar-background2]] + [status-im.contacts.views.contact :refer [contact-view-2]] [status-im.components.toolbar :refer [toolbar]] [status-im.components.drawer.view :refer [drawer-view open-drawer]] [status-im.components.icons.ionicons :refer [icon]] [status-im.components.styles :refer [color-blue + flex hamburger-icon icon-search create-icon @@ -23,7 +24,7 @@ [status-im.i18n :refer [label]])) (defn render-row [row _ _] - (list-item [contact-view row])) + (list-item [contact-view-2 row])) (defn contact-list-toolbar [] [toolbar {:nav-action {:image {:source {:uri :icon_hamburger} @@ -36,17 +37,34 @@ :handler (fn [])}}]) (defview contact-list [] - [contacts [:contacts-with-letters]] + [contacts [:get-contacts]] [drawer-view [view st/contacts-list-container [contact-list-toolbar] - ;; todo what if there is no contacts, should we show some information - ;; about this? - (when contacts - [list-view {:dataSource (lw/to-datasource contacts) - :enableEmptySections true - :renderRow render-row - :style st/contacts-list}]) + [scroll-view {:style flex} + ;; TODO not implemented: dapps and persons separation + [view st/contact-group + [text {:style st/contact-group-text} (label :contacs-group-dapps)] + [text {:style st/contact-group-size-text} (str (count contacts))]] + ;; todo what if there is no contacts, should we show some information + ;; about this? + (when contacts + [view {:flexDirection :column} + (for [contact (take 4 contacts)] + ^{:key contact} [contact-view-2 contact])]) + [view st/show-all + [touchable-highlight {:on-press #(dispatch [:show-group-contacts :dapps])} + [text {:style st/show-all-text} (label :show-all)]]] + [view st/contact-group + [text {:style st/contact-group-text} (label :contacs-group-people)] + [text {:style st/contact-group-size-text} (str (count contacts))]] + (when contacts + [view {:flexDirection :column} + (for [contact (take 4 contacts)] + ^{:key contact} [contact-view-2 contact])]) + [view st/show-all + [touchable-highlight {:on-press #(dispatch [:show-group-contacts :people])} + [text {:style st/show-all-text} (label :show-all)]]]] [action-button {:buttonColor color-blue :offsetY 16 :offsetX 16} @@ -55,5 +73,4 @@ :buttonColor :#9b59b6 :onPress #(dispatch [:navigate-to :new-contact])} [icon {:name :android-create - :style create-icon}]] - ]]]) + :style create-icon}]]]]]) diff --git a/src/status_im/contacts/styles.cljs b/src/status_im/contacts/styles.cljs index 59fa3453e3..bf0e3499d8 100644 --- a/src/status_im/contacts/styles.cljs +++ b/src/status_im/contacts/styles.cljs @@ -1,8 +1,11 @@ (ns status-im.contacts.styles (:require [status-im.components.styles :refer [font + font-medium title-font text1-color + text2-color text3-color + text5-color color-white toolbar-background2 online-color]])) @@ -14,6 +17,38 @@ (def contacts-list {:backgroundColor :white}) +(def contact-group + {:flexDirection :row + :alignItems :center + :height 52 + :backgroundColor toolbar-background2}) + +(def contact-group-text + {:flex 1 + :marginLeft 16 + :fontSize 14 + :fontFamily font-medium + :color text5-color}) + +(def contact-group-size-text + {:marginRight 14 + :fontSize 12 + :fontFamily font + :color text2-color}) + +(def show-all + {:flexDirection :row + :alignItems :center + :height 56}) + +(def show-all-text + {:marginLeft 72 + :fontSize 14 + :fontFamily font-medium + :color text3-color + ;; ios only: + :letterSpacing 0.5}) + (def letter-container {:paddingTop 11 :paddingLeft 20 @@ -33,7 +68,8 @@ :height 56}) (def name-container - {:marginLeft 12 + {:flex 1 + :marginLeft 12 :justifyContent :center}) (def name-text @@ -41,6 +77,16 @@ :fontFamily font :color text1-color}) +(def more-btn + {:width 56 + :height 56 + :alignItems :center + :justifyContent :center}) + +(def more-btn-icon + {:width 4 + :height 16}) + ; new contact (def contact-form-container diff --git a/src/status_im/contacts/views/contact.cljs b/src/status_im/contacts/views/contact.cljs index f253789990..cc67dba1d3 100644 --- a/src/status_im/contacts/views/contact.cljs +++ b/src/status_im/contacts/views/contact.cljs @@ -2,7 +2,8 @@ (:require-macros [status-im.utils.views :refer [defview]]) (:require [status-im.components.react :refer [view touchable-highlight]] [re-frame.core :refer [dispatch subscribe]] - [status-im.contacts.views.contact-inner :refer [contact-inner-view]])) + [status-im.contacts.views.contact-inner :refer [contact-inner-view + contact-inner-view-2]])) (defn on-press [chat whisper-identity] (if chat @@ -14,3 +15,9 @@ [touchable-highlight {:onPress (on-press chat whisper-identity)} [view {} [contact-inner-view contact]]]) + +(defview contact-view-2 [{:keys [whisper-identity] :as contact}] + [chat [:get-chat whisper-identity]] + [touchable-highlight + {:onPress (on-press chat whisper-identity)} + [view [contact-inner-view-2 contact]]]) diff --git a/src/status_im/contacts/views/contact_inner.cljs b/src/status_im/contacts/views/contact_inner.cljs index 9512ef3120..2a5eb95257 100644 --- a/src/status_im/contacts/views/contact_inner.cljs +++ b/src/status_im/contacts/views/contact_inner.cljs @@ -1,6 +1,6 @@ (ns status-im.contacts.views.contact-inner (:require [clojure.string :as s] - [status-im.components.react :refer [view image text]] + [status-im.components.react :refer [view image text touchable-highlight icon]] [status-im.components.chat-icon.screen :refer [contact-icon-contacts-tab]] [status-im.contacts.styles :as st] [status-im.i18n :refer [label]])) @@ -25,3 +25,18 @@ name ;; todo is this correct behaviour? (label :t/no-name))]]]) + +(defn contact-inner-view-2 [{:keys [name] :as contact}] + [view st/contact-container + [contact-photo contact] + [view st/name-container + [text {:style st/name-text} + (if (pos? (count name)) + name + ;; todo is this correct behaviour? + (label :t/no-name))]] + [touchable-highlight + ;; TODO not imlemented: contact more button + {:on-press nil} + [view st/more-btn + [icon :more-vertical st/more-btn-icon]]]]) diff --git a/src/status_im/contacts/views/contact_list.cljs b/src/status_im/contacts/views/contact_list.cljs new file mode 100644 index 0000000000..abb60fa465 --- /dev/null +++ b/src/status_im/contacts/views/contact_list.cljs @@ -0,0 +1,48 @@ +(ns status-im.contacts.views.contact-list + (:require-macros [status-im.utils.views :refer [defview]]) + (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] + [status-im.components.react :refer [view text + image + touchable-highlight + list-view + list-item]] + [status-im.contacts.views.contact :refer [contact-view]] + [status-im.components.styles :refer [toolbar-background2]] + [status-im.components.toolbar :refer [toolbar]] + [status-im.components.drawer.view :refer [drawer-view open-drawer]] + [status-im.components.icons.ionicons :refer [icon]] + [status-im.components.styles :refer [color-blue + hamburger-icon + icon-search + create-icon + toolbar-background2]] + [status-im.contacts.styles :as st] + [status-im.utils.listview :as lw] + [status-im.i18n :refer [label]])) + +(defn render-row [row _ _] + (list-item [contact-view row])) + +(defview contact-list-toolbar [] + [group [:get :contacts-group]] + [toolbar {;; TODO contacts group name + :title (label (if (= group :dapps) + :t/contacs-group-dapps + :t/contacs-group-people)) + :background-color toolbar-background2 + :action {:image {:source {:uri :icon_search} + :style icon-search} + :handler (fn [])}}]) + +(defview contact-list [] + [contacts [:contacts-with-letters]] + [drawer-view + [view st/contacts-list-container + [contact-list-toolbar] + ;; todo what if there is no contacts, should we show some information + ;; about this? + (when contacts + [list-view {:dataSource (lw/to-datasource contacts) + :enableEmptySections true + :renderRow render-row + :style st/contacts-list}])]]) diff --git a/src/status_im/navigation/handlers.cljs b/src/status_im/navigation/handlers.cljs index aa9c1eee32..405d172f74 100644 --- a/src/status_im/navigation/handlers.cljs +++ b/src/status_im/navigation/handlers.cljs @@ -67,6 +67,12 @@ (fn [db _] (push-view db :contact-list))) +(register-handler :show-group-contacts + (fn [db [_ group]] + (-> db + (assoc :contacts-group group) + (push-view :group-contacts)))) + (defn show-profile [db [_ identity]] (-> db diff --git a/src/status_im/translations/en.cljs b/src/status_im/translations/en.cljs index 8ec3ba3fb7..96ea2c8768 100644 --- a/src/status_im/translations/en.cljs +++ b/src/status_im/translations/en.cljs @@ -72,6 +72,9 @@ :contacts "Contacts" :no-name "Noname" :new-contact "New Contact" + :show-all "SHOW ALL" + :contacs-group-dapps "DApps" + :contacs-group-people "People" ;group-settings :remove "Remove" From 383602200e5714338091c48d87d7e48ef5290dc4 Mon Sep 17 00:00:00 2001 From: virvar Date: Mon, 20 Jun 2016 12:21:57 +0300 Subject: [PATCH 07/14] Add shadows --- src/status_im/contacts/screen.cljs | 96 +++++++++++++++++------------- src/status_im/contacts/styles.cljs | 51 ++++++++++++++-- 2 files changed, 99 insertions(+), 48 deletions(-) diff --git a/src/status_im/contacts/screen.cljs b/src/status_im/contacts/screen.cljs index 2be5a9c2cb..406d85de1d 100644 --- a/src/status_im/contacts/screen.cljs +++ b/src/status_im/contacts/screen.cljs @@ -1,9 +1,11 @@ (ns status-im.contacts.screen (:require-macros [status-im.utils.views :refer [defview]]) (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] + [reagent.core :as r] [status-im.components.react :refer [view text image touchable-highlight + linear-gradient scroll-view list-view list-item]] @@ -14,7 +16,6 @@ [status-im.components.drawer.view :refer [drawer-view open-drawer]] [status-im.components.icons.ionicons :refer [icon]] [status-im.components.styles :refer [color-blue - flex hamburger-icon icon-search create-icon @@ -27,50 +28,61 @@ (list-item [contact-view-2 row])) (defn contact-list-toolbar [] - [toolbar {:nav-action {:image {:source {:uri :icon_hamburger} - :style hamburger-icon} - :handler open-drawer} + [toolbar {:nav-action {:image {:source {:uri :icon_hamburger} + :style hamburger-icon} + :handler open-drawer} :title (label :t/contacts) :background-color toolbar-background2 + :style {:elevation 0} :action {:image {:source {:uri :icon_search} :style icon-search} :handler (fn [])}}]) -(defview contact-list [] - [contacts [:get-contacts]] - [drawer-view - [view st/contacts-list-container - [contact-list-toolbar] - [scroll-view {:style flex} - ;; TODO not implemented: dapps and persons separation - [view st/contact-group - [text {:style st/contact-group-text} (label :contacs-group-dapps)] - [text {:style st/contact-group-size-text} (str (count contacts))]] - ;; todo what if there is no contacts, should we show some information - ;; about this? - (when contacts - [view {:flexDirection :column} - (for [contact (take 4 contacts)] - ^{:key contact} [contact-view-2 contact])]) - [view st/show-all - [touchable-highlight {:on-press #(dispatch [:show-group-contacts :dapps])} - [text {:style st/show-all-text} (label :show-all)]]] - [view st/contact-group - [text {:style st/contact-group-text} (label :contacs-group-people)] - [text {:style st/contact-group-size-text} (str (count contacts))]] - (when contacts - [view {:flexDirection :column} - (for [contact (take 4 contacts)] - ^{:key contact} [contact-view-2 contact])]) - [view st/show-all - [touchable-highlight {:on-press #(dispatch [:show-group-contacts :people])} - [text {:style st/show-all-text} (label :show-all)]]]] - [action-button {:buttonColor color-blue - :offsetY 16 - :offsetX 16} - [action-button-item - {:title (label :t/new-contact) - :buttonColor :#9b59b6 - :onPress #(dispatch [:navigate-to :new-contact])} - [icon {:name :android-create - :style create-icon}]]]]]) +(defn contact-group [contacts title group top?] + [view st/contact-group + [view st/contact-group-header + (when-not top? + [linear-gradient {:style st/contact-group-header-gradient-top + :colors st/contact-group-header-gradient-top-colors}]) + [view st/contact-group-header-inner + [text {:style st/contact-group-text} title] + [text {:style st/contact-group-size-text} (str (count contacts))]] + [linear-gradient {:style st/contact-group-header-gradient-bottom + :colors st/contact-group-header-gradient-bottom-colors}]] + ;; todo what if there is no contacts, should we show some information + ;; about this? + (when contacts + [view {:flexDirection :column} + (for [contact (take 4 contacts)] + ^{:key contact} [contact-view-2 contact])]) + [view st/show-all + [touchable-highlight {:on-press #(dispatch [:show-group-contacts group])} + [text {:style st/show-all-text} (label :show-all)]]]]) + +(defn contact-list [] + (let [contacts (subscribe [:get-contacts]) + show-toolbar-shadow? (r/atom false)] + (fn [] + [drawer-view + [view st/contacts-list-container + [contact-list-toolbar] + [view {:style st/toolbar-shadow} + (when @show-toolbar-shadow? + [linear-gradient {:style st/contact-group-header-gradient-bottom + :colors st/contact-group-header-gradient-bottom-colors}])] + [scroll-view {:style st/contact-groups + :onScroll (fn [e] + (let [offset (.. e -nativeEvent -contentOffset -y)] + (reset! show-toolbar-shadow? (<= st/contact-group-header-height offset))))} + ;; TODO not implemented: dapps and persons separation + [contact-group @contacts (label :contacs-group-dapps) :dapps true] + [contact-group @contacts (label :contacs-group-people) :people false]] + [action-button {:buttonColor color-blue + :offsetY 16 + :offsetX 16} + [action-button-item + {:title (label :t/new-contact) + :buttonColor :#9b59b6 + :onPress #(dispatch [:navigate-to :new-contact])} + [icon {:name :android-create + :style create-icon}]]]]]))) diff --git a/src/status_im/contacts/styles.cljs b/src/status_im/contacts/styles.cljs index bf0e3499d8..d685191212 100644 --- a/src/status_im/contacts/styles.cljs +++ b/src/status_im/contacts/styles.cljs @@ -14,13 +14,28 @@ {:flex 1 :backgroundColor :white}) +(def toolbar-shadow + {:height 2 + :backgroundColor toolbar-background2}) + +(def contact-groups + {:flex 1 + :backgroundColor toolbar-background2}) + (def contacts-list {:backgroundColor :white}) (def contact-group + {:flexDirection :column}) + +(def contact-group-header + {:flexDirection :column + :backgroundColor toolbar-background2}) + +(def contact-group-header-inner {:flexDirection :row :alignItems :center - :height 52 + :height 48 :backgroundColor toolbar-background2}) (def contact-group-text @@ -36,10 +51,33 @@ :fontFamily font :color text2-color}) +(def contact-group-header-gradient-top + {:flexDirection :row + :height 3 + :backgroundColor toolbar-background2}) + +(def contact-group-header-gradient-top-colors + ["rgba(24, 52, 76, 0.165)" + "rgba(24, 52, 76, 0.03)" + "rgba(24, 52, 76, 0.01)"]) + +(def contact-group-header-gradient-bottom + {:flexDirection :row + :height 2 + :backgroundColor toolbar-background2}) + +(def contact-group-header-gradient-bottom-colors + ["rgba(24, 52, 76, 0.01)" + "rgba(24, 52, 76, 0.05)"]) + +(def contact-group-header-height (+ (:height contact-group-header-inner) + (:height contact-group-header-gradient-bottom))) + (def show-all - {:flexDirection :row - :alignItems :center - :height 56}) + {:flexDirection :row + :alignItems :center + :height 56 + :backgroundColor color-white}) (def show-all-text {:marginLeft 72 @@ -64,8 +102,9 @@ :marginLeft 12}) (def contact-container - {:flexDirection :row - :height 56}) + {:flexDirection :row + :height 56 + :backgroundColor color-white}) (def name-container {:flex 1 From c6205fdd651dba71f5c81dd142a9d6f868c2a166 Mon Sep 17 00:00:00 2001 From: virvar Date: Mon, 20 Jun 2016 12:50:20 +0300 Subject: [PATCH 08/14] Fix contacts sorting --- src/status_im/components/chat_icon/screen.cljs | 7 +++---- src/status_im/contacts/screen.cljs | 2 +- src/status_im/contacts/subs.cljs | 8 ++++++-- src/status_im/contacts/views/contact_inner.cljs | 4 ++-- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/status_im/components/chat_icon/screen.cljs b/src/status_im/components/chat_icon/screen.cljs index 8b89575422..3b6437c193 100644 --- a/src/status_im/components/chat_icon/screen.cljs +++ b/src/status_im/components/chat_icon/screen.cljs @@ -63,8 +63,7 @@ :default-chat-icon (st/default-chat-icon-menu-item color) :default-chat-icon-text st/default-chat-icon-text}]) -(defview contact-icon-view [identity styles] - [contact [:contact-by-identity identity]] +(defn contact-icon-view [contact styles] (let [photo-path (:photo-path contact) ;; TODO stub data online true] @@ -74,8 +73,8 @@ [default-chat-icon (:name contact) styles]) [contact-online online styles]])) -(defn contact-icon-contacts-tab [identity] - [contact-icon-view identity +(defn contact-icon-contacts-tab [contact] + [contact-icon-view contact {:container st/container-chat-list :online-view st/online-view :online-dot-left st/online-dot-left diff --git a/src/status_im/contacts/screen.cljs b/src/status_im/contacts/screen.cljs index 406d85de1d..df87dc87da 100644 --- a/src/status_im/contacts/screen.cljs +++ b/src/status_im/contacts/screen.cljs @@ -60,7 +60,7 @@ [text {:style st/show-all-text} (label :show-all)]]]]) (defn contact-list [] - (let [contacts (subscribe [:get-contacts]) + (let [contacts (subscribe [:all-contacts]) show-toolbar-shadow? (r/atom false)] (fn [] [drawer-view diff --git a/src/status_im/contacts/subs.cljs b/src/status_im/contacts/subs.cljs index 3f5764c9b5..e44c89b36a 100644 --- a/src/status_im/contacts/subs.cljs +++ b/src/status_im/contacts/subs.cljs @@ -7,10 +7,14 @@ (let [contacts (reaction (:contacts @db))] (reaction (vals @contacts))))) +(defn sort-contacts [contacts] + (sort-by :name #(compare (clojure.string/lower-case %1) + (clojure.string/lower-case %2)) (vals contacts))) + (register-sub :all-contacts (fn [db _] (let [contacts (reaction (:contacts @db))] - (reaction (sort-by :name (vals @contacts)))))) + (reaction (sort-contacts @contacts))))) (defn get-contact-letter [contact] (when-let [letter (first (:name contact))] @@ -20,7 +24,7 @@ (fn [db _] (let [contacts (reaction (:contacts @db))] (reaction - (let [ordered (sort-by :name (vals @contacts))] + (let [ordered (sort-contacts @contacts)] (map (fn [prev cur] (let [prev-letter (get-contact-letter prev) cur-letter (get-contact-letter cur)] diff --git a/src/status_im/contacts/views/contact_inner.cljs b/src/status_im/contacts/views/contact_inner.cljs index 2a5eb95257..8b44f601c8 100644 --- a/src/status_im/contacts/views/contact_inner.cljs +++ b/src/status_im/contacts/views/contact_inner.cljs @@ -11,9 +11,9 @@ [text {:style st/letter-text} letter])]) -(defn contact-photo [{:keys [whisper-identity]}] +(defn contact-photo [contact] [view st/contact-photo-container - [contact-icon-contacts-tab whisper-identity]]) + [contact-icon-contacts-tab contact]]) (defn contact-inner-view [{:keys [name letter] :as contact}] [view st/contact-container From dc1c32210647b198c27d9a792d3af3996e5790cb Mon Sep 17 00:00:00 2001 From: virvar Date: Mon, 20 Jun 2016 13:20:37 +0300 Subject: [PATCH 09/14] Contact list toolbar color --- src/status_im/contacts/views/contact_list.cljs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/status_im/contacts/views/contact_list.cljs b/src/status_im/contacts/views/contact_list.cljs index abb60fa465..9010f26881 100644 --- a/src/status_im/contacts/views/contact_list.cljs +++ b/src/status_im/contacts/views/contact_list.cljs @@ -7,7 +7,6 @@ list-view list-item]] [status-im.contacts.views.contact :refer [contact-view]] - [status-im.components.styles :refer [toolbar-background2]] [status-im.components.toolbar :refer [toolbar]] [status-im.components.drawer.view :refer [drawer-view open-drawer]] [status-im.components.icons.ionicons :refer [icon]] @@ -15,7 +14,7 @@ hamburger-icon icon-search create-icon - toolbar-background2]] + toolbar-background1]] [status-im.contacts.styles :as st] [status-im.utils.listview :as lw] [status-im.i18n :refer [label]])) @@ -25,11 +24,10 @@ (defview contact-list-toolbar [] [group [:get :contacts-group]] - [toolbar {;; TODO contacts group name - :title (label (if (= group :dapps) + [toolbar {:title (label (if (= group :dapps) :t/contacs-group-dapps :t/contacs-group-people)) - :background-color toolbar-background2 + :background-color toolbar-background1 :action {:image {:source {:uri :icon_search} :style icon-search} :handler (fn [])}}]) From 93de4680de470003df22d6894f7bd5107d625608 Mon Sep 17 00:00:00 2001 From: virvar Date: Mon, 20 Jun 2016 16:37:26 +0300 Subject: [PATCH 10/14] Contact view refactoring --- src/status_im/chats_list/screen.cljs | 2 +- src/status_im/contacts/screen.cljs | 8 +- src/status_im/contacts/styles.cljs | 18 ++++- src/status_im/contacts/views/contact.cljs | 31 ++++++-- .../contacts/views/contact_inner.cljs | 48 ++++-------- .../contacts/views/contact_list.cljs | 4 +- .../group_settings/styles/member.cljs | 78 ------------------- .../group_settings/views/member.cljs | 47 ++--------- 8 files changed, 68 insertions(+), 168 deletions(-) delete mode 100644 src/status_im/group_settings/styles/member.cljs diff --git a/src/status_im/chats_list/screen.cljs b/src/status_im/chats_list/screen.cljs index 572f0ac5a9..ae6219d985 100644 --- a/src/status_im/chats_list/screen.cljs +++ b/src/status_im/chats_list/screen.cljs @@ -46,7 +46,7 @@ [action-button-item {:title (label :t/new-chat) :buttonColor :#9b59b6 - :onPress #(dispatch [:navigate-to :contact-list])} + :onPress #(dispatch [:show-group-contacts :people])} [icon {:name :android-create :style st/create-icon}]] [action-button-item diff --git a/src/status_im/contacts/screen.cljs b/src/status_im/contacts/screen.cljs index df87dc87da..db0e2e1b2e 100644 --- a/src/status_im/contacts/screen.cljs +++ b/src/status_im/contacts/screen.cljs @@ -11,7 +11,7 @@ list-item]] [status-im.components.action-button :refer [action-button action-button-item]] - [status-im.contacts.views.contact :refer [contact-view-2]] + [status-im.contacts.views.contact :refer [contact-extended-view]] [status-im.components.toolbar :refer [toolbar]] [status-im.components.drawer.view :refer [drawer-view open-drawer]] [status-im.components.icons.ionicons :refer [icon]] @@ -24,9 +24,6 @@ [status-im.utils.listview :as lw] [status-im.i18n :refer [label]])) -(defn render-row [row _ _] - (list-item [contact-view-2 row])) - (defn contact-list-toolbar [] [toolbar {:nav-action {:image {:source {:uri :icon_hamburger} :style hamburger-icon} @@ -54,7 +51,8 @@ (when contacts [view {:flexDirection :column} (for [contact (take 4 contacts)] - ^{:key contact} [contact-view-2 contact])]) + ;; TODO not imlemented: contact more button handler + ^{:key contact} [contact-extended-view contact nil nil])]) [view st/show-all [touchable-highlight {:on-press #(dispatch [:show-group-contacts group])} [text {:style st/show-all-text} (label :show-all)]]]]) diff --git a/src/status_im/contacts/styles.cljs b/src/status_im/contacts/styles.cljs index d685191212..05e6d22c01 100644 --- a/src/status_im/contacts/styles.cljs +++ b/src/status_im/contacts/styles.cljs @@ -87,6 +87,10 @@ ;; ios only: :letterSpacing 0.5}) +(def contact-container + {:flexDirection :row + :backgroundColor color-white}) + (def letter-container {:paddingTop 11 :paddingLeft 20 @@ -101,13 +105,15 @@ {:marginTop 4 :marginLeft 12}) -(def contact-container - {:flexDirection :row +(def contact-inner-container + {:flex 1 + :flexDirection :row :height 56 :backgroundColor color-white}) -(def name-container +(def info-container {:flex 1 + :flexDirection :column :marginLeft 12 :justifyContent :center}) @@ -116,6 +122,12 @@ :fontFamily font :color text1-color}) +(def info-text + {:marginTop 1 + :fontSize 12 + :fontFamily font + :color text2-color}) + (def more-btn {:width 56 :height 56 diff --git a/src/status_im/contacts/views/contact.cljs b/src/status_im/contacts/views/contact.cljs index cc67dba1d3..9a0d532fde 100644 --- a/src/status_im/contacts/views/contact.cljs +++ b/src/status_im/contacts/views/contact.cljs @@ -1,23 +1,42 @@ (ns status-im.contacts.views.contact (:require-macros [status-im.utils.views :refer [defview]]) - (:require [status-im.components.react :refer [view touchable-highlight]] + (:require [status-im.components.react :refer [view text icon touchable-highlight]] [re-frame.core :refer [dispatch subscribe]] - [status-im.contacts.views.contact-inner :refer [contact-inner-view - contact-inner-view-2]])) + [status-im.contacts.styles :as st] + [status-im.contacts.views.contact-inner :refer [contact-inner-view]])) + +(defn letter-view [letter] + [view st/letter-container + (when letter + [text {:style st/letter-text} + letter])]) (defn on-press [chat whisper-identity] (if chat #(dispatch [:navigate-to :chat whisper-identity]) #(dispatch [:start-chat whisper-identity]))) -(defview contact-view [{:keys [whisper-identity] :as contact}] +(defview contact-with-letter-view [{:keys [whisper-identity letter] :as contact}] + [chat [:get-chat whisper-identity]] + [touchable-highlight + {:onPress (on-press chat whisper-identity)} + [view st/contact-container + [letter-view letter] + [contact-inner-view contact]]]) + +(defview contact-simple-view [{:keys [whisper-identity] :as contact}] [chat [:get-chat whisper-identity]] [touchable-highlight {:onPress (on-press chat whisper-identity)} [view {} [contact-inner-view contact]]]) -(defview contact-view-2 [{:keys [whisper-identity] :as contact}] +(defview contact-extended-view [{:keys [whisper-identity] :as contact} info more-click-handler] [chat [:get-chat whisper-identity]] [touchable-highlight {:onPress (on-press chat whisper-identity)} - [view [contact-inner-view-2 contact]]]) + [view st/contact-container + [contact-inner-view contact info] + [touchable-highlight + {:on-press more-click-handler} + [view st/more-btn + [icon :more-vertical st/more-btn-icon]]]]]) diff --git a/src/status_im/contacts/views/contact_inner.cljs b/src/status_im/contacts/views/contact_inner.cljs index 8b44f601c8..4c27d5e1a1 100644 --- a/src/status_im/contacts/views/contact_inner.cljs +++ b/src/status_im/contacts/views/contact_inner.cljs @@ -1,42 +1,26 @@ (ns status-im.contacts.views.contact-inner (:require [clojure.string :as s] - [status-im.components.react :refer [view image text touchable-highlight icon]] + [status-im.components.react :refer [view image text]] [status-im.components.chat-icon.screen :refer [contact-icon-contacts-tab]] [status-im.contacts.styles :as st] [status-im.i18n :refer [label]])) -(defn letter-view [letter] - [view st/letter-container - (when letter - [text {:style st/letter-text} - letter])]) - (defn contact-photo [contact] [view st/contact-photo-container [contact-icon-contacts-tab contact]]) -(defn contact-inner-view [{:keys [name letter] :as contact}] - [view st/contact-container - [letter-view letter] - [contact-photo contact] - [view st/name-container - [text {:style st/name-text} - (if (pos? (count name)) - name - ;; todo is this correct behaviour? - (label :t/no-name))]]]) - -(defn contact-inner-view-2 [{:keys [name] :as contact}] - [view st/contact-container - [contact-photo contact] - [view st/name-container - [text {:style st/name-text} - (if (pos? (count name)) - name - ;; todo is this correct behaviour? - (label :t/no-name))]] - [touchable-highlight - ;; TODO not imlemented: contact more button - {:on-press nil} - [view st/more-btn - [icon :more-vertical st/more-btn-icon]]]]) +(defn contact-inner-view + ([contact] + (contact-inner-view contact nil)) + ([{:keys [name] :as contact} info] + [view st/contact-inner-container + [contact-photo contact] + [view st/info-container + [text {:style st/name-text} + (if (pos? (count (:name contact))) + name + ;; todo is this correct behaviour? + (label :t/no-name))] + (when info + [text {:style st/info-text} + info])]])) diff --git a/src/status_im/contacts/views/contact_list.cljs b/src/status_im/contacts/views/contact_list.cljs index 9010f26881..9a346e3829 100644 --- a/src/status_im/contacts/views/contact_list.cljs +++ b/src/status_im/contacts/views/contact_list.cljs @@ -6,7 +6,7 @@ touchable-highlight list-view list-item]] - [status-im.contacts.views.contact :refer [contact-view]] + [status-im.contacts.views.contact :refer [contact-with-letter-view]] [status-im.components.toolbar :refer [toolbar]] [status-im.components.drawer.view :refer [drawer-view open-drawer]] [status-im.components.icons.ionicons :refer [icon]] @@ -20,7 +20,7 @@ [status-im.i18n :refer [label]])) (defn render-row [row _ _] - (list-item [contact-view row])) + (list-item [contact-with-letter-view row])) (defview contact-list-toolbar [] [group [:get :contacts-group]] diff --git a/src/status_im/group_settings/styles/member.cljs b/src/status_im/group_settings/styles/member.cljs deleted file mode 100644 index e5ef3f9f58..0000000000 --- a/src/status_im/group_settings/styles/member.cljs +++ /dev/null @@ -1,78 +0,0 @@ -(ns status-im.group-settings.styles.member - (:require [status-im.components.styles :refer [font - title-font - text1-color - text2-color - color-white - online-color]])) - -(def contact-photo-container - {:borderRadius 50}) - -(def photo-image - {:borderRadius 50 - :width 40 - :height 40}) - -(def online-container - {:position :absolute - :top 24 - :left 24 - :width 20 - :height 20 - :borderRadius 50 - :backgroundColor online-color - :borderWidth 2 - :borderColor color-white}) - -(def online-dot - {:position :absolute - :top 6 - :width 4 - :height 4 - :borderRadius 50 - :backgroundColor color-white}) - -(def online-dot-left - (assoc online-dot :left 3)) - -(def online-dot-right - (assoc online-dot :left 9)) - -(def contact-container - {:flexDirection :row - :height 56}) - -(def photo-container - {:marginTop 8 - :marginLeft 16 - :width 44 - :height 44}) - -(def info-container - {:flex 1 - :flexDirection :column - :marginLeft 16 - :justifyContent :center}) - -(def name-text - {:marginTop -2 - :fontSize 16 - :fontFamily font - :color text1-color}) - -(def role-text - {:marginTop 1 - :fontSize 12 - :fontFamily font - :color text2-color}) - -(def more-btn - {:width 56 - :height 56 - :alignItems :center - :justifyContent :center }) - -(def more-btn-icon - {:width 4 - :height 16}) diff --git a/src/status_im/group_settings/views/member.cljs b/src/status_im/group_settings/views/member.cljs index c5292daa36..e73d46cc2a 100644 --- a/src/status_im/group_settings/views/member.cljs +++ b/src/status_im/group_settings/views/member.cljs @@ -1,44 +1,9 @@ (ns status-im.group-settings.views.member - (:require [clojure.string :as s] - [re-frame.core :refer [subscribe dispatch dispatch-sync]] - [status-im.components.react :refer [view - image - text - icon - touchable-highlight]] - [status-im.resources :as res] - [status-im.group-settings.styles.member :as st] + (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] + [status-im.contacts.views.contact :refer [contact-extended-view]] [status-im.i18n :refer [label]])) -(defn contact-photo [{:keys [photo-path]}] - [view st/contact-photo-container - [image {:source (if (s/blank? photo-path) - res/user-no-photo - {:uri photo-path}) - :style st/photo-image}]]) - -(defn contact-online [{:keys [online]}] - (when online - [view st/online-container - [view st/online-dot-left] - [view st/online-dot-right]])) - -(defn member-view [{:keys [whisper-identity name photo-path online role]}] - [view st/contact-container - [view st/photo-container - [contact-photo {:photo-path photo-path}] - [contact-online {:online online}]] - [view st/info-container - [text {:style st/name-text} - (if (pos? (count name)) - name - ;; todo is this correct behaviour? - (label :t/no-name))] - ;; TODO implement :role property for group chat contact - (when role - [text {:style st/role-text} - role])] - [touchable-highlight - {:on-press #(dispatch [:set :selected-participants #{whisper-identity}])} - [view st/more-btn - [icon :more-vertical st/more-btn-icon]]]]) +(defn member-view [{:keys [whisper-identity role] :as contact}] + ;; TODO implement :role property for group chat contact + [contact-extended-view contact role + #(dispatch [:set :selected-participants #{whisper-identity}])]) From defc5a44b35e1b4baef3a24f218878143514a905 Mon Sep 17 00:00:00 2001 From: Jarrad Hope Date: Tue, 21 Jun 2016 13:46:46 +0200 Subject: [PATCH 11/14] set linear-gradient to last known working version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b26cf32faf..877b612afe 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "react-native-contacts": "^0.2.4", "react-native-i18n": "0.0.8", "react-native-invertible-scroll-view": "^1.0.0", - "react-native-linear-gradient": "^1.5.7", + "react-native-linear-gradient": "1.5.7", "react-native-loading-spinner-overlay": "0.0.8", "react-native-qrcode": "^0.2.2", "react-native-randombytes": "^2.1.0", From eab8dd5ca21b851da7fa508b978a0fb1ffa79647 Mon Sep 17 00:00:00 2001 From: Adrian Tiberius Date: Tue, 21 Jun 2016 15:23:19 +0300 Subject: [PATCH 12/14] added identicons for default profile pictures --- .re-natal | 3 ++- package.json | 1 + src/status_im/contacts/views/new_contact.cljs | 3 ++- src/status_im/models/contacts.cljs | 5 +++-- src/status_im/utils/identicon.cljs | 9 +++++++++ 5 files changed, 17 insertions(+), 4 deletions(-) create mode 100644 src/status_im/utils/identicon.cljs diff --git a/.re-natal b/.re-natal index 4fecaf5ca5..a547f09695 100644 --- a/.re-natal +++ b/.re-natal @@ -18,7 +18,8 @@ "react-native-linear-gradient", "react-native-android-sms-listener", "react-native-camera", - "react-native-qrcode" + "react-native-qrcode", + "identicon.js" ], "imageDirs": [ "images" diff --git a/package.json b/package.json index b26cf32faf..dae27c270e 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ }, "dependencies": { "awesome-phonenumber": "^1.0.13", + "identicon.js": "github:status-im/identicon.js", "react": "^0.14.5", "react-native": "^0.24.1", "react-native-action-button": "^1.1.4", diff --git a/src/status_im/contacts/views/new_contact.cljs b/src/status_im/contacts/views/new_contact.cljs index 469ed6b914..8a6132f19d 100644 --- a/src/status_im/contacts/views/new_contact.cljs +++ b/src/status_im/contacts/views/new_contact.cljs @@ -7,6 +7,7 @@ image linear-gradient touchable-highlight]] + [status-im.utils.identicon :refer [identicon]] [status-im.components.toolbar :refer [toolbar]] [status-im.components.drawer.view :refer [drawer-view open-drawer]] [status-im.components.styles :refer [color-purple @@ -71,7 +72,7 @@ :custom-content toolbar-title :action {:image {:source {:uri :icon_add} :style icon-search} - :handler #(dispatch [:add-new-contact new-contact])}}] + :handler #(dispatch [:add-new-contact (merge new-contact {:photo-path (identicon whisper-identity 40)})])}}] [view st/form-container [contact-whisper-id-input whisper-identity] [contact-name-input name] diff --git a/src/status_im/models/contacts.cljs b/src/status_im/models/contacts.cljs index ec286b17a6..03194bc37c 100644 --- a/src/status_im/models/contacts.cljs +++ b/src/status_im/models/contacts.cljs @@ -1,5 +1,6 @@ (ns status-im.models.contacts (:require [status-im.persistence.realm :as r] + [status-im.utils.identicon :refer [identicon]] [status-im.persistence.realm-queries :refer [include-query exclude-query]])) @@ -8,9 +9,9 @@ (r/sorted :name :asc) r/collection->map)) -(defn create-contact [{:keys [name photo-path] :as contact}] +(defn create-contact [{:keys [name photo-path whisper-identity] :as contact}] (->> {:name (or name "") - :photo-path (or photo-path "")} + :photo-path (or photo-path (identicon whisper-identity 40))} (merge contact) (r/create :contacts))) diff --git a/src/status_im/utils/identicon.cljs b/src/status_im/utils/identicon.cljs new file mode 100644 index 0000000000..de585e4b04 --- /dev/null +++ b/src/status_im/utils/identicon.cljs @@ -0,0 +1,9 @@ +(ns status-im.utils.identicon + (:require [clojure.string :as s] + [status-im.utils.utils :as u])) + +(def identicon-js (u/require "identicon.js")) + +(defn identicon [hash options] + (str "data:image/png;base64," (.toString (new identicon-js hash options)))) + From 5a409ed6bf803c75c128ab74ad78fdcb84113bab Mon Sep 17 00:00:00 2001 From: Adrian Tiberius Date: Tue, 21 Jun 2016 16:05:46 +0300 Subject: [PATCH 13/14] cleanup --- src/status_im/contacts/views/new_contact.cljs | 2 +- src/status_im/models/contacts.cljs | 2 +- src/status_im/utils/identicon.cljs | 8 ++++++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/status_im/contacts/views/new_contact.cljs b/src/status_im/contacts/views/new_contact.cljs index 8a6132f19d..4af9d3c5d4 100644 --- a/src/status_im/contacts/views/new_contact.cljs +++ b/src/status_im/contacts/views/new_contact.cljs @@ -72,7 +72,7 @@ :custom-content toolbar-title :action {:image {:source {:uri :icon_add} :style icon-search} - :handler #(dispatch [:add-new-contact (merge new-contact {:photo-path (identicon whisper-identity 40)})])}}] + :handler #(dispatch [:add-new-contact (merge {:photo-path (identicon whisper-identity)} new-contact)])}}] [view st/form-container [contact-whisper-id-input whisper-identity] [contact-name-input name] diff --git a/src/status_im/models/contacts.cljs b/src/status_im/models/contacts.cljs index 03194bc37c..6c3ea62a7a 100644 --- a/src/status_im/models/contacts.cljs +++ b/src/status_im/models/contacts.cljs @@ -11,7 +11,7 @@ (defn create-contact [{:keys [name photo-path whisper-identity] :as contact}] (->> {:name (or name "") - :photo-path (or photo-path (identicon whisper-identity 40))} + :photo-path (or photo-path (identicon whisper-identity))} (merge contact) (r/create :contacts))) diff --git a/src/status_im/utils/identicon.cljs b/src/status_im/utils/identicon.cljs index de585e4b04..2d8dac64e1 100644 --- a/src/status_im/utils/identicon.cljs +++ b/src/status_im/utils/identicon.cljs @@ -2,8 +2,12 @@ (:require [clojure.string :as s] [status-im.utils.utils :as u])) +(def default-size 40) + (def identicon-js (u/require "identicon.js")) -(defn identicon [hash options] - (str "data:image/png;base64," (.toString (new identicon-js hash options)))) +(defn identicon + ([hash] (identicon hash default-size)) + ([hash options] + (str "data:image/png;base64," (.toString (new identicon-js hash options))))) From dc567356ca90eac2d99c73b4a0008c65ac748e39 Mon Sep 17 00:00:00 2001 From: Roman Volosovskyi Date: Tue, 21 Jun 2016 18:48:41 +0300 Subject: [PATCH 14/14] no contacts screen & some fixes --- .../main/res/drawable-hdpi/icon_group_big.png | Bin 0 -> 2577 bytes .../main/res/drawable-mdpi/icon_group_big.png | Bin 0 -> 1599 bytes .../res/drawable-xhdpi/icon_group_big.png | Bin 0 -> 3114 bytes .../res/drawable-xxhdpi/icon_group_big.png | Bin 0 -> 5197 bytes .../res/drawable-xxxhdpi/icon_group_big.png | Bin 0 -> 7086 bytes src/status_im/components/styles.cljs | 3 +- src/status_im/contacts/screen.cljs | 54 +++++++++++------- src/status_im/contacts/styles.cljs | 19 +++++- src/status_im/contacts/subs.cljs | 51 ++++++++++------- src/status_im/contacts/views/contact.cljs | 9 +-- src/status_im/discovery/styles.cljs | 3 +- src/status_im/translations/en.cljs | 7 ++- 12 files changed, 91 insertions(+), 55 deletions(-) create mode 100644 android/app/src/main/res/drawable-hdpi/icon_group_big.png create mode 100644 android/app/src/main/res/drawable-mdpi/icon_group_big.png create mode 100644 android/app/src/main/res/drawable-xhdpi/icon_group_big.png create mode 100644 android/app/src/main/res/drawable-xxhdpi/icon_group_big.png create mode 100644 android/app/src/main/res/drawable-xxxhdpi/icon_group_big.png diff --git a/android/app/src/main/res/drawable-hdpi/icon_group_big.png b/android/app/src/main/res/drawable-hdpi/icon_group_big.png new file mode 100644 index 0000000000000000000000000000000000000000..68687a6dfa3621e6c4e87b071bb85553ab0514ec GIT binary patch literal 2577 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 @chat - :contacts - (map :identity) - set)] - (fn #(current-participants (:whisper-identity %)) - (vals @contacts))))))) + (when @chat + (let [current-participants (->> @chat + :contacts + (map :identity) + set)] + (fn #(current-participants (:whisper-identity %)) + (vals @contacts))))))) (defn contacts-by-current-chat [fn db] (let [current-chat-id (:current-chat-id @db)] @@ -72,8 +83,8 @@ chat (reaction (get-in @db [:chats chat-id])) contacts (contacts-by-chat filter db chat-id)] (reaction - (when @chat - (if (:group-chat @chat) - ;; TODO return group chat icon - nil - (:photo-path (first @contacts)))))))) + (when @chat + (if (:group-chat @chat) + ;; TODO return group chat icon + nil + (:photo-path (first @contacts)))))))) diff --git a/src/status_im/contacts/views/contact.cljs b/src/status_im/contacts/views/contact.cljs index 9a0d532fde..b63ea9c31e 100644 --- a/src/status_im/contacts/views/contact.cljs +++ b/src/status_im/contacts/views/contact.cljs @@ -8,8 +8,7 @@ (defn letter-view [letter] [view st/letter-container (when letter - [text {:style st/letter-text} - letter])]) + [text {:style st/letter-text} letter])]) (defn on-press [chat whisper-identity] (if chat @@ -24,12 +23,6 @@ [letter-view letter] [contact-inner-view contact]]]) -(defview contact-simple-view [{:keys [whisper-identity] :as contact}] - [chat [:get-chat whisper-identity]] - [touchable-highlight - {:onPress (on-press chat whisper-identity)} - [view {} [contact-inner-view contact]]]) - (defview contact-extended-view [{:keys [whisper-identity] :as contact} info more-click-handler] [chat [:get-chat whisper-identity]] [touchable-highlight diff --git a/src/status_im/discovery/styles.cljs b/src/status_im/discovery/styles.cljs index dd998b352d..540dc04a03 100644 --- a/src/status_im/discovery/styles.cljs +++ b/src/status_im/discovery/styles.cljs @@ -2,6 +2,7 @@ (:require [status-im.components.styles :refer [font title-font color-white + color-gray2 chat-background online-color selected-message-color @@ -49,7 +50,7 @@ :elevation 0}) (def discovery-subtitle - {:color "#8f838c93" + {:color color-gray2 :fontFamily "sans-serif-medium" :fontSize 14}) diff --git a/src/status_im/translations/en.cljs b/src/status_im/translations/en.cljs index 96ea2c8768..792dc4ebd5 100644 --- a/src/status_im/translations/en.cljs +++ b/src/status_im/translations/en.cljs @@ -73,8 +73,9 @@ :no-name "Noname" :new-contact "New Contact" :show-all "SHOW ALL" - :contacs-group-dapps "DApps" + :contacs-group-dapps "Dapps" :contacs-group-people "People" + :no-contacts "No contacts yet" ;group-settings :remove "Remove" @@ -130,6 +131,6 @@ :login "Login" ;users - :add-account "Add account" + :add-account "Add account" - }) \ No newline at end of file + })