mirror of
https://github.com/status-im/status-react.git
synced 2025-02-23 08:08:33 +00:00
- Extract tabs component from the main screen component to reduce rendering cost when switching tabs. - Display empty state component when there are no notifications. - Fix screen flickering due to quickly flushing and filling the db state. - Display top bar as a sticky header. - Namespace icon keywords. - Correctly sorts notifications during reconciliation. - Remove warning about children without unique key.
This commit is contained in:
parent
adfb0ddb5d
commit
af5f979443
@ -4,6 +4,7 @@
|
|||||||
[quo2.components.tabs.tab :as tab]
|
[quo2.components.tabs.tab :as tab]
|
||||||
[reagent.core :as reagent]
|
[reagent.core :as reagent]
|
||||||
[status-im.ui.components.react :as react]
|
[status-im.ui.components.react :as react]
|
||||||
|
[status-im.utils.core :as utils]
|
||||||
[status-im.utils.number :as number-utils]))
|
[status-im.utils.number :as number-utils]))
|
||||||
|
|
||||||
(def default-tab-size 32)
|
(def default-tab-size 32)
|
||||||
@ -82,8 +83,8 @@
|
|||||||
(let [maybe-mask-wrapper (if fade-end?
|
(let [maybe-mask-wrapper (if fade-end?
|
||||||
[react/masked-view
|
[react/masked-view
|
||||||
{:mask-element (reagent/as-element
|
{:mask-element (reagent/as-element
|
||||||
[react/linear-gradient {:colors ["black" "transparent"]
|
[react/linear-gradient {:colors [:black :transparent]
|
||||||
:locations [(@fading :fade-end-percentage) 1]
|
:locations [(get @fading :fade-end-percentage) 1]
|
||||||
:start {:x 0 :y 0}
|
:start {:x 0 :y 0}
|
||||||
:end {:x 1 :y 0}
|
:end {:x 1 :y 0}
|
||||||
:pointer-events :none
|
:pointer-events :none
|
||||||
@ -99,7 +100,9 @@
|
|||||||
:on-change
|
:on-change
|
||||||
:scroll-on-press?
|
:scroll-on-press?
|
||||||
:size)
|
:size)
|
||||||
{:ref (partial reset! flat-list-ref)
|
(when scroll-on-press?
|
||||||
|
{:initial-scroll-index (utils/first-index #(= @active-tab-id (:id %)) data)})
|
||||||
|
{:ref #(reset! flat-list-ref %)
|
||||||
:extra-data (str @active-tab-id)
|
:extra-data (str @active-tab-id)
|
||||||
:horizontal true
|
:horizontal true
|
||||||
:scroll-event-throttle scroll-event-throttle
|
:scroll-event-throttle scroll-event-throttle
|
||||||
@ -116,7 +119,7 @@
|
|||||||
:layout-width layout-width
|
:layout-width layout-width
|
||||||
:max-fade-percentage fade-end-percentage})]
|
:max-fade-percentage fade-end-percentage})]
|
||||||
;; Avoid unnecessary re-rendering.
|
;; Avoid unnecessary re-rendering.
|
||||||
(when (not= new-percentage (@fading :fade-end-percentage))
|
(when (not= new-percentage (get @fading :fade-end-percentage))
|
||||||
(swap! fading assoc :fade-end-percentage new-percentage))))
|
(swap! fading assoc :fade-end-percentage new-percentage))))
|
||||||
(when on-scroll
|
(when on-scroll
|
||||||
(on-scroll e)))
|
(on-scroll e)))
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
(ns status-im.activity-center.core
|
(ns status-im.activity-center.core
|
||||||
(:require [re-frame.core :as re-frame]
|
(:require [re-frame.core :as re-frame]
|
||||||
|
[status-im.constants :as constants]
|
||||||
[status-im.data-store.activities :as data-store.activities]
|
[status-im.data-store.activities :as data-store.activities]
|
||||||
[status-im.ethereum.json-rpc :as json-rpc]
|
[status-im.ethereum.json-rpc :as json-rpc]
|
||||||
[status-im.utils.fx :as fx]
|
[status-im.utils.fx :as fx]
|
||||||
@ -8,90 +9,117 @@
|
|||||||
;;;; Notification reconciliation
|
;;;; Notification reconciliation
|
||||||
|
|
||||||
(defn- update-notifications
|
(defn- update-notifications
|
||||||
[old new]
|
"Insert `new-notifications` in `db-notifications`.
|
||||||
(let [ids-to-be-removed (->> new
|
|
||||||
(filter #(or (:dismissed %) (:accepted %)))
|
Although correct, this is a naive implementation for reconciling notifications
|
||||||
(map :id))
|
because for every notification in `new-notifications`, linear scans will be
|
||||||
grouped-new (apply dissoc (group-by :id new) ids-to-be-removed)
|
performed to remove it and sorting will be performed for every new insertion.
|
||||||
grouped-old (apply dissoc (group-by :id old) ids-to-be-removed)]
|
If the number of existing notifications cached in the app db becomes
|
||||||
(->> (merge grouped-old grouped-new)
|
~excessively~ big, this implementation will probably need to be revisited."
|
||||||
vals
|
[db-notifications new-notifications]
|
||||||
(map first))))
|
(reduce (fn [acc {:keys [id type read] :as notification}]
|
||||||
|
(let [filter-status (if read :read :unread)]
|
||||||
|
(cond-> (-> acc
|
||||||
|
(update-in [type :read :data]
|
||||||
|
(fn [data]
|
||||||
|
(remove #(= id (:id %)) data)))
|
||||||
|
(update-in [type :unread :data]
|
||||||
|
(fn [data]
|
||||||
|
(remove #(= id (:id %)) data))))
|
||||||
|
(not (or (:dismissed notification) (:accepted notification)))
|
||||||
|
(update-in [type filter-status :data]
|
||||||
|
(fn [data]
|
||||||
|
(->> notification
|
||||||
|
(conj data)
|
||||||
|
(sort-by (juxt :timestamp :id))
|
||||||
|
reverse))))))
|
||||||
|
db-notifications
|
||||||
|
new-notifications))
|
||||||
|
|
||||||
(fx/defn notifications-reconcile
|
(fx/defn notifications-reconcile
|
||||||
{:events [:activity-center.notifications/reconcile]}
|
{:events [:activity-center.notifications/reconcile]}
|
||||||
[{:keys [db]} new-notifications]
|
[{:keys [db]} new-notifications]
|
||||||
(let [{read-new true
|
(when (seq new-notifications)
|
||||||
unread-new false} (group-by :read new-notifications)
|
{:db (update-in db [:activity-center :notifications]
|
||||||
read-old (get-in db [:activity-center :notifications-read :data])
|
update-notifications new-notifications)}))
|
||||||
unread-old (get-in db [:activity-center :notifications-unread :data])]
|
|
||||||
{:db (-> db
|
|
||||||
(assoc-in [:activity-center :notifications-read :data]
|
|
||||||
(update-notifications read-old read-new))
|
|
||||||
(assoc-in [:activity-center :notifications-unread :data]
|
|
||||||
(update-notifications unread-old unread-new)))}))
|
|
||||||
|
|
||||||
;;;; Notifications fetching and pagination
|
;;;; Notifications fetching and pagination
|
||||||
|
|
||||||
(def notifications-per-page
|
(def defaults
|
||||||
20)
|
{:filter-status :unread
|
||||||
|
:filter-type constants/activity-center-notification-type-no-type
|
||||||
|
:notifications-per-page 10})
|
||||||
|
|
||||||
(def start-or-end-cursor
|
(def start-or-end-cursor
|
||||||
"")
|
"")
|
||||||
|
|
||||||
(defn notifications-group->rpc-method
|
(defn- valid-cursor?
|
||||||
[notifications-group]
|
[cursor]
|
||||||
(if (= notifications-group :notifications-read)
|
(and (some? cursor)
|
||||||
|
(not= cursor start-or-end-cursor)))
|
||||||
|
|
||||||
|
(defn- filter-status->rpc-method
|
||||||
|
[filter-status]
|
||||||
|
(if (= filter-status :read)
|
||||||
"wakuext_readActivityCenterNotifications"
|
"wakuext_readActivityCenterNotifications"
|
||||||
"wakuext_unreadActivityCenterNotifications"))
|
"wakuext_unreadActivityCenterNotifications"))
|
||||||
|
|
||||||
(defn notifications-read-status->group
|
|
||||||
[status-filter]
|
|
||||||
(if (= status-filter :read)
|
|
||||||
:notifications-read
|
|
||||||
:notifications-unread))
|
|
||||||
|
|
||||||
(fx/defn notifications-fetch
|
(fx/defn notifications-fetch
|
||||||
[{:keys [db]} cursor notifications-group]
|
[{:keys [db]} {:keys [cursor filter-type filter-status reset-data?]}]
|
||||||
(when-not (get-in db [:activity-center notifications-group :loading?])
|
(when-not (get-in db [:activity-center :notifications filter-type filter-status :loading?])
|
||||||
{:db (assoc-in db [:activity-center notifications-group :loading?] true)
|
{:db (assoc-in db [:activity-center :notifications filter-type filter-status :loading?] true)
|
||||||
::json-rpc/call [{:method (notifications-group->rpc-method notifications-group)
|
::json-rpc/call [{:method (filter-status->rpc-method filter-status)
|
||||||
:params [cursor notifications-per-page]
|
:params [cursor (defaults :notifications-per-page) filter-type]
|
||||||
:on-success #(re-frame/dispatch [:activity-center.notifications/fetch-success notifications-group %])
|
:on-success #(re-frame/dispatch [:activity-center.notifications/fetch-success filter-type filter-status reset-data? %])
|
||||||
:on-error #(re-frame/dispatch [:activity-center.notifications/fetch-error notifications-group %])}]}))
|
:on-error #(re-frame/dispatch [:activity-center.notifications/fetch-error filter-type filter-status %])}]}))
|
||||||
|
|
||||||
(fx/defn notifications-fetch-first-page
|
(fx/defn notifications-fetch-first-page
|
||||||
{:events [:activity-center.notifications/fetch-first-page]}
|
{:events [:activity-center.notifications/fetch-first-page]}
|
||||||
[{:keys [db] :as cofx} {:keys [status-filter] :or {status-filter :unread}}]
|
[{:keys [db] :as cofx} {:keys [filter-type filter-status]}]
|
||||||
(let [notifications-group (notifications-read-status->group status-filter)]
|
(let [filter-type (or filter-type
|
||||||
|
(get-in db [:activity-center :filter :type]
|
||||||
|
(defaults :filter-type)))
|
||||||
|
filter-status (or filter-status
|
||||||
|
(get-in db [:activity-center :filter :status]
|
||||||
|
(defaults :filter-status)))]
|
||||||
(fx/merge cofx
|
(fx/merge cofx
|
||||||
{:db (-> db
|
{:db (-> db
|
||||||
(assoc-in [:activity-center :current-status-filter] status-filter)
|
(assoc-in [:activity-center :filter :type] filter-type)
|
||||||
(update-in [:activity-center notifications-group] dissoc :loading?)
|
(assoc-in [:activity-center :filter :status] filter-status))}
|
||||||
(update-in [:activity-center notifications-group] dissoc :data))}
|
(notifications-fetch {:cursor start-or-end-cursor
|
||||||
(notifications-fetch start-or-end-cursor notifications-group))))
|
:filter-type filter-type
|
||||||
|
:filter-status filter-status
|
||||||
|
:reset-data? true}))))
|
||||||
|
|
||||||
(fx/defn notifications-fetch-next-page
|
(fx/defn notifications-fetch-next-page
|
||||||
{:events [:activity-center.notifications/fetch-next-page]}
|
{:events [:activity-center.notifications/fetch-next-page]}
|
||||||
[{:keys [db] :as cofx}]
|
[{:keys [db] :as cofx}]
|
||||||
(let [status-filter (get-in db [:activity-center :current-status-filter])
|
(let [{:keys [type status]} (get-in db [:activity-center :filter])
|
||||||
notifications-group (notifications-read-status->group status-filter)
|
{:keys [cursor]} (get-in db [:activity-center :notifications type status])]
|
||||||
{:keys [cursor]} (get-in db [:activity-center notifications-group])]
|
(when (valid-cursor? cursor)
|
||||||
(when-not (= cursor start-or-end-cursor)
|
(notifications-fetch cofx {:cursor cursor
|
||||||
(notifications-fetch cofx cursor notifications-group))))
|
:filter-type type
|
||||||
|
:filter-status status
|
||||||
|
:reset-data? false}))))
|
||||||
|
|
||||||
(fx/defn notifications-fetch-success
|
(fx/defn notifications-fetch-success
|
||||||
{:events [:activity-center.notifications/fetch-success]}
|
{:events [:activity-center.notifications/fetch-success]}
|
||||||
[{:keys [db]} notifications-group {:keys [cursor notifications]}]
|
[{:keys [db]}
|
||||||
{:db (-> db
|
filter-type
|
||||||
(update-in [:activity-center notifications-group] dissoc :loading?)
|
filter-status
|
||||||
(assoc-in [:activity-center notifications-group :cursor] cursor)
|
reset-data?
|
||||||
(update-in [:activity-center notifications-group :data]
|
{:keys [cursor notifications]}]
|
||||||
concat
|
(let [processed (map data-store.activities/<-rpc notifications)]
|
||||||
(map data-store.activities/<-rpc notifications)))})
|
{:db (-> db
|
||||||
|
(assoc-in [:activity-center :notifications filter-type filter-status :cursor] cursor)
|
||||||
|
(update-in [:activity-center :notifications filter-type filter-status] dissoc :loading?)
|
||||||
|
(update-in [:activity-center :notifications filter-type filter-status :data]
|
||||||
|
(if reset-data?
|
||||||
|
(constantly processed)
|
||||||
|
#(concat %1 processed))))}))
|
||||||
|
|
||||||
(fx/defn notifications-fetch-error
|
(fx/defn notifications-fetch-error
|
||||||
{:events [:activity-center.notifications/fetch-error]}
|
{:events [:activity-center.notifications/fetch-error]}
|
||||||
[{:keys [db]} notifications-group error]
|
[{:keys [db]} filter-type filter-status error]
|
||||||
(log/warn "Failed to load Activity Center notifications" error)
|
(log/warn "Failed to load Activity Center notifications" error)
|
||||||
{:db (update-in db [:activity-center notifications-group] dissoc :loading?)})
|
{:db (update-in db [:activity-center :notifications filter-type filter-status] dissoc :loading?)})
|
||||||
|
@ -2,99 +2,285 @@
|
|||||||
(:require [cljs.test :refer [deftest is testing]]
|
(:require [cljs.test :refer [deftest is testing]]
|
||||||
[day8.re-frame.test :as rf-test]
|
[day8.re-frame.test :as rf-test]
|
||||||
[re-frame.core :as rf]
|
[re-frame.core :as rf]
|
||||||
|
[status-im.constants :as c]
|
||||||
[status-im.ethereum.json-rpc :as json-rpc]
|
[status-im.ethereum.json-rpc :as json-rpc]
|
||||||
[status-im.test-helpers :as h]
|
status-im.events
|
||||||
status-im.events))
|
[status-im.test-helpers :as h]))
|
||||||
|
|
||||||
(defn setup []
|
(defn setup []
|
||||||
(h/register-helper-events)
|
(h/register-helper-events)
|
||||||
(rf/dispatch [:init/app-started]))
|
(rf/dispatch [:init/app-started]))
|
||||||
|
|
||||||
|
(defn remove-color-key
|
||||||
|
"Remove `:color` key from notifications because they have random values that we
|
||||||
|
can't assert against."
|
||||||
|
[grouped-notifications {:keys [type status]}]
|
||||||
|
(update-in grouped-notifications
|
||||||
|
[type status :data]
|
||||||
|
(fn [old _]
|
||||||
|
(map #(dissoc % :color) old))
|
||||||
|
nil))
|
||||||
|
|
||||||
(deftest notifications-reconcile-test
|
(deftest notifications-reconcile-test
|
||||||
(testing "does nothing when there are no new notifications"
|
(testing "does nothing when there are no new notifications"
|
||||||
(rf-test/run-test-sync
|
(rf-test/run-test-sync
|
||||||
(setup)
|
(setup)
|
||||||
(let [read [{:id "0x1" :read true}]
|
(let [notifications {c/activity-center-notification-type-one-to-one-chat
|
||||||
unread [{:id "0x4" :read false}]]
|
{:read {:cursor ""
|
||||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications-read :data] read])
|
:data [{:id "0x1"
|
||||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications-unread :data] unread])
|
:read true
|
||||||
|
:type c/activity-center-notification-type-one-to-one-chat}
|
||||||
|
{:id "0x2"
|
||||||
|
:read true
|
||||||
|
:type c/activity-center-notification-type-one-to-one-chat}]}
|
||||||
|
:unread {:cursor ""
|
||||||
|
:data [{:id "0x3"
|
||||||
|
:read false
|
||||||
|
:type c/activity-center-notification-type-one-to-one-chat}]}}
|
||||||
|
c/activity-center-notification-type-private-group-chat
|
||||||
|
{:unread {:cursor ""
|
||||||
|
:data [{:id "0x4"
|
||||||
|
:read false
|
||||||
|
:type c/activity-center-notification-type-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 (= read (get-in (h/db) [:activity-center :notifications-read :data])))
|
(is (= notifications (get-in (h/db) [:activity-center :notifications]))))))
|
||||||
(is (= unread (get-in (h/db) [:activity-center :notifications-unread :data]))))))
|
|
||||||
|
|
||||||
(testing "removes dismissed or accepted notifications"
|
(testing "removes dismissed or accepted notifications"
|
||||||
(rf-test/run-test-sync
|
(rf-test/run-test-sync
|
||||||
(setup)
|
(setup)
|
||||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications-read :data]
|
(rf/dispatch [:test/assoc-in [:activity-center :notifications]
|
||||||
[{:id "0x1" :read true}
|
{c/activity-center-notification-type-one-to-one-chat
|
||||||
{:id "0x2" :read true}
|
{:read {:cursor ""
|
||||||
{:id "0x3" :read true}]])
|
:data [{:id "0x1" :read true :type c/activity-center-notification-type-one-to-one-chat}
|
||||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications-unread :data]
|
{:id "0x2" :read true :type c/activity-center-notification-type-one-to-one-chat}]}
|
||||||
[{:id "0x4" :read false}
|
:unread {:cursor ""
|
||||||
{:id "0x5" :read false}
|
:data [{:id "0x3" :read false :type c/activity-center-notification-type-one-to-one-chat}]}}
|
||||||
{:id "0x6" :read false}]])
|
2 {:unread {:cursor ""
|
||||||
|
:data [{:id "0x4" :read false :type 2}
|
||||||
|
{:id "0x6" :read false :type 2}]}}}])
|
||||||
|
|
||||||
(rf/dispatch [:activity-center.notifications/reconcile
|
(rf/dispatch [:activity-center.notifications/reconcile
|
||||||
[{:id "0x1" :read true :dismissed true}
|
[{:id "0x1"
|
||||||
{:id "0x3" :read true :accepted true}
|
:read true
|
||||||
{:id "0x4" :read false :dismissed true}
|
:type c/activity-center-notification-type-one-to-one-chat
|
||||||
{:id "0x5" :read false :accepted true}]])
|
:dismissed true}
|
||||||
|
{:id "0x3"
|
||||||
|
:read false
|
||||||
|
:type c/activity-center-notification-type-one-to-one-chat
|
||||||
|
:accepted true}
|
||||||
|
{:id "0x4"
|
||||||
|
:read false
|
||||||
|
:type c/activity-center-notification-type-private-group-chat
|
||||||
|
:dismissed true}
|
||||||
|
{:id "0x5"
|
||||||
|
:read false
|
||||||
|
:type c/activity-center-notification-type-private-group-chat
|
||||||
|
:accepted true}]])
|
||||||
|
|
||||||
(is (= [{:id "0x2" :read true}]
|
(is (= {c/activity-center-notification-type-one-to-one-chat
|
||||||
(get-in (h/db) [:activity-center :notifications-read :data])))
|
{:read {:cursor ""
|
||||||
(is (= [{:id "0x6" :read false}]
|
:data [{:id "0x2"
|
||||||
(get-in (h/db) [:activity-center :notifications-unread :data])))))
|
:read true
|
||||||
|
:type c/activity-center-notification-type-one-to-one-chat}]}
|
||||||
|
:unread {:cursor ""
|
||||||
|
:data []}}
|
||||||
|
c/activity-center-notification-type-private-group-chat
|
||||||
|
{:read {:data []}
|
||||||
|
:unread {:cursor ""
|
||||||
|
:data [{:id "0x6"
|
||||||
|
:read false
|
||||||
|
:type c/activity-center-notification-type-private-group-chat}]}}}
|
||||||
|
(get-in (h/db) [:activity-center :notifications])))))
|
||||||
|
|
||||||
(testing "replaces old notifications with newly arrived ones"
|
(testing "replaces old notifications with newly arrived ones"
|
||||||
(rf-test/run-test-sync
|
(rf-test/run-test-sync
|
||||||
(setup)
|
(setup)
|
||||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications-read :data]
|
(rf/dispatch [:test/assoc-in [:activity-center :notifications]
|
||||||
[{:id "0x1" :read true}
|
{c/activity-center-notification-type-one-to-one-chat
|
||||||
{:id "0x2" :read true}]])
|
{:read {:cursor ""
|
||||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications-unread :data]
|
:data [{:id "0x1"
|
||||||
[{:id "0x3" :read false}
|
:read true
|
||||||
{:id "0x4" :read false}]])
|
:type c/activity-center-notification-type-one-to-one-chat}]}}
|
||||||
|
c/activity-center-notification-type-private-group-chat
|
||||||
|
{:unread {:cursor ""
|
||||||
|
:data [{:id "0x4"
|
||||||
|
:read false
|
||||||
|
:type c/activity-center-notification-type-private-group-chat}
|
||||||
|
{:id "0x6"
|
||||||
|
:read false
|
||||||
|
:type c/activity-center-notification-type-private-group-chat}]}}}])
|
||||||
|
|
||||||
(rf/dispatch [:activity-center.notifications/reconcile
|
(rf/dispatch [:activity-center.notifications/reconcile
|
||||||
[{:id "0x1" :read true :name "ABC"}
|
[{:id "0x1"
|
||||||
{:id "0x3" :read false :name "XYZ"}]])
|
:read true
|
||||||
|
:type c/activity-center-notification-type-one-to-one-chat
|
||||||
|
:last-message {}}
|
||||||
|
{:id "0x4"
|
||||||
|
:read false
|
||||||
|
:type c/activity-center-notification-type-private-group-chat
|
||||||
|
:author "0xabc"}
|
||||||
|
{:id "0x6"
|
||||||
|
:read false
|
||||||
|
:type c/activity-center-notification-type-private-group-chat}]])
|
||||||
|
|
||||||
(is (= [{:id "0x1" :read true :name "ABC"}
|
(is (= {c/activity-center-notification-type-one-to-one-chat
|
||||||
{:id "0x2" :read true}]
|
{:read {:cursor ""
|
||||||
(get-in (h/db) [:activity-center :notifications-read :data])))
|
:data [{:id "0x1"
|
||||||
(is (= [{:id "0x3" :read false :name "XYZ"}
|
:read true
|
||||||
{:id "0x4" :read false}]
|
:type c/activity-center-notification-type-one-to-one-chat
|
||||||
(get-in (h/db) [:activity-center :notifications-unread :data]))))))
|
:last-message {}}]}
|
||||||
|
:unread {:data []}}
|
||||||
|
c/activity-center-notification-type-private-group-chat
|
||||||
|
{:read {:data []}
|
||||||
|
:unread {:cursor ""
|
||||||
|
:data [{:id "0x6"
|
||||||
|
:read false
|
||||||
|
:type c/activity-center-notification-type-private-group-chat}
|
||||||
|
{:id "0x4"
|
||||||
|
:read false
|
||||||
|
:type c/activity-center-notification-type-private-group-chat
|
||||||
|
:author "0xabc"}]}}}
|
||||||
|
(get-in (h/db) [:activity-center :notifications])))))
|
||||||
|
|
||||||
|
(testing "reconciles notifications that switched their read/unread status"
|
||||||
|
(rf-test/run-test-sync
|
||||||
|
(setup)
|
||||||
|
(rf/dispatch [:test/assoc-in [:activity-center :notifications]
|
||||||
|
{c/activity-center-notification-type-one-to-one-chat
|
||||||
|
{:read {:cursor ""
|
||||||
|
:data [{:id "0x1"
|
||||||
|
:read true
|
||||||
|
:type c/activity-center-notification-type-one-to-one-chat}]}}}])
|
||||||
|
|
||||||
|
(rf/dispatch [:activity-center.notifications/reconcile
|
||||||
|
[{:id "0x1"
|
||||||
|
:read false
|
||||||
|
:type c/activity-center-notification-type-one-to-one-chat}]])
|
||||||
|
|
||||||
|
(is (= {c/activity-center-notification-type-one-to-one-chat
|
||||||
|
{:read {:cursor ""
|
||||||
|
:data []}
|
||||||
|
:unread {:data [{:id "0x1"
|
||||||
|
:read false
|
||||||
|
:type c/activity-center-notification-type-one-to-one-chat}]}}}
|
||||||
|
(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)
|
||||||
|
(rf/dispatch [:test/assoc-in [:activity-center :notifications]
|
||||||
|
{c/activity-center-notification-type-one-to-one-chat
|
||||||
|
{:read {:cursor ""
|
||||||
|
:data [{:id "0x1" :read true :type c/activity-center-notification-type-one-to-one-chat :timestamp 1}
|
||||||
|
{:id "0x2" :read true :type c/activity-center-notification-type-one-to-one-chat :timestamp 1}]}
|
||||||
|
:unread {:cursor ""
|
||||||
|
:data [{:id "0x3" :read false :type c/activity-center-notification-type-one-to-one-chat :timestamp 50}
|
||||||
|
{:id "0x4" :read false :type c/activity-center-notification-type-one-to-one-chat :timestamp 100}
|
||||||
|
{:id "0x5" :read false :type c/activity-center-notification-type-one-to-one-chat :timestamp 100}]}}}])
|
||||||
|
|
||||||
|
(rf/dispatch [:activity-center.notifications/reconcile
|
||||||
|
[{:id "0x1" :read true :type c/activity-center-notification-type-one-to-one-chat :timestamp 1 :last-message {}}
|
||||||
|
{:id "0x4" :read false :type c/activity-center-notification-type-one-to-one-chat :timestamp 100 :last-message {}}]])
|
||||||
|
|
||||||
|
(is (= {c/activity-center-notification-type-one-to-one-chat
|
||||||
|
{:read {:cursor ""
|
||||||
|
:data [{:id "0x2"
|
||||||
|
:read true
|
||||||
|
:type c/activity-center-notification-type-one-to-one-chat
|
||||||
|
:timestamp 1}
|
||||||
|
{:id "0x1"
|
||||||
|
:read true
|
||||||
|
:type c/activity-center-notification-type-one-to-one-chat
|
||||||
|
:timestamp 1
|
||||||
|
:last-message {}}]}
|
||||||
|
:unread {:cursor ""
|
||||||
|
:data [{:id "0x5"
|
||||||
|
:read false
|
||||||
|
:type c/activity-center-notification-type-one-to-one-chat
|
||||||
|
:timestamp 100}
|
||||||
|
{:id "0x4"
|
||||||
|
:read false
|
||||||
|
:type c/activity-center-notification-type-one-to-one-chat
|
||||||
|
:timestamp 100
|
||||||
|
:last-message {}}
|
||||||
|
{:id "0x3"
|
||||||
|
:read false
|
||||||
|
:type c/activity-center-notification-type-one-to-one-chat
|
||||||
|
:timestamp 50}]}}}
|
||||||
|
(get-in (h/db) [:activity-center :notifications]))))))
|
||||||
|
|
||||||
(deftest notifications-fetch-test
|
(deftest notifications-fetch-test
|
||||||
(testing "fetches first page"
|
(testing "fetches first page"
|
||||||
(rf-test/run-test-sync
|
(rf-test/run-test-sync
|
||||||
(setup)
|
(setup)
|
||||||
(let [spy-queue (atom [])]
|
(let [spy-queue (atom [])]
|
||||||
(h/stub-fx-with-callbacks ::json-rpc/call
|
(h/stub-fx-with-callbacks
|
||||||
:on-success (constantly {:cursor "10"
|
::json-rpc/call
|
||||||
:notifications [{:chatId "0x1"}]}))
|
:on-success (constantly {:cursor "10"
|
||||||
|
:notifications [{:id "0x1"
|
||||||
|
:type c/activity-center-notification-type-one-to-one-chat
|
||||||
|
:read false
|
||||||
|
:chatId "0x9"}]}))
|
||||||
(h/spy-fx spy-queue ::json-rpc/call)
|
(h/spy-fx spy-queue ::json-rpc/call)
|
||||||
|
|
||||||
(rf/dispatch [:activity-center.notifications/fetch-first-page])
|
(rf/dispatch [:activity-center.notifications/fetch-first-page
|
||||||
|
{:filter-type c/activity-center-notification-type-one-to-one-chat}])
|
||||||
|
|
||||||
(is (= :unread (get-in (h/db) [:activity-center :current-status-filter])))
|
(is (= :unread (get-in (h/db) [:activity-center :filter :status])))
|
||||||
(is (nil? (get-in (h/db) [:activity-center :notifications-unread :loading?])))
|
(is (= "" (get-in @spy-queue [0 :args 0 :params 0]))
|
||||||
(is (= "10" (get-in (h/db) [:activity-center :notifications-unread :cursor])))
|
"Should be called with empty cursor when fetching first page")
|
||||||
(is (= "" (get-in @spy-queue [0 :args 0 :params 0])))
|
(is (= {c/activity-center-notification-type-one-to-one-chat
|
||||||
(is (= [{:chat-id "0x1"}]
|
{:unread {:cursor "10"
|
||||||
(->> (get-in (h/db) [:activity-center :notifications-unread :data])
|
:data [{:chat-id "0x9"
|
||||||
(map #(select-keys % [:chat-id]))))))))
|
:chat-name nil
|
||||||
|
:chat-type c/activity-center-notification-type-one-to-one-chat
|
||||||
|
:group-chat false
|
||||||
|
:id "0x1"
|
||||||
|
:public? false
|
||||||
|
:last-message nil
|
||||||
|
:message nil
|
||||||
|
:read false
|
||||||
|
:reply-message nil
|
||||||
|
:type c/activity-center-notification-type-one-to-one-chat}]}}}
|
||||||
|
(remove-color-key (get-in (h/db) [:activity-center :notifications])
|
||||||
|
{:status :unread
|
||||||
|
:type c/activity-center-notification-type-one-to-one-chat}))))))
|
||||||
|
|
||||||
(testing "does not fetch next page when pagination cursor reached the end"
|
(testing "does not fetch next page when pagination cursor reached the end"
|
||||||
(rf-test/run-test-sync
|
(rf-test/run-test-sync
|
||||||
(setup)
|
(setup)
|
||||||
(let [spy-queue (atom [])]
|
(let [spy-queue (atom [])]
|
||||||
(h/spy-fx spy-queue ::json-rpc/call)
|
(h/spy-fx spy-queue ::json-rpc/call)
|
||||||
(rf/dispatch [:test/assoc-in [:activity-center :current-status-filter] :unread])
|
(rf/dispatch [:test/assoc-in [:activity-center :filter :status]
|
||||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications-unread :cursor] ""])
|
:unread])
|
||||||
|
(rf/dispatch [:test/assoc-in [:activity-center :filter :type]
|
||||||
|
c/activity-center-notification-type-one-to-one-chat])
|
||||||
|
(rf/dispatch [:test/assoc-in [:activity-center :notifications c/activity-center-notification-type-one-to-one-chat :unread :cursor]
|
||||||
|
""])
|
||||||
|
|
||||||
|
(rf/dispatch [:activity-center.notifications/fetch-next-page])
|
||||||
|
|
||||||
|
(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]
|
||||||
|
c/activity-center-notification-type-one-to-one-chat])
|
||||||
|
(rf/dispatch [:test/assoc-in [:activity-center :notifications c/activity-center-notification-type-one-to-one-chat :unread :cursor]
|
||||||
|
nil])
|
||||||
|
|
||||||
(rf/dispatch [:activity-center.notifications/fetch-next-page])
|
(rf/dispatch [:activity-center.notifications/fetch-next-page])
|
||||||
|
|
||||||
@ -104,30 +290,54 @@
|
|||||||
(rf-test/run-test-sync
|
(rf-test/run-test-sync
|
||||||
(setup)
|
(setup)
|
||||||
(let [spy-queue (atom [])]
|
(let [spy-queue (atom [])]
|
||||||
(h/stub-fx-with-callbacks ::json-rpc/call
|
(h/stub-fx-with-callbacks
|
||||||
:on-success (constantly {:cursor ""
|
::json-rpc/call
|
||||||
:notifications [{:chatId "0x1"}]}))
|
:on-success (constantly {:cursor ""
|
||||||
|
:notifications [{:id "0x1"
|
||||||
|
:type c/activity-center-notification-type-mention
|
||||||
|
:read false
|
||||||
|
:chatId "0x9"}]}))
|
||||||
(h/spy-fx spy-queue ::json-rpc/call)
|
(h/spy-fx spy-queue ::json-rpc/call)
|
||||||
(rf/dispatch [:test/assoc-in [:activity-center :current-status-filter] :unread])
|
(rf/dispatch [:test/assoc-in [:activity-center :filter :status]
|
||||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications-unread :cursor] "10"])
|
:unread])
|
||||||
|
(rf/dispatch [:test/assoc-in [:activity-center :filter :type]
|
||||||
|
c/activity-center-notification-type-mention])
|
||||||
|
(rf/dispatch [:test/assoc-in [:activity-center :notifications c/activity-center-notification-type-mention :unread :cursor]
|
||||||
|
"10"])
|
||||||
|
|
||||||
(rf/dispatch [:activity-center.notifications/fetch-next-page])
|
(rf/dispatch [:activity-center.notifications/fetch-next-page])
|
||||||
|
|
||||||
(is (= "wakuext_unreadActivityCenterNotifications" (get-in @spy-queue [0 :args 0 :method])))
|
(is (= "wakuext_unreadActivityCenterNotifications" (get-in @spy-queue [0 :args 0 :method])))
|
||||||
(is (= "10" (get-in @spy-queue [0 :args 0 :params 0])))
|
(is (= "10" (get-in @spy-queue [0 :args 0 :params 0]))
|
||||||
(is (= "" (get-in (h/db) [:activity-center :notifications-unread :cursor])))
|
"Should be called with current cursor")
|
||||||
(is (= [{:chat-id "0x1"}]
|
(is (= {c/activity-center-notification-type-mention
|
||||||
(->> (get-in (h/db) [:activity-center :notifications-unread :data])
|
{:unread {:cursor ""
|
||||||
(map #(select-keys % [:chat-id]))))))))
|
:data [{:chat-id "0x9"
|
||||||
|
:chat-name nil
|
||||||
|
:chat-type 3
|
||||||
|
:id "0x1"
|
||||||
|
:last-message nil
|
||||||
|
:message nil
|
||||||
|
:read false
|
||||||
|
:reply-message nil
|
||||||
|
:type c/activity-center-notification-type-mention}]}}}
|
||||||
|
(remove-color-key (get-in (h/db) [:activity-center :notifications])
|
||||||
|
{:status :unread
|
||||||
|
:type c/activity-center-notification-type-mention}))))))
|
||||||
|
|
||||||
(testing "does not fetch next page while it is still loading"
|
(testing "does not fetch next page while it is still loading"
|
||||||
(rf-test/run-test-sync
|
(rf-test/run-test-sync
|
||||||
(setup)
|
(setup)
|
||||||
(let [spy-queue (atom [])]
|
(let [spy-queue (atom [])]
|
||||||
(h/spy-fx spy-queue ::json-rpc/call)
|
(h/spy-fx spy-queue ::json-rpc/call)
|
||||||
(rf/dispatch [:test/assoc-in [:activity-center :current-status-filter] :read])
|
(rf/dispatch [:test/assoc-in [:activity-center :filter :status]
|
||||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications-read :cursor] "10"])
|
:read])
|
||||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications-read :loading?] true])
|
(rf/dispatch [:test/assoc-in [:activity-center :filter :type]
|
||||||
|
c/activity-center-notification-type-one-to-one-chat])
|
||||||
|
(rf/dispatch [:test/assoc-in [:activity-center :notifications c/activity-center-notification-type-one-to-one-chat :read :cursor]
|
||||||
|
"10"])
|
||||||
|
(rf/dispatch [:test/assoc-in [:activity-center :notifications c/activity-center-notification-type-one-to-one-chat :read :loading?]
|
||||||
|
true])
|
||||||
|
|
||||||
(rf/dispatch [:activity-center.notifications/fetch-next-page])
|
(rf/dispatch [:activity-center.notifications/fetch-next-page])
|
||||||
|
|
||||||
@ -140,13 +350,18 @@
|
|||||||
(h/stub-fx-with-callbacks ::json-rpc/call :on-error (constantly :fake-error))
|
(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-event-fx spy-queue :activity-center.notifications/fetch-error)
|
||||||
(h/spy-fx spy-queue ::json-rpc/call)
|
(h/spy-fx spy-queue ::json-rpc/call)
|
||||||
(rf/dispatch [:test/assoc-in [:activity-center :current-status-filter] :unread])
|
(rf/dispatch [:test/assoc-in [:activity-center :filter :status]
|
||||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications-unread :cursor] ""])
|
:unread])
|
||||||
|
(rf/dispatch [:test/assoc-in [:activity-center :filter :type]
|
||||||
|
c/activity-center-notification-type-one-to-one-chat])
|
||||||
|
(rf/dispatch [:test/assoc-in [:activity-center :notifications c/activity-center-notification-type-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-unread :loading?])))
|
(is (nil? (get-in (h/db) [:activity-center :notifications c/activity-center-notification-type-one-to-one-chat :unread :loading?])))
|
||||||
(is (= [:activity-center.notifications/fetch-error
|
(is (= [:activity-center.notifications/fetch-error
|
||||||
:notifications-unread
|
c/activity-center-notification-type-one-to-one-chat
|
||||||
|
:unread
|
||||||
:fake-error]
|
:fake-error]
|
||||||
(:args (last @spy-queue))))))))
|
(:args (last @spy-queue))))))))
|
||||||
|
@ -176,6 +176,7 @@
|
|||||||
(def ^:const docs-link "https://status.im/docs/")
|
(def ^:const docs-link "https://status.im/docs/")
|
||||||
(def ^:const principles-link "https://our.status.im/our-principles/")
|
(def ^:const principles-link "https://our.status.im/our-principles/")
|
||||||
|
|
||||||
|
(def ^:const activity-center-notification-type-no-type 0)
|
||||||
(def ^:const activity-center-notification-type-one-to-one-chat 1)
|
(def ^:const activity-center-notification-type-one-to-one-chat 1)
|
||||||
(def ^:const activity-center-notification-type-private-group-chat 2)
|
(def ^:const activity-center-notification-type-private-group-chat 2)
|
||||||
(def ^:const activity-center-notification-type-mention 3)
|
(def ^:const activity-center-notification-type-mention 3)
|
||||||
@ -183,6 +184,13 @@
|
|||||||
(def ^:const activity-center-notification-type-contact-request 5)
|
(def ^:const activity-center-notification-type-contact-request 5)
|
||||||
(def ^:const activity-center-notification-type-contact-request-retracted 6)
|
(def ^:const activity-center-notification-type-contact-request-retracted 6)
|
||||||
|
|
||||||
|
;; TODO: Replace with correct enum values once status-go implements them.
|
||||||
|
(def ^:const activity-center-notification-type-admin 66610)
|
||||||
|
(def ^:const activity-center-notification-type-identity-verification 66611)
|
||||||
|
(def ^:const activity-center-notification-type-tx 66612)
|
||||||
|
(def ^:const activity-center-notification-type-membership 66613)
|
||||||
|
(def ^:const activity-center-notification-type-system 66614)
|
||||||
|
|
||||||
(def ^:const visibility-status-unknown 0)
|
(def ^:const visibility-status-unknown 0)
|
||||||
(def ^:const visibility-status-automatic 1)
|
(def ^:const visibility-status-automatic 1)
|
||||||
(def ^:const visibility-status-dnd 2)
|
(def ^:const visibility-status-dnd 2)
|
||||||
|
@ -6,35 +6,36 @@
|
|||||||
[clojure.string :as string]))
|
[clojure.string :as string]))
|
||||||
|
|
||||||
(re-frame/reg-sub
|
(re-frame/reg-sub
|
||||||
:activity-center/notifications-read
|
:activity-center/notifications
|
||||||
(fn [db]
|
:<- [:activity-center]
|
||||||
(get-in db [:activity-center :notifications-read :data])))
|
(fn [activity-center]
|
||||||
|
(:notifications activity-center)))
|
||||||
|
|
||||||
(re-frame/reg-sub
|
(re-frame/reg-sub
|
||||||
:activity-center/notifications-unread
|
:activity-center/filter-status
|
||||||
(fn [db]
|
:<- [:activity-center]
|
||||||
(get-in db [:activity-center :notifications-unread :data])))
|
(fn [activity-center]
|
||||||
|
(get-in activity-center [:filter :status])))
|
||||||
|
|
||||||
(re-frame/reg-sub
|
(re-frame/reg-sub
|
||||||
:activity-center/current-status-filter
|
:activity-center/filter-type
|
||||||
(fn [db]
|
:<- [:activity-center]
|
||||||
(get-in db [:activity-center :current-status-filter])))
|
(fn [activity-center]
|
||||||
|
(get-in activity-center [:filter :type] constants/activity-center-notification-type-no-type)))
|
||||||
|
|
||||||
(re-frame/reg-sub
|
(re-frame/reg-sub
|
||||||
:activity-center/status-filter-unread-enabled?
|
:activity-center/filtered-notifications
|
||||||
:<- [:activity-center/current-status-filter]
|
:<- [:activity-center/filter-type]
|
||||||
(fn [current-status-filter]
|
:<- [:activity-center/filter-status]
|
||||||
(= :unread current-status-filter)))
|
:<- [:activity-center/notifications]
|
||||||
|
(fn [[filter-type filter-status notifications]]
|
||||||
|
(get-in notifications [filter-type filter-status :data])))
|
||||||
|
|
||||||
(re-frame/reg-sub
|
(re-frame/reg-sub
|
||||||
:activity-center/notifications-per-read-status
|
:activity-center/filter-status-unread-enabled?
|
||||||
:<- [:activity-center/notifications-read]
|
:<- [:activity-center/filter-status]
|
||||||
:<- [:activity-center/notifications-unread]
|
(fn [filter-status]
|
||||||
:<- [:activity-center/status-filter-unread-enabled?]
|
(= :unread filter-status)))
|
||||||
(fn [[notifications-read notifications-unread unread-filter-enabled?]]
|
|
||||||
(if unread-filter-enabled?
|
|
||||||
notifications-unread
|
|
||||||
notifications-read)))
|
|
||||||
|
|
||||||
(defn- group-notifications-by-date
|
(defn- group-notifications-by-date
|
||||||
[notifications]
|
[notifications]
|
||||||
|
@ -228,6 +228,7 @@
|
|||||||
|
|
||||||
(reg-root-key-sub :activity.center/notifications :activity.center/notifications)
|
(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/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)
|
(reg-root-key-sub :bug-report/description-error :bug-report/description-error)
|
||||||
(reg-root-key-sub :bug-report/details :bug-report/details)
|
(reg-root-key-sub :bug-report/details :bug-report/details)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
(ns status-im.ui.screens.activity-center.views
|
(ns status-im.ui.screens.activity-center.views
|
||||||
(:require [quo.components.animated.pressable :as animation]
|
(:require [quo.components.animated.pressable :as animation]
|
||||||
|
[quo.design-system.colors :as quo.colors]
|
||||||
[quo.react-native :as rn]
|
[quo.react-native :as rn]
|
||||||
[quo2.components.buttons.button :as button]
|
[quo2.components.buttons.button :as button]
|
||||||
[quo2.components.markdown.text :as text]
|
[quo2.components.markdown.text :as text]
|
||||||
@ -14,9 +15,6 @@
|
|||||||
[status-im.utils.datetime :as datetime]
|
[status-im.utils.datetime :as datetime]
|
||||||
[status-im.utils.handlers :refer [<sub >evt]]))
|
[status-im.utils.handlers :refer [<sub >evt]]))
|
||||||
|
|
||||||
(defonce selected-activity-type
|
|
||||||
(reagent/atom :activity-type/all))
|
|
||||||
|
|
||||||
(defn activity-title
|
(defn activity-title
|
||||||
[{:keys [type]}]
|
[{:keys [type]}]
|
||||||
(case type
|
(case type
|
||||||
@ -32,8 +30,8 @@
|
|||||||
[{:keys [type]}]
|
[{:keys [type]}]
|
||||||
(case type
|
(case type
|
||||||
constants/activity-center-notification-type-contact-request
|
constants/activity-center-notification-type-contact-request
|
||||||
:add-user
|
:main-icons2/add-user
|
||||||
:placeholder))
|
:main-icons2/placeholder))
|
||||||
|
|
||||||
(defn activity-context
|
(defn activity-context
|
||||||
[{:keys [message last-message type]}]
|
[{:keys [message last-message type]}]
|
||||||
@ -81,7 +79,7 @@
|
|||||||
nil))
|
nil))
|
||||||
|
|
||||||
(defn activity-pressable
|
(defn activity-pressable
|
||||||
[notification & children]
|
[notification activity]
|
||||||
(case (get-in notification [:message :contact-request-state])
|
(case (get-in notification [:message :contact-request-state])
|
||||||
constants/contact-request-message-state-accepted
|
constants/contact-request-message-state-accepted
|
||||||
;; NOTE [2022-09-21]: We need to dispatch to
|
;; NOTE [2022-09-21]: We need to dispatch to
|
||||||
@ -89,14 +87,13 @@
|
|||||||
;; `:chat.ui/navigate-to-chat`, otherwise the chat screen looks completely
|
;; `:chat.ui/navigate-to-chat`, otherwise the chat screen looks completely
|
||||||
;; broken if it has never been opened before for the accepted contact.
|
;; broken if it has never been opened before for the accepted contact.
|
||||||
[animation/pressable {:on-press #(>evt [:contact.ui/send-message-pressed {:public-key (:author notification)}])}
|
[animation/pressable {:on-press #(>evt [:contact.ui/send-message-pressed {:public-key (:author notification)}])}
|
||||||
children]
|
activity]
|
||||||
[:<> children]))
|
activity))
|
||||||
|
|
||||||
(defn render-notification
|
(defn render-notification
|
||||||
[notification index]
|
[notification index]
|
||||||
[rn/view {:flex 1
|
[rn/view {:margin-top (if (= 0 index) 0 4)
|
||||||
:flex-direction :column
|
:padding-horizontal 20}
|
||||||
:margin-top (if (= 0 index) 0 4)}
|
|
||||||
[activity-pressable notification
|
[activity-pressable notification
|
||||||
[activity-logs/activity-log
|
[activity-logs/activity-log
|
||||||
(merge {:context (activity-context notification)
|
(merge {:context (activity-context notification)
|
||||||
@ -108,67 +105,108 @@
|
|||||||
:unread? (not (:read notification))}
|
:unread? (not (:read notification))}
|
||||||
(activity-buttons notification))]]])
|
(activity-buttons notification))]]])
|
||||||
|
|
||||||
(defn notifications-list
|
(defn filter-selector-read-toggle
|
||||||
[]
|
[]
|
||||||
(let [notifications (<sub [:activity-center/notifications-per-read-status])]
|
(let [unread-filter-enabled? (<sub [:activity-center/filter-status-unread-enabled?])]
|
||||||
[rn/flat-list {:data notifications
|
|
||||||
:key-fn :id
|
|
||||||
:on-end-reached #(>evt [:activity-center.notifications/fetch-next-page])
|
|
||||||
:render-fn render-notification}]))
|
|
||||||
|
|
||||||
(defn filter-selector-read []
|
|
||||||
(let [unread-filter-enabled? (<sub [:activity-center/status-filter-unread-enabled?])]
|
|
||||||
;; TODO: Replace the button by a Filter Selector component once available for use.
|
;; TODO: Replace the button by a Filter Selector component once available for use.
|
||||||
[button/button {:icon true
|
[button/button {:icon true
|
||||||
:type (if unread-filter-enabled? :primary :outline)
|
:type (if unread-filter-enabled? :primary :outline)
|
||||||
:size 32
|
:size 32
|
||||||
:on-press #(if unread-filter-enabled?
|
:on-press #(>evt [:activity-center.notifications/fetch-first-page
|
||||||
(>evt [:activity-center.notifications/fetch-first-page {:status-filter :read}])
|
{:filter-status (if unread-filter-enabled?
|
||||||
(>evt [:activity-center.notifications/fetch-first-page {:status-filter :unread}]))}
|
:read
|
||||||
:unread]))
|
:unread)}])}
|
||||||
|
:main-icons2/unread]))
|
||||||
|
|
||||||
(defn activity-center []
|
;; TODO(2022-10-07): The empty state is still under design analysis, so we
|
||||||
|
;; shouldn't even care about translations at this point. A placeholder box is
|
||||||
|
;; used instead of an image.
|
||||||
|
(defn empty-tab
|
||||||
|
[]
|
||||||
|
[rn/view {:style {:align-items :center
|
||||||
|
:flex 1
|
||||||
|
:justify-content :center
|
||||||
|
:padding-vertical 12}}
|
||||||
|
[rn/view {:style {:background-color colors/neutral-80
|
||||||
|
:height 120
|
||||||
|
:margin-bottom 20
|
||||||
|
:width 120}}]
|
||||||
|
[text/text {:size :paragraph-1
|
||||||
|
:style {:padding-bottom 2}
|
||||||
|
:weight :semi-bold}
|
||||||
|
"No notifications"]
|
||||||
|
[text/text {:size :paragraph-2}
|
||||||
|
"Your notifications will be here"]])
|
||||||
|
|
||||||
|
(defn tabs
|
||||||
|
[]
|
||||||
|
(let [filter-type (<sub [:activity-center/filter-type])]
|
||||||
|
[tabs/scrollable-tabs {:size 32
|
||||||
|
:style {:padding-left 20}
|
||||||
|
:fade-end-percentage 0.79
|
||||||
|
:scroll-on-press? true
|
||||||
|
:fade-end? true
|
||||||
|
:on-change #(>evt [:activity-center.notifications/fetch-first-page {:filter-type %}])
|
||||||
|
:default-active filter-type
|
||||||
|
:data [{:id constants/activity-center-notification-type-no-type
|
||||||
|
:label (i18n/label :t/all)}
|
||||||
|
{:id constants/activity-center-notification-type-admin
|
||||||
|
:label (i18n/label :t/admin)}
|
||||||
|
{:id constants/activity-center-notification-type-mention
|
||||||
|
:label (i18n/label :t/mentions)}
|
||||||
|
{:id constants/activity-center-notification-type-reply
|
||||||
|
:label (i18n/label :t/replies)}
|
||||||
|
{:id constants/activity-center-notification-type-contact-request
|
||||||
|
:label (i18n/label :t/contact-requests)}
|
||||||
|
{:id constants/activity-center-notification-type-identity-verification
|
||||||
|
:label (i18n/label :t/identity-verification)}
|
||||||
|
{:id constants/activity-center-notification-type-tx
|
||||||
|
:label (i18n/label :t/transactions)}
|
||||||
|
{:id constants/activity-center-notification-type-membership
|
||||||
|
:label (i18n/label :t/membership)}
|
||||||
|
{:id constants/activity-center-notification-type-system
|
||||||
|
:label (i18n/label :t/system)}]}]))
|
||||||
|
|
||||||
|
(defn header
|
||||||
|
[]
|
||||||
|
(let [screen-padding 20]
|
||||||
|
;; TODO: Remove temporary (and old) background color when the screen and
|
||||||
|
;; header are properly blurred.
|
||||||
|
[rn/view {:background-color (:ui-background @quo.colors/theme)}
|
||||||
|
[button/button {:icon true
|
||||||
|
:type :grey
|
||||||
|
:size 32
|
||||||
|
:style {:margin-vertical 12
|
||||||
|
:margin-left screen-padding}
|
||||||
|
:on-press #(>evt [:navigate-back])}
|
||||||
|
:main-icons2/close]
|
||||||
|
[text/text {:size :heading-1
|
||||||
|
:weight :semi-bold
|
||||||
|
:style {:padding-horizontal screen-padding
|
||||||
|
:padding-vertical 12}}
|
||||||
|
(i18n/label :t/notifications)]
|
||||||
|
[rn/view {:flex-direction :row
|
||||||
|
:padding-vertical 12}
|
||||||
|
[rn/view {:flex 1
|
||||||
|
:align-self :stretch}
|
||||||
|
[tabs]]
|
||||||
|
[rn/view {:flex-grow 0
|
||||||
|
:margin-left 16
|
||||||
|
:padding-right screen-padding}
|
||||||
|
[filter-selector-read-toggle]]]]))
|
||||||
|
|
||||||
|
(defn activity-center
|
||||||
|
[]
|
||||||
(reagent/create-class
|
(reagent/create-class
|
||||||
{:component-did-mount #(>evt [:activity-center.notifications/fetch-first-page {:status-filter :unread}])
|
{:component-did-mount #(>evt [:activity-center.notifications/fetch-first-page])
|
||||||
:reagent-render
|
:reagent-render
|
||||||
(fn []
|
(fn []
|
||||||
(let [screen-padding 20]
|
(let [notifications (<sub [:activity-center/filtered-notifications])]
|
||||||
[:<>
|
[rn/flat-list {:content-container-style {:flex-grow 1}
|
||||||
[button/button {:icon true
|
:data notifications
|
||||||
:type :grey
|
:empty-component [empty-tab]
|
||||||
:size 32
|
:header [header]
|
||||||
:style {:margin-vertical 12
|
:key-fn :id
|
||||||
:margin-left screen-padding}
|
:on-end-reached #(>evt [:activity-center.notifications/fetch-next-page])
|
||||||
:on-press #(>evt [:navigate-back])}
|
:render-fn render-notification
|
||||||
:close]
|
:sticky-header-indices [0]}]))}))
|
||||||
[text/text {:size :heading-1
|
|
||||||
:weight :semi-bold
|
|
||||||
:style {:padding-horizontal screen-padding
|
|
||||||
:padding-vertical 12}}
|
|
||||||
(i18n/label :t/notifications)]
|
|
||||||
[rn/view {:flex-direction :row
|
|
||||||
:padding-vertical 12}
|
|
||||||
[rn/view {:flex 1
|
|
||||||
:align-self :stretch}
|
|
||||||
[tabs/scrollable-tabs {:size 32
|
|
||||||
:style {:padding-left screen-padding}
|
|
||||||
:fade-end-percentage 0.79
|
|
||||||
:scroll-on-press? true
|
|
||||||
:fade-end? true
|
|
||||||
:on-change (partial reset! selected-activity-type)
|
|
||||||
:default-active :activity-type/all
|
|
||||||
:data [{:id :activity-type/all :label (i18n/label :t/all)}
|
|
||||||
{:id :activity-type/admin :label (i18n/label :t/admin)}
|
|
||||||
{:id :activity-type/mention :label (i18n/label :t/mentions)}
|
|
||||||
{:id :activity-type/reply :label (i18n/label :t/replies)}
|
|
||||||
{:id :activity-type/contact-request :label (i18n/label :t/contact-requests)}
|
|
||||||
{:id :activity-type/identity-verification :label (i18n/label :t/identity-verification)}
|
|
||||||
{:id :activity-type/transaction :label (i18n/label :t/transactions)}
|
|
||||||
{:id :activity-type/membership :label (i18n/label :t/membership)}
|
|
||||||
{:id :activity-type/system :label (i18n/label :t/system)}]}]]
|
|
||||||
[rn/view {:flex-grow 0
|
|
||||||
:margin-left 16
|
|
||||||
:padding-right screen-padding}
|
|
||||||
[filter-selector-read]]]
|
|
||||||
[rn/view {:padding-horizontal screen-padding}
|
|
||||||
[notifications-list]]]))}))
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user