Messages home tabs new UI in ui2 (#14183)

* feat: messages home new ui
This commit is contained in:
Omar Basem 2022-10-20 08:54:27 +04:00 committed by GitHub
parent c017c01c53
commit 153dbb2de1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 518 additions and 208 deletions

View File

@ -196,7 +196,7 @@
title subtitle subtitle-secondary active on-press on-long-press chevron size text-size
accessory-text accessibility-label title-accessibility-label accessory-style
haptic-feedback haptic-type error animated animated-accessory? title-text-weight container-style
active-background-enabled]
active-background-enabled background-color]
:or {subtitle-max-lines 1
theme :main
haptic-feedback true
@ -217,9 +217,12 @@
rn/view
animated animated/pressable
:else gh/touchable-highlight)]
[rn/view {:background-color (if (and (= accessory :radio) active)
active-background
passive-background)}
[rn/view {:background-color (cond (not= background-color nil)
background-color
(and (= accessory :radio) active)
active-background
:else
passive-background)}
[component
(merge {:type :list-item
:disabled disabled

View File

@ -9,23 +9,22 @@
(def default-tab-size 32)
(defn tabs [{:keys [default-active on-change]}]
(let [active-tab-id (reagent/atom default-active)]
(fn [{:keys [data size] :or {size default-tab-size}}]
(let [active-id @active-tab-id]
[rn/view {:flex-direction :row}
(for [{:keys [label id]} data]
^{:key id}
[rn/view {:style {:margin-right (if (= size default-tab-size) 12 8)}}
[tab/tab
{:id id
:size size
:active (= id active-id)
:on-press (fn [^js press-event id]
(reset! active-tab-id id)
(when on-change
(on-change press-event id)))}
label]])]))))
(defn tabs [{:keys [default-active on-change style]}]
(fn [{:keys [data size] :or {size default-tab-size}}]
(let [active-id @default-active]
[rn/view (merge {:flex-direction :row} style)
(for [{:keys [label id]} data]
^{:key id}
[rn/view {:style {:margin-right (if (= size default-tab-size) 12 8)}}
[tab/tab
{:id id
:size size
:active (= id active-id)
:on-press (fn [^js press-event id]
(reset! default-active id)
(when on-change
(on-change press-event id)))}
label]])])))
(defn- calculate-fade-end-percentage
[{:keys [offset-x content-width layout-width max-fade-percentage]}]

View File

@ -2,7 +2,7 @@
(:require [quo2.reanimated :as reanimated]
[status-im.utils.platform :as platform]
[status-im.switcher.switcher :as switcher]
[status-im.ui.screens.home.views :as home]
[status-im.ui2.screens.chat.home :as chat.home]
[status-im.switcher.constants :as constants]
[status-im.switcher.bottom-tabs :as bottom-tabs]
[status-im.ui.screens.profile.user.views :as profile.user]
@ -30,7 +30,7 @@
:position :absolute})}
(case stack-id
:communities-stack [communities/communities-list]
:chats-stack [home/home]
:chats-stack [chat.home/home]
:wallet-stack [wallet.accounts/accounts-overview]
:browser-stack [profile.user/my-profile])])]))

View File

@ -74,15 +74,15 @@
:photo-container (:default-chat-icon styles)}])])
(defn chat-icon-view-toolbar
[chat-id group-chat name color emoji]
[chat-id group-chat name color emoji size]
[emoji-chat-icon-view chat-id group-chat name emoji
{:container styles/container-chat-toolbar
:size 36
:chat-icon styles/chat-icon-chat-toolbar
:default-chat-icon (styles/default-chat-icon-chat-toolbar color)
{:container (styles/container-chat-toolbar size)
:size size
:chat-icon (styles/chat-icon-chat-toolbar size)
:default-chat-icon (styles/default-chat-icon-chat-toolbar color size)
:default-chat-icon-text (if (string/blank? emoji)
(styles/default-chat-icon-text 36)
(styles/emoji-chat-icon-text 36))}])
(styles/default-chat-icon-text size)
(styles/emoji-chat-icon-text size))}])
(defn chat-icon-view-chat-list
[chat-id group-chat name color]

View File

@ -44,11 +44,11 @@
:height 20
:border-radius 20}))
(defn default-chat-icon-chat-toolbar [color]
(defn default-chat-icon-chat-toolbar [color size]
(merge (default-chat-icon color)
{:width 36
:height 36
:border-radius 18}))
{:width size
:height size
:border-radius size}))
(defn default-chat-icon-profile [color size]
(merge (default-chat-icon color)
@ -115,10 +115,10 @@
:height 20
:margin 0}))
(def chat-icon-chat-toolbar
(defn chat-icon-chat-toolbar [size]
(merge chat-icon
{:width 36
:height 36
{:width size
:height size
:margin 0}))
(defn custom-size-icon [size]
@ -149,9 +149,9 @@
{:width size
:height size})
(def container-chat-toolbar
{:width 36
:height 36})
(defn container-chat-toolbar [size]
{:width size
:height size})
(defn chat-icon-profile-edit []
{:width 24

View File

@ -114,11 +114,11 @@
sticker
(if replied-message
[fast-image/fast-image {:style {:margin 4 :width 56 :height 56}
;; Get sticker url of the message replied to
;; Get sticker url of the message replied to
:source {:uri (((replied-message :content) :sticker) :url)}}]
;; Let the user know if the message was deleted
;; Let the user know if the message was deleted
[react/text {:style (style/quoted-message-text (and outgoing (not pinned)))}
;; This hardcorded text can be modified to come from the parsed-text. Also, translations can be added.
;; This hardcorded text can be modified to come from the parsed-text. Also, translations can be added.
"This message was deleted!"])
:else [react/text {:style (style/quoted-message-text (and outgoing (not pinned)))
:number-of-lines 5}
@ -213,13 +213,13 @@
message-status [react/text {:style (style/message-status-placeholder)}
(str (if (and outgoing (not in-popover?)) " " " ") (when (and (not in-popover?) edited-at) edited-at-text))]
last-element (peek elements)]
;; 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
;; 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
(if (= react/text-class (nth last-element 0))
;; Append message status to last text
;; Append message status to last text
(conj (pop elements) (conj last-element message-status))
;; Append message status to new block
;; Append message status to new block
(conj elements message-status))))
(defn unknown-content-type
@ -250,7 +250,7 @@
(defn pin-author-name [pinned-by]
(let [user-contact @(re-frame/subscribe [:multiaccount/contact])
contact-names @(re-frame/subscribe [:contacts/contact-two-names-by-identity pinned-by])]
;; We append empty spaces to the name as a workaround to make one-line and multi-line label components show correctly
;; We append empty spaces to the name as a workaround to make one-line and multi-line label components show correctly
(str " " (if (= pinned-by (user-contact :public-key)) (i18n/label :t/You) (first contact-names)))))
(def pin-icon-width 9)
@ -346,10 +346,10 @@
:on-press #(do (when modal (close-modal))
(re-frame/dispatch [:chat.ui/show-profile from]))}
[message-author-name from {:modal modal}]])
;;MESSAGE CONTENT
;;MESSAGE CONTENT
content
[link-preview/link-preview-wrapper (:links (:content message)) outgoing false]]]
; delivery status
; delivery status
[react/view (style/delivery-status outgoing)
[message-delivery-status message]]])
@ -361,7 +361,7 @@
(let [width (.-width (.-nativeEvent evt))
height (.-height (.-nativeEvent evt))]
(if (< width height)
;; if width less than the height we reduce width proportionally to height
;; if width less than the height we reduce width proportionally to height
(let [k (/ height image-max-height)]
(when (not= (/ width k) (first @dimensions))
(reset! dimensions {:width (/ width k) :height image-max-height :loaded true})))
@ -540,7 +540,7 @@
(fn [{:keys [content current-public-key outgoing edit-enabled public? pinned in-popover? message-pin-enabled content-type edited-at] :as message}
{:keys [on-long-press modal]
:as reaction-picker}]
;; Makes sure to render a text-messsage and not an emoji-message if it has been edited with text
;; Makes sure to render a text-messsage and not an emoji-message if it has been edited with text
(if (= content-type constants/content-type-text)
[message-content-wrapper message
[collapsible-text-message message on-long-press modal] reaction-picker]

View File

@ -36,7 +36,7 @@
chat-info]
[react/view {:style st/toolbar-container}
[react/view {:margin-right 10}
[chat-icon.screen/chat-icon-view-toolbar chat-id group-chat chat-name color emoji]]
[chat-icon.screen/chat-icon-view-toolbar chat-id group-chat chat-name color emoji 36]]
[react/view {:style st/chat-name-view}
(if group-chat
[react/text {:style st/chat-name-text

View File

@ -380,7 +380,7 @@
:keyboard-should-persist-taps :handled
:onMomentumScrollBegin state/start-scrolling
:onMomentumScrollEnd state/stop-scrolling
;;TODO https://github.com/facebook/react-native/issues/30034
;;TODO https://github.com/facebook/react-native/issues/30034
:inverted (when platform/ios? true)
:style (when platform/android? {:scaleY -1})})]))
@ -481,4 +481,4 @@
(react/hw-back-remove-listener navigate-back-handler)
(react/hw-back-add-listener navigate-back-handler))
:component-will-unmount (fn [] (react/hw-back-remove-listener navigate-back-handler))
:reagent-render chat-render}))
:reagent-render chat-render}))

View File

@ -80,7 +80,7 @@
[react/view {:flex 1}
[quo2.tabs/tabs {:size 32
:on-change #(reset! selected-tab %)
:default-active :all
:default-active selected-tab
:data [{:id :all
:label (i18n/label :t/all)}
{:id :open

View File

@ -48,7 +48,7 @@
id
true
display-name
(or color (rand-nth colors/chat-colors))])]
(or color (rand-nth colors/chat-colors)) nil 36])]
[rn/view {:style {:flex 1 :justify-content :center}}
[quo/text {:number-of-lines 1
:accessibility-label :community-name-text}

View File

@ -58,7 +58,8 @@
[status-im.ui.screens.glossary.view :as glossary]
[status-im.ui.screens.group.views :as group-chat]
[status-im.ui.screens.help-center.views :as help-center]
[status-im.ui.screens.home.views :as home]
[status-im.ui.screens.home.views :as old-chat.home]
[status-im.ui2.screens.chat.home :as chat.home]
[status-im.ui.screens.keycard.authentication-method.views :as keycard.authentication]
[status-im.ui.screens.keycard.onboarding.views :as keycard.onboarding]
[status-im.ui.screens.keycard.pairing.views :as keycard.pairing]
@ -218,7 +219,7 @@
;Home
{:name :home
:component home/home-old}
:component (if config/new-ui-enabled? chat.home/home old-chat.home/home-old)}
;Chat
{:name :chat

View File

@ -11,6 +11,7 @@
[status-im.ui2.screens.chat.composer.style :as style]
[re-frame.core :as re-frame]
[status-im.chat.models.mentions :as mentions]
[quo2.foundations.colors :as quo2.colors]
[quo.react]))
(defonce input-texts (atom {}))
@ -173,6 +174,6 @@
[idx item])
(<sub [:chat/input-with-mentions]))]
^{:key (str idx "_" type "_" text)}
[rn/text (when (= type :mention) {:style {:color "#0DA4C9"}})
[rn/text (when (= type :mention) {:style {:color quo2.colors/primary-50}})
text])
(get @input-texts chat-id))]))
(get @input-texts chat-id))]))

View File

@ -11,7 +11,7 @@
(defn mention-item
[[public-key {:keys [alias name nickname] :as user}] _ _ text-input-ref]
(let [ens-name? (not= alias name)]
[rn/touchable-opacity {:on-press #(>evt [:chat.ui/select-mention text-input-ref user]) :style {:border-width 1 :border-color :red}}
[rn/touchable-opacity {:on-press #(>evt [:chat.ui/select-mention text-input-ref user])}
;;TODO quo2 item should be used
[list-item/list-item
(cond-> {:icon [photos/member-photo public-key]
@ -53,4 +53,4 @@
:data suggestions
:key-fn first
:render-fn mention-item
:content-container-style {:padding-bottom 12}}]]))])
:content-container-style {:padding-bottom 12}}]]))])

View File

@ -0,0 +1,301 @@
(ns status-im.ui2.screens.chat.home
(:require [re-frame.core :as re-frame]
[reagent.core :as reagent]
[status-im.i18n.i18n :as i18n]
[status-im.react-native.resources :as resources]
[status-im.ui.components.connectivity.view :as connectivity]
[status-im.ui.components.icons.icons :as icons]
[status-im.ui.components.list.views :as list]
[status-im.ui.components.react :as react]
[status-im.ui.screens.home.styles :as styles]
[status-im.ui.screens.home.views.inner-item :refer [home-list-item]]
[quo.design-system.colors :as colors]
[quo.core :as quo]
[quo.platform :as platform]
[status-im.add-new.core :as new-chat]
[status-im.ui.components.search-input.view :as search-input]
[status-im.add-new.db :as db]
[status-im.utils.debounce :as debounce]
[status-im.utils.utils :as utils]
[status-im.ui.components.topbar :as topbar]
[status-im.ui.components.plus-button :as components.plus-button]
[status-im.ui.screens.chat.sheets :as sheets]
[status-im.ui.components.tabbar.core :as tabbar]
[status-im.ui.components.invite.views :as invite]
[status-im.utils.handlers :refer [<sub]]
[status-im.utils.config :as config]
[quo2.components.markdown.text :as quo2.text]
[status-im.qr-scanner.core :as qr-scanner]
[status-im.ui.components.chat-icon.styles :as chat-icon.styles]
[quo.react-native :as rn]
[quo2.foundations.colors :as quo2.colors]
[quo2.foundations.typography :as typography]
[quo2.components.buttons.button :as quo2.button]
[quo2.components.tabs.tabs :as quo2.tabs]
[status-im.multiaccounts.core :as multiaccounts]
[status-im.ui.components.chat-icon.screen :as chat-icon])
(:require-macros [status-im.utils.views :as views]))
(defn home-tooltip-view []
[rn/view (styles/chat-tooltip)
[rn/view {:style {:width 66 :position :absolute :top -6 :background-color colors/white
:align-items :center}}
[rn/image {:source (resources/get-image :empty-chats-header)
:style {:width 50 :height 50}}]]
[rn/touchable-highlight
{:style {:position :absolute :right 0 :top 0
:width 44 :height 44 :align-items :center :justify-content :center}
:on-press #(re-frame/dispatch [:multiaccounts.ui/hide-home-tooltip])
:accessibility-label :hide-home-button}
[icons/icon :main-icons/close-circle {:color colors/gray}]]
[react/i18n-text {:style styles/no-chats-text :key :chat-and-transact}]
[rn/view {:align-items :center
:margin-top 8
:margin-bottom 12}
[invite/button]]])
(defn welcome-blank-chats []
[rn/view {:style {:flex 1 :align-items :center :justify-content :center}}
[icons/icon :main-icons/placeholder20 {:width 120 :height 120}]
[rn/text {:style (merge typography/font-semi-bold typography/paragraph-1)} (i18n/label :t/no-messages)]
[rn/text {:style (merge typography/font-regular typography/paragraph-2)} (i18n/label :t/blank-messages-text)]])
(defn welcome-blank-page []
[rn/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}]])
(defonce search-active? (reagent/atom false))
(defn search-input-wrapper [search-filter chats-empty]
[rn/view {:padding-horizontal 16
:padding-vertical 10}
[search-input/search-input
{:search-active? search-active?
:search-filter search-filter
:on-cancel #(re-frame/dispatch [:search/home-filter-changed nil])
:on-blur (fn []
(when chats-empty
(re-frame/dispatch [:search/home-filter-changed nil]))
(re-frame/dispatch [::new-chat/clear-new-identity]))
:on-focus (fn [search-filter]
(when-not search-filter
(re-frame/dispatch [:search/home-filter-changed ""])
(re-frame/dispatch [::new-chat/clear-new-identity])))
:on-change (fn [text]
(re-frame/dispatch [:search/home-filter-changed text])
(re-frame/dispatch [:set-in [:contacts/new-identity :state] :searching])
(debounce/debounce-and-dispatch [:new-chat/set-new-identity text] 300))}]])
(defn start-suggestion [search-value]
(let [{:keys [state ens-name public-key]}
@(re-frame/subscribe [:contacts/new-identity])
valid-private? (= state :valid)
valid-public? (db/valid-topic? search-value)]
(when (or valid-public? valid-private?)
[rn/view
[quo/list-header (i18n/label :t/search-no-chat-found)]
(when valid-private?
[quo/list-item {:theme :accent
:icon :main-icons/private-chat
:title (or ens-name (utils/get-shortened-address public-key))
:subtitle (i18n/label :t/join-new-private-chat)
:on-press (fn []
(debounce/dispatch-and-chill [:contact.ui/contact-code-submitted false] 3000)
(re-frame/dispatch [:search/home-filter-changed nil]))}])
(when valid-public?
[quo/list-item {:theme :accent
:icon :main-icons/public-chat
:title (str "#" search-value)
:subtitle (i18n/label :t/join-new-public-chat)
:on-press (fn []
(re-frame/dispatch [:chat.ui/start-public-chat search-value])
(re-frame/dispatch [:set :public-group-topic nil])
(re-frame/dispatch [:search/home-filter-changed nil]))}])])))
(defn render-fn [{:keys [chat-id] :as home-item}]
[home-list-item
home-item
{:on-press (fn []
(re-frame/dispatch [:dismiss-keyboard])
(if platform/android?
(re-frame/dispatch [:chat.ui/navigate-to-chat-nav2 chat-id])
(re-frame/dispatch [:chat.ui/navigate-to-chat chat-id]))
(re-frame/dispatch [:search/home-filter-changed nil])
(re-frame/dispatch [:accept-all-activity-center-notifications-from-chat chat-id]))
:on-long-press #(re-frame/dispatch [:bottom-sheet/show-sheet
{:content (fn []
[sheets/actions home-item])}])}])
(defn- render-contact [{:keys [public-key] :as row}]
(let [[first-name second-name] (multiaccounts/contact-two-names row true)
row (assoc row :chat-id public-key)]
[quo/list-item
{:title first-name
:subtitle second-name
:background-color quo2.colors/neutral-5
:on-press (fn []
(re-frame/dispatch [:dismiss-keyboard])
(if platform/android?
(re-frame/dispatch [:chat.ui/navigate-to-chat-nav2 public-key])
(re-frame/dispatch [:chat.ui/navigate-to-chat public-key]))
(re-frame/dispatch [:search/home-filter-changed nil])
(re-frame/dispatch [:accept-all-activity-center-notifications-from-chat public-key]))
:on-long-press #(re-frame/dispatch [:bottom-sheet/show-sheet
{:content (fn []
[sheets/actions row])}])
:icon [chat-icon/contact-icon-contacts-tab
(multiaccounts/displayed-photo row)]}]))
(defn chat-list-key-fn [item]
(or (:chat-id item) (:public-key item)))
(defn get-item-layout [_ index]
#js {:length 64 :offset (* 64 index) :index index})
(def selected-tab (reagent/atom :recent))
(defn prepare-items [current-active-tab items]
(if (= current-active-tab :groups)
(filter #(-> % :group-chat (= true)) items)
items))
(defn prepare-contacts [contacts]
(let [data (atom {})]
(doseq [i (range (count contacts))]
(let [first-char (get (:alias (nth contacts i)) 0)]
(if-not (contains? @data first-char)
(swap! data #(assoc % first-char {:title first-char :data [(nth contacts i)]}))
(swap! data #(assoc-in % [first-char :data] (conj (:data (get @data first-char)) (nth contacts i)))))))
(swap! data #(sort @data))
(vals @data)))
(defn contacts-section-header [{:keys [title]}]
[rn/view {:style {:border-top-width 1 :border-top-color quo2.colors/neutral-20 :padding-vertical 8 :padding-horizontal 20 :margin-top 8}}
[rn/text {:style (merge typography/font-medium typography/paragraph-2 {:color quo2.colors/neutral-50})} title]])
(defn chats []
(let [{:keys [items search-filter]} (<sub [:home-items])
current-active-tab @selected-tab
items (prepare-items current-active-tab items)
contacts (<sub [:contacts/active])
contacts (prepare-contacts contacts)]
[rn/view {:style {:flex 1}}
[quo2.tabs/tabs {:style {:margin-left 20 :margin-bottom 20} :size 32
:on-change #(reset! selected-tab %)
:default-active selected-tab
:data [{:id :recent
:label (i18n/label :t/recent)}
{:id :groups
:label (i18n/label :t/groups)}
{:id :contacts
:label (i18n/label :t/contacts)}]}]
(if (and (empty? items)
(empty? search-filter)
(not @search-active?))
[welcome-blank-chats]
(if (not= current-active-tab :contacts)
[list/flat-list
{:key-fn chat-list-key-fn
:getItemLayout get-item-layout
:on-end-reached #(re-frame/dispatch [:chat.ui/show-more-chats])
:keyboard-should-persist-taps :always
:data items
:render-fn render-fn}]
[list/section-list
{:key-fn :title
:sticky-section-headers-enabled false
:sections contacts
:render-section-header-fn contacts-section-header
:render-fn render-contact}]))]))
(views/defview chats-list []
(views/letsubs [loading? [:chats/loading?]]
[:<>
[connectivity/loading-indicator]
(if loading?
[rn/view {:flex 1 :align-items :center :justify-content :center}
[rn/activity-indicator {:animating true}]]
[chats])]))
(views/defview plus-button []
(views/letsubs [logging-in? [:multiaccounts/login]]
[components.plus-button/plus-button
{:on-press (when-not logging-in?
#(re-frame/dispatch [:bottom-sheet/show-sheet :add-new {}]))
:loading logging-in?
:accessibility-label :new-chat-button}]))
(views/defview notifications-button []
(views/letsubs [notif-count [:activity.center/notifications-count]]
[rn/view
[quo2.button/button {:type :grey
:size 32
:width 32
:style {:margin-left 12}
:accessibility-label :notifications-button
:on-press #(do
(re-frame/dispatch [:mark-all-activity-center-notifications-as-read])
(if config/new-activity-center-enabled?
(re-frame/dispatch [:navigate-to :activity-center])
(re-frame/dispatch [:navigate-to :notifications-center])))}
[icons/icon :main-icons/notification2 {:color (quo2.colors/theme-colors quo2.colors/neutral-100 quo2.colors/white)}]]
(when (pos? notif-count)
[rn/view {:style (merge (styles/counter-public-container) {:top 5 :right 5})
:pointer-events :none}
[rn/view {:style styles/counter-public
:accessibility-label :notifications-unread-badge}]])]))
(defn qr-button []
[quo2.button/button {:type :grey
:accessibility-label "qr-button"
:size 32
:width 32
:style {:margin-left 12}
:on-press #(do
(re-frame/dispatch [::qr-scanner/scan-code
{:handler ::qr-scanner/on-scan-success}]))}
[icons/icon :main-icons/qr2 {:color (quo2.colors/theme-colors quo2.colors/neutral-100 quo2.colors/white)}]])
(defn scan-button []
[quo2.button/button {:type :grey
:size 32
:width 32
:accessibility-label "scan-button"
:on-press #(do
(re-frame/dispatch [::qr-scanner/scan-code
{:handler ::qr-scanner/on-scan-success}]))}
[icons/icon :main-icons/scan2 {:color (quo2.colors/theme-colors quo2.colors/neutral-100 quo2.colors/white)}]])
(views/defview profile-button []
(views/letsubs [{:keys [public-key preferred-name emoji]} [:multiaccount]]
[rn/view
[chat-icon/emoji-chat-icon-view public-key false preferred-name emoji
{:size 28
:chat-icon chat-icon.styles/chat-icon-chat-list}]]))
(defn home []
[rn/keyboard-avoiding-view {:style {:flex 1
:background-color (quo2.colors/theme-colors quo2.colors/neutral-5 quo2.colors/neutral-95)}
:ignore-offset true}
[topbar/topbar {:navigation :none
:use-insets true
:background (quo2.colors/theme-colors quo2.colors/neutral-5 quo2.colors/neutral-95)
:left-component [rn/view {:flex-direction :row :margin-left 20}
[profile-button]]
:right-component [rn/view {:flex-direction :row :margin-right 20}
[scan-button]
[qr-button]
[notifications-button]]
:border-bottom false}]
[rn/view {:flex-direction :row
:justify-content :space-between
:align-items :center
:margin-horizontal 20
:margin-top 15
:margin-bottom 8}
[quo2.text/text {:size :heading-1 :weight :semi-bold} (i18n/label :t/messages)]
[plus-button]]
[chats-list]
[tabbar/tabs-counts-subscriptions]])

View File

@ -33,7 +33,8 @@
[status-im.utils.security :as security]
[quo2.foundations.typography :as typography]
[quo2.foundations.colors :as quo2.colors]
[status-im.ui2.screens.chat.components.reply :as reply])
[status-im.ui2.screens.chat.components.reply :as reply]
[quo.react-native :as rn])
(:require-macros [status-im.utils.views :refer [defview letsubs]]))
(defn message-timestamp-anim
@ -62,7 +63,7 @@
(defn message-status [{:keys [outgoing content outgoing-status pinned edited-at in-popover?]}]
(when-not in-popover? ;; We keep track if showing this message in a list in pin-limit-popover
[react/view
[rn/view
{:align-self :flex-end
:position :absolute
:bottom 9 ; 6 Bubble bottom, 3 message baseline
@ -80,22 +81,22 @@
:height 12
:color (if pinned colors/gray colors/white)
:accessibility-label (name outgoing-status)}])
(when edited-at [react/text {:style (style/message-status-text)} edited-at-text])]))
(when edited-at [rn/text {:style (style/message-status-text)} edited-at-text])]))
(defn message-timestamp
[{:keys [timestamp-str in-popover?]} show-timestamp?]
(when-not in-popover? ;; We keep track if showing this message in a list in pin-limit-popover
(let [anim-opacity (animation/create-value 0)]
[react/animated-view {:style (style/message-timestamp-wrapper) :opacity anim-opacity}
[rn/animated-view {:style (style/message-timestamp-wrapper) :opacity anim-opacity}
(when @show-timestamp? (message-timestamp-anim anim-opacity show-timestamp?))
[react/text
[rn/text
{:style (style/message-timestamp-text)
:accessibility-label :message-timestamp}
timestamp-str]])))
(defview quoted-message
[_ reply pin?]
[react/view {:style (when-not pin? (style/quoted-message-container))}
[rn/view {:style (when-not pin? (style/quoted-message-container))}
[reply/reply-message reply false pin?]])
(defn system-text? [content-type]
@ -113,20 +114,20 @@
literal])
"emph"
(conj acc [react/text-class (style/emph-style) literal])
(conj acc [rn/text (style/emph-style) literal])
"strong"
(conj acc [react/text-class (style/strong-style) literal])
(conj acc [rn/text (style/strong-style) literal])
"strong-emph"
(conj acc [quo/text (style/strong-emph-style) literal])
"del"
(conj acc [react/text-class (style/strikethrough-style) literal])
(conj acc [rn/text (style/strikethrough-style) literal])
"link"
(conj acc
[react/text-class
[rn/text
{:style
{:color colors/blue
:text-decoration-line :underline}
@ -139,15 +140,15 @@
"mention"
(conj acc
[react/view {:style {:background-color quo2.colors/primary-50-opa-10 :border-radius 6 :padding-horizontal 3}}
[react/text-class
[rn/view {:style {:background-color quo2.colors/primary-50-opa-10 :border-radius 6 :padding-horizontal 3}}
[rn/text
{:style (merge {:color (if (system-text? content-type) colors/black quo2.colors/primary-50)}
(if (system-text? content-type) typography/font-regular typography/font-medium))
:on-press (when-not (system-text? content-type)
#(>evt [:chat.ui/show-profile literal]))}
[mention-element literal]]])
"status-tag"
(conj acc [react/text-class
(conj acc [rn/text
{:style {:color colors/blue
:text-decoration-line :underline}
:on-press
@ -165,16 +166,16 @@
"paragraph"
(conj acc (reduce
(fn [acc e] (render-inline (:text content) content-type acc e))
[react/text-class (style/text-style content-type in-popover?)]
[rn/text (style/text-style content-type in-popover?)]
children))
"blockquote"
(conj acc [react/view (style/blockquote-style)
[react/text-class (style/blockquote-text-style)
(conj acc [rn/view (style/blockquote-style)
[rn/text (style/blockquote-text-style)
(.substring literal 0 (.-length literal))]])
"codeblock"
(conj acc [react/view {:style style/codeblock-style}
(conj acc [rn/view {:style style/codeblock-style}
[quo/text {:max-font-size-multiplier react/max-font-size-multiplier
:style style/codeblock-text-style
:monospace true}
@ -187,13 +188,13 @@
(defn render-parsed-text-with-message-status [{:keys [edited-at in-popover?] :as message} tree]
(let [elements (render-parsed-text message tree)
message-status [react/text {:style (style/message-status-placeholder)}
message-status [rn/text {:style (style/message-status-placeholder)}
(str (if (not in-popover?) " " " ") (when (and (not in-popover?) edited-at) edited-at-text))]
last-element (peek elements)]
;; 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
(if (= react/text-class (nth last-element 0))
(if (= rn/text (nth last-element 0))
;; Append message status to last text
(conj (pop elements) (conj last-element message-status))
;; Append message status to new block
@ -201,8 +202,8 @@
(defn unknown-content-type
[{:keys [content-type content] :as message}]
[react/view (style/message-view message)
[react/text
[rn/view (style/message-view message)
[rn/text
{:style {:color colors/white-persist}}
(if (seq (:text content))
(:text content)
@ -210,7 +211,7 @@
(defn message-not-sent-text
[chat-id message-id]
[react/touchable-highlight
[rn/touchable-highlight
{:on-press
(fn []
(re-frame/dispatch
@ -218,10 +219,10 @@
{:content (sheets/options chat-id message-id)
:content-height 200}])
(react/dismiss-keyboard!))}
[react/view style/not-sent-view
[react/text {:style style/not-sent-text}
[rn/view style/not-sent-view
[rn/text {:style style/not-sent-text}
(i18n/label :t/status-not-sent-tap)]
[react/view style/not-sent-icon
[rn/view style/not-sent-icon
[icons/icon :main-icons/warning {:color colors/red}]]]])
(defn pin-author-name [pinned-by]
@ -240,8 +241,8 @@
:width pin-icon-width}])
(defn pinned-by-indicator [pinned-by]
[react/view {:style (style/pin-indicator)
:accessibility-label :pinned-by}
[rn/view {:style (style/pin-indicator)
:accessibility-label :pinned-by}
[pin-icon]
[quo/text {:size :small
:color :main
@ -266,32 +267,32 @@
(letsubs [{:keys [name description verified] :as community} [:communities/community community-id]
communities-enabled? [:communities/enabled?]]
(when (and communities-enabled? community)
[react/view {:style (assoc (style/message-wrapper message)
:margin-vertical 10
:margin-left 8
:width 271)}
[rn/view {:style (assoc (style/message-wrapper message)
:margin-vertical 10
:margin-left 8
:width 271)}
(when verified
[react/view (style/community-verified)
[react/text {:style {:font-size 13
:color colors/blue}} (i18n/label :t/communities-verified)]])
[react/view (style/community-message verified)
[react/view {:width 62
:padding-left 14}
[rn/view (style/community-verified)
[rn/text {:style {:font-size 13
:color colors/blue}} (i18n/label :t/communities-verified)]])
[rn/view (style/community-message verified)
[rn/view {:width 62
:padding-left 14}
(if (= community-id constants/status-community-id)
[react/image {:source (resources/get-image :status-logo)
:style {:width 40
:height 40}}]
[rn/image {:source (resources/get-image :status-logo)
:style {:width 40
:height 40}}]
[communities.icon/community-icon community])]
[react/view {:padding-right 14 :flex 1}
[react/text {:style {:font-weight "700" :font-size 17}}
[rn/view {:padding-right 14 :flex 1}
[rn/text {:style {:font-weight "700" :font-size 17}}
name]
[react/text description]]]
[react/view (style/community-view-button)
[react/touchable-highlight {:on-press #(re-frame/dispatch [:navigate-to
:community
{:community-id (:id community)}])}
[react/text {:style {:text-align :center
:color colors/blue}} (i18n/label :t/view)]]]])))
[rn/text description]]]
[rn/view (style/community-view-button)
[rn/touchable-highlight {:on-press #(re-frame/dispatch [:navigate-to
:community
{:community-id (:id community)}])}
[rn/text {:style {:text-align :center
:color colors/blue}} (i18n/label :t/view)]]]])))
(defn message-content-wrapper
"Author, userpic and delivery wrapper"
@ -301,27 +302,27 @@
deleted-for-me? pinned]
:as message} content {:keys [modal close-modal]}]
(let [response-to (:response-to (:content message))]
[react/view {:style (style/message-wrapper message)
:pointer-events :box-none
:accessibility-label :chat-item}
[rn/view {:style (style/message-wrapper message)
:pointer-events :box-none
:accessibility-label :chat-item}
(when (and (seq response-to) (:quoted-message message))
[quoted-message response-to (:quoted-message message)])
[react/view {:style (style/message-body)
:pointer-events :box-none}
[react/view (style/message-author-userpic)
[rn/view {:style (style/message-body)
:pointer-events :box-none}
[rn/view (style/message-author-userpic)
(when (or (and (seq response-to) (:quoted-message message)) last-in-group? pinned)
[react/touchable-highlight {:on-press #(do (when modal (close-modal))
(re-frame/dispatch [:chat.ui/show-profile from]))}
[rn/touchable-highlight {:on-press #(do (when modal (close-modal))
(re-frame/dispatch [:chat.ui/show-profile from]))}
[photos/member-photo from identicon]])]
[react/view {:style (style/message-author-wrapper)}
[rn/view {:style (style/message-author-wrapper)}
(when (or (and (seq response-to) (:quoted-message message)) last-in-group? pinned)
[react/view {:style {:flex-direction :row :align-items :center}}
[react/touchable-opacity {:style style/message-author-touchable
:disabled in-popover?
:on-press #(do (when modal (close-modal))
(re-frame/dispatch [:chat.ui/show-profile from]))}
[rn/view {:style {:flex-direction :row :align-items :center}}
[rn/touchable-opacity {:style style/message-author-touchable
:disabled in-popover?
:on-press #(do (when modal (close-modal))
(re-frame/dispatch [:chat.ui/show-profile from]))}
[message-author-name from {:modal modal}]]
[react/text
[rn/text
{:style (merge
{:padding-left 5
:margin-top 2}
@ -331,13 +332,13 @@
;; MESSAGE CONTENT
;; TODO(yqrashawn): wait for system message component to display deleted for me UI
(if deleted-for-me?
[react/view {:style {:border-width 2
:border-color :red}}
[rn/view {:style {:border-width 2
:border-color :red}}
content]
content)
[link-preview/link-preview-wrapper (:links (:content message)) false false]]]
; delivery status
[react/view (style/delivery-status)
[rn/view (style/delivery-status)
[message-delivery-status message]]]))
(def image-max-width 260)
@ -370,25 +371,25 @@
:visible @visible
:on-close #(do (reset! visible false)
(reagent/flush))}]
[react/touchable-highlight {:on-press (fn []
(reset! visible true)
(react/dismiss-keyboard!))
:on-long-press @on-long-press
:disabled in-popover?}
[react/view {:style (style/image-message style-opts)
:accessibility-label :image-message}
[rn/touchable-highlight {:on-press (fn []
(reset! visible true)
(react/dismiss-keyboard!))
:on-long-press @on-long-press
:disabled in-popover?}
[rn/view {:style (style/image-message style-opts)
:accessibility-label :image-message}
(when (or (:error @dimensions) (not (:loaded @dimensions)))
[react/view
[rn/view
(merge (dissoc style-opts :opacity)
{:flex 1 :align-items :center :justify-content :center :position :absolute})
(if (:error @dimensions)
[icons/icon :main-icons/cancel]
[react/activity-indicator {:animating true}])])
[rn/activity-indicator {:animating true}])])
[fast-image/fast-image {:style (dissoc style-opts :outgoing)
:on-load (image-set-size dimensions)
:on-error #(swap! dimensions assoc :error true)
:source {:uri uri}}]
[react/view {:style (style/image-message-border style-opts)}]]]]))))
[rn/view {:style (style/image-message-border style-opts)}]]]]))))
(defmulti ->message :content-type)
@ -401,10 +402,10 @@
[message.gap/gap message])
(defmethod ->message constants/content-type-system-text [{:keys [content] :as message}]
[react/view {:accessibility-label :chat-item}
[react/view (style/system-message-body message)
[react/view (style/message-view message)
[react/view (style/message-view-content)
[rn/view {:accessibility-label :chat-item}
[rn/view (style/system-message-body message)
[rn/view (style/message-view message)
[rn/view (style/message-view-content)
[render-parsed-text message (:parsed-text content)]]]]])
(defn pin-message [{:keys [chat-id pinned] :as message}]
@ -469,7 +470,7 @@
(js/setTimeout #(on-long-press-fn on-long-press message content) 200))
(on-long-press-fn on-long-press message content)))]
(reset! ref on-long-press)
[react/touchable-highlight
[rn/touchable-highlight
(when-not modal
{:on-press (fn [_]
(react/dismiss-keyboard!)
@ -477,11 +478,11 @@
:delay-long-press 100
:on-long-press on-long-press
:disabled in-popover?})
[react/view style/message-view-wrapper
[rn/view style/message-view-wrapper
[message-timestamp message show-timestamp?]
[react/view {:style (style/message-view message)}
[react/view {:style (style/message-view-content)}
[react/view
[rn/view {:style (style/message-view message)}
[rn/view {:style (style/message-view-content)}
[rn/view
[render-parsed-text-with-message-status message (:parsed-text content)]]]]]]))))
(defmethod ->message constants/content-type-text
@ -492,24 +493,24 @@
(defmethod ->message constants/content-type-pin [{:keys [from in-popover? timestamp-str] :as message} {:keys [modal close-modal]}]
(let [response-to (:response-to (:content message))]
[react/view {:style (merge {:flex-direction :row :margin-vertical 8} (style/message-wrapper message))}
[react/view {:style {:width photos.style/default-size
:height photos.style/default-size
:margin-horizontal 8
:border-radius photos.style/default-size
:justify-content :center
:align-items :center
:background-color quo2.colors/primary-50-opa-10}}
[rn/view {:style (merge {:flex-direction :row :margin-vertical 8} (style/message-wrapper message))}
[rn/view {:style {:width photos.style/default-size
:height photos.style/default-size
:margin-horizontal 8
:border-radius photos.style/default-size
:justify-content :center
:align-items :center
:background-color quo2.colors/primary-50-opa-10}}
[pin-icon]]
[react/view
[react/view {:style {:flex-direction :row :align-items :center}}
[react/touchable-opacity {:style style/message-author-touchable
:disabled in-popover?
:on-press #(do (when modal (close-modal))
(re-frame/dispatch [:chat.ui/show-profile from]))}
[rn/view
[rn/view {:style {:flex-direction :row :align-items :center}}
[rn/touchable-opacity {:style style/message-author-touchable
:disabled in-popover?
:on-press #(do (when modal (close-modal))
(re-frame/dispatch [:chat.ui/show-profile from]))}
[message-author-name from {:modal modal}]]
[react/text {:style {:font-size 13}} (str " " (i18n/label :pinned-a-message))]
[react/text
[rn/text {:style {:font-size 13}} (str " " (i18n/label :pinned-a-message))]
[rn/text
{:style (merge
{:padding-left 5
:margin-top 2}
@ -525,11 +526,11 @@
(defmethod ->message constants/content-type-status
[{:keys [content content-type] :as message}]
[message-content-wrapper message
[react/view style/status-container
[react/text {:style (style/status-text)}
[rn/view style/status-container
[rn/text {:style (style/status-text)}
(reduce
(fn [acc e] (render-inline (:text content) content-type acc e))
[react/text-class {:style (style/status-text)}]
[rn/text {:style (style/status-text)}]
(-> content :parsed-text peek :children))]]])
(defmethod ->message constants/content-type-emoji []
@ -557,19 +558,19 @@
:label (if pinned (i18n/label :t/unpin) (i18n/label :t/pin))}]))))]
(reset! ref on-long-press)
[message-content-wrapper message
[react/touchable-highlight (when-not modal
{:disabled in-popover?
:on-press (fn []
(react/dismiss-keyboard!)
(reset! show-timestamp? true))
:delay-long-press 100
:on-long-press on-long-press})
[react/view style/message-view-wrapper
[rn/touchable-highlight (when-not modal
{:disabled in-popover?
:on-press (fn []
(react/dismiss-keyboard!)
(reset! show-timestamp? true))
:delay-long-press 100
:on-long-press on-long-press})
[rn/view style/message-view-wrapper
[message-timestamp message show-timestamp?]
[react/view (style/message-view message)
[react/view {:style (style/message-view-content)}
[react/view {:style (style/style-message-text)}
[react/text {:style (style/emoji-message message)}
[rn/view (style/message-view message)
[rn/view {:style (style/message-view-content)}
[rn/view {:style (style/style-message-text)}
[rn/text {:style (style/emoji-message message)}
(:text content)]]
[message-status message]]]]]
reaction-picker]))))
@ -590,15 +591,15 @@
:label (i18n/label :t/see-sticker-set)}])))]
(reset! ref on-long-press)
[message-content-wrapper message
[react/touchable-highlight (when-not modal
{:disabled in-popover?
:accessibility-label :sticker-message
:on-press (fn [_]
(when pack
(re-frame/dispatch [:stickers/open-sticker-pack (str pack)]))
(react/dismiss-keyboard!))
:delay-long-press 100
:on-long-press on-long-press})
[rn/touchable-highlight (when-not modal
{:disabled in-popover?
:accessibility-label :sticker-message
:on-press (fn [_]
(when pack
(re-frame/dispatch [:stickers/open-sticker-pack (str pack)]))
(react/dismiss-keyboard!))
:delay-long-press 100
:on-long-press on-long-press})
[fast-image/fast-image {:style {:margin 10 :width 140 :height 140}
:source {:uri (str (-> content :sticker :url) "&download=true")}}]]
reaction-picker]))
@ -677,27 +678,27 @@
:id :delete})]))]
(reset! ref on-long-press)
[message-content-wrapper message
[react/touchable-highlight
[rn/touchable-highlight
(when-not modal
{:on-long-press on-long-press
:on-press (fn []
(reset! show-timestamp? true))})
[react/view style/message-view-wrapper
[rn/view style/message-view-wrapper
[message-timestamp message show-timestamp?]
[react/view {:style (style/message-view message) :accessibility-label :audio-message}
[react/view {:style (style/message-view-content)}
[rn/view {:style (style/message-view message) :accessibility-label :audio-message}
[rn/view {:style (style/message-view-content)}
[message.audio/message-content message] [message-status message]]]]]
reaction-picker]))))
(defn contact-request-status-pending []
[react/view {:style {:flex-direction :row}}
[rn/view {:style {:flex-direction :row}}
[quo/text {:style {:margin-right 5.27}
:weight :medium
:color :secondary}
(i18n/label :t/contact-request-pending)]
[react/activity-indicator {:animating true
:size :small
:color colors/gray}]])
[rn/activity-indicator {:animating true
:size :small
:color colors/gray}]])
(defn contact-request-status-accepted []
[quo/text {:style {:color colors/green}
@ -710,7 +711,7 @@
(i18n/label :t/contact-request-declined)])
(defn contact-request-status-label [state]
[react/view {:style (style/contact-request-status-label state)}
[rn/view {:style (style/contact-request-status-label state)}
(case state
constants/contact-request-message-state-pending [contact-request-status-pending]
constants/contact-request-message-state-accepted [contact-request-status-accepted]
@ -718,15 +719,15 @@
(defmethod ->message constants/content-type-contact-request
[message _]
[react/view {:style (style/content-type-contact-request)}
[react/image {:source (resources/get-image :hand-wave)
:style {:width 112
:height 97}}]
[rn/view {:style (style/content-type-contact-request)}
[rn/image {:source (resources/get-image :hand-wave)
:style {:width 112
:height 97}}]
[quo/text {:style {:margin-top 6}
:weight :bold
:size :large}
(i18n/label :t/contact-request)]
[react/view {:style {:padding-horizontal 16}}
[rn/view {:style {:padding-horizontal 16}}
[quo/text {:style {:margin-top 2
:margin-bottom 14}}
(get-in message [:content :text])]]
@ -763,9 +764,9 @@
(into #{} (js->clj own-reactions))
#(on-emoji-press %))}]))
on-long-press (atom nil)]
[react/view {:style (merge (when (or mentioned pinned) {:background-color quo2.colors/primary-50-opa-5 :border-radius 16 :margin-bottom 4}) {:margin-horizontal 8})}
[rn/view {:style (merge (when (or mentioned pinned) {:background-color quo2.colors/primary-50-opa-5 :border-radius 16 :margin-bottom 4}) {:margin-horizontal 8})}
(when pinned
[react/view {:style (style/pin-indicator-container)}
[rn/view {:style (style/pin-indicator-container)}
[pinned-by-indicator pinned-by]])
[->message message {:ref on-long-press
:modal false

View File

@ -82,4 +82,4 @@
(react/hw-back-remove-listener navigate-back-handler)
(react/hw-back-add-listener navigate-back-handler))
:component-will-unmount (fn [] (react/hw-back-remove-listener navigate-back-handler))
:reagent-render chat-render}))
:reagent-render chat-render}))

View File

@ -1798,7 +1798,9 @@
"kicked": "Kicked",
"delete-for-everyone": "Delete for everyone",
"pin-to-chat": "Pin to the chat",
"pin-to-channel": "Pin to the channel",
"unpin-from-chat": "Unpin from the chat",
"unpin-from-channel": "Unpin from the channel",
"copy-text": "Copy text",
"edit-message": "Edit message",
"save-image-library": "Save image to library",
@ -1809,5 +1811,7 @@
"replies": "Replies",
"identity-verification": "Identity verification",
"membership": "Membership",
"jump-to": "Jump to"
"jump-to": "Jump to",
"blank-messages-text": "Your messages will be here",
"groups": "Groups"
}