fix: delete message action logic (#14977)
This commit is contained in:
parent
ee5dcb3683
commit
866ba63ab7
|
@ -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]]))])
|
||||
|
|
|
@ -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
|
||||
[]
|
||||
|
|
|
@ -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]}]
|
||||
|
|
|
@ -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")))
|
||||
|
|
|
@ -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)}])))
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)})
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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])))))
|
||||
|
|
Loading…
Reference in New Issue