chat list optimization
This commit is contained in:
parent
db848a1f19
commit
073371c4ed
|
@ -45,14 +45,16 @@
|
||||||
;; i18n ignores nil value, leading to misleading messages
|
;; i18n ignores nil value, leading to misleading messages
|
||||||
(into {} (for [[k v] options] [k (or v default-option-value)])))
|
(into {} (for [[k v] options] [k (or v default-option-value)])))
|
||||||
|
|
||||||
(defn label
|
(defn label-fn
|
||||||
([path] (label path {}))
|
([path] (label-fn path {}))
|
||||||
([path options]
|
([path options]
|
||||||
(if (exists? (.t i18n))
|
(if (exists? (.t i18n))
|
||||||
(let [options (update options :amount label-number)]
|
(let [options (update options :amount label-number)]
|
||||||
(.t i18n (name path) (clj->js (label-options options))))
|
(.t i18n (name path) (clj->js (label-options options))))
|
||||||
(name path))))
|
(name path))))
|
||||||
|
|
||||||
|
(def label (memoize label-fn))
|
||||||
|
|
||||||
(defn label-pluralize [count path & options]
|
(defn label-pluralize [count path & options]
|
||||||
(if (exists? (.t i18n))
|
(if (exists? (.t i18n))
|
||||||
(.p i18n count (name path) (clj->js options))
|
(.p i18n count (name path) (clj->js options))
|
||||||
|
|
|
@ -10,19 +10,24 @@
|
||||||
|
|
||||||
;;TODO REWORK THIS NAMESPACE
|
;;TODO REWORK THIS NAMESPACE
|
||||||
|
|
||||||
|
(def get-name-first-char
|
||||||
|
(memoize
|
||||||
|
(fn [name]
|
||||||
|
;; TODO: for now we check if the first letter is a #
|
||||||
|
;; which means it is most likely a public chat and
|
||||||
|
;; use the second letter if that is the case
|
||||||
|
;; a broader refactoring should clean up upstream params
|
||||||
|
;; for default-chat-icon
|
||||||
|
(string/capitalize (if (and (= "#" (first name))
|
||||||
|
(< 1 (count name)))
|
||||||
|
(second name)
|
||||||
|
(first name))))))
|
||||||
|
|
||||||
(defn default-chat-icon [name styles]
|
(defn default-chat-icon [name styles]
|
||||||
(when-not (string/blank? name)
|
(when-not (string/blank? name)
|
||||||
[react/view (:default-chat-icon styles)
|
[react/view (:default-chat-icon styles)
|
||||||
[react/text {:style (:default-chat-icon-text styles)}
|
[react/text {:style (:default-chat-icon-text styles)}
|
||||||
;; TODO: for now we check if the first letter is a #
|
(get-name-first-char name)]]))
|
||||||
;; which means it is most likely a public chat and
|
|
||||||
;; use the second letter if that is the case
|
|
||||||
;; a broader refactoring should clean up upstream params
|
|
||||||
;; for default-chat-icon
|
|
||||||
(string/capitalize (if (and (= "#" (first name))
|
|
||||||
(< 1 (count name)))
|
|
||||||
(second name)
|
|
||||||
(first name)))]]))
|
|
||||||
|
|
||||||
(defn chat-icon-view
|
(defn chat-icon-view
|
||||||
[chat-id group-chat name styles]
|
[chat-id group-chat name styles]
|
||||||
|
|
|
@ -26,27 +26,25 @@
|
||||||
:else
|
:else
|
||||||
colors/black))
|
colors/black))
|
||||||
|
|
||||||
(defn icon
|
(defn memo-icon-fn
|
||||||
([name] (icon name nil))
|
([name] (memo-icon-fn name nil))
|
||||||
([name {:keys [color resize-mode container-style
|
([name {:keys [color resize-mode container-style
|
||||||
accessibility-label width height]
|
accessibility-label width height]
|
||||||
:or {accessibility-label :icon}}]
|
:or {accessibility-label :icon}}]
|
||||||
^{:key name}
|
^{:key name}
|
||||||
[react/view
|
[react/image {:style (merge (cond-> {:width (or width 24)
|
||||||
{:style (or
|
:height (or height 24)}
|
||||||
container-style
|
|
||||||
{:width (or width 24)
|
|
||||||
:height (or height 24)})
|
|
||||||
:accessibility-label accessibility-label}
|
|
||||||
[react/image {:style (cond-> {:width (or width 24)
|
|
||||||
:height (or height 24)}
|
|
||||||
|
|
||||||
resize-mode
|
resize-mode
|
||||||
(assoc :resize-mode resize-mode)
|
(assoc :resize-mode resize-mode)
|
||||||
|
|
||||||
:always
|
:always
|
||||||
(assoc :tint-color (match-color color)))
|
(assoc :tint-color (match-color color)))
|
||||||
:source (icon-source name)}]]))
|
container-style)
|
||||||
|
:accessibility-label accessibility-label
|
||||||
|
:source (icon-source name)}]))
|
||||||
|
|
||||||
|
(def icon (memoize memo-icon-fn))
|
||||||
|
|
||||||
(defn tiny-icon
|
(defn tiny-icon
|
||||||
([name] (tiny-icon name {}))
|
([name] (tiny-icon name {}))
|
||||||
|
|
|
@ -62,30 +62,41 @@
|
||||||
[react/view {:style (merge style styles/item-checkbox)}
|
[react/view {:style (merge style styles/item-checkbox)}
|
||||||
[radio/radio (:checked? props)]])])
|
[radio/radio (:checked? props)]])])
|
||||||
|
|
||||||
(defn- wrap-render-fn [f render-data]
|
(def memo-wrap-render-fn
|
||||||
(fn [^js data]
|
(memoize
|
||||||
(reagent/as-element [f (.-item data) (.-index data) (.-separators data) render-data])))
|
(fn [f render-data]
|
||||||
|
(fn [^js data]
|
||||||
(defn- wrap-key-fn [f]
|
(reagent/as-element [f (.-item data) (.-index data) (.-separators data) render-data])))))
|
||||||
(fn [data index]
|
|
||||||
{:post [(some? %)]}
|
|
||||||
(f data index)))
|
|
||||||
|
|
||||||
(def base-separator [react/view styles/base-separator])
|
(def base-separator [react/view styles/base-separator])
|
||||||
|
|
||||||
(def default-separator [react/view styles/separator])
|
(def default-separator [react/view styles/separator])
|
||||||
|
|
||||||
|
(def memo-separator-fn
|
||||||
|
(memoize
|
||||||
|
(fn [separator default-separator?]
|
||||||
|
(reagent/as-element (or separator (when (and platform/ios? default-separator?) default-separator))))))
|
||||||
|
|
||||||
|
(def memo-as-element
|
||||||
|
(memoize
|
||||||
|
(fn [element]
|
||||||
|
(reagent/as-element element))))
|
||||||
|
|
||||||
|
(def memo-wrap-key-fn
|
||||||
|
(memoize
|
||||||
|
(fn [f]
|
||||||
|
(fn [data index]
|
||||||
|
{:post [(some? %)]}
|
||||||
|
(f data index)))))
|
||||||
|
|
||||||
(defn- base-list-props
|
(defn- base-list-props
|
||||||
[{:keys [key-fn render-fn empty-component header footer separator default-separator? render-data]}]
|
[{:keys [key-fn render-fn empty-component header footer separator default-separator? render-data]}]
|
||||||
(let [separator (or separator (when (and platform/ios? default-separator?) default-separator))]
|
(merge (when key-fn {:keyExtractor (memo-wrap-key-fn key-fn)})
|
||||||
(merge (when key-fn {:keyExtractor (wrap-key-fn key-fn)})
|
(when render-fn {:renderItem (memo-wrap-render-fn render-fn render-data)})
|
||||||
(when render-fn {:renderItem (wrap-render-fn render-fn render-data)})
|
(when separator {:ItemSeparatorComponent (memo-separator-fn separator default-separator?)})
|
||||||
(when separator {:ItemSeparatorComponent (fn [] (reagent/as-element separator))})
|
(when empty-component {:ListEmptyComponent (memo-as-element empty-component)})
|
||||||
(when empty-component {:ListEmptyComponent (fn [] (reagent/as-element empty-component))})
|
(when header {:ListHeaderComponent (memo-as-element header)})
|
||||||
;; header and footer not wrapped in anonymous function to prevent re-creation on every re-render
|
(when footer {:ListFooterComponent (memo-as-element footer)})))
|
||||||
;; More details can be found here - https://github.com/facebook/react-native/issues/13602#issuecomment-300608431
|
|
||||||
(when header {:ListHeaderComponent (reagent/as-element header)})
|
|
||||||
(when footer {:ListFooterComponent (reagent/as-element footer)}))))
|
|
||||||
|
|
||||||
(defn flat-list
|
(defn flat-list
|
||||||
"A wrapper for FlatList.
|
"A wrapper for FlatList.
|
||||||
|
@ -115,7 +126,7 @@
|
||||||
(defn- wrap-per-section-render-fn [props]
|
(defn- wrap-per-section-render-fn [props]
|
||||||
(update
|
(update
|
||||||
(if-let [f (:render-fn props)]
|
(if-let [f (:render-fn props)]
|
||||||
(assoc (dissoc props :render-fn :render-data) :renderItem (wrap-render-fn f (:render-data props)))
|
(assoc (dissoc props :render-fn :render-data) :renderItem (memo-wrap-render-fn f (:render-data props)))
|
||||||
props)
|
props)
|
||||||
:data to-array))
|
:data to-array))
|
||||||
|
|
||||||
|
|
|
@ -6,15 +6,20 @@
|
||||||
[status-im.multiaccounts.core :as multiaccounts]
|
[status-im.multiaccounts.core :as multiaccounts]
|
||||||
[status-im.utils.image :as utils.image]))
|
[status-im.utils.image :as utils.image]))
|
||||||
|
|
||||||
|
(def memo-photo-rend
|
||||||
|
(memoize
|
||||||
|
(fn [photo-path 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)
|
||||||
|
:style (style/photo size)
|
||||||
|
:resize-mode :cover
|
||||||
|
:accessibility-label (or accessibility-label :chat-icon)}]
|
||||||
|
(when identicon?
|
||||||
|
[react/view {:style (style/photo-border size)}])]))))
|
||||||
|
|
||||||
(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))]
|
[memo-photo-rend photo-path size accessibility-label])
|
||||||
[react/view {:style (style/photo-container size)}
|
|
||||||
[react/image {:source (utils.image/source photo-path)
|
|
||||||
:style (style/photo size)
|
|
||||||
:resize-mode :cover
|
|
||||||
:accessibility-label (or accessibility-label :chat-icon)}]
|
|
||||||
(when identicon?
|
|
||||||
[react/view {:style (style/photo-border size)}])]))
|
|
||||||
|
|
||||||
;; We optionally pass identicon for perfomance reason, so it does not have to be calculated for each message
|
;; We optionally pass identicon for perfomance reason, so it does not have to be calculated for each message
|
||||||
(defn member-photo [pub-key identicon]
|
(defn member-photo [pub-key identicon]
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
(re-frame/dispatch [:bottom-sheet/hide])
|
(re-frame/dispatch [:bottom-sheet/hide])
|
||||||
(re-frame/dispatch event))
|
(re-frame/dispatch event))
|
||||||
|
|
||||||
(defn one-to-one-chat-accents [{:keys [chat-id]}]
|
(defn one-to-one-chat-accents [chat-id]
|
||||||
(let [photo @(re-frame/subscribe [:chats/photo-path chat-id])
|
(let [photo @(re-frame/subscribe [:chats/photo-path chat-id])
|
||||||
contact-name @(re-frame/subscribe [:contacts/contact-name-by-identity chat-id])]
|
contact-name @(re-frame/subscribe [:contacts/contact-name-by-identity chat-id])]
|
||||||
[react/view
|
[react/view
|
||||||
|
@ -45,7 +45,7 @@
|
||||||
:icon :main-icons/delete
|
:icon :main-icons/delete
|
||||||
:on-press #(re-frame/dispatch [:chat.ui/remove-chat-pressed chat-id])}]]))
|
:on-press #(re-frame/dispatch [:chat.ui/remove-chat-pressed chat-id])}]]))
|
||||||
|
|
||||||
(defn public-chat-accents [{:keys [chat-id]}]
|
(defn public-chat-accents [chat-id]
|
||||||
(let [link (universal-links/generate-link :public-chat :external chat-id)
|
(let [link (universal-links/generate-link :public-chat :external chat-id)
|
||||||
message (i18n/label :t/share-public-chat-text {:link link})]
|
message (i18n/label :t/share-public-chat-text {:link link})]
|
||||||
[react/view
|
[react/view
|
||||||
|
@ -147,13 +147,13 @@
|
||||||
:icon :main-icons/delete
|
:icon :main-icons/delete
|
||||||
:on-press #(hide-sheet-and-dispatch [:group-chats.ui/remove-chat-confirmed chat-id])}])]))))
|
:on-press #(hide-sheet-and-dispatch [:group-chats.ui/remove-chat-confirmed chat-id])}])]))))
|
||||||
|
|
||||||
(defn actions [{:keys [chat-type]
|
(defn actions [{:keys [chat-type chat-id]
|
||||||
:as current-chat}]
|
:as current-chat}]
|
||||||
(cond
|
(cond
|
||||||
(#{constants/public-chat-type
|
(#{constants/public-chat-type
|
||||||
constants/profile-chat-type
|
constants/profile-chat-type
|
||||||
constants/timeline-chat-type} chat-type)
|
constants/timeline-chat-type} chat-type)
|
||||||
[public-chat-accents current-chat]
|
[public-chat-accents chat-id]
|
||||||
|
|
||||||
(= chat-type constants/community-chat-type)
|
(= chat-type constants/community-chat-type)
|
||||||
[community-chat-accents current-chat]
|
[community-chat-accents current-chat]
|
||||||
|
@ -161,11 +161,14 @@
|
||||||
(= chat-type constants/private-group-chat-type)
|
(= chat-type constants/private-group-chat-type)
|
||||||
[group-chat-accents current-chat]
|
[group-chat-accents current-chat]
|
||||||
|
|
||||||
:else [one-to-one-chat-accents current-chat]))
|
:else [one-to-one-chat-accents chat-id]))
|
||||||
|
|
||||||
(defn current-chat-actions []
|
(defn current-chat-actions []
|
||||||
[actions @(re-frame/subscribe [:chats/current-chat])])
|
[actions @(re-frame/subscribe [:chats/current-chat])])
|
||||||
|
|
||||||
|
(defn chat-actions [chat-id]
|
||||||
|
[actions @(re-frame/subscribe [:chat-by-id chat-id])])
|
||||||
|
|
||||||
(defn options [chat-id message-id]
|
(defn options [chat-id message-id]
|
||||||
(fn []
|
(fn []
|
||||||
[react/view
|
[react/view
|
||||||
|
|
|
@ -330,38 +330,41 @@
|
||||||
space-keeper (get-space-keeper-ios bottom-space panel-space active-panel text-input-ref)
|
space-keeper (get-space-keeper-ios bottom-space panel-space active-panel text-input-ref)
|
||||||
set-active-panel (get-set-active-panel active-panel)
|
set-active-panel (get-set-active-panel active-panel)
|
||||||
on-close #(set-active-panel nil)]
|
on-close #(set-active-panel nil)]
|
||||||
(fn []
|
(reagent/create-class
|
||||||
(let [{:keys [chat-id show-input? group-chat admins invitation-admin] :as chat}
|
{:component-will-unmount #(re-frame/dispatch-sync [:close-chat])
|
||||||
;;we want to react only on these fields, do not use full chat map here
|
:reagent-render
|
||||||
@(re-frame/subscribe [:chats/current-chat-chat-view])
|
(fn []
|
||||||
max-bottom-space (max @bottom-space @panel-space)]
|
(let [{:keys [chat-id show-input? group-chat admins invitation-admin] :as chat}
|
||||||
[:<>
|
;;we want to react only on these fields, do not use full chat map here
|
||||||
[topbar]
|
@(re-frame/subscribe [:chats/current-chat-chat-view])
|
||||||
[connectivity/loading-indicator]
|
max-bottom-space (max @bottom-space @panel-space)]
|
||||||
(when chat-id
|
[:<>
|
||||||
(if group-chat
|
[topbar]
|
||||||
[invitation-requests chat-id admins]
|
[connectivity/loading-indicator]
|
||||||
[add-contact-bar chat-id]))
|
(when chat-id
|
||||||
;;MESSAGES LIST
|
(if group-chat
|
||||||
[messages-view {:chat chat
|
[invitation-requests chat-id admins]
|
||||||
:bottom-space max-bottom-space
|
[add-contact-bar chat-id]))
|
||||||
:pan-responder pan-responder
|
;;MESSAGES LIST
|
||||||
:space-keeper space-keeper
|
[messages-view {:chat chat
|
||||||
:show-input? show-input?}]
|
:bottom-space max-bottom-space
|
||||||
(when (and group-chat invitation-admin)
|
:pan-responder pan-responder
|
||||||
[accessory/view {:y position-y
|
:space-keeper space-keeper
|
||||||
:on-update-inset on-update}
|
:show-input? show-input?}]
|
||||||
[invitation-bar chat-id]])
|
(when (and group-chat invitation-admin)
|
||||||
[components/autocomplete-mentions text-input-ref max-bottom-space]
|
[accessory/view {:y position-y
|
||||||
(when show-input?
|
:on-update-inset on-update}
|
||||||
[accessory/view {:y position-y
|
[invitation-bar chat-id]])
|
||||||
:pan-state pan-state
|
[components/autocomplete-mentions text-input-ref max-bottom-space]
|
||||||
:has-panel (boolean @active-panel)
|
(when show-input?
|
||||||
:on-close on-close
|
[accessory/view {:y position-y
|
||||||
:on-update-inset on-update}
|
:pan-state pan-state
|
||||||
[components/chat-toolbar
|
:has-panel (boolean @active-panel)
|
||||||
{:chat-id chat-id
|
:on-close on-close
|
||||||
:active-panel @active-panel
|
:on-update-inset on-update}
|
||||||
:set-active-panel set-active-panel
|
[components/chat-toolbar
|
||||||
:text-input-ref text-input-ref}]
|
{:chat-id chat-id
|
||||||
[bottom-sheet @active-panel]])]))))
|
:active-panel @active-panel
|
||||||
|
:set-active-panel set-active-panel
|
||||||
|
:text-input-ref text-input-ref}]
|
||||||
|
[bottom-sheet @active-panel]])]))})))
|
||||||
|
|
|
@ -13,7 +13,8 @@
|
||||||
[status-im.ui.components.colors :as colors]
|
[status-im.ui.components.colors :as colors]
|
||||||
[status-im.ui.components.toolbar :as toolbar]
|
[status-im.ui.components.toolbar :as toolbar]
|
||||||
[status-im.ui.components.react :as react]
|
[status-im.ui.components.react :as react]
|
||||||
[status-im.ui.screens.communities.icon :as communities.icon]))
|
[status-im.ui.screens.communities.icon :as communities.icon]
|
||||||
|
[quo.design-system.colors :as quo.colors]))
|
||||||
|
|
||||||
(defn hide-sheet-and-dispatch [event]
|
(defn hide-sheet-and-dispatch [event]
|
||||||
(>evt [:bottom-sheet/hide])
|
(>evt [:bottom-sheet/hide])
|
||||||
|
@ -30,36 +31,35 @@
|
||||||
:accessibility-label :unviewed-messages-public}]))
|
:accessibility-label :unviewed-messages-public}]))
|
||||||
|
|
||||||
(defn community-home-list-item [{:keys [id name last?] :as community}]
|
(defn community-home-list-item [{:keys [id name last?] :as community}]
|
||||||
[react/view
|
[react/touchable-opacity {:style (merge {:height 64}
|
||||||
[quo/list-item
|
(when last?
|
||||||
{:icon [communities.icon/community-icon community]
|
{:border-bottom-color (quo.colors/get-color :ui-01)
|
||||||
:title [react/view {:flex-direction :row
|
:border-bottom-width 1}))
|
||||||
:flex 1}
|
:on-press (fn [id]
|
||||||
[react/view {:flex-direction :row
|
(>evt [:dismiss-keyboard])
|
||||||
:flex 1
|
(>evt [:navigate-to :community {:community-id id}]))}
|
||||||
:padding-right 16
|
[:<>
|
||||||
:align-items :center}
|
[react/view {:top 12 :left 16 :position :absolute}
|
||||||
[quo/text {:weight :medium
|
[communities.icon/community-icon community]]
|
||||||
:accessibility-label :chat-name-text
|
[react/view {:style {:margin-left 72
|
||||||
:font-size 17
|
:flex-direction :row
|
||||||
:ellipsize-mode :tail
|
:flex 1}
|
||||||
:number-of-lines 1}
|
:accessibility-label :chat-name-text}
|
||||||
name]]
|
[react/view {:flex-direction :row
|
||||||
[react/view {:flex-direction :row
|
:flex 1
|
||||||
:flex 1
|
:padding-right 16
|
||||||
:justify-content :flex-end
|
:align-items :center}
|
||||||
:align-items :center}
|
[quo/text {:weight :medium
|
||||||
[community-unviewed-count id]]]
|
:accessibility-label :chat-name-text
|
||||||
:title-accessibility-label :chat-name-text
|
:font-size 17
|
||||||
:on-press #(do
|
:ellipsize-mode :tail
|
||||||
(>evt [:dismiss-keyboard])
|
:number-of-lines 1}
|
||||||
(>evt [:navigate-to :community {:community-id id}]))}]
|
name]]
|
||||||
;; TODO: actions
|
[react/view {:flex-direction :row
|
||||||
;; :on-long-press #(>evt [:bottom-sheet/show-sheet
|
:flex 1
|
||||||
;; nil])
|
:justify-content :flex-end
|
||||||
|
:align-items :center}
|
||||||
(when last?
|
[community-unviewed-count id]]]]])
|
||||||
[quo/separator])])
|
|
||||||
|
|
||||||
(defn community-list-item [{:keys [id permissions members name description] :as community}]
|
(defn community-list-item [{:keys [id permissions members name description] :as community}]
|
||||||
(let [members-count (count members)
|
(let [members-count (count members)
|
||||||
|
@ -103,13 +103,6 @@
|
||||||
:icon :main-icons/add
|
:icon :main-icons/add
|
||||||
:on-press #(hide-sheet-and-dispatch [::communities/open-create-community])}]])
|
:on-press #(hide-sheet-and-dispatch [::communities/open-create-community])}]])
|
||||||
|
|
||||||
(defn communities-home-list [communities]
|
|
||||||
[list/flat-list
|
|
||||||
{:key-fn :id
|
|
||||||
:keyboard-should-persist-taps :always
|
|
||||||
:data communities
|
|
||||||
:render-fn community-home-list-item}])
|
|
||||||
|
|
||||||
(defn communities-list [communities]
|
(defn communities-list [communities]
|
||||||
[list/section-list
|
[list/section-list
|
||||||
{:content-container-style {:padding-vertical 8}
|
{:content-container-style {:padding-vertical 8}
|
||||||
|
|
|
@ -21,7 +21,10 @@
|
||||||
:text-align :right
|
:text-align :right
|
||||||
:letter-spacing 0.4
|
:letter-spacing 0.4
|
||||||
:align-items :center
|
:align-items :center
|
||||||
:line-height 12})
|
:line-height 12
|
||||||
|
:position :absolute
|
||||||
|
:top 10
|
||||||
|
:right 16})
|
||||||
|
|
||||||
(defn chat-tooltip []
|
(defn chat-tooltip []
|
||||||
{:align-items :center
|
{:align-items :center
|
||||||
|
|
|
@ -101,7 +101,7 @@
|
||||||
|
|
||||||
(defonce search-active? (reagent/atom false))
|
(defonce search-active? (reagent/atom false))
|
||||||
|
|
||||||
(defn search-input-wrapper [search-filter chats]
|
(defn search-input-wrapper [search-filter chats-empty]
|
||||||
[react/view {:padding-horizontal 16
|
[react/view {:padding-horizontal 16
|
||||||
:padding-vertical 10}
|
:padding-vertical 10}
|
||||||
[search-input/search-input
|
[search-input/search-input
|
||||||
|
@ -109,7 +109,7 @@
|
||||||
:search-filter search-filter
|
:search-filter search-filter
|
||||||
:on-cancel #(re-frame/dispatch [:search/home-filter-changed nil])
|
:on-cancel #(re-frame/dispatch [:search/home-filter-changed nil])
|
||||||
:on-blur (fn []
|
:on-blur (fn []
|
||||||
(when-not (seq chats)
|
(when chats-empty
|
||||||
(re-frame/dispatch [:search/home-filter-changed nil]))
|
(re-frame/dispatch [:search/home-filter-changed nil]))
|
||||||
(re-frame/dispatch [::new-chat/clear-new-identity]))
|
(re-frame/dispatch [::new-chat/clear-new-identity]))
|
||||||
:on-focus (fn [search-filter]
|
:on-focus (fn [search-filter]
|
||||||
|
@ -174,12 +174,13 @@
|
||||||
[welcome-blank-page]
|
[welcome-blank-page]
|
||||||
[list/flat-list
|
[list/flat-list
|
||||||
{:key-fn chat-list-key-fn
|
{:key-fn chat-list-key-fn
|
||||||
|
:initialNumToRender 5
|
||||||
:keyboard-should-persist-taps :always
|
:keyboard-should-persist-taps :always
|
||||||
:data items
|
:data items
|
||||||
:render-fn render-fn
|
:render-fn render-fn
|
||||||
:header [:<>
|
:header [:<>
|
||||||
(when (or (seq items) @search-active? (seq search-filter))
|
(when (or (seq items) @search-active? (seq search-filter))
|
||||||
[search-input-wrapper search-filter items])
|
[search-input-wrapper search-filter (empty? items)])
|
||||||
[referral-item/list-item]
|
[referral-item/list-item]
|
||||||
(when (and (empty? items)
|
(when (and (empty? items)
|
||||||
(or @search-active? (seq search-filter)))
|
(or @search-active? (seq search-filter)))
|
||||||
|
|
|
@ -12,7 +12,9 @@
|
||||||
[status-im.ui.components.icons.icons :as icons]
|
[status-im.ui.components.icons.icons :as icons]
|
||||||
[status-im.utils.contenthash :as contenthash]
|
[status-im.utils.contenthash :as contenthash]
|
||||||
[status-im.utils.core :as utils]
|
[status-im.utils.core :as utils]
|
||||||
[status-im.utils.datetime :as time]))
|
[status-im.utils.datetime :as time]
|
||||||
|
[status-im.ui.components.chat-icon.styles :as chat-icon.styles]
|
||||||
|
[status-im.ui.screens.chat.sheets :as sheets]))
|
||||||
|
|
||||||
(defn mention-element [from]
|
(defn mention-element [from]
|
||||||
@(re-frame/subscribe [:contacts/contact-name-by-identity from]))
|
@(re-frame/subscribe [:contacts/contact-name-by-identity from]))
|
||||||
|
@ -39,7 +41,7 @@
|
||||||
|
|
||||||
"mention"
|
"mention"
|
||||||
{:components [react/text-class [mention-element literal]]
|
{:components [react/text-class [mention-element literal]]
|
||||||
:length 4} ;; we can't predict name length so take the smallest possible
|
:length 4} ;; we can't predict name length so take the smallest possible
|
||||||
|
|
||||||
"status-tag"
|
"status-tag"
|
||||||
(truncate-literal (str "#" literal))
|
(truncate-literal (str "#" literal))
|
||||||
|
@ -72,7 +74,7 @@
|
||||||
(:components result)))
|
(:components result)))
|
||||||
|
|
||||||
(defn message-content-text [{:keys [content content-type community-id]}]
|
(defn message-content-text [{:keys [content content-type community-id]}]
|
||||||
[:<>
|
[react/view {:position :absolute :left 72 :top 32 :right 80}
|
||||||
(cond
|
(cond
|
||||||
|
|
||||||
(not (and content content-type))
|
(not (and content content-type))
|
||||||
|
@ -109,76 +111,88 @@
|
||||||
(:text content)
|
(:text content)
|
||||||
(render-subheader (:parsed-text content)))])
|
(render-subheader (:parsed-text content)))])
|
||||||
|
|
||||||
(defn message-timestamp [timestamp]
|
(def memo-timestamp
|
||||||
[react/view
|
(memoize
|
||||||
(when timestamp
|
(fn [timestamp]
|
||||||
[react/text {:style styles/datetime-text
|
(string/upper-case (time/to-short-str timestamp)))))
|
||||||
:number-of-lines 1
|
|
||||||
:accessibility-label :last-message-time-text}
|
|
||||||
;;TODO (perf) move to event
|
|
||||||
(string/upper-case (time/to-short-str timestamp))])])
|
|
||||||
|
|
||||||
(defn unviewed-indicator [{:keys [unviewed-messages-count public?]}]
|
(defn unviewed-indicator [{:keys [unviewed-messages-count public?]}]
|
||||||
(when (pos? unviewed-messages-count)
|
(when (pos? unviewed-messages-count)
|
||||||
[react/view {:padding-left 16
|
[react/view {:position :absolute :right 16 :bottom 12}
|
||||||
:justify-content :flex-end
|
|
||||||
:align-items :flex-end}
|
|
||||||
(if public?
|
(if public?
|
||||||
[react/view {:style styles/public-unread
|
[react/view {:style styles/public-unread
|
||||||
:accessibility-label :unviewed-messages-public}]
|
:accessibility-label :unviewed-messages-public}]
|
||||||
[badge/message-counter unviewed-messages-count])]))
|
[badge/message-counter unviewed-messages-count])]))
|
||||||
|
|
||||||
|
(def memo-on-long-press
|
||||||
|
(memoize
|
||||||
|
(fn [chat-id]
|
||||||
|
(fn []
|
||||||
|
(re-frame/dispatch [:bottom-sheet/show-sheet
|
||||||
|
{:content (fn [] [sheets/chat-actions chat-id])}])))))
|
||||||
|
|
||||||
|
(def memo-on-press
|
||||||
|
(memoize
|
||||||
|
(fn [chat-id]
|
||||||
|
(fn []
|
||||||
|
(re-frame/dispatch [:dismiss-keyboard])
|
||||||
|
(re-frame/dispatch [:chat.ui/navigate-to-chat chat-id])
|
||||||
|
(re-frame/dispatch [:search/home-filter-changed nil])))))
|
||||||
|
|
||||||
(defn icon-style []
|
(defn icon-style []
|
||||||
{:color colors/black
|
{:color colors/black
|
||||||
:width 15
|
:width 15
|
||||||
:height 15
|
:height 15
|
||||||
:container-style {:width 15
|
:container-style {:top 13 :left 72
|
||||||
:height 15
|
:position :absolute
|
||||||
:margin-right 2}})
|
:width 15
|
||||||
|
:height 15
|
||||||
|
:margin-right 2}})
|
||||||
|
|
||||||
|
(defn chat-item-icon [muted private-group? public-group?]
|
||||||
|
(cond
|
||||||
|
muted
|
||||||
|
[icons/icon :main-icons/tiny-muted (assoc (icon-style) :color colors/gray)]
|
||||||
|
private-group?
|
||||||
|
[icons/icon :main-icons/tiny-group (icon-style)]
|
||||||
|
public-group?
|
||||||
|
[icons/icon :main-icons/tiny-public (icon-style)]
|
||||||
|
:else
|
||||||
|
[icons/icon :main-icons/tiny-new-contact (icon-style)]))
|
||||||
|
|
||||||
|
(defn chat-item-title [chat-id muted group-chat chat-name]
|
||||||
|
[quo/text {:weight :medium
|
||||||
|
:color (when muted :secondary)
|
||||||
|
:accessibility-label :chat-name-text
|
||||||
|
:ellipsize-mode :tail
|
||||||
|
:number-of-lines 1
|
||||||
|
:style {:position :absolute :left 92 :top 10 :right 90}}
|
||||||
|
(if group-chat
|
||||||
|
(utils/truncate-str chat-name 30)
|
||||||
|
;; This looks a bit odd, but I would like only to subscribe
|
||||||
|
;; if it's a one-to-one. If wrapped in a component styling
|
||||||
|
;; won't be applied correctly.
|
||||||
|
(first @(re-frame/subscribe [:contacts/contact-two-names-by-identity chat-id])))])
|
||||||
|
|
||||||
(defn home-list-item [home-item opts]
|
(defn home-list-item [home-item opts]
|
||||||
(let [{:keys [chat-id chat-name color online group-chat
|
(let [{:keys [chat-id chat-name color group-chat public? timestamp last-message muted]} home-item]
|
||||||
public? timestamp last-message muted]}
|
[react/touchable-opacity (merge {:style {:height 64}} opts)
|
||||||
home-item
|
[:<>
|
||||||
private-group? (and group-chat (not public?))
|
[chat-item-icon muted (and group-chat (not public?)) (and group-chat public?)]
|
||||||
public-group? (and group-chat public?)]
|
[chat-icon.screen/chat-icon-view chat-id group-chat chat-name
|
||||||
[quo/list-item
|
{:container (assoc chat-icon.styles/container-chat-list
|
||||||
(merge {:icon [chat-icon.screen/chat-icon-view-chat-list
|
:top 12 :left 16 :position :absolute)
|
||||||
chat-id group-chat chat-name color online false]
|
:size 40
|
||||||
:title [react/view {:flex-direction :row
|
:chat-icon chat-icon.styles/chat-icon-chat-list
|
||||||
:flex 1}
|
:default-chat-icon (chat-icon.styles/default-chat-icon-chat-list color)
|
||||||
[react/view {:flex-direction :row
|
:default-chat-icon-text (chat-icon.styles/default-chat-icon-text 40)}]
|
||||||
:flex 1
|
[chat-item-title chat-id muted group-chat chat-name]
|
||||||
:padding-right 16
|
[react/text {:style styles/datetime-text
|
||||||
:align-items :center}
|
:number-of-lines 1
|
||||||
(cond
|
:accessibility-label :last-message-time-text}
|
||||||
muted
|
;;TODO (perf) move to event
|
||||||
[icons/icon :main-icons/tiny-muted (assoc (icon-style) :color colors/gray)]
|
(memo-timestamp (if (pos? (:whisper-timestamp last-message))
|
||||||
private-group?
|
(:whisper-timestamp last-message)
|
||||||
[icons/icon :main-icons/tiny-group (icon-style)]
|
timestamp))]
|
||||||
public-group?
|
[message-content-text (select-keys last-message [:content :content-type :community-id])]
|
||||||
[icons/icon :main-icons/tiny-public (icon-style)]
|
[unviewed-indicator home-item]]]))
|
||||||
:else
|
|
||||||
[icons/icon :main-icons/tiny-new-contact (icon-style)])
|
|
||||||
[quo/text {:weight :medium
|
|
||||||
:color (when muted :secondary)
|
|
||||||
:accessibility-label :chat-name-text
|
|
||||||
:ellipsize-mode :tail
|
|
||||||
:number-of-lines 1}
|
|
||||||
(if group-chat
|
|
||||||
(utils/truncate-str chat-name 30)
|
|
||||||
;; This looks a bit odd, but I would like only to subscribe
|
|
||||||
;; if it's a one-to-one. If wrapped in a component styling
|
|
||||||
;; won't be applied correctly.
|
|
||||||
(first @(re-frame/subscribe [:contacts/contact-two-names-by-identity chat-id])))]]
|
|
||||||
[message-timestamp (if (pos? (:whisper-timestamp last-message))
|
|
||||||
(:whisper-timestamp last-message)
|
|
||||||
timestamp)]]
|
|
||||||
:title-accessibility-label :chat-name-text
|
|
||||||
:subtitle [react/view {:flex-direction :row}
|
|
||||||
[react/view {:flex 1}
|
|
||||||
[message-content-text (select-keys last-message [:content
|
|
||||||
:content-type
|
|
||||||
:community-id])]]
|
|
||||||
[unviewed-indicator home-item]]}
|
|
||||||
opts)]))
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
(:require [clojure.string :as string]
|
(:require [clojure.string :as string]
|
||||||
#?(:cljs [taoensso.timbre :as log])))
|
#?(:cljs [taoensso.timbre :as log])))
|
||||||
|
|
||||||
(defn truncate-str
|
(defn truncate-str-memo
|
||||||
"Given string and max threshold, trims the string to threshold length with `...`
|
"Given string and max threshold, trims the string to threshold length with `...`
|
||||||
appended to end or in the middle if length of the string exceeds max threshold,
|
appended to end or in the middle if length of the string exceeds max threshold,
|
||||||
returns the same string if threshold is not exceeded"
|
returns the same string if threshold is not exceeded"
|
||||||
|
@ -19,6 +19,8 @@
|
||||||
(str (subs s 0 (- threshold 3)) "..."))
|
(str (subs s 0 (- threshold 3)) "..."))
|
||||||
s))
|
s))
|
||||||
|
|
||||||
|
(def truncate-str (memoize truncate-str-memo))
|
||||||
|
|
||||||
(defn clean-text [s]
|
(defn clean-text [s]
|
||||||
(-> s
|
(-> s
|
||||||
(string/replace #"\n" "")
|
(string/replace #"\n" "")
|
||||||
|
|
Loading…
Reference in New Issue