feat: delete for me logic without UI (#14007)
This commit is contained in:
parent
d6a8f912ea
commit
bc050af9ed
|
@ -0,0 +1,68 @@
|
|||
(ns status-im.chat.models.delete-message-for-me
|
||||
(:require [re-frame.core :as re-frame]
|
||||
[status-im.ethereum.json-rpc :as json-rpc]
|
||||
[status-im.utils.datetime :as datetime]
|
||||
[status-im.utils.fx :as fx]
|
||||
[taoensso.timbre :as log]))
|
||||
|
||||
(defn- update-db-clear-undo-timer
|
||||
[db chat-id message-id]
|
||||
(when (get-in db [:messages chat-id message-id])
|
||||
(update-in db
|
||||
[:messages chat-id message-id]
|
||||
dissoc
|
||||
:deleted-for-me-undoable-till)))
|
||||
|
||||
(defn- update-db-delete-locally
|
||||
"Delete message for me in re-frame db and set the undo timelimit"
|
||||
[db chat-id message-id undo-time-limit-ms]
|
||||
(when (get-in db [:messages chat-id message-id])
|
||||
(update-in db
|
||||
[:messages chat-id message-id]
|
||||
assoc
|
||||
:deleted-for-me? true
|
||||
:deleted-for-me-undoable-till (+ (datetime/timestamp)
|
||||
undo-time-limit-ms))))
|
||||
|
||||
(defn- update-db-undo-locally
|
||||
"Restore deleted-for-me message if called within timelimit"
|
||||
[db chat-id message-id]
|
||||
(let [{:keys [deleted-for-me? deleted-for-me-undoable-till]}
|
||||
(get-in db [:messages chat-id message-id])]
|
||||
(if (and deleted-for-me?
|
||||
(> deleted-for-me-undoable-till (datetime/timestamp)))
|
||||
(update-in db
|
||||
[:messages chat-id message-id]
|
||||
dissoc
|
||||
:deleted-for-me?
|
||||
:deleted-for-me-undoable-till)
|
||||
(update-db-clear-undo-timer db chat-id message-id))))
|
||||
|
||||
(fx/defn delete
|
||||
"Delete message for me now locally and broadcast after undo time limit timeout"
|
||||
{:events [:chat.ui/delete-message-for-me]}
|
||||
[{:keys [db]} {:keys [chat-id message-id]} undo-time-limit-ms]
|
||||
(when (get-in db [:messages chat-id message-id])
|
||||
{:db (update-db-delete-locally db chat-id message-id undo-time-limit-ms)
|
||||
:utils/dispatch-later [{:dispatch [:chat.ui/delete-message-for-me-and-sync
|
||||
{:chat-id chat-id
|
||||
:message-id message-id}]
|
||||
:ms undo-time-limit-ms}]}))
|
||||
|
||||
(fx/defn undo
|
||||
{:events [:chat.ui/undo-delete-message-for-me]}
|
||||
[{:keys [db]} {:keys [chat-id message-id]}]
|
||||
(when (get-in db [:messages chat-id message-id])
|
||||
{:db (update-db-undo-locally db chat-id message-id)}))
|
||||
|
||||
(fx/defn delete-and-sync
|
||||
{:events [:chat.ui/delete-message-for-me-and-sync]}
|
||||
[{:keys [db]} {:keys [message-id chat-id]}]
|
||||
(when (get-in db [:messages chat-id message-id])
|
||||
{:db (update-db-clear-undo-timer db chat-id message-id)
|
||||
::json-rpc/call [{:method "wakuext_deleteMessageForMeAndSync"
|
||||
:params [chat-id message-id]
|
||||
:js-response true
|
||||
:on-error #(log/error "failed to delete message for me " %)
|
||||
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response
|
||||
%])}]}))
|
|
@ -0,0 +1,113 @@
|
|||
(ns status-im.chat.models.delete-message-for-me-test
|
||||
(:require [cljs.test :refer-macros [deftest is testing]]
|
||||
[status-im.chat.models.delete-message-for-me :as
|
||||
delete-message-for-me]
|
||||
[status-im.utils.datetime :as datetime]))
|
||||
|
||||
(defonce mid "message-id")
|
||||
(defonce cid "chat-id")
|
||||
|
||||
(deftest delete-for-me
|
||||
(let [db {:messages {cid {mid {:id mid}}}}
|
||||
message {:message-id mid :chat-id cid}]
|
||||
(testing "delete for me"
|
||||
(let [expected {:db {:messages {"chat-id" {"message-id"
|
||||
{:id "message-id"
|
||||
:deleted-for-me? true
|
||||
:deleted-for-me-undoable-till
|
||||
(+ (datetime/timestamp)
|
||||
1000)}}}}
|
||||
:utils/dispatch-later
|
||||
[{:dispatch [:chat.ui/delete-message-for-me-and-sync
|
||||
{:chat-id "chat-id" :message-id "message-id"}]
|
||||
:ms 1000}]}]
|
||||
(is (= (delete-message-for-me/delete {:db db} message 1000) expected))))
|
||||
(testing "should return nil if message in db"
|
||||
(is (= (delete-message-for-me/delete {:db {:messages []}} message 1000)
|
||||
nil)))))
|
||||
|
||||
(deftest undo-delete-for-me
|
||||
(let [db {:messages {cid {mid {:id mid}}}}
|
||||
message {:message-id mid :chat-id cid}]
|
||||
(testing "undo delete for me in time"
|
||||
(let [db (update-in db
|
||||
[:messages cid mid]
|
||||
assoc
|
||||
:deleted-for-me? true
|
||||
:deleted-for-me-undoable-till
|
||||
(+ (datetime/timestamp) 1000))
|
||||
|
||||
expected {:db {:messages {"chat-id" {"message-id"
|
||||
{:id "message-id"}}}}}]
|
||||
(is (= (delete-message-for-me/undo {:db db} message) expected))))
|
||||
(testing "remain deleted for me when undo delete for me late"
|
||||
(let [db (update-in db
|
||||
[:messages cid mid]
|
||||
assoc
|
||||
:deleted-for-me? true
|
||||
:deleted-for-me-undoable-till
|
||||
(- (datetime/timestamp) 1000))
|
||||
|
||||
expected {:db {:messages {"chat-id" {"message-id" {:id "message-id"
|
||||
:deleted-for-me?
|
||||
true}}}}}]
|
||||
(is (= (delete-message-for-me/undo {:db db} message) expected))))
|
||||
(testing "remain deleted for me when undo delete for me late"
|
||||
(let [db (update-in db
|
||||
[:messages cid mid]
|
||||
assoc
|
||||
:deleted-for-me? true
|
||||
:deleted-for-me-undoable-till
|
||||
(- (datetime/timestamp) 1000))
|
||||
|
||||
expected {:db {:messages {"chat-id" {"message-id" {:id "message-id"
|
||||
:deleted-for-me?
|
||||
true}}}}}]
|
||||
(is (= (delete-message-for-me/undo {:db db} message) expected))))
|
||||
(testing "should return nil if message in db"
|
||||
(is (= (delete-message-for-me/undo {:db {:messages []}} message)
|
||||
nil)))))
|
||||
|
||||
(deftest delete-for-me-and-sync
|
||||
(let [db {:messages {cid {mid {:id mid}}}}
|
||||
message {:message-id mid :chat-id cid}]
|
||||
(testing "delete for me and sync"
|
||||
(let [expected-db {:messages {"chat-id" {"message-id" {:id "message-id"}}}}
|
||||
effects (delete-message-for-me/delete-and-sync {:db db} message)
|
||||
result-db (:db effects)
|
||||
rpc-calls (:status-im.ethereum.json-rpc/call effects)]
|
||||
(is (= result-db expected-db))
|
||||
(is (= (count rpc-calls) 1))
|
||||
(is (= (-> rpc-calls
|
||||
first
|
||||
:method)
|
||||
"wakuext_deleteMessageForMeAndSync"))
|
||||
(is (= (-> rpc-calls
|
||||
first
|
||||
:params
|
||||
count)
|
||||
2))
|
||||
(is (= (-> rpc-calls
|
||||
first
|
||||
:params
|
||||
first)
|
||||
cid))
|
||||
(is (= (-> rpc-calls
|
||||
first
|
||||
:params
|
||||
second)
|
||||
mid))))
|
||||
(testing "delete for me and sync, should clean undo timer"
|
||||
(let [expected-db {:messages {"chat-id" {"message-id" {:id "message-id"}}}}
|
||||
effects (delete-message-for-me/delete-and-sync
|
||||
{:db (update-in db
|
||||
[:messages cid mid
|
||||
:deleted-for-me-undoable-till]
|
||||
(constantly (datetime/timestamp)))}
|
||||
message)
|
||||
result-db (:db effects)]
|
||||
(is (= result-db expected-db))))
|
||||
(testing "should return nil if message in db"
|
||||
(is (= (delete-message-for-me/delete-and-sync {:db {:messages []}}
|
||||
message)
|
||||
nil)))))
|
|
@ -31,6 +31,7 @@
|
|||
:quotedMessage :quoted-message
|
||||
:outgoingStatus :outgoing-status
|
||||
:audioDurationMs :audio-duration-ms
|
||||
:deletedForMe :deleted-for-me?
|
||||
:new :new?})
|
||||
|
||||
(update :quoted-message clojure.set/rename-keys {:parsedText :parsed-text :communityId :community-id})
|
||||
|
|
|
@ -1,34 +1,35 @@
|
|||
(ns status-im.ui.screens.chat.message.message
|
||||
(:require [re-frame.core :as re-frame]
|
||||
(:require [quo.core :as quo]
|
||||
[quo.design-system.colors :as colors]
|
||||
[re-frame.core :as re-frame]
|
||||
[reagent.core :as reagent]
|
||||
[status-im.chat.models.delete-message-for-me]
|
||||
[status-im.chat.models.images :as images]
|
||||
[status-im.chat.models.pin-message :as models.pin-message]
|
||||
[status-im.chat.models.reactions :as models.reactions]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im.react-native.resources :as resources]
|
||||
[quo.design-system.colors :as colors]
|
||||
[status-im.ui.components.animation :as animation]
|
||||
[status-im.ui.components.fast-image :as fast-image]
|
||||
[status-im.ui.components.icons.icons :as icons]
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im.ui.screens.chat.bottom-sheets.context-drawer :as message-context-drawer]
|
||||
[status-im.ui.screens.chat.components.reply :as components.reply]
|
||||
[status-im.ui.screens.chat.image.preview.views :as preview]
|
||||
[status-im.ui.screens.chat.message.audio :as message.audio]
|
||||
[status-im.chat.models.reactions :as models.reactions]
|
||||
[status-im.ui.screens.chat.message.command :as message.command]
|
||||
[status-im.ui.screens.chat.message.gap :as message.gap]
|
||||
[status-im.ui.screens.chat.message.link-preview :as link-preview]
|
||||
[status-im.ui.screens.chat.message.reactions :as reactions]
|
||||
[status-im.ui.screens.chat.message.reactions-row :as reaction-row]
|
||||
[status-im.ui.screens.chat.photos :as photos]
|
||||
[status-im.ui.screens.chat.sheets :as sheets]
|
||||
[status-im.ui.screens.chat.message.gap :as message.gap]
|
||||
[status-im.ui.screens.chat.styles.message.message :as style]
|
||||
[status-im.ui.screens.chat.utils :as chat.utils]
|
||||
[status-im.utils.security :as security]
|
||||
[status-im.ui.screens.chat.message.reactions :as reactions]
|
||||
[status-im.ui.screens.chat.image.preview.views :as preview]
|
||||
[quo.core :as quo]
|
||||
[status-im.utils.config :as config]
|
||||
[reagent.core :as reagent]
|
||||
[status-im.ui.screens.chat.components.reply :as components.reply]
|
||||
[status-im.ui.screens.chat.message.link-preview :as link-preview]
|
||||
[status-im.ui.screens.communities.icon :as communities.icon]
|
||||
[status-im.ui.components.animation :as animation]
|
||||
[status-im.chat.models.images :as images]
|
||||
[status-im.chat.models.pin-message :as models.pin-message]
|
||||
[status-im.ui.components.fast-image :as fast-image]
|
||||
[status-im.ui.screens.chat.bottom-sheets.context-drawer :as message-context-drawer]
|
||||
[status-im.ui.screens.chat.message.reactions-row :as reaction-row])
|
||||
[status-im.utils.config :as config]
|
||||
[status-im.utils.security :as security])
|
||||
(:require-macros [status-im.utils.views :refer [defview letsubs]]))
|
||||
|
||||
(defn message-timestamp-anim
|
||||
|
@ -294,7 +295,8 @@
|
|||
"Author, userpic and delivery wrapper"
|
||||
[{:keys [last-in-group?
|
||||
identicon
|
||||
from in-popover? timestamp-str]
|
||||
from in-popover? timestamp-str
|
||||
deleted-for-me?]
|
||||
:as message} content {:keys [modal close-modal]}]
|
||||
(let [response-to (:response-to (:content message))]
|
||||
[react/view {:style (style/message-wrapper message)
|
||||
|
@ -325,7 +327,12 @@
|
|||
:accessibility-label :message-timestamp}
|
||||
timestamp-str]])
|
||||
;; MESSAGE CONTENT
|
||||
content
|
||||
;; TODO(yqrashawn): wait for system message component to display deleted for me UI
|
||||
(if deleted-for-me?
|
||||
[react/view {:style {:border-width 2
|
||||
:border-color :red}}
|
||||
content]
|
||||
content)
|
||||
[link-preview/link-preview-wrapper (:links (:content message)) false false]]]
|
||||
; delivery status
|
||||
[react/view (style/delivery-status)
|
||||
|
@ -436,6 +443,13 @@
|
|||
:label (i18n/label (if pinned :t/unpin-from-chat :t/pin-to-chat))
|
||||
:icon :main-icons/pin-context20
|
||||
:id (if pinned :unpin :pin)}])
|
||||
[{:type :danger
|
||||
:on-press #(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 :main-icons/delete-context20
|
||||
:id :delete-for-me}]
|
||||
(when (and outgoing config/delete-message-enabled?)
|
||||
[{:type :danger
|
||||
:on-press #(re-frame/dispatch [:chat.ui/soft-delete-message message])
|
||||
|
@ -581,6 +595,13 @@
|
|||
:id :share
|
||||
:icon :main-icons/share-context20
|
||||
:label (i18n/label :t/share-image)}]
|
||||
[{:type :danger
|
||||
:on-press #(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 :main-icons/delete-context20
|
||||
:id :delete-for-me}]
|
||||
(when (and outgoing config/delete-message-enabled?)
|
||||
[{:type :danger
|
||||
:on-press #(re-frame/dispatch [:chat.ui/soft-delete-message message])
|
||||
|
@ -611,6 +632,13 @@
|
|||
:label (i18n/label (if pinned :t/unpin-from-chat :t/pin-to-chat))
|
||||
:icon :main-icons/pin-context20
|
||||
:id (if pinned :unpin :pin)}
|
||||
{:type :danger
|
||||
:on-press #(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 :main-icons/delete-context20
|
||||
:id :delete-for-me}
|
||||
(when (and outgoing config/delete-message-enabled?)
|
||||
{:type :danger
|
||||
:on-press #(re-frame/dispatch [:chat.ui/soft-delete-message message])
|
||||
|
|
|
@ -182,3 +182,5 @@
|
|||
;; usable enough to replace the old one **in the new UI**.
|
||||
(def new-activity-center-enabled?
|
||||
(atom false))
|
||||
|
||||
(def delete-message-for-me-undo-time-limit-ms 4000)
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"_comment": "Instead use: scripts/update-status-go.sh <rev>",
|
||||
"owner": "status-im",
|
||||
"repo": "status-go",
|
||||
"version": "v0.110.3",
|
||||
"commit-sha1": "506921509e477445a95e1961a2557ef584d7caec",
|
||||
"src-sha256": "1xmngbfl67n031rxhhbs7m14gzmfhgr1d34szclf1a6qxb9kw946"
|
||||
"version": "f47cb8572de921fb4cff5d0f8d87b7b92386a226",
|
||||
"commit-sha1": "f47cb8572de921fb4cff5d0f8d87b7b92386a226",
|
||||
"src-sha256": "0wbiwq6dh531crqsij836bdkggn8f1z9f121aqpyxwdgp1njzxjk"
|
||||
}
|
||||
|
|
|
@ -418,6 +418,7 @@
|
|||
"delete-chat-confirmation": "Are you sure you want to delete this chat?",
|
||||
"delete-category-confirmation": "Are you sure you want to delete this category?",
|
||||
"delete-confirmation": "Delete?",
|
||||
"delete-for-me": "Delete for me",
|
||||
"delete-mailserver": "Delete Status node",
|
||||
"delete-mailserver-are-you-sure": "Are you sure you want to delete this Status node?",
|
||||
"delete-mailserver-title": "Delete Status node",
|
||||
|
|
Loading…
Reference in New Issue