Show unread indicator on Activity Center tabs (#14883)
Fixes https://github.com/status-im/status-mobile/issues/14852 Fixes https://github.com/status-im/status-mobile/issues/14882 Summary ======= - [x] Fixes https://github.com/status-im/status-mobile/issues/14852 - [x] Fixes https://github.com/status-im/status-mobile/issues/14882 - [x] Refactors `tab` component to follow new guidelines. - [x] Improves preview area for tabs to optionally show notification dots. - [x] Refactors `tabs` component: break the component into smaller chunks; remove duplication between scrollable & non-scrollable tabs; update outdated docstring; - [x] Adds accessibility labels to tabs. Cutout with light theme on the background: https://user-images.githubusercontent.com/46027/214553974-2b2e8c6e-71b8-46f3-ba75-a66cb4755490.png Cutout with dark theme on the background: https://user-images.githubusercontent.com/46027/214554602-d637d4d6-3b20-4aa8-a2ef-64836f4454bb.png Cutout in non-scrollable tabs: https://user-images.githubusercontent.com/46027/214555646-bbb85546-20d9-48bb-8273-716564fb5e0f.png Notification dots in preview area: https://user-images.githubusercontent.com/46027/214556532-10a044ae-9df5-4c86-be5f-447955276bfd.png
This commit is contained in:
parent
9ded9da7e3
commit
8626cd3e6d
|
@ -137,10 +137,13 @@ globalThis.__STATUS_MOBILE_JS_IDENTITY_PROXY__ = new Proxy({}, {get() { return (
|
||||||
(def react-native-share #js {:default {}})
|
(def react-native-share #js {:default {}})
|
||||||
(def react-native-svg
|
(def react-native-svg
|
||||||
#js
|
#js
|
||||||
{:SvgUri #js {:render identity}
|
{:ClipPath #js {:render identity}
|
||||||
|
:Defs #js {:render identity}
|
||||||
|
:Path #js {:render identity}
|
||||||
|
:Rect #js {:render identity}
|
||||||
|
:SvgUri #js {:render identity}
|
||||||
:SvgXml #js {:render identity}
|
:SvgXml #js {:render identity}
|
||||||
:default #js {:render identity}
|
:default #js {:render identity}})
|
||||||
:Path #js {:render identity}})
|
|
||||||
(def react-native-webview #js {:default {}})
|
(def react-native-webview #js {:default {}})
|
||||||
(def react-native-audio-toolkit #js {:MediaStates {}})
|
(def react-native-audio-toolkit #js {:MediaStates {}})
|
||||||
(def net-info #js {})
|
(def net-info #js {})
|
||||||
|
|
|
@ -2,13 +2,17 @@
|
||||||
(:require [quo2.foundations.colors :as colors]
|
(:require [quo2.foundations.colors :as colors]
|
||||||
[react-native.core :as rn]))
|
[react-native.core :as rn]))
|
||||||
|
|
||||||
|
(def ^:const size 8)
|
||||||
|
|
||||||
(defn notification-dot
|
(defn notification-dot
|
||||||
[style]
|
[{:keys [style]}]
|
||||||
[rn/view
|
[rn/view
|
||||||
{:style (merge {:background-color (colors/theme-colors colors/primary-50 colors/primary-60)
|
{:accessibility-label :notification-dot
|
||||||
:width 8
|
:style (merge
|
||||||
:height 8
|
{:background-color (colors/theme-colors colors/primary-50 colors/primary-60)
|
||||||
:border-radius 4
|
:width size
|
||||||
|
:height size
|
||||||
|
:border-radius (/ size 2)
|
||||||
:position :absolute
|
:position :absolute
|
||||||
:z-index 1}
|
:z-index 1}
|
||||||
style)}])
|
style)}])
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
(ns quo2.components.tabs.segmented-tab
|
(ns quo2.components.tabs.segmented-tab
|
||||||
(:require [quo2.components.tabs.tab :as tab]
|
(:require [quo2.components.tabs.tab.view :as tab]
|
||||||
[quo2.foundations.colors :as colors]
|
[quo2.foundations.colors :as colors]
|
||||||
[quo2.theme :as theme]
|
[quo2.theme :as theme]
|
||||||
[react-native.core :as rn]
|
[react-native.core :as rn]
|
||||||
|
@ -28,7 +28,7 @@
|
||||||
[rn/view
|
[rn/view
|
||||||
{:margin-left (if (= 0 indx) 0 2)
|
{:margin-left (if (= 0 indx) 0 2)
|
||||||
:flex 1}
|
:flex 1}
|
||||||
[tab/tab
|
[tab/view
|
||||||
{:id id
|
{:id id
|
||||||
:segmented true
|
:segmented true
|
||||||
:size size
|
:size size
|
||||||
|
|
|
@ -1,109 +0,0 @@
|
||||||
(ns quo2.components.tabs.tab
|
|
||||||
(:require [quo2.components.icon :as icons]
|
|
||||||
[quo2.components.markdown.text :as text]
|
|
||||||
[quo2.foundations.colors :as colors]
|
|
||||||
[quo2.theme :as theme]
|
|
||||||
[react-native.core :as rn]))
|
|
||||||
|
|
||||||
(def themes
|
|
||||||
{:light {:default {:background-color colors/neutral-20
|
|
||||||
:icon-color colors/neutral-50
|
|
||||||
:label {:style {:color colors/neutral-100}}}
|
|
||||||
:active {:background-color colors/neutral-50
|
|
||||||
:icon-color colors/white
|
|
||||||
:label {:style {:color colors/white}}}
|
|
||||||
:disabled {:background-color colors/neutral-20
|
|
||||||
:icon-color colors/neutral-50
|
|
||||||
:label {:style {:color colors/neutral-100}}}}
|
|
||||||
:dark {:default {:background-color colors/neutral-80
|
|
||||||
:icon-color colors/neutral-40
|
|
||||||
:label {:style {:color colors/white}}}
|
|
||||||
:active {:background-color colors/neutral-60
|
|
||||||
:icon-color colors/white
|
|
||||||
:label {:style {:color colors/white}}}
|
|
||||||
:disabled {:background-color colors/neutral-80
|
|
||||||
:icon-color colors/neutral-40
|
|
||||||
:label {:style {:color colors/white}}}}})
|
|
||||||
|
|
||||||
(def themes-for-blur-background
|
|
||||||
{:light {:default {:background-color colors/neutral-80-opa-5
|
|
||||||
:icon-color colors/neutral-80-opa-40
|
|
||||||
:label {:style {:color colors/neutral-100}}}
|
|
||||||
:active {:background-color colors/neutral-80-opa-60
|
|
||||||
:icon-color colors/white
|
|
||||||
:label {:style {:color colors/white}}}
|
|
||||||
:disabled {:background-color colors/neutral-80-opa-5
|
|
||||||
:icon-color colors/neutral-80-opa-40
|
|
||||||
:label {:style {:color colors/neutral-100}}}}
|
|
||||||
:dark {:default {:background-color colors/white-opa-5
|
|
||||||
:icon-color colors/white
|
|
||||||
:label {:style {:color colors/white}}}
|
|
||||||
:active {:background-color colors/white-opa-20
|
|
||||||
:icon-color colors/white
|
|
||||||
:label {:style {:color colors/white}}}
|
|
||||||
:disabled {:background-color colors/white-opa-5
|
|
||||||
:icon-color colors/neutral-40
|
|
||||||
:label {:style {:color colors/white}}}}})
|
|
||||||
|
|
||||||
(defn style-container
|
|
||||||
[size disabled background-color]
|
|
||||||
(merge {:height size
|
|
||||||
:align-items :center
|
|
||||||
:justify-content :center
|
|
||||||
:flex-direction :row
|
|
||||||
:border-radius (case size
|
|
||||||
32 10
|
|
||||||
28 8
|
|
||||||
24 8
|
|
||||||
20 6)
|
|
||||||
:background-color background-color
|
|
||||||
:padding-horizontal (case size
|
|
||||||
32 12
|
|
||||||
28 12
|
|
||||||
24 8
|
|
||||||
20 8)}
|
|
||||||
(when disabled
|
|
||||||
{:opacity 0.3})))
|
|
||||||
|
|
||||||
(defn tab
|
|
||||||
"[tab opts \"label\"]
|
|
||||||
opts
|
|
||||||
{:type :primary/:secondary/:grey/:outline/:ghost/:danger
|
|
||||||
:size 40/32/24
|
|
||||||
:icon true/false
|
|
||||||
:before :icon-keyword
|
|
||||||
:after :icon-keyword}"
|
|
||||||
[_ _]
|
|
||||||
(fn [{:keys [id on-press disabled size before active accessibility-label blur? override-theme]
|
|
||||||
:or {size 32}}
|
|
||||||
children]
|
|
||||||
(let [state (cond disabled :disabled
|
|
||||||
active :active
|
|
||||||
:else :default)
|
|
||||||
{:keys [icon-color background-color label]}
|
|
||||||
(get-in (if blur? themes-for-blur-background themes)
|
|
||||||
[(or override-theme (theme/get-theme)) state])]
|
|
||||||
[rn/touchable-without-feedback
|
|
||||||
(merge {:disabled disabled
|
|
||||||
:accessibility-label accessibility-label}
|
|
||||||
(when on-press
|
|
||||||
{:on-press (fn []
|
|
||||||
(on-press id))}))
|
|
||||||
[rn/view {:style (style-container size disabled background-color)}
|
|
||||||
(when before
|
|
||||||
[rn/view
|
|
||||||
[icons/icon before {:color icon-color}]])
|
|
||||||
[rn/view
|
|
||||||
(cond
|
|
||||||
(string? children)
|
|
||||||
[text/text
|
|
||||||
(merge {:size (case size
|
|
||||||
24 :paragraph-2
|
|
||||||
20 :label
|
|
||||||
nil)
|
|
||||||
:weight :medium
|
|
||||||
:number-of-lines 1}
|
|
||||||
label)
|
|
||||||
children]
|
|
||||||
(vector? children)
|
|
||||||
children)]]])))
|
|
|
@ -0,0 +1,101 @@
|
||||||
|
(ns quo2.components.tabs.tab.style
|
||||||
|
(:require [quo2.foundations.colors :as colors]
|
||||||
|
[quo2.theme :as theme]))
|
||||||
|
|
||||||
|
(def tab-background-opacity 0.3)
|
||||||
|
|
||||||
|
(defn size->padding-left
|
||||||
|
[size]
|
||||||
|
(case size
|
||||||
|
32 12
|
||||||
|
28 12
|
||||||
|
24 8
|
||||||
|
20 8
|
||||||
|
nil))
|
||||||
|
|
||||||
|
(defn size->border-radius
|
||||||
|
[size]
|
||||||
|
(case size
|
||||||
|
32 10
|
||||||
|
28 8
|
||||||
|
24 8
|
||||||
|
20 6
|
||||||
|
nil))
|
||||||
|
|
||||||
|
(def notification-dot
|
||||||
|
{:position :absolute
|
||||||
|
:top -2
|
||||||
|
:right -2})
|
||||||
|
|
||||||
|
(def container
|
||||||
|
{:flex-direction :row})
|
||||||
|
|
||||||
|
(defn tab
|
||||||
|
[{:keys [size disabled background-color show-notification-dot?]}]
|
||||||
|
(let [border-radius (size->border-radius size)
|
||||||
|
padding (size->padding-left size)]
|
||||||
|
(merge {:height size
|
||||||
|
:align-items :center
|
||||||
|
:flex-direction :row
|
||||||
|
:border-top-left-radius border-radius
|
||||||
|
:border-bottom-left-radius border-radius
|
||||||
|
:background-color background-color
|
||||||
|
:padding-left padding}
|
||||||
|
;; The minimum padding right of 1 is a mandatory workaround. Without
|
||||||
|
;; it, the SVG rendered besides the tab will have a 1px margin. This
|
||||||
|
;; issue still exists in the latest react-native-svg versions.
|
||||||
|
(if show-notification-dot?
|
||||||
|
{:padding-right 1}
|
||||||
|
{:border-radius border-radius
|
||||||
|
:padding-right padding})
|
||||||
|
(when disabled
|
||||||
|
{:opacity tab-background-opacity}))))
|
||||||
|
|
||||||
|
(def themes
|
||||||
|
{:light {:default {:background-color colors/neutral-20
|
||||||
|
:icon-color colors/neutral-50
|
||||||
|
:label {:style {:color colors/neutral-100}}}
|
||||||
|
:active {:background-color colors/neutral-50
|
||||||
|
:icon-color colors/white
|
||||||
|
:label {:style {:color colors/white}}}
|
||||||
|
:disabled {:background-color colors/neutral-20
|
||||||
|
:icon-color colors/neutral-50
|
||||||
|
:label {:style {:color colors/neutral-100}}}}
|
||||||
|
:dark {:default {:background-color colors/neutral-80
|
||||||
|
:icon-color colors/neutral-40
|
||||||
|
:label {:style {:color colors/white}}}
|
||||||
|
:active {:background-color colors/neutral-60
|
||||||
|
:icon-color colors/white
|
||||||
|
:label {:style {:color colors/white}}}
|
||||||
|
:disabled {:background-color colors/neutral-80
|
||||||
|
:icon-color colors/neutral-40
|
||||||
|
:label {:style {:color colors/white}}}}})
|
||||||
|
|
||||||
|
(def themes-for-blur-background
|
||||||
|
{:light {:default {:background-color colors/neutral-80-opa-5
|
||||||
|
:icon-color colors/neutral-80-opa-40
|
||||||
|
:label {:style {:color colors/neutral-100}}}
|
||||||
|
:active {:background-color colors/neutral-80-opa-60
|
||||||
|
:icon-color colors/white
|
||||||
|
:label {:style {:color colors/white}}}
|
||||||
|
:disabled {:background-color colors/neutral-80-opa-5
|
||||||
|
:icon-color colors/neutral-80-opa-40
|
||||||
|
:label {:style {:color colors/neutral-100}}}}
|
||||||
|
:dark {:default {:background-color colors/white-opa-5
|
||||||
|
:icon-color colors/white
|
||||||
|
:label {:style {:color colors/white}}}
|
||||||
|
:active {:background-color colors/white-opa-20
|
||||||
|
:icon-color colors/white
|
||||||
|
:label {:style {:color colors/white}}}
|
||||||
|
:disabled {:background-color colors/white-opa-5
|
||||||
|
:icon-color colors/neutral-40
|
||||||
|
:label {:style {:color colors/white}}}}})
|
||||||
|
|
||||||
|
(defn by-theme
|
||||||
|
[{:keys [override-theme disabled active blur?]}]
|
||||||
|
(let [state (cond disabled :disabled
|
||||||
|
active :active
|
||||||
|
:else :default)
|
||||||
|
theme (or override-theme (theme/get-theme))]
|
||||||
|
(get-in (if blur? themes-for-blur-background themes)
|
||||||
|
[theme state])))
|
|
@ -0,0 +1,94 @@
|
||||||
|
(ns quo2.components.tabs.tab.view
|
||||||
|
(:require [quo2.components.icon :as icons]
|
||||||
|
[quo2.components.markdown.text :as text]
|
||||||
|
[quo2.components.tabs.tab.style :as style]
|
||||||
|
[quo2.components.notifications.notification-dot :as notification-dot]
|
||||||
|
[react-native.core :as rn]
|
||||||
|
[react-native.svg :as svg]))
|
||||||
|
|
||||||
|
(defn- right-side-with-cutout
|
||||||
|
"SVG exported from Figma."
|
||||||
|
[{:keys [height width background-color disabled]}]
|
||||||
|
;; Do not add a view-box property, it'll cause an artifact where the SVG is
|
||||||
|
;; rendered slightly smaller than the proper width and height.
|
||||||
|
[svg/svg
|
||||||
|
{:width width
|
||||||
|
:height height
|
||||||
|
:fill background-color
|
||||||
|
:fill-opacity (when disabled style/tab-background-opacity)}
|
||||||
|
[svg/path
|
||||||
|
{:d
|
||||||
|
"M 11.468 6.781 C 11.004 6.923 10.511 7 10 7 C 7.239 7 5 4.761 5 2 C 5
|
||||||
|
1.489 5.077 0.996 5.219 0.532 C 4.687 0.351 4.134 0.213 3.564 0.123 C 2.787
|
||||||
|
0 1.858 0 0 0 L 0 32 C 1.858 32 2.787 32 3.564 31.877 C 7.843 31.199 11.199
|
||||||
|
27.843 11.877 23.564 C 12 22.787 12 21.858 12 20 L 12 12 C 12 10.142 12
|
||||||
|
9.213 11.877 8.436 C 11.787 7.866 11.649 7.313 11.468 6.781 Z"
|
||||||
|
:clip-path "url(#clip0_5514_84289)"}]
|
||||||
|
[svg/defs
|
||||||
|
[svg/clippath {:id "clip0_5514_84289"}
|
||||||
|
[svg/rect {:width width :height height :fill :none}]]]])
|
||||||
|
|
||||||
|
(defn- content
|
||||||
|
[{:keys [size label]} children]
|
||||||
|
[rn/view
|
||||||
|
(cond
|
||||||
|
(string? children)
|
||||||
|
[text/text
|
||||||
|
(merge {:size (case size
|
||||||
|
24 :paragraph-2
|
||||||
|
20 :label
|
||||||
|
nil)
|
||||||
|
:weight :medium
|
||||||
|
:number-of-lines 1}
|
||||||
|
label)
|
||||||
|
children]
|
||||||
|
|
||||||
|
(vector? children)
|
||||||
|
children)])
|
||||||
|
|
||||||
|
(defn view
|
||||||
|
[{:keys [accessibility-label
|
||||||
|
active
|
||||||
|
before
|
||||||
|
blur?
|
||||||
|
disabled
|
||||||
|
id
|
||||||
|
on-press
|
||||||
|
override-theme
|
||||||
|
size
|
||||||
|
notification-dot?]
|
||||||
|
:or {size 32}}
|
||||||
|
children]
|
||||||
|
(let [show-notification-dot? (and notification-dot? (= size 32))
|
||||||
|
{:keys [icon-color
|
||||||
|
background-color
|
||||||
|
label]}
|
||||||
|
(style/by-theme {:override-theme override-theme
|
||||||
|
:blur? blur?
|
||||||
|
:disabled disabled
|
||||||
|
:active active})]
|
||||||
|
[rn/touchable-without-feedback
|
||||||
|
(merge {:disabled disabled
|
||||||
|
:accessibility-label accessibility-label}
|
||||||
|
(when on-press
|
||||||
|
{:on-press (fn []
|
||||||
|
(on-press id))}))
|
||||||
|
[rn/view {:style style/container}
|
||||||
|
(when show-notification-dot?
|
||||||
|
[notification-dot/notification-dot
|
||||||
|
{:style style/notification-dot}])
|
||||||
|
[rn/view
|
||||||
|
{:style (style/tab {:size size
|
||||||
|
:disabled disabled
|
||||||
|
:background-color background-color
|
||||||
|
:show-notification-dot? show-notification-dot?})}
|
||||||
|
(when before
|
||||||
|
[rn/view
|
||||||
|
[icons/icon before {:color icon-color}]])
|
||||||
|
[content {:size size :label label} children]]
|
||||||
|
(when show-notification-dot?
|
||||||
|
[right-side-with-cutout
|
||||||
|
{:width (style/size->padding-left size)
|
||||||
|
:height size
|
||||||
|
:disabled disabled
|
||||||
|
:background-color background-color}])]]))
|
|
@ -1,8 +1,6 @@
|
||||||
(ns quo2.components.tabs.tabs
|
(ns quo2.components.tabs.tabs
|
||||||
(:require [oops.core :refer [oget]]
|
(:require [oops.core :refer [oget]]
|
||||||
[quo2.components.notifications.notification-dot :refer [notification-dot]]
|
[quo2.components.tabs.tab.view :as tab]
|
||||||
[quo2.components.tabs.tab :as tab]
|
|
||||||
[quo2.foundations.colors :as colors]
|
|
||||||
[react-native.core :as rn]
|
[react-native.core :as rn]
|
||||||
[react-native.linear-gradient :as linear-gradient]
|
[react-native.linear-gradient :as linear-gradient]
|
||||||
[react-native.masked-view :as masked-view]
|
[react-native.masked-view :as masked-view]
|
||||||
|
@ -11,22 +9,7 @@
|
||||||
[utils.number :as utils.number]))
|
[utils.number :as utils.number]))
|
||||||
|
|
||||||
(def default-tab-size 32)
|
(def default-tab-size 32)
|
||||||
|
(def unread-count-offset 3)
|
||||||
(defn indicator
|
|
||||||
[]
|
|
||||||
[rn/view
|
|
||||||
{:accessibility-label :notification-dot
|
|
||||||
:style {:position :absolute
|
|
||||||
:z-index 1
|
|
||||||
:right -2
|
|
||||||
:top -2
|
|
||||||
:width 10
|
|
||||||
:height 10
|
|
||||||
:border-radius 5
|
|
||||||
:justify-content :center
|
|
||||||
:align-items :center
|
|
||||||
:background-color (colors/theme-colors colors/neutral-5 colors/neutral-95)}}
|
|
||||||
[notification-dot]])
|
|
||||||
|
|
||||||
(defn- calculate-fade-end-percentage
|
(defn- calculate-fade-end-percentage
|
||||||
[{:keys [offset-x content-width layout-width max-fade-percentage]}]
|
[{:keys [offset-x content-width layout-width max-fade-percentage]}]
|
||||||
|
@ -38,109 +21,30 @@
|
||||||
0.99
|
0.99
|
||||||
(utils.number/naive-round fade-percentage 2))))
|
(utils.number/naive-round fade-percentage 2))))
|
||||||
|
|
||||||
(defn tabs
|
(defn- masked-view-wrapper
|
||||||
"Usage:
|
[{:keys [fade-end-percentage fade-end?]} & children]
|
||||||
{:type :icon/:emoji/:label
|
(if fade-end?
|
||||||
:component tag/tab
|
(into [masked-view/masked-view
|
||||||
:size 32/24
|
|
||||||
:on-press fn
|
|
||||||
:blurred? true/false
|
|
||||||
:labelled? true/false
|
|
||||||
:disabled? true/false
|
|
||||||
:scrollable? false
|
|
||||||
:scroll-on-press? true
|
|
||||||
:fade-end? true
|
|
||||||
:on-change fn
|
|
||||||
:default-active tag-id
|
|
||||||
:data [{:id :label \"\" :resource \"url\"}
|
|
||||||
{:id :label \"\" :resource \"url\"}]}
|
|
||||||
Opts:
|
|
||||||
- `component` this is to determine which component is to be rendered since the
|
|
||||||
logic in this view is shared between tab and tag component
|
|
||||||
- `blurred` boolean: use to determine border color if the background is blurred
|
|
||||||
- `type` can be icon or emoji with or without a tag label
|
|
||||||
- `labelled` boolean: is true if tag has label else false
|
|
||||||
- `size` number
|
|
||||||
- `scroll-on-press?` When non-nil, clicking on a tag centers it the middle
|
|
||||||
(with animation enabled).
|
|
||||||
- `fade-end?` When non-nil, causes the end of the scrollable view to fade out.
|
|
||||||
- `fade-end-percentage` Percentage where fading starts relative to the total
|
|
||||||
layout width of the `flat-list` data."
|
|
||||||
|
|
||||||
[{:keys [default-active fade-end-percentage]
|
|
||||||
:or {fade-end-percentage 0.8}}]
|
|
||||||
(let [active-tab-id (reagent/atom default-active)
|
|
||||||
fading (reagent/atom {:fade-end-percentage fade-end-percentage})
|
|
||||||
flat-list-ref (atom nil)]
|
|
||||||
(fn
|
|
||||||
[{:keys [data
|
|
||||||
fade-end-percentage
|
|
||||||
fade-end?
|
|
||||||
on-change
|
|
||||||
on-scroll
|
|
||||||
scroll-event-throttle
|
|
||||||
scroll-on-press?
|
|
||||||
scrollable?
|
|
||||||
style
|
|
||||||
size
|
|
||||||
blur?
|
|
||||||
override-theme]
|
|
||||||
:or {fade-end-percentage fade-end-percentage
|
|
||||||
fade-end? false
|
|
||||||
scroll-event-throttle 64
|
|
||||||
scrollable? false
|
|
||||||
scroll-on-press? false
|
|
||||||
size default-tab-size}
|
|
||||||
:as props}]
|
|
||||||
(if scrollable?
|
|
||||||
(let [maybe-mask-wrapper (if fade-end?
|
|
||||||
[masked-view/masked-view
|
|
||||||
{:mask-element
|
{:mask-element
|
||||||
(reagent/as-element
|
(reagent/as-element
|
||||||
[linear-gradient/linear-gradient
|
[linear-gradient/linear-gradient
|
||||||
{:colors [:black :transparent]
|
{:colors [:black :transparent]
|
||||||
:locations [(get @fading :fade-end-percentage) 1]
|
:locations [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
|
||||||
:style {:width "100%"
|
:style {:width "100%"
|
||||||
:height "100%"}}])}]
|
:height "100%"}}])}]
|
||||||
[:<>])]
|
children)
|
||||||
(conj
|
(into [:<>] children)))
|
||||||
maybe-mask-wrapper
|
|
||||||
[rn/flat-list
|
(defn- on-scroll-handler
|
||||||
(merge
|
[{:keys [on-scroll fading fade-end-percentage fade-end?]} ^js e]
|
||||||
(dissoc props
|
|
||||||
:default-active
|
|
||||||
:fade-end-percentage
|
|
||||||
:fade-end?
|
|
||||||
:on-change
|
|
||||||
:scroll-on-press?
|
|
||||||
:size)
|
|
||||||
(when scroll-on-press?
|
|
||||||
{:initial-scroll-index (utils.collection/first-index #(= @active-tab-id (:id %)) data)})
|
|
||||||
{:ref #(reset! flat-list-ref %)
|
|
||||||
:extra-data (str @active-tab-id)
|
|
||||||
:horizontal true
|
|
||||||
:scroll-event-throttle scroll-event-throttle
|
|
||||||
:shows-horizontal-scroll-indicator false
|
|
||||||
:data data
|
|
||||||
:key-fn (comp str :id)
|
|
||||||
:on-scroll-to-index-failed identity
|
|
||||||
:on-scroll (fn [^js e]
|
|
||||||
(when fade-end?
|
(when fade-end?
|
||||||
(let [offset-x (oget
|
(let [offset-x (oget e "nativeEvent.contentOffset.x")
|
||||||
e
|
content-width (oget e "nativeEvent.contentSize.width")
|
||||||
"nativeEvent.contentOffset.x")
|
layout-width (oget e "nativeEvent.layoutMeasurement.width")
|
||||||
content-width
|
new-percentage (calculate-fade-end-percentage
|
||||||
(oget
|
|
||||||
e
|
|
||||||
"nativeEvent.contentSize.width")
|
|
||||||
layout-width
|
|
||||||
(oget e
|
|
||||||
"nativeEvent.layoutMeasurement.width")
|
|
||||||
new-percentage
|
|
||||||
(calculate-fade-end-percentage
|
|
||||||
{:offset-x offset-x
|
{:offset-x offset-x
|
||||||
:content-width content-width
|
:content-width content-width
|
||||||
:layout-width layout-width
|
:layout-width layout-width
|
||||||
|
@ -153,50 +57,143 @@
|
||||||
new-percentage))))
|
new-percentage))))
|
||||||
(when on-scroll
|
(when on-scroll
|
||||||
(on-scroll e)))
|
(on-scroll e)))
|
||||||
:render-fn (fn [{:keys [id label]} index]
|
|
||||||
|
(defn- render-tab
|
||||||
|
[{:keys [active-tab-id
|
||||||
|
blur?
|
||||||
|
flat-list-ref
|
||||||
|
number-of-items
|
||||||
|
on-change
|
||||||
|
override-theme
|
||||||
|
scroll-on-press?
|
||||||
|
size
|
||||||
|
style]}
|
||||||
|
{:keys [id label notification-dot? accessibility-label]}
|
||||||
|
index]
|
||||||
[rn/view
|
[rn/view
|
||||||
{:style {:margin-right (if (= size default-tab-size)
|
{:style {:margin-right (if (= size default-tab-size) 12 8)
|
||||||
12
|
:padding-right (when (= index (dec number-of-items))
|
||||||
8)
|
(:padding-left style))}}
|
||||||
:padding-right (when (= index
|
[tab/view
|
||||||
(dec (count data)))
|
|
||||||
(get-in props
|
|
||||||
[:style
|
|
||||||
:padding-left]))}}
|
|
||||||
[tab/tab
|
|
||||||
{:id id
|
{:id id
|
||||||
|
:notification-dot? notification-dot?
|
||||||
|
:accessibility-label accessibility-label
|
||||||
:size size
|
:size size
|
||||||
:override-theme override-theme
|
:override-theme override-theme
|
||||||
:blur? blur?
|
:blur? blur?
|
||||||
:active (= id @active-tab-id)
|
:active (= id @active-tab-id)
|
||||||
:on-press (fn [id]
|
:on-press (fn [id]
|
||||||
(reset! active-tab-id id)
|
(reset! active-tab-id id)
|
||||||
(when scroll-on-press?
|
(when (and scroll-on-press? @flat-list-ref)
|
||||||
(.scrollToIndex
|
(.scrollToIndex ^js @flat-list-ref
|
||||||
^js
|
|
||||||
@flat-list-ref
|
|
||||||
#js
|
#js
|
||||||
{:animated true
|
{:animated true
|
||||||
:index index
|
:index index
|
||||||
:viewPosition
|
:viewPosition 0.5}))
|
||||||
0.5}))
|
|
||||||
(when on-change
|
(when on-change
|
||||||
(on-change id)))}
|
(on-change id)))}
|
||||||
label]])})]))
|
label]])
|
||||||
[rn/view (merge {:flex-direction :row} style)
|
|
||||||
(doall
|
(defn tabs
|
||||||
(for [{:keys [label id notification-dot? accessibility-label]} data]
|
" Common options (for scrollable and non-scrollable tabs):
|
||||||
^{:key id}
|
|
||||||
[rn/view {:style {:margin-right (if (= size default-tab-size) 12 8)}}
|
- `blur?` Boolean passed down to `quo2.components.tabs.tab/tab`.
|
||||||
(when notification-dot?
|
- `data` Vector of tab items.
|
||||||
[indicator])
|
- `on-change` Callback called after a tab is selected.
|
||||||
[tab/tab
|
- `override-theme` Passed down to `quo2.components.tabs.tab/tab`.
|
||||||
{:id id
|
- `size` 32/24
|
||||||
|
- `style` Style map passed to View wrapping tabs or to the FlatList when tabs
|
||||||
|
are scrollable.
|
||||||
|
|
||||||
|
Options for scrollable tabs:
|
||||||
|
- `fade-end-percentage` Percentage where fading starts relative to the total
|
||||||
|
layout width of the `flat-list` data.
|
||||||
|
- `fade-end?` When non-nil, causes the end of the scrollable view to fade out.
|
||||||
|
- `on-scroll` Callback called on the on-scroll event of the FlatList. Only
|
||||||
|
used when `scrollable?` is non-nil.
|
||||||
|
- `scrollable?` When non-nil, use a scrollable flat-list to render tabs.
|
||||||
|
- `scroll-on-press?` When non-nil, clicking on a tag centers it the middle
|
||||||
|
(with animation enabled).
|
||||||
|
"
|
||||||
|
[{:keys [default-active fade-end-percentage]
|
||||||
|
:or {fade-end-percentage 0.8}}]
|
||||||
|
(let [active-tab-id (reagent/atom default-active)
|
||||||
|
fading (reagent/atom {:fade-end-percentage fade-end-percentage})
|
||||||
|
flat-list-ref (atom nil)]
|
||||||
|
(fn
|
||||||
|
[{:keys [data
|
||||||
|
fade-end-percentage
|
||||||
|
fade-end?
|
||||||
|
on-change
|
||||||
|
on-scroll
|
||||||
|
scroll-on-press?
|
||||||
|
scrollable?
|
||||||
|
style
|
||||||
|
size
|
||||||
|
blur?
|
||||||
|
override-theme]
|
||||||
|
:or {fade-end-percentage fade-end-percentage
|
||||||
|
fade-end? false
|
||||||
|
scrollable? false
|
||||||
|
scroll-on-press? false
|
||||||
|
size default-tab-size}
|
||||||
|
:as props}]
|
||||||
|
(if scrollable?
|
||||||
|
[rn/view {:style {:margin-top (- (dec unread-count-offset))}}
|
||||||
|
[masked-view-wrapper
|
||||||
|
{:fade-end-percentage (get @fading :fade-end-percentage) :fade-end? fade-end?}
|
||||||
|
[rn/flat-list
|
||||||
|
(merge
|
||||||
|
(dissoc props
|
||||||
|
:default-active
|
||||||
|
:fade-end-percentage
|
||||||
|
:fade-end?
|
||||||
|
:on-change
|
||||||
|
:scroll-on-press?
|
||||||
|
:size)
|
||||||
|
(when scroll-on-press?
|
||||||
|
{:initial-scroll-index (utils.collection/first-index #(= @active-tab-id (:id %)) data)})
|
||||||
|
{:ref #(reset! flat-list-ref %)
|
||||||
|
:style style
|
||||||
|
;; The padding-top workaround is needed because on Android
|
||||||
|
;; {:overflow :visible} doesn't work on components inheriting
|
||||||
|
;; from ScrollView (e.g. FlatList). There are open issues, here's
|
||||||
|
;; just one about this topic:
|
||||||
|
;; https://github.com/facebook/react-native/issues/3121
|
||||||
|
:content-container-style {:padding-top (dec unread-count-offset)}
|
||||||
|
:extra-data (str @active-tab-id)
|
||||||
|
:horizontal true
|
||||||
|
:scroll-event-throttle 64
|
||||||
|
:shows-horizontal-scroll-indicator false
|
||||||
|
:data data
|
||||||
|
:key-fn (comp str :id)
|
||||||
|
:on-scroll-to-index-failed identity
|
||||||
|
:on-scroll (partial on-scroll-handler
|
||||||
|
{:fade-end-percentage fade-end-percentage
|
||||||
|
:fade-end? fade-end?
|
||||||
|
:fading fading
|
||||||
|
:on-scroll on-scroll})
|
||||||
|
:render-fn (partial render-tab
|
||||||
|
{:active-tab-id active-tab-id
|
||||||
|
:blur? blur?
|
||||||
|
:flat-list-ref flat-list-ref
|
||||||
|
:number-of-items (count data)
|
||||||
|
:on-change on-change
|
||||||
|
:override-theme override-theme
|
||||||
|
:scroll-on-press? scroll-on-press?
|
||||||
:size size
|
:size size
|
||||||
:accessibility-label accessibility-label
|
:style style})})]]]
|
||||||
:active (= id @active-tab-id)
|
[rn/view (merge style {:flex-direction :row})
|
||||||
:on-press (fn []
|
(map-indexed (fn [index item]
|
||||||
(reset! active-tab-id id)
|
^{:key (:id item)}
|
||||||
(when on-change
|
[render-tab
|
||||||
(on-change id)))}
|
{:active-tab-id active-tab-id
|
||||||
label]]))]))))
|
:blur? blur?
|
||||||
|
:number-of-items (count data)
|
||||||
|
:on-change on-change
|
||||||
|
:override-theme override-theme
|
||||||
|
:size size
|
||||||
|
:style style}
|
||||||
|
item
|
||||||
|
index])
|
||||||
|
data)]))))
|
||||||
|
|
|
@ -4,3 +4,6 @@
|
||||||
|
|
||||||
(def svg (reagent/adapt-react-class Svg/default))
|
(def svg (reagent/adapt-react-class Svg/default))
|
||||||
(def path (reagent/adapt-react-class Svg/Path))
|
(def path (reagent/adapt-react-class Svg/Path))
|
||||||
|
(def rect (reagent/adapt-react-class Svg/Rect))
|
||||||
|
(def clippath (reagent/adapt-react-class Svg/ClipPath))
|
||||||
|
(def defs (reagent/adapt-react-class Svg/Defs))
|
||||||
|
|
|
@ -365,16 +365,30 @@
|
||||||
(rf/defn notifications-fetch-unread-count
|
(rf/defn notifications-fetch-unread-count
|
||||||
{:events [:activity-center.notifications/fetch-unread-count]}
|
{:events [:activity-center.notifications/fetch-unread-count]}
|
||||||
[_]
|
[_]
|
||||||
|
{:dispatch-n (mapv (fn [notification-type]
|
||||||
|
[:activity-center.notifications/fetch-unread-count-for-type notification-type])
|
||||||
|
types/all-supported)})
|
||||||
|
|
||||||
|
(rf/defn notifications-fetch-unread-count-for-type
|
||||||
|
{:events [:activity-center.notifications/fetch-unread-count-for-type]}
|
||||||
|
[_ notification-type]
|
||||||
{:json-rpc/call [{:method "wakuext_unreadAndAcceptedActivityCenterNotificationsCount"
|
{:json-rpc/call [{:method "wakuext_unreadAndAcceptedActivityCenterNotificationsCount"
|
||||||
:params [types/all-supported]
|
:params [[notification-type]]
|
||||||
:on-success #(rf/dispatch [:activity-center.notifications/fetch-unread-count-success
|
:on-success #(rf/dispatch [:activity-center.notifications/fetch-unread-count-success
|
||||||
%])
|
notification-type %])
|
||||||
:on-error #()}]})
|
:on-error #(rf/dispatch [:activity-center.notifications/fetch-unread-count-error
|
||||||
|
%])}]})
|
||||||
|
|
||||||
(rf/defn notifications-fetch-unread-count-success
|
(rf/defn notifications-fetch-unread-count-success
|
||||||
{:events [:activity-center.notifications/fetch-unread-count-success]}
|
{:events [:activity-center.notifications/fetch-unread-count-success]}
|
||||||
[{:keys [db]} result]
|
[{:keys [db]} notification-type result]
|
||||||
{:db (assoc-in db [:activity-center :unread-count] result)})
|
{:db (assoc-in db [:activity-center :unread-counts-by-type notification-type] result)})
|
||||||
|
|
||||||
|
(rf/defn notifications-fetch-unread-count-error
|
||||||
|
{:events [:activity-center.notifications/fetch-unread-count-error]}
|
||||||
|
[_ error]
|
||||||
|
(log/error "Failed to fetch count of notifications" {:error error})
|
||||||
|
nil)
|
||||||
|
|
||||||
(rf/defn notifications-fetch-error
|
(rf/defn notifications-fetch-error
|
||||||
{:events [:activity-center.notifications/fetch-error]}
|
{:events [:activity-center.notifications/fetch-error]}
|
||||||
|
|
|
@ -693,11 +693,26 @@
|
||||||
(h/run-test-sync
|
(h/run-test-sync
|
||||||
(setup)
|
(setup)
|
||||||
(let [spy-queue (atom [])]
|
(let [spy-queue (atom [])]
|
||||||
(h/stub-fx-with-callbacks :json-rpc/call :on-success (constantly 9))
|
(h/stub-fx-with-callbacks :json-rpc/call
|
||||||
|
:on-success
|
||||||
|
(fn [{:keys [params]}]
|
||||||
|
(if (= types/mention (ffirst params))
|
||||||
|
9
|
||||||
|
0)))
|
||||||
(h/spy-fx spy-queue :json-rpc/call)
|
(h/spy-fx spy-queue :json-rpc/call)
|
||||||
|
|
||||||
(rf/dispatch [:activity-center.notifications/fetch-unread-count])
|
(rf/dispatch [:activity-center.notifications/fetch-unread-count])
|
||||||
|
|
||||||
(is (= "wakuext_unreadAndAcceptedActivityCenterNotificationsCount"
|
(is (= "wakuext_unreadAndAcceptedActivityCenterNotificationsCount"
|
||||||
(get-in @spy-queue [0 :args 0 :method])))
|
(get-in @spy-queue [0 :args 0 :method])))
|
||||||
(is (= 9 (get-in (h/db) [:activity-center :unread-count])))))))
|
|
||||||
|
(let [actual (get-in (h/db) [:activity-center :unread-counts-by-type])]
|
||||||
|
(is (= {types/one-to-one-chat 0
|
||||||
|
types/private-group-chat 0
|
||||||
|
types/contact-verification 0
|
||||||
|
types/contact-request 0
|
||||||
|
types/mention 9
|
||||||
|
types/reply 0
|
||||||
|
types/admin 0}
|
||||||
|
actual))
|
||||||
|
(is (= types/all-supported (set (keys actual)))))))))
|
||||||
|
|
|
@ -50,7 +50,8 @@
|
||||||
|
|
||||||
(defn tabs
|
(defn tabs
|
||||||
[]
|
[]
|
||||||
(let [filter-type (rf/sub [:activity-center/filter-type])]
|
(let [filter-type (rf/sub [:activity-center/filter-type])
|
||||||
|
types-with-unread (rf/sub [:activity-center/notification-types-with-unread])]
|
||||||
[quo/tabs
|
[quo/tabs
|
||||||
{:size 32
|
{:size 32
|
||||||
:scrollable? true
|
:scrollable? true
|
||||||
|
@ -66,21 +67,38 @@
|
||||||
:data [{:id types/no-type
|
:data [{:id types/no-type
|
||||||
:label (i18n/label :t/all)}
|
:label (i18n/label :t/all)}
|
||||||
{:id types/admin
|
{:id types/admin
|
||||||
:label (i18n/label :t/admin)}
|
:label (i18n/label :t/admin)
|
||||||
|
:accessibility-label :tab-admin
|
||||||
|
:notification-dot? (contains? types-with-unread types/admin)}
|
||||||
{:id types/mention
|
{:id types/mention
|
||||||
:label (i18n/label :t/mentions)}
|
:label (i18n/label :t/mentions)
|
||||||
|
:accessibility-label :tab-mention
|
||||||
|
:notification-dot? (contains? types-with-unread types/mention)}
|
||||||
{:id types/reply
|
{:id types/reply
|
||||||
:label (i18n/label :t/replies)}
|
:label (i18n/label :t/replies)
|
||||||
|
:accessibility-label :tab-reply
|
||||||
|
:notification-dot? (contains? types-with-unread types/reply)}
|
||||||
{:id types/contact-request
|
{:id types/contact-request
|
||||||
:label (i18n/label :t/contact-requests)}
|
:label (i18n/label :t/contact-requests)
|
||||||
|
:accessibility-label :tab-contact-request
|
||||||
|
:notification-dot? (contains? types-with-unread types/contact-request)}
|
||||||
{:id types/contact-verification
|
{:id types/contact-verification
|
||||||
:label (i18n/label :t/identity-verification)}
|
:label (i18n/label :t/identity-verification)
|
||||||
|
:accessibility-label :tab-contact-verification
|
||||||
|
:notification-dot? (contains? types-with-unread
|
||||||
|
types/contact-verification)}
|
||||||
{:id types/tx
|
{:id types/tx
|
||||||
:label (i18n/label :t/transactions)}
|
:label (i18n/label :t/transactions)
|
||||||
|
:accessibility-label :tab-tx
|
||||||
|
:notification-dot? (contains? types-with-unread types/tx)}
|
||||||
{:id types/membership
|
{:id types/membership
|
||||||
:label (i18n/label :t/membership)}
|
:label (i18n/label :t/membership)
|
||||||
|
:accessibility-label :tab-membership
|
||||||
|
:notification-dot? (contains? types-with-unread types/membership)}
|
||||||
{:id types/system
|
{:id types/system
|
||||||
:label (i18n/label :t/system)}]}]))
|
:label (i18n/label :t/system)
|
||||||
|
:accessibility-label :tab-system
|
||||||
|
:notification-dot? (contains? types-with-unread types/system)}]}]))
|
||||||
|
|
||||||
(defn header
|
(defn header
|
||||||
[]
|
[]
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
(ns status-im2.contexts.quo-preview.tabs.tabs
|
(ns status-im2.contexts.quo-preview.tabs.tabs
|
||||||
(:require [quo2.components.tabs.tabs :as quo2]
|
(:require [quo2.core :as quo]
|
||||||
[quo2.foundations.colors :as colors]
|
[quo2.foundations.colors :as colors]
|
||||||
[react-native.core :as rn]
|
[react-native.core :as rn]
|
||||||
[reagent.core :as reagent]
|
[reagent.core :as reagent]
|
||||||
|
@ -13,10 +13,21 @@
|
||||||
:value "32"}
|
:value "32"}
|
||||||
{:key 24
|
{:key 24
|
||||||
:value "24"}]}
|
:value "24"}]}
|
||||||
|
{:label "Show unread indicators?"
|
||||||
|
:key :unread-indicators?
|
||||||
|
:type :boolean}
|
||||||
{:label "Scrollable:"
|
{:label "Scrollable:"
|
||||||
:key :scrollable?
|
:key :scrollable?
|
||||||
:type :boolean}])
|
:type :boolean}])
|
||||||
|
|
||||||
|
(defn generate-tab-items
|
||||||
|
[length unread-indicators?]
|
||||||
|
(for [index (range length)]
|
||||||
|
^{:key index}
|
||||||
|
{:id index
|
||||||
|
:label (str "Tab " (inc index))
|
||||||
|
:notification-dot? (and unread-indicators? (zero? (rem index 2)))}))
|
||||||
|
|
||||||
(defn cool-preview
|
(defn cool-preview
|
||||||
[]
|
[]
|
||||||
(let [state (reagent/atom {:size 32
|
(let [state (reagent/atom {:size 32
|
||||||
|
@ -28,15 +39,14 @@
|
||||||
[preview/customizer state descriptor]]
|
[preview/customizer state descriptor]]
|
||||||
[rn/view
|
[rn/view
|
||||||
{:padding-vertical 60
|
{:padding-vertical 60
|
||||||
|
:padding-horizontal 20
|
||||||
:flex-direction :row
|
:flex-direction :row
|
||||||
:justify-content :center}
|
:justify-content :center}
|
||||||
[quo2/tabs
|
[quo/tabs
|
||||||
(merge @state
|
(merge @state
|
||||||
{:default-active 1
|
{:default-active 1
|
||||||
:data [{:id 1 :label "Tab 1"}
|
:data (generate-tab-items (if (:scrollable? @state) 15 4)
|
||||||
{:id 2 :label "Tab 2"}
|
(:unread-indicators? @state))
|
||||||
{:id 3 :label "Tab 3"}
|
|
||||||
{:id 4 :label "Tab 4"}]
|
|
||||||
:on-change #(println "Active tab" %)}
|
:on-change #(println "Active tab" %)}
|
||||||
(when (:scrollable? @state)
|
(when (:scrollable? @state)
|
||||||
{:scroll-on-press? true
|
{:scroll-on-press? true
|
||||||
|
@ -50,6 +60,6 @@
|
||||||
:flex 1}
|
:flex 1}
|
||||||
[rn/flat-list
|
[rn/flat-list
|
||||||
{:flex 1
|
{:flex 1
|
||||||
:keyboardShouldPersistTaps :always
|
:keyboard-should-persist-taps :always
|
||||||
:header [cool-preview]
|
:header [cool-preview]
|
||||||
:key-fn str}]])
|
:key-fn str}]])
|
||||||
|
|
|
@ -9,10 +9,30 @@
|
||||||
(:notifications activity-center)))
|
(:notifications activity-center)))
|
||||||
|
|
||||||
(re-frame/reg-sub
|
(re-frame/reg-sub
|
||||||
:activity-center/unread-count
|
:activity-center/unread-counts-by-type
|
||||||
:<- [:activity-center]
|
:<- [:activity-center]
|
||||||
(fn [activity-center]
|
(fn [activity-center]
|
||||||
(:unread-count activity-center)))
|
(:unread-counts-by-type activity-center)))
|
||||||
|
|
||||||
|
(re-frame/reg-sub
|
||||||
|
:activity-center/notification-types-with-unread
|
||||||
|
:<- [:activity-center/unread-counts-by-type]
|
||||||
|
(fn [unread-counts]
|
||||||
|
(reduce-kv
|
||||||
|
(fn [acc notification-type unread-count]
|
||||||
|
(if (pos? unread-count)
|
||||||
|
(conj acc notification-type)
|
||||||
|
acc))
|
||||||
|
#{}
|
||||||
|
unread-counts)))
|
||||||
|
|
||||||
|
(re-frame/reg-sub
|
||||||
|
:activity-center/unread-count
|
||||||
|
:<- [:activity-center/unread-counts-by-type]
|
||||||
|
(fn [unread-counts]
|
||||||
|
(->> unread-counts
|
||||||
|
vals
|
||||||
|
(reduce + 0))))
|
||||||
|
|
||||||
(re-frame/reg-sub
|
(re-frame/reg-sub
|
||||||
:activity-center/filter-status
|
:activity-center/filter-status
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
(ns status-im2.subs.activity-center-test
|
(ns status-im2.subs.activity-center-test
|
||||||
(:require [cljs.test :refer [is testing]]
|
(:require [cljs.test :refer [is testing]]
|
||||||
[re-frame.db :as rf-db]
|
[re-frame.db :as rf-db]
|
||||||
[test-helpers.unit :as h]
|
[status-im2.contexts.activity-center.notification-types :as types]
|
||||||
status-im2.subs.activity-center
|
status-im2.subs.activity-center
|
||||||
|
[test-helpers.unit :as h]
|
||||||
[utils.re-frame :as rf]))
|
[utils.re-frame :as rf]))
|
||||||
|
|
||||||
(h/deftest-sub :activity-center/filter-status-unread-enabled?
|
(h/deftest-sub :activity-center/filter-status-unread-enabled?
|
||||||
|
@ -14,3 +15,51 @@
|
||||||
(testing "returns false when filter status is not unread"
|
(testing "returns false when filter status is not unread"
|
||||||
(swap! rf-db/app-db assoc-in [:activity-center :filter :status] :all)
|
(swap! rf-db/app-db assoc-in [:activity-center :filter :status] :all)
|
||||||
(is (false? (rf/sub [sub-name])))))
|
(is (false? (rf/sub [sub-name])))))
|
||||||
|
|
||||||
|
(h/deftest-sub :activity-center/notification-types-with-unread
|
||||||
|
[sub-name]
|
||||||
|
(testing "returns an empty set when no types have unread notifications"
|
||||||
|
(swap! rf-db/app-db assoc-in
|
||||||
|
[:activity-center :unread-counts-by-type]
|
||||||
|
{types/one-to-one-chat 0
|
||||||
|
types/private-group-chat 0
|
||||||
|
types/contact-verification 0
|
||||||
|
types/contact-request 0
|
||||||
|
types/mention 0
|
||||||
|
types/reply 0
|
||||||
|
types/admin 0})
|
||||||
|
|
||||||
|
(is (= #{} (rf/sub [sub-name]))))
|
||||||
|
|
||||||
|
(testing "returns a set with all types containing positive unread counts"
|
||||||
|
(swap! rf-db/app-db assoc-in
|
||||||
|
[:activity-center :unread-counts-by-type]
|
||||||
|
{types/one-to-one-chat 1
|
||||||
|
types/private-group-chat 0
|
||||||
|
types/contact-verification 1
|
||||||
|
types/contact-request 0
|
||||||
|
types/mention 3
|
||||||
|
types/reply 0
|
||||||
|
types/admin 2})
|
||||||
|
|
||||||
|
(let [actual (rf/sub [sub-name])]
|
||||||
|
(is (= #{types/one-to-one-chat
|
||||||
|
types/contact-verification
|
||||||
|
types/mention
|
||||||
|
types/admin}
|
||||||
|
actual))
|
||||||
|
(is (set? actual)))))
|
||||||
|
|
||||||
|
(h/deftest-sub :activity-center/unread-count
|
||||||
|
[sub-name]
|
||||||
|
(swap! rf-db/app-db assoc-in
|
||||||
|
[:activity-center :unread-counts-by-type]
|
||||||
|
{types/one-to-one-chat 1
|
||||||
|
types/private-group-chat 2
|
||||||
|
types/contact-verification 3
|
||||||
|
types/contact-request 4
|
||||||
|
types/mention 5
|
||||||
|
types/reply 6
|
||||||
|
types/admin 7})
|
||||||
|
|
||||||
|
(is (= 28 (rf/sub [sub-name]))))
|
||||||
|
|
Loading…
Reference in New Issue