chat screens performance
Signed-off-by: Andrey Shovkoplyas <motor4ik@gmail.com>
This commit is contained in:
parent
4e567cf782
commit
c4a7849c9d
|
@ -314,9 +314,7 @@
|
||||||
(defn main-screen-modal-view [current-view & components]
|
(defn main-screen-modal-view [current-view & components]
|
||||||
[(create-main-screen-view current-view)
|
[(create-main-screen-view current-view)
|
||||||
styles/flex
|
styles/flex
|
||||||
[(if (= current-view :chat-modal)
|
[keyboard-avoiding-view
|
||||||
view
|
|
||||||
keyboard-avoiding-view)
|
|
||||||
(merge {:flex 1 :flex-direction :column}
|
(merge {:flex 1 :flex-direction :column}
|
||||||
(when platform/android?
|
(when platform/android?
|
||||||
{:background-color :white}))
|
{:background-color :white}))
|
||||||
|
|
|
@ -324,8 +324,6 @@
|
||||||
(views/letsubs []
|
(views/letsubs []
|
||||||
(let [main-screen-view (create-main-screen-view current-view)]
|
(let [main-screen-view (create-main-screen-view current-view)]
|
||||||
[main-screen-view styles/flex
|
[main-screen-view styles/flex
|
||||||
[(if (= current-view :chat-modal)
|
[keyboard-avoiding-view
|
||||||
view
|
|
||||||
keyboard-avoiding-view)
|
|
||||||
{:flex 1 :flex-direction :column}
|
{:flex 1 :flex-direction :column}
|
||||||
(apply vector view styles/flex components)]])))
|
(apply vector view styles/flex components)]])))
|
||||||
|
|
|
@ -248,40 +248,33 @@
|
||||||
|
|
||||||
(fx/defn navigate-to-chat
|
(fx/defn navigate-to-chat
|
||||||
"Takes coeffects map and chat-id, returns effects necessary for navigation and preloading data"
|
"Takes coeffects map and chat-id, returns effects necessary for navigation and preloading data"
|
||||||
[cofx chat-id {:keys [modal? navigation-reset?]}]
|
[cofx chat-id]
|
||||||
(cond
|
(fx/merge cofx
|
||||||
modal?
|
(navigation/navigate-to-cofx :chat {})
|
||||||
(fx/merge cofx
|
(preload-chat-data chat-id)))
|
||||||
(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))))
|
|
||||||
|
|
||||||
(fx/defn start-chat
|
(fx/defn start-chat
|
||||||
"Start a chat, making sure it exists"
|
"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
|
;; don't allow to open chat with yourself
|
||||||
(when (not= (multiaccounts.model/current-public-key cofx) chat-id)
|
(when (not= (multiaccounts.model/current-public-key cofx) chat-id)
|
||||||
(fx/merge cofx
|
(fx/merge cofx
|
||||||
(upsert-chat {:chat-id chat-id
|
(upsert-chat {:chat-id chat-id
|
||||||
:is-active true})
|
:is-active true})
|
||||||
(transport.filters/load-chat chat-id)
|
(transport.filters/load-chat chat-id)
|
||||||
(navigate-to-chat chat-id opts))))
|
(navigate-to-chat chat-id))))
|
||||||
|
|
||||||
(fx/defn start-public-chat
|
(fx/defn start-public-chat
|
||||||
"Starts a new public chat"
|
"Starts a new public chat"
|
||||||
[cofx topic {:keys [dont-navigate?] :as opts}]
|
[cofx topic {:keys [dont-navigate?]}]
|
||||||
(if (active-chat? cofx topic)
|
(if (active-chat? cofx topic)
|
||||||
(when-not dont-navigate?
|
(when-not dont-navigate?
|
||||||
(navigate-to-chat cofx topic opts))
|
(navigate-to-chat cofx topic))
|
||||||
(fx/merge cofx
|
(fx/merge cofx
|
||||||
(add-public-chat topic)
|
(add-public-chat topic)
|
||||||
(transport.filters/load-chat topic)
|
(transport.filters/load-chat topic)
|
||||||
#(when-not dont-navigate?
|
#(when-not dont-navigate?
|
||||||
(navigate-to-chat % topic opts)))))
|
(navigate-to-chat % topic)))))
|
||||||
|
|
||||||
(fx/defn disable-chat-cooldown
|
(fx/defn disable-chat-cooldown
|
||||||
"Turns off chat cooldown (protection against message spamming)"
|
"Turns off chat cooldown (protection against message spamming)"
|
||||||
|
|
|
@ -110,8 +110,7 @@
|
||||||
content] :as message}]
|
content] :as message}]
|
||||||
(let [{:keys [current-chat-id view-id]} db
|
(let [{:keys [current-chat-id view-id]} db
|
||||||
cursor-clock-value (get-in db [:chats current-chat-id :cursor-clock-value])
|
cursor-clock-value (get-in db [:chats current-chat-id :cursor-clock-value])
|
||||||
current-chat? (and (or (= :chat view-id)
|
current-chat? (and (= :chat view-id)
|
||||||
(= :chat-modal view-id))
|
|
||||||
(= current-chat-id chat-id))]
|
(= current-chat-id chat-id))]
|
||||||
(when (and current-chat?
|
(when (and current-chat?
|
||||||
(or (not cursor-clock-value)
|
(or (not cursor-clock-value)
|
||||||
|
@ -165,8 +164,7 @@
|
||||||
message-id] :as message}]
|
message-id] :as message}]
|
||||||
(when-not (= message-type constants/message-type-private-group-system-message)
|
(when-not (= message-type constants/message-type-private-group-system-message)
|
||||||
(let [{:keys [current-chat-id view-id]} db
|
(let [{:keys [current-chat-id view-id]} db
|
||||||
chat-view? (or (= :chat view-id)
|
chat-view? (= :chat view-id)
|
||||||
(= :chat-modal view-id))
|
|
||||||
current-count (get-in db [:chats chat-id :unviewed-messages-count])]
|
current-count (get-in db [:chats chat-id :unviewed-messages-count])]
|
||||||
(cond
|
(cond
|
||||||
(= from (multiaccounts.model/current-public-key cofx))
|
(= from (multiaccounts.model/current-public-key cofx))
|
||||||
|
|
|
@ -490,8 +490,8 @@
|
||||||
|
|
||||||
(handlers/register-handler-fx
|
(handlers/register-handler-fx
|
||||||
:chat.ui/navigate-to-chat
|
:chat.ui/navigate-to-chat
|
||||||
(fn [cofx [_ chat-id opts]]
|
(fn [cofx [_ chat-id _]]
|
||||||
(chat/navigate-to-chat cofx chat-id opts)))
|
(chat/navigate-to-chat cofx chat-id)))
|
||||||
|
|
||||||
(handlers/register-handler-fx
|
(handlers/register-handler-fx
|
||||||
:chat.ui/load-more-messages
|
:chat.ui/load-more-messages
|
||||||
|
|
|
@ -65,7 +65,7 @@
|
||||||
(data-store.messages/<-rpc %))
|
(data-store.messages/<-rpc %))
|
||||||
messages)
|
messages)
|
||||||
navigate-fx #(if (get-in % [:db :chats chat-id :is-active])
|
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 {}))]
|
(navigation/navigate-to-cofx % :home {}))]
|
||||||
|
|
||||||
(apply fx/merge cofx (concat [chat-fx]
|
(apply fx/merge cofx (concat [chat-fx]
|
||||||
|
|
|
@ -86,10 +86,8 @@
|
||||||
(reg-root-key-sub :keyboard-height :keyboard-height)
|
(reg-root-key-sub :keyboard-height :keyboard-height)
|
||||||
(reg-root-key-sub :keyboard-max-height :keyboard-max-height)
|
(reg-root-key-sub :keyboard-max-height :keyboard-max-height)
|
||||||
(reg-root-key-sub :sync-data :sync-data)
|
(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 :mobile-network/remember-choice? :mobile-network/remember-choice?)
|
||||||
(reg-root-key-sub :qr-modal :qr-modal)
|
(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 :bootnodes/manage :bootnodes/manage)
|
||||||
(reg-root-key-sub :networks/current-network :networks/current-network)
|
(reg-root-key-sub :networks/current-network :networks/current-network)
|
||||||
(reg-root-key-sub :networks/networks :networks/networks)
|
(reg-root-key-sub :networks/networks :networks/networks)
|
||||||
|
@ -566,32 +564,6 @@
|
||||||
(fn [chats [_ chat-id]]
|
(fn [chats [_ chat-id]]
|
||||||
(get 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
|
(re-frame/reg-sub
|
||||||
:chats/current-chat-ui-props
|
:chats/current-chat-ui-props
|
||||||
:<- [::chat-ui-props]
|
:<- [::chat-ui-props]
|
||||||
|
@ -703,11 +675,8 @@
|
||||||
(assoc :show-input? true))))
|
(assoc :show-input? true))))
|
||||||
|
|
||||||
(defn enrich-current-chat
|
(defn enrich-current-chat
|
||||||
[{:keys [messages chat-id might-have-join-time-messages?] :as chat}
|
[{:keys [messages chat-id might-have-join-time-messages?] :as chat} ranges]
|
||||||
ranges height input-height]
|
|
||||||
(assoc chat
|
(assoc chat
|
||||||
:height height
|
|
||||||
:input-height input-height
|
|
||||||
:range
|
:range
|
||||||
(get ranges chat-id)
|
(get ranges chat-id)
|
||||||
:intro-status
|
:intro-status
|
||||||
|
@ -735,12 +704,10 @@
|
||||||
:<- [:chats/current-raw-chat]
|
:<- [:chats/current-raw-chat]
|
||||||
:<- [:multiaccount/public-key]
|
:<- [:multiaccount/public-key]
|
||||||
:<- [:mailserver/ranges]
|
:<- [:mailserver/ranges]
|
||||||
:<- [:chats/content-layout-height]
|
(fn [[{:keys [group-chat chat-id messages] :as current-chat}
|
||||||
:<- [:chats/current-chat-ui-prop :input-height]
|
my-public-key ranges]]
|
||||||
(fn [[{:keys [group-chat chat-id contact messages] :as current-chat}
|
|
||||||
my-public-key ranges height input-height]]
|
|
||||||
(when current-chat
|
(when current-chat
|
||||||
(cond-> (enrich-current-chat current-chat ranges height input-height)
|
(cond-> (enrich-current-chat current-chat ranges)
|
||||||
(empty? messages)
|
(empty? messages)
|
||||||
(assoc :universal-link
|
(assoc :universal-link
|
||||||
(links/generate-link :public-chat :external chat-id))
|
(links/generate-link :public-chat :external chat-id))
|
||||||
|
@ -830,6 +797,7 @@
|
||||||
:<- [:chats/all-loaded?]
|
:<- [:chats/all-loaded?]
|
||||||
:<- [:chats/public?]
|
:<- [:chats/public?]
|
||||||
(fn [[message-list messages messages-gaps range all-loaded? 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)
|
(-> (models.message-list/->seq message-list)
|
||||||
(chat.db/add-datemarks)
|
(chat.db/add-datemarks)
|
||||||
(hydrate-messages messages)
|
(hydrate-messages messages)
|
||||||
|
@ -851,7 +819,7 @@
|
||||||
:<- [:contacts/contacts]
|
:<- [:contacts/contacts]
|
||||||
:<- [:multiaccount]
|
:<- [:multiaccount]
|
||||||
(fn [[contacts multiaccount] [_ id]]
|
(fn [[contacts multiaccount] [_ id]]
|
||||||
(multiaccounts/displayed-photo (or (contacts id)
|
(multiaccounts/displayed-photo (or (get contacts id)
|
||||||
(when (= id (:public-key multiaccount))
|
(when (= id (:public-key multiaccount))
|
||||||
multiaccount)
|
multiaccount)
|
||||||
(contact.db/public-key->new-contact id)))))
|
(contact.db/public-key->new-contact id)))))
|
||||||
|
|
|
@ -1,24 +1,17 @@
|
||||||
(ns status-im.ui.components.connectivity.styles
|
(ns status-im.ui.components.connectivity.styles
|
||||||
(:require [status-im.ui.components.colors :as colors]
|
(:require [status-im.ui.components.colors :as colors]))
|
||||||
[status-im.utils.platform :as platform]))
|
|
||||||
|
|
||||||
(defn text-wrapper
|
(defn text-wrapper
|
||||||
[{:keys [window-width height background-color opacity transform]}]
|
[{:keys [window-width height background-color opacity transform]}]
|
||||||
(cond-> {:flex-direction :row
|
{:flex-direction :row
|
||||||
:justify-content :center
|
:justify-content :center
|
||||||
:transform [{:translateY transform}]
|
:transform [{:translateY transform}]
|
||||||
:opacity opacity
|
:opacity opacity
|
||||||
:background-color (or background-color colors/gray)
|
:background-color (or background-color colors/gray)
|
||||||
:height height}
|
:height height
|
||||||
|
:width window-width})
|
||||||
platform/desktop?
|
|
||||||
(assoc :left 0
|
|
||||||
:right 0)
|
|
||||||
|
|
||||||
(not platform/desktop?)
|
|
||||||
(assoc :width window-width)))
|
|
||||||
|
|
||||||
(def text
|
(def text
|
||||||
{:color :white
|
{:color :white
|
||||||
:font-size 14
|
:font-size 14
|
||||||
:top 8})
|
:top 8})
|
|
@ -43,7 +43,7 @@
|
||||||
:background-color color})
|
:background-color color})
|
||||||
|
|
||||||
(views/defview loading-indicator [parent-width]
|
(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)]
|
white-bar-left-margin (animation/create-value 0)]
|
||||||
{:component-did-mount
|
{:component-did-mount
|
||||||
(fn [_]
|
(fn [_]
|
||||||
|
@ -62,11 +62,11 @@
|
||||||
(animation/parallel
|
(animation/parallel
|
||||||
[(animation/timing blue-bar-left-margin (easing :out 0))
|
[(animation/timing blue-bar-left-margin (easing :out 0))
|
||||||
(animation/timing white-bar-left-margin (easing :out 0))])]))))}
|
(animation/timing white-bar-left-margin (easing :out 0))])]))))}
|
||||||
[react/view {:style {:width parent-width
|
[react/view {:style {:width parent-width
|
||||||
:position :absolute
|
:position :absolute
|
||||||
:top -3
|
:top -3
|
||||||
:z-index 3
|
:z-index 3
|
||||||
:height 3
|
:height 3
|
||||||
:background-color colors/white}}
|
:background-color colors/white}}
|
||||||
[react/animated-view {:style (animated-bar-style blue-bar-left-margin
|
[react/animated-view {:style (animated-bar-style blue-bar-left-margin
|
||||||
parent-width
|
parent-width
|
||||||
|
@ -87,10 +87,10 @@ all connectivity views (we have at least one view in home and one in chat)"
|
||||||
(animation/start
|
(animation/start
|
||||||
(animation/parallel
|
(animation/parallel
|
||||||
[(animation/timing anim-opacity
|
[(animation/timing anim-opacity
|
||||||
{:toValue 0
|
{:toValue 0
|
||||||
:delay 800
|
:delay 800
|
||||||
:duration 150
|
:duration 150
|
||||||
:easing (.-ease (animation/easing))
|
:easing (.-ease (animation/easing))
|
||||||
:useNativeDriver true})
|
:useNativeDriver true})
|
||||||
(animation/timing anim-y
|
(animation/timing anim-y
|
||||||
{:toValue (if platform/desktop? 0 neg-connectivity-bar-height)
|
{: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/start
|
||||||
(animation/parallel
|
(animation/parallel
|
||||||
[(animation/timing anim-opacity
|
[(animation/timing anim-opacity
|
||||||
{:toValue 1
|
{:toValue 1
|
||||||
:duration 150
|
:duration 150
|
||||||
:easing (.-ease (animation/easing))
|
:easing (.-ease (animation/easing))
|
||||||
:useNativeDriver true})
|
:useNativeDriver true})
|
||||||
(animation/timing anim-y
|
(animation/timing anim-y
|
||||||
{:toValue (if platform/desktop? connectivity-bar-height 0)
|
{:toValue (if platform/desktop? connectivity-bar-height 0)
|
||||||
:duration 150
|
:duration 150
|
||||||
:easing (.-ease (animation/easing))
|
:easing (.-ease (animation/easing))
|
||||||
:useNativeDriver true})])
|
:useNativeDriver true})])
|
||||||
;; second param of start() - a callback that fires when animation stops
|
;; second param of start() - a callback that fires when animation stops
|
||||||
#(do (reset! to-hide? true) (reset! status-hidden false))))
|
#(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)))))
|
(reset! status-hidden false)))))
|
||||||
|
|
||||||
(defn connectivity-status
|
(defn connectivity-status
|
||||||
[{:keys [connected?]} anim-translate-y status-hidden]
|
[{:keys [connected?]} status-hidden]
|
||||||
(let [anim-translate-y (or anim-translate-y (animation/create-value 0))
|
(let [anim-translate-y (animation/create-value neg-connectivity-bar-height)
|
||||||
anim-opacity (animation/create-value 0)]
|
anim-opacity (animation/create-value 0)]
|
||||||
(reagent/create-class
|
(reagent/create-class
|
||||||
{:component-did-mount
|
{: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
|
(manage-visibility (:connected? (reagent/props comp)) true
|
||||||
anim-opacity anim-translate-y status-hidden))
|
anim-opacity anim-translate-y status-hidden))
|
||||||
:reagent-render
|
:reagent-render
|
||||||
(fn [{:keys [view-id message on-press-event connected? connecting?] :as opts}]
|
(fn [{:keys [message on-press-event connected? connecting?] :as opts}]
|
||||||
[react/animated-view {:style (styles/text-wrapper
|
(when-not @status-hidden
|
||||||
(assoc opts
|
[react/animated-view {:style (styles/text-wrapper
|
||||||
:height (if platform/desktop?
|
(assoc opts
|
||||||
anim-translate-y
|
:height connectivity-bar-height
|
||||||
connectivity-bar-height)
|
:background-color (if connected?
|
||||||
:background-color (if connected?
|
colors/green
|
||||||
colors/green
|
colors/gray)
|
||||||
colors/gray)
|
:transform anim-translate-y
|
||||||
;;TODO how does this affect desktop?
|
:opacity anim-opacity))
|
||||||
:transform anim-translate-y
|
:accessibility-label :connection-status-text}
|
||||||
:opacity anim-opacity
|
(when connecting?
|
||||||
:modal? (= view-id :chat-modal)))
|
[react/activity-indicator {:color colors/white :margin-right 6}])
|
||||||
:accessibility-label :connection-status-text}
|
(if (= message :mobile-network)
|
||||||
(when connecting?
|
[react/nested-text {:style styles/text
|
||||||
[react/activity-indicator {:color colors/white :margin-right 6}])
|
:on-press (when on-press-event #(re-frame/dispatch [on-press-event]))}
|
||||||
(if (= message :mobile-network)
|
(i18n/label :t/waiting-for-wifi) " "
|
||||||
[react/nested-text {:style styles/text
|
[{:style {:text-decoration-line :underline}}
|
||||||
:on-press (when on-press-event #(re-frame/dispatch [on-press-event]))}
|
(i18n/label :t/waiting-for-wifi-change)]]
|
||||||
(i18n/label :t/waiting-for-wifi) " "
|
(when message
|
||||||
[{:style {:text-decoration-line :underline}}
|
[react/text {:style styles/text
|
||||||
(i18n/label :t/waiting-for-wifi-change)]]
|
:on-press (when on-press-event #(re-frame/dispatch [on-press-event]))}
|
||||||
(when message
|
(i18n/label 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
|
;; 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
|
;; 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
|
(defn propagate-status
|
||||||
"Smoothly propagate from :connectivity/status-properties subscription to
|
"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 queued with new state and start a timer if not yet started
|
||||||
(reset! enqueued-connectivity-status-properties status-properties)
|
(reset! enqueued-connectivity-status-properties status-properties)
|
||||||
(when-not @timer
|
(when-not @timer
|
||||||
(reset! timer (utils/set-timeout #(do
|
(reset!
|
||||||
(reset! timer nil)
|
timer
|
||||||
(when @enqueued-connectivity-status-properties
|
(utils/set-timeout
|
||||||
(re-frame/dispatch [:set :connectivity/ui-status-properties @enqueued-connectivity-status-properties])
|
#(do
|
||||||
(reset! enqueued-connectivity-status-properties nil)))
|
(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:
|
;; timeout choice:
|
||||||
;; if the app is in foreground or logged-in for less than <timeframe>,
|
;; if the app is in foreground or logged-in for less than <timeframe>,
|
||||||
;; postpone state changes for <long> otherwise <short>
|
;; postpone state changes for <long> otherwise <short>
|
||||||
(let [ts (max
|
(let [ts (max
|
||||||
(or logged-in-since 0)
|
(or logged-in-since 0)
|
||||||
(or app-active-since 0))
|
(or app-active-since 0))
|
||||||
ts-diff (- (datetime/timestamp) ts)
|
ts-diff (- (datetime/timestamp) ts)
|
||||||
timeout (if (< ts-diff timewindow-for-long-delay)
|
timeout (if (< ts-diff timewindow-for-long-delay)
|
||||||
long-delay
|
long-delay
|
||||||
standard-delay)]
|
standard-delay)]
|
||||||
(log/debug "propagate-status set-timeout: " logged-in-since app-active-since ts-diff timeout)
|
(log/debug "propagate-status set-timeout: " logged-in-since app-active-since ts-diff timeout)
|
||||||
timeout))))))
|
timeout))))))
|
||||||
|
|
||||||
(defn status-propagator-dummy-view
|
(defn status-propagator-dummy-view
|
||||||
"this empty view is needed to react propagate status-properties to ui-status-properties"
|
"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
|
:reagent-render
|
||||||
#()}))
|
#()}))
|
||||||
|
|
||||||
(defview connectivity-view [anim-translate-y]
|
(defview connectivity [header footer]
|
||||||
(letsubs [status-properties [:connectivity/status-properties]
|
(letsubs [status-properties [:connectivity/status-properties]
|
||||||
app-active-since [:app-active-since]
|
app-active-since [:app-active-since]
|
||||||
logged-in-since [:logged-in-since]
|
logged-in-since [:logged-in-since]
|
||||||
ui-status-properties [:connectivity/ui-status-properties]
|
ui-status-properties [:connectivity/ui-status-properties]
|
||||||
status-hidden (reagent/atom true)
|
status-hidden (reagent/atom true)
|
||||||
view-id [:view-id]
|
window-width (reagent/atom 0)]
|
||||||
window-width (reagent/atom 0)]
|
|
||||||
(let [loading-indicator? (:loading-indicator? ui-status-properties)]
|
(let [loading-indicator? (:loading-indicator? ui-status-properties)]
|
||||||
[react/view {:style {:align-items :stretch
|
[react/view {:style {:flex 1}
|
||||||
:z-index 1}
|
|
||||||
:on-layout #(reset! window-width (-> % .-nativeEvent .-layout .-width))}
|
:on-layout #(reset! window-width (-> % .-nativeEvent .-layout .-width))}
|
||||||
(when (and loading-indicator? @status-hidden)
|
[react/view {:style {:z-index 2 :background-color :white}}
|
||||||
[loading-indicator @window-width])
|
header
|
||||||
;; This view below exists only to hide the connectivity-status bar when "connected".
|
[react/view
|
||||||
;; Ideally connectivity-status bar would be hidden under "toolbar/toolbar",
|
(when (and loading-indicator? @status-hidden)
|
||||||
;; but that has to be transparent(enven though it sits above the bar)
|
[loading-indicator @window-width])]]
|
||||||
;; 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}]
|
|
||||||
[connectivity-status
|
[connectivity-status
|
||||||
;on startup default connected
|
|
||||||
(merge (or ui-status-properties
|
(merge (or ui-status-properties
|
||||||
{:connected? true :message :t/connected})
|
{:connected? true :message :t/connected})
|
||||||
{:view-id view-id
|
{:window-width @window-width})
|
||||||
:window-width @window-width})
|
|
||||||
anim-translate-y
|
|
||||||
status-hidden]
|
status-hidden]
|
||||||
[status-propagator-dummy-view {:status-properties status-properties
|
;;TODO this is something weird, rework
|
||||||
:app-active-since app-active-since
|
[status-propagator-dummy-view {:status-properties status-properties
|
||||||
:logged-in-since logged-in-since
|
:app-active-since app-active-since
|
||||||
:ui-status-properties ui-status-properties}]])))
|
:logged-in-since logged-in-since
|
||||||
|
:ui-status-properties ui-status-properties}]
|
||||||
;; "push?" determines whether "content" gets pushed down when disconnected
|
footer])))
|
||||||
;; 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)))
|
|
|
@ -199,28 +199,6 @@
|
||||||
(when header {:ListHeaderComponent (reagent/as-element header)})
|
(when header {:ListHeaderComponent (reagent/as-element header)})
|
||||||
(when footer {:ListFooterComponent (reagent/as-element footer)}))))
|
(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
|
(defn flat-list
|
||||||
"A wrapper for FlatList.
|
"A wrapper for FlatList.
|
||||||
See https://facebook.github.io/react-native/docs/flatlist.html"
|
See https://facebook.github.io/react-native/docs/flatlist.html"
|
||||||
|
@ -232,7 +210,7 @@
|
||||||
[class
|
[class
|
||||||
(merge (base-list-props props)
|
(merge (base-list-props props)
|
||||||
props
|
props
|
||||||
{:data (wrap-data data)})])))
|
{:data (to-array data)})])))
|
||||||
|
|
||||||
(defn flat-list-generic-render-fn
|
(defn flat-list-generic-render-fn
|
||||||
"A generic status-react specific `render-fn` for `list-item`.
|
"A generic status-react specific `render-fn` for `list-item`.
|
||||||
|
@ -262,7 +240,7 @@
|
||||||
(if-let [f (:render-fn props)]
|
(if-let [f (:render-fn props)]
|
||||||
(assoc (dissoc props :render-fn) :renderItem (wrap-render-fn f))
|
(assoc (dissoc props :render-fn) :renderItem (wrap-render-fn f))
|
||||||
props)
|
props)
|
||||||
:data wrap-data))
|
:data to-array))
|
||||||
;;TODO DEPRECATED, use status-im.ui.components.list-item.views
|
;;TODO DEPRECATED, use status-im.ui.components.list-item.views
|
||||||
(defn section-list
|
(defn section-list
|
||||||
"A wrapper for SectionList.
|
"A wrapper for SectionList.
|
||||||
|
|
|
@ -1,52 +1,44 @@
|
||||||
(ns status-im.ui.components.search-input.view
|
(ns status-im.ui.components.search-input.view
|
||||||
(:require-macros [status-im.utils.views :as views])
|
(:require [status-im.i18n :as i18n]
|
||||||
(:require [reagent.core :as reagent]
|
|
||||||
[status-im.i18n :as i18n]
|
|
||||||
[status-im.ui.components.react :as react]
|
[status-im.ui.components.react :as react]
|
||||||
[status-im.ui.components.colors :as colors]
|
[status-im.ui.components.colors :as colors]
|
||||||
[status-im.ui.components.search-input.styles :as styles]
|
[status-im.ui.components.search-input.styles :as styles]
|
||||||
[status-im.ui.components.icons.vector-icons :as icons]))
|
[status-im.ui.components.icons.vector-icons :as icons]))
|
||||||
|
|
||||||
(views/defview search-input [{:keys [on-cancel
|
(defn search-input [_]
|
||||||
on-focus
|
(let [input-ref (atom nil)]
|
||||||
on-change
|
(fn [{:keys [on-cancel on-focus on-change search-active?
|
||||||
search-active?
|
search-container-style search-filter auto-focus]}]
|
||||||
search-container-style
|
[react/view {:style (or search-container-style styles/search-container)}
|
||||||
search-filter
|
[react/view {:style styles/search-input-container}
|
||||||
auto-focus]}]
|
[icons/icon :main-icons/search {:color colors/gray
|
||||||
(views/letsubs
|
:container-style {:margin-left 6
|
||||||
[input-ref (reagent/atom nil)]
|
:margin-right 2}}]
|
||||||
[react/view {:style (or search-container-style styles/search-container)}
|
[react/text-input {:placeholder (i18n/label :t/search)
|
||||||
[react/view {:style styles/search-input-container}
|
:blur-on-submit true
|
||||||
[icons/icon :main-icons/search {:color colors/gray
|
:multiline false
|
||||||
:container-style {:margin-left 6
|
:ref #(reset! input-ref %)
|
||||||
:margin-right 2}}]
|
:style styles/search-input
|
||||||
[react/text-input {:placeholder (i18n/label :t/search)
|
:default-value search-filter
|
||||||
:blur-on-submit true
|
:auto-focus auto-focus
|
||||||
:multiline false
|
:auto-correct false
|
||||||
:ref #(reset! input-ref %)
|
:auto-capitalize false
|
||||||
:style styles/search-input
|
:on-focus #(do
|
||||||
:default-value search-filter
|
(when on-focus
|
||||||
:auto-focus auto-focus
|
(on-focus search-filter))
|
||||||
:auto-correct false
|
(reset! search-active? true))
|
||||||
:auto-capitalize false
|
:on-change (fn [e]
|
||||||
:on-focus #(do
|
(let [native-event (.-nativeEvent e)
|
||||||
(when on-focus
|
text (.-text native-event)]
|
||||||
(on-focus search-filter))
|
(when on-change
|
||||||
(reset! search-active? true))
|
(on-change text))))}]]
|
||||||
:on-change (fn [e]
|
(when @search-active?
|
||||||
(let [native-event (.-nativeEvent e)
|
[react/touchable-highlight
|
||||||
text (.-text native-event)]
|
{:on-press (fn []
|
||||||
(when on-change
|
(.clear @input-ref)
|
||||||
(on-change text))))}]]
|
(.blur @input-ref)
|
||||||
(when @search-active?
|
(when on-cancel (on-cancel))
|
||||||
[react/touchable-highlight
|
(reset! search-active? false))
|
||||||
{:on-press (fn []
|
:style {:margin-left 16}}
|
||||||
(.clear @input-ref)
|
[react/text {:style {:color colors/blue}}
|
||||||
(.blur @input-ref)
|
(i18n/label :t/cancel)]])])))
|
||||||
(when on-cancel
|
|
||||||
(on-cancel))
|
|
||||||
(reset! search-active? false))
|
|
||||||
:style {:margin-left 16}}
|
|
||||||
[react/text {:style {:color colors/blue}}
|
|
||||||
(i18n/label :t/cancel)]])]))
|
|
|
@ -35,7 +35,7 @@
|
||||||
|
|
||||||
(defn topbar [_]
|
(defn topbar [_]
|
||||||
(let [title-padding (reagent/atom 16)]
|
(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?))]
|
(let [navigation (or navigation (default-navigation modal?))]
|
||||||
[react/view (cond-> {:height 56 :align-items :center :flex-direction :row}
|
[react/view (cond-> {:height 56 :align-items :center :flex-direction :row}
|
||||||
show-border?
|
show-border?
|
||||||
|
@ -49,8 +49,12 @@
|
||||||
(for [value accessories]
|
(for [value accessories]
|
||||||
^{:key value}
|
^{:key value}
|
||||||
[button value false])])
|
[button value false])])
|
||||||
|
(when content
|
||||||
|
[react/view {:position :absolute :left @title-padding :right @title-padding
|
||||||
|
:top 0 :bottom 0}
|
||||||
|
content])
|
||||||
(when title
|
(when title
|
||||||
[react/view {:position :absolute :left @title-padding :right @title-padding
|
[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}
|
[react/text {:style {:typography :title-bold :text-align :center} :number-of-lines 2}
|
||||||
(utils.label/stringify title)]])]))))
|
(utils.label/stringify title)]])]))))
|
|
@ -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)])
|
|
|
@ -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})]))))))
|
|
@ -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))]])))
|
|
|
@ -9,15 +9,11 @@
|
||||||
[status-im.ui.screens.chat.photos :as photos]
|
[status-im.ui.screens.chat.photos :as photos]
|
||||||
[status-im.ui.screens.chat.utils :as chat-utils]
|
[status-im.ui.screens.chat.utils :as chat-utils]
|
||||||
[status-im.i18n :as i18n]
|
[status-im.i18n :as i18n]
|
||||||
[status-im.ui.components.animation :as animation]
|
|
||||||
[status-im.ui.components.colors :as colors]
|
[status-im.ui.components.colors :as colors]
|
||||||
[status-im.ui.components.react :as react]
|
[status-im.ui.components.react :as react]
|
||||||
[status-im.ui.components.icons.vector-icons :as vector-icons]
|
[status-im.ui.components.icons.vector-icons :as vector-icons]
|
||||||
[status-im.utils.platform :as platform]
|
[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]
|
[status-im.utils.config :as config]
|
||||||
[taoensso.timbre :as log]
|
|
||||||
[status-im.ui.screens.chat.stickers.views :as stickers]
|
[status-im.ui.screens.chat.stickers.views :as stickers]
|
||||||
[status-im.ui.screens.chat.extensions.views :as extensions]))
|
[status-im.ui.screens.chat.extensions.views :as extensions]))
|
||||||
|
|
||||||
|
@ -32,10 +28,7 @@
|
||||||
:default-value (or input-text "")
|
:default-value (or input-text "")
|
||||||
:editable (not cooldown-enabled?)
|
:editable (not cooldown-enabled?)
|
||||||
:blur-on-submit false
|
:blur-on-submit false
|
||||||
:on-focus #(re-frame/dispatch [:chat.ui/set-chat-ui-props {:input-focused? true
|
:on-focus #(re-frame/dispatch [:chat.ui/set-chat-ui-props {:input-bottom-sheet nil}])
|
||||||
:input-bottom-sheet nil
|
|
||||||
:messages-focused? false}])
|
|
||||||
:on-blur #(re-frame/dispatch [:chat.ui/set-chat-ui-props {:input-focused? false}])
|
|
||||||
:on-submit-editing #(when single-line-input?
|
:on-submit-editing #(when single-line-input?
|
||||||
(re-frame/dispatch [:chat.ui/send-current-message]))
|
(re-frame/dispatch [:chat.ui/send-current-message]))
|
||||||
:on-layout #(set-container-width-fn (.-width (.-layout (.-nativeEvent %))))
|
:on-layout #(set-container-width-fn (.-width (.-layout (.-nativeEvent %))))
|
||||||
|
@ -63,10 +56,7 @@
|
||||||
:default-value @state-text
|
:default-value @state-text
|
||||||
:editable (not cooldown-enabled?)
|
:editable (not cooldown-enabled?)
|
||||||
:blur-on-submit false
|
:blur-on-submit false
|
||||||
:on-focus #(re-frame/dispatch [:chat.ui/set-chat-ui-props {:input-focused? true
|
:on-focus #(re-frame/dispatch [:chat.ui/set-chat-ui-props {:input-bottom-sheet nil}])
|
||||||
:input-bottom-sheet nil
|
|
||||||
:messages-focused? false}])
|
|
||||||
:on-blur #(re-frame/dispatch [:chat.ui/set-chat-ui-props {:input-focused? false}])
|
|
||||||
:submit-shortcut {:key "Enter"}
|
:submit-shortcut {:key "Enter"}
|
||||||
:on-submit-editing #(do
|
:on-submit-editing #(do
|
||||||
(.clear @inp-ref)
|
(.clear @inp-ref)
|
||||||
|
@ -162,12 +152,7 @@
|
||||||
input-text-empty? (if platform/desktop?
|
input-text-empty? (if platform/desktop?
|
||||||
(string/blank? state-text)
|
(string/blank? state-text)
|
||||||
(string/blank? input-text))]
|
(string/blank? input-text))]
|
||||||
[react/view {:style (style/root margin)
|
[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}])))}
|
|
||||||
[reply-message-view]
|
[reply-message-view]
|
||||||
[react/view {:style style/input-container}
|
[react/view {:style style/input-container}
|
||||||
[input-view {:single-line-input? single-line-input? :set-text set-text :state-text state-text}]
|
[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])
|
(re-frame/dispatch [:chat.ui/send-current-message])
|
||||||
(set-text ""))]
|
(set-text ""))]
|
||||||
[send-button/send-button-view {:input-text input-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])]))]])))
|
|
@ -1,7 +1,6 @@
|
||||||
(ns status-im.ui.screens.chat.input.send-button
|
(ns status-im.ui.screens.chat.input.send-button
|
||||||
(:require-macros [status-im.utils.views :refer [defview letsubs]])
|
(:require-macros [status-im.utils.views :refer [defview letsubs]])
|
||||||
(:require [clojure.string :as string]
|
(: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.screens.chat.styles.input.send-button :as style]
|
||||||
[status-im.ui.components.react :as react]
|
[status-im.ui.components.react :as react]
|
||||||
[status-im.ui.components.icons.vector-icons :as vector-icons]))
|
[status-im.ui.components.icons.vector-icons :as vector-icons]))
|
||||||
|
@ -21,4 +20,4 @@
|
||||||
[vector-icons/icon :main-icons/arrow-up
|
[vector-icons/icon :main-icons/arrow-up
|
||||||
{:container-style style/send-message-container
|
{:container-style style/send-message-container
|
||||||
:accessibility-label :send-message-button
|
:accessibility-label :send-message-button
|
||||||
:color :white}]])))
|
:color :white}]])))
|
|
@ -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])]))]]]))
|
|
@ -5,17 +5,10 @@
|
||||||
[status-im.ui.screens.chat.styles.message.datemark :as style]))
|
[status-im.ui.screens.chat.styles.message.datemark :as style]))
|
||||||
|
|
||||||
(defn chat-datemark [value]
|
(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
|
[react/touchable-without-feedback
|
||||||
{:on-press (fn [_]
|
{:on-press (fn [_]
|
||||||
(re-frame/dispatch
|
(re-frame/dispatch
|
||||||
[:chat.ui/set-chat-ui-props {:messages-focused? true
|
[:chat.ui/set-chat-ui-props {:input-bottom-sheet nil}])
|
||||||
:input-bottom-sheet nil}])
|
|
||||||
(react/dismiss-keyboard!))}
|
(react/dismiss-keyboard!))}
|
||||||
[react/view style/datemark-mobile
|
[react/view style/datemark-mobile
|
||||||
[react/text {:style style/datemark-text}
|
[react/text {:style style/datemark-text}
|
||||||
|
|
|
@ -1,25 +1,20 @@
|
||||||
(ns status-im.ui.screens.chat.message.message
|
(ns status-im.ui.screens.chat.message.message
|
||||||
(:require [re-frame.core :as re-frame]
|
(:require [re-frame.core :as re-frame]
|
||||||
[status-im.commands.core :as commands]
|
|
||||||
[status-im.constants :as constants]
|
[status-im.constants :as constants]
|
||||||
[status-im.utils.http :as http]
|
[status-im.utils.http :as http]
|
||||||
[status-im.i18n :as i18n]
|
[status-im.i18n :as i18n]
|
||||||
[status-im.ethereum.eip55 :as eip55]
|
|
||||||
[reagent.core :as reagent]
|
|
||||||
[status-im.ui.components.colors :as colors]
|
[status-im.ui.components.colors :as colors]
|
||||||
[status-im.utils.security :as security]
|
[status-im.utils.security :as security]
|
||||||
[status-im.ui.components.icons.vector-icons :as vector-icons]
|
[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.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.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.sheets :as sheets]
|
||||||
[status-im.ui.screens.chat.photos :as photos]
|
[status-im.ui.screens.chat.photos :as photos]
|
||||||
[status-im.ui.screens.chat.styles.message.message :as style]
|
[status-im.ui.screens.chat.styles.message.message :as style]
|
||||||
[status-im.ui.screens.chat.utils :as chat.utils]
|
[status-im.ui.screens.chat.utils :as chat.utils]
|
||||||
[status-im.utils.contenthash :as contenthash]
|
[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]]))
|
(:require-macros [status-im.utils.views :refer [defview letsubs]]))
|
||||||
|
|
||||||
(defview mention-element [from]
|
(defview mention-element [from]
|
||||||
|
@ -34,7 +29,7 @@
|
||||||
(:rtl? content)
|
(:rtl? content)
|
||||||
(= content-type constants/content-type-emoji))} t])
|
(= content-type constants/content-type-emoji))} t])
|
||||||
|
|
||||||
(defn message-view
|
(defn message-bubble-wrapper
|
||||||
[{:keys [timestamp-str outgoing content content-type] :as message}
|
[{:keys [timestamp-str outgoing content content-type] :as message}
|
||||||
message-content {:keys [justify-timestamp?]}]
|
message-content {:keys [justify-timestamp?]}]
|
||||||
[react/view (style/message-view message)
|
[react/view (style/message-view message)
|
||||||
|
@ -58,11 +53,6 @@
|
||||||
:number-of-lines 5}
|
:number-of-lines 5}
|
||||||
(or text (:text quote))]])))
|
(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]}]
|
(defn render-inline [message-text outgoing acc {:keys [type literal destination]}]
|
||||||
(case type
|
(case type
|
||||||
""
|
""
|
||||||
|
@ -120,10 +110,7 @@
|
||||||
[react/text-class {:style style/status-text}]
|
[react/text-class {:style style/status-text}]
|
||||||
(-> content :parsed-text peek :children))]])
|
(-> content :parsed-text peek :children))]])
|
||||||
|
|
||||||
(defn render-block [{:keys [chat-id message-id content
|
(defn render-block [{:keys [content outgoing]} acc
|
||||||
timestamp-str group-chat outgoing
|
|
||||||
current-public-key expanded?] :as message}
|
|
||||||
acc
|
|
||||||
{:keys [type literal children]}]
|
{:keys [type literal children]}]
|
||||||
(case type
|
(case type
|
||||||
|
|
||||||
|
@ -145,14 +132,12 @@
|
||||||
|
|
||||||
acc))
|
acc))
|
||||||
|
|
||||||
(defn render-parsed-text [{:keys [timestamp-str
|
(defn render-parsed-text [{:keys [timestamp-str outgoing] :as message} tree]
|
||||||
outgoing] :as message}
|
|
||||||
|
|
||||||
tree]
|
|
||||||
(let [elements (reduce (fn [acc e] (render-block message acc e)) [react/view {}] tree)
|
(let [elements (reduce (fn [acc e] (render-block message acc e)) [react/view {}] tree)
|
||||||
timestamp [react/text {:style (style/message-timestamp-placeholder outgoing)}
|
timestamp [react/text {:style (style/message-timestamp-placeholder outgoing)}
|
||||||
(str " " timestamp-str)]
|
(str " " timestamp-str)]
|
||||||
last-element (peek elements)]
|
last-element (peek elements)]
|
||||||
|
;; TODO (perf)
|
||||||
;; Using `nth` here as slightly faster than `first`, roughly 30%
|
;; Using `nth` here as slightly faster than `first`, roughly 30%
|
||||||
;; It's worth considering pure js structures for this code path as
|
;; It's worth considering pure js structures for this code path as
|
||||||
;; it's perfomance critical
|
;; it's perfomance critical
|
||||||
|
@ -163,9 +148,8 @@
|
||||||
(conj elements timestamp))))
|
(conj elements timestamp))))
|
||||||
|
|
||||||
(defn text-message
|
(defn text-message
|
||||||
[{:keys [chat-id message-id content
|
[{:keys [content outgoing current-public-key] :as message}]
|
||||||
timestamp-str group-chat outgoing current-public-key expanded?] :as message}]
|
[message-bubble-wrapper message
|
||||||
[message-view message
|
|
||||||
(let [response-to (:response-to content)]
|
(let [response-to (:response-to content)]
|
||||||
[react/view
|
[react/view
|
||||||
(when (seq response-to)
|
(when (seq response-to)
|
||||||
|
@ -176,275 +160,13 @@
|
||||||
(defn emoji-message
|
(defn emoji-message
|
||||||
[{:keys [content current-public-key alias outgoing] :as message}]
|
[{:keys [content current-public-key alias outgoing] :as message}]
|
||||||
(let [response-to (:response-to content)]
|
(let [response-to (:response-to content)]
|
||||||
[message-view message
|
[message-bubble-wrapper message
|
||||||
[react/view {:style (style/style-message-text outgoing)}
|
[react/view {:style (style/style-message-text outgoing)}
|
||||||
(when response-to
|
(when response-to
|
||||||
[quoted-message response-to (:quoted-message message) alias outgoing current-public-key])
|
[quoted-message response-to (:quoted-message message) alias outgoing current-public-key])
|
||||||
[react/text {:style (style/emoji-message message)}
|
[react/text {:style (style/emoji-message message)}
|
||||||
(:text content)]]]))
|
(: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
|
(defn message-activity-indicator
|
||||||
[]
|
[]
|
||||||
[react/view style/message-activity-indicator
|
[react/view style/message-activity-indicator
|
||||||
|
@ -473,84 +195,81 @@
|
||||||
|
|
||||||
(defn message-delivery-status
|
(defn message-delivery-status
|
||||||
[{:keys [chat-id message-id outgoing-status
|
[{:keys [chat-id message-id outgoing-status
|
||||||
first-outgoing?
|
first-outgoing? message-type]}]
|
||||||
content message-type] :as message}]
|
|
||||||
(when (not= constants/message-type-private-group-system-message message-type)
|
(when (not= constants/message-type-private-group-system-message message-type)
|
||||||
(case outgoing-status
|
(case outgoing-status
|
||||||
:sending [message-activity-indicator]
|
:sending [message-activity-indicator]
|
||||||
:not-sent [message-not-sent-text chat-id message-id]
|
:not-sent [message-not-sent-text chat-id message-id]
|
||||||
:sent (when first-outgoing?
|
:sent (when first-outgoing?
|
||||||
[react/view style/delivery-view
|
[react/text {:style style/delivery-text}
|
||||||
[react/text {:style style/delivery-text}
|
(i18n/label :t/status-sent)])
|
||||||
(i18n/label :t/status-sent)]])
|
|
||||||
nil)))
|
nil)))
|
||||||
|
|
||||||
(defview message-author-name [from alias]
|
(defview message-author-name [from alias]
|
||||||
(letsubs [{:keys [ens-name]} [:contacts/contact-name-by-identity from]]
|
(letsubs [{:keys [ens-name]} [:contacts/contact-name-by-identity from]]
|
||||||
(chat.utils/format-author alias style/message-author-name-container ens-name)))
|
(chat.utils/format-author alias style/message-author-name-container ens-name)))
|
||||||
|
|
||||||
(defn message-body
|
(defn message-press-handlers [{:keys [outgoing from content-type content] :as message}]
|
||||||
[{:keys [alias
|
(let [pack (get-in content [:sticker :pack])]
|
||||||
last-in-group?
|
{:on-press (fn [_]
|
||||||
first-in-group?
|
(when (and (= content-type constants/content-type-sticker) pack)
|
||||||
display-photo?
|
(re-frame/dispatch [:stickers/open-sticker-pack pack]))
|
||||||
identicon
|
(re-frame/dispatch [:chat.ui/set-chat-ui-props {:input-bottom-sheet nil}])
|
||||||
display-username?
|
(react/dismiss-keyboard!))
|
||||||
from
|
:on-long-press #(cond (or (= content-type constants/content-type-text)
|
||||||
outgoing
|
(= content-type constants/content-type-emoji))
|
||||||
modal?
|
(re-frame/dispatch [:bottom-sheet/show-sheet
|
||||||
content]
|
{:content (sheets/message-long-press message)
|
||||||
:as message} child]
|
:height 192}])
|
||||||
[react/view (style/group-message-wrapper message)
|
(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)
|
[react/view (style/message-body message)
|
||||||
(when display-photo?
|
(when display-photo?
|
||||||
[react/view (style/message-author outgoing)
|
; userpic
|
||||||
|
[react/view (style/message-author-userpic outgoing)
|
||||||
(when first-in-group?
|
(when first-in-group?
|
||||||
[react/touchable-highlight {:on-press #(when-not modal? (re-frame/dispatch [:chat.ui/show-profile from]))}
|
[react/touchable-highlight {:on-press #(re-frame/dispatch [:chat.ui/show-profile from])}
|
||||||
[react/view
|
[photos/member-identicon identicon]])])
|
||||||
[photos/member-photo from identicon]]])])
|
; username
|
||||||
[react/view (style/group-message-view outgoing display-photo?)
|
[react/view (style/message-author-wrapper outgoing display-photo?)
|
||||||
(when display-username?
|
(when display-username?
|
||||||
[react/touchable-opacity {:style style/message-author-touchable
|
[react/touchable-opacity {:style style/message-author-touchable
|
||||||
:on-press #(re-frame/dispatch [:chat.ui/show-profile from])}
|
:on-press #(re-frame/dispatch [:chat.ui/show-profile from])}
|
||||||
|
;;TODO (perf) move to event
|
||||||
[message-author-name from alias]])
|
[message-author-name from alias]])
|
||||||
[react/view {:style (style/timestamp-content-wrapper outgoing)}
|
;;MESSAGE CONTENT
|
||||||
child]]]
|
content]]
|
||||||
|
; delivery status
|
||||||
[react/view (style/delivery-status outgoing)
|
[react/view (style/delivery-status outgoing)
|
||||||
[message-delivery-status message]]])
|
[message-delivery-status message]]])
|
||||||
|
|
||||||
(defn chat-message
|
(defn chat-message [{:keys [content content-type] :as message}]
|
||||||
[{:keys [outgoing from group-chat modal? current-public-key content-type content] :as message}]
|
(if (= content-type constants/content-type-command)
|
||||||
(let [sticker (:sticker content)]
|
[message.command/comand-content message-content-wrapper message]
|
||||||
[react/view
|
[react/touchable-highlight (message-press-handlers message)
|
||||||
[react/touchable-highlight
|
[message-content-wrapper
|
||||||
{:on-press (fn [arg]
|
message
|
||||||
(if (and platform/desktop? (= "right" (.-button (.-nativeEvent arg))))
|
(if (= content-type constants/content-type-text)
|
||||||
(re-frame/dispatch [:bottom-sheet/show-sheet
|
; text message
|
||||||
{:content (sheets/message-long-press message)
|
[text-message message]
|
||||||
:height 192}])
|
(if (= content-type constants/content-type-status)
|
||||||
(do
|
[message-content-status message]
|
||||||
(when (and (= content-type constants/content-type-sticker) (:pack sticker))
|
(if (= content-type constants/content-type-emoji)
|
||||||
(re-frame/dispatch [:stickers/open-sticker-pack (:pack sticker)]))
|
[emoji-message message]
|
||||||
(re-frame/dispatch [:chat.ui/set-chat-ui-props {:messages-focused? true
|
(if (= content-type constants/content-type-sticker)
|
||||||
:input-bottom-sheet nil}])
|
[react/image {:style {:margin 10 :width 140 :height 140}
|
||||||
(when-not platform/desktop?
|
;;TODO (perf) move to event
|
||||||
(react/dismiss-keyboard!)))))
|
:source {:uri (contenthash/url (-> content :sticker :hash))}}]
|
||||||
:on-long-press #(cond (or (= content-type constants/content-type-text)
|
[message-bubble-wrapper message
|
||||||
(= content-type constants/content-type-emoji))
|
[react/text (str "Unhandled content-type " content-type)]]))))]]))
|
||||||
(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})])]]]))
|
|
|
@ -1,15 +1,11 @@
|
||||||
(ns status-im.ui.screens.chat.photos
|
(ns status-im.ui.screens.chat.photos
|
||||||
(:require [clojure.string :as string]
|
(:require [status-im.ui.components.react :as react]
|
||||||
[status-im.multiaccounts.core :as multiaccounts]
|
|
||||||
[status-im.ui.components.react :as react]
|
|
||||||
[status-im.ui.screens.chat.styles.photos :as style]
|
[status-im.ui.screens.chat.styles.photos :as style]
|
||||||
[status-im.ui.screens.profile.db :as profile.db]
|
[status-im.ui.screens.profile.db :as profile.db]
|
||||||
[status-im.utils.identicon :as identicon]
|
|
||||||
[status-im.utils.image :as utils.image])
|
[status-im.utils.image :as utils.image])
|
||||||
(:require-macros [status-im.utils.views :refer [defview letsubs]]))
|
(:require-macros [status-im.utils.views :refer [defview letsubs]]))
|
||||||
|
|
||||||
(defn photo [photo-path {:keys [size
|
(defn photo [photo-path {:keys [size accessibility-label]}]
|
||||||
accessibility-label]}]
|
|
||||||
(let [identicon? (when photo-path (profile.db/base64-png? photo-path))]
|
(let [identicon? (when photo-path (profile.db/base64-png? photo-path))]
|
||||||
[react/view {:style (style/photo-container size)}
|
[react/view {:style (style/photo-container size)}
|
||||||
[react/image {:source (utils.image/source photo-path)
|
[react/image {:source (utils.image/source photo-path)
|
||||||
|
@ -24,3 +20,12 @@
|
||||||
(photo (or photo-path identicon)
|
(photo (or photo-path identicon)
|
||||||
{:accessibility-label :member-photo
|
{:accessibility-label :member-photo
|
||||||
:size (or size style/default-size)})))
|
: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)}]]))
|
|
@ -1,40 +1,11 @@
|
||||||
(ns status-im.ui.screens.chat.styles.main
|
(ns status-im.ui.screens.chat.styles.main
|
||||||
(:require [status-im.ui.components.colors :as colors]))
|
(:require [status-im.ui.components.colors :as colors]))
|
||||||
|
|
||||||
(def chat-view
|
|
||||||
{:flex 1})
|
|
||||||
|
|
||||||
(def toolbar-container
|
(def toolbar-container
|
||||||
{:flex 1
|
{:flex 1
|
||||||
:align-items :center
|
:align-items :center
|
||||||
:flex-direction :row})
|
: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
|
(def chat-name-view
|
||||||
{:flex 1
|
{:flex 1
|
||||||
:justify-content :center})
|
:justify-content :center})
|
||||||
|
@ -44,12 +15,6 @@
|
||||||
:font-size 15
|
:font-size 15
|
||||||
:line-height 22})
|
:line-height 22})
|
||||||
|
|
||||||
(def group-icon
|
|
||||||
{:margin-top 4
|
|
||||||
:margin-bottom 2.7
|
|
||||||
:width 14
|
|
||||||
:height 9})
|
|
||||||
|
|
||||||
(def toolbar-subtitle
|
(def toolbar-subtitle
|
||||||
{:typography :caption
|
{:typography :caption
|
||||||
:line-height 16
|
:line-height 16
|
||||||
|
@ -60,57 +25,6 @@
|
||||||
:margin-top 4
|
:margin-top 4
|
||||||
:color colors/text-gray})
|
: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
|
(def overlay-highlight
|
||||||
{:flex 1})
|
{:flex 1})
|
||||||
|
|
||||||
|
@ -134,31 +48,6 @@
|
||||||
:right 16
|
:right 16
|
||||||
:height height})
|
: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
|
(def add-contact
|
||||||
{:flex-direction :row
|
{:flex-direction :row
|
||||||
:align-items :center
|
:align-items :center
|
||||||
|
@ -171,14 +60,6 @@
|
||||||
{:margin-left 4
|
{:margin-left 4
|
||||||
:color colors/blue})
|
: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
|
(def empty-chat-container
|
||||||
{:flex 1
|
{:flex 1
|
||||||
:justify-content :center
|
:justify-content :center
|
||||||
|
@ -187,18 +68,17 @@
|
||||||
:margin-right 6})
|
:margin-right 6})
|
||||||
|
|
||||||
(defn intro-header-container
|
(defn intro-header-container
|
||||||
[height status no-messages]
|
[status no-messages]
|
||||||
(let [adjusted-height (if (< height 280) 324 height)]
|
(if (or no-messages (= status (or :loading :empty)))
|
||||||
(if (or no-messages (= status (or :loading :empty)))
|
{:flex 1
|
||||||
{:flex 1
|
:flex-direction :column
|
||||||
:flex-direction :column
|
:justify-content :center
|
||||||
:justify-content :center
|
:align-items :center
|
||||||
:align-items :center
|
:height 324}
|
||||||
:height adjusted-height}
|
{:flex 1
|
||||||
{:flex 1
|
:flex-direction :column
|
||||||
:flex-direction :column
|
:justify-content :center
|
||||||
:justify-content :center
|
:align-items :center}))
|
||||||
:align-items :center})))
|
|
||||||
|
|
||||||
(defn intro-header-icon [diameter color]
|
(defn intro-header-icon [diameter color]
|
||||||
{:width diameter
|
{:width diameter
|
||||||
|
@ -238,20 +118,12 @@
|
||||||
:margin-right 4
|
:margin-right 4
|
||||||
:text-align :center})
|
:text-align :center})
|
||||||
|
|
||||||
(def empty-chat-text-name
|
|
||||||
{:margin-bottom 5})
|
|
||||||
|
|
||||||
(def intro-header-description
|
(def intro-header-description
|
||||||
{:color colors/gray
|
{:color colors/gray
|
||||||
:line-height 22
|
:line-height 22
|
||||||
:text-align :center
|
:text-align :center
|
||||||
:margin-horizontal 32})
|
:margin-horizontal 32})
|
||||||
|
|
||||||
(def group-chat-icon
|
|
||||||
{:color colors/white
|
|
||||||
:font-size 40
|
|
||||||
:font-weight "700"})
|
|
||||||
|
|
||||||
(def group-chat-join-footer
|
(def group-chat-join-footer
|
||||||
{:flex 1
|
{:flex 1
|
||||||
:justify-content :center})
|
:justify-content :center})
|
||||||
|
@ -261,21 +133,10 @@
|
||||||
:align-items :center
|
:align-items :center
|
||||||
:justify-content :center})
|
:justify-content :center})
|
||||||
|
|
||||||
(def group-chat-join-name
|
|
||||||
{:typography :header})
|
|
||||||
|
|
||||||
(def join-button
|
|
||||||
{:margin-bottom 15})
|
|
||||||
|
|
||||||
(def decline-chat
|
(def decline-chat
|
||||||
{:color colors/blue
|
{:color colors/blue
|
||||||
:margin-bottom 40})
|
:margin-bottom 40})
|
||||||
|
|
||||||
(def select-chat
|
|
||||||
{:color colors/gray})
|
|
||||||
|
|
||||||
(def messages-list-vertical-padding 46)
|
|
||||||
|
|
||||||
(def are-you-friends-bubble
|
(def are-you-friends-bubble
|
||||||
{:border-radius 8
|
{:border-radius 8
|
||||||
:border-width 1
|
:border-width 1
|
||||||
|
@ -303,4 +164,4 @@
|
||||||
(def tribute-received-note
|
(def tribute-received-note
|
||||||
{:font-size 13
|
{:font-size 13
|
||||||
:line-height 18
|
:line-height 18
|
||||||
:text-align :center})
|
:text-align :center})
|
|
@ -47,29 +47,20 @@
|
||||||
:bottom 9 ; 6 Bubble bottom, 3 message baseline
|
:bottom 9 ; 6 Bubble bottom, 3 message baseline
|
||||||
(if rtl? :left :right) 12})))
|
(if rtl? :left :right) 12})))
|
||||||
|
|
||||||
(def message-expand-button
|
|
||||||
{:color colors/gray
|
|
||||||
:font-size 12
|
|
||||||
:opacity 0.7
|
|
||||||
:margin-bottom 20})
|
|
||||||
|
|
||||||
(def selected-message
|
(def selected-message
|
||||||
{:margin-top 18
|
{:margin-top 18
|
||||||
:margin-left 40
|
:margin-left 40
|
||||||
:font-size 12
|
:font-size 12
|
||||||
:color colors/text-gray})
|
:color colors/text-gray})
|
||||||
|
|
||||||
(defn group-message-wrapper [{:keys [outgoing] :as message}]
|
(defn message-wrapper [{:keys [outgoing] :as message}]
|
||||||
(merge {:flex-direction :column}
|
(merge {:flex-direction :column}
|
||||||
(if outgoing
|
(if outgoing
|
||||||
{:margin-left 96}
|
{:margin-left 96}
|
||||||
{:margin-right 52})
|
{:margin-right 52})
|
||||||
(last-message-padding message)))
|
(last-message-padding message)))
|
||||||
|
|
||||||
(defn timestamp-content-wrapper [outgoing]
|
(defn message-author-wrapper
|
||||||
{:flex-direction (if outgoing :row-reverse :row)})
|
|
||||||
|
|
||||||
(defn group-message-view
|
|
||||||
[outgoing display-photo?]
|
[outgoing display-photo?]
|
||||||
(let [align (if outgoing :flex-end :flex-start)]
|
(let [align (if outgoing :flex-end :flex-start)]
|
||||||
(merge {:flex-direction :column
|
(merge {:flex-direction :column
|
||||||
|
@ -91,7 +82,7 @@
|
||||||
{:margin-left 12
|
{:margin-left 12
|
||||||
:padding-vertical 2})
|
:padding-vertical 2})
|
||||||
|
|
||||||
(defn message-author [outgoing]
|
(defn message-author-userpic [outgoing]
|
||||||
(merge
|
(merge
|
||||||
{:width (+ 16 photos/default-size) ;; 16 is for the padding
|
{:width (+ 16 photos/default-size) ;; 16 is for the padding
|
||||||
:align-self :flex-end}
|
:align-self :flex-end}
|
||||||
|
@ -100,18 +91,15 @@
|
||||||
{:padding-horizontal 8
|
{:padding-horizontal 8
|
||||||
:padding-right 8})))
|
:padding-right 8})))
|
||||||
|
|
||||||
(def delivery-view
|
|
||||||
{:flex-direction :row
|
|
||||||
:margin-top 2})
|
|
||||||
|
|
||||||
(def delivery-text
|
(def delivery-text
|
||||||
{:color colors/gray
|
{:color colors/gray
|
||||||
|
:margin-top 2
|
||||||
:font-size 12})
|
:font-size 12})
|
||||||
|
|
||||||
(def not-sent-view
|
(def not-sent-view
|
||||||
(assoc delivery-view
|
{:flex-direction :row
|
||||||
:margin-bottom 2
|
:margin-bottom 2
|
||||||
:padding-top 2))
|
:padding-top 2})
|
||||||
|
|
||||||
(def not-sent-text
|
(def not-sent-text
|
||||||
(assoc delivery-text
|
(assoc delivery-text
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
(ns status-im.ui.screens.chat.toolbar-content
|
(ns status-im.ui.screens.chat.toolbar-content
|
||||||
(:require [cljs-time.core :as t]
|
(:require [status-im.i18n :as i18n]
|
||||||
[status-im.i18n :as i18n]
|
|
||||||
[status-im.ui.components.chat-icon.screen :as chat-icon.screen]
|
[status-im.ui.components.chat-icon.screen :as chat-icon.screen]
|
||||||
[status-im.ui.components.react :as react]
|
[status-im.ui.components.react :as react]
|
||||||
[status-im.ui.screens.chat.styles.main :as st]
|
[status-im.ui.screens.chat.styles.main :as st])
|
||||||
[status-im.utils.datetime :as time])
|
|
||||||
(:require-macros [status-im.utils.views :refer [defview letsubs]]))
|
(:require-macros [status-im.utils.views :refer [defview letsubs]]))
|
||||||
|
|
||||||
(defn- in-progress-text [{:keys [highestBlock currentBlock startBlock]}]
|
(defn- in-progress-text [{:keys [highestBlock currentBlock startBlock]}]
|
||||||
|
@ -47,9 +45,9 @@
|
||||||
(i18n/label :chat-is-not-a-contact))]])
|
(i18n/label :chat-is-not-a-contact))]])
|
||||||
|
|
||||||
(defview toolbar-content-view []
|
(defview toolbar-content-view []
|
||||||
(letsubs [{:keys [group-chat color online contacts chat-name contact
|
(letsubs [{:keys [group-chat color online contacts chat-name contact public?]}
|
||||||
public? chat-id] :as chat} [:chats/current-chat]
|
[:chats/current-chat]
|
||||||
sync-state [:sync-state]]
|
sync-state [:sync-state]]
|
||||||
(let [has-subtitle? (or group-chat (not= :done sync-state))]
|
(let [has-subtitle? (or group-chat (not= :done sync-state))]
|
||||||
[react/view {:style st/toolbar-container}
|
[react/view {:style st/toolbar-container}
|
||||||
[react/view {:margin-right 10}
|
[react/view {:margin-right 10}
|
||||||
|
|
|
@ -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]))
|
|
@ -1,15 +1,8 @@
|
||||||
(ns status-im.ui.screens.chat.utils
|
(ns status-im.ui.screens.chat.utils
|
||||||
(:require [re-frame.core :as re-frame]
|
(:require [status-im.ethereum.stateofus :as stateofus]
|
||||||
[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]
|
|
||||||
[status-im.i18n :as i18n]
|
[status-im.i18n :as i18n]
|
||||||
[status-im.utils.core :as core-utils]
|
|
||||||
[status-im.ui.components.react :as react]
|
[status-im.ui.components.react :as react]
|
||||||
[status-im.ui.components.colors :as colors]
|
[status-im.ui.components.colors :as colors]))
|
||||||
[status-im.utils.http :as http]))
|
|
||||||
|
|
||||||
(defn format-author [alias style name]
|
(defn format-author [alias style name]
|
||||||
(let [additional-styles (style false)]
|
(let [additional-styles (style false)]
|
||||||
|
@ -37,60 +30,4 @@
|
||||||
(or (and (= from current-public-key)
|
(or (and (= from current-public-key)
|
||||||
[react/text {:style (style true)}
|
[react/text {:style (style true)}
|
||||||
(str reply-symbol (i18n/label :t/You))])
|
(str reply-symbol (i18n/label :t/You))])
|
||||||
(format-author (subs reply-name 0 80) style false))))
|
(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))
|
|
|
@ -1,37 +1,45 @@
|
||||||
(ns status-im.ui.screens.chat.views
|
(ns status-im.ui.screens.chat.views
|
||||||
(:require [re-frame.core :as re-frame]
|
(:require [re-frame.core :as re-frame]
|
||||||
[reagent.core :as reagent]
|
|
||||||
[status-im.contact.db :as contact.db]
|
[status-im.contact.db :as contact.db]
|
||||||
[status-im.i18n :as i18n]
|
[status-im.i18n :as i18n]
|
||||||
[status-im.multiaccounts.core :as multiaccounts]
|
[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.chat-icon.screen :as chat-icon.screen]
|
||||||
[status-im.ui.components.colors :as colors]
|
[status-im.ui.components.colors :as colors]
|
||||||
[status-im.ui.components.connectivity.view :as connectivity]
|
[status-im.ui.components.connectivity.view :as connectivity]
|
||||||
[status-im.ui.components.icons.vector-icons :as vector-icons]
|
[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.list.views :as list]
|
||||||
[status-im.ui.components.react :as react]
|
[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.sheets :as sheets]
|
||||||
[status-im.ui.screens.chat.input.input :as input]
|
[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.message.message :as message]
|
||||||
[status-im.ui.screens.chat.stickers.views :as stickers]
|
[status-im.ui.screens.chat.stickers.views :as stickers]
|
||||||
[status-im.ui.screens.chat.styles.main :as style]
|
[status-im.ui.screens.chat.styles.main :as style]
|
||||||
[status-im.ui.screens.chat.toolbar-content :as toolbar-content]
|
[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.ui.screens.chat.state :as state]
|
||||||
[status-im.utils.debounce :as debounce]
|
[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]]))
|
(: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
|
(defn add-contact-bar
|
||||||
[public-key]
|
[public-key]
|
||||||
[react/touchable-highlight
|
[react/touchable-highlight
|
||||||
|
@ -43,250 +51,30 @@
|
||||||
{:color colors/blue}]
|
{:color colors/blue}]
|
||||||
[react/i18n-text {:style style/add-contact-text :key :add-to-contacts}]]])
|
[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
|
(defn intro-header
|
||||||
[contact]
|
[contact]
|
||||||
[react/text {:style (assoc style/intro-header-description
|
[react/text {:style (assoc style/intro-header-description
|
||||||
:margin-bottom 32)}
|
:margin-bottom 32)}
|
||||||
(str (i18n/label :t/empty-chat-description-one-to-one) (multiaccounts/displayed-name contact))])
|
(str
|
||||||
|
(i18n/label :t/empty-chat-description-one-to-one)
|
||||||
(defn join-chat-button [chat-id]
|
(multiaccounts/displayed-name contact))])
|
||||||
[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]))
|
|
||||||
|
|
||||||
(defn chat-intro-header-container
|
(defn chat-intro-header-container
|
||||||
[{:keys [group-chat name pending-invite-inviter-name
|
[{:keys [group-chat name pending-invite-inviter-name color chat-id chat-name
|
||||||
inviter-name color chat-id chat-name public?
|
public? contact intro-status] :as chat}
|
||||||
contact universal-link intro-status height input-height] :as chat}
|
|
||||||
no-messages]
|
no-messages]
|
||||||
(let [icon-text (if public? chat-id name)
|
(let [icon-text (if public? chat-id name)
|
||||||
intro-name (if public? chat-name (multiaccounts/displayed-name contact))]
|
intro-name (if public? chat-name (multiaccounts/displayed-name contact))]
|
||||||
;; TODO This when check ought to be unnecessary but for now it prevents
|
(when (or pending-invite-inviter-name
|
||||||
;; 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
|
|
||||||
(not= (get-in contact [:tribute-to-talk :snt-amount]) 0))
|
(not= (get-in contact [:tribute-to-talk :snt-amount]) 0))
|
||||||
[react/touchable-without-feedback
|
[react/touchable-without-feedback
|
||||||
{:style {:flex 1
|
{:style {:flex 1
|
||||||
:align-items :flex-start}
|
:align-items :flex-start}
|
||||||
:on-press (fn [_]
|
:on-press (fn [_]
|
||||||
(re-frame/dispatch
|
(re-frame/dispatch
|
||||||
[:chat.ui/set-chat-ui-props {:messages-focused? true
|
[:chat.ui/set-chat-ui-props {:input-bottom-sheet nil}])
|
||||||
:input-bottom-sheet nil}])
|
|
||||||
(react/dismiss-keyboard!))}
|
(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
|
;; Icon section
|
||||||
[react/view {:style {:margin-top 42
|
[react/view {:style {:margin-top 42
|
||||||
:margin-bottom 24}}
|
:margin-bottom 24}}
|
||||||
|
@ -296,11 +84,12 @@
|
||||||
:default-chat-icon-text style/intro-header-icon-text
|
:default-chat-icon-text style/intro-header-icon-text
|
||||||
:size 120}]]
|
:size 120}]]
|
||||||
;; Chat title section
|
;; 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
|
;; Description section
|
||||||
(if group-chat
|
(if group-chat
|
||||||
[group-chat-description-container chat]
|
[chat.group/group-chat-description-container chat]
|
||||||
[one-to-one-chat-description-container chat])]])))
|
[intro-header contact])]])))
|
||||||
|
|
||||||
(defonce messages-list-ref (atom nil))
|
(defonce messages-list-ref (atom nil))
|
||||||
|
|
||||||
|
@ -308,11 +97,11 @@
|
||||||
(when @messages-list-ref
|
(when @messages-list-ref
|
||||||
(reset! state/viewable-item
|
(reset! state/viewable-item
|
||||||
(when-let [last-visible-element (aget (.-viewableItems e) (dec (.-length (.-viewableItems e))))]
|
(when-let [last-visible-element (aget (.-viewableItems e) (dec (.-length (.-viewableItems e))))]
|
||||||
(let [index (.-index last-visible-element)
|
(let [index (.-index last-visible-element)
|
||||||
;; Get first not visible element, if it's a datemark/gap
|
;; Get first not visible element, if it's a datemark/gap
|
||||||
;; we might unnecessarely add messages on receiving as
|
;; we might unnecessarely add messages on receiving as
|
||||||
;; they do not have a clock value, but most of the times
|
;; they do not have a clock value, but most of the times
|
||||||
;; it will be a message
|
;; it will be a message
|
||||||
first-not-visible (aget (.-data (.-props @messages-list-ref)) (inc index))]
|
first-not-visible (aget (.-data (.-props @messages-list-ref)) (inc index))]
|
||||||
(when (and first-not-visible
|
(when (and first-not-visible
|
||||||
(= :message (:type first-not-visible)))
|
(= :message (:type first-not-visible)))
|
||||||
|
@ -320,149 +109,54 @@
|
||||||
(debounce/debounce-and-dispatch [:chat.ui/message-visibility-changed e] 5000))
|
(debounce/debounce-and-dispatch [:chat.ui/message-visibility-changed e] 5000))
|
||||||
|
|
||||||
(defview messages-view
|
(defview messages-view
|
||||||
[{:keys [group-chat chat-id pending-invite-inviter-name contact] :as chat}
|
[{:keys [group-chat chat-id pending-invite-inviter-name] :as chat}]
|
||||||
modal?]
|
|
||||||
(letsubs [messages [:chats/current-chat-messages-stream]
|
(letsubs [messages [:chats/current-chat-messages-stream]
|
||||||
current-public-key [:multiaccount/public-key]]
|
current-public-key [:multiaccount/public-key]]
|
||||||
{:component-did-update
|
[list/flat-list
|
||||||
(fn [args]
|
{:key-fn #(or (:message-id %) (:value %))
|
||||||
(re-frame/dispatch [:chat.ui/set-chat-ui-props
|
:ref #(reset! messages-list-ref %)
|
||||||
{:messages-focused? true
|
:header (when pending-invite-inviter-name
|
||||||
:input-focused? false}]))}
|
[chat.group/group-chat-footer chat-id])
|
||||||
(let [no-messages (empty? messages)
|
:footer [chat-intro-header-container chat (empty? messages)]
|
||||||
flat-list-conf
|
:data messages
|
||||||
{:data messages
|
:inverted true
|
||||||
:ref #(reset! messages-list-ref %)
|
:render-fn (fn [{:keys [outgoing] :as message} idx]
|
||||||
:footer [chat-intro-header-container chat no-messages]
|
(let [type (:type message)]
|
||||||
:key-fn #(or (:message-id %) (:value %))
|
(if (= type :datemark)
|
||||||
:render-fn (fn [message idx]
|
[message-datemark/chat-datemark (:value message)]
|
||||||
[message-row
|
(if (= type :gap)
|
||||||
{:group-chat group-chat
|
[gap/gap message idx messages-list-ref]
|
||||||
:modal? modal?
|
; message content
|
||||||
:current-public-key current-public-key
|
[message/chat-message
|
||||||
:row message
|
(assoc message
|
||||||
:idx idx
|
:incoming-group (and group-chat (not outgoing))
|
||||||
:list-ref messages-list-ref}])
|
:group-chat group-chat
|
||||||
:inverted true
|
:current-public-key current-public-key)]))))
|
||||||
:onViewableItemsChanged on-viewable-items-changed
|
:on-viewable-items-changed on-viewable-items-changed
|
||||||
:onEndReached #(re-frame/dispatch [:chat.ui/load-more-messages])
|
:on-end-reached #(re-frame/dispatch [:chat.ui/load-more-messages])
|
||||||
:onScrollToIndexFailed #()
|
:on-scroll-to-index-failed #() ;;don't remove this
|
||||||
:keyboardShouldPersistTaps :handled}
|
:keyboard-should-persist-taps :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]))))
|
|
||||||
|
|
||||||
(def load-step 5)
|
(defview bottom-sheet []
|
||||||
|
(letsubs [input-bottom-sheet [:chats/current-chat-ui-prop :input-bottom-sheet]]
|
||||||
(defn load-more [all-messages-count messages-to-load]
|
(case input-bottom-sheet
|
||||||
(let [next-count (min all-messages-count (+ @messages-to-load load-step))]
|
:stickers
|
||||||
(reset! messages-to-load next-count)))
|
[stickers/stickers-view]
|
||||||
|
:extensions
|
||||||
(defview messages-view-desktop [{:keys [chat-id group-chat pending-invite-inviter-name]}
|
[extensions/extensions-view]
|
||||||
modal?]
|
nil)))
|
||||||
(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 chat []
|
(defview chat []
|
||||||
[chat-root false])
|
(letsubs [{:keys [chat-id show-input? group-chat contact] :as current-chat}
|
||||||
|
[:chats/current-chat]]
|
||||||
(defview chat-modal []
|
[react/view {:style {:flex 1}}
|
||||||
[chat-root true])
|
[connectivity/connectivity
|
||||||
|
[topbar current-chat]
|
||||||
(defview select-chat []
|
[react/view {:style {:flex 1}}
|
||||||
[react/view {:align-items :center :justify-content :center :flex 1}
|
;;TODO contact.db/added? looks weird here, move to events
|
||||||
[react/text style/select-chat
|
(when (and (not group-chat) (not (contact.db/added? contact)))
|
||||||
(i18n/label :t/select-chat)]])
|
[add-contact-bar chat-id])
|
||||||
|
[messages-view current-chat]]]
|
||||||
|
(when show-input?
|
||||||
|
[input/container])
|
||||||
|
[bottom-sheet]]))
|
|
@ -1,12 +1,8 @@
|
||||||
(ns status-im.ui.screens.home.sheet.views
|
(ns status-im.ui.screens.home.sheet.views
|
||||||
(:require-macros [status-im.utils.views :refer [defview letsubs]])
|
|
||||||
(:require [re-frame.core :as re-frame]
|
(:require [re-frame.core :as re-frame]
|
||||||
[status-im.i18n :as i18n]
|
[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.list-selection :as list-selection]
|
||||||
[status-im.ui.components.react :as react]
|
[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.ui.components.list-item.views :as list-item]
|
||||||
[status-im.utils.config :as config]))
|
[status-im.utils.config :as config]))
|
||||||
|
|
||||||
|
@ -45,4 +41,4 @@
|
||||||
(list-selection/open-share {:message (i18n/label :t/get-status-at)}))}]])
|
(list-selection/open-share {:message (i18n/label :t/get-status-at)}))}]])
|
||||||
|
|
||||||
(def add-new
|
(def add-new
|
||||||
{:content add-new-view})
|
{:content add-new-view})
|
|
@ -1,22 +1,12 @@
|
||||||
(ns status-im.ui.screens.home.styles
|
(ns status-im.ui.screens.home.styles
|
||||||
(:require [status-im.ui.components.colors :as colors]
|
(:require [status-im.ui.components.colors :as colors]
|
||||||
[status-im.utils.styles :as styles]
|
[status-im.utils.platform :as platform]
|
||||||
[status-im.ui.components.search-input.styles :as search-input.styles]
|
[status-im.ui.components.tabbar.styles :as tabs.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})
|
|
||||||
|
|
||||||
(def last-message-container
|
(def last-message-container
|
||||||
{:flex-shrink 1})
|
{:flex-shrink 1})
|
||||||
|
|
||||||
(styles/def last-message-text
|
(def last-message-text
|
||||||
{:flex 1
|
{:flex 1
|
||||||
:align-self :stretch
|
:align-self :stretch
|
||||||
:line-height 22
|
:line-height 22
|
||||||
|
@ -31,31 +21,6 @@
|
||||||
:width 12
|
:width 12
|
||||||
:height 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
|
(def datetime-text
|
||||||
{:color colors/text-gray
|
{:color colors/text-gray
|
||||||
:font-size 10
|
:font-size 10
|
||||||
|
@ -64,34 +29,13 @@
|
||||||
:align-items :center
|
:align-items :center
|
||||||
:line-height 12})
|
: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
|
(def chat-tooltip
|
||||||
{:align-items :center
|
{:align-items :center
|
||||||
:border-color colors/gray-lighter
|
:border-color colors/gray-lighter
|
||||||
:border-width 1
|
:border-width 1
|
||||||
:border-radius 16
|
:border-radius 16
|
||||||
:margin 16})
|
:margin 16
|
||||||
|
:margin-bottom 68})
|
||||||
|
|
||||||
(def no-chats-text
|
(def no-chats-text
|
||||||
{:margin-top 50
|
{:margin-top 50
|
||||||
|
@ -103,9 +47,6 @@
|
||||||
{:flex 1
|
{:flex 1
|
||||||
:justify-content :flex-end})
|
:justify-content :flex-end})
|
||||||
|
|
||||||
(def welcome-image-container
|
|
||||||
{:align-items :center})
|
|
||||||
|
|
||||||
(def welcome-text
|
(def welcome-text
|
||||||
{:typography :header
|
{:typography :header
|
||||||
:text-align :center})
|
:text-align :center})
|
||||||
|
@ -124,12 +65,12 @@
|
||||||
:margin-horizontal 40
|
:margin-horizontal 40
|
||||||
:color colors/gray})
|
:color colors/gray})
|
||||||
|
|
||||||
(defn action-button-container [home-width]
|
(def action-button-container
|
||||||
{:position :absolute
|
{:position :absolute
|
||||||
:z-index 2
|
:z-index 2
|
||||||
:align-items :center
|
:align-items :center
|
||||||
|
:align-self :center
|
||||||
:bottom 16
|
:bottom 16
|
||||||
:left (- (/ home-width 2) 20)
|
|
||||||
:width 40
|
:width 40
|
||||||
:height 40})
|
:height 40})
|
||||||
|
|
||||||
|
@ -169,16 +110,6 @@
|
||||||
{:margin-top 10
|
{:margin-top 10
|
||||||
:margin-bottom 18})
|
: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
|
(def close-icon-container
|
||||||
{:width 24
|
{:width 24
|
||||||
:height 24
|
:height 24
|
||||||
|
@ -186,3 +117,10 @@
|
||||||
:background-color colors/gray
|
:background-color colors/gray
|
||||||
:align-items :center
|
:align-items :center
|
||||||
:justify-content :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})))
|
|
@ -7,37 +7,31 @@
|
||||||
[status-im.ui.components.icons.vector-icons :as icons]
|
[status-im.ui.components.icons.vector-icons :as icons]
|
||||||
[status-im.ui.components.list.views :as list]
|
[status-im.ui.components.list.views :as list]
|
||||||
[status-im.ui.components.react :as react]
|
[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.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.screens.home.views.inner-item :as inner-item]
|
||||||
[status-im.ui.components.common.common :as components.common]
|
[status-im.ui.components.common.common :as components.common]
|
||||||
[status-im.ui.components.list-selection :as list-selection]
|
[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.components.colors :as colors]
|
||||||
[status-im.ui.screens.add-new.new-public-chat.view :as new-public-chat]
|
[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.button :as button]
|
||||||
[status-im.ui.components.search-input.view :as search-input]
|
[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]))
|
(:require-macros [status-im.utils.views :as views]))
|
||||||
|
|
||||||
(defonce search-active? (reagent/atom false))
|
|
||||||
|
|
||||||
(defn welcome-image-wrapper []
|
(defn welcome-image-wrapper []
|
||||||
(let [dimensions (reagent/atom {})]
|
(let [dimensions (reagent/atom {})]
|
||||||
(fn []
|
(fn []
|
||||||
[react/view {:on-layout (fn [e]
|
[react/view {:on-layout (fn [e]
|
||||||
(reset! dimensions (js->clj (-> e .-nativeEvent .-layout) :keywordize-keys true)))
|
(reset! dimensions (bean/->clj (-> e .-nativeEvent .-layout))))
|
||||||
:style {:align-items :center
|
:style {:align-items :center
|
||||||
:justify-content :center
|
:justify-content :center
|
||||||
:flex 1}}
|
:flex 1}}
|
||||||
(let [padding 0
|
(let [padding 0
|
||||||
image-size (- (min (:width @dimensions) (:height @dimensions)) padding)]
|
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
|
:resize-mode :contain
|
||||||
:style {:width image-size :height image-size}}])])))
|
:style {:width image-size :height image-size}}])])))
|
||||||
|
|
||||||
(defn welcome []
|
(defn welcome []
|
||||||
[react/view {:style styles/welcome-view}
|
[react/view {:style styles/welcome-view}
|
||||||
|
@ -47,9 +41,9 @@
|
||||||
[react/i18n-text {:style styles/welcome-text-description
|
[react/i18n-text {:style styles/welcome-text-description
|
||||||
:key :welcome-to-status-description}]]
|
:key :welcome-to-status-description}]]
|
||||||
[react/view {:align-items :center :margin-bottom 50}
|
[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
|
:accessibility-label :lets-go-button
|
||||||
:label (i18n/label :t/lets-go)}]]])
|
:label (i18n/label :t/lets-go)}]]])
|
||||||
|
|
||||||
(defn home-tooltip-view []
|
(defn home-tooltip-view []
|
||||||
[react/view styles/chat-tooltip
|
[react/view styles/chat-tooltip
|
||||||
|
@ -86,121 +80,56 @@
|
||||||
[react/view {:style {:flex 1 :flex-direction :row :align-items :center :justify-content :center}}
|
[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}]])
|
[react/i18n-text {:style styles/welcome-blank-text :key :welcome-blank-message}]])
|
||||||
|
|
||||||
(defn chat-list-footer [hide-home-tooltip?]
|
(defonce search-active? (reagent/atom false))
|
||||||
(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}]]))
|
|
||||||
|
|
||||||
(defn search-input-wrapper [search-filter]
|
(defn search-input-wrapper [search-filter]
|
||||||
[search-input/search-input
|
[search-input/search-input
|
||||||
{:search-active? search-active?
|
{:search-active? search-active?
|
||||||
:search-container-style styles/search-container
|
:search-filter search-filter
|
||||||
:search-filter search-filter
|
:on-cancel #(re-frame/dispatch [:search/home-filter-changed nil])
|
||||||
:on-cancel #(re-frame/dispatch [:search/home-filter-changed nil])
|
:on-focus (fn [search-filter]
|
||||||
:on-focus (fn [search-filter]
|
(when-not search-filter
|
||||||
(when-not search-filter
|
(re-frame/dispatch [:search/home-filter-changed ""])))
|
||||||
(re-frame/dispatch [:search/home-filter-changed ""])))
|
:on-change (fn [text]
|
||||||
:on-change (fn [text]
|
(re-frame/dispatch [:search/home-filter-changed text]))}])
|
||||||
(re-frame/dispatch [:search/home-filter-changed text]))}])
|
|
||||||
|
|
||||||
(defn section-footer [{:keys [title data]}]
|
(views/defview chats-list []
|
||||||
(when (and @search-active? (empty? data))
|
(views/letsubs [loading? [:chats/loading?]
|
||||||
[list/big-list-item
|
{:keys [chats all-home-items search-filter]} [:home-items]
|
||||||
{:text (i18n/label :t/no-result)
|
{:keys [hide-home-tooltip?]} [:multiaccount]]
|
||||||
:text-color colors/gray
|
(if loading?
|
||||||
:hide-chevron? true
|
[react/activity-indicator {:flex 1 :animating true}]
|
||||||
:action-fn #()
|
(if (and (empty? all-home-items) hide-home-tooltip? (not @search-active?))
|
||||||
:icon (case title
|
[welcome-blank-page]
|
||||||
"messages" :main-icons/one-on-one-chat
|
(let [data (if @search-active? chats all-home-items)]
|
||||||
"browser" :main-icons/browser
|
[list/flat-list
|
||||||
"chats" :main-icons/message)
|
{:key-fn first
|
||||||
:icon-color colors/gray}]))
|
: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/defview plus-button []
|
||||||
(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/letsubs [logging-in? [:multiaccounts/login]]
|
(views/letsubs [logging-in? [:multiaccounts/login]]
|
||||||
[react/view (styles/action-button-container home-width)
|
[react/view styles/action-button-container
|
||||||
[react/touchable-highlight {:accessibility-label :new-chat-button
|
[react/touchable-highlight
|
||||||
:on-press (when-not logging-in? #(re-frame/dispatch [:bottom-sheet/show-sheet :add-new {}]))}
|
{: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
|
[react/view styles/action-button
|
||||||
(if logging-in?
|
(if logging-in?
|
||||||
[react/activity-indicator {:color :white
|
[react/activity-indicator {:color :white
|
||||||
:animating true}]
|
:animating true}]
|
||||||
[icons/icon :main-icons/add {:color :white}])]]]))
|
[icons/icon :main-icons/add {:color :white}])]]]))
|
||||||
|
|
||||||
(views/defview home [loading?]
|
(defn home []
|
||||||
(views/letsubs
|
[react/keyboard-avoiding-view {:style styles/home-container}
|
||||||
[anim-translate-y (animation/create-value connectivity/neg-connectivity-bar-height)
|
[connectivity/connectivity
|
||||||
{:keys [all-home-items]} [:home-items]
|
[topbar/topbar {:title :t/chat :navigation :none
|
||||||
{:keys [hide-home-tooltip?]} [:multiaccount]
|
:show-border? true}]
|
||||||
window-width [:dimensions/window-width]
|
[chats-list]]
|
||||||
two-pane-ui-enabled? [:two-pane-ui-enabled?]]
|
[plus-button]])
|
||||||
(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?]))
|
|
|
@ -4,7 +4,6 @@
|
||||||
[status-im.constants :as constants]
|
[status-im.constants :as constants]
|
||||||
[status-im.i18n :as i18n]
|
[status-im.i18n :as i18n]
|
||||||
[status-im.ui.components.chat-icon.screen :as chat-icon.screen]
|
[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.screens.chat.sheets :as sheets]
|
||||||
[status-im.ui.components.list-item.views :as list-item]
|
[status-im.ui.components.list-item.views :as list-item]
|
||||||
[status-im.ui.components.badge :as badge]
|
[status-im.ui.components.badge :as badge]
|
||||||
|
@ -15,7 +14,7 @@
|
||||||
[status-im.utils.datetime :as time])
|
[status-im.utils.datetime :as time])
|
||||||
(:require-macros [status-im.utils.views :refer [defview letsubs]]))
|
(: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
|
[react/view styles/last-message-container
|
||||||
(cond
|
(cond
|
||||||
|
|
||||||
|
@ -26,6 +25,7 @@
|
||||||
|
|
||||||
(= constants/content-type-sticker content-type)
|
(= constants/content-type-sticker content-type)
|
||||||
[react/image {:style {:margin 1 :width 20 :height 20}
|
[react/image {:style {:margin 1 :width 20 :height 20}
|
||||||
|
;;TODO (perf) move to event
|
||||||
:source {:uri (contenthash/url (-> content :sticker :hash))}}]
|
:source {:uri (contenthash/url (-> content :sticker :hash))}}]
|
||||||
|
|
||||||
(string/blank? (:text content))
|
(string/blank? (:text content))
|
||||||
|
@ -37,39 +37,40 @@
|
||||||
:number-of-lines 1
|
:number-of-lines 1
|
||||||
:ellipsize-mode :tail
|
:ellipsize-mode :tail
|
||||||
:accessibility-label :chat-message-text}
|
: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]
|
(defn message-timestamp [timestamp]
|
||||||
(when timestamp
|
(when timestamp
|
||||||
[react/text {:style styles/datetime-text
|
[react/text {:style styles/datetime-text
|
||||||
:accessibility-label :last-message-time-text}
|
:accessibility-label :last-message-time-text}
|
||||||
|
;;TODO (perf) move to event
|
||||||
(string/upper-case (time/to-short-str timestamp))]))
|
(string/upper-case (time/to-short-str timestamp))]))
|
||||||
|
|
||||||
(defview unviewed-indicator [chat-id]
|
(defn unviewed-indicator [{:keys [unviewed-messages-count public?]}]
|
||||||
(letsubs [{:keys [unviewed-messages-count public?]} [:chats/chat chat-id]]
|
(when (pos? unviewed-messages-count)
|
||||||
(when (pos? unviewed-messages-count)
|
(if public?
|
||||||
(if public?
|
[react/view {:style styles/public-unread
|
||||||
[react/view {:style styles/public-unread
|
:accessibility-label :unviewed-messages-public}]
|
||||||
:accessibility-label :unviewed-messages-public}]
|
[badge/message-counter unviewed-messages-count])))
|
||||||
[badge/message-counter unviewed-messages-count]))))
|
|
||||||
|
|
||||||
(defn home-list-item [[_ home-item]]
|
(defn home-list-item [[_ home-item]]
|
||||||
(let [{:keys
|
(let [{:keys [chat-id chat-name color online group-chat
|
||||||
[chat-id chat-name
|
public? contact timestamp last-message]}
|
||||||
color online group-chat
|
home-item
|
||||||
public? contact
|
|
||||||
timestamp
|
|
||||||
last-message]} home-item
|
|
||||||
private-group? (and group-chat (not public?))
|
private-group? (and group-chat (not public?))
|
||||||
public-group? (and group-chat public?)
|
public-group? (and group-chat public?)
|
||||||
|
;;TODO (perf) move to event
|
||||||
truncated-chat-name (utils/truncate-str chat-name 30)]
|
truncated-chat-name (utils/truncate-str chat-name 30)]
|
||||||
[list-item/list-item
|
[list-item/list-item
|
||||||
{:icon [chat-icon.screen/chat-icon-view-chat-list
|
{:icon [chat-icon.screen/chat-icon-view-chat-list
|
||||||
contact group-chat truncated-chat-name color online false]
|
contact group-chat truncated-chat-name color online false]
|
||||||
:title-prefix (cond
|
:title-prefix (cond
|
||||||
private-group? :main-icons/tiny-group
|
private-group? :main-icons/tiny-group
|
||||||
public-group? :main-icons/tiny-public
|
public-group? :main-icons/tiny-public
|
||||||
:else nil)
|
:else nil)
|
||||||
:title truncated-chat-name
|
:title truncated-chat-name
|
||||||
:title-accessibility-label :chat-name-text
|
:title-accessibility-label :chat-name-text
|
||||||
:title-row-accessory [message-timestamp (if (pos? (:whisper-timestamp last-message))
|
:title-row-accessory [message-timestamp (if (pos? (:whisper-timestamp last-message))
|
||||||
|
@ -81,7 +82,7 @@
|
||||||
[message-content-text {:content (:content last-message)
|
[message-content-text {:content (:content last-message)
|
||||||
:content-type (:content-type last-message)}]
|
:content-type (:content-type last-message)}]
|
||||||
tribute-label))
|
tribute-label))
|
||||||
:subtitle-row-accessory [unviewed-indicator chat-id]
|
:subtitle-row-accessory [unviewed-indicator home-item]
|
||||||
:on-press #(do
|
:on-press #(do
|
||||||
(re-frame/dispatch [:dismiss-keyboard])
|
(re-frame/dispatch [:dismiss-keyboard])
|
||||||
(re-frame/dispatch [:chat.ui/navigate-to-chat chat-id])
|
(re-frame/dispatch [:chat.ui/navigate-to-chat chat-id])
|
||||||
|
@ -91,4 +92,4 @@
|
||||||
:on-long-press #(re-frame/dispatch [:bottom-sheet/show-sheet
|
:on-long-press #(re-frame/dispatch [:bottom-sheet/show-sheet
|
||||||
{:content (fn []
|
{:content (fn []
|
||||||
[sheets/actions home-item])
|
[sheets/actions home-item])
|
||||||
:height 256}])}]))
|
:height 256}])}]))
|
|
@ -4,7 +4,6 @@
|
||||||
{:name :chat-stack
|
{:name :chat-stack
|
||||||
:screens [:home
|
:screens [:home
|
||||||
:chat
|
:chat
|
||||||
:select-chat
|
|
||||||
:profile
|
:profile
|
||||||
:take-picture
|
:take-picture
|
||||||
:new-group
|
:new-group
|
||||||
|
@ -13,5 +12,4 @@
|
||||||
:group-chat-profile
|
:group-chat-profile
|
||||||
:stickers
|
:stickers
|
||||||
:stickers-pack]
|
:stickers-pack]
|
||||||
:config {:initialRouteName :home
|
:config {:initialRouteName :home}})
|
||||||
:emptyRightPaneName :select-chat}})
|
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
(ns status-im.ui.screens.routing.modals)
|
(ns status-im.ui.screens.routing.modals)
|
||||||
|
|
||||||
(def modal-screens
|
(def modal-screens
|
||||||
[:chat-modal
|
[:stickers-pack-modal
|
||||||
:stickers-pack-modal
|
|
||||||
:tribute-learn-more
|
:tribute-learn-more
|
||||||
:selection-modal-screen
|
:selection-modal-screen
|
||||||
:wallet-transactions-filter
|
:wallet-transactions-filter
|
||||||
|
|
|
@ -99,9 +99,8 @@
|
||||||
:keycard-unpaired keycard/unpaired
|
:keycard-unpaired keycard/unpaired
|
||||||
:keycard-login-pin keycard/login-pin
|
:keycard-login-pin keycard/login-pin
|
||||||
:not-keycard keycard/not-keycard
|
:not-keycard keycard/not-keycard
|
||||||
:home home/home-wrapper
|
:home home/home
|
||||||
:chat chat/chat
|
:chat chat/chat
|
||||||
:select-chat chat/select-chat
|
|
||||||
:profile profile.contact/profile
|
:profile profile.contact/profile
|
||||||
:new-chat [:modal new-chat/new-chat]
|
:new-chat [:modal new-chat/new-chat]
|
||||||
:qr-scanner [:modal qr-scanner/qr-scanner]
|
:qr-scanner [:modal qr-scanner/qr-scanner]
|
||||||
|
@ -116,7 +115,6 @@
|
||||||
:stickers-pack stickers/pack
|
:stickers-pack stickers/pack
|
||||||
:stickers-pack-modal [:modal stickers/pack-modal]
|
:stickers-pack-modal [:modal stickers/pack-modal]
|
||||||
:tribute-learn-more [:modal tr-to-talk/learn-more]
|
:tribute-learn-more [:modal tr-to-talk/learn-more]
|
||||||
:chat-modal [:modal chat/chat-modal]
|
|
||||||
:wallet wallet.accounts/accounts-overview
|
:wallet wallet.accounts/accounts-overview
|
||||||
:wallet-account wallet.account/account
|
:wallet-account wallet.account/account
|
||||||
:collectibles-list collectibles/collectibles-list
|
:collectibles-list collectibles/collectibles-list
|
||||||
|
|
|
@ -55,12 +55,14 @@
|
||||||
hex/decode
|
hex/decode
|
||||||
b58/encode))})))
|
b58/encode))})))
|
||||||
|
|
||||||
(defn url [hex]
|
(defn url-fn [hex]
|
||||||
(let [{:keys [namespace hash]} (decode (ethereum/normalized-hex hex))]
|
(let [{:keys [namespace hash]} (decode (ethereum/normalized-hex hex))]
|
||||||
(case namespace
|
(case namespace
|
||||||
:ipfs (str "https://ipfs.infura.io/ipfs/" hash)
|
:ipfs (str "https://ipfs.infura.io/ipfs/" hash)
|
||||||
"")))
|
"")))
|
||||||
|
|
||||||
|
(def url (memoize url-fn))
|
||||||
|
|
||||||
(fx/defn cat
|
(fx/defn cat
|
||||||
[cofx {:keys [contenthash on-success on-failure]}]
|
[cofx {:keys [contenthash on-success on-failure]}]
|
||||||
(let [{:keys [namespace hash]} (decode contenthash)]
|
(let [{:keys [namespace hash]} (decode contenthash)]
|
||||||
|
|
Loading…
Reference in New Issue