chore: cleanup event definitions and tests for contact-requests and deleting messages (#21885)

* chore: add error logging events and effects

* chore: restructure tests and effects for delete-message-for-me

* chore: refactor events and effects and tests for chat-contacts

* chore: refactor and add tests for accepting and declining contact request

* chore: refactor chat-contacts rf/defn to defn

* chore: add :fx key as a mergeable effect for rf/merge when running tests

* chore: refactor rf/defn and add tests for delete-message-for-me

* chore: remove log effects for contacts

* chore: remove logs effects for delete-message-for-me

* fix: update chat usage of sync-all

* fix: resolve schema error with missing action-label for contact-request dismissed state

* fix: ensure chat-contacts event error handlers are compatible with re-frame

* fix: ensure delete-message-for-me event error handlers are compatible with re-frame

* chore: refactor and test delete-message events

* chore: refactor log events and effects

* chore: use log effect in delete-message

* chore: use log events and effects inside chat-contacts

* chore: use log effects inside delete-message-for-me

* chore: remove unneeded test for delete-message-for-me

* chore: format bottom-drawer component

* chore: format delete-message

* chore: format delete-message-for-me
This commit is contained in:
Sean Hagstrom 2025-01-14 06:41:47 -08:00 committed by GitHub
parent 2d8ba41d89
commit 5dc1bfe03b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 552 additions and 184 deletions

View File

@ -5,7 +5,6 @@
[legacy.status-im.data-store.messages :as data-store.messages]
[legacy.status-im.utils.deprecated-types :as types]
[react-native.platform :as platform]
[status-im.contexts.chat.messenger.messages.delete-message.events :as delete-message]
[status-im.contexts.chat.messenger.messages.list.events :as message-list]
[status-im.contexts.chat.messenger.messages.list.state :as view.state]
[utils.re-frame :as rf]))
@ -119,6 +118,26 @@
(when (get-in db [:messages chat-id message-id])
{:db (assoc-in db [:messages chat-id message-id :outgoing-status] status)}))
(defn- update-db-delete-locally-without-time-limit
"Delete message in re-frame db, used to handle received removed-messages"
[db chat-id message-id deleted-by]
(when (get-in db [:messages chat-id message-id])
(update-in db [:messages chat-id message-id] assoc :deleted? true :deleted-by deleted-by)))
(rf/defn delete-messages-locally
"Mark messages :deleted? localy in client"
{:events [:chat.ui/delete-messages-localy]}
[{:keys [db]} messages chat-id]
(let [new-db (->> messages
(filter #(get-in db [:messages chat-id (:message-id %)]))
(reduce #(update-db-delete-locally-without-time-limit %1
chat-id
(:message-id %2)
(:deleted-by %2))
db))]
(when new-db
(message-list/rebuild-message-list {:db new-db} chat-id))))
(rf/defn handle-removed-messages
[cofx removed-messages]
(let [mark-as-deleted-fx (->> removed-messages
@ -127,7 +146,7 @@
:deleted-by (:deletedBy %)))
(group-by :chatId)
(mapv (fn [[chat-id messages]]
(delete-message/delete-messages-localy messages chat-id))))
(delete-messages-locally messages chat-id))))
mark-as-seen-fx (mapv
(fn [removed-message]
(let [chat-id (:chatId removed-message)

View File

@ -129,7 +129,8 @@
:transport/confirm-messages-processed
:group-chats/extract-membership-signature
:utils/dispatch-later
:json-rpc/call})
:json-rpc/call
:fx})
(let [opts (parse-args args)]
(execute-cli opts)))

View File

@ -38,3 +38,75 @@
:logs/set-level
(fn [level]
(setup level)))
(defn log-error
([error]
(log/error error))
([message error]
(log/error message error))
([message context error]
(log/error message context error)))
(re-frame/reg-event-fx
:log/error
(fn [_ args]
{:fx [[:effects.log/error args]]}))
(re-frame/reg-fx
:effects.log/error
(fn [args]
(apply log-error args)))
(defn log-info
([info]
(log/info info))
([message info]
(log/info message info))
([message context info]
(log/info message context info)))
(re-frame/reg-event-fx
:log/info
(fn [_ args]
{:fx [[:effects.log/info args]]}))
(re-frame/reg-fx
:effects.log/info
(fn [args]
(apply log-info args)))
(defn log-warn
([warning]
(log/warn warning))
([message warning]
(log/warn message warning))
([message context warning]
(log/warn message context warning)))
(re-frame/reg-event-fx
:log/warn
(fn [_ args]
{:fx [[:effects.log/warn args]]}))
(re-frame/reg-fx
:effects.log/warn
(fn [args]
(apply log-warn args)))
(defn log-debug
([value]
(log/debug value))
([message value]
(log/debug message value))
([message context value]
(log/debug message context value)))
(re-frame/reg-event-fx
:log/debug
(fn [_ args]
{:fx [[:effects.log/debug args]]}))
(re-frame/reg-fx
:effects.log/debug
(fn [args]
(apply log-debug args)))

View File

@ -4,7 +4,6 @@
[re-frame.core :as re-frame]
[status-im.common.json-rpc.events :as json-rpc]
[status-im.constants :as constants]
[taoensso.timbre :as log]
[utils.i18n :as i18n]
[utils.re-frame :as rf]
[utils.transforms :as transforms]))
@ -85,7 +84,7 @@
:params []
:js-response true
:on-success [:contacts/contacts-loaded]
:on-error #(log/error "failed to fetch contacts" %)})))
:on-error [:log/error "failed to fetch contacts"]})))
(defn send-contact-request
[{:keys [db]} [id message]]
@ -94,40 +93,61 @@
[{:method "wakuext_sendContactRequest"
:js-response true
:params [{:id id :message (or message (i18n/label :t/add-me-to-your-contacts))}]
:on-error [:contact.ui/send-contact-request-failure id]
:on-error [:contacts/send-contact-request-error id]
:on-success [:transport/message-sent]}]]]}))
(rf/reg-event-fx :contact.ui/send-contact-request send-contact-request)
(defn send-contact-request-failure
(defn send-contact-request-error
[_ [id error]]
(log/error "Failed to send contact request"
{:error error
:event :contact.ui/send-contact-request
:id id}))
{:fx [[:effects.log/error
["Failed to send contact request"
{:id id
:error error
:event :contact.ui/send-contact-request}]]]})
(rf/reg-event-fx :contact.ui/send-contact-request-failure send-contact-request-failure)
(rf/reg-event-fx :contact.ui/send-contact-request-error send-contact-request-error)
(rf/defn remove-contact
(defn remove-contact
"Remove a contact from current account's contact list"
{:events [:contact.ui/remove-contact-pressed]}
[{:keys [db]} {:keys [public-key]}]
{:db (-> db
(assoc-in [:contacts/contacts public-key :added?] false)
(assoc-in [:contacts/contacts public-key :active?] false)
(assoc-in [:contacts/contacts public-key :contact-request-state]
constants/contact-request-state-none))
:json-rpc/call [{:method "wakuext_retractContactRequest"
:params [{:id public-key}]
:js-response true
:on-success #(rf/dispatch [:sanitize-messages-and-process-response %])
:on-error #(log/error "failed to remove contact" public-key %)}]})
[{:keys [db]} [{:keys [public-key]}]]
{:db (-> db
(assoc-in [:contacts/contacts public-key :added?] false)
(assoc-in [:contacts/contacts public-key :active?] false)
(assoc-in [:contacts/contacts public-key :contact-request-state]
constants/contact-request-state-none))
:fx [[:json-rpc/call
[{:method "wakuext_retractContactRequest"
:params [{:id public-key}]
:js-response true
:on-success [:sanitize-messages-and-process-response]
:on-error [:contacts/remove-contact-error public-key]}]]]})
(rf/defn update-nickname
{:events [:contacts/update-nickname]}
[_ public-key nickname]
{:json-rpc/call [{:method "wakuext_setContactLocalNickname"
:params [{:id public-key :nickname nickname}]
:js-response true
:on-success #(rf/dispatch [:sanitize-messages-and-process-response %])
:on-error #(log/error "failed to set contact nickname " public-key nickname %)}]})
(rf/reg-event-fx :contact.ui/remove-contact-pressed remove-contact)
(defn remove-contact-error
[_ [public-key error]]
{:fx [[:effects.log/error ["failed to remove contact" public-key error]]]})
(rf/reg-event-fx :contacts/remove-contact-error remove-contact-error)
(defn update-nickname
[_ [public-key nickname]]
{:fx [[:json-rpc/call
[{:method "wakuext_setContactLocalNickname"
:params [{:id public-key :nickname nickname}]
:js-response true
:on-success [:sanitize-messages-and-process-response]
:on-error [:contacts/update-nickname-error public-key nickname]}]]]})
(rf/reg-event-fx :contacts/update-nickname update-nickname)
(defn update-nickname-error
[_ [public-key nickname error]]
{:fx [[:effects.log/error
["failed to set contact nickname"
{:public-key public-key
:nickname nickname}
error]]]})
(rf/reg-event-fx :contacts/update-nickname-error update-nickname-error)

View File

@ -2,6 +2,7 @@
(:require
[cljs.test :refer [deftest is testing]]
matcher-combinators.test
[status-im.constants :as constants]
[status-im.contexts.chat.contacts.events :as chat.contacts]
[utils.i18n :as i18n]))
@ -21,7 +22,7 @@
:js-response true
:params [{:id contact-public-key
:message (i18n/label :t/add-me-to-your-contacts)}]
:on-error [:contact.ui/send-contact-request-failure contact-public-key]
:on-error [:contacts/send-contact-request-error contact-public-key]
:on-success [:transport/message-sent]}]]]}
(chat.contacts/send-contact-request cofx [contact-public-key])))))
@ -36,6 +37,45 @@
:js-response true
:params [{:id contact-public-key
:message custom-message}]
:on-error [:contact.ui/send-contact-request-failure contact-public-key]
:on-error [:contacts/send-contact-request-error contact-public-key]
:on-success [:transport/message-sent]}]]]}
(chat.contacts/send-contact-request cofx [contact-public-key custom-message]))))))
(deftest remove-contact-test
(testing "removes existing contact"
(let [public-key "0x2"
initial-db {:contacts/contacts
{public-key
{:added? true
:active? true
:contact-request-state constants/contact-request-state-mutual}}}
expected-db {:contacts/contacts
{public-key
{:added? false
:active? false
:contact-request-state constants/contact-request-state-none}}}
expected-fx [[:json-rpc/call
[{:method "wakuext_retractContactRequest"
:params [{:id public-key}]
:js-response true
:on-success [:sanitize-messages-and-process-response]
:on-error [:contacts/remove-contact-error public-key]}]]]]
(is (match?
{:db expected-db
:fx expected-fx}
(chat.contacts/remove-contact {:db initial-db}
[{:public-key public-key}]))))))
(deftest update-nickname-test
(testing "updates contact nickname"
(let [public-key "0x2"
new-nickname "Joe"
expected-fx [[:json-rpc/call
[{:method "wakuext_setContactLocalNickname"
:params [{:id public-key :nickname new-nickname}]
:js-response true
:on-success [:sanitize-messages-and-process-response]
:on-error [:contacts/update-nickname-error public-key new-nickname]}]]]]
(is (match?
{:fx expected-fx}
(chat.contacts/update-nickname {:db {}} [public-key new-nickname]))))))

View File

@ -151,8 +151,10 @@
:effects.async-storage/set {:chat-id nil
:key-uid nil}}
(link-preview/reset-all)
(delete-for-me/sync-all)
(delete-message/send-all)
(fn [cofx]
(delete-for-me/sync-all cofx))
(fn [cofx]
(delete-message/send-all cofx))
(offload-messages chat-id)))))
(rf/defn deactivate-chat

View File

@ -28,6 +28,8 @@
constants/contact-request-state-received)
contact-request-pending? (= contact-request-state
constants/contact-request-state-sent)
contact-request-dismissed? (= contact-request-state
constants/contact-request-state-dismissed)
keycard? (rf/sub [:keycard/keycard-profile?])
keycard-feature-unavailable (rn/use-callback
#(rf/dispatch [:keycard/feature-unavailable-show]))]
@ -70,4 +72,8 @@
(i18n/label :t/contact-request-chat-received {:name primary-name})
contact-request-pending?
(i18n/label :t/contact-request-chat-pending))}]]))
(i18n/label :t/contact-request-chat-pending)
contact-request-dismissed?
(i18n/label :t/contact-request-chat-add {:name primary-name}))}]]))

View File

@ -1,7 +1,6 @@
(ns status-im.contexts.chat.messenger.messages.delete-message.events
(:require
[status-im.contexts.chat.messenger.messages.list.events :as message-list]
[taoensso.timbre :as log]
[utils.datetime :as datetime]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
@ -40,12 +39,6 @@
:deleted-undoable-till)
(update-db-clear-undo-timer db chat-id message-id))))
(defn- update-db-delete-locally-without-time-limit
"Delete message in re-frame db, used to handle received removed-messages"
[db chat-id message-id deleted-by]
(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
@ -67,10 +60,15 @@
message-id])]
(and pinned message-pin-enabled)))
(rf/defn delete
(defn toast-undo-delete-message-for-everyone
[]
(rf/dispatch [:chat.ui/undo-all-delete-message])
(rf/dispatch [:toasts/close
:delete-message-for-everyone]))
(defn delete
"Delete message now locally and broadcast after undo time limit timeout"
{:events [:chat.ui/delete-message]}
[{:keys [db]} {:keys [chat-id message-id]} undo-time-limit-ms]
[{:keys [db]} [{:keys [chat-id message-id]} undo-time-limit-ms]]
(when-let [message (get-in db [:messages chat-id message-id])]
;; all delete message toast are the same toast with id :delete-message-for-everyone new delete
;; operation will reset prev pending deletes' undo timelimit undo will undo all pending deletes
@ -113,9 +111,7 @@
:t/message-deleted-for-everyone-count)
:duration undo-time-limit-ms
: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]))}]
:undo-on-press toast-undo-delete-message-for-everyone}]
(when unpin?
[:pin-message/send-pin-message-locally
{:chat-id chat-id :message-id message-id :pinned false}])]
@ -125,9 +121,10 @@
:ms undo-time-limit-ms})
existing-undos)))))
(rf/defn undo
{:events [:chat.ui/undo-delete-message]}
[{:keys [db]} {:keys [chat-id message-id should-pin-back?]}]
(rf/reg-event-fx :chat.ui/delete-message delete)
(defn undo
[{:keys [db]} [{:keys [chat-id message-id should-pin-back?]}]]
(when (get-in db [:messages chat-id message-id])
(let [effects (message-list/rebuild-message-list
{:db (update-db-undo-locally db chat-id message-id)}
@ -138,14 +135,17 @@
(assoc :dispatch
[:pin-message/send-pin-message-locally (assoc message :pinned true)])))))
(rf/defn undo-all
{:events [:chat.ui/undo-all-delete-message]}
(rf/reg-event-fx :chat.ui/undo-delete-message undo)
(defn undo-all
[{:keys [db]}]
(when-let [pending-undos (get-in db
[:toasts :toasts :delete-message-for-everyone
:message-deleted-for-everyone-undos])]
{:dispatch-n (mapv #(vector :chat.ui/undo-delete-message %) pending-undos)}))
(rf/reg-event-fx :chat.ui/undo-all-delete-message undo-all)
(defn- check-before-delete-and-send
"make sure message alredy deleted? locally and undo timelimit has passed"
[db chat-id message-id]
@ -155,9 +155,8 @@
deleted-undoable-till
(>= (datetime/timestamp) deleted-undoable-till))))
(rf/defn delete-and-send
{:events [:chat.ui/delete-message-and-send]}
[{:keys [db]} {:keys [message-id chat-id]} force?]
(defn delete-and-send
[{:keys [db]} [{:keys [message-id chat-id]} force?]]
(when (get-in db [:messages chat-id message-id])
(when (or force? (check-before-delete-and-send db chat-id message-id))
(let [unpin-locally?
@ -171,20 +170,30 @@
(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?)}]}))))
{:db (update-db-clear-undo-timer db chat-id message-id)
:fx [[:json-rpc/call
[{:method "wakuext_deleteMessageAndSend"
:params [message-id]
:js-response true
:on-error [:chat/delete-and-send-error message-id]
:on-success [: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?)}]]]}))))
(rf/reg-event-fx :chat.ui/delete-message-and-send delete-and-send)
(defn delete-and-send-error
[_ [message-id error]]
{:fx [[:effects.log/error
["failed to delete message"
{:message-id message-id
:error error}]]]})
(rf/reg-event-fx :chat/delete-and-send-error delete-and-send-error)
(defn- filter-pending-send-messages
"traverse all messages find not yet synced deleted? messages"
@ -194,23 +203,14 @@
(map (fn [message] {:chat-id chat-id :message-id (first message)}))
(concat acc)))
(rf/defn send-all
(defn send-all
"Get all deleted messages that not yet synced with status-go and send them"
{:events [:chat.ui/send-all-deleted-messages]}
[{:keys [db] :as cofx}]
(let [pending-send-messages (reduce-kv filter-pending-send-messages [] (:messages db))]
(apply rf/merge cofx (map #(delete-and-send % true) pending-send-messages))))
(let [pending-send-messages (reduce-kv filter-pending-send-messages [] (:messages db))
pending-effects (map (fn [message]
(fn [cofx]
(delete-and-send cofx [message true])))
pending-send-messages)]
(apply rf/merge cofx pending-effects)))
(rf/defn delete-messages-localy
"Mark messages :deleted? localy in client"
{:events [:chat.ui/delete-messages-localy]}
[{:keys [db]} messages chat-id]
(let [new-db (->> messages
(filter #(get-in db [:messages chat-id (:message-id %)]))
(reduce #(update-db-delete-locally-without-time-limit %1
chat-id
(:message-id %2)
(:deleted-by %2))
db))]
(when new-db
(message-list/rebuild-message-list {:db new-db} chat-id))))
(rf/reg-event-fx :chat.ui/send-all-deleted-messages send-all)

View File

@ -9,15 +9,41 @@
(deftest delete-test
(with-redefs [datetime/timestamp (constantly 1)]
(let [db {:messages {cid {mid {:id mid :whisper-timestamp 1}}}}
(let [pub-key "0x1"
db {:profile/profile {:public-key pub-key}
:messages {cid {mid {:id mid
:from pub-key
:whisper-timestamp 1}}}}
message {:message-id mid :chat-id cid}]
(testing "delete"
(testing "dispatch right db fx"
(let [result-message (get-in (delete-message/delete {:db db} message 1000)
[:db :messages cid mid])]
(is (= (:id result-message) mid))
(is (true? (:deleted? result-message)) "mark message :deleted?")
(is (= (:deleted-undoable-till result-message) 1001) "set message undo timelimit")))
(testing "dispatch right db fx when deleting own message"
(let [effects (delete-message/delete {:db db} [message 1000])
result-messages (get-in effects [:db :messages] effects)
expected-messages {cid
{mid
{:id mid
:from pub-key
:whisper-timestamp 1
:deleted? true
:deleted-by nil
:deleted-undoable-till 1001}}}]
(is (match? result-messages
expected-messages))))
(testing "dispatch right db fx when deleting another user message"
(let [other-pub-key "0x2"
mod-db (assoc-in db [:messages cid mid :from] other-pub-key)
effects (delete-message/delete {:db mod-db} [message 1000])
result-messages (get-in effects [:db :messages] effects)
expected-messages {cid
{mid
{:id mid
:from other-pub-key
:whisper-timestamp 1
:deleted? true
:deleted-by pub-key
:deleted-undoable-till 1001}}}]
(is (match? result-messages
expected-messages))))
(testing "delete with pending deletes"
(let [db (-> db
(update-in [:messages cid "pending-delete-message"]
@ -31,7 +57,7 @@
:message-deleted-for-everyone-undos [{:message-id
"pending-delete-message"
:chat-id cid}]))
effects (delete-message/delete {:db db} message 1000)]
effects (delete-message/delete {:db db} [message 1000])]
(is (= (get-in effects [:db :messages cid mid :deleted-undoable-till])
(get-in effects [:db :messages cid "pending-delete-message" :deleted-undoable-till])
1001)
@ -55,7 +81,7 @@
(= mid)))
"pending deletes are in order"))))
(testing "return nil if message not in db"
(is (= (delete-message/delete {:db {:messages []}} message 1000) nil)))))))
(is (= (delete-message/delete {:db {:messages []}} [message 1000]) nil)))))))
(deftest undo-delete-test
(let [db {:messages {cid {mid {:id mid :whisper-timestamp 1}}}}
@ -67,60 +93,140 @@
assoc
:deleted? true
:deleted-undoable-till (+ (datetime/timestamp) 1000))
result-message (get-in (delete-message/undo {:db db} message) [:db :messages cid mid])]
(is (= (:id result-message) mid))
(is (nil? (:deleted? result-message)))
(is (nil? (:deleted-undoable-till result-message)))))
effects (delete-message/undo {:db db} [message])
result-message (get-in effects [:db :messages cid mid])]
(is (match? result-message
{:id mid
:whisper-timestamp 1}))))
(testing "remain deleted when undo after timelimit"
(let [db (update-in db
[:messages cid mid]
assoc
:deleted? true
:deleted-undoable-till (- (datetime/timestamp) 1000))
result-message (get-in (delete-message/undo {:db db} message) [:db :messages cid mid])]
(is (= (:id result-message) mid))
(is (nil? (:deleted-undoable-till result-message)))
(is (true? (:deleted? result-message)))))
effects (delete-message/undo {:db db} [message])
result-message (get-in effects [:db :messages cid mid])]
(is (match? result-message
{:id mid
:whisper-timestamp 1
:deleted? true}))))
(testing "return nil if message not in db"
(is (= (delete-message/undo {:db {:messages []}} message) nil))))))
(is (= (delete-message/undo {:db {:messages []}} [message]) nil))))))
(deftest undo-all-test
(testing "does nothing when there are no pending todos"
(let [cofx {:db {:toasts
{:toasts
{:delete-message-for-everyone
{:message-deleted-for-everyone-undos nil}}}}}
effects (delete-message/undo-all cofx)
expected nil]
(is (match? effects expected))))
(testing "undo all pending undos"
(let [pending-undo {:undo-id 1}
pending-undos [pending-undo]
cofx {:db {:toasts
{:toasts
{:delete-message-for-everyone
{:message-deleted-for-everyone-undos pending-undos}}}}}
effects (delete-message/undo-all cofx)
expected {:dispatch-n [[:chat.ui/undo-delete-message pending-undo]]}]
(is (match? effects expected)))))
(deftest delete-and-send-test
(let [db {:messages {cid {mid {:id mid :deleted? true :deleted-undoable-till 0}}}}
message {:message-id mid :chat-id cid}]
(testing "delete and send"
(testing "dispatch right rpc call fx"
(let [expected-db {:messages {cid {mid {:id mid :deleted? true}}}}
effects (delete-message/delete-and-send {:db db} message false)
result-db (:db effects)
rpc-calls (:json-rpc/call effects)]
(is (= result-db expected-db))
(is (= (count rpc-calls) 1))
(is (= (-> rpc-calls first :method) "wakuext_deleteMessageAndSend"))
(is (= (-> rpc-calls first :params count) 1))
(is (= (-> rpc-calls first :params first) mid))))
(let [effects (delete-message/delete-and-send {:db db} [message false])
expected-db {:messages
{cid
{mid
{:id mid
:deleted? true}}}}
expected-fx [[:json-rpc/call
[{:method "wakuext_deleteMessageAndSend"
:params [mid]
:js-response true
:on-error [:chat/delete-and-send-error mid]
:on-success [:sanitize-messages-and-process-response]}]]
[:dispatch
[:pin-message/send-pin-message
{:chat-id cid
:message-id mid
:pinned false
:remote-only? true}]]]]
(is (match? effects
{:db expected-db
:fx expected-fx}))))
(testing "clean undo timer"
(let [expected-db {:messages {cid {mid {:id mid :deleted? true}}}}
effects (delete-message/delete-and-send
{:db (update-in db
[:messages cid mid :deleted-undoable-till]
(constantly (datetime/timestamp)))}
message
false)
[message false])
result-db (:db effects)]
(is (= result-db expected-db))))
(testing "before deleted locally"
(let [effects (delete-message/delete-and-send
{:db (update-in db [:messages cid mid] dissoc :deleted?)}
message
false)]
[message false])]
(is (-> effects :db nil?) "not delete and send")))
(testing "before undo timelimit"
(with-redefs [datetime/timestamp (constantly 1)]
(let [effects (delete-message/delete-and-send
{:db (update-in db [:messages cid mid] assoc :deleted-undoable-till 2)}
message
false)]
[message false])]
(is (-> effects :db nil?)))))
(testing "return nil if message not in db"
(is (= (delete-message/delete-and-send {:db {:messages []}} message false)
(is (= (delete-message/delete-and-send {:db {:messages []}} [message false])
nil))))))
(deftest send-all-test
(testing "sends all delete messages"
(let [chat-id "0x1"
message-id-1 "1x1"
message-id-2 "1x2"
pending-deleted-message {:deleted? true
:deleted-undoable-till (datetime/timestamp)}
cofx {:db
{:messages
{chat-id
{message-id-1 pending-deleted-message
message-id-2 pending-deleted-message}}}}
effects (delete-message/send-all cofx)
expected {:db
{:messages
{chat-id
{message-id-1 {:deleted? true}
message-id-2 {:deleted? true}}}}
:fx [[:json-rpc/call
[{:method "wakuext_deleteMessageAndSend"
:params [message-id-1]
:js-response true
:on-error [:chat/delete-and-send-error message-id-1]
:on-success [:sanitize-messages-and-process-response]}]]
[:dispatch
[:pin-message/send-pin-message
{:chat-id chat-id
:message-id message-id-1
:pinned false
:remote-only? true}]]
[:json-rpc/call
[{:method "wakuext_deleteMessageAndSend"
:params [message-id-2]
:js-response true
:on-error [:chat/delete-and-send-error message-id-2]
:on-success [:sanitize-messages-and-process-response]}]]
[:dispatch
[:pin-message/send-pin-message
{:chat-id chat-id
:message-id message-id-2
:pinned false
:remote-only? true}]]]}]
(is (match? effects expected)))))
(comment
(cljs.test/run-tests))

View File

@ -1,7 +1,6 @@
(ns status-im.contexts.chat.messenger.messages.delete-message-for-me.events
(:require
[status-im.contexts.chat.messenger.messages.list.events :as message-list]
[taoensso.timbre :as log]
[utils.datetime :as datetime]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
@ -39,10 +38,9 @@
:deleted-for-me-undoable-till)
(update-db-clear-undo-timer db chat-id message-id))))
(rf/defn delete
(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]
[{:keys [db]} [{:keys [chat-id message-id]} undo-time-limit-ms]]
(when (get-in db [:messages chat-id message-id])
(let [existing-undo-toast (get-in db [:toasts :toasts :delete-message-for-me])
toast-count (inc (get existing-undo-toast :message-deleted-for-me-count 0))
@ -76,22 +74,26 @@
{:chat-id chat-id :message-id message-id}]
:ms undo-time-limit-ms}]))))
(rf/defn undo
{:events [:chat.ui/undo-delete-message-for-me]}
[{:keys [db]} {:keys [chat-id message-id]}]
(rf/reg-event-fx :chat.ui/delete-message-for-me delete)
(defn undo
[{:keys [db]} [{:keys [chat-id message-id]}]]
(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)))
(rf/defn undo-all
{:events [:chat.ui/undo-all-delete-message-for-me]}
(rf/reg-event-fx :chat.ui/undo-delete-message-for-me undo)
(defn undo-all
[{:keys [db]}]
(when-let [pending-undos (get-in db
[:toasts :toasts :delete-message-for-me
:message-deleted-for-me-undos])]
{:dispatch-n (mapv #(vector :chat.ui/undo-delete-message-for-me %) pending-undos)}))
(rf/reg-event-fx :chat.ui/undo-all-delete-message-for-me undo-all)
(defn- check-before-delete-and-sync
"Make sure message alredy deleted-for-me? locally and undo timelimit has passed"
[db chat-id message-id]
@ -101,20 +103,28 @@
deleted-for-me-undoable-till
(>= (datetime/timestamp) deleted-for-me-undoable-till))))
(rf/defn delete-and-sync
{:events [:chat.ui/delete-message-for-me-and-sync]}
[{:keys [db]} {:keys [message-id chat-id]} force?]
(defn delete-and-sync
[{: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-sync db 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, message id: "
{:message-id message-id :error %})
:on-success #(rf/dispatch [:sanitize-messages-and-process-response
%])}]})))
{:db (update-db-clear-undo-timer db chat-id message-id)
:fx [[:json-rpc/call
[{:method "wakuext_deleteMessageForMeAndSync"
:params [chat-id message-id]
:js-response true
:on-error [:chat/delete-message-for-me-and-sync-error message-id]
:on-success [:sanitize-messages-and-process-response]}]]]})))
(rf/reg-event-fx :chat.ui/delete-message-for-me-and-sync delete-and-sync)
(defn delete-and-sync-error
[_ [message-id error]]
{:fx [[:effects.log/error
["failed to delete message for me, message id:"
{:message-id message-id
:error error}]]]})
(rf/reg-event-fx :chat/delete-message-for-me-and-sync-error delete-and-sync-error)
(defn- filter-pending-sync-messages
"traverse all messages find not yet synced deleted-for-me? messages"
@ -125,9 +135,14 @@
(map (fn [message] {:chat-id chat-id :message-id (first message)}))
(concat acc)))
(rf/defn sync-all
(defn sync-all
"Get all deleted-for-me messages that not yet synced with status-go and sync them"
{:events [:chat.ui/sync-all-deleted-for-me-messages]}
[{:keys [db] :as cofx}]
(let [pending-sync-messages (reduce-kv filter-pending-sync-messages [] (:messages db))]
(apply rf/merge cofx (map #(delete-and-sync % true) pending-sync-messages))))
(let [pending-sync-messages (reduce-kv filter-pending-sync-messages [] (:messages db))
pending-effects (map (fn [message]
(fn [cofx]
(delete-and-sync cofx [message true])))
pending-sync-messages)]
(apply rf/merge cofx pending-effects)))
(rf/reg-event-fx :chat.ui/sync-all-deleted-for-me-messages sync-all)

View File

@ -1,6 +1,7 @@
(ns status-im.contexts.chat.messenger.messages.delete-message-for-me.events-test
(:require
[cljs.test :refer-macros [deftest is testing]]
matcher-combinators.test
[status-im.contexts.chat.messenger.messages.delete-message-for-me.events :as
delete-message-for-me]
[utils.datetime :as datetime]))
@ -14,7 +15,7 @@
message {:message-id mid :chat-id cid}]
(testing "delete for me"
(testing "dispatch right db fx"
(let [result-message (get-in (delete-message-for-me/delete {:db db} message 1000)
(let [result-message (get-in (delete-message-for-me/delete {:db db} [message 1000])
[:db :messages cid mid])]
(is (= (:id result-message) mid))
(is (true? (:deleted-for-me? result-message)))
@ -32,10 +33,11 @@
:message-deleted-for-me-undos [{:message-id
"pending-delete-message"
:chat-id cid}]))
effects (delete-message-for-me/delete {:db db} message 1000)]
effects (delete-message-for-me/delete {:db db} [message 1000])]
(is (= (get-in effects [:db :messages cid mid :deleted-for-me-undoable-till])
(get-in effects
[:db :messages cid "pending-delete-message" :deleted-for-me-undoable-till])
[:db :messages cid "pending-delete-message"
:deleted-for-me-undoable-till])
1001)
"sync all pending delete undo timelimit")
(let [upsert-toast (-> effects :dispatch-n second)]
@ -57,7 +59,7 @@
(= mid)))
"pending deletes are in order"))))
(testing "return nil if message not in db"
(is (= (delete-message-for-me/delete {:db {:messages []}} message 1000)
(is (= (delete-message-for-me/delete {:db {:messages []}} [message 1000])
nil)))))))
(deftest undo-delete-for-me-test
@ -71,7 +73,7 @@
:deleted-for-me? true
:deleted-for-me-undoable-till
(+ (datetime/timestamp) 1000))
result-message (get-in (delete-message-for-me/undo {:db db} message)
result-message (get-in (delete-message-for-me/undo {:db db} [message])
[:db :messages cid mid])]
(is (= (:id result-message) mid))
(is (nil? (:deleted-for-me? result-message)))
@ -83,53 +85,106 @@
assoc
:deleted-for-me? true
:deleted-for-me-undoable-till (- (datetime/timestamp) 1000))
result-message (get-in (delete-message-for-me/undo {:db db} message)
result-message (get-in (delete-message-for-me/undo {:db db} [message])
[:db :messages cid mid])]
(is (= (:id result-message) mid))
(is (nil? (:deleted-for-me-undoable-till result-message)))
(is (true? (:deleted-for-me? result-message)))))
(testing "return nil if message not in db"
(is (= (delete-message-for-me/undo {:db {:messages []}} message)
(is (= (delete-message-for-me/undo {:db {:messages []}} [message])
nil))))))
(deftest undo-all-test
(testing "does nothing when there are no pending todos"
(let [cofx {:db {:toasts
{:toasts
{:delete-message-for-me
{:message-deleted-for-me-undos nil}}}}}
effects (delete-message-for-me/undo-all cofx)
expected nil]
(is (match? effects expected))))
(testing "undo all pending undos"
(let [pending-undo {:undo-id 1}
pending-undos [pending-undo]
cofx {:db {:toasts
{:toasts
{:delete-message-for-me
{:message-deleted-for-me-undos pending-undos}}}}}
effects (delete-message-for-me/undo-all cofx)
expected {:dispatch-n [[:chat.ui/undo-delete-message-for-me pending-undo]]}]
(is (match? effects expected)))))
(deftest delete-for-me-and-sync-test
(let [db {:messages {cid {mid {:id mid :deleted-for-me? true :deleted-for-me-undoable-till 0}}}}
message {:message-id mid :chat-id cid}]
(testing "delete for me and sync"
(testing "dispatch right rpc call"
(let [expected-db {:messages {cid {mid {:id mid :deleted-for-me? true}}}}
effects (delete-message-for-me/delete-and-sync {:db db} message false)
result-db (:db effects)
rpc-calls (: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))))
expected-fx [[:json-rpc/call
[{:method "wakuext_deleteMessageForMeAndSync"
:params [cid mid]
:js-response true
:on-success [:sanitize-messages-and-process-response]
:on-error [:chat/delete-message-for-me-and-sync-error mid]}]]]
effects (delete-message-for-me/delete-and-sync {:db db} [message false])]
(is (match? effects
{:db expected-db
:fx expected-fx}))))
(testing "clean undo timer"
(let [expected-db {:messages {cid {mid {:id mid :deleted-for-me? true}}}}
effects (delete-message-for-me/delete-and-sync
{:db (update-in db
[:messages cid mid :deleted-for-me-undoable-till]
(constantly (datetime/timestamp)))}
message
false)
[message false])
result-db (:db effects)]
(is (= result-db expected-db))))
(testing "before deleted locally"
(let [effects (delete-message-for-me/delete-and-sync
{:db (update-in db [:messages cid mid] dissoc :deleted-for-me?)}
message
false)]
[message false])]
(is (-> effects :db nil?) "not delete and send")))
(testing "before undo timelimit"
(with-redefs [datetime/timestamp (constantly 1)]
(let [effects (delete-message-for-me/delete-and-sync
{:db (update-in db [:messages cid mid] assoc :deleted-for-me-undoable-till 2)}
message
false)]
[message false])]
(is (-> effects :db nil?)))))
(testing "return nil if message not in db"
(is (= (delete-message-for-me/delete-and-sync {:db {:messages []}} message false) nil))))))
(is (= (delete-message-for-me/delete-and-sync {:db {:messages []}} [message false]) nil))))))
(deftest sync-all-test
(testing "syncs all delete-for-me messages"
(let [chat-id "0x1"
message-id-1 "1x1"
message-id-2 "1x2"
pending-deleted-message {:deleted-for-me? true
:deleted-for-me-undoable-till (datetime/timestamp)}
cofx {:db
{:messages
{chat-id
{message-id-1 pending-deleted-message
message-id-2 pending-deleted-message}}}}
effects (delete-message-for-me/sync-all cofx)
expected {:db
{:messages
{chat-id
{message-id-1 {:deleted-for-me? true}
message-id-2 {:deleted-for-me? true}}}}
:fx [[:json-rpc/call
[{:method "wakuext_deleteMessageForMeAndSync"
:params [chat-id message-id-1]
:js-response true
:on-error [:chat/delete-message-for-me-and-sync-error
message-id-1]
:on-success [:sanitize-messages-and-process-response]}]]
[:json-rpc/call
[{:method "wakuext_deleteMessageForMeAndSync"
:params [chat-id message-id-2]
:js-response true
:on-error [:chat/delete-message-for-me-and-sync-error
message-id-2]
:on-success [:sanitize-messages-and-process-response]}]]]}]
(is (match? effects expected)))))

View File

@ -6,12 +6,12 @@
(rf/defn accept-contact-request
{:events [:activity-center.contact-requests/accept]}
[_ contact-id]
{:json-rpc/call
[{:method "wakuext_acceptContactRequest"
:params [{:id contact-id}]
:js-response true
:on-success #(rf/dispatch [:sanitize-messages-and-process-response %])
:on-error #(rf/dispatch [:activity-center.contact-requests/accept-error contact-id %])}]})
{:fx [[:json-rpc/call
[{:method "wakuext_acceptContactRequest"
:params [{:id contact-id}]
:js-response true
:on-success [:sanitize-messages-and-process-response]
:on-error [:activity-center.contact-requests/accept-error contact-id]}]]]})
(rf/defn accept-contact-request-error
{:events [:activity-center.contact-requests/accept-error]}
@ -24,12 +24,12 @@
(rf/defn decline-contact-request
{:events [:activity-center.contact-requests/decline]}
[_ contact-id]
{:json-rpc/call
[{:method "wakuext_declineContactRequest"
:params [{:id contact-id}]
:js-response true
:on-success #(rf/dispatch [:sanitize-messages-and-process-response %])
:on-error #(rf/dispatch [:activity-center.contact-requests/decline-error contact-id %])}]})
{:fx [[:json-rpc/call
[{:method "wakuext_declineContactRequest"
:params [{:id contact-id}]
:js-response true
:on-success [:sanitize-messages-and-process-response]
:on-error [:activity-center.contact-requests/decline-error contact-id]}]]]})
(rf/defn decline-contact-request-error
{:events [:activity-center.contact-requests/decline-error]}

View File

@ -0,0 +1,32 @@
(ns status-im.contexts.shell.activity-center.notification.contact-requests.events-test
(:require
[cljs.test :refer [deftest is testing]]
matcher-combinators.test
[status-im.contexts.shell.activity-center.notification.contact-requests.events :as
contact-requests]))
(deftest accept-contact-request-test
(testing "creates effect for accepting a contact request"
(let [contact-id "0x2"
cofx {:db {}}]
(is (match?
{:fx [[:json-rpc/call
[{:method "wakuext_acceptContactRequest"
:params [{:id contact-id}]
:js-response true
:on-success [:sanitize-messages-and-process-response]
:on-error [:activity-center.contact-requests/accept-error contact-id]}]]]}
(contact-requests/accept-contact-request cofx contact-id))))))
(deftest decline-contact-request-test
(testing "creates effect for declining a contact request"
(let [contact-id "0x2"
cofx {:db {}}]
(is (match?
{:fx [[:json-rpc/call
[{:method "wakuext_declineContactRequest"
:params [{:id contact-id}]
:js-response true
:on-success [:sanitize-messages-and-process-response]
:on-error [:activity-center.contact-requests/decline-error contact-id]}]]]}
(contact-requests/decline-contact-request cofx contact-id))))))