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