Messages home items (#14256)

* feat: messages home items
This commit is contained in:
Omar Basem 2022-11-08 13:01:02 +04:00 committed by GitHub
parent 187d147dc7
commit f17f57cc5e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 291 additions and 95 deletions

View File

@ -4,7 +4,8 @@
[quo2.foundations.colors :as colors]
[quo2.components.icon :as icons]
[clojure.string :refer [upper-case split blank?]]
[quo.theme :refer [dark?]]))
[quo.theme :refer [dark?]]
[status-im.ui.components.fast-image :as fast-image]))
(def sizes {:big {:outer 80
:inner 72
@ -131,8 +132,8 @@
:no-color true}])
(if profile-picture
;; display image
[rn/image {:style (container-styling inner-dimensions outer-dimensions)
:source profile-picture}]
[fast-image/fast-image {:source {:uri profile-picture}
:style (container-styling inner-dimensions outer-dimensions)}]
;; else display initials
[container inner-dimensions outer-dimensions
[text/text {:weight :semi-bold

View File

@ -1,4 +1,4 @@
(ns quo2.components.list-items.received-contact-request
(ns quo2.components.list-items.received-cr-item
(:require [quo.react-native :as rn]
[quo2.foundations.colors :as colors]
[status-im.utils.handlers :refer [<sub >evt]]
@ -10,17 +10,9 @@
[status-im.i18n.i18n :as i18n]
[quo2.components.notifications.notification-dot :refer [notification-dot]]))
(defn get-display-name [chat-id no-ens-name no-nickname]
(let [name (first (<sub [:contacts/contact-two-names-by-identity chat-id]))]
(if (and no-ens-name no-nickname)
(let [[word1 word2] (str/split name " ")]
(str word1 " " word2))
name)))
(defn list-item [{:keys [chat-id image contact message timestamp read]}]
(defn received-cr-item [{:keys [chat-id message timestamp read]}]
(let [no-ens-name (str/blank? (get-in message [:content :ens-name]))
no-nickname (nil? (get-in contact [:names :nickname]))
display-name (get-display-name chat-id no-ens-name no-nickname)]
display-name (first (<sub [:contacts/contact-two-names-by-identity chat-id]))]
[rn/view {:style {:flex-direction :row
:padding-top 8
:margin-top 4
@ -32,7 +24,6 @@
:status-indicator? true
:online? true
:size :small
:profile-picture image
:ring? false}]
[rn/view {:style {:margin-horizontal 8}}
[rn/view {:style {:flex-direction :row}}
@ -82,3 +73,5 @@
:padding-horizontal 8
:margin-left 8}}
[rn/text {:style (merge typography/font-medium typography/paragraph-2 {:color colors/white})} (i18n/label :t/accept)]]]]]))

View File

@ -0,0 +1,15 @@
(ns quo2.components.notifications.info-count
(:require [quo.react-native :as rn]
[quo2.foundations.colors :as colors]
[quo2.foundations.typography :as typography]))
(defn info-count [count style]
(when (> count 0)
[rn/view {:style (merge {:width 16
:height 16
:position :absolute
:right 22
:border-radius 6
:background-color (colors/theme-colors colors/primary-50 colors/primary-60)}
style)}
[rn/text {:style (merge typography/font-medium typography/label {:color colors/white :text-align :center})} count]]))

View File

@ -410,4 +410,4 @@
:<- [:chats/current-chat-id]
:<- [:chat/inputs-with-mentions]
(fn [[chat-id cursor]]
(get cursor chat-id)))
(get cursor chat-id)))

View File

@ -89,4 +89,4 @@
{:names names
:multiaccount multiaccount
:preferred-name preferred-name
:registrations registrations}))
:registrations registrations}))

View File

@ -158,7 +158,7 @@
[channel-list-component]]])
(defn overview []
(let [community-mock (<sub [:get-screen-params :community-overview]) ;;TODO stop using mock data and only pass community id
(let [community-mock (<sub [:get-screen-params :community-overview]) ;;TODO stop using mock data and only pass community id
community (<sub [:communities/community (:id community-mock)])]
[rn/view {:style
{:height "100%"}}

View File

@ -0,0 +1,3 @@
(ns status-im.ui2.screens.chat.components.contact-item.test)
;; TODO: tests to be added when RNTL is integrated into our project

View File

@ -0,0 +1,51 @@
(ns status-im.ui2.screens.chat.components.contact-item.view
(:require [status-im.utils.handlers :refer [<sub >evt]]
[quo2.foundations.typography :as typography]
[quo2.components.icon :as icons]
[quo2.foundations.colors :as colors]
[quo2.components.avatars.user-avatar :as user-avatar]
[quo.react-native :as rn]
[status-im.utils.utils :refer [get-shortened-address]]
[quo.platform :as platform]
[quo2.components.markdown.text :as text]
[status-im.ui2.screens.chat.components.message-home-item.style :as style]))
(defn open-chat [chat-id]
(>evt [:dismiss-keyboard])
(if platform/android?
(>evt [:chat.ui/navigate-to-chat-nav2 chat-id])
(>evt [:chat.ui/navigate-to-chat chat-id]))
(>evt [:search/home-filter-changed nil])
(>evt [:accept-all-activity-center-notifications-from-chat chat-id]))
(defn contact-item [item]
(let [{:keys [public-key ens-verified added? images]} item
display-name (first (<sub [:contacts/contact-two-names-by-identity public-key]))
photo-path (if-not (empty? images) (<sub [:chats/photo-path public-key]) nil)]
[rn/touchable-opacity (merge {:style (style/container)
:on-press #(open-chat public-key)})
[user-avatar/user-avatar {:full-name display-name
:profile-picture photo-path
:status-indicator? true
:online? true
:size :small
:ring? false}]
[rn/view {:style {:margin-left 8}}
[rn/view {:style {:flex-direction :row}}
[text/text {:style (merge typography/paragraph-1 typography/font-semi-bold
{:color (colors/theme-colors colors/neutral-100 colors/white)})}
display-name]
(if ens-verified
[rn/view {:style {:margin-left 5 :margin-top 4}}
[icons/icon :main-icons2/verified {:no-color true :size 12 :color (colors/theme-colors colors/success-50 colors/success-60)}]]
(when added?
[rn/view {:style {:margin-left 5 :margin-top 4}}
[icons/icon :main-icons2/contact {:no-color true :size 12 :color (colors/theme-colors colors/primary-50 colors/primary-60)}]]))]
[text/text {:size :paragraph-1
:style {:color (colors/theme-colors colors/neutral-50 colors/neutral-40)}}
(get-shortened-address public-key)]]
[rn/touchable-opacity {:style {:position :absolute
:right 20}
:active-opacity 1} ; TODO: on-long-press to be added when contact bottom sheet is implemented
[icons/icon :main-icons2/options {:size 20 :color (colors/theme-colors colors/primary-50 colors/primary-60)}]]]))

View File

@ -0,0 +1,35 @@
(ns status-im.ui2.screens.chat.components.message-home-item.style
(:require [quo2.foundations.colors :as colors]
[quo2.foundations.typography :as typography]))
(defn container []
{:margin-top 8
:margin-horizontal 8
:padding-vertical 8
:padding-horizontal 12
:border-radius 12
:flex-direction :row
:align-items :center})
(defn group-chat-icon [color]
{:width 32
:height 32
:background-color color
:justify-content :center
:align-items :center
:border-radius 16})
(defn count-container []
{:width 8
:height 8
:border-radius 4
:position :absolute
:right 26
:top 16
:background-color (colors/theme-colors colors/neutral-40 colors/neutral-60)})
(defn timestamp []
(merge typography/font-regular typography/label
{:color (colors/theme-colors colors/neutral-50 colors/neutral-40)
:margin-top 3
:margin-left 8}))

View File

@ -0,0 +1,3 @@
(ns status-im.ui2.screens.chat.components.message-home-item.test)
;; TODO: tests to be added when RNTL is integrated into our project

View File

@ -0,0 +1,124 @@
(ns status-im.ui2.screens.chat.components.message-home-item.view
(:require [clojure.string :as string]
[status-im.utils.handlers :refer [<sub >evt]]
[status-im.utils.datetime :as time]
[quo2.foundations.typography :as typography]
[quo2.components.notifications.info-count :refer [info-count]]
[quo2.components.icon :as icons]
[quo2.foundations.colors :as colors]
[quo2.components.avatars.user-avatar :as user-avatar]
[quo.react-native :as rn]
[status-im.ui.screens.chat.sheets :as sheets]
[quo.platform :as platform]
[quo2.components.markdown.text :as text]
[status-im.ui2.screens.chat.components.message-home-item.style :as style]))
(def max-subheader-length 50)
(defn truncate-literal [literal]
(when literal
(let [size (min max-subheader-length (.-length literal))]
{:components (.substring literal 0 size)
:length size})))
(defn add-parsed-to-subheader [acc {:keys [type destination literal children]}]
(let [result (case type
"paragraph"
(reduce
(fn [{:keys [_ length] :as acc-paragraph} parsed-child]
(if (>= length max-subheader-length)
(reduced acc-paragraph)
(add-parsed-to-subheader acc-paragraph parsed-child)))
{:components [rn/text]
:length 0}
children)
"mention"
{:components [rn/text (<sub [:contacts/contact-name-by-identity literal])]
:length 4} ;; we can't predict name length so take the smallest possible
"status-tag"
(truncate-literal (str "#" literal))
"link"
(truncate-literal destination)
(truncate-literal literal))]
{:components (conj (:components acc) (:components result))
:length (+ (:length acc) (:length result))}))
(defn render-subheader
"Render the preview of a last message to a maximum of max-subheader-length characters"
[parsed-text]
(let [result
(reduce
(fn [{:keys [_ length] :as acc-text} new-text-chunk]
(if (>= length max-subheader-length)
(reduced acc-text)
(add-parsed-to-subheader acc-text new-text-chunk)))
{:components [rn/text {:style (merge typography/paragraph-2 typography/font-regular
{:color (colors/theme-colors colors/neutral-50 colors/neutral-40)
:width "90%"})
:number-of-lines 1
:ellipsize-mode :tail
:accessibility-label :chat-message-text}]
:length 0}
parsed-text)]
(:components result)))
(defn display-name-view [display-name contact timestamp]
[rn/view {:style {:flex-direction :row}}
[text/text {:weight :semi-bold
:size :paragraph-1}
display-name]
(if (:ens-verified contact)
[rn/view {:style {:margin-left 5 :margin-top 4}}
[icons/icon :main-icons2/verified {:no-color true
:size 12
:color (colors/theme-colors colors/success-50 colors/success-60)}]]
(when (:added? contact)
[rn/view {:style {:margin-left 5 :margin-top 4}}
[icons/icon :main-icons2/contact {:no-color true
:size 12
:color (colors/theme-colors colors/primary-50 colors/primary-60)}]]))
[text/text {:style (style/timestamp)}
(time/to-short-str timestamp)]])
(defn messages-home-item [item]
(let [{:keys [chat-id color group-chat last-message timestamp name unviewed-mentions-count unviewed-messages-count]} item
display-name (if-not group-chat (first (<sub [:contacts/contact-two-names-by-identity chat-id])) name)
contact (when-not group-chat (<sub [:contacts/contact-by-address chat-id]))
photo-path (when-not (empty? (:images contact)) (<sub [:chats/photo-path chat-id]))]
[rn/touchable-opacity (merge {:style (style/container)
:on-press (fn []
(>evt [:dismiss-keyboard])
(if platform/android?
(>evt [:chat.ui/navigate-to-chat-nav2 chat-id])
(>evt [:chat.ui/navigate-to-chat chat-id]))
(>evt [:search/home-filter-changed nil])
(>evt [:accept-all-activity-center-notifications-from-chat chat-id]))
:on-long-press #(>evt [:bottom-sheet/show-sheet
{:content (fn [] [sheets/actions item])}])})
(if group-chat
[rn/view {:style (style/group-chat-icon color)}
[icons/icon :main-icons2/group {:size 16 :color colors/white-opa-70}]]
[user-avatar/user-avatar {:full-name display-name
:profile-picture photo-path
:status-indicator? true
:online? true
:size :small
:ring? false}])
[rn/view {:style {:margin-left 8}}
[display-name-view display-name contact timestamp]
(if (string/blank? (get-in last-message [:content :parsed-text]))
[text/text {:size :paragraph-2
:style {:color (colors/theme-colors colors/neutral-50 colors/neutral-40)}}
(get-in last-message [:content :text])]
[render-subheader (get-in last-message [:content :parsed-text])])]
(if (> unviewed-mentions-count 0)
[info-count unviewed-mentions-count {:top 16}]
(when (> unviewed-messages-count 0)
[rn/view {:style (style/count-container)}]))]))

View File

@ -8,11 +8,8 @@
[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.ui2.screens.chat.actions :as actions]
[status-im.ui.screens.home.views.inner-item :refer [home-list-item]]
[quo.design-system.colors :as quo.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]
@ -34,11 +31,14 @@
[quo2.components.buttons.button :as quo2.button]
[quo2.components.tabs.tabs :as quo2.tabs]
[quo2.components.community.discover-card :as discover-card]
[status-im.multiaccounts.core :as multiaccounts]
[status-im.ui.components.chat-icon.screen :as chat-icon]
[quo2.components.icon :as quo2.icons]
[quo.components.safe-area :as safe-area]
[quo2.components.list-items.received-contact-request :as received-contact-request])
[quo2.components.notifications.info-count :refer [info-count]]
[quo2.components.list-items.received-cr-item :refer [received-cr-item]]
[status-im.ui2.screens.chat.components.message-home-item.view :refer [messages-home-item]]
[status-im.ui2.screens.chat.components.contact-item.view :refer [contact-item]]
[clojure.string :as str])
(:require-macros [status-im.utils.views :as views]))
(defn home-tooltip-view []
@ -66,7 +66,10 @@
[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}}
[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))
@ -117,43 +120,6 @@
(re-frame/dispatch [:set :public-group-topic nil])
(re-frame/dispatch [:search/home-filter-changed nil]))}])])))
(defn render-fn [{:keys [chat-id chat-type] :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 []
[actions/actions
chat-type
chat-id])}])}])
(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 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 TODO: new UI yet to be implemented
; {: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) (:id item)))
@ -165,7 +131,7 @@
(defn prepare-items [current-active-tab items]
(if (= current-active-tab :groups)
(filter #(-> % :group-chat (= true)) items)
items))
(filter #(-> % (contains? :chat-id)) items)))
(defn prepare-contacts [contacts]
(let [data (atom {})]
@ -183,14 +149,14 @@
(defn find-contact-requests [notifications]
(let [received-requests (atom [])
has-unread (atom false)]
has-unread? (atom false)]
(doseq [i (range (count notifications))]
(doseq [j (range (count (:data (nth notifications i))))]
(when (= 1 (get-in (nth (:data (nth notifications i)) j) [:message :contact-request-state]))
(swap! received-requests conj (nth (:data (nth notifications i)) j)))
(when (= false (get-in (nth (:data (nth notifications i)) j) [:read]))
(reset! has-unread true))))
{:received-requests @received-requests :has-unread @has-unread}))
(reset! has-unread? true))))
{:received-requests @received-requests :has-unread? @has-unread?}))
(def selected-requests-tab (reagent/atom :received))
@ -198,10 +164,10 @@
[:f>
(fn []
(let [{window-height :height} (rn/use-window-dimensions)
safe-area (safe-area/use-safe-area)
notifications (<sub [:activity.center/notifications-grouped-by-date])
safe-area (safe-area/use-safe-area)
notifications (<sub [:activity.center/notifications-grouped-by-date])
{received-requests :received-requests} (find-contact-requests notifications)
sent-requests []]
sent-requests []]
[rn/view {:style {:margin-left 20
:height (- window-height (:top safe-area))}}
[rn/touchable-opacity
@ -232,9 +198,24 @@
[list/flat-list
{:key-fn :first
:data (if (= @selected-requests-tab :received) received-requests sent-requests)
:render-fn received-contact-request/list-item}]]))])
:render-fn received-cr-item}]]))])
(defn contact-requests [count]
(defn get-display-name [{:keys [chat-id message]}]
(let [name (first (<sub [:contacts/contact-two-names-by-identity chat-id]))
no-ens-name (str/blank? (get-in message [:content :ens-name]))]
(if no-ens-name
(first (str/split name " "))
name)))
(defn requests-summary [requests]
(case (count requests)
1
(get-display-name (first requests))
2
(str (get-display-name (first requests)) " " (i18n/label :t/and) " " (get-display-name (second requests)))
(str (get-display-name (first requests)) ", " (get-display-name (second requests)) " " (i18n/label :t/and) " " (- (count requests) 2) " " (i18n/label :t/more))))
(defn contact-requests [requests]
[rn/touchable-opacity
{:active-opacity 1
:on-press #(do
@ -258,14 +239,8 @@
[rn/view {:style {:margin-left 8}}
[rn/text {:style
(merge typography/paragraph-1 typography/font-semi-bold {:color (colors/theme-colors "#000000" "#ffffff")})} (i18n/label :t/pending-requests)]
[rn/text {:style (merge typography/paragraph-2 typography/font-regular {:color (colors/theme-colors colors/neutral-50 colors/neutral-40)})} "Alice, Pedro and 3 others"]]
[rn/view {:style {:width 16
:height 16
:position :absolute
:right 22
:border-radius 6
:background-color (colors/theme-colors colors/primary-50 colors/primary-60)}}
[rn/text {:style (merge typography/font-medium typography/label {:color "#ffffff" :text-align :center})} count]]])
[rn/text {:style (merge typography/paragraph-2 typography/font-regular {:color (colors/theme-colors colors/neutral-50 colors/neutral-40)})} (requests-summary requests)]]
[info-count (count requests)]])
(defn chats []
(let [{:keys [items search-filter]} (<sub [:home-items])
@ -274,7 +249,7 @@
contacts (<sub [:contacts/active])
contacts (prepare-contacts contacts)
notifications (<sub [:activity.center/notifications-grouped-by-date])
{requests :received-requests new-info :has-unread} (find-contact-requests notifications)]
{requests :received-requests new-info :has-unread?} (find-contact-requests notifications)]
[rn/view {:style {:flex 1}}
[discover-card/discover-card {:title (i18n/label :t/invite-friends-to-status)
:description (i18n/label :t/share-invite-link)}]
@ -283,13 +258,13 @@
:margin-top 24}
:size 32
:on-change #(reset! selected-tab %)
:default-active 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)
{:id :contacts
:label (i18n/label :t/contacts)
:new-info new-info}]}]
(if (and (empty? items)
(empty? search-filter)
@ -302,15 +277,15 @@
:on-end-reached #(re-frame/dispatch [:chat.ui/show-more-chats])
:keyboard-should-persist-taps :always
:data items
:render-fn render-fn}]
:render-fn messages-home-item}]
[rn/view {:style {:flex 1}} (when (> (count requests) 0)
[contact-requests (count requests)])
[contact-requests requests])
[list/section-list
{:key-fn :title
:sticky-section-headers-enabled false
:sections contacts
:render-section-header-fn contacts-section-header
:render-fn render-contact}]]))]))
:render-fn contact-item}]]))]))
(views/defview chats-list []
(views/letsubs [loading? [:chats/loading?]]
@ -340,12 +315,7 @@
:on-press #(do
(re-frame/dispatch [:mark-all-activity-center-notifications-as-read])
(if config/new-activity-center-enabled?
(re-frame/dispatch [:show-popover {:view :activity-center
:style {:margin 0}
:disable-touchable-overlay? true
:blur-view? true
:blur-view-props {:blur-amount 20
:blur-type :dark}}])
(re-frame/dispatch [:navigate-to :activity-center])
(re-frame/dispatch [:navigate-to :notifications-center])))}
[icons/icon :main-icons/notification2 {:color (colors/theme-colors colors/neutral-100 colors/white)}]]
(when (pos? notif-count)
@ -409,3 +379,4 @@
[plus-button]]
[chats-list]
[tabbar/tabs-counts-subscriptions]])])

View File

@ -868,4 +868,3 @@
:align-items :center
:background-color colors/neutral-80-opa-5}}
[rn/text {:style (merge typography/label typography/font-medium {:color (colors/theme-colors colors/neutral-100 colors/white)})} pins-count]]])))

View File

@ -405,7 +405,7 @@
"other": "S"
},
"datetime-today": "today",
"datetime-yesterday": "yesterday",
"datetime-yesterday": "Yesterday",
"decimals": "Decimals",
"decline": "Decline",
"decryption-failed-content": "An error occured decrypting your data. You might need to erase your old data and generate a new account. Tap “Apply” to erase or “Cancel” to try again",
@ -1832,12 +1832,13 @@
"blank-messages-text": "Your messages will be here",
"groups": "Groups",
"shell-placeholder-title": "Your apps will run here",
"no-pinned-messages-desc": "This chat doesn't have any pinned messages.",
"no-pinned-messages-community-desc": "This channel doesn't have any pinned messages.",
"shell-placeholder-subtitle": "Open tabs of your communities, messages,\nwallet account and browser windows",
"invite-friends-to-status": "Invite friends to status",
"share-invite-link": "Share an invite link",
"pending-requests": "Pending requests",
"received": "Received",
"sent": "Sent",
"no-pinned-messages-desc": "This chat doesn't have any pinned messages.",
"no-pinned-messages-community-desc": "This channel doesn't have any pinned messages."
"and": "and"
}