refactor: chat message list context related (#15313)

This commit is contained in:
yqrashawn 2023-03-16 10:52:47 +08:00 committed by GitHub
parent dabe8285be
commit 9723479634
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 202 additions and 115 deletions

View File

@ -22,11 +22,10 @@
(defn get-actions (defn get-actions
[{:keys [outgoing content pinned outgoing-status deleted? deleted-for-me? content-type] [{:keys [outgoing content pinned outgoing-status deleted? deleted-for-me? content-type]
:as message-data} :as message-data}
{:keys [edit-enabled show-input? community? can-delete-message-for-everyone? {:keys [able-to-send-message? community? can-delete-message-for-everyone?
message-pin-enabled group-chat group-admin?]}] message-pin-enabled group-chat group-admin?]}]
(concat (concat
(when (and outgoing (when (and outgoing
edit-enabled
(not (or deleted? deleted-for-me?)) (not (or deleted? deleted-for-me?))
(not= content-type constants/content-type-audio)) (not= content-type constants/content-type-audio))
[{:type :main [{:type :main
@ -34,7 +33,7 @@
:label (i18n/label :t/edit-message) :label (i18n/label :t/edit-message)
:icon :i/edit :icon :i/edit
:id :edit}]) :id :edit}])
(when (and show-input? (not= outgoing-status :sending) (not (or deleted? deleted-for-me?))) (when (and able-to-send-message? (not= outgoing-status :sending) (not (or deleted? deleted-for-me?)))
[{:type :main [{:type :main
:on-press #(rf/dispatch [:chat.ui/reply-to-message message-data]) :on-press #(rf/dispatch [:chat.ui/reply-to-message message-data])
:label (i18n/label :t/message-reply) :label (i18n/label :t/message-reply)

View File

@ -17,8 +17,8 @@
(defonce messages-list-ref (atom nil)) (defonce messages-list-ref (atom nil))
(defonce list-key-fn #(or (:message-id %) (:value %))) (defn list-key-fn [{:keys [message-id value]}] (or message-id value))
(defonce list-ref #(reset! messages-list-ref %)) (defn list-ref [ref] (reset! messages-list-ref ref))
(defn scroll-to-bottom (defn scroll-to-bottom
[] []
@ -68,37 +68,6 @@
(background-timer/set-timeout #(rf/dispatch [:chat.ui/load-more-messages-for-current-chat]) (background-timer/set-timeout #(rf/dispatch [:chat.ui/load-more-messages-for-current-chat])
(if platform/low-device? 700 200)))) (if platform/low-device? 700 200))))
(defn get-render-data
"compute data used to render message list, including pinned message list and message list in chats"
[{:keys [group-chat chat-id public? community-id admins space-keeper show-input? edit-enabled
in-pinned-view?]}]
(let [current-public-key (rf/sub [:multiaccount/public-key])
{:keys [can-delete-message-for-everyone?
admin-settings]
:as community} (rf/sub [:communities/community
community-id])
{:keys [pin-message-all-members-enabled?]} admin-settings
group-admin? (get admins current-public-key)
community-admin? (when community (community :admin))
message-pin-enabled (and (not public?)
(or (not group-chat)
(and group-chat
(or group-admin?
pin-message-all-members-enabled?
community-admin?))))]
{:group-chat group-chat
:group-admin? group-admin?
:public? public?
:community? (not (nil? community-id))
:current-public-key current-public-key
:space-keeper space-keeper
:chat-id chat-id
:show-input? show-input?
:message-pin-enabled message-pin-enabled
:edit-enabled edit-enabled
:in-pinned-view? in-pinned-view?
:can-delete-message-for-everyone? can-delete-message-for-everyone?}))
(defonce messages-view-height (reagent/atom 0)) (defonce messages-view-height (reagent/atom 0))
(defn on-messages-view-layout (defn on-messages-view-layout
@ -133,49 +102,37 @@
[message/message-with-reactions message-data context])]))]) [message/message-with-reactions message-data context])]))])
(defn messages-list (defn messages-list
[{:keys [chat [{:keys [chat-id] :as chat}]
pan-responder (let [render-data (rf/sub [:chats/current-chat-message-list-view-context])
show-input?]}] messages (rf/sub [:chats/raw-chat-messages-stream chat-id])
(let [{:keys [group-chat chat-id public? community-id admins]} chat
messages (rf/sub [:chats/raw-chat-messages-stream
chat-id])
bottom-space 15] bottom-space 15]
[rn/view [rn/view
{:style {:flex 1}} {:style {:flex 1}}
;;DO NOT use anonymous functions for handlers ;; NOTE: DO NOT use anonymous functions for handlers
[rn/flat-list [rn/flat-list
(merge
pan-responder
{:key-fn list-key-fn {:key-fn list-key-fn
:ref list-ref :ref list-ref
:header [list-header chat] :header [list-header chat]
:footer [list-footer chat] :footer [list-footer chat]
:data messages :data messages
:render-data (get-render-data {:group-chat group-chat :render-data render-data
:chat-id chat-id
:public? public?
:community-id community-id
:admins admins
:show-input? show-input?
:edit-enabled true
:in-pinned-view? false})
:render-fn render-fn :render-fn render-fn
:on-viewable-items-changed on-viewable-items-changed :on-viewable-items-changed on-viewable-items-changed
:on-end-reached list-on-end-reached :on-end-reached list-on-end-reached
:on-scroll-to-index-failed identity ;;don't remove this :on-scroll-to-index-failed identity ; don't remove this
:content-container-style {:padding-top (+ bottom-space 32) :content-container-style {:padding-top (+ bottom-space 32)
:padding-bottom 16} :padding-bottom 16}
:scroll-indicator-insets {:top bottom-space} ;;ios only :scroll-indicator-insets {:top bottom-space} ; iOS only
:keyboard-dismiss-mode :interactive :keyboard-dismiss-mode :interactive
:keyboard-should-persist-taps :handled :keyboard-should-persist-taps :handled
:onMomentumScrollBegin state/start-scrolling :onMomentumScrollBegin state/start-scrolling
:onMomentumScrollEnd state/stop-scrolling :onMomentumScrollEnd state/stop-scrolling
:scrollEventThrottle 16 :scrollEventThrottle 16
:on-scroll on-scroll :on-scroll on-scroll
;;TODO https://github.com/facebook/react-native/issues/30034 ;; TODO https://github.com/facebook/react-native/issues/30034
:inverted (when platform/ios? true) :inverted (when platform/ios? true)
:style (when platform/android? {:scaleY -1}) :style (when platform/android? {:scaleY -1})
:on-layout on-messages-view-layout})] :on-layout on-messages-view-layout}]
[quo/floating-shell-button [quo/floating-shell-button
(merge {:jump-to (merge {:jump-to
{:on-press #(do {:on-press #(do

View File

@ -4,7 +4,6 @@
[react-native.core :as rn] [react-native.core :as rn]
[status-im2.contexts.chat.messages.content.deleted.view :as content.deleted] [status-im2.contexts.chat.messages.content.deleted.view :as content.deleted]
[status-im2.contexts.chat.messages.content.view :as message] [status-im2.contexts.chat.messages.content.view :as message]
[status-im2.contexts.chat.messages.list.view :as list.view]
[utils.i18n :as i18n] [utils.i18n :as i18n]
[utils.re-frame :as rf])) [utils.re-frame :as rf]))
@ -19,15 +18,11 @@
(defn pinned-messages-list (defn pinned-messages-list
[chat-id] [chat-id]
(let [pinned-messages (rf/sub [:chats/pinned-sorted-list (let [pinned-messages (rf/sub [:chats/pinned-sorted-list chat-id])
chat-id]) render-data (rf/sub [:chats/current-chat-message-list-view-context :in-pinned-view])
current-chat (rf/sub [:chat-by-id chat-id]) current-chat (rf/sub [:chat-by-id chat-id])
{:keys [community-id]} current-chat
{:keys [group-chat chat-id public? community-id admins]} community (rf/sub [:communities/community community-id])]
current-chat
community (rf/sub [:communities/community
community-id])]
[rn/view {:accessibility-label :pinned-messages-list} [rn/view {:accessibility-label :pinned-messages-list}
;; TODO (flexsurfer) this should be a component in quo2 ;; TODO (flexsurfer) this should be a component in quo2
;; https://github.com/status-im/status-mobile/issues/14529 ;; https://github.com/status-im/status-mobile/issues/14529
@ -56,17 +51,10 @@
{:style {:margin-left 4 {:style {:margin-left 4
:margin-right 8}} :margin-right 8}}
(str "# " (:chat-name current-chat))]])] (str "# " (:chat-name current-chat))]])]
(if (> (count pinned-messages) 0) (if (pos? (count pinned-messages))
[rn/flat-list [rn/flat-list
{:data pinned-messages {:data pinned-messages
:render-data (list.view/get-render-data {:group-chat group-chat :render-data render-data
:chat-id chat-id
:public? public?
:community-id community-id
:show-input? false
:admins admins
:edit-enabled true
:in-pinned-view? true})
:render-fn message-render-fn :render-fn message-render-fn
:key-fn list-key-fn :key-fn list-key-fn
:separator quo/separator}] :separator quo/separator}]

View File

@ -4,11 +4,11 @@
[react-native.core :as rn] [react-native.core :as rn]
[react-native.safe-area :as safe-area] [react-native.safe-area :as safe-area]
[reagent.core :as reagent] [reagent.core :as reagent]
[status-im2.contexts.chat.messages.composer.view :as composer]
[status-im2.constants :as constants] [status-im2.constants :as constants]
[status-im2.contexts.chat.messages.list.view :as messages.list] [status-im2.contexts.chat.messages.composer.view :as composer]
[status-im2.contexts.chat.messages.contact-requests.bottom-drawer :as [status-im2.contexts.chat.messages.contact-requests.bottom-drawer :as
contact-requests.bottom-drawer] contact-requests.bottom-drawer]
[status-im2.contexts.chat.messages.list.view :as messages.list]
[status-im2.contexts.chat.messages.pin.banner.view :as pin.banner] [status-im2.contexts.chat.messages.pin.banner.view :as pin.banner]
[status-im2.navigation.state :as navigation.state] [status-im2.navigation.state :as navigation.state]
[utils.debounce :as debounce] [utils.debounce :as debounce]
@ -67,7 +67,7 @@
(defn chat-render (defn chat-render
[] []
(let [;;NOTE: we want to react only on these fields, do not use full chat map here (let [;;NOTE: we want to react only on these fields, do not use full chat map here
{:keys [chat-id contact-request-state group-chat show-input?] :as chat} {:keys [chat-id contact-request-state group-chat able-to-send-message?] :as chat}
(rf/sub [:chats/current-chat-chat-view])] (rf/sub [:chats/current-chat-chat-view])]
[safe-area/consumer [safe-area/consumer
(fn [insets] (fn [insets]
@ -76,8 +76,8 @@
:keyboardVerticalOffset (- (:bottom insets))} :keyboardVerticalOffset (- (:bottom insets))}
[page-nav] [page-nav]
[pin.banner/banner chat-id] [pin.banner/banner chat-id]
[messages.list/messages-list {:chat chat :show-input? show-input?}] [messages.list/messages-list chat]
(if-not show-input? (if-not able-to-send-message?
[contact-requests.bottom-drawer/view chat-id contact-request-state group-chat] [contact-requests.bottom-drawer/view chat-id contact-request-state group-chat]
[composer/composer chat-id insets])])])) [composer/composer chat-id insets])])]))

View File

@ -3,16 +3,16 @@
[quo.design-system.colors :as colors] [quo.design-system.colors :as colors]
[re-frame.core :as re-frame] [re-frame.core :as re-frame]
[status-im.add-new.db :as db] [status-im.add-new.db :as db]
[status-im2.contexts.chat.events :as chat.events]
[status-im.chat.models.mentions :as mentions] [status-im.chat.models.mentions :as mentions]
[status-im.communities.core :as communities] [status-im.communities.core :as communities]
[status-im.group-chats.core :as group-chat] [status-im.group-chats.core :as group-chat]
[status-im.group-chats.db :as group-chats.db] [status-im.group-chats.db :as group-chats.db]
[utils.i18n :as i18n]
[status-im.multiaccounts.core :as multiaccounts] [status-im.multiaccounts.core :as multiaccounts]
[status-im2.config :as config]
[status-im.utils.image-server :as image-server] [status-im.utils.image-server :as image-server]
[status-im2.constants :as constants])) [status-im2.config :as config]
[status-im2.constants :as constants]
[status-im2.contexts.chat.events :as chat.events]
[utils.i18n :as i18n]))
(re-frame/reg-sub (re-frame/reg-sub
:chats/chat :chats/chat
@ -178,21 +178,21 @@
(when current-chat (when current-chat
(cond-> current-chat (cond-> current-chat
(chat.events/public-chat? current-chat) (chat.events/public-chat? current-chat)
(assoc :show-input? true) (assoc :able-to-send-message? true)
(and (chat.events/group-chat? current-chat) (and (chat.events/group-chat? current-chat)
(group-chats.db/member? my-public-key current-chat)) (group-chats.db/member? my-public-key current-chat))
(assoc :show-input? true (assoc :able-to-send-message? true
:member? true) :member? true)
(and (chat.events/community-chat? current-chat) (and (chat.events/community-chat? current-chat)
(communities/can-post? community my-public-key (:chat-id current-chat))) (communities/can-post? community my-public-key (:chat-id current-chat)))
(assoc :show-input? true) (assoc :able-to-send-message? true)
(not group-chat) (not group-chat)
(assoc (assoc
:contact-request-state (get-in contacts [chat-id :contact-request-state]) :contact-request-state (get-in contacts [chat-id :contact-request-state])
:show-input? :able-to-send-message?
(and (and
(or (or
(get-in inputs [chat-id :metadata :sending-contact-request]) (get-in inputs [chat-id :metadata :sending-contact-request])
@ -206,7 +206,7 @@
(fn [current-chat] (fn [current-chat]
(select-keys current-chat (select-keys current-chat
[:chat-id [:chat-id
:show-input? :able-to-send-message?
:group-chat :group-chat
:admins :admins
:invitation-admin :invitation-admin
@ -220,6 +220,44 @@
:community-id :community-id
:emoji]))) :emoji])))
(re-frame/reg-sub
:chats/current-chat-message-list-view-context
:<- [:chats/current-chat-chat-view]
:<- [:communities/current-community]
:<- [:multiaccount/public-key]
(fn [[current-chat current-community current-public-key] [_ in-pinned-view?]]
(let [{:keys [group-chat chat-id public? admins space-keeper able-to-send-message?]}
current-chat
{:keys [can-delete-message-for-everyone? admin-settings]}
current-community
{:keys [pin-message-all-members-enabled?]} admin-settings
community? (some? current-community)
group-admin? (contains? admins current-public-key)
community-admin? (get current-community :admin false)
message-pin-enabled
(cond public? false
(not group-chat) true ; one to one chat
;; in public group or community
group-chat (or group-admin?
pin-message-all-members-enabled?
community-admin?)
:else false)]
{:group-chat group-chat
:group-admin? group-admin?
:public? public?
:community? community?
:community-admin? community-admin?
:current-public-key current-public-key
:space-keeper space-keeper
:chat-id chat-id
:in-pinned-view? (boolean in-pinned-view?)
:able-to-send-message? able-to-send-message?
:message-pin-enabled message-pin-enabled
:can-delete-message-for-everyone? can-delete-message-for-everyone?})))
(re-frame/reg-sub (re-frame/reg-sub
:current-chat/metadata :current-chat/metadata
:<- [:chats/current-raw-chat] :<- [:chats/current-raw-chat]

View File

@ -1,14 +1,16 @@
(ns status-im2.subs.chat.chats-test (ns status-im2.subs.chat.chats-test
(:require [cljs.test :refer [is testing]] (:require [cljs.test :refer [is testing]]
[re-frame.db :as rf-db] [re-frame.db :as rf-db]
[test-helpers.unit :as h]
[status-im2.constants :as constants] [status-im2.constants :as constants]
[test-helpers.unit :as h]
[utils.re-frame :as rf])) [utils.re-frame :as rf]))
(def public-key "0xpk") (def public-key "0xpk")
(def multiaccount {:public-key public-key}) (def multiaccount {:public-key public-key})
(def chat-id "1") (def chat-id "1")
(def chat {:chat-id chat-id}) (def chat {:chat-id chat-id})
(def community-id "community-1")
(def community {:community-id community-id})
(def private-group-chat (def private-group-chat
(assoc (assoc
@ -17,6 +19,13 @@
:group-chat true :group-chat true
:chat-type constants/private-group-chat-type)) :chat-type constants/private-group-chat-type))
(def community-chat
(assoc
chat
:group-chat true
:community-id community-id
:chat-type constants/community-chat-type))
(def one-to-one-chat (def one-to-one-chat
(assoc (assoc
chat chat
@ -31,14 +40,14 @@
:multiaccount multiaccount :multiaccount multiaccount
:current-chat-id chat-id :current-chat-id chat-id
:chats chats) :chats chats)
(is (true? (:show-input? (rf/sub [sub-name])))))) (is (true? (:able-to-send-message? (rf/sub [sub-name]))))))
(testing "private group chat, user is not member" (testing "private group chat, user is not member"
(let [chats {chat-id (dissoc private-group-chat :members)}] (let [chats {chat-id (dissoc private-group-chat :members)}]
(swap! rf-db/app-db assoc (swap! rf-db/app-db assoc
:multiaccount multiaccount :multiaccount multiaccount
:current-chat-id chat-id :current-chat-id chat-id
:chats chats) :chats chats)
(is (not (:show-input? (rf/sub [sub-name])))))) (is (not (:able-to-send-message? (rf/sub [sub-name]))))))
(testing "one to one chat, mutual contacts" (testing "one to one chat, mutual contacts"
(let [chats {chat-id one-to-one-chat}] (let [chats {chat-id one-to-one-chat}]
(swap! rf-db/app-db assoc (swap! rf-db/app-db assoc
@ -46,7 +55,7 @@
:multiaccount multiaccount :multiaccount multiaccount
:current-chat-id chat-id :current-chat-id chat-id
:chats chats) :chats chats)
(is (:show-input? (rf/sub [sub-name]))))) (is (:able-to-send-message? (rf/sub [sub-name])))))
(testing "one to one chat, not a contact" (testing "one to one chat, not a contact"
(let [chats {chat-id one-to-one-chat}] (let [chats {chat-id one-to-one-chat}]
(swap! rf-db/app-db assoc (swap! rf-db/app-db assoc
@ -54,4 +63,100 @@
:multiaccount multiaccount :multiaccount multiaccount
:current-chat-id chat-id :current-chat-id chat-id
:chats chats) :chats chats)
(is (not (:show-input? (rf/sub [sub-name]))))))) (is (not (:able-to-send-message? (rf/sub [sub-name])))))))
(h/deftest-sub :chats/current-chat-message-list-view-context
[sub-name]
(testing "reflect :in-pinned-view? in the result"
(reset! rf-db/app-db {})
(is (true? (:in-pinned-view? (rf/sub [sub-name :in-pinned-view]))))
(is (false? (:in-pinned-view? (rf/sub [sub-name])))))
(testing "reflect current community in community?"
(let [chats {chat-id community-chat}
communities {community-id community}]
(is (false? (:community? (rf/sub [sub-name]))))
(swap! rf-db/app-db assoc
:communities/enabled? true
:current-chat-id chat-id
:chats chats
:communities communities)
(is (true? (:community? (rf/sub [sub-name]))))))
(testing "community admin"
(let [chats {chat-id community-chat}
communities {community-id (assoc community
:admin true
:can-delete-message-for-everyone? true
:admin-settings {:pin-message-all-members-enabled? false})}]
(is (false? (:community? (rf/sub [sub-name]))))
(swap! rf-db/app-db assoc
:communities/enabled? true
:multiaccount multiaccount
:current-chat-id chat-id
:chats chats
:communities communities)
(is (= chat-id (:chat-id (rf/sub [sub-name]))))
(is (= public-key (:current-public-key (rf/sub [sub-name]))))
(is (true? (:community? (rf/sub [sub-name]))))
(is (true? (:group-chat (rf/sub [sub-name]))))
(is (true? (:community-admin? (rf/sub [sub-name]))))
(is (true? (:can-delete-message-for-everyone? (rf/sub [sub-name]))))
(is (not (:group-admin? (rf/sub [sub-name]))))
(is (true? (:message-pin-enabled (rf/sub [sub-name]))))))
(testing "community member"
(let [chats {chat-id community-chat}
communities {community-id (assoc community
:admin false
:can-delete-message-for-everyone? false
:admin-settings {:pin-message-all-members-enabled?
false})}]
(is (false? (:community? (rf/sub [sub-name]))))
(swap! rf-db/app-db assoc
:communities/enabled? true
:multiaccount multiaccount
:current-chat-id chat-id
:chats chats
:communities communities)
(is (= chat-id (:chat-id (rf/sub [sub-name]))))
(is (= public-key (:current-public-key (rf/sub [sub-name]))))
(is (true? (:community? (rf/sub [sub-name]))))
(is (true? (:group-chat (rf/sub [sub-name]))))
(is (not (:community-admin? (rf/sub [sub-name]))))
(is (not (:can-delete-message-for-everyone? (rf/sub [sub-name]))))
(is (not (:group-admin? (rf/sub [sub-name]))))
(is (not (:message-pin-enabled (rf/sub [sub-name]))))))
(testing "group admin"
(let [chats {chat-id (assoc private-group-chat
:admins
#{public-key})}]
(swap! rf-db/app-db assoc
:communities/enabled? true
:multiaccount multiaccount
:current-chat-id chat-id
:chats chats)
(is (= chat-id (:chat-id (rf/sub [sub-name]))))
(is (= public-key (:current-public-key (rf/sub [sub-name]))))
(is (not (:public? (rf/sub [sub-name]))))
(is (not (:community? (rf/sub [sub-name]))))
(is (true? (:group-chat (rf/sub [sub-name]))))
(is (not (:community-admin? (rf/sub [sub-name]))))
(is (not (:can-delete-message-for-everyone? (rf/sub [sub-name]))))
(is (true? (:group-admin? (rf/sub [sub-name]))))
(is (true? (:message-pin-enabled (rf/sub [sub-name]))))))
(testing "group member"
(let [chats {chat-id (assoc private-group-chat
:admins
#{})}]
(swap! rf-db/app-db assoc
:communities/enabled? true
:multiaccount multiaccount
:current-chat-id chat-id
:chats chats)
(is (= chat-id (:chat-id (rf/sub [sub-name]))))
(is (= public-key (:current-public-key (rf/sub [sub-name]))))
(is (not (:public? (rf/sub [sub-name]))))
(is (not (:community? (rf/sub [sub-name]))))
(is (true? (:group-chat (rf/sub [sub-name]))))
(is (not (:community-admin? (rf/sub [sub-name]))))
(is (not (:can-delete-message-for-everyone? (rf/sub [sub-name]))))
(is (not (:group-admin? (rf/sub [sub-name]))))
(is (not (:message-pin-enabled (rf/sub [sub-name])))))))