fix: delete message action logic (#14977)

This commit is contained in:
yqrashawn 2023-02-17 19:21:19 +08:00 committed by GitHub
parent ee5dcb3683
commit 866ba63ab7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 330 additions and 243 deletions

View File

@ -173,7 +173,7 @@
(utils/truncate-str (:info content) 24)])]]]])
(defn system-message
[{:keys [type style non-pressable? animate-landing? labels] :as message}]
[{:keys [type style non-pressable? animate-landing? labels on-long-press] :as message}]
[:f>
(fn []
(let [sv-color (reanimated/use-shared-value
@ -186,16 +186,17 @@
:linear
1000))
[reanimated/touchable-opacity
{:on-press #(when-not non-pressable?
(reanimated/set-shared-value sv-color (get-color :bg :pressed type)))
:style (reanimated/apply-animations-to-style
{:background-color sv-color}
(merge
{:flex-direction :row
:flex 1
:border-radius 16
:padding-vertical 9
:padding-horizontal 11
:background-color sv-color}
style))}
{:on-press #(when-not non-pressable?
(reanimated/set-shared-value sv-color (get-color :bg :pressed type)))
:on-long-press on-long-press
:style (reanimated/apply-animations-to-style
{:background-color sv-color}
(merge
{:flex-direction :row
:flex 1
:border-radius 16
:padding-vertical 9
:padding-horizontal 11
:background-color sv-color}
style))}
[sm-render message labels]]))])

View File

@ -45,7 +45,7 @@
[from username current-public-key]
(or (and (= from current-public-key)
(i18n/label :t/You))
(format-author username)))
(when username (format-author username))))
(defn reply-deleted-message
[]

View File

@ -4,15 +4,13 @@
[quo.react-native :as rn]
[quo2.components.icon :as icons]
[quo2.components.markdown.text :as text]
[quo2.core :as quo]
[quo2.foundations.colors :as colors]
[quo2.foundations.typography :as typography]
[re-frame.core :as re-frame]
[reagent.core :as reagent]
[status-im2.constants :as constants]
[utils.i18n :as i18n]
[status-im.react-native.resources :as resources]
[status-im.ui.components.fast-image :as fast-image]
[status-im.ui.components.react :as react]
[status-im.ui.screens.chat.image.preview.views :as preview]
[status-im.ui.screens.chat.message.audio :as message.audio]
[status-im.ui.screens.chat.message.gap :as message.gap]
@ -21,14 +19,14 @@
[status-im.ui.screens.chat.utils :as chat.utils]
[status-im.ui.screens.communities.icon :as communities.icon]
[status-im.ui2.screens.chat.components.reply.view :as components.reply]
[status-im2.config :as config]
[utils.datetime :as datetime]
[status-im.utils.utils :as utils]
[status-im2.constants :as constants]
[status-im2.contexts.chat.home.chat-list-item.view :as home.chat-list-item]
[status-im2.contexts.chat.messages.delete-message-for-me.events]
[status-im2.contexts.chat.messages.delete-message.events]
[utils.re-frame :as rf]
[quo2.core :as quo])
[utils.datetime :as datetime]
[utils.i18n :as i18n]
[utils.re-frame :as rf])
(:require-macros [status-im.utils.views :refer [defview letsubs]]))
(defn system-text?
@ -230,58 +228,6 @@
(re-frame/dispatch [:pin-message/show-pin-limit-modal chat-id]))
(re-frame/dispatch [:pin-message/send-pin-message (assoc message :pinned (not pinned))]))))
(defn on-long-press-fn
[on-long-press
{:keys [pinned message-pin-enabled outgoing edit-enabled show-input? community?
can-delete-message-for-everyone?]
:as message} content]
(on-long-press
(concat
(when (and outgoing edit-enabled)
[{:type :main
:on-press #(re-frame/dispatch [:chat.ui/edit-message message])
:label (i18n/label :t/edit-message)
:icon :i/edit
:id :edit}])
(when show-input?
[{:type :main
:on-press #(re-frame/dispatch [:chat.ui/reply-to-message message])
:label (i18n/label :t/message-reply)
:icon :i/reply
:id :reply}])
[{:type :main
:on-press #(react/copy-to-clipboard
(components.reply/get-quoted-text-with-mentions
(get content :parsed-text)))
:label (i18n/label :t/copy-text)
:icon :i/copy
:id :copy}]
(when message-pin-enabled
[{:type :main
:on-press #(pin-message message)
:label (i18n/label (if pinned
(if community? :t/unpin-from-channel :t/unpin-from-chat)
(if community? :t/pin-to-channel :t/pin-to-chat)))
:icon :i/pin
:id (if pinned :unpin :pin)}])
(when-not pinned
[{:type :danger
:on-press (fn []
(re-frame/dispatch
[:chat.ui/delete-message-for-me message
config/delete-message-for-me-undo-time-limit-ms]))
:label (i18n/label :t/delete-for-me)
:icon :i/delete
:id :delete-for-me}])
(when (and (or outgoing can-delete-message-for-everyone?) config/delete-message-enabled?)
[{:type :danger
:on-press #(re-frame/dispatch [:chat.ui/delete-message
message
config/delete-message-undo-time-limit-ms])
:label (i18n/label :t/delete-for-everyone)
:icon :i/delete
:id :delete-for-all}]))))
;; STATUS ? whats that ?
(defmethod ->message constants/content-type-status
[{:keys [content content-type]}]

View File

@ -34,7 +34,6 @@
(def communities-enabled? (enabled? (get-config :COMMUNITIES_ENABLED "0")))
(def database-management-enabled? (enabled? (get-config :DATABASE_MANAGEMENT_ENABLED "0")))
(def debug-webview? (enabled? (get-config :DEBUG_WEBVIEW "0")))
(def delete-message-enabled? (enabled? (get-config :DELETE_MESSAGE_ENABLED "0")))
(def collectibles-enabled? (enabled? (get-config :COLLECTIBLES_ENABLED "1")))
(def test-stateofus? (enabled? (get-config :TEST_STATEOFUS "0")))
(def two-minutes-syncing? (enabled? (get-config :TWO_MINUTES_SYNCING "0")))

View File

@ -1,6 +1,7 @@
(ns status-im2.contexts.chat.messages.content.deleted.view
(:require [quo2.core :as quo]
[react-native.core :as rn]
[status-im2.contexts.chat.messages.drawers.view :as drawers]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
@ -20,8 +21,20 @@
[quo/text {:style {:margin-left 4} :size :paragraph-2}
(i18n/label :t/deleted-this-message)]])
(defn- compute-on-long-press-fn
[{:keys [deleted? pinned] :as message}
{:keys [message-pin-enabled] :as context}]
;; only show drawer for user who has the permission to unpin messages
(when (and pinned deleted? message-pin-enabled)
(fn []
(rf/dispatch [:dismiss-keyboard])
(rf/dispatch [:bottom-sheet/show-sheet
{:content (drawers/reactions-and-actions message
context)}]))))
(defn deleted-by-message
[{:keys [deleted-by deleted-undoable-till timestamp-str deleted-for-me-undoable-till from]}]
[{:keys [deleted-by deleted-undoable-till timestamp-str deleted-for-me-undoable-till from]}
on-long-press-fn]
(let [;; deleted message with nil deleted-by is deleted by (:from message)
display-name (first (rf/sub [:contacts/contact-two-names-by-identity (or deleted-by from)]))
contact (rf/sub [:contacts/contact-by-address (or deleted-by from)])
@ -32,21 +45,25 @@
:timestamp-str timestamp-str
:child [user-xxx-deleted-this-message
{:display-name display-name :profile-picture photo-path}]
:non-pressable? true
:on-long-press on-long-press-fn
:non-pressable? (if on-long-press-fn false true)
:animate-landing? (or deleted-undoable-till deleted-for-me-undoable-till)}]))
(defn deleted-message
[{:keys [deleted? deleted-by deleted-undoable-till timestamp-str deleted-for-me-undoable-till from]
:as message}]
(let [pub-key (rf/sub [:multiaccount/public-key])
deleted-by-me? (= (or deleted-by from) pub-key)]
(if (not deleted-by-me?)
[deleted-by-message message]
:as message}
context]
(let [pub-key (rf/sub [:multiaccount/public-key])
deleted-by-me? (= (or deleted-by from) pub-key)
on-long-press-fn (compute-on-long-press-fn message context)]
(if-not deleted-by-me?
[deleted-by-message message on-long-press-fn]
[quo/system-message
{:type :deleted
:label (if deleted? :message-deleted :message-deleted-for-you)
:labels {:message-deleted (i18n/label :t/message-deleted-for-everyone)
:message-deleted-for-you (i18n/label :t/message-deleted-for-you)}
:on-long-press on-long-press-fn
:timestamp-str timestamp-str
:non-pressable? true
:non-pressable? (if on-long-press-fn false true)
:animate-landing? (or deleted-undoable-till deleted-for-me-undoable-till)}])))

View File

@ -1,10 +1,10 @@
(ns status-im2.contexts.chat.messages.delete-message.events
(:require
[utils.i18n :as i18n]
[quo2.foundations.colors :as colors]
[status-im2.contexts.chat.messages.list.events :as message-list]
[taoensso.timbre :as log]
[utils.datetime :as datetime]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn- update-db-clear-undo-timer
@ -47,6 +47,26 @@
(when (get-in db [:messages chat-id message-id])
(update-in db [:messages chat-id message-id] assoc :deleted? true :deleted-by deleted-by)))
(defn- should-and-able-to-unpin-to-be-deleted-message
"check
1. if the to-be deleted message is pinned
2. if user has the permission to unpin message in current chat"
[db {:keys [chat-id message-id]}]
(let [{:keys [group-chat chat-id public? community-id admins]} (get-in db [:chats chat-id])
community (get-in db [:communities community-id])
community-admin? (get community :admin false)
pub-key (get-in db [:multiaccount :public-key])
group-admin? (get admins pub-key)
message-pin-enabled (and (not public?)
(or (not group-chat)
(and group-chat
(or group-admin?
community-admin?))))
pinned (get-in db
[:pin-messages chat-id
message-id])]
(and pinned message-pin-enabled)))
(rf/defn delete
"Delete message now locally and broadcast after undo time limit timeout"
{:events [:chat.ui/delete-message]}
@ -56,14 +76,22 @@
;; new delete operation will reset prev pending deletes' undo timelimit
;; undo will undo all pending deletes
;; all pending deletes are stored in toast
(let [pub-key (get-in db [:multiaccount :public-key])
(let [unpin? (should-and-able-to-unpin-to-be-deleted-message
db
{:chat-id chat-id :message-id message-id})
pub-key (get-in db [:multiaccount :public-key])
;; compute deleted by
message-from (get message :from)
deleted-by (when (not= pub-key message-from) pub-key)
existing-undo-toast (get-in db [:toasts :toasts :delete-message-for-everyone])
toast-count (inc (get existing-undo-toast :message-deleted-for-everyone-count 0))
existing-undos (-> existing-undo-toast
(get :message-deleted-for-everyone-undos [])
(conj {:message-id message-id :chat-id chat-id}))]
existing-undos
(-> existing-undo-toast
(get :message-deleted-for-everyone-undos [])
(conj {:message-id message-id :chat-id chat-id :should-pin-back? unpin?}))]
(assoc
(message-list/rebuild-message-list
{:db (reduce
@ -89,7 +117,10 @@
:undo-duration (/ undo-time-limit-ms 1000)
:undo-on-press #(do (rf/dispatch [:chat.ui/undo-all-delete-message])
(rf/dispatch [:toasts/close
:delete-message-for-everyone]))}]]
:delete-message-for-everyone]))}]
(when unpin?
[:pin-message/send-pin-message-locally
{:chat-id chat-id :message-id message-id :pinned false}])]
:utils/dispatch-later (mapv (fn [{:keys [chat-id message-id]}]
{:dispatch [:chat.ui/delete-message-and-send
{:chat-id chat-id :message-id message-id}]
@ -98,11 +129,16 @@
(rf/defn undo
{:events [:chat.ui/undo-delete-message]}
[{:keys [db]} {:keys [chat-id message-id]}]
[{:keys [db]} {:keys [chat-id message-id should-pin-back?]}]
(when (get-in db [:messages chat-id message-id])
(message-list/rebuild-message-list
{:db (update-db-undo-locally db chat-id message-id)}
chat-id)))
(let [effects (message-list/rebuild-message-list
{:db (update-db-undo-locally db chat-id message-id)}
chat-id)
message (get-in effects [:db :messages chat-id message-id])]
(cond-> effects
should-pin-back?
(assoc :dispatch
[:pin-message/send-pin-message-locally (assoc message :pinned true)])))))
(rf/defn undo-all
{:events [:chat.ui/undo-all-delete-message]}
@ -126,19 +162,32 @@
[{:keys [db]} {:keys [message-id chat-id]} force?]
(when-let [message (get-in db [:messages chat-id message-id])]
(when (or force? (check-before-delete-and-send db chat-id message-id))
(cond-> {:db (update-db-clear-undo-timer db chat-id message-id)
:json-rpc/call [{:method "wakuext_deleteMessageAndSend"
:params [message-id]
:js-response true
:on-error #(log/error "failed to delete message "
{:message-id message-id :error %})
:on-success #(rf/dispatch
[:sanitize-messages-and-process-response
%])}]}
(get-in db [:pin-messages chat-id message-id])
(assoc :dispatch
[:pin-message/send-pin-message
{:chat-id chat-id :message-id message-id :pinned false}])))))
(let [unpin-locally?
;; this only check against local client data
;; generally msg is already unpinned at delete locally phase when user
;; has unpin permission
;;
;; will be true only if
;; 1. admin delete an unpinned msg
;; 2. another admin pin the msg within the undo time limit
;; 3. msg finally deleted
(should-and-able-to-unpin-to-be-deleted-message db
{:chat-id chat-id
:message-id message-id})]
{:db (update-db-clear-undo-timer db chat-id message-id)
:json-rpc/call [{:method "wakuext_deleteMessageAndSend"
:params [message-id]
:js-response true
:on-error #(log/error "failed to delete message "
{:message-id message-id :error %})
:on-success #(rf/dispatch
[:sanitize-messages-and-process-response
%])}]
:dispatch [:pin-message/send-pin-message
{:chat-id chat-id
:message-id message-id
:pinned false
:remote-only? (not unpin-locally?)}]}))))
(defn- filter-pending-send-messages
"traverse all messages find not yet synced deleted? messages"

View File

@ -1,13 +1,12 @@
(ns status-im2.contexts.chat.messages.drawers.view
(:require [react-native.core :as rn]
[status-im2.constants :as constants]
[utils.re-frame :as rf]
[quo2.core :as quo]
[utils.i18n :as i18n]
[status-im2.config :as config]
(:require [quo2.core :as quo]
[react-native.core :as rn]
[status-im.ui.components.react :as react]
[status-im.ui2.screens.chat.components.reply.view :as components.reply]
[status-im2.common.not-implemented :as not-implemented]))
[status-im2.common.not-implemented :as not-implemented]
[status-im2.constants :as constants]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn pin-message
[{:keys [chat-id pinned pinned-by] :as message-data}]
@ -21,37 +20,39 @@
(assoc message-data :pinned message-not-pinned?)]))))
(defn get-actions
[{:keys [outgoing content pinned-by outgoing-status] :as message-data}
{:keys [edit-enabled show-input? can-delete-message-for-everyone? community? message-pin-enabled]}]
[{:keys [outgoing content pinned outgoing-status deleted? deleted-for-me?] :as message-data}
{:keys [edit-enabled show-input? community? can-delete-message-for-everyone?
message-pin-enabled group-chat group-admin?]}]
(concat
(when (and outgoing edit-enabled)
(when (and outgoing edit-enabled (not (or deleted? deleted-for-me?)))
[{:type :main
:on-press #(rf/dispatch [:chat.ui/edit-message message-data])
:label (i18n/label :t/edit-message)
:icon :i/edit
:id :edit}])
(when (and show-input? (not= outgoing-status :sending))
(when (and show-input? (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)
:icon :i/reply
:id :reply}])
[{:type :main
:on-press #(react/copy-to-clipboard
(components.reply/get-quoted-text-with-mentions
(get content :parsed-text)))
:label (i18n/label :t/copy-text)
:icon :i/copy
:id :copy}]
(when-not (or deleted? deleted-for-me?)
[{:type :main
:on-press #(react/copy-to-clipboard
(components.reply/get-quoted-text-with-mentions
(get content :parsed-text)))
:label (i18n/label :t/copy-text)
:icon :i/copy
:id :copy}])
(when message-pin-enabled
[{:type :main
:on-press #(pin-message message-data)
:label (i18n/label (if pinned-by
:label (i18n/label (if pinned
(if community? :t/unpin-from-channel :t/unpin-from-chat)
(if community? :t/pin-to-channel :t/pin-to-chat)))
:icon :i/pin
:id (if pinned-by :unpin :pin)}])
(when-not pinned-by
:id (if pinned :unpin :pin)}])
(when-not (or pinned deleted? deleted-for-me?)
[{:type :danger
:on-press (fn []
(rf/dispatch
@ -62,7 +63,13 @@
:label (i18n/label :t/delete-for-me)
:icon :i/delete
:id :delete-for-me}])
(when (and (or outgoing can-delete-message-for-everyone?) config/delete-message-enabled?)
(when (cond
deleted? false
deleted-for-me? false
outgoing true
community? can-delete-message-for-everyone?
group-chat group-admin?
:else false)
[{:type :danger
:on-press (fn []
(rf/dispatch [:bottom-sheet/hide])
@ -119,7 +126,8 @@
icon]])))]))
(defn reactions-and-actions
[{:keys [message-id outgoing-status] :as message-data} {:keys [chat-id] :as context}]
[{:keys [message-id outgoing-status deleted? deleted-for-me?] :as message-data}
{:keys [chat-id] :as context}]
(fn []
(let [actions (get-actions message-data context)
main-actions (filter #(= (:type %) :main) actions)
@ -127,7 +135,7 @@
admin-actions (filter #(= (:type %) :admin) actions)]
[:<>
;; REACTIONS
(when (not= outgoing-status :sending)
(when (and (not= outgoing-status :sending) (not (or deleted? deleted-for-me?)))
[reactions {:chat-id chat-id :message-id message-id}])
;; MAIN ACTIONS

View File

@ -1,19 +1,19 @@
(ns status-im2.contexts.chat.messages.list.view
(:require [utils.i18n :as i18n]
[oops.core :as oops]
(:require [oops.core :as oops]
[quo2.core :as quo]
[react-native.background-timer :as background-timer]
[react-native.core :as rn]
[react-native.platform :as platform]
[reagent.core :as reagent]
[utils.re-frame :as rf]
[status-im2.contexts.chat.messages.content.view :as message]
[status-im.ui.screens.chat.group :as chat.group]
[status-im.ui.screens.chat.message.gap :as message.gap]
[status-im2.common.not-implemented :as not-implemented]
[status-im2.constants :as constants]
[status-im2.contexts.chat.messages.content.deleted.view :as content.deleted]
[status-im2.common.not-implemented :as not-implemented]
[status-im.ui.screens.chat.group :as chat.group]
[status-im2.contexts.chat.messages.content.view :as message]
[status-im2.contexts.chat.messages.list.state :as state]
[status-im.ui.screens.chat.message.gap :as message.gap]))
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defonce messages-list-ref (atom nil))
@ -69,6 +69,7 @@
(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])
@ -82,6 +83,7 @@
(or group-admin?
community-admin?))))]
{:group-chat group-chat
:group-admin? group-admin?
:public? public?
:community? (not (nil? community-id))
:current-public-key current-public-key
@ -123,7 +125,7 @@
[message.gap/gap message-data]]
[rn/view {:padding-horizontal 8}
(if (or deleted? deleted-for-me?)
[content.deleted/deleted-message message-data]
[content.deleted/deleted-message message-data context]
[message/message-with-reactions message-data context])]))])
(defn messages-list

View File

@ -1,15 +1,21 @@
(ns status-im2.contexts.chat.messages.pin.banner.view
(:require [quo2.core :as quo]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn banner
[chat-id]
(let [pinned-messages (rf/sub [:chats/pinned-sorted-list chat-id])
latest-pin-text (->> pinned-messages
last
:content
:text)
pins-count (count pinned-messages)]
(let [pinned-messages (rf/sub [:chats/pinned-sorted-list
chat-id])
latest-pinned-message (last pinned-messages)
latest-pin-text (get-in latest-pinned-message [:content :text])
{:keys [deleted? deleted-for-me?]} latest-pinned-message
pins-count (count pinned-messages)
latest-pin-text
(cond deleted? (i18n/label :t/message-deleted-for-everyone)
deleted-for-me? (i18n/label :t/message-deleted-for-you)
:else latest-pin-text)]
[quo/banner
{:latest-pin-text latest-pin-text
:pins-count pins-count

View File

@ -1,14 +1,14 @@
(ns status-im2.contexts.chat.messages.pin.events
(:require [re-frame.core :as re-frame]
[utils.i18n :as i18n]
[quo2.foundations.colors :as colors]
[status-im2.contexts.chat.messages.list.events :as message-list]
[status-im2.common.toasts.events :as toasts]
[status-im2.constants :as constants]
(:require [quo2.foundations.colors :as colors]
[re-frame.core :as re-frame]
[status-im.data-store.pin-messages :as data-store.pin-messages]
[status-im.transport.message.protocol :as protocol]
[utils.re-frame :as rf]
[taoensso.timbre :as log]))
[status-im2.common.toasts.events :as toasts]
[status-im2.constants :as constants]
[status-im2.contexts.chat.messages.list.events :as message-list]
[taoensso.timbre :as log]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(rf/defn handle-failed-loading-pin-messages
{:events [:pin-message/failed-loading-pin-messages]}
@ -59,23 +59,34 @@
(assoc-in [:pin-message-lists chat-id]
(message-list/add-many nil (vals all-messages))))}))))
(rf/defn send-pin-message
"Pin message, rebuild pinned messages list"
{:events [:pin-message/send-pin-message]}
(rf/defn send-pin-message-locally
"Pin message, rebuild pinned messages list locally"
{:events [:pin-message/send-pin-message-locally]}
[{:keys [db] :as cofx} {:keys [chat-id message-id pinned] :as pin-message}]
(let [current-public-key (get-in db [:multiaccount :public-key])
message (merge pin-message {:pinned-by current-public-key})
preferred-name (get-in db [:multiaccount :preferred-name])]
(let [current-public-key (get-in db [:multiaccount :public-key])
message (merge pin-message {:pinned-by current-public-key})
pin-message-lists-exist? (some? (get-in db [:pin-message-lists chat-id]))]
(rf/merge cofx
{:db (cond-> db
pinned
(->
(update-in [:pin-message-lists chat-id] message-list/add message)
(assoc-in [:pin-messages chat-id message-id] message))
(not pinned)
(and (not pinned) pin-message-lists-exist?)
(->
(update-in [:pin-message-lists chat-id] message-list/remove-message pin-message)
(update-in [:pin-messages chat-id] dissoc message-id)))}
(update-in [:pin-messages chat-id] dissoc message-id)))})))
(rf/defn send-pin-message
"Pin message, rebuild pinned messages list"
{:events [:pin-message/send-pin-message]}
[{:keys [db] :as cofx} {:keys [chat-id message-id pinned remote-only?] :as pin-message}]
(let [current-public-key (get-in db [:multiaccount :public-key])
message (merge pin-message {:pinned-by current-public-key})
preferred-name (get-in db [:multiaccount :preferred-name])]
(rf/merge cofx
(when-not remote-only? (send-pin-message-locally pin-message))
(data-store.pin-messages/send-pin-message {:chat-id (pin-message :chat-id)
:message_id (pin-message :message-id)
:pinned (pin-message :pinned)})

View File

@ -1,37 +1,33 @@
(ns status-im2.contexts.chat.messages.pin.list.view
(:require [utils.i18n :as i18n]
[quo2.core :as quo]
(:require [quo2.core :as quo]
[quo2.foundations.colors :as colors]
[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]
[utils.re-frame :as rf]
[utils.datetime :as datetime]))
[status-im2.contexts.chat.messages.list.view :as list.view]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(def list-key-fn #(or (:message-id %) (:value %)))
(defn message-render-fn
[{:keys [whisper-timestamp] :as message}
_
{:keys [group-chat public? community? current-public-key show-input? edit-enabled]}]
[{:keys [deleted? deleted-for-me?] :as message} _ _ context]
;; TODO (flexsurfer) probably we don't want reactions here
[message/message-with-reactions
message
{:group-chat group-chat
:public? public?
:community? community?
:current-public-key current-public-key
:show-input? show-input?
:message-pin-enabled true
:in-pinned-view? true
:pinned true
:timestamp-str (datetime/timestamp->time whisper-timestamp)
:edit-enabled edit-enabled}])
(if (or deleted? deleted-for-me?)
[content.deleted/deleted-message message context]
[message/message-with-reactions message context]))
(defn pinned-messages-list
[chat-id]
(let [pinned-messages (vec (vals (rf/sub [:chats/pinned chat-id])))
current-chat (rf/sub [:chat-by-id chat-id])
community (rf/sub [:communities/community (:community-id current-chat)])]
(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])]
[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
@ -62,10 +58,18 @@
(str "# " (:chat-name current-chat))]])]
(if (> (count pinned-messages) 0)
[rn/flat-list
{:data pinned-messages
:render-fn message-render-fn
:key-fn list-key-fn
:separator quo/separator}]
{: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-fn message-render-fn
:key-fn list-key-fn
:separator quo/separator}]
[rn/view
{:style {:justify-content :center
:align-items :center

View File

@ -1,9 +1,9 @@
(ns status-im2.subs.chat.messages
(:require [re-frame.core :as re-frame]
[status-im2.contexts.chat.messages.list.events :as models.message-list]
[status-im.chat.models.reactions :as models.reactions]
[utils.datetime :as datetime]
[status-im2.constants :as constants]))
[status-im2.constants :as constants]
[status-im2.contexts.chat.messages.list.events :as models.message-list]
[utils.datetime :as datetime]))
(defn intersperse-datemark
"Reduce step which expects the input list of messages to be sorted by clock value.
@ -24,7 +24,7 @@
:acc (conj acc msg)}
(and (not= last-datemark datemark) ; not the same day
(< whisper-timestamp last-timestamp)) ; not out-of-order
(< whisper-timestamp last-timestamp)) ; not out-of-order
{:last-timestamp whisper-timestamp
:last-datemark datemark
:acc (conj acc
@ -32,7 +32,7 @@
:type :datemark}
msg)}
:else
{:last-timestamp (min whisper-timestamp last-timestamp) ; use last datemark
{:last-timestamp (min whisper-timestamp last-timestamp) ; use last datemark
:last-datemark last-datemark
:acc (conj acc (assoc msg :datemark last-datemark))}))
@ -158,14 +158,18 @@
[(re-frame/subscribe [:messages/pin-messages])
(re-frame/subscribe [:chats/chat-messages chat-id])])
(fn [[pin-messages messages] [_ chat-id]]
(let [pin-messages (get pin-messages chat-id {})]
(reduce-kv (fn [acc message-id message]
(let [{:keys [deleted? deleted-for-me?]} (get messages message-id)]
(if (or deleted? deleted-for-me?)
acc
(assoc acc message-id message))))
{}
pin-messages))))
(let [pinned-messages (get pin-messages chat-id {})]
(reduce-kv
(fn [pinned-messages pinned-message-id pinned-message]
(let [{:keys [deleted? deleted-for-me? deleted-by]} (get messages pinned-message-id)
pinned-message (assoc pinned-message
:deleted? deleted?
:deleted-for-me? deleted-for-me?
:deleted-by deleted-by
:pinned true)]
(assoc pinned-messages pinned-message-id pinned-message)))
pinned-messages
pinned-messages))))
;; local messages will not have a :pinned-at key until user navigates away and to
;; chat screen. For this reason we want to retain order of local messages with :pinned-at nil

View File

@ -1,10 +1,10 @@
(ns status-im2.subs.chat.messages-test
(:require [cljs.test :refer [deftest is testing]]
[re-frame.db :as rf-db]
[status-im2.constants :as constants]
[status-im2.subs.chat.messages :as messages]
[test-helpers.unit :as h]
[utils.re-frame :as rf]
[re-frame.db :as rf-db]
[status-im2.constants :as constants]))
[utils.re-frame :as rf]))
(def messages-state
[{:message-id "0x111" :album-id "abc" :albumize? true}
@ -109,51 +109,91 @@
[sub-name]
(testing "It sorts three messages with pinned-at property"
(swap! rf-db/app-db assoc :pin-messages pinned-messages-state)
(is (= [{:chat-id :0xChat
:message-id :0x1
:pinned-at 1000
:pinned-by :test-user}
{:chat-id :0xChat
:message-id :0x2
:pinned-at 2000
:pinned-by :test-user}
{:chat-id :0xChat
:message-id :0x3
:pinned-at 3000
:pinned-by :test-user}]
(is (= [{:chat-id :0xChat
:message-id :0x1
:pinned-at 1000
:pinned-by :test-user
:pinned true
:deleted? nil
:deleted-for-me? nil
:deleted-by nil}
{:chat-id :0xChat
:message-id :0x2
:pinned-at 2000
:pinned-by :test-user
:pinned true
:deleted? nil
:deleted-for-me? nil
:deleted-by nil}
{:chat-id :0xChat
:message-id :0x3
:pinned-at 3000
:pinned-by :test-user
:pinned true
:deleted? nil
:deleted-for-me? nil
:deleted-by nil}]
(rf/sub [sub-name :0xChat]))))
(testing "It sorts messages from backend with pinned-at property and 1 new local pinned message"
(swap! rf-db/app-db assoc :pin-messages pinned-messages-state-with-1-new-local-message)
(is (= [{:chat-id :0xChat
:message-id :0x1
:pinned-at 2000
:pinned-by :test-user}
{:chat-id :0xChat
:message-id :0x2
:pinned-at 3000
:pinned-by :test-user}
{:chat-id :0xChat
:message-id :0x3
:pinned-at nil
:pinned-by :test-user}]
(is (= [{:chat-id :0xChat
:message-id :0x1
:pinned-at 2000
:pinned-by :test-user
:pinned true
:deleted? nil
:deleted-for-me? nil
:deleted-by nil}
{:chat-id :0xChat
:message-id :0x2
:pinned-at 3000
:pinned-by :test-user
:pinned true
:deleted? nil
:deleted-for-me? nil
:deleted-by nil}
{:chat-id :0xChat
:message-id :0x3
:pinned-at nil
:pinned-by :test-user
:pinned true
:deleted? nil
:deleted-for-me? nil
:deleted-by nil}]
(rf/sub [sub-name :0xChat]))))
(testing "It sorts messages from backend with pinned-at property and 2 new local pinned messages"
(swap! rf-db/app-db assoc :pin-messages pinned-messages-state-with-2-new-local-messages)
(is (= [{:chat-id :0xChat
:message-id :0x1
:pinned-at 2000
:pinned-by :test-user}
{:chat-id :0xChat
:message-id :0x2
:pinned-at 3000
:pinned-by :test-user}
{:chat-id :0xChat
:message-id :0x3
:pinned-at nil
:pinned-by :test-user}
{:chat-id :0xChat
:message-id :0x4
:pinned-at nil
:pinned-by :test-user}]
(rf/sub [sub-name :0xChat])))))
(is
(= [{:chat-id :0xChat
:message-id :0x1
:pinned-at 2000
:pinned-by :test-user
:pinned true
:deleted? nil
:deleted-for-me? nil
:deleted-by nil}
{:chat-id :0xChat
:message-id :0x2
:pinned-at 3000
:pinned-by :test-user
:pinned true
:deleted? nil
:deleted-for-me? nil
:deleted-by nil}
{:chat-id :0xChat
:message-id :0x3
:pinned-at nil
:pinned-by :test-user
:pinned true
:deleted? nil
:deleted-for-me? nil
:deleted-by nil}
{:chat-id :0xChat
:message-id :0x4
:pinned-at nil
:pinned-by :test-user
:pinned true
:deleted? nil
:deleted-for-me? nil
:deleted-by nil}]
(rf/sub [sub-name :0xChat])))))