From c4a7849c9daa106ccee9b85c6d877812791d8973 Mon Sep 17 00:00:00 2001 From: Andrey Shovkoplyas Date: Tue, 10 Mar 2020 16:43:27 +0100 Subject: [PATCH] chat screens performance Signed-off-by: Andrey Shovkoplyas --- .../src/status_im/ui/components/react.cljs | 4 +- fiddle/src/status_im/ui/components/react.cljs | 4 +- src/status_im/chat/models.cljs | 25 +- src/status_im/chat/models/message.cljs | 6 +- src/status_im/events.cljs | 4 +- src/status_im/group_chats/core.cljs | 2 +- src/status_im/subs.cljs | 44 +- .../ui/components/connectivity/styles.cljs | 29 +- .../ui/components/connectivity/view.cljs | 205 ++++---- src/status_im/ui/components/list/views.cljs | 26 +- .../ui/components/search_input/view.cljs | 84 ++-- src/status_im/ui/components/topbar.cljs | 8 +- .../ui/screens/chat/bottom_info.cljs | 16 - src/status_im/ui/screens/chat/group.cljs | 85 ++++ .../chat/input/animations/expandable.cljs | 47 -- .../ui/screens/chat/input/input.cljs | 23 +- .../ui/screens/chat/input/send_button.cljs | 3 +- .../ui/screens/chat/message/command.cljs | 257 ++++++++++ .../ui/screens/chat/message/datemark.cljs | 9 +- .../ui/screens/chat/message/message.cljs | 413 +++------------ src/status_im/ui/screens/chat/photos.cljs | 17 +- .../ui/screens/chat/styles/main.cljs | 163 +----- .../screens/chat/styles/message/message.cljs | 26 +- .../ui/screens/chat/toolbar_content.cljs | 12 +- src/status_im/ui/screens/chat/ttt.cljs | 92 ++++ src/status_im/ui/screens/chat/utils.cljs | 69 +-- src/status_im/ui/screens/chat/views.cljs | 474 ++++-------------- .../ui/screens/home/sheet/views.cljs | 6 +- src/status_im/ui/screens/home/styles.cljs | 90 +--- src/status_im/ui/screens/home/views.cljs | 175 ++----- .../ui/screens/home/views/inner_item.cljs | 41 +- .../ui/screens/routing/chat_stack.cljs | 4 +- src/status_im/ui/screens/routing/modals.cljs | 3 +- src/status_im/ui/screens/routing/screens.cljs | 4 +- src/status_im/utils/contenthash.cljs | 4 +- 35 files changed, 886 insertions(+), 1588 deletions(-) delete mode 100644 src/status_im/ui/screens/chat/bottom_info.cljs create mode 100644 src/status_im/ui/screens/chat/group.cljs delete mode 100644 src/status_im/ui/screens/chat/input/animations/expandable.cljs create mode 100644 src/status_im/ui/screens/chat/message/command.cljs create mode 100644 src/status_im/ui/screens/chat/ttt.cljs diff --git a/components/src/status_im/ui/components/react.cljs b/components/src/status_im/ui/components/react.cljs index d3957ce58b..541c8f6b4f 100644 --- a/components/src/status_im/ui/components/react.cljs +++ b/components/src/status_im/ui/components/react.cljs @@ -314,9 +314,7 @@ (defn main-screen-modal-view [current-view & components] [(create-main-screen-view current-view) styles/flex - [(if (= current-view :chat-modal) - view - keyboard-avoiding-view) + [keyboard-avoiding-view (merge {:flex 1 :flex-direction :column} (when platform/android? {:background-color :white})) diff --git a/fiddle/src/status_im/ui/components/react.cljs b/fiddle/src/status_im/ui/components/react.cljs index 00a65e2a7c..5acae7d96b 100644 --- a/fiddle/src/status_im/ui/components/react.cljs +++ b/fiddle/src/status_im/ui/components/react.cljs @@ -324,8 +324,6 @@ (views/letsubs [] (let [main-screen-view (create-main-screen-view current-view)] [main-screen-view styles/flex - [(if (= current-view :chat-modal) - view - keyboard-avoiding-view) + [keyboard-avoiding-view {:flex 1 :flex-direction :column} (apply vector view styles/flex components)]]))) diff --git a/src/status_im/chat/models.cljs b/src/status_im/chat/models.cljs index 7fc6b3f9e3..7eb96eccbc 100644 --- a/src/status_im/chat/models.cljs +++ b/src/status_im/chat/models.cljs @@ -248,40 +248,33 @@ (fx/defn navigate-to-chat "Takes coeffects map and chat-id, returns effects necessary for navigation and preloading data" - [cofx chat-id {:keys [modal? navigation-reset?]}] - (cond - modal? - (fx/merge cofx - (navigation/navigate-to-cofx :chat-modal {}) - (preload-chat-data chat-id)) - - :else - (fx/merge cofx - (navigation/navigate-to-cofx :chat {}) - (preload-chat-data chat-id)))) + [cofx chat-id] + (fx/merge cofx + (navigation/navigate-to-cofx :chat {}) + (preload-chat-data chat-id))) (fx/defn start-chat "Start a chat, making sure it exists" - [{:keys [db] :as cofx} chat-id opts] + [{:keys [db] :as cofx} chat-id _] ;; don't allow to open chat with yourself (when (not= (multiaccounts.model/current-public-key cofx) chat-id) (fx/merge cofx (upsert-chat {:chat-id chat-id :is-active true}) (transport.filters/load-chat chat-id) - (navigate-to-chat chat-id opts)))) + (navigate-to-chat chat-id)))) (fx/defn start-public-chat "Starts a new public chat" - [cofx topic {:keys [dont-navigate?] :as opts}] + [cofx topic {:keys [dont-navigate?]}] (if (active-chat? cofx topic) (when-not dont-navigate? - (navigate-to-chat cofx topic opts)) + (navigate-to-chat cofx topic)) (fx/merge cofx (add-public-chat topic) (transport.filters/load-chat topic) #(when-not dont-navigate? - (navigate-to-chat % topic opts))))) + (navigate-to-chat % topic))))) (fx/defn disable-chat-cooldown "Turns off chat cooldown (protection against message spamming)" diff --git a/src/status_im/chat/models/message.cljs b/src/status_im/chat/models/message.cljs index 03d152eed2..99b7a89124 100644 --- a/src/status_im/chat/models/message.cljs +++ b/src/status_im/chat/models/message.cljs @@ -110,8 +110,7 @@ content] :as message}] (let [{:keys [current-chat-id view-id]} db cursor-clock-value (get-in db [:chats current-chat-id :cursor-clock-value]) - current-chat? (and (or (= :chat view-id) - (= :chat-modal view-id)) + current-chat? (and (= :chat view-id) (= current-chat-id chat-id))] (when (and current-chat? (or (not cursor-clock-value) @@ -165,8 +164,7 @@ message-id] :as message}] (when-not (= message-type constants/message-type-private-group-system-message) (let [{:keys [current-chat-id view-id]} db - chat-view? (or (= :chat view-id) - (= :chat-modal view-id)) + chat-view? (= :chat view-id) current-count (get-in db [:chats chat-id :unviewed-messages-count])] (cond (= from (multiaccounts.model/current-public-key cofx)) diff --git a/src/status_im/events.cljs b/src/status_im/events.cljs index 322feed6ff..16761c2f61 100644 --- a/src/status_im/events.cljs +++ b/src/status_im/events.cljs @@ -490,8 +490,8 @@ (handlers/register-handler-fx :chat.ui/navigate-to-chat - (fn [cofx [_ chat-id opts]] - (chat/navigate-to-chat cofx chat-id opts))) + (fn [cofx [_ chat-id _]] + (chat/navigate-to-chat cofx chat-id))) (handlers/register-handler-fx :chat.ui/load-more-messages diff --git a/src/status_im/group_chats/core.cljs b/src/status_im/group_chats/core.cljs index 69d74a82a1..2bb0be1819 100644 --- a/src/status_im/group_chats/core.cljs +++ b/src/status_im/group_chats/core.cljs @@ -65,7 +65,7 @@ (data-store.messages/<-rpc %)) messages) navigate-fx #(if (get-in % [:db :chats chat-id :is-active]) - (models.chat/navigate-to-chat % chat-id {:navigation-reset? true}) + (models.chat/navigate-to-chat % chat-id) (navigation/navigate-to-cofx % :home {}))] (apply fx/merge cofx (concat [chat-fx] diff --git a/src/status_im/subs.cljs b/src/status_im/subs.cljs index b3875660c9..bd5a983d36 100644 --- a/src/status_im/subs.cljs +++ b/src/status_im/subs.cljs @@ -86,10 +86,8 @@ (reg-root-key-sub :keyboard-height :keyboard-height) (reg-root-key-sub :keyboard-max-height :keyboard-max-height) (reg-root-key-sub :sync-data :sync-data) -(reg-root-key-sub :layout-height :layout-height) (reg-root-key-sub :mobile-network/remember-choice? :mobile-network/remember-choice?) (reg-root-key-sub :qr-modal :qr-modal) -(reg-root-key-sub :content-layout-height :content-layout-height) (reg-root-key-sub :bootnodes/manage :bootnodes/manage) (reg-root-key-sub :networks/current-network :networks/current-network) (reg-root-key-sub :networks/networks :networks/networks) @@ -566,32 +564,6 @@ (fn [chats [_ chat-id]] (get chats chat-id))) -(re-frame/reg-sub - :chats/content-layout-height - :<- [:content-layout-height] - :<- [:chats/current-chat-ui-prop :input-height] - :<- [:chats/current-chat-ui-prop :input-focused?] - :<- [:keyboard-height] - :<- [:chats/current-chat-ui-prop :input-bottom-sheet] - (fn [[home-content-layout-height input-height input-focused? kheight stickers?]] - (- (+ home-content-layout-height tabs.styles/tabs-height) - (if platform/iphone-x? - (* 2 toolbar.styles/toolbar-height) - toolbar.styles/toolbar-height) - (if input-height input-height 0) - (if stickers? - (stickers.styles/stickers-panel-height) - kheight) - (if input-focused? - (cond - platform/iphone-x? 0 - platform/ios? tabs.styles/tabs-diff - :else 0) - (cond - platform/iphone-x? (* 2 tabs.styles/minimized-tabs-height) - platform/ios? tabs.styles/tabs-height - :else tabs.styles/minimized-tabs-height))))) - (re-frame/reg-sub :chats/current-chat-ui-props :<- [::chat-ui-props] @@ -703,11 +675,8 @@ (assoc :show-input? true)))) (defn enrich-current-chat - [{:keys [messages chat-id might-have-join-time-messages?] :as chat} - ranges height input-height] + [{:keys [messages chat-id might-have-join-time-messages?] :as chat} ranges] (assoc chat - :height height - :input-height input-height :range (get ranges chat-id) :intro-status @@ -735,12 +704,10 @@ :<- [:chats/current-raw-chat] :<- [:multiaccount/public-key] :<- [:mailserver/ranges] - :<- [:chats/content-layout-height] - :<- [:chats/current-chat-ui-prop :input-height] - (fn [[{:keys [group-chat chat-id contact messages] :as current-chat} - my-public-key ranges height input-height]] + (fn [[{:keys [group-chat chat-id messages] :as current-chat} + my-public-key ranges]] (when current-chat - (cond-> (enrich-current-chat current-chat ranges height input-height) + (cond-> (enrich-current-chat current-chat ranges) (empty? messages) (assoc :universal-link (links/generate-link :public-chat :external chat-id)) @@ -830,6 +797,7 @@ :<- [:chats/all-loaded?] :<- [:chats/public?] (fn [[message-list messages messages-gaps range all-loaded? public?]] + ;;TODO (perf) we need to move all these to status-go (-> (models.message-list/->seq message-list) (chat.db/add-datemarks) (hydrate-messages messages) @@ -851,7 +819,7 @@ :<- [:contacts/contacts] :<- [:multiaccount] (fn [[contacts multiaccount] [_ id]] - (multiaccounts/displayed-photo (or (contacts id) + (multiaccounts/displayed-photo (or (get contacts id) (when (= id (:public-key multiaccount)) multiaccount) (contact.db/public-key->new-contact id))))) diff --git a/src/status_im/ui/components/connectivity/styles.cljs b/src/status_im/ui/components/connectivity/styles.cljs index 27c79ad819..b5edfd4504 100644 --- a/src/status_im/ui/components/connectivity/styles.cljs +++ b/src/status_im/ui/components/connectivity/styles.cljs @@ -1,24 +1,17 @@ (ns status-im.ui.components.connectivity.styles - (:require [status-im.ui.components.colors :as colors] - [status-im.utils.platform :as platform])) + (:require [status-im.ui.components.colors :as colors])) (defn text-wrapper [{:keys [window-width height background-color opacity transform]}] - (cond-> {:flex-direction :row - :justify-content :center - :transform [{:translateY transform}] - :opacity opacity - :background-color (or background-color colors/gray) - :height height} - - platform/desktop? - (assoc :left 0 - :right 0) - - (not platform/desktop?) - (assoc :width window-width))) + {:flex-direction :row + :justify-content :center + :transform [{:translateY transform}] + :opacity opacity + :background-color (or background-color colors/gray) + :height height + :width window-width}) (def text - {:color :white - :font-size 14 - :top 8}) + {:color :white + :font-size 14 + :top 8}) \ No newline at end of file diff --git a/src/status_im/ui/components/connectivity/view.cljs b/src/status_im/ui/components/connectivity/view.cljs index d84a24bc7b..3852cd194f 100644 --- a/src/status_im/ui/components/connectivity/view.cljs +++ b/src/status_im/ui/components/connectivity/view.cljs @@ -43,7 +43,7 @@ :background-color color}) (views/defview loading-indicator [parent-width] - (views/letsubs [blue-bar-left-margin (animation/create-value 0) + (views/letsubs [blue-bar-left-margin (animation/create-value 0) white-bar-left-margin (animation/create-value 0)] {:component-did-mount (fn [_] @@ -62,11 +62,11 @@ (animation/parallel [(animation/timing blue-bar-left-margin (easing :out 0)) (animation/timing white-bar-left-margin (easing :out 0))])]))))} - [react/view {:style {:width parent-width - :position :absolute - :top -3 - :z-index 3 - :height 3 + [react/view {:style {:width parent-width + :position :absolute + :top -3 + :z-index 3 + :height 3 :background-color colors/white}} [react/animated-view {:style (animated-bar-style blue-bar-left-margin parent-width @@ -87,10 +87,10 @@ all connectivity views (we have at least one view in home and one in chat)" (animation/start (animation/parallel [(animation/timing anim-opacity - {:toValue 0 - :delay 800 - :duration 150 - :easing (.-ease (animation/easing)) + {:toValue 0 + :delay 800 + :duration 150 + :easing (.-ease (animation/easing)) :useNativeDriver true}) (animation/timing anim-y {:toValue (if platform/desktop? 0 neg-connectivity-bar-height) @@ -111,14 +111,14 @@ all connectivity views (we have at least one view in home and one in chat)" (animation/start (animation/parallel [(animation/timing anim-opacity - {:toValue 1 - :duration 150 - :easing (.-ease (animation/easing)) + {:toValue 1 + :duration 150 + :easing (.-ease (animation/easing)) :useNativeDriver true}) (animation/timing anim-y - {:toValue (if platform/desktop? connectivity-bar-height 0) - :duration 150 - :easing (.-ease (animation/easing)) + {:toValue (if platform/desktop? connectivity-bar-height 0) + :duration 150 + :easing (.-ease (animation/easing)) :useNativeDriver true})]) ;; second param of start() - a callback that fires when animation stops #(do (reset! to-hide? true) (reset! status-hidden false)))) @@ -129,8 +129,8 @@ all connectivity views (we have at least one view in home and one in chat)" (reset! status-hidden false))))) (defn connectivity-status - [{:keys [connected?]} anim-translate-y status-hidden] - (let [anim-translate-y (or anim-translate-y (animation/create-value 0)) + [{:keys [connected?]} status-hidden] + (let [anim-translate-y (animation/create-value neg-connectivity-bar-height) anim-opacity (animation/create-value 0)] (reagent/create-class {:component-did-mount @@ -147,38 +147,35 @@ all connectivity views (we have at least one view in home and one in chat)" (manage-visibility (:connected? (reagent/props comp)) true anim-opacity anim-translate-y status-hidden)) :reagent-render - (fn [{:keys [view-id message on-press-event connected? connecting?] :as opts}] - [react/animated-view {:style (styles/text-wrapper - (assoc opts - :height (if platform/desktop? - anim-translate-y - connectivity-bar-height) - :background-color (if connected? - colors/green - colors/gray) - ;;TODO how does this affect desktop? - :transform anim-translate-y - :opacity anim-opacity - :modal? (= view-id :chat-modal))) - :accessibility-label :connection-status-text} - (when connecting? - [react/activity-indicator {:color colors/white :margin-right 6}]) - (if (= message :mobile-network) - [react/nested-text {:style styles/text - :on-press (when on-press-event #(re-frame/dispatch [on-press-event]))} - (i18n/label :t/waiting-for-wifi) " " - [{:style {:text-decoration-line :underline}} - (i18n/label :t/waiting-for-wifi-change)]] - (when message - [react/text {:style styles/text - :on-press (when on-press-event #(re-frame/dispatch [on-press-event]))} - (i18n/label message)]))])}))) + (fn [{:keys [message on-press-event connected? connecting?] :as opts}] + (when-not @status-hidden + [react/animated-view {:style (styles/text-wrapper + (assoc opts + :height connectivity-bar-height + :background-color (if connected? + colors/green + colors/gray) + :transform anim-translate-y + :opacity anim-opacity)) + :accessibility-label :connection-status-text} + (when connecting? + [react/activity-indicator {:color colors/white :margin-right 6}]) + (if (= message :mobile-network) + [react/nested-text {:style styles/text + :on-press (when on-press-event #(re-frame/dispatch [on-press-event]))} + (i18n/label :t/waiting-for-wifi) " " + [{:style {:text-decoration-line :underline}} + (i18n/label :t/waiting-for-wifi-change)]] + (when message + [react/text {:style styles/text + :on-press (when on-press-event #(re-frame/dispatch [on-press-event]))} + (i18n/label message)]))]))}))) ;; timer updating the enqueued status -(def timer (atom nil)) +(def timer (atom nil)) ;; connectivity status change going to be persisted to :connectivity/ui-status-properties -(def enqueued-connectivity-status-properties (atom nil)) +(def enqueued-connectivity-status-properties (atom nil)) (defn propagate-status "Smoothly propagate from :connectivity/status-properties subscription to @@ -191,24 +188,29 @@ all connectivity views (we have at least one view in home and one in chat)" ;; reset queued with new state and start a timer if not yet started (reset! enqueued-connectivity-status-properties status-properties) (when-not @timer - (reset! timer (utils/set-timeout #(do - (reset! timer nil) - (when @enqueued-connectivity-status-properties - (re-frame/dispatch [:set :connectivity/ui-status-properties @enqueued-connectivity-status-properties]) - (reset! enqueued-connectivity-status-properties nil))) + (reset! + timer + (utils/set-timeout + #(do + (reset! timer nil) + (when @enqueued-connectivity-status-properties + (re-frame/dispatch [:set + :connectivity/ui-status-properties + @enqueued-connectivity-status-properties]) + (reset! enqueued-connectivity-status-properties nil))) - ;; timeout choice: - ;; if the app is in foreground or logged-in for less than , - ;; postpone state changes for otherwise - (let [ts (max - (or logged-in-since 0) - (or app-active-since 0)) - ts-diff (- (datetime/timestamp) ts) - timeout (if (< ts-diff timewindow-for-long-delay) - long-delay - standard-delay)] - (log/debug "propagate-status set-timeout: " logged-in-since app-active-since ts-diff timeout) - timeout)))))) + ;; timeout choice: + ;; if the app is in foreground or logged-in for less than , + ;; postpone state changes for otherwise + (let [ts (max + (or logged-in-since 0) + (or app-active-since 0)) + ts-diff (- (datetime/timestamp) ts) + timeout (if (< ts-diff timewindow-for-long-delay) + long-delay + standard-delay)] + (log/debug "propagate-status set-timeout: " logged-in-since app-active-since ts-diff timeout) + timeout)))))) (defn status-propagator-dummy-view "this empty view is needed to react propagate status-properties to ui-status-properties" @@ -223,66 +225,29 @@ all connectivity views (we have at least one view in home and one in chat)" :reagent-render #()})) -(defview connectivity-view [anim-translate-y] - (letsubs [status-properties [:connectivity/status-properties] - app-active-since [:app-active-since] - logged-in-since [:logged-in-since] - ui-status-properties [:connectivity/ui-status-properties] - status-hidden (reagent/atom true) - view-id [:view-id] - window-width (reagent/atom 0)] +(defview connectivity [header footer] + (letsubs [status-properties [:connectivity/status-properties] + app-active-since [:app-active-since] + logged-in-since [:logged-in-since] + ui-status-properties [:connectivity/ui-status-properties] + status-hidden (reagent/atom true) + window-width (reagent/atom 0)] (let [loading-indicator? (:loading-indicator? ui-status-properties)] - [react/view {:style {:align-items :stretch - :z-index 1} + [react/view {:style {:flex 1} :on-layout #(reset! window-width (-> % .-nativeEvent .-layout .-width))} - (when (and loading-indicator? @status-hidden) - [loading-indicator @window-width]) - ;; This view below exists only to hide the connectivity-status bar when "connected". - ;; Ideally connectivity-status bar would be hidden under "toolbar/toolbar", - ;; but that has to be transparent(enven though it sits above the bar) - ;; to show through the "loading-indicator" - ;; TODO consider making the height the same height as the "toolbar/toolbar" - [react/view {:position :absolute - :top neg-connectivity-bar-height - :width @window-width - :z-index 2 - :height connectivity-bar-height - :background-color colors/white}] + [react/view {:style {:z-index 2 :background-color :white}} + header + [react/view + (when (and loading-indicator? @status-hidden) + [loading-indicator @window-width])]] [connectivity-status - ;on startup default connected (merge (or ui-status-properties {:connected? true :message :t/connected}) - {:view-id view-id - :window-width @window-width}) - anim-translate-y + {:window-width @window-width}) status-hidden] - [status-propagator-dummy-view {:status-properties status-properties - :app-active-since app-active-since - :logged-in-since logged-in-since - :ui-status-properties ui-status-properties}]]))) - -;; "push?" determines whether "content" gets pushed down when disconnected -;; like in :home view, or stays put like in :chat view -;; TODO determine-how-this-affects/fix desktop -(defn connectivity-animation-wrapper [style anim-value push? & content] - (vec (concat - (if platform/desktop? - [react/view {:style {:flex 1}}] - [react/animated-view - {:style (merge {:flex 1 - :margin-bottom neg-connectivity-bar-height} - ;; A translated view (connectivity-view in this case) - ;; prevents touch interaction to component below - ;; them. If we don't bring this view on the same level - ;; or above as the translated view, the top - ;; portion(same height as connectivity-view) of - ;; "content" (which now occupies translated view's - ;; natural[untranslated] position) becomes - ;; unresponsive to touch - (when-not @to-hide? - {:z-index 1}) - (if push? - {:transform [{:translateY anim-value}]} - {:transform [{:translateY neg-connectivity-bar-height}]}) - style)}]) - content))) + ;;TODO this is something weird, rework + [status-propagator-dummy-view {:status-properties status-properties + :app-active-since app-active-since + :logged-in-since logged-in-since + :ui-status-properties ui-status-properties}] + footer]))) \ No newline at end of file diff --git a/src/status_im/ui/components/list/views.cljs b/src/status_im/ui/components/list/views.cljs index 9a568dde26..aec295eabe 100644 --- a/src/status_im/ui/components/list/views.cljs +++ b/src/status_im/ui/components/list/views.cljs @@ -199,28 +199,6 @@ (when header {:ListHeaderComponent (reagent/as-element header)}) (when footer {:ListFooterComponent (reagent/as-element footer)})))) -;; Workaround an issue in reagent that does not consider JS array as JS value -;; This forces clj <-> js serialization and breaks clj semantic -;; See https://github.com/reagent-project/reagent/issues/335 - -(deftype Item [value] - IEncodeJS - (-clj->js [x] (.-value x)) - (-key->js [x] (.-value x)) - IEncodeClojure - (-js->clj [x _] (.-value x))) - -(defn- to-js-array - "Converts a collection to a JS array (but leave content as is)" - [coll] - (let [arr (array)] - (doseq [x coll] - (.push arr x)) - arr)) - -(defn- wrap-data [o] - (Item. (to-js-array o))) - (defn flat-list "A wrapper for FlatList. See https://facebook.github.io/react-native/docs/flatlist.html" @@ -232,7 +210,7 @@ [class (merge (base-list-props props) props - {:data (wrap-data data)})]))) + {:data (to-array data)})]))) (defn flat-list-generic-render-fn "A generic status-react specific `render-fn` for `list-item`. @@ -262,7 +240,7 @@ (if-let [f (:render-fn props)] (assoc (dissoc props :render-fn) :renderItem (wrap-render-fn f)) props) - :data wrap-data)) + :data to-array)) ;;TODO DEPRECATED, use status-im.ui.components.list-item.views (defn section-list "A wrapper for SectionList. diff --git a/src/status_im/ui/components/search_input/view.cljs b/src/status_im/ui/components/search_input/view.cljs index 182d58f83a..9fb6347063 100644 --- a/src/status_im/ui/components/search_input/view.cljs +++ b/src/status_im/ui/components/search_input/view.cljs @@ -1,52 +1,44 @@ (ns status-im.ui.components.search-input.view - (:require-macros [status-im.utils.views :as views]) - (:require [reagent.core :as reagent] - [status-im.i18n :as i18n] + (:require [status-im.i18n :as i18n] [status-im.ui.components.react :as react] [status-im.ui.components.colors :as colors] [status-im.ui.components.search-input.styles :as styles] [status-im.ui.components.icons.vector-icons :as icons])) -(views/defview search-input [{:keys [on-cancel - on-focus - on-change - search-active? - search-container-style - search-filter - auto-focus]}] - (views/letsubs - [input-ref (reagent/atom nil)] - [react/view {:style (or search-container-style styles/search-container)} - [react/view {:style styles/search-input-container} - [icons/icon :main-icons/search {:color colors/gray - :container-style {:margin-left 6 - :margin-right 2}}] - [react/text-input {:placeholder (i18n/label :t/search) - :blur-on-submit true - :multiline false - :ref #(reset! input-ref %) - :style styles/search-input - :default-value search-filter - :auto-focus auto-focus - :auto-correct false - :auto-capitalize false - :on-focus #(do - (when on-focus - (on-focus search-filter)) - (reset! search-active? true)) - :on-change (fn [e] - (let [native-event (.-nativeEvent e) - text (.-text native-event)] - (when on-change - (on-change text))))}]] - (when @search-active? - [react/touchable-highlight - {:on-press (fn [] - (.clear @input-ref) - (.blur @input-ref) - (when on-cancel - (on-cancel)) - (reset! search-active? false)) - :style {:margin-left 16}} - [react/text {:style {:color colors/blue}} - (i18n/label :t/cancel)]])])) \ No newline at end of file +(defn search-input [_] + (let [input-ref (atom nil)] + (fn [{:keys [on-cancel on-focus on-change search-active? + search-container-style search-filter auto-focus]}] + [react/view {:style (or search-container-style styles/search-container)} + [react/view {:style styles/search-input-container} + [icons/icon :main-icons/search {:color colors/gray + :container-style {:margin-left 6 + :margin-right 2}}] + [react/text-input {:placeholder (i18n/label :t/search) + :blur-on-submit true + :multiline false + :ref #(reset! input-ref %) + :style styles/search-input + :default-value search-filter + :auto-focus auto-focus + :auto-correct false + :auto-capitalize false + :on-focus #(do + (when on-focus + (on-focus search-filter)) + (reset! search-active? true)) + :on-change (fn [e] + (let [native-event (.-nativeEvent e) + text (.-text native-event)] + (when on-change + (on-change text))))}]] + (when @search-active? + [react/touchable-highlight + {:on-press (fn [] + (.clear @input-ref) + (.blur @input-ref) + (when on-cancel (on-cancel)) + (reset! search-active? false)) + :style {:margin-left 16}} + [react/text {:style {:color colors/blue}} + (i18n/label :t/cancel)]])]))) \ No newline at end of file diff --git a/src/status_im/ui/components/topbar.cljs b/src/status_im/ui/components/topbar.cljs index 24956ad23a..64afe525e4 100644 --- a/src/status_im/ui/components/topbar.cljs +++ b/src/status_im/ui/components/topbar.cljs @@ -35,7 +35,7 @@ (defn topbar [_] (let [title-padding (reagent/atom 16)] - (fn [& [{:keys [title navigation accessories show-border? modal?]}]] + (fn [& [{:keys [title navigation accessories show-border? modal? content]}]] (let [navigation (or navigation (default-navigation modal?))] [react/view (cond-> {:height 56 :align-items :center :flex-direction :row} show-border? @@ -49,8 +49,12 @@ (for [value accessories] ^{:key value} [button value false])]) + (when content + [react/view {:position :absolute :left @title-padding :right @title-padding + :top 0 :bottom 0} + content]) (when title [react/view {:position :absolute :left @title-padding :right @title-padding - :top 0 :bottom 0 :align-items :center :justify-content :center} + :top 0 :bottom 0 :align-items :center :justify-content :center} [react/text {:style {:typography :title-bold :text-align :center} :number-of-lines 2} (utils.label/stringify title)]])])))) \ No newline at end of file diff --git a/src/status_im/ui/screens/chat/bottom_info.cljs b/src/status_im/ui/screens/chat/bottom_info.cljs deleted file mode 100644 index abbad123ab..0000000000 --- a/src/status_im/ui/screens/chat/bottom_info.cljs +++ /dev/null @@ -1,16 +0,0 @@ -(ns status-im.ui.screens.chat.bottom-info - (:require [status-im.ui.components.react :as react] - [status-im.ui.screens.chat.styles.main :as styles])) - -(defn overlay - [{:keys [on-click-outside]} items] - [react/view styles/bottom-info-overlay - [react/touchable-highlight {:on-press on-click-outside - :style styles/overlay-highlight} - [react/view nil]] - items]) - -(defn container - [height & children] - [react/view {:style (styles/bottom-info-container height)} - (into [react/view] children)]) diff --git a/src/status_im/ui/screens/chat/group.cljs b/src/status_im/ui/screens/chat/group.cljs new file mode 100644 index 0000000000..7fcbcc9543 --- /dev/null +++ b/src/status_im/ui/screens/chat/group.cljs @@ -0,0 +1,85 @@ +(ns status-im.ui.screens.chat.group + (:require [re-frame.core :as re-frame] + [status-im.ui.components.button :as button] + [status-im.ui.components.react :as react] + [status-im.ui.screens.chat.styles.main :as style] + [status-im.i18n :as i18n] + [status-im.ui.components.list-selection :as list-selection] + [status-im.ui.components.colors :as colors])) + +(defn join-chat-button [chat-id] + [button/button + {:type :secondary + :on-press #(re-frame/dispatch [:group-chats.ui/join-pressed chat-id]) + :label :t/join-group-chat}]) + +(defn decline-chat [chat-id] + [react/touchable-highlight + {:on-press + #(re-frame/dispatch [:group-chats.ui/remove-chat-confirmed chat-id])} + [react/text {:style style/decline-chat} + (i18n/label :t/group-chat-decline-invitation)]]) + +(defn group-chat-footer + [chat-id] + [react/view {:style style/group-chat-join-footer} + [react/view {:style style/group-chat-join-container} + [join-chat-button chat-id] + [decline-chat chat-id]]]) + +(defn group-chat-description-loading + [] + [react/view {:style (merge style/intro-header-description-container + {:margin-bottom 36 + :height 44})} + [react/text {:style style/intro-header-description} + (i18n/label :t/loading)] + [react/activity-indicator {:animating true + :size :small + :color colors/gray}]]) + +(defn group-chat-description-container + [{:keys [pending-invite-inviter-name inviter-name chat-name public? + universal-link range intro-status]}] + (let [{:keys [lowest-request-from highest-request-to]} range] + (case intro-status + :loading + [group-chat-description-loading] + + :empty + (when public? + [react/nested-text {:style (merge style/intro-header-description + {:margin-bottom 36})} + (let [quiet-hours (quot (- highest-request-to lowest-request-from) + (* 60 60)) + quiet-time (if (<= quiet-hours 24) + (i18n/label :t/quiet-hours + {:quiet-hours quiet-hours}) + (i18n/label :t/quiet-days + {:quiet-days (quot quiet-hours 24)}))] + (i18n/label :t/empty-chat-description-public + {:quiet-hours quiet-time})) + [{:style {:color colors/blue} + :on-press #(list-selection/open-share + {:message + (i18n/label + :t/share-public-chat-text {:link universal-link})})} + (i18n/label :t/empty-chat-description-public-share-this)]]) + + :messages + (when (not public?) + (if pending-invite-inviter-name + [react/nested-text {:style style/intro-header-description} + [{:style {:color :black}} pending-invite-inviter-name] + (i18n/label :t/join-group-chat-description + {:username "" + :group-name chat-name})] + (if (not= inviter-name "Unknown") + [react/nested-text {:style style/intro-header-description} + (i18n/label :t/joined-group-chat-description + {:username "" + :group-name chat-name}) + [{:style {:color :black}} inviter-name]] + [react/text {:style style/intro-header-description} + (i18n/label :t/created-group-chat-description + {:group-name chat-name})])))))) \ No newline at end of file diff --git a/src/status_im/ui/screens/chat/input/animations/expandable.cljs b/src/status_im/ui/screens/chat/input/animations/expandable.cljs deleted file mode 100644 index 22662d8eb2..0000000000 --- a/src/status_im/ui/screens/chat/input/animations/expandable.cljs +++ /dev/null @@ -1,47 +0,0 @@ -(ns status-im.ui.screens.chat.input.animations.expandable - (:require-macros [status-im.utils.views :refer [defview letsubs]]) - (:require [reagent.core :as reagent] - [status-im.ui.components.animation :as animation] - [status-im.ui.components.react :as react] - [status-im.ui.screens.chat.styles.animations :as style] - [status-im.ui.screens.chat.styles.input.input :as input-style] - [status-im.utils.platform :as platform])) - -(def top-offset 100) - -(defn expandable-view-on-update [anim-value] - (animation/start - (animation/spring anim-value {:toValue -53 - :friction 10 - :tension 60 - :useNativeDriver true}))) - -(defview expandable-view [_ & elements] - (letsubs [;; Default value of translateY is 104, it is sufficient to - ;; hide two commands below an input view. - ;; With bigger view like assets parameter animation looks good - ;; enough too, even if initially the view isn't fully covered by - ;; input. It might be inferred for each case but it would require - ;; more efforts and will not change too much the way how animation - ;; looks atm. - anim-value (animation/create-value 104) - input-focused? [:chats/current-chat-ui-prop :input-focused?] - messages-focused? [:chats/current-chat-ui-prop :messages-focused?] - input-height [:chats/current-chat-ui-prop :input-height] - chat-input-margin [:chats/input-margin] - chat-layout-height [:layout-height] - keyboard-height [:keyboard-height]] - {:component-did-mount - (fn [] - (expandable-view-on-update anim-value))} - (let [input-height (or input-height (+ input-style/padding-vertical - input-style/min-input-height - input-style/padding-vertical - input-style/border-height)) - max-height (- chat-layout-height (when platform/ios? keyboard-height) input-height top-offset)] - [react/view style/overlap-container - [react/animated-view {:style (style/expandable-container anim-value chat-input-margin max-height)} - (into [react/scroll-view {:keyboard-should-persist-taps :always - :bounces false}] - (when (or input-focused? (not messages-focused?)) - elements))]]))) diff --git a/src/status_im/ui/screens/chat/input/input.cljs b/src/status_im/ui/screens/chat/input/input.cljs index 7fa3a994ba..13f457c39a 100644 --- a/src/status_im/ui/screens/chat/input/input.cljs +++ b/src/status_im/ui/screens/chat/input/input.cljs @@ -9,15 +9,11 @@ [status-im.ui.screens.chat.photos :as photos] [status-im.ui.screens.chat.utils :as chat-utils] [status-im.i18n :as i18n] - [status-im.ui.components.animation :as animation] [status-im.ui.components.colors :as colors] [status-im.ui.components.react :as react] [status-im.ui.components.icons.vector-icons :as vector-icons] [status-im.utils.platform :as platform] - [status-im.utils.gfycat.core :as gfycat] - [status-im.utils.utils :as utils] [status-im.utils.config :as config] - [taoensso.timbre :as log] [status-im.ui.screens.chat.stickers.views :as stickers] [status-im.ui.screens.chat.extensions.views :as extensions])) @@ -32,10 +28,7 @@ :default-value (or input-text "") :editable (not cooldown-enabled?) :blur-on-submit false - :on-focus #(re-frame/dispatch [:chat.ui/set-chat-ui-props {:input-focused? true - :input-bottom-sheet nil - :messages-focused? false}]) - :on-blur #(re-frame/dispatch [:chat.ui/set-chat-ui-props {:input-focused? false}]) + :on-focus #(re-frame/dispatch [:chat.ui/set-chat-ui-props {:input-bottom-sheet nil}]) :on-submit-editing #(when single-line-input? (re-frame/dispatch [:chat.ui/send-current-message])) :on-layout #(set-container-width-fn (.-width (.-layout (.-nativeEvent %)))) @@ -63,10 +56,7 @@ :default-value @state-text :editable (not cooldown-enabled?) :blur-on-submit false - :on-focus #(re-frame/dispatch [:chat.ui/set-chat-ui-props {:input-focused? true - :input-bottom-sheet nil - :messages-focused? false}]) - :on-blur #(re-frame/dispatch [:chat.ui/set-chat-ui-props {:input-focused? false}]) + :on-focus #(re-frame/dispatch [:chat.ui/set-chat-ui-props {:input-bottom-sheet nil}]) :submit-shortcut {:key "Enter"} :on-submit-editing #(do (.clear @inp-ref) @@ -162,12 +152,7 @@ input-text-empty? (if platform/desktop? (string/blank? state-text) (string/blank? input-text))] - [react/view {:style (style/root margin) - :on-layout #(let [h (-> (.-nativeEvent %) - (.-layout) - (.-height))] - (when (> h 0) - (re-frame/dispatch [:chat.ui/set-chat-ui-props {:input-height h}])))} + [react/view {:style (style/root margin)} [reply-message-view] [react/view {:style style/input-container} [input-view {:single-line-input? single-line-input? :set-text set-text :state-text state-text}] @@ -183,4 +168,4 @@ (re-frame/dispatch [:chat.ui/send-current-message]) (set-text ""))] [send-button/send-button-view {:input-text input-text} - #(re-frame/dispatch [:chat.ui/send-current-message])]))]]))) + #(re-frame/dispatch [:chat.ui/send-current-message])]))]]))) \ No newline at end of file diff --git a/src/status_im/ui/screens/chat/input/send_button.cljs b/src/status_im/ui/screens/chat/input/send_button.cljs index 21f50664aa..8acdc1bb4e 100644 --- a/src/status_im/ui/screens/chat/input/send_button.cljs +++ b/src/status_im/ui/screens/chat/input/send_button.cljs @@ -1,7 +1,6 @@ (ns status-im.ui.screens.chat.input.send-button (:require-macros [status-im.utils.views :refer [defview letsubs]]) (:require [clojure.string :as string] - [re-frame.core :as re-frame] [status-im.ui.screens.chat.styles.input.send-button :as style] [status-im.ui.components.react :as react] [status-im.ui.components.icons.vector-icons :as vector-icons])) @@ -21,4 +20,4 @@ [vector-icons/icon :main-icons/arrow-up {:container-style style/send-message-container :accessibility-label :send-message-button - :color :white}]]))) + :color :white}]]))) \ No newline at end of file diff --git a/src/status_im/ui/screens/chat/message/command.cljs b/src/status_im/ui/screens/chat/message/command.cljs new file mode 100644 index 0000000000..b5f48acae0 --- /dev/null +++ b/src/status_im/ui/screens/chat/message/command.cljs @@ -0,0 +1,257 @@ +(ns status-im.ui.screens.chat.message.command + (:require [re-frame.core :as re-frame] + [status-im.commands.core :as commands] + [status-im.ui.components.react :as react] + [status-im.ui.components.colors :as colors] + [status-im.i18n :as i18n] + [status-im.constants :as constants] + [status-im.utils.money :as money] + [status-im.ethereum.transactions.core :as transactions] + [status-im.ui.components.chat-icon.screen :as chat-icon] + [status-im.ui.components.icons.vector-icons :as vector-icons])) + +(defn- final-status? [command-state] + (or (= command-state constants/command-state-request-address-for-transaction-declined) + (= command-state constants/command-state-request-transaction-declined) + (= command-state constants/command-state-transaction-sent))) + +(defn- command-pending-status + [command-state direction to transaction-type] + [react/view {:style {:flex-direction :row + :height 28 + :align-items :center + :border-width 1 + :border-color colors/gray-lighter + :border-radius 16 + :padding-horizontal 8 + :margin-right 12 + :margin-bottom 2}} + [vector-icons/icon :tiny-icons/tiny-pending + {:width 16 + :height 16 + :color colors/gray + :container-style {:margin-right 6}}] + [react/text {:style {:color colors/gray + :font-weight "500" + :line-height 16 + :margin-right 4 + :font-size 13}} + (if (and (or (= command-state constants/command-state-request-transaction) + (= command-state constants/command-state-request-address-for-transaction-accepted)) + (= direction :incoming)) + (str (i18n/label :t/shared) " '" (:name @(re-frame/subscribe [:account-by-address to])) "'") + (i18n/label (cond + (= command-state constants/command-state-transaction-pending) + :t/status-pending + (= command-state constants/command-state-request-address-for-transaction) + :t/address-requested + (= command-state constants/command-state-request-address-for-transaction-accepted) + :t/address-request-accepted + (= command-state constants/command-state-transaction-sent) + (case transaction-type + :pending :t/status-pending + :failed :t/transaction-failed + :t/status-confirmed) + (= command-state constants/command-state-request-transaction) + :t/address-received)))]]) + +(defn- command-final-status + [command-state direction transaction-type] + [react/view {:style {:flex-direction :row + :height 28 + :align-items :center + :border-width 1 + :border-color colors/gray-lighter + :border-radius 16 + :padding-horizontal 8 + :margin-right 12 + :margin-bottom 2}} + (if (or (= command-state constants/command-state-request-address-for-transaction-declined) + (= command-state constants/command-state-request-transaction-declined) + (= :failed transaction-type)) + [vector-icons/icon :tiny-icons/tiny-warning + {:width 16 + :height 16 + :container-style {:margin-right 6}}] + (if (= :pending transaction-type) + [vector-icons/icon :tiny-icons/tiny-pending + {:color colors/gray + :width 16 + :height 16 + :container-style {:margin-right 6}}] + [vector-icons/icon :tiny-icons/tiny-check + {:width 16 + :height 16 + :container-style {:margin-right 6}}])) + [react/text {:style (merge {:margin-right 4 + :line-height 16 + :font-size 13} + (if (= transaction-type :pending) + {:color colors/gray} + {:font-weight "500"}))} + (i18n/label (if (or (= command-state constants/command-state-request-address-for-transaction-declined) + (= command-state constants/command-state-request-transaction-declined)) + :t/transaction-declined + (case transaction-type + :pending :t/status-pending + :failed :t/transaction-failed + :t/status-confirmed)))]]) + +(defn- command-status-and-timestamp + [command-state direction to timestamp-str transaction-type] + [react/view {:style {:flex-direction :row + :align-items :flex-end + :justify-content :space-between}} + (if (final-status? command-state) + [command-final-status command-state direction transaction-type] + [command-pending-status command-state direction to transaction-type]) + [react/text {:style {:font-size 10 + :line-height 12 + :text-align-vertical :bottom + :color colors/gray}} + timestamp-str]]) + +(defn- command-actions + [accept-label on-accept on-decline] + [react/view + [react/touchable-highlight + {:on-press #(do (react/dismiss-keyboard!) + (on-accept)) + :style {:border-color colors/gray-lighter + :border-top-width 1 + :margin-top 8 + :margin-horizontal -12 + :padding-horizontal 15 + :padding-vertical 10}} + [react/text {:style {:text-align :center + :color colors/blue + :font-weight "500" + :font-size 15 + :line-height 22}} + (i18n/label accept-label)]] + (when on-decline + [react/touchable-highlight + {:on-press on-decline + :style {:border-color colors/gray-lighter + :border-top-width 1 + :margin-horizontal -12 + :padding-top 10}} + [react/text {:style {:text-align :center + :color colors/blue + :font-size 15 + :line-height 22}} + (i18n/label :t/decline)]])]) + +(defn- command-transaction-info + [contract value] + (let [{:keys [symbol icon decimals color] :as token} + (if (seq contract) + (get @(re-frame/subscribe [:wallet/chain-tokens]) + contract + transactions/default-erc20-token) + @(re-frame/subscribe [:ethereum/native-currency])) + amount (money/internal->formatted value symbol decimals) + {:keys [code]} + @(re-frame/subscribe [:wallet/currency]) + prices @(re-frame/subscribe [:prices]) + amount-fiat (money/fiat-amount-value amount symbol (keyword code) prices)] + [react/view {:style {:flex-direction :row + :margin-top 8 + :margin-bottom 12}} + (if icon + [react/image (-> icon + (update :source #(%)) + (assoc-in [:style :height] 24) + (assoc-in [:style :width] 24))] + [react/view {:style {:margin-right 14 + :padding-vertical 2 + :justify-content :flex-start + :max-width 40 + :align-items :center + :align-self :stretch}} + [chat-icon/custom-icon-view-list (:name token) color 24]]) + [react/view {:style {:margin-left 6}} + [react/text {:style {:margin-bottom 2 + :font-size 20 + :line-height 24}} + (str amount " " (name symbol))] + [react/text {:style {:font-size 12 + :line-height 16 + :color colors/gray}} + (str amount-fiat " " code)]]])) + +(defn calculate-direction [outgoing command-state] + (case command-state + (constants/command-state-request-address-for-transaction-accepted + constants/command-state-request-address-for-transaction-declined + constants/command-state-request-transaction) + (if outgoing :incoming :outgoing) + (if outgoing :outgoing :incoming))) + +(defn comand-content + [wrapper {:keys [message-id + chat-id + outgoing + command-parameters + timestamp-str] :as message}] + (let [{:keys [contract value address command-state transaction-hash]} command-parameters + direction (calculate-direction outgoing command-state) + transaction (when transaction-hash + @(re-frame/subscribe + [:wallet/account-by-transaction-hash + transaction-hash]))] + [wrapper (assoc message :outgoing (= direction :outgoing)) + [react/touchable-highlight + {:on-press #(when (:address transaction) + (re-frame/dispatch [:wallet.ui/show-transaction-details + transaction-hash (:address transaction)]))} + [react/view {:padding-horizontal 12 + :padding-bottom 10 + :padding-top 10 + :margin-top 4 + :border-width 1 + :border-color colors/gray-lighter + :border-radius 16 + (case direction + :outgoing :border-bottom-right-radius + :incoming :border-bottom-left-radius) 4 + :background-color :white} + [react/text {:style {:font-size 13 + :line-height 18 + :font-weight "500" + :color colors/gray}} + (case direction + :outgoing (str "↑ " (i18n/label :t/outgoing-transaction)) + :incoming (str "↓ " (i18n/label :t/incoming-transaction)))] + [command-transaction-info contract value] + [command-status-and-timestamp + command-state direction address timestamp-str (:type transaction)] + (when (not outgoing) + (cond + (= command-state constants/command-state-request-transaction) + [command-actions + :t/sign-and-send + #(re-frame/dispatch + [:wallet.ui/accept-request-transaction-button-clicked-from-command + chat-id + command-parameters]) + #(re-frame/dispatch [::commands/decline-request-transaction message-id])] + + (= command-state + constants/command-state-request-address-for-transaction-accepted) + [command-actions + :t/sign-and-send + #(re-frame/dispatch + [:wallet.ui/accept-request-transaction-button-clicked-from-command + chat-id + command-parameters])] + + (= command-state constants/command-state-request-address-for-transaction) + [command-actions + :t/accept-and-share-address + #(re-frame/dispatch + [::commands/prepare-accept-request-address-for-transaction + message]) + #(re-frame/dispatch + [::commands/decline-request-address-for-transaction + message-id])]))]]])) \ No newline at end of file diff --git a/src/status_im/ui/screens/chat/message/datemark.cljs b/src/status_im/ui/screens/chat/message/datemark.cljs index b692eb3673..48e42a30be 100644 --- a/src/status_im/ui/screens/chat/message/datemark.cljs +++ b/src/status_im/ui/screens/chat/message/datemark.cljs @@ -5,17 +5,10 @@ [status-im.ui.screens.chat.styles.message.datemark :as style])) (defn chat-datemark [value] - [react/view style/datemark-wrapper - [react/view style/datemark - [react/text {:style style/datemark-text} - (string/capitalize value)]]]) - -(defn chat-datemark-mobile [value] [react/touchable-without-feedback {:on-press (fn [_] (re-frame/dispatch - [:chat.ui/set-chat-ui-props {:messages-focused? true - :input-bottom-sheet nil}]) + [:chat.ui/set-chat-ui-props {:input-bottom-sheet nil}]) (react/dismiss-keyboard!))} [react/view style/datemark-mobile [react/text {:style style/datemark-text} diff --git a/src/status_im/ui/screens/chat/message/message.cljs b/src/status_im/ui/screens/chat/message/message.cljs index 1f10d69595..ff5138f818 100644 --- a/src/status_im/ui/screens/chat/message/message.cljs +++ b/src/status_im/ui/screens/chat/message/message.cljs @@ -1,25 +1,20 @@ (ns status-im.ui.screens.chat.message.message (:require [re-frame.core :as re-frame] - [status-im.commands.core :as commands] [status-im.constants :as constants] [status-im.utils.http :as http] [status-im.i18n :as i18n] - [status-im.ethereum.eip55 :as eip55] - [reagent.core :as reagent] [status-im.ui.components.colors :as colors] [status-im.utils.security :as security] [status-im.ui.components.icons.vector-icons :as vector-icons] [status-im.ui.components.popup-menu.views :as desktop.pop-up] - [status-im.ui.components.chat-icon.screen :as chat-icon] [status-im.ui.components.react :as react] - [status-im.utils.money :as money] - [status-im.ethereum.transactions.core :as transactions] [status-im.ui.screens.chat.sheets :as sheets] [status-im.ui.screens.chat.photos :as photos] [status-im.ui.screens.chat.styles.message.message :as style] [status-im.ui.screens.chat.utils :as chat.utils] [status-im.utils.contenthash :as contenthash] - [status-im.utils.platform :as platform]) + [status-im.utils.platform :as platform] + [status-im.ui.screens.chat.message.command :as message.command]) (:require-macros [status-im.utils.views :refer [defview letsubs]])) (defview mention-element [from] @@ -34,7 +29,7 @@ (:rtl? content) (= content-type constants/content-type-emoji))} t]) -(defn message-view +(defn message-bubble-wrapper [{:keys [timestamp-str outgoing content content-type] :as message} message-content {:keys [justify-timestamp?]}] [react/view (style/message-view message) @@ -58,11 +53,6 @@ :number-of-lines 5} (or text (:text quote))]]))) -(defn expand-button [expanded? chat-id message-id] - [react/text {:style style/message-expand-button - :on-press #(re-frame/dispatch [:chat.ui/message-expand-toggled chat-id message-id])} - (i18n/label (if expanded? :show-less :show-more))]) - (defn render-inline [message-text outgoing acc {:keys [type literal destination]}] (case type "" @@ -120,10 +110,7 @@ [react/text-class {:style style/status-text}] (-> content :parsed-text peek :children))]]) -(defn render-block [{:keys [chat-id message-id content - timestamp-str group-chat outgoing - current-public-key expanded?] :as message} - acc +(defn render-block [{:keys [content outgoing]} acc {:keys [type literal children]}] (case type @@ -145,14 +132,12 @@ acc)) -(defn render-parsed-text [{:keys [timestamp-str - outgoing] :as message} - - tree] +(defn render-parsed-text [{:keys [timestamp-str outgoing] :as message} tree] (let [elements (reduce (fn [acc e] (render-block message acc e)) [react/view {}] tree) timestamp [react/text {:style (style/message-timestamp-placeholder outgoing)} (str " " timestamp-str)] last-element (peek elements)] + ;; TODO (perf) ;; Using `nth` here as slightly faster than `first`, roughly 30% ;; It's worth considering pure js structures for this code path as ;; it's perfomance critical @@ -163,9 +148,8 @@ (conj elements timestamp)))) (defn text-message - [{:keys [chat-id message-id content - timestamp-str group-chat outgoing current-public-key expanded?] :as message}] - [message-view message + [{:keys [content outgoing current-public-key] :as message}] + [message-bubble-wrapper message (let [response-to (:response-to content)] [react/view (when (seq response-to) @@ -176,275 +160,13 @@ (defn emoji-message [{:keys [content current-public-key alias outgoing] :as message}] (let [response-to (:response-to content)] - [message-view message + [message-bubble-wrapper message [react/view {:style (style/style-message-text outgoing)} (when response-to [quoted-message response-to (:quoted-message message) alias outgoing current-public-key]) [react/text {:style (style/emoji-message message)} (:text content)]]])) -(defmulti message-content (fn [_ message _] (message :content-type))) - -(defmethod message-content constants/content-type-text - [wrapper message] - [wrapper message [text-message message]]) - -(defmethod message-content constants/content-type-status - [wrapper message] - [wrapper message [message-content-status message]]) - -(defmethod message-content constants/content-type-emoji - [wrapper message] - [wrapper message [emoji-message message]]) - -(defmethod message-content constants/content-type-sticker - [wrapper {:keys [content] :as message}] - [wrapper message - [react/image {:style {:margin 10 :width 140 :height 140} - :source {:uri (contenthash/url (-> content :sticker :hash))}}]]) - -(defn- final-status? [command-state] - (or (= command-state constants/command-state-request-address-for-transaction-declined) - (= command-state constants/command-state-request-transaction-declined) - (= command-state constants/command-state-transaction-sent))) - -(defn- command-pending-status - [command-state direction to transaction-type] - [react/view {:style {:flex-direction :row - :height 28 - :align-items :center - :border-width 1 - :border-color colors/gray-lighter - :border-radius 16 - :padding-horizontal 8 - :margin-right 12 - :margin-bottom 2}} - [vector-icons/icon :tiny-icons/tiny-pending - {:width 16 - :height 16 - :color colors/gray - :container-style {:margin-right 6}}] - [react/text {:style {:color colors/gray - :font-weight "500" - :line-height 16 - :margin-right 4 - :font-size 13}} - (if (and (or (= command-state constants/command-state-request-transaction) - (= command-state constants/command-state-request-address-for-transaction-accepted)) - (= direction :incoming)) - (str (i18n/label :t/shared) " '" (:name @(re-frame/subscribe [:account-by-address to])) "'") - (i18n/label (cond - (= command-state constants/command-state-transaction-pending) - :t/status-pending - (= command-state constants/command-state-request-address-for-transaction) - :t/address-requested - (= command-state constants/command-state-request-address-for-transaction-accepted) - :t/address-request-accepted - (= command-state constants/command-state-transaction-sent) - (case transaction-type - :pending :t/status-pending - :failed :t/transaction-failed - :t/status-confirmed) - (= command-state constants/command-state-request-transaction) - :t/address-received)))]]) - -(defn- command-final-status - [command-state direction transaction-type] - [react/view {:style {:flex-direction :row - :height 28 - :align-items :center - :border-width 1 - :border-color colors/gray-lighter - :border-radius 16 - :padding-horizontal 8 - :margin-right 12 - :margin-bottom 2}} - (if (or (= command-state constants/command-state-request-address-for-transaction-declined) - (= command-state constants/command-state-request-transaction-declined) - (= :failed transaction-type)) - [vector-icons/icon :tiny-icons/tiny-warning - {:width 16 - :height 16 - :container-style {:margin-right 6}}] - (if (= :pending transaction-type) - [vector-icons/icon :tiny-icons/tiny-pending - {:color colors/gray - :width 16 - :height 16 - :container-style {:margin-right 6}}] - [vector-icons/icon :tiny-icons/tiny-check - {:width 16 - :height 16 - :container-style {:margin-right 6}}])) - [react/text {:style (merge {:margin-right 4 - :line-height 16 - :font-size 13} - (if (= transaction-type :pending) - {:color colors/gray} - {:font-weight "500"}))} - (i18n/label (if (or (= command-state constants/command-state-request-address-for-transaction-declined) - (= command-state constants/command-state-request-transaction-declined)) - :t/transaction-declined - (case transaction-type - :pending :t/status-pending - :failed :t/transaction-failed - :t/status-confirmed)))]]) - -(defn- command-status-and-timestamp - [command-state direction to timestamp-str transaction-type] - [react/view {:style {:flex-direction :row - :align-items :flex-end - :justify-content :space-between}} - (if (final-status? command-state) - [command-final-status command-state direction transaction-type] - [command-pending-status command-state direction to transaction-type]) - [react/text {:style {:font-size 10 - :line-height 12 - :text-align-vertical :bottom - :color colors/gray}} - timestamp-str]]) - -(defn- command-actions - [accept-label on-accept on-decline] - [react/view - [react/touchable-highlight - {:on-press #(do (react/dismiss-keyboard!) - (on-accept)) - :style {:border-color colors/gray-lighter - :border-top-width 1 - :margin-top 8 - :margin-horizontal -12 - :padding-horizontal 15 - :padding-vertical 10}} - [react/text {:style {:text-align :center - :color colors/blue - :font-weight "500" - :font-size 15 - :line-height 22}} - (i18n/label accept-label)]] - (when on-decline - [react/touchable-highlight - {:on-press on-decline - :style {:border-color colors/gray-lighter - :border-top-width 1 - :margin-horizontal -12 - :padding-top 10}} - [react/text {:style {:text-align :center - :color colors/blue - :font-size 15 - :line-height 22}} - (i18n/label :t/decline)]])]) - -(defn- command-transaction-info - [contract value] - (let [{:keys [symbol icon decimals color] :as token} - (if (seq contract) - (get @(re-frame/subscribe [:wallet/chain-tokens]) - contract - transactions/default-erc20-token) - @(re-frame/subscribe [:ethereum/native-currency])) - amount (money/internal->formatted value symbol decimals) - {:keys [code] :as currency} - @(re-frame/subscribe [:wallet/currency]) - prices @(re-frame/subscribe [:prices]) - amount-fiat - (money/fiat-amount-value amount symbol (keyword code) prices)] - [react/view {:style {:flex-direction :row - :margin-top 8 - :margin-bottom 12}} - (if icon - [react/image (-> icon - (update :source #(%)) - (assoc-in [:style :height] 24) - (assoc-in [:style :width] 24))] - [react/view {:style {:margin-right 14 - :padding-vertical 2 - :justify-content :flex-start - :max-width 40 - :align-items :center - :align-self :stretch}} - [chat-icon/custom-icon-view-list (:name token) color 24]]) - [react/view {:style {:margin-left 6}} - [react/text {:style {:margin-bottom 2 - :font-size 20 - :line-height 24}} - (str amount " " (name symbol))] - [react/text {:style {:font-size 12 - :line-height 16 - :color colors/gray}} - (str amount-fiat " " code)]]])) - -(defn calculate-direction [outgoing command-state] - (case command-state - (constants/command-state-request-address-for-transaction-accepted - constants/command-state-request-address-for-transaction-declined - constants/command-state-request-transaction) - (if outgoing :incoming :outgoing) - (if outgoing :outgoing :incoming))) - -(defmethod message-content constants/content-type-command - [wrapper {:keys [message-id - chat-id - outgoing - command-parameters - timestamp-str] :as message}] - (let [{:keys [contract value address command-state transaction-hash]} command-parameters - direction (calculate-direction outgoing command-state) - transaction (when transaction-hash - @(re-frame/subscribe - [:wallet/account-by-transaction-hash - transaction-hash]))] - [wrapper (assoc message :outgoing (= direction :outgoing)) - [react/touchable-highlight - {:on-press #(when (:address transaction) - (re-frame/dispatch [:wallet.ui/show-transaction-details - transaction-hash (:address transaction)]))} - [react/view {:padding-horizontal 12 - :padding-bottom 10 - :padding-top 10 - :margin-top 4 - :border-width 1 - :border-color colors/gray-lighter - :border-radius 16 - (case direction - :outgoing :border-bottom-right-radius - :incoming :border-bottom-left-radius) 4 - :background-color :white} - [react/text {:style {:font-size 13 - :line-height 18 - :font-weight "500" - :color colors/gray}} - (case direction - :outgoing (str "↑ " (i18n/label :t/outgoing-transaction)) - :incoming (str "↓ " (i18n/label :t/incoming-transaction)))] - [command-transaction-info contract value] - [command-status-and-timestamp - command-state direction address timestamp-str (:type transaction)] - (when (not outgoing) - (cond - (= command-state constants/command-state-request-transaction) - [command-actions - :t/sign-and-send - #(re-frame/dispatch [:wallet.ui/accept-request-transaction-button-clicked-from-command chat-id command-parameters]) - #(re-frame/dispatch [::commands/decline-request-transaction message-id])] - - (= command-state constants/command-state-request-address-for-transaction-accepted) - [command-actions - :t/sign-and-send - #(re-frame/dispatch [:wallet.ui/accept-request-transaction-button-clicked-from-command chat-id command-parameters])] - - (= command-state constants/command-state-request-address-for-transaction) - [command-actions - :t/accept-and-share-address - #(re-frame/dispatch [::commands/prepare-accept-request-address-for-transaction message]) - #(re-frame/dispatch [::commands/decline-request-address-for-transaction message-id])]))]]])) - -(defmethod message-content :default - [wrapper {:keys [content-type] :as message}] - [wrapper message - [message-view message - [react/text (str "Unhandled content-type " content-type)]]]) - (defn message-activity-indicator [] [react/view style/message-activity-indicator @@ -473,84 +195,81 @@ (defn message-delivery-status [{:keys [chat-id message-id outgoing-status - first-outgoing? - content message-type] :as message}] + first-outgoing? message-type]}] (when (not= constants/message-type-private-group-system-message message-type) (case outgoing-status :sending [message-activity-indicator] :not-sent [message-not-sent-text chat-id message-id] :sent (when first-outgoing? - [react/view style/delivery-view - [react/text {:style style/delivery-text} - (i18n/label :t/status-sent)]]) + [react/text {:style style/delivery-text} + (i18n/label :t/status-sent)]) nil))) (defview message-author-name [from alias] (letsubs [{:keys [ens-name]} [:contacts/contact-name-by-identity from]] (chat.utils/format-author alias style/message-author-name-container ens-name))) -(defn message-body - [{:keys [alias - last-in-group? - first-in-group? - display-photo? - identicon - display-username? - from - outgoing - modal? - content] - :as message} child] - [react/view (style/group-message-wrapper message) +(defn message-press-handlers [{:keys [outgoing from content-type content] :as message}] + (let [pack (get-in content [:sticker :pack])] + {:on-press (fn [_] + (when (and (= content-type constants/content-type-sticker) pack) + (re-frame/dispatch [:stickers/open-sticker-pack pack])) + (re-frame/dispatch [:chat.ui/set-chat-ui-props {:input-bottom-sheet nil}]) + (react/dismiss-keyboard!)) + :on-long-press #(cond (or (= content-type constants/content-type-text) + (= content-type constants/content-type-emoji)) + (re-frame/dispatch [:bottom-sheet/show-sheet + {:content (sheets/message-long-press message) + :height 192}]) + (and (= content-type constants/content-type-sticker) + from (not outgoing)) + (re-frame/dispatch [:bottom-sheet/show-sheet + {:content (sheets/sticker-long-press message) + :height 64}]))})) + +(defn message-content-wrapper + "Author, userpic and delivery wrapper" + [{:keys [alias first-in-group? display-photo? identicon display-username? + from outgoing] + :as message} content] + [react/view {:style (style/message-wrapper message) + :accessibility-label :chat-item} [react/view (style/message-body message) (when display-photo? - [react/view (style/message-author outgoing) + ; userpic + [react/view (style/message-author-userpic outgoing) (when first-in-group? - [react/touchable-highlight {:on-press #(when-not modal? (re-frame/dispatch [:chat.ui/show-profile from]))} - [react/view - [photos/member-photo from identicon]]])]) - [react/view (style/group-message-view outgoing display-photo?) + [react/touchable-highlight {:on-press #(re-frame/dispatch [:chat.ui/show-profile from])} + [photos/member-identicon identicon]])]) + ; username + [react/view (style/message-author-wrapper outgoing display-photo?) (when display-username? [react/touchable-opacity {:style style/message-author-touchable :on-press #(re-frame/dispatch [:chat.ui/show-profile from])} + ;;TODO (perf) move to event [message-author-name from alias]]) - [react/view {:style (style/timestamp-content-wrapper outgoing)} - child]]] + ;;MESSAGE CONTENT + content]] + ; delivery status [react/view (style/delivery-status outgoing) [message-delivery-status message]]]) -(defn chat-message - [{:keys [outgoing from group-chat modal? current-public-key content-type content] :as message}] - (let [sticker (:sticker content)] - [react/view - [react/touchable-highlight - {:on-press (fn [arg] - (if (and platform/desktop? (= "right" (.-button (.-nativeEvent arg)))) - (re-frame/dispatch [:bottom-sheet/show-sheet - {:content (sheets/message-long-press message) - :height 192}]) - (do - (when (and (= content-type constants/content-type-sticker) (:pack sticker)) - (re-frame/dispatch [:stickers/open-sticker-pack (:pack sticker)])) - (re-frame/dispatch [:chat.ui/set-chat-ui-props {:messages-focused? true - :input-bottom-sheet nil}]) - (when-not platform/desktop? - (react/dismiss-keyboard!))))) - :on-long-press #(cond (or (= content-type constants/content-type-text) - (= content-type constants/content-type-emoji)) - (re-frame/dispatch [:bottom-sheet/show-sheet - {:content (sheets/message-long-press message) - :height 192}]) - - (and (= content-type constants/content-type-sticker) - from (not outgoing)) - (re-frame/dispatch [:bottom-sheet/show-sheet - {:content (sheets/sticker-long-press message) - :height 64}]))} - [react/view {:accessibility-label :chat-item} - (let [incoming-group (and group-chat (not outgoing))] - [message-content message-body (merge message - {:current-public-key current-public-key - :group-chat group-chat - :modal? modal? - :incoming-group incoming-group})])]]])) +(defn chat-message [{:keys [content content-type] :as message}] + (if (= content-type constants/content-type-command) + [message.command/comand-content message-content-wrapper message] + [react/touchable-highlight (message-press-handlers message) + [message-content-wrapper + message + (if (= content-type constants/content-type-text) + ; text message + [text-message message] + (if (= content-type constants/content-type-status) + [message-content-status message] + (if (= content-type constants/content-type-emoji) + [emoji-message message] + (if (= content-type constants/content-type-sticker) + [react/image {:style {:margin 10 :width 140 :height 140} + ;;TODO (perf) move to event + :source {:uri (contenthash/url (-> content :sticker :hash))}}] + [message-bubble-wrapper message + [react/text (str "Unhandled content-type " content-type)]]))))]])) \ No newline at end of file diff --git a/src/status_im/ui/screens/chat/photos.cljs b/src/status_im/ui/screens/chat/photos.cljs index 5ccd86022c..6a23b2019c 100644 --- a/src/status_im/ui/screens/chat/photos.cljs +++ b/src/status_im/ui/screens/chat/photos.cljs @@ -1,15 +1,11 @@ (ns status-im.ui.screens.chat.photos - (:require [clojure.string :as string] - [status-im.multiaccounts.core :as multiaccounts] - [status-im.ui.components.react :as react] + (:require [status-im.ui.components.react :as react] [status-im.ui.screens.chat.styles.photos :as style] [status-im.ui.screens.profile.db :as profile.db] - [status-im.utils.identicon :as identicon] [status-im.utils.image :as utils.image]) (:require-macros [status-im.utils.views :refer [defview letsubs]])) -(defn photo [photo-path {:keys [size - accessibility-label]}] +(defn photo [photo-path {:keys [size accessibility-label]}] (let [identicon? (when photo-path (profile.db/base64-png? photo-path))] [react/view {:style (style/photo-container size)} [react/image {:source (utils.image/source photo-path) @@ -24,3 +20,12 @@ (photo (or photo-path identicon) {:accessibility-label :member-photo :size (or size style/default-size)}))) + +(defn member-identicon [identicon] + (let [size style/default-size] + [react/view {:style (style/photo-container size)} + [react/image {:source {:uri identicon} + :style (style/photo size) + :resize-mode :cover + :accessibility-label :member-photo}] + [react/view {:style (style/photo-border size)}]])) \ No newline at end of file diff --git a/src/status_im/ui/screens/chat/styles/main.cljs b/src/status_im/ui/screens/chat/styles/main.cljs index e8fc3cd84f..b31b9d3f71 100644 --- a/src/status_im/ui/screens/chat/styles/main.cljs +++ b/src/status_im/ui/screens/chat/styles/main.cljs @@ -1,40 +1,11 @@ (ns status-im.ui.screens.chat.styles.main (:require [status-im.ui.components.colors :as colors])) -(def chat-view - {:flex 1}) - (def toolbar-container {:flex 1 :align-items :center :flex-direction :row}) -(def messages-container - {:flex 1 - :padding-bottom 0 - :margin-bottom 0}) - -(def action - {:width 56 - :height 56 - :top 0 - :align-items :center - :justify-content :center}) - -(def icon-view - {:width 56 - :height 56}) - -(def back-icon - {:margin-top 21 - :margin-left 23 - :width 8 - :height 14}) - -(def chat-toolbar-contents - {:flex-direction :row - :flex 1}) - (def chat-name-view {:flex 1 :justify-content :center}) @@ -44,12 +15,6 @@ :font-size 15 :line-height 22}) -(def group-icon - {:margin-top 4 - :margin-bottom 2.7 - :width 14 - :height 9}) - (def toolbar-subtitle {:typography :caption :line-height 16 @@ -60,57 +25,6 @@ :margin-top 4 :color colors/text-gray}) -(defn actions-wrapper [status-bar-height] - {:background-color colors/white - :elevation 2 - :position :absolute - :top (+ 55 status-bar-height) - :left 0 - :right 0}) - -(def actions-separator - {:margin-left 16 - :height 1.5 - :background-color colors/black-transparent}) - -(def actions-view - {:margin-vertical 10}) - -(def action-icon-row - {:flex-direction :row - :height 56}) - -(def action-icon-view - (merge icon-view - {:align-items :center - :justify-content :center})) - -(def action-view - {:flex 1 - :align-items :flex-start - :justify-content :center}) - -(def action-title - {:margin-top -2.5 - :color colors/text - :font-size 14}) - -(def typing-all - {:marginBottom 20}) - -(def typing-view - {:width 260 - :margin-top 10 - :padding-left 8 - :padding-right 8 - :align-items :flex-start - :align-self :flex-start}) - -(def typing-text - {:margin-top -2 - :font-size 12 - :color colors/text-gray}) - (def overlay-highlight {:flex 1}) @@ -134,31 +48,6 @@ :right 16 :height height}) -(def bottom-info-list-container - {:padding-left 16 - :padding-right 16 - :padding-top 8 - :padding-bottom 8}) - -(def item-height 60) - -(def bottom-info-row - {:flex-direction "row" - :padding-top 4 - :padding-bottom 4}) - -(def bottom-info-row-photo-size 42) - -(def bottom-info-row-text-container - {:margin-left 16 - :margin-right 16}) - -(def bottom-info-row-text1 - {:color "black"}) - -(def bottom-info-row-text2 - {:color "#888888"}) - (def add-contact {:flex-direction :row :align-items :center @@ -171,14 +60,6 @@ {:margin-left 4 :color colors/blue}) -(def add-contact-close-icon - {:margin-right 12}) - -(defn message-view-animated [opacity] - {:opacity opacity - :flex 1 - :background-color :white}) - (def empty-chat-container {:flex 1 :justify-content :center @@ -187,18 +68,17 @@ :margin-right 6}) (defn intro-header-container - [height status no-messages] - (let [adjusted-height (if (< height 280) 324 height)] - (if (or no-messages (= status (or :loading :empty))) - {:flex 1 - :flex-direction :column - :justify-content :center - :align-items :center - :height adjusted-height} - {:flex 1 - :flex-direction :column - :justify-content :center - :align-items :center}))) + [status no-messages] + (if (or no-messages (= status (or :loading :empty))) + {:flex 1 + :flex-direction :column + :justify-content :center + :align-items :center + :height 324} + {:flex 1 + :flex-direction :column + :justify-content :center + :align-items :center})) (defn intro-header-icon [diameter color] {:width diameter @@ -238,20 +118,12 @@ :margin-right 4 :text-align :center}) -(def empty-chat-text-name - {:margin-bottom 5}) - (def intro-header-description {:color colors/gray :line-height 22 :text-align :center :margin-horizontal 32}) -(def group-chat-icon - {:color colors/white - :font-size 40 - :font-weight "700"}) - (def group-chat-join-footer {:flex 1 :justify-content :center}) @@ -261,21 +133,10 @@ :align-items :center :justify-content :center}) -(def group-chat-join-name - {:typography :header}) - -(def join-button - {:margin-bottom 15}) - (def decline-chat {:color colors/blue :margin-bottom 40}) -(def select-chat - {:color colors/gray}) - -(def messages-list-vertical-padding 46) - (def are-you-friends-bubble {:border-radius 8 :border-width 1 @@ -303,4 +164,4 @@ (def tribute-received-note {:font-size 13 :line-height 18 - :text-align :center}) + :text-align :center}) \ No newline at end of file diff --git a/src/status_im/ui/screens/chat/styles/message/message.cljs b/src/status_im/ui/screens/chat/styles/message/message.cljs index 1f178ef453..5a78480632 100644 --- a/src/status_im/ui/screens/chat/styles/message/message.cljs +++ b/src/status_im/ui/screens/chat/styles/message/message.cljs @@ -47,29 +47,20 @@ :bottom 9 ; 6 Bubble bottom, 3 message baseline (if rtl? :left :right) 12}))) -(def message-expand-button - {:color colors/gray - :font-size 12 - :opacity 0.7 - :margin-bottom 20}) - (def selected-message {:margin-top 18 :margin-left 40 :font-size 12 :color colors/text-gray}) -(defn group-message-wrapper [{:keys [outgoing] :as message}] +(defn message-wrapper [{:keys [outgoing] :as message}] (merge {:flex-direction :column} (if outgoing {:margin-left 96} {:margin-right 52}) (last-message-padding message))) -(defn timestamp-content-wrapper [outgoing] - {:flex-direction (if outgoing :row-reverse :row)}) - -(defn group-message-view +(defn message-author-wrapper [outgoing display-photo?] (let [align (if outgoing :flex-end :flex-start)] (merge {:flex-direction :column @@ -91,7 +82,7 @@ {:margin-left 12 :padding-vertical 2}) -(defn message-author [outgoing] +(defn message-author-userpic [outgoing] (merge {:width (+ 16 photos/default-size) ;; 16 is for the padding :align-self :flex-end} @@ -100,18 +91,15 @@ {:padding-horizontal 8 :padding-right 8}))) -(def delivery-view - {:flex-direction :row - :margin-top 2}) - (def delivery-text {:color colors/gray + :margin-top 2 :font-size 12}) (def not-sent-view - (assoc delivery-view - :margin-bottom 2 - :padding-top 2)) + {:flex-direction :row + :margin-bottom 2 + :padding-top 2}) (def not-sent-text (assoc delivery-text diff --git a/src/status_im/ui/screens/chat/toolbar_content.cljs b/src/status_im/ui/screens/chat/toolbar_content.cljs index 14c25976df..c8fe47daea 100644 --- a/src/status_im/ui/screens/chat/toolbar_content.cljs +++ b/src/status_im/ui/screens/chat/toolbar_content.cljs @@ -1,10 +1,8 @@ (ns status-im.ui.screens.chat.toolbar-content - (:require [cljs-time.core :as t] - [status-im.i18n :as i18n] + (:require [status-im.i18n :as i18n] [status-im.ui.components.chat-icon.screen :as chat-icon.screen] [status-im.ui.components.react :as react] - [status-im.ui.screens.chat.styles.main :as st] - [status-im.utils.datetime :as time]) + [status-im.ui.screens.chat.styles.main :as st]) (:require-macros [status-im.utils.views :refer [defview letsubs]])) (defn- in-progress-text [{:keys [highestBlock currentBlock startBlock]}] @@ -47,9 +45,9 @@ (i18n/label :chat-is-not-a-contact))]]) (defview toolbar-content-view [] - (letsubs [{:keys [group-chat color online contacts chat-name contact - public? chat-id] :as chat} [:chats/current-chat] - sync-state [:sync-state]] + (letsubs [{:keys [group-chat color online contacts chat-name contact public?]} + [:chats/current-chat] + sync-state [:sync-state]] (let [has-subtitle? (or group-chat (not= :done sync-state))] [react/view {:style st/toolbar-container} [react/view {:margin-right 10} diff --git a/src/status_im/ui/screens/chat/ttt.cljs b/src/status_im/ui/screens/chat/ttt.cljs new file mode 100644 index 0000000000..953595e679 --- /dev/null +++ b/src/status_im/ui/screens/chat/ttt.cljs @@ -0,0 +1,92 @@ +(ns status-im.ui.screens.chat.ttt + (:require [status-im.ui.screens.chat.styles.main :as style] + [status-im.ui.components.react :as react] + [status-im.ui.screens.profile.tribute-to-talk.views :as tribute-to-talk.views] + [status-im.i18n :as i18n] + [status-im.ui.components.colors :as colors] + [re-frame.core :as re-frame])) + +(defn tribute-to-talk-header + [name] + [react/nested-text {:style (assoc style/intro-header-description + :margin-bottom 32)} + (i18n/label :t/tribute-required-by-multiaccount {:multiaccount-name name}) + [{:style {:color colors/blue} + :on-press #(re-frame/dispatch [:navigate-to :tribute-learn-more])} + (str " " (i18n/label :learn-more))]]) + +(defn pay-to-chat-messages + [snt-amount chat-id tribute-status tribute-label + fiat-amount fiat-currency token] + [tribute-to-talk.views/pay-to-chat-message + {:snt-amount snt-amount + :public-key chat-id + :tribute-status tribute-status + :tribute-label tribute-label + :fiat-amount fiat-amount + :fiat-currency fiat-currency + :token token + :style {:margin-horizontal 8 + :align-items :flex-start + :align-self (if snt-amount :flex-start :flex-end)}}]) + +(defn one-to-one-chat-description-container + [{:keys [chat-id name contact show-input? tribute-to-talk] + :tribute-to-talk/keys [my-message received? message tribute-status + tribute-label snt-amount on-share-my-profile + fiat-amount fiat-currency token]}] + (case tribute-status + :loading + [react/view (assoc (dissoc style/empty-chat-container :flex) + :justify-content :flex-end) + [react/view {:style {:align-items :center :justify-content :flex-end}} + [react/view {:style {:flex-direction :row :justify-content :center}} + [react/text {:style style/loading-text} + (i18n/label :t/loading)] + [react/activity-indicator {:color colors/gray + :animating true}]]]] + + :required + [react/view + [tribute-to-talk-header name] + [pay-to-chat-messages snt-amount chat-id tribute-status tribute-label + fiat-amount fiat-currency token] + [react/view {:style style/are-you-friends-bubble} + [react/text {:style (assoc style/are-you-friends-text + :font-weight "500")} + (i18n/label :t/tribute-to-talk-are-you-friends)] + [react/text {:style style/are-you-friends-text} + (i18n/label :t/tribute-to-talk-ask-to-be-added)] + [react/text {:style style/share-my-profile + :on-press on-share-my-profile} + (i18n/label :t/share-my-profile)]]] + + :pending + [react/view + [tribute-to-talk-header name] + [pay-to-chat-messages snt-amount chat-id tribute-status tribute-label + fiat-amount fiat-currency token]] + + (:paid :none) + [react/view + ;[intro-header contact] + (when (= tribute-status :paid) + [pay-to-chat-messages snt-amount chat-id tribute-status tribute-label + fiat-amount fiat-currency token]) + (when received? + [pay-to-chat-messages nil nil nil nil nil nil nil]) + + (when (or (= tribute-status :paid) received?) + [react/view {:style {:margin-top 16 :margin-horizontal 8}} + [react/nested-text {:style style/tribute-received-note} + (when received? + [{:style (assoc style/tribute-received-note :color colors/gray)} + (i18n/label :tribute-to-talk-tribute-received1)]) + [{:style (assoc style/tribute-received-note :font-weight "500")} + name] + [{:style (assoc style/tribute-received-note :color colors/gray)} + (i18n/label (if received? + :tribute-to-talk-tribute-received2 + :tribute-to-talk-contact-received-your-tribute))]]])])) + + ;[intro-header contact])) \ No newline at end of file diff --git a/src/status_im/ui/screens/chat/utils.cljs b/src/status_im/ui/screens/chat/utils.cljs index 6b74acf23b..08c4142640 100644 --- a/src/status_im/ui/screens/chat/utils.cljs +++ b/src/status_im/ui/screens/chat/utils.cljs @@ -1,15 +1,8 @@ (ns status-im.ui.screens.chat.utils - (:require [re-frame.core :as re-frame] - [status-im.ethereum.ens :as ens] - [status-im.ethereum.stateofus :as stateofus] - [status-im.utils.gfycat.core :as gfycat] - [status-im.utils.platform :as platform] - [status-im.utils.security :as security] + (:require [status-im.ethereum.stateofus :as stateofus] [status-im.i18n :as i18n] - [status-im.utils.core :as core-utils] [status-im.ui.components.react :as react] - [status-im.ui.components.colors :as colors] - [status-im.utils.http :as http])) + [status-im.ui.components.colors :as colors])) (defn format-author [alias style name] (let [additional-styles (style false)] @@ -37,60 +30,4 @@ (or (and (= from current-public-key) [react/text {:style (style true)} (str reply-symbol (i18n/label :t/You))]) - (format-author (subs reply-name 0 80) style false)))) - -(def ^:private styling->prop - {:bold {:style {:font-weight "700"}} - :italic {:style {:font-style :italic}} - :backquote {:style {:background-color colors/black - :color colors/green}}}) - -(def ^:private action->prop-fn - {:link (fn [text {:keys [outgoing] :as message}] - {:style {:color (if outgoing colors/white colors/blue) - :text-decoration-line :underline} - :on-press #(when (and (security/safe-link? text) - (security/safe-link-text? (-> message :content :text))) - (if platform/desktop? - (.openURL react/linking (http/normalize-url text)) - (re-frame/dispatch [:browser.ui/message-link-pressed text])))}) - :tag (fn [text {:keys [outgoing]}] - {:style {:color (if outgoing colors/white colors/blue) - :text-decoration-line :underline} - :on-press #(re-frame/dispatch [:chat.ui/start-public-chat (subs text 1) {:navigation-reset? true}])})}) - -(defn- lookup-props [text-chunk message kind] - (let [prop (get styling->prop (keyword kind)) - prop-fn (get action->prop-fn (keyword kind))] - (if prop-fn (prop-fn text-chunk message) prop))) - -(defn render-chunks [render-recipe message] - (vec (map-indexed (fn [idx [text-chunk kind]] - (if (= :text kind) - text-chunk - [(into {:key idx} (lookup-props text-chunk message kind)) - text-chunk])) - render-recipe))) - -(defn render-chunks-desktop [limit render-recipe message] - "This fn is only needed as a temporary hack - until rn-desktop supports text/number-of-lines property" - (->> render-recipe - (map vector (range)) - (reduce (fn [[total-length acc] [idx [text-chunk kind]]] - (if (<= limit total-length) - (reduced [total-length acc]) - (let [chunk-len (count text-chunk) - cut-chunk-len (min chunk-len (- limit total-length)) - cut-chunk (if (= chunk-len cut-chunk-len) - text-chunk - (core-utils/truncate-str text-chunk cut-chunk-len))] - [(+ total-length cut-chunk-len) - (conj acc - (if (= :text kind) - cut-chunk - [react/text (into {:key idx} (lookup-props text-chunk message kind)) - cut-chunk]))]))) - [0 []]) - second - seq)) + (format-author (subs reply-name 0 80) style false)))) \ No newline at end of file diff --git a/src/status_im/ui/screens/chat/views.cljs b/src/status_im/ui/screens/chat/views.cljs index 16bdaf5b36..7848f0cde0 100644 --- a/src/status_im/ui/screens/chat/views.cljs +++ b/src/status_im/ui/screens/chat/views.cljs @@ -1,37 +1,45 @@ (ns status-im.ui.screens.chat.views (:require [re-frame.core :as re-frame] - [reagent.core :as reagent] [status-im.contact.db :as contact.db] [status-im.i18n :as i18n] [status-im.multiaccounts.core :as multiaccounts] - [status-im.ui.components.animation :as animation] - [status-im.ui.components.button :as button] [status-im.ui.components.chat-icon.screen :as chat-icon.screen] [status-im.ui.components.colors :as colors] [status-im.ui.components.connectivity.view :as connectivity] [status-im.ui.components.icons.vector-icons :as vector-icons] - [status-im.ui.components.list-selection :as list-selection] [status-im.ui.components.list.views :as list] [status-im.ui.components.react :as react] - [status-im.ui.components.toolbar.actions :as toolbar.actions] - [status-im.ui.components.toolbar.view :as toolbar] [status-im.ui.screens.chat.sheets :as sheets] [status-im.ui.screens.chat.input.input :as input] - [status-im.ui.screens.chat.message.datemark :as message-datemark] - [status-im.ui.screens.chat.message.gap :as gap] [status-im.ui.screens.chat.message.message :as message] [status-im.ui.screens.chat.stickers.views :as stickers] [status-im.ui.screens.chat.styles.main :as style] [status-im.ui.screens.chat.toolbar-content :as toolbar-content] - [status-im.ui.screens.profile.tribute-to-talk.views - :as - tribute-to-talk.views] [status-im.ui.screens.chat.state :as state] [status-im.utils.debounce :as debounce] - [status-im.utils.platform :as platform] - [status-im.ui.screens.chat.extensions.views :as extensions]) + [status-im.ui.screens.chat.extensions.views :as extensions] + [status-im.ui.components.topbar :as topbar] + [status-im.ui.screens.chat.group :as chat.group] + [status-im.ui.screens.chat.message.gap :as gap] + [status-im.ui.screens.chat.message.datemark :as message-datemark]) (:require-macros [status-im.utils.views :refer [defview letsubs]])) +(defn topbar [current-chat] + [topbar/topbar + {:content [toolbar-content/toolbar-content-view] + :show-border? true + :navigation {:icon :main-icons/back + :accessibility-label :back-button + :handler + #(re-frame/dispatch [:navigate-to :home])} + :accessories [{:icon :main-icons/more + :accessibility-label :chat-menu-button + :handler + #(re-frame/dispatch [:bottom-sheet/show-sheet + {:content (fn [] + [sheets/actions current-chat]) + :height 256}])}]}]) + (defn add-contact-bar [public-key] [react/touchable-highlight @@ -43,250 +51,30 @@ {:color colors/blue}] [react/i18n-text {:style style/add-contact-text :key :add-to-contacts}]]]) -(defmulti message-row - (fn [{{:keys [type]} :row}] type)) - -(defmethod message-row :datemark - [{{:keys [value]} :row}] - [message-datemark/chat-datemark-mobile value]) - -(defmethod message-row :gap - [{:keys [row idx list-ref]}] - [gap/gap row idx list-ref]) - -(defmethod message-row :default - [{:keys [group-chat current-public-key modal? row]}] - [message/chat-message (assoc row - :group-chat group-chat - :modal? modal? - :current-public-key current-public-key)]) - -(def animation-duration 200) - -(defview messages-view-animation [add-contact-bar message-view] - ;; smooths out appearance of message-view - (letsubs [opacity (animation/create-value 0)] - {:component-did-mount (fn [_] - (animation/start - (animation/timing - opacity - {:toValue 1 - :duration animation-duration - :useNativeDriver true})))} - (if platform/desktop? - message-view - [react/animated-view {:style (style/message-view-animated opacity)} - add-contact-bar - message-view]))) - -(defn tribute-to-talk-header - [name] - [react/nested-text {:style (assoc style/intro-header-description - :margin-bottom 32)} - (i18n/label :t/tribute-required-by-multiaccount {:multiaccount-name name}) - [{:style {:color colors/blue} - :on-press #(re-frame/dispatch [:navigate-to :tribute-learn-more])} - (str " " (i18n/label :learn-more))]]) - (defn intro-header [contact] [react/text {:style (assoc style/intro-header-description :margin-bottom 32)} - (str (i18n/label :t/empty-chat-description-one-to-one) (multiaccounts/displayed-name contact))]) - -(defn join-chat-button [chat-id] - [button/button - {:type :secondary - :on-press #(re-frame/dispatch [:group-chats.ui/join-pressed chat-id]) - :label :t/join-group-chat}]) - -(defn decline-chat [chat-id] - [react/touchable-highlight - {:on-press - #(re-frame/dispatch [:group-chats.ui/remove-chat-confirmed chat-id])} - [react/text {:style style/decline-chat} - (i18n/label :t/group-chat-decline-invitation)]]) - -(defn group-chat-footer - [chat-id] - [react/view {:style style/group-chat-join-footer} - [react/view {:style style/group-chat-join-container} - [join-chat-button chat-id] - [decline-chat chat-id]]]) - -;; TODO this is now used only in Desktop - unnecessary for mobile -(defn group-chat-join-section - [inviter-name {:keys [name group-chat color chat-id]}] - [react/view style/empty-chat-container - [react/view {:style {:margin-bottom 170}} - [chat-icon.screen/profile-icon-view - nil name color false 100 - {:default-chat-icon-text style/group-chat-icon}]] - [react/view {:style style/group-chat-join-footer} - [react/view {:style style/group-chat-join-container} - [react/view - [react/text {:style style/group-chat-join-name} name]] - [react/text {:style style/intro-header-description} - (i18n/label :t/join-group-chat-description {:username inviter-name - :group-name name})] - [join-chat-button chat-id] - [decline-chat chat-id]]]]) - -(defn group-chat-description-loading - [] - [react/view {:style (merge style/intro-header-description-container - {:margin-bottom 36 - :height 44})} - [react/text {:style style/intro-header-description} - (i18n/label :t/loading)] - [react/activity-indicator {:animating true - :size :small - :color colors/gray}]]) - -(defn group-chat-description-container - [{:keys [group-chat name pending-invite-inviter-name - inviter-name color chat-id chat-name public? - contact universal-link range intro-status] :as chat}] - (let [{:keys [lowest-request-from highest-request-to]} range] - (case intro-status - :loading - [group-chat-description-loading] - - :empty - (when public? - [react/nested-text {:style (merge style/intro-header-description - {:margin-bottom 36})} - (let [quiet-hours (quot (- highest-request-to lowest-request-from) - (* 60 60)) - quiet-time (if (<= quiet-hours 24) - (i18n/label :t/quiet-hours - {:quiet-hours quiet-hours}) - (i18n/label :t/quiet-days - {:quiet-days (quot quiet-hours 24)}))] - (i18n/label :t/empty-chat-description-public - {:quiet-hours quiet-time})) - [{:style {:color colors/blue} - :on-press #(list-selection/open-share - {:message - (i18n/label - :t/share-public-chat-text {:link universal-link})})} - (i18n/label :t/empty-chat-description-public-share-this)]]) - - :messages - (when (not public?) - (if pending-invite-inviter-name - [react/nested-text {:style style/intro-header-description} - [{:style {:color :black}} pending-invite-inviter-name] - (i18n/label :t/join-group-chat-description - {:username "" - :group-name chat-name})] - (if (not= inviter-name "Unknown") - [react/nested-text {:style style/intro-header-description} - (i18n/label :t/joined-group-chat-description - {:username "" - :group-name chat-name}) - [{:style {:color :black}} inviter-name]] - [react/text {:style style/intro-header-description} - (i18n/label :t/created-group-chat-description - {:group-name chat-name})])))))) - -(defn pay-to-chat-messages - [snt-amount chat-id tribute-status tribute-label - fiat-amount fiat-currency token] - [tribute-to-talk.views/pay-to-chat-message - {:snt-amount snt-amount - :public-key chat-id - :tribute-status tribute-status - :tribute-label tribute-label - :fiat-amount fiat-amount - :fiat-currency fiat-currency - :token token - :style {:margin-horizontal 8 - :align-items :flex-start - :align-self (if snt-amount :flex-start :flex-end)}}]) - -(defn one-to-one-chat-description-container - [{:keys [chat-id name contact show-input? tribute-to-talk] - :tribute-to-talk/keys [my-message received? message tribute-status - tribute-label snt-amount on-share-my-profile - fiat-amount fiat-currency token]}] - (case tribute-status - :loading - [react/view (assoc (dissoc style/empty-chat-container :flex) - :justify-content :flex-end) - [react/view {:style {:align-items :center :justify-content :flex-end}} - [react/view {:style {:flex-direction :row :justify-content :center}} - [react/text {:style style/loading-text} - (i18n/label :t/loading)] - [react/activity-indicator {:color colors/gray - :animating true}]]]] - - :required - [react/view - [tribute-to-talk-header name] - [pay-to-chat-messages snt-amount chat-id tribute-status tribute-label - fiat-amount fiat-currency token] - [react/view {:style style/are-you-friends-bubble} - [react/text {:style (assoc style/are-you-friends-text - :font-weight "500")} - (i18n/label :t/tribute-to-talk-are-you-friends)] - [react/text {:style style/are-you-friends-text} - (i18n/label :t/tribute-to-talk-ask-to-be-added)] - [react/text {:style style/share-my-profile - :on-press on-share-my-profile} - (i18n/label :t/share-my-profile)]]] - - :pending - [react/view - [tribute-to-talk-header name] - [pay-to-chat-messages snt-amount chat-id tribute-status tribute-label - fiat-amount fiat-currency token]] - - (:paid :none) - [react/view - [intro-header contact] - (when (= tribute-status :paid) - [pay-to-chat-messages snt-amount chat-id tribute-status tribute-label - fiat-amount fiat-currency token]) - (when received? - [pay-to-chat-messages nil nil nil nil nil nil nil]) - - (when (or (= tribute-status :paid) received?) - [react/view {:style {:margin-top 16 :margin-horizontal 8}} - [react/nested-text {:style style/tribute-received-note} - (when received? - [{:style (assoc style/tribute-received-note :color colors/gray)} - (i18n/label :tribute-to-talk-tribute-received1)]) - [{:style (assoc style/tribute-received-note :font-weight "500")} - name] - [{:style (assoc style/tribute-received-note :color colors/gray)} - (i18n/label (if received? :tribute-to-talk-tribute-received2 - :tribute-to-talk-contact-received-your-tribute))]]])] - - [intro-header contact])) + (str + (i18n/label :t/empty-chat-description-one-to-one) + (multiaccounts/displayed-name contact))]) (defn chat-intro-header-container - [{:keys [group-chat name pending-invite-inviter-name - inviter-name color chat-id chat-name public? - contact universal-link intro-status height input-height] :as chat} + [{:keys [group-chat name pending-invite-inviter-name color chat-id chat-name + public? contact intro-status] :as chat} no-messages] (let [icon-text (if public? chat-id name) intro-name (if public? chat-name (multiaccounts/displayed-name contact))] - ;; TODO This when check ought to be unnecessary but for now it prevents - ;; jerky motion when fresh chat is created, when input-height can be null - ;; affecting the calculation of content-layout-height to be briefly adjusted - (when (or input-height - pending-invite-inviter-name + (when (or pending-invite-inviter-name (not= (get-in contact [:tribute-to-talk :snt-amount]) 0)) [react/touchable-without-feedback {:style {:flex 1 :align-items :flex-start} :on-press (fn [_] (re-frame/dispatch - [:chat.ui/set-chat-ui-props {:messages-focused? true - :input-bottom-sheet nil}]) + [:chat.ui/set-chat-ui-props {:input-bottom-sheet nil}]) (react/dismiss-keyboard!))} - [react/view (style/intro-header-container height intro-status no-messages) + [react/view (style/intro-header-container intro-status no-messages) ;; Icon section [react/view {:style {:margin-top 42 :margin-bottom 24}} @@ -296,11 +84,12 @@ :default-chat-icon-text style/intro-header-icon-text :size 120}]] ;; Chat title section - [react/text {:style style/intro-header-chat-name} (if group-chat chat-name intro-name)] + [react/text {:style style/intro-header-chat-name} + (if group-chat chat-name intro-name)] ;; Description section (if group-chat - [group-chat-description-container chat] - [one-to-one-chat-description-container chat])]]))) + [chat.group/group-chat-description-container chat] + [intro-header contact])]]))) (defonce messages-list-ref (atom nil)) @@ -308,11 +97,11 @@ (when @messages-list-ref (reset! state/viewable-item (when-let [last-visible-element (aget (.-viewableItems e) (dec (.-length (.-viewableItems e))))] - (let [index (.-index last-visible-element) - ;; Get first not visible element, if it's a datemark/gap - ;; we might unnecessarely add messages on receiving as - ;; they do not have a clock value, but most of the times - ;; it will be a message + (let [index (.-index last-visible-element) + ;; Get first not visible element, if it's a datemark/gap + ;; we might unnecessarely add messages on receiving as + ;; they do not have a clock value, but most of the times + ;; it will be a message first-not-visible (aget (.-data (.-props @messages-list-ref)) (inc index))] (when (and first-not-visible (= :message (:type first-not-visible))) @@ -320,149 +109,54 @@ (debounce/debounce-and-dispatch [:chat.ui/message-visibility-changed e] 5000)) (defview messages-view - [{:keys [group-chat chat-id pending-invite-inviter-name contact] :as chat} - modal?] + [{:keys [group-chat chat-id pending-invite-inviter-name] :as chat}] (letsubs [messages [:chats/current-chat-messages-stream] current-public-key [:multiaccount/public-key]] - {:component-did-update - (fn [args] - (re-frame/dispatch [:chat.ui/set-chat-ui-props - {:messages-focused? true - :input-focused? false}]))} - (let [no-messages (empty? messages) - flat-list-conf - {:data messages - :ref #(reset! messages-list-ref %) - :footer [chat-intro-header-container chat no-messages] - :key-fn #(or (:message-id %) (:value %)) - :render-fn (fn [message idx] - [message-row - {:group-chat group-chat - :modal? modal? - :current-public-key current-public-key - :row message - :idx idx - :list-ref messages-list-ref}]) - :inverted true - :onViewableItemsChanged on-viewable-items-changed - :onEndReached #(re-frame/dispatch [:chat.ui/load-more-messages]) - :onScrollToIndexFailed #() - :keyboardShouldPersistTaps :handled} - group-header {:header [group-chat-footer chat-id]}] - (if pending-invite-inviter-name - [list/flat-list (merge flat-list-conf group-header)] - [list/flat-list flat-list-conf])))) + [list/flat-list + {:key-fn #(or (:message-id %) (:value %)) + :ref #(reset! messages-list-ref %) + :header (when pending-invite-inviter-name + [chat.group/group-chat-footer chat-id]) + :footer [chat-intro-header-container chat (empty? messages)] + :data messages + :inverted true + :render-fn (fn [{:keys [outgoing] :as message} idx] + (let [type (:type message)] + (if (= type :datemark) + [message-datemark/chat-datemark (:value message)] + (if (= type :gap) + [gap/gap message idx messages-list-ref] + ; message content + [message/chat-message + (assoc message + :incoming-group (and group-chat (not outgoing)) + :group-chat group-chat + :current-public-key current-public-key)])))) + :on-viewable-items-changed on-viewable-items-changed + :on-end-reached #(re-frame/dispatch [:chat.ui/load-more-messages]) + :on-scroll-to-index-failed #() ;;don't remove this + :keyboard-should-persist-taps :handled}])) -(def load-step 5) - -(defn load-more [all-messages-count messages-to-load] - (let [next-count (min all-messages-count (+ @messages-to-load load-step))] - (reset! messages-to-load next-count))) - -(defview messages-view-desktop [{:keys [chat-id group-chat pending-invite-inviter-name]} - modal?] - (letsubs [messages [:chats/current-chat-messages-stream] - current-public-key [:multiaccount/public-key] - messages-to-load (reagent/atom load-step) - chat-id* (reagent/atom nil)] - {:component-did-update #(if (:messages-initialized? (second (.-argv (.-props %1)))) - (load-more (count messages) messages-to-load) - (re-frame/dispatch [:chat.ui/load-more-messages])) - :component-did-mount #(if (:messages-initialized? (second (.-argv (.-props %1)))) - (load-more (count messages) messages-to-load) - (re-frame/dispatch [:chat.ui/load-more-messages]))} - (let [messages-list-ref (atom nil) - scroll-timer (atom nil) - scroll-height (atom nil) - _ (when (or (not @chat-id*) (not= @chat-id* chat-id)) - (do - (reset! messages-to-load load-step) - (reset! chat-id* chat-id)))] - [react/view {:style style/chat-view} - [react/scroll-view {:scrollEventThrottle 16 - :headerHeight style/messages-list-vertical-padding - :footerWidth style/messages-list-vertical-padding - :enableArrayScrollingOptimization true - :inverted true - :ref #(reset! messages-list-ref %) - :on-scroll (fn [e] - (let [ne (.-nativeEvent e) - y (.-y (.-contentOffset ne))] - (when (<= y 0) - (when @scroll-timer (js/clearTimeout @scroll-timer)) - (reset! scroll-timer (js/setTimeout #(re-frame/dispatch [:chat.ui/load-more-messages]) 300))) - (reset! scroll-height (+ y (.-height (.-layoutMeasurement ne))))))} - [react/view - (doall - (for [{:keys [from content] :as message-obj} (take @messages-to-load messages)] - ^{:key message-obj} - [message-row - {:group-chat group-chat - :modal? modal? - :current-public-key current-public-key - :row message-obj - :idx #(or (:message-id message-obj) (:value message-obj)) - :list-ref messages-list-ref}]))]] - (if pending-invite-inviter-name - [group-chat-footer chat-id])]))) - -(defview chat-root [modal?] - (letsubs [{:keys [public? chat-id chat-name show-input? group-chat contact] :as current-chat} - [:chats/current-chat] - current-chat-id [:chats/current-chat-id] - input-bottom-sheet [:chats/current-chat-ui-prop :input-bottom-sheet] - two-pane-ui-enabled? [:two-pane-ui-enabled?] - anim-translate-y (animation/create-value - (if two-pane-ui-enabled? 0 connectivity/neg-connectivity-bar-height))] - [react/view {:style style/chat-view - :on-layout (fn [e] - (re-frame/dispatch [:set :layout-height (-> e .-nativeEvent .-layout .-height)]))} - [toolbar/toolbar - {:chat? true - :style {:z-index 2}} - (if modal? - [toolbar/nav-button - (toolbar.actions/close toolbar.actions/default-handler)] - toolbar/nav-back-home) - [toolbar-content/toolbar-content-view] - (when-not modal? - [toolbar/actions - [{:icon :main-icons/more - :icon-opts {:color :black - :accessibility-label :chat-menu-button} - :handler #(re-frame/dispatch [:bottom-sheet/show-sheet - {:content (fn [] - [sheets/actions current-chat]) - :height 256}])}]])] - (when-not two-pane-ui-enabled? - [connectivity/connectivity-view anim-translate-y]) - [connectivity/connectivity-animation-wrapper - {} - anim-translate-y - false - [messages-view-animation - (if (and (= chat-id current-chat-id) (not group-chat) (not (contact.db/added? contact))) - [add-contact-bar chat-id]) - ;;TODO(kozieiev) : When FlatList in react-native-desktop become viable it should be used instead of optimized ScrollView for chat - (if platform/desktop? - [messages-view-desktop current-chat modal?] - [messages-view current-chat modal?])]] - (when show-input? - [input/container]) - (case input-bottom-sheet - :stickers - [stickers/stickers-view] - :extensions - [extensions/extensions-view] - nil)])) +(defview bottom-sheet [] + (letsubs [input-bottom-sheet [:chats/current-chat-ui-prop :input-bottom-sheet]] + (case input-bottom-sheet + :stickers + [stickers/stickers-view] + :extensions + [extensions/extensions-view] + nil))) (defview chat [] - [chat-root false]) - -(defview chat-modal [] - [chat-root true]) - -(defview select-chat [] - [react/view {:align-items :center :justify-content :center :flex 1} - [react/text style/select-chat - (i18n/label :t/select-chat)]]) + (letsubs [{:keys [chat-id show-input? group-chat contact] :as current-chat} + [:chats/current-chat]] + [react/view {:style {:flex 1}} + [connectivity/connectivity + [topbar current-chat] + [react/view {:style {:flex 1}} + ;;TODO contact.db/added? looks weird here, move to events + (when (and (not group-chat) (not (contact.db/added? contact))) + [add-contact-bar chat-id]) + [messages-view current-chat]]] + (when show-input? + [input/container]) + [bottom-sheet]])) \ No newline at end of file diff --git a/src/status_im/ui/screens/home/sheet/views.cljs b/src/status_im/ui/screens/home/sheet/views.cljs index 5d8c338227..01134c6ef4 100644 --- a/src/status_im/ui/screens/home/sheet/views.cljs +++ b/src/status_im/ui/screens/home/sheet/views.cljs @@ -1,12 +1,8 @@ (ns status-im.ui.screens.home.sheet.views - (:require-macros [status-im.utils.views :refer [defview letsubs]]) (:require [re-frame.core :as re-frame] [status-im.i18n :as i18n] - [status-im.ui.components.colors :as colors] [status-im.ui.components.list-selection :as list-selection] [status-im.ui.components.react :as react] - [status-im.utils.universal-links.core :as universal-links] - [status-im.utils.platform :as platform] [status-im.ui.components.list-item.views :as list-item] [status-im.utils.config :as config])) @@ -45,4 +41,4 @@ (list-selection/open-share {:message (i18n/label :t/get-status-at)}))}]]) (def add-new - {:content add-new-view}) + {:content add-new-view}) \ No newline at end of file diff --git a/src/status_im/ui/screens/home/styles.cljs b/src/status_im/ui/screens/home/styles.cljs index e2f32bc182..157e92c693 100644 --- a/src/status_im/ui/screens/home/styles.cljs +++ b/src/status_im/ui/screens/home/styles.cljs @@ -1,22 +1,12 @@ (ns status-im.ui.screens.home.styles (:require [status-im.ui.components.colors :as colors] - [status-im.utils.styles :as styles] - [status-im.ui.components.search-input.styles :as search-input.styles] - [status-im.utils.platform :as platform])) - -(defn toolbar [] - {:background-color colors/white}) - -(def sync-wrapper - {:flex-direction :row}) - -(def sync-info - {:margin-horizontal 15}) + [status-im.utils.platform :as platform] + [status-im.ui.components.tabbar.styles :as tabs.styles])) (def last-message-container {:flex-shrink 1}) -(styles/def last-message-text +(def last-message-text {:flex 1 :align-self :stretch :line-height 22 @@ -31,31 +21,6 @@ :width 12 :height 12}) -(def search-container - (merge - search-input.styles/search-container - (when platform/ios? - {:position :absolute - :top (- search-input.styles/search-input-height) - :width "100%"}))) - -(def filter-section-title - {:margin-left 16 - :margin-top 14 - :margin-bottom 4 - :color colors/gray}) - -(def status-container - {:flex-direction :row - :top 16 - :right 16}) - -(def status-image - {:opacity 0.6 - :margin-right 4 - :width 16 - :height 16}) - (def datetime-text {:color colors/text-gray :font-size 10 @@ -64,34 +29,13 @@ :align-items :center :line-height 12}) -(styles/def new-messages-text - {:left 0 - :font-size 12 - :color colors/blue - :text-align :center - :android {:top 2} - :ios {:top 3} - :desktop {:top 3}}) - -(def group-icon - {:margin-top 8 - :margin-right 6 - :width 14 - :height 9 - :tint-color :white}) - -(def no-chats - {:flex 1 - :padding-top 16 - :padding-horizontal 16 - :background-color :white}) - (def chat-tooltip {:align-items :center :border-color colors/gray-lighter :border-width 1 :border-radius 16 - :margin 16}) + :margin 16 + :margin-bottom 68}) (def no-chats-text {:margin-top 50 @@ -103,9 +47,6 @@ {:flex 1 :justify-content :flex-end}) -(def welcome-image-container - {:align-items :center}) - (def welcome-text {:typography :header :text-align :center}) @@ -124,12 +65,12 @@ :margin-horizontal 40 :color colors/gray}) -(defn action-button-container [home-width] +(def action-button-container {:position :absolute :z-index 2 :align-items :center + :align-self :center :bottom 16 - :left (- (/ home-width 2) 20) :width 40 :height 40}) @@ -169,16 +110,6 @@ {:margin-top 10 :margin-bottom 18}) -(def tag-text - {:font-size 13 - :font-weight "500" - :line-height 20 - :margin-left 10 - :margin-right 10 - :margin-top 6 - :margin-bottom 6 - :color colors/blue}) - (def close-icon-container {:width 24 :height 24 @@ -186,3 +117,10 @@ :background-color colors/gray :align-items :center :justify-content :center}) + +(def home-container + (merge + {:flex 1} + ;;TODO move this to navigation layer + (when platform/ios? + {:margin-bottom tabs.styles/tabs-diff}))) \ No newline at end of file diff --git a/src/status_im/ui/screens/home/views.cljs b/src/status_im/ui/screens/home/views.cljs index dd72b90ae3..66f7146044 100644 --- a/src/status_im/ui/screens/home/views.cljs +++ b/src/status_im/ui/screens/home/views.cljs @@ -7,37 +7,31 @@ [status-im.ui.components.icons.vector-icons :as icons] [status-im.ui.components.list.views :as list] [status-im.ui.components.react :as react] - [status-im.ui.components.toolbar.view :as toolbar] [status-im.ui.screens.home.styles :as styles] - [status-im.utils.platform :as platform] - [status-im.ui.components.tabbar.styles :as tabs.styles] [status-im.ui.screens.home.views.inner-item :as inner-item] [status-im.ui.components.common.common :as components.common] [status-im.ui.components.list-selection :as list-selection] - [status-im.ui.components.animation :as animation] - [status-im.constants :as constants] [status-im.ui.components.colors :as colors] [status-im.ui.screens.add-new.new-public-chat.view :as new-public-chat] [status-im.ui.components.button :as button] [status-im.ui.components.search-input.view :as search-input] - [status-im.ui.components.search-input.styles :as search-input.styles]) + [cljs-bean.core :as bean] + [status-im.ui.components.topbar :as topbar]) (:require-macros [status-im.utils.views :as views])) -(defonce search-active? (reagent/atom false)) - (defn welcome-image-wrapper [] (let [dimensions (reagent/atom {})] (fn [] - [react/view {:on-layout (fn [e] - (reset! dimensions (js->clj (-> e .-nativeEvent .-layout) :keywordize-keys true))) - :style {:align-items :center - :justify-content :center - :flex 1}} + [react/view {:on-layout (fn [e] + (reset! dimensions (bean/->clj (-> e .-nativeEvent .-layout)))) + :style {:align-items :center + :justify-content :center + :flex 1}} (let [padding 0 image-size (- (min (:width @dimensions) (:height @dimensions)) padding)] - [react/image {:source (resources/get-image :welcome) + [react/image {:source (resources/get-image :welcome) :resize-mode :contain - :style {:width image-size :height image-size}}])]))) + :style {:width image-size :height image-size}}])]))) (defn welcome [] [react/view {:style styles/welcome-view} @@ -47,9 +41,9 @@ [react/i18n-text {:style styles/welcome-text-description :key :welcome-to-status-description}]] [react/view {:align-items :center :margin-bottom 50} - [components.common/button {:on-press #(re-frame/dispatch [:navigate-back]) + [components.common/button {:on-press #(re-frame/dispatch [:navigate-back]) :accessibility-label :lets-go-button - :label (i18n/label :t/lets-go)}]]]) + :label (i18n/label :t/lets-go)}]]]) (defn home-tooltip-view [] [react/view styles/chat-tooltip @@ -86,121 +80,56 @@ [react/view {:style {:flex 1 :flex-direction :row :align-items :center :justify-content :center}} [react/i18n-text {:style styles/welcome-blank-text :key :welcome-blank-message}]]) -(defn chat-list-footer [hide-home-tooltip?] - (let [show-tooltip? (and (not hide-home-tooltip?) (not @search-active?))] - [react/view - (when show-tooltip? - [home-tooltip-view]) - [react/view {:height 68 :flex 1}]])) +(defonce search-active? (reagent/atom false)) (defn search-input-wrapper [search-filter] [search-input/search-input - {:search-active? search-active? - :search-container-style styles/search-container - :search-filter search-filter - :on-cancel #(re-frame/dispatch [:search/home-filter-changed nil]) - :on-focus (fn [search-filter] - (when-not search-filter - (re-frame/dispatch [:search/home-filter-changed ""]))) - :on-change (fn [text] - (re-frame/dispatch [:search/home-filter-changed text]))}]) + {:search-active? search-active? + :search-filter search-filter + :on-cancel #(re-frame/dispatch [:search/home-filter-changed nil]) + :on-focus (fn [search-filter] + (when-not search-filter + (re-frame/dispatch [:search/home-filter-changed ""]))) + :on-change (fn [text] + (re-frame/dispatch [:search/home-filter-changed text]))}]) -(defn section-footer [{:keys [title data]}] - (when (and @search-active? (empty? data)) - [list/big-list-item - {:text (i18n/label :t/no-result) - :text-color colors/gray - :hide-chevron? true - :action-fn #() - :icon (case title - "messages" :main-icons/one-on-one-chat - "browser" :main-icons/browser - "chats" :main-icons/message) - :icon-color colors/gray}])) +(views/defview chats-list [] + (views/letsubs [loading? [:chats/loading?] + {:keys [chats all-home-items search-filter]} [:home-items] + {:keys [hide-home-tooltip?]} [:multiaccount]] + (if loading? + [react/activity-indicator {:flex 1 :animating true}] + (if (and (empty? all-home-items) hide-home-tooltip? (not @search-active?)) + [welcome-blank-page] + (let [data (if @search-active? chats all-home-items)] + [list/flat-list + {:key-fn first + :keyboard-should-persist-taps :always + :data data + :render-fn inner-item/home-list-item + :header (when (or (not-empty data) @search-active?) + [search-input-wrapper search-filter]) + :footer (if (and (not hide-home-tooltip?) (not @search-active?)) + [home-tooltip-view] + [react/view {:height 68}])}]))))) -(views/defview home-filtered-items-list [] - (views/letsubs - [{:keys [chats all-home-items search-filter]} [:home-items] - {:keys [hide-home-tooltip?]} [:multiaccount]] - (let [list-ref (reagent/atom nil)] - [list/section-list - (merge - {:sections [{:title :t/chats - :data (if @search-active? chats all-home-items)}] - :key-fn first - ;; true by default on iOS - :stickySectionHeadersEnabled false - :keyboard-should-persist-taps :always - :ref #(reset! list-ref %) - :footer [chat-list-footer hide-home-tooltip?] - :contentInset {:top search-input.styles/search-input-height} - :render-section-header-fn (fn [data] [react/view]) - :render-section-footer-fn section-footer - :render-fn (fn [home-item] - [inner-item/home-list-item home-item]) - :header (when (or @search-active? (not-empty all-home-items)) - [search-input-wrapper search-filter]) - :on-scroll-end-drag - (fn [e] - (let [y (-> e .-nativeEvent .-contentOffset .-y) - hide-searchbar? (cond - platform/ios? (and (neg? y) (> y (- (/ search-input.styles/search-input-height 2)))) - platform/android? (and (< y search-input.styles/search-input-height) (> y (/ search-input.styles/search-input-height 2))))] - (if hide-searchbar? - (.scrollToLocation @list-ref #js {:sectionIndex 0 :itemIndex 0}))))})]))) - -(views/defview home-action-button [home-width] +(views/defview plus-button [] (views/letsubs [logging-in? [:multiaccounts/login]] - [react/view (styles/action-button-container home-width) - [react/touchable-highlight {:accessibility-label :new-chat-button - :on-press (when-not logging-in? #(re-frame/dispatch [:bottom-sheet/show-sheet :add-new {}]))} + [react/view styles/action-button-container + [react/touchable-highlight + {:accessibility-label :new-chat-button + :on-press (when-not logging-in? + #(re-frame/dispatch [:bottom-sheet/show-sheet :add-new {}]))} [react/view styles/action-button (if logging-in? [react/activity-indicator {:color :white :animating true}] [icons/icon :main-icons/add {:color :white}])]]])) -(views/defview home [loading?] - (views/letsubs - [anim-translate-y (animation/create-value connectivity/neg-connectivity-bar-height) - {:keys [all-home-items]} [:home-items] - {:keys [hide-home-tooltip?]} [:multiaccount] - window-width [:dimensions/window-width] - two-pane-ui-enabled? [:two-pane-ui-enabled?]] - (let [home-width (if (> window-width constants/two-pane-min-width) - (max constants/left-pane-min-width (/ window-width 3)) - window-width)] - [react/view (merge {:flex 1 - :width home-width} - (when platform/ios? - {:margin-bottom tabs.styles/tabs-diff}) - (when two-pane-ui-enabled? - {:border-right-width 1 :border-right-color colors/gray-lighter})) - [react/keyboard-avoiding-view {:style {:flex 1} - :on-layout (fn [e] - (re-frame/dispatch - [:set-once :content-layout-height - (-> e .-nativeEvent .-layout .-height)]))} - [toolbar/toolbar {:style {:z-index 2}} nil [toolbar/content-title (i18n/label :t/chat)]] - ;; toolbar, connectivity-view, cannectivity-animation-wrapper are expected - ;; to be next to each other as siblings for them to work effctively. - ;; les-debug-info being here could disrupt that. Assuming its purpose is - ;; debug only, commenting it out for now. - ;; [les-debug-info] - [connectivity/connectivity-view anim-translate-y] - [connectivity/connectivity-animation-wrapper - {} - anim-translate-y - true - (if loading? - [react/activity-indicator {:flex 1 - :animating true}] - [react/view {:flex 1} - (if (and (empty? all-home-items) hide-home-tooltip? (not @search-active?)) - [welcome-blank-page] - [home-filtered-items-list])])] - [home-action-button home-width]]]))) - -(views/defview home-wrapper [] - (views/letsubs [loading? [:chats/loading?]] - [home loading?])) +(defn home [] + [react/keyboard-avoiding-view {:style styles/home-container} + [connectivity/connectivity + [topbar/topbar {:title :t/chat :navigation :none + :show-border? true}] + [chats-list]] + [plus-button]]) \ No newline at end of file diff --git a/src/status_im/ui/screens/home/views/inner_item.cljs b/src/status_im/ui/screens/home/views/inner_item.cljs index 57c0845e3b..94b6d6d276 100644 --- a/src/status_im/ui/screens/home/views/inner_item.cljs +++ b/src/status_im/ui/screens/home/views/inner_item.cljs @@ -4,7 +4,6 @@ [status-im.constants :as constants] [status-im.i18n :as i18n] [status-im.ui.components.chat-icon.screen :as chat-icon.screen] - [status-im.ui.components.common.common :as components.common] [status-im.ui.screens.chat.sheets :as sheets] [status-im.ui.components.list-item.views :as list-item] [status-im.ui.components.badge :as badge] @@ -15,7 +14,7 @@ [status-im.utils.datetime :as time]) (:require-macros [status-im.utils.views :refer [defview letsubs]])) -(defn message-content-text [{:keys [content content-type] :as message}] +(defn message-content-text [{:keys [content content-type]}] [react/view styles/last-message-container (cond @@ -26,6 +25,7 @@ (= constants/content-type-sticker content-type) [react/image {:style {:margin 1 :width 20 :height 20} + ;;TODO (perf) move to event :source {:uri (contenthash/url (-> content :sticker :hash))}}] (string/blank? (:text content)) @@ -37,39 +37,40 @@ :number-of-lines 1 :ellipsize-mode :tail :accessibility-label :chat-message-text} - (string/trim-newline (:text content))])]) + ;;TODO (perf) move to event + (-> (:text content) + (subs 0 40) + (string/trim-newline))])]) (defn message-timestamp [timestamp] (when timestamp [react/text {:style styles/datetime-text :accessibility-label :last-message-time-text} + ;;TODO (perf) move to event (string/upper-case (time/to-short-str timestamp))])) -(defview unviewed-indicator [chat-id] - (letsubs [{:keys [unviewed-messages-count public?]} [:chats/chat chat-id]] - (when (pos? unviewed-messages-count) - (if public? - [react/view {:style styles/public-unread - :accessibility-label :unviewed-messages-public}] - [badge/message-counter unviewed-messages-count])))) +(defn unviewed-indicator [{:keys [unviewed-messages-count public?]}] + (when (pos? unviewed-messages-count) + (if public? + [react/view {:style styles/public-unread + :accessibility-label :unviewed-messages-public}] + [badge/message-counter unviewed-messages-count]))) (defn home-list-item [[_ home-item]] - (let [{:keys - [chat-id chat-name - color online group-chat - public? contact - timestamp - last-message]} home-item + (let [{:keys [chat-id chat-name color online group-chat + public? contact timestamp last-message]} + home-item private-group? (and group-chat (not public?)) public-group? (and group-chat public?) + ;;TODO (perf) move to event truncated-chat-name (utils/truncate-str chat-name 30)] [list-item/list-item {:icon [chat-icon.screen/chat-icon-view-chat-list contact group-chat truncated-chat-name color online false] :title-prefix (cond private-group? :main-icons/tiny-group - public-group? :main-icons/tiny-public - :else nil) + public-group? :main-icons/tiny-public + :else nil) :title truncated-chat-name :title-accessibility-label :chat-name-text :title-row-accessory [message-timestamp (if (pos? (:whisper-timestamp last-message)) @@ -81,7 +82,7 @@ [message-content-text {:content (:content last-message) :content-type (:content-type last-message)}] tribute-label)) - :subtitle-row-accessory [unviewed-indicator chat-id] + :subtitle-row-accessory [unviewed-indicator home-item] :on-press #(do (re-frame/dispatch [:dismiss-keyboard]) (re-frame/dispatch [:chat.ui/navigate-to-chat chat-id]) @@ -91,4 +92,4 @@ :on-long-press #(re-frame/dispatch [:bottom-sheet/show-sheet {:content (fn [] [sheets/actions home-item]) - :height 256}])}])) + :height 256}])}])) \ No newline at end of file diff --git a/src/status_im/ui/screens/routing/chat_stack.cljs b/src/status_im/ui/screens/routing/chat_stack.cljs index c5c3edf3f5..a03dc55bb8 100644 --- a/src/status_im/ui/screens/routing/chat_stack.cljs +++ b/src/status_im/ui/screens/routing/chat_stack.cljs @@ -4,7 +4,6 @@ {:name :chat-stack :screens [:home :chat - :select-chat :profile :take-picture :new-group @@ -13,5 +12,4 @@ :group-chat-profile :stickers :stickers-pack] - :config {:initialRouteName :home - :emptyRightPaneName :select-chat}}) + :config {:initialRouteName :home}}) diff --git a/src/status_im/ui/screens/routing/modals.cljs b/src/status_im/ui/screens/routing/modals.cljs index 435b0a47f6..dffa818478 100644 --- a/src/status_im/ui/screens/routing/modals.cljs +++ b/src/status_im/ui/screens/routing/modals.cljs @@ -1,8 +1,7 @@ (ns status-im.ui.screens.routing.modals) (def modal-screens - [:chat-modal - :stickers-pack-modal + [:stickers-pack-modal :tribute-learn-more :selection-modal-screen :wallet-transactions-filter diff --git a/src/status_im/ui/screens/routing/screens.cljs b/src/status_im/ui/screens/routing/screens.cljs index 07416fdaf6..ecb58f9633 100644 --- a/src/status_im/ui/screens/routing/screens.cljs +++ b/src/status_im/ui/screens/routing/screens.cljs @@ -99,9 +99,8 @@ :keycard-unpaired keycard/unpaired :keycard-login-pin keycard/login-pin :not-keycard keycard/not-keycard - :home home/home-wrapper + :home home/home :chat chat/chat - :select-chat chat/select-chat :profile profile.contact/profile :new-chat [:modal new-chat/new-chat] :qr-scanner [:modal qr-scanner/qr-scanner] @@ -116,7 +115,6 @@ :stickers-pack stickers/pack :stickers-pack-modal [:modal stickers/pack-modal] :tribute-learn-more [:modal tr-to-talk/learn-more] - :chat-modal [:modal chat/chat-modal] :wallet wallet.accounts/accounts-overview :wallet-account wallet.account/account :collectibles-list collectibles/collectibles-list diff --git a/src/status_im/utils/contenthash.cljs b/src/status_im/utils/contenthash.cljs index 387eb3e303..5e6e466a3e 100644 --- a/src/status_im/utils/contenthash.cljs +++ b/src/status_im/utils/contenthash.cljs @@ -55,12 +55,14 @@ hex/decode b58/encode))}))) -(defn url [hex] +(defn url-fn [hex] (let [{:keys [namespace hash]} (decode (ethereum/normalized-hex hex))] (case namespace :ipfs (str "https://ipfs.infura.io/ipfs/" hash) ""))) +(def url (memoize url-fn)) + (fx/defn cat [cofx {:keys [contenthash on-success on-failure]}] (let [{:keys [namespace hash]} (decode contenthash)]