diff --git a/src/status_im/activity_center/core.cljs b/src/status_im/activity_center/core.cljs index 9878e17858..8a4c3b1674 100644 --- a/src/status_im/activity_center/core.cljs +++ b/src/status_im/activity_center/core.cljs @@ -178,13 +178,14 @@ 99)) (fx/defn notifications-fetch - [{:keys [db]} {:keys [cursor filter-type filter-status reset-data?]}] + [{:keys [db]} {:keys [cursor per-page filter-type filter-status reset-data?]}] (when-not (get-in db [:activity-center :notifications filter-type filter-status :loading?]) - {:db (assoc-in db [:activity-center :notifications filter-type filter-status :loading?] true) - ::json-rpc/call [{:method "wakuext_activityCenterNotificationsBy" - :params [cursor (defaults :notifications-per-page) filter-type (status filter-status)] - :on-success #(rf/dispatch [:activity-center.notifications/fetch-success filter-type filter-status reset-data? %]) - :on-error #(rf/dispatch [:activity-center.notifications/fetch-error filter-type filter-status %])}]})) + (let [per-page (or per-page (defaults :notifications-per-page))] + {:db (assoc-in db [:activity-center :notifications filter-type filter-status :loading?] true) + ::json-rpc/call [{:method "wakuext_activityCenterNotificationsBy" + :params [cursor per-page filter-type (status filter-status)] + :on-success #(rf/dispatch [:activity-center.notifications/fetch-success filter-type filter-status reset-data? %]) + :on-error #(rf/dispatch [:activity-center.notifications/fetch-error filter-type filter-status %])}]}))) (fx/defn notifications-fetch-first-page {:events [:activity-center.notifications/fetch-first-page]} @@ -231,6 +232,35 @@ (constantly processed) #(concat %1 processed))))})) +(fx/defn notifications-fetch-unread-contact-requests + "Unread contact requests are, in practical terms, the same as pending contact + requests in the new Activity Center, because pending contact requests are + always marked as unread, and once the user declines/accepts the request, they + are marked as read. + + If this relationship ever changes, we will probably need to change the backend + to explicitly support fetching notifications for 'pending' contact requests." + {:events [:activity-center.notifications/fetch-unread-contact-requests]} + [cofx] + (notifications-fetch cofx {:cursor start-or-end-cursor + :filter-status :unread + :filter-type types/contact-request + :per-page 20 + :reset-data? true})) + +(fx/defn notifications-fetch-unread-count + {:events [:activity-center.notifications/fetch-unread-count]} + [_] + {::json-rpc/call [{:method "wakuext_unreadActivityCenterNotificationsCount" + :params [] + :on-success #(rf/dispatch [:activity-center.notifications/fetch-unread-count-success %]) + :on-error #()}]}) + +(fx/defn notifications-fetch-unread-count-success + {:events [:activity-center.notifications/fetch-unread-count-success]} + [{:keys [db]} result] + {:db (assoc-in db [:activity-center :unread-count] result)}) + (fx/defn notifications-fetch-error {:events [:activity-center.notifications/fetch-error]} [{:keys [db]} filter-type filter-status error] diff --git a/src/status_im/activity_center/core_test.cljs b/src/status_im/activity_center/core_test.cljs index 73d451fed2..7c7325d0bd 100644 --- a/src/status_im/activity_center/core_test.cljs +++ b/src/status_im/activity_center/core_test.cljs @@ -2,12 +2,12 @@ (:require [cljs.test :refer [deftest is testing]] [day8.re-frame.test :as rf-test] [re-frame.core :as rf] + [status-im.activity-center.core :as activity-center] [status-im.activity-center.notification-types :as types] [status-im.constants :as constants] [status-im.ethereum.json-rpc :as json-rpc] status-im.events - [status-im.test-helpers :as h] - [status-im2.setup.config :as config])) + [status-im.test-helpers :as h])) (def notification-id "0x1") @@ -174,345 +174,370 @@ (select-keys [:method :params]))))))) (deftest contact-verification-decline-test - (with-redefs [config/new-activity-center-enabled? true] - (testing "declines notification and reconciles" - (test-contact-verification-event - {:event [:activity-center.contact-verification/decline notification-id] - :expected-rpc-call {:method "wakuext_declineContactVerificationRequest" - :params [notification-id]}})) - (testing "logs on failure" - (test-log-on-failure - {:notification-id notification-id - :event [:activity-center.contact-verification/decline notification-id] - :action :contact-verification/decline})))) + (testing "declines notification and reconciles" + (test-contact-verification-event + {:event [:activity-center.contact-verification/decline notification-id] + :expected-rpc-call {:method "wakuext_declineContactVerificationRequest" + :params [notification-id]}})) + (testing "logs on failure" + (test-log-on-failure + {:notification-id notification-id + :event [:activity-center.contact-verification/decline notification-id] + :action :contact-verification/decline}))) (deftest contact-verification-reply-test - (with-redefs [config/new-activity-center-enabled? true] - (testing "sends reply and reconciles" - (let [reply "any answer"] - (test-contact-verification-event - {:event [:activity-center.contact-verification/reply notification-id reply] - :expected-rpc-call {:method "wakuext_acceptContactVerificationRequest" - :params [notification-id reply]}}))) - (testing "logs on failure" - (test-log-on-failure - {:notification-id notification-id - :event [:activity-center.contact-verification/reply notification-id "any answer"] - :action :contact-verification/reply})))) + (testing "sends reply and reconciles" + (let [reply "any answer"] + (test-contact-verification-event + {:event [:activity-center.contact-verification/reply notification-id reply] + :expected-rpc-call {:method "wakuext_acceptContactVerificationRequest" + :params [notification-id reply]}}))) + (testing "logs on failure" + (test-log-on-failure + {:notification-id notification-id + :event [:activity-center.contact-verification/reply notification-id "any answer"] + :action :contact-verification/reply}))) (deftest contact-verification-mark-as-trusted-test - (with-redefs [config/new-activity-center-enabled? true] - (testing "marks notification as trusted and reconciles" - (test-contact-verification-event - {:event [:activity-center.contact-verification/mark-as-trusted notification-id] - :expected-rpc-call {:method "wakuext_verifiedTrusted" - :params [{:id notification-id}]}})) - (testing "logs on failure" - (test-log-on-failure - {:notification-id notification-id - :event [:activity-center.contact-verification/mark-as-trusted notification-id] - :action :contact-verification/mark-as-trusted})))) + (testing "marks notification as trusted and reconciles" + (test-contact-verification-event + {:event [:activity-center.contact-verification/mark-as-trusted notification-id] + :expected-rpc-call {:method "wakuext_verifiedTrusted" + :params [{:id notification-id}]}})) + (testing "logs on failure" + (test-log-on-failure + {:notification-id notification-id + :event [:activity-center.contact-verification/mark-as-trusted notification-id] + :action :contact-verification/mark-as-trusted}))) (deftest contact-verification-mark-as-untrustworthy-test - (with-redefs [config/new-activity-center-enabled? true] - (testing "marks notification as untrustworthy and reconciles" - (test-contact-verification-event - {:event [:activity-center.contact-verification/mark-as-untrustworthy notification-id] - :expected-rpc-call {:method "wakuext_verifiedUntrustworthy" - :params [{:id notification-id}]}})) - (testing "logs on failure" - (test-log-on-failure - {:notification-id notification-id - :event [:activity-center.contact-verification/mark-as-untrustworthy notification-id] - :action :contact-verification/mark-as-untrustworthy})))) + (testing "marks notification as untrustworthy and reconciles" + (test-contact-verification-event + {:event [:activity-center.contact-verification/mark-as-untrustworthy notification-id] + :expected-rpc-call {:method "wakuext_verifiedUntrustworthy" + :params [{:id notification-id}]}})) + (testing "logs on failure" + (test-log-on-failure + {:notification-id notification-id + :event [:activity-center.contact-verification/mark-as-untrustworthy notification-id] + :action :contact-verification/mark-as-untrustworthy}))) ;;;; Notification reconciliation (deftest notifications-reconcile-test - (with-redefs [config/new-activity-center-enabled? true] - (testing "does nothing when there are no new notifications" - (rf-test/run-test-sync - (setup) - (let [notifications {types/one-to-one-chat - {:all {:cursor "" - :data [{:id "0x1" - :read true - :type types/one-to-one-chat} - {:id "0x2" - :read false - :type types/one-to-one-chat}]} - :unread {:cursor "" - :data [{:id "0x3" - :read false - :type types/one-to-one-chat}]}} - types/private-group-chat - {:unread {:cursor "" - :data [{:id "0x4" - :read false - :type types/private-group-chat}]}}}] - (rf/dispatch [:test/assoc-in [:activity-center :notifications] notifications]) + (testing "does nothing when there are no new notifications" + (rf-test/run-test-sync + (setup) + (let [notifications {types/one-to-one-chat + {:all {:cursor "" + :data [{:id "0x1" + :read true + :type types/one-to-one-chat} + {:id "0x2" + :read false + :type types/one-to-one-chat}]} + :unread {:cursor "" + :data [{:id "0x3" + :read false + :type types/one-to-one-chat}]}} + types/private-group-chat + {:unread {:cursor "" + :data [{:id "0x4" + :read false + :type types/private-group-chat}]}}}] + (rf/dispatch [:test/assoc-in [:activity-center :notifications] notifications]) - (rf/dispatch [:activity-center.notifications/reconcile nil]) + (rf/dispatch [:activity-center.notifications/reconcile nil]) - (is (= notifications (get-in (h/db) [:activity-center :notifications])))))) + (is (= notifications (get-in (h/db) [:activity-center :notifications])))))) - (testing "removes dismissed or accepted notifications" - (rf-test/run-test-sync - (setup) - (let [notif-1 {:id "0x1" :read true :type types/one-to-one-chat} - notif-2 {:id "0x2" :read false :type types/one-to-one-chat} - notif-3 {:id "0x3" :read false :type types/one-to-one-chat} - notif-4 {:id "0x4" :read false :type types/private-group-chat} - notif-5 {:id "0x5" :read true :type types/private-group-chat} - notif-6 {:id "0x6" :read false :type types/private-group-chat}] - (rf/dispatch [:test/assoc-in [:activity-center :notifications] - {types/one-to-one-chat - {:all {:cursor "" :data [notif-1 notif-2]} - :unread {:cursor "" :data [notif-3]}} - types/private-group-chat - {:unread {:cursor "" :data [notif-4 notif-6]}}}]) + (testing "removes dismissed or accepted notifications" + (rf-test/run-test-sync + (setup) + (let [notif-1 {:id "0x1" :read true :type types/one-to-one-chat} + notif-2 {:id "0x2" :read false :type types/one-to-one-chat} + notif-3 {:id "0x3" :read false :type types/one-to-one-chat} + notif-4 {:id "0x4" :read false :type types/private-group-chat} + notif-5 {:id "0x5" :read true :type types/private-group-chat} + notif-6 {:id "0x6" :read false :type types/private-group-chat}] + (rf/dispatch [:test/assoc-in [:activity-center :notifications] + {types/one-to-one-chat + {:all {:cursor "" :data [notif-1 notif-2]} + :unread {:cursor "" :data [notif-3]}} + types/private-group-chat + {:unread {:cursor "" :data [notif-4 notif-6]}}}]) - (rf/dispatch [:activity-center.notifications/reconcile - [(assoc notif-1 :dismissed true) - (assoc notif-3 :accepted true) - (assoc notif-4 :dismissed true) - notif-5]]) + (rf/dispatch [:activity-center.notifications/reconcile + [(assoc notif-1 :dismissed true) + (assoc notif-3 :accepted true) + (assoc notif-4 :dismissed true) + notif-5]]) - (is (= {types/no-type - {:all {:data [notif-5]} - :unread {:data []}} - types/one-to-one-chat - {:all {:cursor "" :data [notif-2]} - :unread {:cursor "" :data []}} - types/private-group-chat - {:all {:data [notif-5]} - :unread {:cursor "" :data [notif-6]}}} - (get-in (h/db) [:activity-center :notifications])))))) + (is (= {types/no-type + {:all {:data [notif-5]} + :unread {:data []}} + types/one-to-one-chat + {:all {:cursor "" :data [notif-2]} + :unread {:cursor "" :data []}} + types/private-group-chat + {:all {:data [notif-5]} + :unread {:cursor "" :data [notif-6]}}} + (get-in (h/db) [:activity-center :notifications])))))) - (testing "replaces old notifications with newly arrived ones" - (rf-test/run-test-sync - (setup) - (let [notif-1 {:id "0x1" :read true :type types/one-to-one-chat} - notif-4 {:id "0x4" :read false :type types/private-group-chat} - notif-6 {:id "0x6" :read false :type types/private-group-chat} - new-notif-1 (assoc notif-1 :last-message {}) - new-notif-4 (assoc notif-4 :author "0xabc")] - (rf/dispatch [:test/assoc-in [:activity-center :notifications] - {types/no-type - {:all {:cursor "" :data [notif-1]} - :unread {:cursor "" :data [notif-4 notif-6]}} - types/one-to-one-chat - {:all {:cursor "" :data [notif-1]}} - types/private-group-chat - {:unread {:cursor "" :data [notif-4 notif-6]}}}]) + (testing "replaces old notifications with newly arrived ones" + (rf-test/run-test-sync + (setup) + (let [notif-1 {:id "0x1" :read true :type types/one-to-one-chat} + notif-4 {:id "0x4" :read false :type types/private-group-chat} + notif-6 {:id "0x6" :read false :type types/private-group-chat} + new-notif-1 (assoc notif-1 :last-message {}) + new-notif-4 (assoc notif-4 :author "0xabc")] + (rf/dispatch [:test/assoc-in [:activity-center :notifications] + {types/no-type + {:all {:cursor "" :data [notif-1]} + :unread {:cursor "" :data [notif-4 notif-6]}} + types/one-to-one-chat + {:all {:cursor "" :data [notif-1]}} + types/private-group-chat + {:unread {:cursor "" :data [notif-4 notif-6]}}}]) - (rf/dispatch [:activity-center.notifications/reconcile [new-notif-1 new-notif-4 notif-6]]) + (rf/dispatch [:activity-center.notifications/reconcile [new-notif-1 new-notif-4 notif-6]]) - (is (= {types/no-type - {:all {:cursor "" :data [notif-6 new-notif-4 new-notif-1]} - :unread {:cursor "" :data [notif-6 new-notif-4]}} - types/one-to-one-chat - {:all {:cursor "" :data [new-notif-1]} - :unread {:data []}} - types/private-group-chat - {:all {:data [notif-6 new-notif-4]} - :unread {:cursor "" :data [notif-6 new-notif-4]}}} - (get-in (h/db) [:activity-center :notifications])))))) + (is (= {types/no-type + {:all {:cursor "" :data [notif-6 new-notif-4 new-notif-1]} + :unread {:cursor "" :data [notif-6 new-notif-4]}} + types/one-to-one-chat + {:all {:cursor "" :data [new-notif-1]} + :unread {:data []}} + types/private-group-chat + {:all {:data [notif-6 new-notif-4]} + :unread {:cursor "" :data [notif-6 new-notif-4]}}} + (get-in (h/db) [:activity-center :notifications])))))) - (testing "reconciles notifications that switched their read/unread status" - (rf-test/run-test-sync - (setup) - (let [notif-1 {:id "0x1" :read true :type types/one-to-one-chat} - new-notif-1 (assoc notif-1 :read false)] - (rf/dispatch [:test/assoc-in [:activity-center :notifications] - {types/one-to-one-chat - {:all {:cursor "" :data [notif-1]}}}]) + (testing "reconciles notifications that switched their read/unread status" + (rf-test/run-test-sync + (setup) + (let [notif-1 {:id "0x1" :read true :type types/one-to-one-chat} + new-notif-1 (assoc notif-1 :read false)] + (rf/dispatch [:test/assoc-in [:activity-center :notifications] + {types/one-to-one-chat + {:all {:cursor "" :data [notif-1]}}}]) - (rf/dispatch [:activity-center.notifications/reconcile [new-notif-1]]) + (rf/dispatch [:activity-center.notifications/reconcile [new-notif-1]]) - (is (= {types/no-type - {:all {:data [new-notif-1]} - :unread {:data [new-notif-1]}} + (is (= {types/no-type + {:all {:data [new-notif-1]} + :unread {:data [new-notif-1]}} - types/one-to-one-chat - {:all {:cursor "" :data [new-notif-1]} - :unread {:data [new-notif-1]}}} - (get-in (h/db) [:activity-center :notifications])))))) + types/one-to-one-chat + {:all {:cursor "" :data [new-notif-1]} + :unread {:data [new-notif-1]}}} + (get-in (h/db) [:activity-center :notifications])))))) - ;; Sorting by timestamp and ID is compatible with what the backend does when - ;; returning paginated results. - (testing "sorts notifications by timestamp and id in descending order" - (rf-test/run-test-sync - (setup) - (let [notif-1 {:id "0x1" :read true :type types/one-to-one-chat :timestamp 1} - notif-2 {:id "0x2" :read true :type types/one-to-one-chat :timestamp 1} - notif-3 {:id "0x3" :read false :type types/one-to-one-chat :timestamp 50} - notif-4 {:id "0x4" :read false :type types/one-to-one-chat :timestamp 100} - notif-5 {:id "0x5" :read false :type types/one-to-one-chat :timestamp 100} - new-notif-1 (assoc notif-1 :last-message {}) - new-notif-4 (assoc notif-4 :last-message {})] - (rf/dispatch [:test/assoc-in [:activity-center :notifications] - {types/one-to-one-chat - {:all {:cursor "" :data [notif-1 notif-2]} - :unread {:cursor "" :data [notif-3 notif-4 notif-5]}}}]) + ;; Sorting by timestamp and ID is compatible with what the backend does when + ;; returning paginated results. + (testing "sorts notifications by timestamp and id in descending order" + (rf-test/run-test-sync + (setup) + (let [notif-1 {:id "0x1" :read true :type types/one-to-one-chat :timestamp 1} + notif-2 {:id "0x2" :read true :type types/one-to-one-chat :timestamp 1} + notif-3 {:id "0x3" :read false :type types/one-to-one-chat :timestamp 50} + notif-4 {:id "0x4" :read false :type types/one-to-one-chat :timestamp 100} + notif-5 {:id "0x5" :read false :type types/one-to-one-chat :timestamp 100} + new-notif-1 (assoc notif-1 :last-message {}) + new-notif-4 (assoc notif-4 :last-message {})] + (rf/dispatch [:test/assoc-in [:activity-center :notifications] + {types/one-to-one-chat + {:all {:cursor "" :data [notif-1 notif-2]} + :unread {:cursor "" :data [notif-3 notif-4 notif-5]}}}]) - (rf/dispatch [:activity-center.notifications/reconcile [new-notif-1 new-notif-4]]) + (rf/dispatch [:activity-center.notifications/reconcile [new-notif-1 new-notif-4]]) - (is (= {types/no-type - {:all {:data [new-notif-4 new-notif-1]} - :unread {:data [new-notif-4]}} - types/one-to-one-chat - {:all {:cursor "" :data [new-notif-4 notif-2 new-notif-1]} - :unread {:cursor "" :data [notif-5 new-notif-4 notif-3]}}} - (get-in (h/db) [:activity-center :notifications])))))))) + (is (= {types/no-type + {:all {:data [new-notif-4 new-notif-1]} + :unread {:data [new-notif-4]}} + types/one-to-one-chat + {:all {:cursor "" :data [new-notif-4 notif-2 new-notif-1]} + :unread {:cursor "" :data [notif-5 new-notif-4 notif-3]}}} + (get-in (h/db) [:activity-center :notifications]))))))) ;;;; Notifications fetching and pagination (deftest notifications-fetch-test - (with-redefs [config/new-activity-center-enabled? true] - (testing "fetches first page" - (rf-test/run-test-sync - (setup) - (let [spy-queue (atom [])] - (h/stub-fx-with-callbacks - ::json-rpc/call - :on-success (constantly {:cursor "10" - :notifications [{:id "0x1" - :type types/one-to-one-chat - :read false - :chatId "0x9"}]})) - (h/spy-fx spy-queue ::json-rpc/call) + (testing "fetches first page" + (rf-test/run-test-sync + (setup) + (let [spy-queue (atom [])] + (h/stub-fx-with-callbacks + ::json-rpc/call + :on-success (constantly {:cursor "10" + :notifications [{:id "0x1" + :type types/one-to-one-chat + :read false + :chatId "0x9"}]})) + (h/spy-fx spy-queue ::json-rpc/call) - (rf/dispatch [:activity-center.notifications/fetch-first-page - {:filter-type types/one-to-one-chat}]) + (rf/dispatch [:activity-center.notifications/fetch-first-page + {:filter-type types/one-to-one-chat}]) - (is (= :unread (get-in (h/db) [:activity-center :filter :status]))) - (is (= "" (get-in @spy-queue [0 :args 0 :params 0])) - "Should be called with empty cursor when fetching first page") - (is (= {types/one-to-one-chat - {:unread {:cursor "10" - :data [{:chat-id "0x9" - :chat-name nil - :chat-type types/one-to-one-chat - :group-chat false - :id "0x1" - :public? false - :last-message nil - :message nil - :read false - :reply-message nil - :type types/one-to-one-chat}]}}} - (get-in (h/db) [:activity-center :notifications])))))) + (is (= :unread (get-in (h/db) [:activity-center :filter :status]))) + (is (= "" (get-in @spy-queue [0 :args 0 :params 0])) + "Should be called with empty cursor when fetching first page") + (is (= {types/one-to-one-chat + {:unread {:cursor "10" + :data [{:chat-id "0x9" + :chat-name nil + :chat-type types/one-to-one-chat + :group-chat false + :id "0x1" + :public? false + :last-message nil + :message nil + :read false + :reply-message nil + :type types/one-to-one-chat}]}}} + (get-in (h/db) [:activity-center :notifications])))))) - (testing "does not fetch next page when pagination cursor reached the end" - (rf-test/run-test-sync - (setup) - (let [spy-queue (atom [])] - (h/spy-fx spy-queue ::json-rpc/call) - (rf/dispatch [:test/assoc-in [:activity-center :filter :status] - :unread]) - (rf/dispatch [:test/assoc-in [:activity-center :filter :type] - types/one-to-one-chat]) - (rf/dispatch [:test/assoc-in [:activity-center :notifications types/one-to-one-chat :unread :cursor] - ""]) + (testing "does not fetch next page when pagination cursor reached the end" + (rf-test/run-test-sync + (setup) + (let [spy-queue (atom [])] + (h/spy-fx spy-queue ::json-rpc/call) + (rf/dispatch [:test/assoc-in [:activity-center :filter :status] + :unread]) + (rf/dispatch [:test/assoc-in [:activity-center :filter :type] + types/one-to-one-chat]) + (rf/dispatch [:test/assoc-in [:activity-center :notifications types/one-to-one-chat :unread :cursor] + ""]) - (rf/dispatch [:activity-center.notifications/fetch-next-page]) + (rf/dispatch [:activity-center.notifications/fetch-next-page]) - (is (= [] @spy-queue))))) + (is (= [] @spy-queue))))) - ;; The cursor can be nil sometimes because the reconciliation doesn't care - ;; about updating the cursor value, but we have to make sure the next page is - ;; only fetched if the current cursor is valid. - (testing "does not fetch next page when cursor is nil" - (rf-test/run-test-sync - (setup) - (let [spy-queue (atom [])] - (h/spy-fx spy-queue ::json-rpc/call) - (rf/dispatch [:test/assoc-in [:activity-center :filter :status] - :unread]) - (rf/dispatch [:test/assoc-in [:activity-center :filter :type] - types/one-to-one-chat]) - (rf/dispatch [:test/assoc-in [:activity-center :notifications types/one-to-one-chat :unread :cursor] - nil]) + ;; The cursor can be nil sometimes because the reconciliation doesn't care + ;; about updating the cursor value, but we have to make sure the next page is + ;; only fetched if the current cursor is valid. + (testing "does not fetch next page when cursor is nil" + (rf-test/run-test-sync + (setup) + (let [spy-queue (atom [])] + (h/spy-fx spy-queue ::json-rpc/call) + (rf/dispatch [:test/assoc-in [:activity-center :filter :status] + :unread]) + (rf/dispatch [:test/assoc-in [:activity-center :filter :type] + types/one-to-one-chat]) + (rf/dispatch [:test/assoc-in [:activity-center :notifications types/one-to-one-chat :unread :cursor] + nil]) - (rf/dispatch [:activity-center.notifications/fetch-next-page]) + (rf/dispatch [:activity-center.notifications/fetch-next-page]) - (is (= [] @spy-queue))))) + (is (= [] @spy-queue))))) - (testing "fetches next page when pagination cursor is not empty" - (rf-test/run-test-sync - (setup) - (let [spy-queue (atom [])] - (h/stub-fx-with-callbacks - ::json-rpc/call - :on-success (constantly {:cursor "" - :notifications [{:id "0x1" - :type types/mention - :read false - :chatId "0x9"}]})) - (h/spy-fx spy-queue ::json-rpc/call) - (rf/dispatch [:test/assoc-in [:activity-center :filter :status] - :unread]) - (rf/dispatch [:test/assoc-in [:activity-center :filter :type] - types/mention]) - (rf/dispatch [:test/assoc-in [:activity-center :notifications types/mention :unread :cursor] - "10"]) + (testing "fetches next page when pagination cursor is not empty" + (rf-test/run-test-sync + (setup) + (let [spy-queue (atom [])] + (h/stub-fx-with-callbacks + ::json-rpc/call + :on-success (constantly {:cursor "" + :notifications [{:id "0x1" + :type types/mention + :read false + :chatId "0x9"}]})) + (h/spy-fx spy-queue ::json-rpc/call) + (rf/dispatch [:test/assoc-in [:activity-center :filter :status] + :unread]) + (rf/dispatch [:test/assoc-in [:activity-center :filter :type] + types/mention]) + (rf/dispatch [:test/assoc-in [:activity-center :notifications types/mention :unread :cursor] + "10"]) - (rf/dispatch [:activity-center.notifications/fetch-next-page]) + (rf/dispatch [:activity-center.notifications/fetch-next-page]) - (is (= "wakuext_activityCenterNotificationsBy" (get-in @spy-queue [0 :args 0 :method]))) - (is (= "10" (get-in @spy-queue [0 :args 0 :params 0])) - "Should be called with current cursor") - (is (= {types/mention - {:unread {:cursor "" - :data [{:chat-id "0x9" - :chat-name nil - :chat-type 3 - :id "0x1" - :last-message nil - :message nil - :read false - :reply-message nil - :type types/mention}]}}} - (get-in (h/db) [:activity-center :notifications])))))) + (is (= "wakuext_activityCenterNotificationsBy" (get-in @spy-queue [0 :args 0 :method]))) + (is (= "10" (get-in @spy-queue [0 :args 0 :params 0])) + "Should be called with current cursor") + (is (= {types/mention + {:unread {:cursor "" + :data [{:chat-id "0x9" + :chat-name nil + :chat-type 3 + :id "0x1" + :last-message nil + :message nil + :read false + :reply-message nil + :type types/mention}]}}} + (get-in (h/db) [:activity-center :notifications])))))) - (testing "does not fetch next page while it is still loading" - (rf-test/run-test-sync - (setup) - (let [spy-queue (atom [])] - (h/spy-fx spy-queue ::json-rpc/call) - (rf/dispatch [:test/assoc-in [:activity-center :filter :status] - :all]) - (rf/dispatch [:test/assoc-in [:activity-center :filter :type] - types/one-to-one-chat]) - (rf/dispatch [:test/assoc-in [:activity-center :notifications types/one-to-one-chat :all :cursor] - "10"]) - (rf/dispatch [:test/assoc-in [:activity-center :notifications types/one-to-one-chat :all :loading?] - true]) + (testing "does not fetch next page while it is still loading" + (rf-test/run-test-sync + (setup) + (let [spy-queue (atom [])] + (h/spy-fx spy-queue ::json-rpc/call) + (rf/dispatch [:test/assoc-in [:activity-center :filter :status] + :all]) + (rf/dispatch [:test/assoc-in [:activity-center :filter :type] + types/one-to-one-chat]) + (rf/dispatch [:test/assoc-in [:activity-center :notifications types/one-to-one-chat :all :cursor] + "10"]) + (rf/dispatch [:test/assoc-in [:activity-center :notifications types/one-to-one-chat :all :loading?] + true]) - (rf/dispatch [:activity-center.notifications/fetch-next-page]) + (rf/dispatch [:activity-center.notifications/fetch-next-page]) - (is (= [] @spy-queue))))) + (is (= [] @spy-queue))))) - (testing "resets loading flag after an error" - (rf-test/run-test-sync - (setup) - (let [spy-queue (atom [])] - (h/stub-fx-with-callbacks ::json-rpc/call :on-error (constantly :fake-error)) - (h/spy-event-fx spy-queue :activity-center.notifications/fetch-error) - (h/spy-fx spy-queue ::json-rpc/call) - (rf/dispatch [:test/assoc-in [:activity-center :filter :status] - :unread]) - (rf/dispatch [:test/assoc-in [:activity-center :filter :type] - types/one-to-one-chat]) - (rf/dispatch [:test/assoc-in [:activity-center :notifications types/one-to-one-chat :unread :cursor] - ""]) + (testing "resets loading flag after an error" + (rf-test/run-test-sync + (setup) + (let [spy-queue (atom [])] + (h/stub-fx-with-callbacks ::json-rpc/call :on-error (constantly :fake-error)) + (h/spy-event-fx spy-queue :activity-center.notifications/fetch-error) + (h/spy-fx spy-queue ::json-rpc/call) + (rf/dispatch [:test/assoc-in [:activity-center :filter :status] + :unread]) + (rf/dispatch [:test/assoc-in [:activity-center :filter :type] + types/one-to-one-chat]) + (rf/dispatch [:test/assoc-in [:activity-center :notifications types/one-to-one-chat :unread :cursor] + ""]) - (rf/dispatch [:activity-center.notifications/fetch-first-page]) + (rf/dispatch [:activity-center.notifications/fetch-first-page]) - (is (nil? (get-in (h/db) [:activity-center :notifications types/one-to-one-chat :unread :loading?]))) - (is (= [:activity-center.notifications/fetch-error - types/one-to-one-chat - :unread - :fake-error] - (:args (last @spy-queue))))))))) + (is (nil? (get-in (h/db) [:activity-center :notifications types/one-to-one-chat :unread :loading?]))) + (is (= [:activity-center.notifications/fetch-error + types/one-to-one-chat + :unread + :fake-error] + (:args (last @spy-queue)))))))) + +(deftest notifications-fetch-unread-contact-requests-test + (testing "fetches latest unread contact requests" + (let [actual (activity-center/notifications-fetch-unread-contact-requests {:db {}}) + per-page 20] + (is (= {:activity-center + {:notifications + {types/contact-request + {:unread {:loading? true}}}}} + (:db actual))) + + (is (= {:method "wakuext_activityCenterNotificationsBy" + :params ["" per-page types/contact-request activity-center/status-unread]} + (-> actual + ::json-rpc/call + first + (select-keys [:method :params]))))))) + +(deftest notifications-fetch-unread-count-test + (testing "fetches total notification count and store in db" + (rf-test/run-test-sync + (setup) + (let [spy-queue (atom [])] + (h/stub-fx-with-callbacks ::json-rpc/call :on-success (constantly 9)) + (h/spy-fx spy-queue ::json-rpc/call) + + (rf/dispatch [:activity-center.notifications/fetch-unread-count]) + + (is (= "wakuext_unreadActivityCenterNotificationsCount" + (get-in @spy-queue [0 :args 0 :method]))) + (is (= 9 (get-in (h/db) [:activity-center :unread-count]))))))) diff --git a/src/status_im/chat/models/loading.cljs b/src/status_im/chat/models/loading.cljs index ca1bb0c1dd..878a38240d 100644 --- a/src/status_im/chat/models/loading.cljs +++ b/src/status_im/chat/models/loading.cljs @@ -7,7 +7,7 @@ [status-im.chat.models.message-list :as message-list] [taoensso.timbre :as log] [status-im.ethereum.json-rpc :as json-rpc] - [status-im.notifications-center.core :as notification-center])) + [status-im.activity-center.core :as activity-center])) (defn cursor->clock-value [^js cursor] @@ -67,14 +67,14 @@ (fx/defn handle-mark-all-read-successful {:events [::mark-all-read-successful]} [cofx] - (notification-center/get-activity-center-notifications-count cofx)) + (activity-center/notifications-fetch-unread-count cofx)) (fx/defn handle-mark-all-read-in-community-successful {:events [::mark-all-read-in-community-successful]} [{:keys [db] :as cofx} chat-ids] (fx/merge cofx {:db (reduce mark-chat-all-read db chat-ids)} - (notification-center/get-activity-center-notifications-count))) + (activity-center/notifications-fetch-unread-count))) (fx/defn handle-mark-all-read {:events [:chat.ui/mark-all-read-pressed :chat/mark-all-as-read]} diff --git a/src/status_im/chat/models/message.cljs b/src/status_im/chat/models/message.cljs index 0ef78dad50..9d5c8a81c1 100644 --- a/src/status_im/chat/models/message.cljs +++ b/src/status_im/chat/models/message.cljs @@ -205,8 +205,7 @@ #(re-frame/dispatch [:chat/decrease-unviewed-count chat-id %3])))) removed-messages) remove-messages-fx (fn [{:keys [db]}] - {:dispatch-n [[:get-activity-center-notifications] - [:get-activity-center-notifications-count]]})] + {:dispatch [:activity-center.notifications/fetch-unread-count]})] (apply fx/merge cofx (-> mark-as-deleted-fx (concat mark-as-seen-fx) (conj remove-messages-fx))))) diff --git a/src/status_im/communities/core.cljs b/src/status_im/communities/core.cljs index 42bb9d41d9..3350d9057d 100644 --- a/src/status_im/communities/core.cljs +++ b/src/status_im/communities/core.cljs @@ -15,7 +15,7 @@ [status-im2.navigation.events :as navigation] [status-im.utils.handlers :refer [>evt]] [status-im.ui.components.emoji-thumbnail.styles :as emoji-thumbnail-styles] - [status-im.notifications-center.core :as notification-center])) + [status-im.activity-center.core :as activity-center])) (def crop-size 1000) @@ -105,7 +105,7 @@ (fx/merge cofx (handle-response cofx response-js) (navigation/pop-to-root-tab :chat-stack) - (notification-center/get-activity-center-notifications-count))) + (activity-center/notifications-fetch-unread-count))) (fx/defn joined {:events [::joined ::requested-to-join]} @@ -457,7 +457,7 @@ (fx/merge cofx (bottom-sheet/hide-bottom-sheet) (handle-response response-js) - (notification-center/get-activity-center-notifications-count))) + (activity-center/notifications-fetch-unread-count))) (fx/defn member-ban {:events [::member-ban]} diff --git a/src/status_im/contact/block.cljs b/src/status_im/contact/block.cljs index ef141c65d8..cc1993bc61 100644 --- a/src/status_im/contact/block.cljs +++ b/src/status_im/contact/block.cljs @@ -6,7 +6,7 @@ [status-im.data-store.contacts :as contacts-store] [status-im2.navigation.events :as navigation] [status-im.utils.types :as types] - [status-im.notifications-center.core :as notification-center] + [status-im.activity-center.core :as activity-center] [status-im.utils.fx :as fx])) (fx/defn clean-up-chat @@ -47,7 +47,7 @@ (assoc-in [:contacts/contacts public-key :added] false)) :clear-message-notifications [[public-key] (get-in db [:multiaccount :remote-push-notifications-enabled?])]} - (notification-center/get-activity-center-notifications-count) + (activity-center/notifications-fetch-unread-count) fxs))) (fx/defn block-contact diff --git a/src/status_im/contact/chat.cljs b/src/status_im/contact/chat.cljs index 34e9af415d..7b02305cca 100644 --- a/src/status_im/contact/chat.cljs +++ b/src/status_im/contact/chat.cljs @@ -3,16 +3,13 @@ [status-im2.navigation.events :as navigation] [status-im.utils.fx :as fx] [status-im.chat.models :as chat] - [status-im.contact.core :as contact] - [status-im.notifications-center.core :as notification-center])) + [status-im.contact.core :as contact])) (fx/defn send-message-pressed {:events [:contact.ui/send-message-pressed] :interceptors [(re-frame/inject-cofx :random-id-generator)]} [cofx {:keys [public-key ens-name]}] - (fx/merge cofx - (chat/start-chat public-key ens-name) - (notification-center/accept-all-activity-center-notifications-from-chat public-key))) + (chat/start-chat cofx public-key ens-name)) (fx/defn contact-code-submitted {:events [:contact.ui/contact-code-submitted] diff --git a/src/status_im/contact/core.cljs b/src/status_im/contact/core.cljs index dbb068a38c..0272c778e7 100644 --- a/src/status_im/contact/core.cljs +++ b/src/status_im/contact/core.cljs @@ -48,7 +48,7 @@ (and blocked (not was-blocked)) (conj [::contact.block/contact-blocked contact chats])))) [[:offload-messages constants/timeline-chat-id] - [:get-activity-center-notifications-count]] + [:activity-center.notifications/fetch-unread-count]] contacts)] (merge {:db (update db :contacts/contacts diff --git a/src/status_im/data_store/activities.cljs b/src/status_im/data_store/activities.cljs index 8aa85eec92..bce20f403d 100644 --- a/src/status_im/data_store/activities.cljs +++ b/src/status_im/data_store/activities.cljs @@ -1,10 +1,8 @@ (ns status-im.data-store.activities (:require [clojure.set :as set] - [quo.design-system.colors :as colors] [status-im.constants :as constants] [status-im.activity-center.notification-types :as notification-types] - [status-im.data-store.messages :as messages] - [status-im2.setup.config :as config])) + [status-im.data-store.messages :as messages])) (defn- rpc->type [{:keys [type name] :as chat}] (case type @@ -35,15 +33,13 @@ chat)) (defn <-rpc [item] - (cond-> (-> item - rpc->type - (set/rename-keys {:lastMessage :last-message - :replyMessage :reply-message - :chatId :chat-id - :contactVerificationStatus :contact-verification-status}) - (update :last-message #(when % (messages/<-rpc %))) - (update :message #(when % (messages/<-rpc %))) - (update :reply-message #(when % (messages/<-rpc %))) - (dissoc :chatId)) - (not config/new-activity-center-enabled?) - (assoc :color (rand-nth colors/chat-colors)))) + (-> item + rpc->type + (set/rename-keys {:lastMessage :last-message + :replyMessage :reply-message + :chatId :chat-id + :contactVerificationStatus :contact-verification-status}) + (update :last-message #(when % (messages/<-rpc %))) + (update :message #(when % (messages/<-rpc %))) + (update :reply-message #(when % (messages/<-rpc %))) + (dissoc :chatId))) diff --git a/src/status_im/data_store/activities_test.cljs b/src/status_im/data_store/activities_test.cljs index 9960824c98..7186ae8e37 100644 --- a/src/status_im/data_store/activities_test.cljs +++ b/src/status_im/data_store/activities_test.cljs @@ -2,8 +2,7 @@ (:require [cljs.test :refer [deftest is testing]] [status-im.constants :as constants] [status-im.activity-center.notification-types :as notification-types] - [status-im.data-store.activities :as store] - [status-im2.setup.config :as config])) + [status-im.data-store.activities :as store])) (def chat-id "0x04c66155") @@ -19,82 +18,81 @@ :replyMessage {}}) (deftest <-rpc-test - (with-redefs [config/new-activity-center-enabled? true] - (testing "renames keys" - (is (= {:name chat-name - :chat-id chat-id - :contact-verification-status constants/contact-verification-status-pending} - (-> raw-notification - store/<-rpc - (dissoc :last-message :message :reply-message))))) + (testing "renames keys" + (is (= {:name chat-name + :chat-id chat-id + :contact-verification-status constants/contact-verification-status-pending} + (-> raw-notification + store/<-rpc + (dissoc :last-message :message :reply-message))))) - (testing "transforms messages from RPC response" - (is (= {:last-message {:quoted-message nil - :outgoing-status nil - :command-parameters nil - :content {:sticker nil - :rtl? nil - :ens-name nil - :parsed-text nil - :response-to nil - :chat-id nil - :image nil - :line-count nil - :links nil - :text nil} - :outgoing false} - :message nil - :reply-message {:quoted-message nil - :outgoing-status nil - :command-parameters nil - :content {:sticker nil - :rtl? nil - :ens-name nil - :parsed-text nil - :response-to nil - :chat-id nil - :image nil - :line-count nil - :links nil - :text nil} - :outgoing false}} - (-> raw-notification - store/<-rpc - (select-keys [:last-message :message :reply-message]))))) + (testing "transforms messages from RPC response" + (is (= {:last-message {:quoted-message nil + :outgoing-status nil + :command-parameters nil + :content {:sticker nil + :rtl? nil + :ens-name nil + :parsed-text nil + :response-to nil + :chat-id nil + :image nil + :line-count nil + :links nil + :text nil} + :outgoing false} + :message nil + :reply-message {:quoted-message nil + :outgoing-status nil + :command-parameters nil + :content {:sticker nil + :rtl? nil + :ens-name nil + :parsed-text nil + :response-to nil + :chat-id nil + :image nil + :line-count nil + :links nil + :text nil} + :outgoing false}} + (-> raw-notification + store/<-rpc + (select-keys [:last-message :message :reply-message]))))) - (testing "augments notification based on its type" - (is (= {:chat-name chat-name - :chat-type constants/private-group-chat-type - :name chat-name} - (-> raw-notification - (assoc :type notification-types/reply) - store/<-rpc - (select-keys [:name :chat-type :chat-name :public? :group-chat])))) + (testing "augments notification based on its type" + (is (= {:chat-name chat-name + :chat-type constants/private-group-chat-type + :name chat-name} + (-> raw-notification + (assoc :type notification-types/reply) + store/<-rpc + (select-keys [:name :chat-type :chat-name :public? :group-chat])))) - (is (= {:chat-name chat-name - :chat-type constants/private-group-chat-type - :name chat-name} - (-> raw-notification - (assoc :type notification-types/mention) - store/<-rpc - (select-keys [:name :chat-type :chat-name :public? :group-chat])))) + (is (= {:chat-name chat-name + :chat-type constants/private-group-chat-type + :name chat-name} + (-> raw-notification + (assoc :type notification-types/mention) + store/<-rpc + (select-keys [:name :chat-type :chat-name :public? :group-chat])))) - (is (= {:chat-name chat-name - :chat-type constants/private-group-chat-type - :group-chat true - :name chat-name - :public? false} - (-> raw-notification - (assoc :type notification-types/private-group-chat) - store/<-rpc - (select-keys [:name :chat-type :chat-name :public? :group-chat])))) + (is (= {:chat-name chat-name + :chat-type constants/private-group-chat-type + :group-chat true + :name chat-name + :public? false} + (-> raw-notification + (assoc :type notification-types/private-group-chat) + store/<-rpc + (select-keys [:name :chat-type :chat-name :public? :group-chat])))) - (is (= {:chat-name chat-name - :chat-type constants/one-to-one-chat-type - :group-chat false - :name chat-name - :public? false} - (-> raw-notification - (assoc :type notification-types/one-to-one-chat) - store/<-rpc - (select-keys [:name :chat-type :chat-name :public? :group-chat]))))))) + (is (= {:chat-name chat-name + :chat-type constants/one-to-one-chat-type + :group-chat false + :name chat-name + :public? false} + (-> raw-notification + (assoc :type notification-types/one-to-one-chat) + store/<-rpc + (select-keys [:name :chat-type :chat-name :public? :group-chat])))))) diff --git a/src/status_im/events.cljs b/src/status_im/events.cljs index 43e45e8e16..8076b2f1a5 100644 --- a/src/status_im/events.cljs +++ b/src/status_im/events.cljs @@ -32,7 +32,6 @@ status-im.multiaccounts.update.core [status-im.native-module.core :as status] [status-im2.navigation.events :as navigation] - status-im.notifications-center.core status-im.activity-center.core status-im.pairing.core status-im.profile.core diff --git a/src/status_im/group_chats/core.cljs b/src/status_im/group_chats/core.cljs index 76eb1c60d7..6a8dfe4cc2 100644 --- a/src/status_im/group_chats/core.cljs +++ b/src/status_im/group_chats/core.cljs @@ -9,7 +9,7 @@ [status-im.utils.fx :as fx] [status-im.constants :as constants] [status-im.i18n.i18n :as i18n] - [status-im.notifications-center.core :as notification-center])) + [status-im.activity-center.core :as activity-center])) (fx/defn navigate-chat-updated {:events [:navigate-chat-updated]} @@ -24,7 +24,7 @@ {:db (dissoc (:db cofx) :current-chat-id) :dispatch-n [[:sanitize-messages-and-process-response response] [:pop-to-root-tab :chat-stack]]} - (notification-center/get-activity-center-notifications-count))) + (activity-center/notifications-fetch-unread-count))) (fx/defn handle-chat-update {:events [:chat-updated]} @@ -62,8 +62,7 @@ (fx/defn create-from-link [cofx {:keys [chat-id invitation-admin chat-name]}] (if (get-in cofx [:db :chats chat-id]) - {:dispatch-n [[:accept-all-activity-center-notifications-from-chat chat-id] - [:chat.ui/navigate-to-chat chat-id]]} + {:dispatch [:chat.ui/navigate-to-chat chat-id]} {::json-rpc/call [{:method "wakuext_createGroupChatFromInvitation" :params [chat-name chat-id invitation-admin] :js-response true diff --git a/src/status_im/multiaccounts/login/core.cljs b/src/status_im/multiaccounts/login/core.cljs index b327fb0f2d..5e2b51a362 100644 --- a/src/status_im/multiaccounts/login/core.cljs +++ b/src/status_im/multiaccounts/login/core.cljs @@ -32,7 +32,7 @@ [status-im.chat.models.link-preview :as link-preview] [status-im.utils.mobile-sync :as utils.mobile-sync] [status-im.async-storage.core :as async-storage] - [status-im.notifications-center.core :as notifications-center] + [status-im.activity-center.core :as activity-center] [status-im2.navigation.events :as navigation] [status-im.signing.eip1559 :as eip1559] [status-im.data-store.chats :as data-store.chats] @@ -392,8 +392,8 @@ (get-node-config) (communities/fetch) (logging/set-log-level (:log-level multiaccount)) - (notifications-center/get-activity-center-notifications) - (notifications-center/get-activity-center-notifications-count)))) + (activity-center/notifications-fetch-unread-contact-requests) + (activity-center/notifications-fetch-unread-count)))) (re-frame/reg-fx ::open-last-chat diff --git a/src/status_im/notifications_center/core.cljs b/src/status_im/notifications_center/core.cljs deleted file mode 100644 index 9d2a318d4d..0000000000 --- a/src/status_im/notifications_center/core.cljs +++ /dev/null @@ -1,203 +0,0 @@ -(ns status-im.notifications-center.core - (:require [status-im.utils.fx :as fx] - [status-im.ethereum.json-rpc :as json-rpc] - [status-im.activity-center.notification-types :as types] - [taoensso.timbre :as log] - [re-frame.core :as re-frame] - [status-im.data-store.activities :as data-store.activities])) - -(def non-dismissable-notifications - #{types/contact-request - types/contact-request-retracted}) - -(fx/defn handle-activities [{:keys [db]} activities] - (let [{:keys [unread-count notifications]} - (reduce (fn [acc {:keys [read dismissed accepted chat-id] :as notification}] - (if (= "" chat-id) - ;; TODO(rasom): sometimes messages come with empty `chat-id`s - ;; (specifically it happens on `SyncActivityCenterRead` message). - ;; In result, if notification is received with notification center - ;; screen opened, and there is another paired device online, the - ;; last notification disappear from the screen and is shown only - ;; after reopening. It likely makes sense to fix it on status-go - ;; side, but I got lost a bit. - acc - (let [index-existing (->> (map-indexed vector (:notifications acc)) - (filter (fn [[idx {:keys [id]}]] (= id (:id notification)))) - first - first)] - (as-> acc a - (if read - (update a :unread-count dec) - (update a :unread-count inc)) - - (if index-existing - (if (or dismissed accepted) - ;; Remove at specific location - (assoc a :notifications - (into (subvec (:notifications a) 0 index-existing) (subvec (:notifications a) (inc index-existing)))) - ;; Replace element - (do - (assoc-in a [:notifications index-existing] notification))) - (update a :notifications conj notification)))))) - {:unread-count (get db :activity.center/notifications-count 0) - :notifications (into [] (get-in db [:activity.center/notifications :notifications]))} - activities)] - (merge - {:db (-> db - (assoc-in [:activity.center/notifications :notifications] notifications) - (assoc :activity.center/notifications-count (max 0 unread-count)))} - (cond - (= (:view-id db) :notifications-center) - {:dispatch [:mark-all-activity-center-notifications-as-read]} - - (= (:view-id db) :chat) - {:dispatch [:accept-all-activity-center-notifications-from-chat (:current-chat-id db)]})))) - -(fx/defn get-activity-center-notifications-count - {:events [:get-activity-center-notifications-count]} - [_] - {::json-rpc/call [{:method "wakuext_unreadActivityCenterNotificationsCount" - :params [] - :on-success #(re-frame/dispatch [:get-activity-center-notifications-count-success %]) - :on-error #()}]}) - -(fx/defn get-activity-center-notifications-count-success - {:events [:get-activity-center-notifications-count-success]} - [{:keys [db]} result] - {:db (assoc db :activity.center/notifications-count result)}) - -(fx/defn dismiss-activity-center-notifications - {:events [:dismiss-activity-center-notifications]} - [{:keys [db]} ids] - {:db (update-in db [:activity.center/notifications :notifications] - (fn [items] (remove #(get ids (:id %)) items))) - ::json-rpc/call [{:method "wakuext_dismissActivityCenterNotifications" - :params [ids] - :on-success #() - :on-error #()}]}) - -(fx/defn accept-activity-center-notifications - {:events [:accept-activity-center-notifications]} - [{:keys [db]} ids] - (when (seq ids) - {:db (update-in db [:activity.center/notifications :notifications] - (fn [items] (remove #(get ids (:id %)) items))) - ::json-rpc/call [{:method "wakuext_acceptActivityCenterNotifications" - :params [ids] - :js-response true - :on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %]) - :on-error #(log/info "unable to accept activity center notifications" %)}]})) - -(fx/defn accept-all-activity-center-notifications-from-chat - {:events [:accept-all-activity-center-notifications-from-chat]} - [{:keys [db]} chat-id] - (let [notifications (get-in db [:activity.center/notifications :notifications]) - notifications-from-chat (filter #(and - (= chat-id (:chat-id %)) - (not (contains? non-dismissable-notifications (:type %)))) - notifications) - notifications-from-chat-not-read (filter #(and (= chat-id (:chat-id %)) - (not (:read %))) - notifications) - ids (into #{} (map :id notifications-from-chat))] - (when (seq ids) - {:db (-> db - (update-in [:activity.center/notifications :notifications] - (fn [items] - (filter - #(not (contains? ids (:id %))) - items))) - (update :activity.center/notifications-count - (min (db :activity.center/notifications-count) (count notifications-from-chat-not-read)))) - ::json-rpc/call [{:method "wakuext_acceptActivityCenterNotifications" - :params [ids] - :js-response true - :on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %]) - :on-error #(log/info "unable to accept activity center notifications" %)}]}))) - -(fx/defn accept-activity-center-notification-and-open-chat - {:events [:accept-activity-center-notification-and-open-chat]} - [{:keys [db]} id] - {:db (update-in db [:activity.center/notifications :notifications] - (fn [items] (remove #(= id (:id %)) items))) - ::json-rpc/call [{:method "wakuext_acceptActivityCenterNotifications" - :params [[id]] - :js-response true - :on-success #(re-frame/dispatch [:ensure-and-open-chat %]) - :on-error #()}]}) - -(fx/defn ensure-and-open-chat - {:events [:ensure-and-open-chat]} - [{:keys [db]} response-js] - {:dispatch-n [[:sanitize-messages-and-process-response response-js] - [:chat.ui/navigate-to-chat (.-id (aget (.-chats response-js) 0))]]}) - -(fx/defn dismiss-all-activity-center-notifications - {:events [:dismiss-all-activity-center-notifications]} - [{:keys [db]}] - {:db (assoc-in db [:activity.center/notifications :notifications] []) - ::json-rpc/call [{:method "wakuext_dismissAllActivityCenterNotifications" - :params [] - :on-success #() - :on-error #()}]}) - -(fx/defn accept-all-activity-center-notifications - {:events [:accept-all-activity-center-notifications]} - [{:keys [db]}] - {:db (assoc-in db [:activity.center/notifications :notifications] []) - ::json-rpc/call [{:method "wakuext_acceptAllActivityCenterNotifications" - :params [] - :js-response true - :on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %]) - :on-error #()}]}) - -(fx/defn mark-all-activity-center-notifications-as-read - {:events [:mark-all-activity-center-notifications-as-read]} - [{:keys [db]}] - {:db (assoc db :activity.center/notifications-count 0) - ::json-rpc/call [{:method "wakuext_markAllActivityCenterNotificationsRead" - :params [] - :on-success #() - :on-error #()}]}) - -(fx/defn load-notifications [{:keys [db]} cursor] - (when-not (:activity.center/loading? db) - {:db (assoc db :activity.center/loading? true) - ::json-rpc/call [{:method "wakuext_activityCenterNotifications" - :params [cursor 20] - :on-success #(re-frame/dispatch [:activity-center-notifications-success %]) - :on-error #(re-frame/dispatch [:activity-center-notifications-error %])}]})) - -(fx/defn clean-notifications [{:keys [db]}] - {:db (dissoc db :activity.center/notifications)}) - -(fx/defn get-activity-center-notifications - {:events [:get-activity-center-notifications]} - [{:keys [db] :as cofx}] - (let [{:keys [cursor]} (:activity.center/notifications db)] - (fx/merge cofx - (clean-notifications) - (load-notifications "")))) - -(fx/defn load-more-activity-center-notifications - {:events [:load-more-activity-center-notifications]} - [{:keys [db] :as cofx}] - (let [{:keys [cursor]} (:activity.center/notifications db)] - (when (not= cursor "") - (load-notifications cofx cursor)))) - -(fx/defn activity-center-notifications-error - {:events [:activity-center-notifications-error]} - [{:keys [db]} error] - (log/warn "failed to load activity center notifications" error) - {:db (dissoc db :activity.center/loading?)}) - -(fx/defn activity-center-notifications-success - {:events [:activity-center-notifications-success]} - [{:keys [db]} {:keys [cursor notifications]}] - {:db (-> db - (dissoc :activity.center/loading?) - (assoc-in [:activity.center/notifications :cursor] cursor) - (update-in [:activity.center/notifications :notifications] - concat - (map data-store.activities/<-rpc notifications)))}) diff --git a/src/status_im/transport/message/core.cljs b/src/status_im/transport/message/core.cljs index 64171f9697..b7b2097763 100644 --- a/src/status_im/transport/message/core.cljs +++ b/src/status_im/transport/message/core.cljs @@ -5,7 +5,6 @@ [status-im2.contexts.chat.messages.pin.events :as messages.pin] [status-im.chat.models :as models.chat] [status-im.chat.models.reactions :as models.reactions] - [status-im2.setup.config :as config] [status-im.contact.core :as models.contact] [status-im.communities.core :as models.communities] [status-im.pairing.core :as models.pairing] @@ -22,7 +21,6 @@ [status-im.constants :as constants] [status-im.multiaccounts.model :as multiaccounts.model] [status-im.multiaccounts.login.core :as multiaccounts.login] - [status-im.notifications-center.core :as notifications-center] [status-im.visibility-status-updates.core :as models.visibility-status-updates] [status-im.browser.core :as browser] [clojure.string :as string])) @@ -72,13 +70,10 @@ (do (js-delete response-js "activityCenterNotifications") (fx/merge cofx - (if config/new-activity-center-enabled? - (->> activity-notifications - types/js->clj - (map data-store.activities/<-rpc) - activity-center/notifications-reconcile) - (notifications-center/handle-activities (map data-store.activities/<-rpc - (types/js->clj activity-notifications)))) + (->> activity-notifications + types/js->clj + (map data-store.activities/<-rpc) + activity-center/notifications-reconcile) (process-next response-js sync-handler))) (seq installations) diff --git a/src/status_im/ui/screens/communities/community.cljs b/src/status_im/ui/screens/communities/community.cljs index 48355fd7a5..e33623455a 100644 --- a/src/status_im/ui/screens/communities/community.cljs +++ b/src/status_im/ui/screens/communities/community.cljs @@ -157,8 +157,7 @@ {:on-press (fn [] (rf/dispatch [:dismiss-keyboard]) (rf/dispatch [:chat.ui/navigate-to-chat-nav2 chat-id]) - (rf/dispatch [:search/home-filter-changed nil]) - (rf/dispatch [:accept-all-activity-center-notifications-from-chat chat-id])) + (rf/dispatch [:search/home-filter-changed nil])) :on-long-press #(rf/dispatch [:bottom-sheet/show-sheet {:content (fn [] [sheets/actions home-item])}])}]) diff --git a/src/status_im/ui/screens/home/views.cljs b/src/status_im/ui/screens/home/views.cljs index 35da0c44f8..f0d7c60f87 100644 --- a/src/status_im/ui/screens/home/views.cljs +++ b/src/status_im/ui/screens/home/views.cljs @@ -133,8 +133,7 @@ (if config/new-ui-enabled? (re-frame/dispatch [:chat.ui/navigate-to-chat-nav2 chat-id]) (re-frame/dispatch [:chat.ui/navigate-to-chat chat-id])) - (re-frame/dispatch [:search/home-filter-changed nil]) - (re-frame/dispatch [:accept-all-activity-center-notifications-from-chat chat-id])) + (re-frame/dispatch [:search/home-filter-changed nil])) :on-long-press #(re-frame/dispatch [:bottom-sheet/show-sheet {:content (fn [] [sheets/actions home-item])}])}] @@ -150,8 +149,7 @@ (if config/new-ui-enabled? (re-frame/dispatch [:chat.ui/navigate-to-chat-nav2 chat-id]) (re-frame/dispatch [:chat.ui/navigate-to-chat chat-id])) - (re-frame/dispatch [:search/home-filter-changed nil]) - (re-frame/dispatch [:accept-all-activity-center-notifications-from-chat chat-id])) + (re-frame/dispatch [:search/home-filter-changed nil])) :on-long-press #(re-frame/dispatch [:bottom-sheet/show-sheet {:content (fn [] [sheets/actions home-item])}])}] @@ -248,18 +246,14 @@ :accessibility-label :new-chat-button}])) (views/defview notifications-button [] - (views/letsubs [notif-count [:activity.center/notifications-count]] + (views/letsubs [notif-count [:activity-center/unread-count]] [react/view [quo2.button/button {:type :grey :size 32 :width 32 :style {:margin-left 12} :accessibility-label :notifications-button - :on-press #(do (if config/new-activity-center-enabled? - (re-frame/dispatch [:activity-center/open]) - (do - (re-frame/dispatch [:mark-all-activity-center-notifications-as-read]) - (re-frame/dispatch [:navigate-to :notifications-center]))))} + :on-press #(re-frame/dispatch [:activity-center/open])} [icons/icon :main-icons/notification2 {:color (quo2.colors/theme-colors quo2.colors/neutral-100 quo2.colors/white)}]] (when (pos? notif-count) [react/view {:style (merge (styles/counter-public-container) {:top 5 :right 5}) @@ -267,23 +261,6 @@ [react/view {:style styles/counter-public :accessibility-label :notifications-unread-badge}]])])) -(views/defview notifications-button-old [] - (views/letsubs [notif-count [:activity.center/notifications-count]] - [react/view - [quo/button {:type :icon - :style {:margin-left 10} - :accessibility-label "notifications-button" - :on-press #(do - (re-frame/dispatch [:mark-all-activity-center-notifications-as-read]) - (re-frame/dispatch [:navigate-to :notifications-center])) - :theme :icon} - :main-icons/notification] - (when (pos? notif-count) - [react/view {:style (merge (styles/counter-public-container) {:top 5 :right 5}) - :pointer-events :none} - [react/view {:style styles/counter-public - :accessibility-label :notifications-unread-badge}]])])) - (defn qr-button [] [quo2.button/button {:type :grey :accessibility-label "qr-button" @@ -341,7 +318,6 @@ [topbar/topbar {:title (i18n/label :t/chat) :navigation :none :right-component [react/view {:flex-direction :row :margin-right 16} - [connectivity/connectivity-button] - [notifications-button-old]]}] + [connectivity/connectivity-button]]}] [chats-list-old] [plus-button-old]]) diff --git a/src/status_im/ui/screens/notifications_center/styles.cljs b/src/status_im/ui/screens/notifications_center/styles.cljs deleted file mode 100644 index 5b36b65a25..0000000000 --- a/src/status_im/ui/screens/notifications_center/styles.cljs +++ /dev/null @@ -1,88 +0,0 @@ -(ns status-im.ui.screens.notifications-center.styles - (:require [quo.design-system.colors :as colors] - [quo.design-system.spacing :as spacing])) - -(def notification-message-text - {:flex 1 - :align-self :stretch - :line-height 22 - :font-size 15}) - -(def notification-reply-text - {:line-height 20 - :font-size 13 - :color colors/text-gray}) - -(def mention-text - {:color colors/blue}) - -(def datetime-text - {:color colors/text-gray - :font-size 10 - :text-align :right - :letter-spacing 0.4 - :align-items :center - :line-height 12 - :position :absolute - :top 17 - :right 16}) - -(def group-info-container - (merge - {:height 22 - :align-self :baseline - :align-items :center - :justify-content :center - :border-radius 11 - :border-color colors/gray-transparent-40 - :border-width 1 - :margin-top 6 - :margin-bottom 10 - :flex-direction :row} - (:x-tiny spacing/padding-horizontal))) - -(def reply-message-container - (merge - {:height 22 - :align-self :baseline - :align-items :center - :border-radius 11 - :border-color colors/gray-transparent-40 - :border-width 1 - :margin-top 6 - :margin-bottom 10 - :margin-right 15 - :flex-direction :row} - (:x-tiny spacing/padding-horizontal))) - -(defn notification-container [read] - {:min-height 64 - :background-color (when-not read colors/blue-light)}) - -(def notification-content-container - {:flex 1}) - -(defn photo-container [has-header?] - {:position :absolute - :top (if has-header? 37 12) - :left 16}) - -(defn title-text [title-text-width] - {:margin-left 72 - :margin-top 12 - :margin-right 50 - :width title-text-width}) - -(def notification-message-container - {:margin-left 72 - :margin-right 16}) - -(def group-icon - {:margin-right 4}) - -(def reply-icon - {:margin-right 1}) - -(def community-info-container - {:flex-direction :row - :align-items :center}) diff --git a/src/status_im/ui/screens/notifications_center/views.cljs b/src/status_im/ui/screens/notifications_center/views.cljs deleted file mode 100644 index 7e197ecd67..0000000000 --- a/src/status_im/ui/screens/notifications_center/views.cljs +++ /dev/null @@ -1,126 +0,0 @@ -(ns status-im.ui.screens.notifications-center.views - (:require [status-im.ui.components.react :as react] - [status-im.ui.components.topbar :as topbar] - [status-im.i18n.i18n :as i18n] - [status-im.ui.components.list.views :as list] - [re-frame.core :as re-frame] - [quo.core :as quo] - [quo.design-system.colors :as colors] - [reagent.core :as reagent] - [status-im.ui.components.toolbar :as toolbar] - [clojure.string :as string] - [status-im.constants :as constants] - [status-im.activity-center.notification-types :as types] - [status-im.ui.screens.notifications-center.views.notification :as notification])) - -(def selecting (reagent/atom nil)) -(def select-all (reagent/atom nil)) -(def selected-items (reagent/atom #{})) - -(defn render-fn [{:keys [id type] :as home-item}] - (when id - (let [selected (get @selected-items id) - on-change (fn [] - (when-not (= type types/mention) (swap! selected-items #(if selected (disj % id) (conj % id)))))] - [react/view {:flex-direction :row :flex 1 :align-items :center} - (when (and @selecting (not (= type types/mention))) - [react/view {:padding-left 16} - [quo/checkbox {:value (or @select-all selected) - :disabled @select-all - :on-change on-change}]]) - [react/view {:flex 1} - [notification/activity-text-item - home-item - {:on-press (fn [] - (if @selecting - (on-change) - ;; We don't dispatch on contact requests unless - ;; accepted - (when (or (not= type types/contact-request) - (= constants/contact-request-message-state-accepted (get-in home-item [:message :contact-request-state]))) - (re-frame/dispatch [:accept-activity-center-notification-and-open-chat id])))) - :on-long-press #(do (reset! selecting true) - (when-not (= type types/mention) (swap! selected-items conj id)))}]]]))) -(defn filter-item [] - [react/view {:padding-vertical 8 :border-bottom-width 1 :border-bottom-color colors/gray-lighter} - [react/view {:align-items :center :justify-content :space-between :padding-horizontal 16 :flex-direction :row} - (if @selecting - [react/view {:flex-direction :row} - [quo/checkbox {:value @select-all - :on-change #(do - (reset! selected-items #{}) - (swap! select-all not))}] - [react/text {:style {:color colors/gray :margin-left 20}} - (str (if @select-all (i18n/label :t/all) (count @selected-items)) - " " (string/lower-case (i18n/label :t/selected)))]] - [quo/button {:type :secondary - :accessibility-label :select-button-activity-center - :on-press #(reset! selecting true)} - (i18n/label :t/select)]) - (when @selecting - [quo/button {:type :secondary - :on-press #(do (reset! selecting false) - (reset! select-all false) - (reset! selected-items #{}))} - (i18n/label :t/cancel)])]]) - -(defn reset-state [] - (reset! selecting nil) - (reset! select-all nil) - (reset! selected-items #{})) - -(defn toolbar-action [accept] - (if accept - (if @select-all - (re-frame/dispatch [:accept-all-activity-center-notifications]) - (re-frame/dispatch [:accept-activity-center-notifications @selected-items])) - (if @select-all - (re-frame/dispatch [:dismiss-all-activity-center-notifications]) - (re-frame/dispatch [:dismiss-activity-center-notifications @selected-items]))) - (reset-state)) - -(defn center [] - (reagent/create-class - {:display-name "activity-center" - :component-did-mount #(re-frame/dispatch [:get-activity-center-notifications]) - :reagent-render - (fn [] - (let [notifications @(re-frame/subscribe [:activity.center/notifications-grouped-by-date])] - [react/keyboard-avoiding-view {:style {:flex 1} - :ignore-offset true} - [topbar/topbar {:navigation {:on-press #(do - (reset-state) - (re-frame/dispatch [:navigate-back]))} - :title (i18n/label :t/activity)}] - (if (= (count notifications) 0) - [react/view {:style {:flex 1 - :justify-content :center - :align-items :center}} - [quo/text {:color :secondary - :size :large - :align :center} - (i18n/label :t/empty-activity-center)]] - [:<> - [filter-item] - [list/section-list - {:key-fn #(str (:timestamp %) (or (:chat-id %) (:id %))) - :on-end-reached #(re-frame/dispatch [:load-more-activity-center-notifications]) - :keyboard-should-persist-taps :always - :sections notifications - :render-fn render-fn - :stickySectionHeadersEnabled false - :render-section-header-fn - (fn [{:keys [title]}] - [quo/list-header title])}] - (when (or @select-all (> (count @selected-items) 0)) - [toolbar/toolbar - {:show-border? true - :left [quo/button {:type :secondary - :theme :negative - :accessibility-label :reject-and-delete-activity-center - :on-press #(toolbar-action false)} - (i18n/label :t/reject-and-delete)] - :right [quo/button {:type :secondary - :accessibility-label :accept-and-add-activity-center - :on-press #(toolbar-action true)} - (i18n/label :t/accept-and-add)]}])])]))})) diff --git a/src/status_im/ui/screens/notifications_center/views/notification.cljs b/src/status_im/ui/screens/notifications_center/views/notification.cljs deleted file mode 100644 index 6a10410099..0000000000 --- a/src/status_im/ui/screens/notifications_center/views/notification.cljs +++ /dev/null @@ -1,137 +0,0 @@ -(ns status-im.ui.screens.notifications-center.views.notification - (:require [status-im.ui.components.react :as react] - [re-frame.core :as re-frame] - [quo.core :as quo] - [quo.components.animated.pressable :as animation] - [status-im.i18n.i18n :as i18n] - [status-im.ui.screens.notifications-center.styles :as styles] - [status-im.utils.handlers :refer [ [quo/discover-card {:title (i18n/label :t/invite-friends-to-status) :description (i18n/label :t/share-invite-link)}] diff --git a/src/status_im2/setup/config.cljs b/src/status_im2/setup/config.cljs index 3f639db558..8d2f21042a 100644 --- a/src/status_im2/setup/config.cljs +++ b/src/status_im2/setup/config.cljs @@ -138,7 +138,3 @@ ;;TODO for development only should be removed in status 2.0 (def new-ui-enabled? true) - -;; TODO: Remove this (highly) temporary flag once the new Activity Center is -;; usable enough to replace the old one **in the new UI**. -(def new-activity-center-enabled? true) diff --git a/src/status_im2/subs/activity_center.cljs b/src/status_im2/subs/activity_center.cljs index b649d9fdfb..d5f677a16c 100644 --- a/src/status_im2/subs/activity_center.cljs +++ b/src/status_im2/subs/activity_center.cljs @@ -1,10 +1,6 @@ (ns status-im2.subs.activity-center (:require [re-frame.core :as re-frame] - [status-im.utils.datetime :as datetime] - [status-im.multiaccounts.core :as multiaccounts] - [status-im2.common.constants :as constants] - [status-im.activity-center.notification-types :as types] - [clojure.string :as string])) + [status-im.activity-center.notification-types :as types])) (re-frame/reg-sub :activity-center/notifications @@ -12,6 +8,12 @@ (fn [activity-center] (:notifications activity-center))) +(re-frame/reg-sub + :activity-center/unread-count + :<- [:activity-center] + (fn [activity-center] + (:unread-count activity-center))) + (re-frame/reg-sub :activity-center/filter-status :<- [:activity-center] @@ -38,48 +40,8 @@ (fn [filter-status] (= :unread filter-status))) -(defn- group-notifications-by-date - [notifications] - (->> notifications - (group-by #(datetime/timestamp->date-key (:timestamp %))) - (sort-by key >) - (map (fn [[date-key notifications]] - (let [first-notification (first notifications)] - {:title (string/capitalize (datetime/day-relative (:timestamp first-notification))) - :key date-key - :data (sort-by :timestamp > notifications)}))))) - (re-frame/reg-sub - :activity.center/notifications-grouped-by-date - :<- [:activity.center/notifications] - :<- [:contacts/contacts] - (fn [[{:keys [notifications]} contacts]] - (let [supported-notifications - (filter (fn [{:keys [type last-message message]}] - (or (and (= types/one-to-one-chat type) - (not (nil? last-message))) - (and (= types/contact-request type) - (not= constants/contact-request-message-state-none - (-> contacts - (multiaccounts/contact-by-identity (:from message)) - :contact-request-state))) - (= types/contact-request-retracted type) - (= types/private-group-chat type) - (= types/reply type) - (= types/mention type))) - notifications)] - (group-notifications-by-date - (map #(assoc % - :timestamp (or (:timestamp %) (:timestamp (or (:message %) (:last-message %)))) - :contact (multiaccounts/contact-by-identity contacts (get-in % [:message :from]))) - supported-notifications))))) - -(re-frame/reg-sub - :activity.center/notifications-contact-requests - :<- [:activity.center/notifications-grouped-by-date] + :activity-center/pending-contact-requests + :<- [:activity-center/notifications] (fn [notifications] - (reduce - (fn [acc {:keys [data]}] - (concat acc (filter #(= 1 (get-in % [:message :contact-request-state])) data))) - [] - notifications))) + (get-in notifications [types/contact-request :unread :data]))) diff --git a/src/status_im2/subs/root.cljs b/src/status_im2/subs/root.cljs index a9cfed6da0..b4490b44be 100644 --- a/src/status_im2/subs/root.cljs +++ b/src/status_im2/subs/root.cljs @@ -228,8 +228,6 @@ (reg-root-key-sub :communities/enabled? :communities/enabled?) (reg-root-key-sub :communities/resolve-community-info :communities/resolve-community-info) -(reg-root-key-sub :activity.center/notifications :activity.center/notifications) -(reg-root-key-sub :activity.center/notifications-count :activity.center/notifications-count) (reg-root-key-sub :activity-center :activity-center) (reg-root-key-sub :bug-report/description-error :bug-report/description-error)