From 972347963498fc4a2bb8f85541e79ed0541698da Mon Sep 17 00:00:00 2001 From: yqrashawn Date: Thu, 16 Mar 2023 10:52:47 +0800 Subject: [PATCH] refactor: chat message list context related (#15313) --- .../contexts/chat/messages/drawers/view.cljs | 5 +- .../contexts/chat/messages/list/view.cljs | 103 +++++----------- .../contexts/chat/messages/pin/list/view.cljs | 26 ++-- .../contexts/chat/messages/view.cljs | 10 +- src/status_im2/subs/chat/chats.cljs | 58 +++++++-- src/status_im2/subs/chat/chats_test.cljs | 115 +++++++++++++++++- 6 files changed, 202 insertions(+), 115 deletions(-) diff --git a/src/status_im2/contexts/chat/messages/drawers/view.cljs b/src/status_im2/contexts/chat/messages/drawers/view.cljs index 4166d65692..eb16af6d87 100644 --- a/src/status_im2/contexts/chat/messages/drawers/view.cljs +++ b/src/status_im2/contexts/chat/messages/drawers/view.cljs @@ -22,11 +22,10 @@ (defn get-actions [{:keys [outgoing content pinned outgoing-status deleted? deleted-for-me? content-type] :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?]}] (concat (when (and outgoing - edit-enabled (not (or deleted? deleted-for-me?)) (not= content-type constants/content-type-audio)) [{:type :main @@ -34,7 +33,7 @@ :label (i18n/label :t/edit-message) :icon :i/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 :on-press #(rf/dispatch [:chat.ui/reply-to-message message-data]) :label (i18n/label :t/message-reply) diff --git a/src/status_im2/contexts/chat/messages/list/view.cljs b/src/status_im2/contexts/chat/messages/list/view.cljs index 5339153289..2e67e46c41 100644 --- a/src/status_im2/contexts/chat/messages/list/view.cljs +++ b/src/status_im2/contexts/chat/messages/list/view.cljs @@ -17,8 +17,8 @@ (defonce messages-list-ref (atom nil)) -(defonce list-key-fn #(or (:message-id %) (:value %))) -(defonce list-ref #(reset! messages-list-ref %)) +(defn list-key-fn [{:keys [message-id value]}] (or message-id value)) +(defn list-ref [ref] (reset! messages-list-ref ref)) (defn scroll-to-bottom [] @@ -68,37 +68,6 @@ (background-timer/set-timeout #(rf/dispatch [:chat.ui/load-more-messages-for-current-chat]) (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)) (defn on-messages-view-layout @@ -133,49 +102,37 @@ [message/message-with-reactions message-data context])]))]) (defn messages-list - [{:keys [chat - pan-responder - show-input?]}] - (let [{:keys [group-chat chat-id public? community-id admins]} chat - messages (rf/sub [:chats/raw-chat-messages-stream - chat-id]) - bottom-space 15] + [{:keys [chat-id] :as chat}] + (let [render-data (rf/sub [:chats/current-chat-message-list-view-context]) + messages (rf/sub [:chats/raw-chat-messages-stream chat-id]) + bottom-space 15] [rn/view {:style {:flex 1}} - ;;DO NOT use anonymous functions for handlers + ;; NOTE: DO NOT use anonymous functions for handlers [rn/flat-list - (merge - pan-responder - {:key-fn list-key-fn - :ref list-ref - :header [list-header chat] - :footer [list-footer chat] - :data messages - :render-data (get-render-data {:group-chat group-chat - :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 - :on-viewable-items-changed on-viewable-items-changed - :on-end-reached list-on-end-reached - :on-scroll-to-index-failed identity ;;don't remove this - :content-container-style {:padding-top (+ bottom-space 32) - :padding-bottom 16} - :scroll-indicator-insets {:top bottom-space} ;;ios only - :keyboard-dismiss-mode :interactive - :keyboard-should-persist-taps :handled - :onMomentumScrollBegin state/start-scrolling - :onMomentumScrollEnd state/stop-scrolling - :scrollEventThrottle 16 - :on-scroll on-scroll - ;;TODO https://github.com/facebook/react-native/issues/30034 - :inverted (when platform/ios? true) - :style (when platform/android? {:scaleY -1}) - :on-layout on-messages-view-layout})] + {:key-fn list-key-fn + :ref list-ref + :header [list-header chat] + :footer [list-footer chat] + :data messages + :render-data render-data + :render-fn render-fn + :on-viewable-items-changed on-viewable-items-changed + :on-end-reached list-on-end-reached + :on-scroll-to-index-failed identity ; don't remove this + :content-container-style {:padding-top (+ bottom-space 32) + :padding-bottom 16} + :scroll-indicator-insets {:top bottom-space} ; iOS only + :keyboard-dismiss-mode :interactive + :keyboard-should-persist-taps :handled + :onMomentumScrollBegin state/start-scrolling + :onMomentumScrollEnd state/stop-scrolling + :scrollEventThrottle 16 + :on-scroll on-scroll + ;; TODO https://github.com/facebook/react-native/issues/30034 + :inverted (when platform/ios? true) + :style (when platform/android? {:scaleY -1}) + :on-layout on-messages-view-layout}] [quo/floating-shell-button (merge {:jump-to {:on-press #(do diff --git a/src/status_im2/contexts/chat/messages/pin/list/view.cljs b/src/status_im2/contexts/chat/messages/pin/list/view.cljs index 69b8430999..d083685fdc 100644 --- a/src/status_im2/contexts/chat/messages/pin/list/view.cljs +++ b/src/status_im2/contexts/chat/messages/pin/list/view.cljs @@ -4,7 +4,6 @@ [react-native.core :as rn] [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.list.view :as list.view] [utils.i18n :as i18n] [utils.re-frame :as rf])) @@ -19,15 +18,11 @@ (defn pinned-messages-list [chat-id] - (let [pinned-messages (rf/sub [:chats/pinned-sorted-list - chat-id]) - current-chat (rf/sub [:chat-by-id chat-id]) - - {:keys [group-chat chat-id public? community-id admins]} - current-chat - - community (rf/sub [:communities/community - community-id])] + (let [pinned-messages (rf/sub [:chats/pinned-sorted-list 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]) + {:keys [community-id]} current-chat + community (rf/sub [:communities/community community-id])] [rn/view {:accessibility-label :pinned-messages-list} ;; TODO (flexsurfer) this should be a component in quo2 ;; https://github.com/status-im/status-mobile/issues/14529 @@ -56,17 +51,10 @@ {:style {:margin-left 4 :margin-right 8}} (str "# " (:chat-name current-chat))]])] - (if (> (count pinned-messages) 0) + (if (pos? (count pinned-messages)) [rn/flat-list {:data pinned-messages - :render-data (list.view/get-render-data {:group-chat group-chat - :chat-id chat-id - :public? public? - :community-id community-id - :show-input? false - :admins admins - :edit-enabled true - :in-pinned-view? true}) + :render-data render-data :render-fn message-render-fn :key-fn list-key-fn :separator quo/separator}] diff --git a/src/status_im2/contexts/chat/messages/view.cljs b/src/status_im2/contexts/chat/messages/view.cljs index 04639bc254..4edbde0960 100644 --- a/src/status_im2/contexts/chat/messages/view.cljs +++ b/src/status_im2/contexts/chat/messages/view.cljs @@ -4,11 +4,11 @@ [react-native.core :as rn] [react-native.safe-area :as safe-area] [reagent.core :as reagent] - [status-im2.contexts.chat.messages.composer.view :as composer] [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 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.navigation.state :as navigation.state] [utils.debounce :as debounce] @@ -67,7 +67,7 @@ (defn chat-render [] (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])] [safe-area/consumer (fn [insets] @@ -76,8 +76,8 @@ :keyboardVerticalOffset (- (:bottom insets))} [page-nav] [pin.banner/banner chat-id] - [messages.list/messages-list {:chat chat :show-input? show-input?}] - (if-not show-input? + [messages.list/messages-list chat] + (if-not able-to-send-message? [contact-requests.bottom-drawer/view chat-id contact-request-state group-chat] [composer/composer chat-id insets])])])) diff --git a/src/status_im2/subs/chat/chats.cljs b/src/status_im2/subs/chat/chats.cljs index affca6d6e6..bb9879e514 100644 --- a/src/status_im2/subs/chat/chats.cljs +++ b/src/status_im2/subs/chat/chats.cljs @@ -3,16 +3,16 @@ [quo.design-system.colors :as colors] [re-frame.core :as re-frame] [status-im.add-new.db :as db] - [status-im2.contexts.chat.events :as chat.events] [status-im.chat.models.mentions :as mentions] [status-im.communities.core :as communities] [status-im.group-chats.core :as group-chat] [status-im.group-chats.db :as group-chats.db] - [utils.i18n :as i18n] [status-im.multiaccounts.core :as multiaccounts] - [status-im2.config :as config] [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 :chats/chat @@ -178,21 +178,21 @@ (when current-chat (cond-> 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) (group-chats.db/member? my-public-key current-chat)) - (assoc :show-input? true - :member? true) + (assoc :able-to-send-message? true + :member? true) (and (chat.events/community-chat? 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) (assoc :contact-request-state (get-in contacts [chat-id :contact-request-state]) - :show-input? + :able-to-send-message? (and (or (get-in inputs [chat-id :metadata :sending-contact-request]) @@ -206,7 +206,7 @@ (fn [current-chat] (select-keys current-chat [:chat-id - :show-input? + :able-to-send-message? :group-chat :admins :invitation-admin @@ -220,6 +220,44 @@ :community-id :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 :current-chat/metadata :<- [:chats/current-raw-chat] diff --git a/src/status_im2/subs/chat/chats_test.cljs b/src/status_im2/subs/chat/chats_test.cljs index 643730fe39..ac6aaf7bde 100644 --- a/src/status_im2/subs/chat/chats_test.cljs +++ b/src/status_im2/subs/chat/chats_test.cljs @@ -1,14 +1,16 @@ (ns status-im2.subs.chat.chats-test (:require [cljs.test :refer [is testing]] [re-frame.db :as rf-db] - [test-helpers.unit :as h] [status-im2.constants :as constants] + [test-helpers.unit :as h] [utils.re-frame :as rf])) (def public-key "0xpk") (def multiaccount {:public-key public-key}) (def chat-id "1") (def chat {:chat-id chat-id}) +(def community-id "community-1") +(def community {:community-id community-id}) (def private-group-chat (assoc @@ -17,6 +19,13 @@ :group-chat true :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 (assoc chat @@ -31,14 +40,14 @@ :multiaccount multiaccount :current-chat-id chat-id :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" (let [chats {chat-id (dissoc private-group-chat :members)}] (swap! rf-db/app-db assoc :multiaccount multiaccount :current-chat-id chat-id :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" (let [chats {chat-id one-to-one-chat}] (swap! rf-db/app-db assoc @@ -46,7 +55,7 @@ :multiaccount multiaccount :current-chat-id chat-id :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" (let [chats {chat-id one-to-one-chat}] (swap! rf-db/app-db assoc @@ -54,4 +63,100 @@ :multiaccount multiaccount :current-chat-id chat-id :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])))))))