diff --git a/src/legacy/status_im/chat/models/message.cljs b/src/legacy/status_im/chat/models/message.cljs index ca3cf5b598..9438b5ade7 100644 --- a/src/legacy/status_im/chat/models/message.cljs +++ b/src/legacy/status_im/chat/models/message.cljs @@ -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) diff --git a/src/legacy/status_im/test_runner.cljs b/src/legacy/status_im/test_runner.cljs index 01ef27a684..ae16b0a814 100644 --- a/src/legacy/status_im/test_runner.cljs +++ b/src/legacy/status_im/test_runner.cljs @@ -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))) diff --git a/src/status_im/common/log.cljs b/src/status_im/common/log.cljs index f887ba70bb..a25a9f8a2e 100644 --- a/src/status_im/common/log.cljs +++ b/src/status_im/common/log.cljs @@ -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))) diff --git a/src/status_im/contexts/chat/contacts/events.cljs b/src/status_im/contexts/chat/contacts/events.cljs index 0796e52c59..d4881115dd 100644 --- a/src/status_im/contexts/chat/contacts/events.cljs +++ b/src/status_im/contexts/chat/contacts/events.cljs @@ -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) diff --git a/src/status_im/contexts/chat/contacts/events_test.cljs b/src/status_im/contexts/chat/contacts/events_test.cljs index 9cedc3a628..8bf70248f4 100644 --- a/src/status_im/contexts/chat/contacts/events_test.cljs +++ b/src/status_im/contexts/chat/contacts/events_test.cljs @@ -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])))))) diff --git a/src/status_im/contexts/chat/events.cljs b/src/status_im/contexts/chat/events.cljs index 8ba733b1aa..bc649c4166 100644 --- a/src/status_im/contexts/chat/events.cljs +++ b/src/status_im/contexts/chat/events.cljs @@ -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 diff --git a/src/status_im/contexts/chat/messenger/messages/contact_requests/bottom_drawer/view.cljs b/src/status_im/contexts/chat/messenger/messages/contact_requests/bottom_drawer/view.cljs index 7ab290d521..48b9c987cd 100644 --- a/src/status_im/contexts/chat/messenger/messages/contact_requests/bottom_drawer/view.cljs +++ b/src/status_im/contexts/chat/messenger/messages/contact_requests/bottom_drawer/view.cljs @@ -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}))}]])) + diff --git a/src/status_im/contexts/chat/messenger/messages/delete_message/events.cljs b/src/status_im/contexts/chat/messenger/messages/delete_message/events.cljs index 50e5b90384..f2b6e4ab15 100644 --- a/src/status_im/contexts/chat/messenger/messages/delete_message/events.cljs +++ b/src/status_im/contexts/chat/messenger/messages/delete_message/events.cljs @@ -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) diff --git a/src/status_im/contexts/chat/messenger/messages/delete_message/events_test.cljs b/src/status_im/contexts/chat/messenger/messages/delete_message/events_test.cljs index 3a0720f301..8abde2db95 100644 --- a/src/status_im/contexts/chat/messenger/messages/delete_message/events_test.cljs +++ b/src/status_im/contexts/chat/messenger/messages/delete_message/events_test.cljs @@ -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)) diff --git a/src/status_im/contexts/chat/messenger/messages/delete_message_for_me/events.cljs b/src/status_im/contexts/chat/messenger/messages/delete_message_for_me/events.cljs index 6faba23a13..0b86d66c38 100644 --- a/src/status_im/contexts/chat/messenger/messages/delete_message_for_me/events.cljs +++ b/src/status_im/contexts/chat/messenger/messages/delete_message_for_me/events.cljs @@ -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) diff --git a/src/status_im/contexts/chat/messenger/messages/delete_message_for_me/events_test.cljs b/src/status_im/contexts/chat/messenger/messages/delete_message_for_me/events_test.cljs index b8bd97be42..11e76833db 100644 --- a/src/status_im/contexts/chat/messenger/messages/delete_message_for_me/events_test.cljs +++ b/src/status_im/contexts/chat/messenger/messages/delete_message_for_me/events_test.cljs @@ -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))))) diff --git a/src/status_im/contexts/shell/activity_center/notification/contact_requests/events.cljs b/src/status_im/contexts/shell/activity_center/notification/contact_requests/events.cljs index 2d504d0245..1ed1593fbf 100644 --- a/src/status_im/contexts/shell/activity_center/notification/contact_requests/events.cljs +++ b/src/status_im/contexts/shell/activity_center/notification/contact_requests/events.cljs @@ -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]} diff --git a/src/status_im/contexts/shell/activity_center/notification/contact_requests/events_test.cljs b/src/status_im/contexts/shell/activity_center/notification/contact_requests/events_test.cljs new file mode 100644 index 0000000000..6561efaf4d --- /dev/null +++ b/src/status_im/contexts/shell/activity_center/notification/contact_requests/events_test.cljs @@ -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))))))