chore(activity-center): Various small refactors (#20690)

Potentially a solution to https://github.com/status-im/status-mobile/issues/15706

- [x] Fixes swipe button on Android and iOS.
- [x] Performance: we now subscribe only to the minimum from each community.
      This could be the reason the AC would lag as described in the parent
      issue.
- [x] Performance: was able to use flex and removed swipe button height
      calculation that was using `onLayout` and was causing a re-render.
- [x] Performance: reduced the initial number of items to render in the flatlist
      from 10 to 7.
- [x] Performance: delay rendering the heavy list of notification components.
      See in the video below how slow it is to open the AC with just 6
      notifications and that the opening animation is never displayed. And then
      check the improved version with the artificial delay provided by
      `rn/delay-render`. By opening the AC first and animating, this gives the
      user something to look for, and hopefully a few milliseconds more to think
      the app is not stuck, which will be preciously used to render
      notifications.

We refactor all views in the AC to:

- [x] Follow our newest standards with React hooks.
- [x] Removed prop-drilling by creating a separate React context to store the
      current swipeable item (because we need to call `.close` on a `Gesture
      Swipeable` instance whenever a new swipeable opens.
This commit is contained in:
Icaro Motta 2024-07-17 12:16:06 -03:00 committed by GitHub
parent 7b09402fcb
commit b3e88508ac
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 447 additions and 367 deletions

View File

@ -45,6 +45,11 @@
(def ^:const activity-center-membership-status-accepted 2) (def ^:const activity-center-membership-status-accepted 2)
(def ^:const activity-center-membership-status-declined 3) (def ^:const activity-center-membership-status-declined 3)
;; Choose the maximum number of notifications that *usually/safely* fit on
;; most screens, so that the UI doesn't have to needlessly render
;; notifications.
(def ^:const notifications-per-page 7)
(def ^:const mute-for-15-mins-type 1) (def ^:const mute-for-15-mins-type 1)
(def ^:const mute-for-1-hour-type 2) (def ^:const mute-for-1-hour-type 2)
(def ^:const mute-for-8-hours-type 3) (def ^:const mute-for-8-hours-type 3)

View File

@ -0,0 +1,18 @@
(ns status-im.contexts.shell.activity-center.context
(:require
["react" :as react]
[oops.core :as oops]
[react-native.core :as rn]))
(defonce ^:private context
(react/createContext {}))
(defn provider
[state & children]
(into [:> (oops/oget context :Provider) {:value state}]
children))
(defn use-context
[]
(let [ctx (rn/use-context context)]
{:active-swipeable (oops/oget ctx :activeSwipeable)}))

View File

@ -15,10 +15,7 @@
(def defaults (def defaults
{:filter-status :unread {:filter-status :unread
:filter-type types/no-type :filter-type types/no-type
;; Choose the maximum number of notifications that *usually/safely* fit on :notifications-per-page constants/notifications-per-page})
;; most screens, so that the UI doesn't have to needlessly render
;; notifications.
:notifications-per-page 8})
;;;; Navigation ;;;; Navigation

View File

@ -1,6 +1,7 @@
(ns status-im.contexts.shell.activity-center.notification.admin.view (ns status-im.contexts.shell.activity-center.notification.admin.view
(:require (:require
[quo.core :as quo] [quo.core :as quo]
[react-native.core :as rn]
[status-im.constants :as constants] [status-im.constants :as constants]
[status-im.contexts.shell.activity-center.notification.common.style :as common-style] [status-im.contexts.shell.activity-center.notification.common.style :as common-style]
[status-im.contexts.shell.activity-center.notification.common.view :as common] [status-im.contexts.shell.activity-center.notification.common.view :as common]
@ -23,42 +24,50 @@
:text (i18n/label :t/decline)}]) :text (i18n/label :t/decline)}])
(defn- swipeable (defn- swipeable
[{:keys [active-swipeable notification extra-fn]} child] [{:keys [notification extra-fn]} child]
(let [{:keys [community-id id membership-status]} notification] (let [{:keys [community-id id
membership-status]} notification
accept (rn/use-callback
(fn []
(rf/dispatch [:communities/accept-request-to-join-pressed
community-id id]))
[community-id id])
decline (rn/use-callback
(fn []
(rf/dispatch [:communities/decline-request-to-join-pressed
community-id id]))
[community-id id])]
(cond (cond
(#{constants/activity-center-membership-status-accepted (#{constants/activity-center-membership-status-accepted
constants/activity-center-membership-status-declined} constants/activity-center-membership-status-declined}
membership-status) membership-status)
[common/swipeable [common/swipeable
{:left-button common/swipe-button-read-or-unread {:left-button common/swipe-button-read-or-unread
:left-on-press common/swipe-on-press-toggle-read :left-on-press common/swipe-on-press-toggle-read
:right-button common/swipe-button-delete :right-button common/swipe-button-delete
:right-on-press common/swipe-on-press-delete :right-on-press common/swipe-on-press-delete
:active-swipeable active-swipeable :extra-fn extra-fn}
:extra-fn extra-fn}
child] child]
(= membership-status constants/activity-center-membership-status-pending) (= membership-status constants/activity-center-membership-status-pending)
[common/swipeable [common/swipeable
{:left-button swipe-button-accept {:left-button swipe-button-accept
:left-on-press #(rf/dispatch [:communities/accept-request-to-join-pressed community-id id]) :left-on-press accept
:right-button swipe-button-decline :right-button swipe-button-decline
:right-on-press #(rf/dispatch [:communities/decline-request-to-join-pressed community-id :right-on-press decline
id]) :extra-fn extra-fn}
:active-swipeable active-swipeable
:extra-fn extra-fn}
child] child]
:else :else
child))) child)))
(defn view (defn view
[{:keys [notification set-swipeable-height customization-color] :as props}] [{:keys [notification] :as props}]
(let [{:keys [author community-id id membership-status (let [{:keys [author community-id id membership-status
read timestamp]} notification read timestamp]} notification
community (rf/sub [:communities/community community-id]) community-name (rf/sub [:communities/name community-id])
community-name (:name community) community-logo (rf/sub [:communities/logo community-id])
community-image (get-in community [:images :thumbnail :uri])] customization-color (rf/sub [:profile/customization-color])]
[swipeable props [swipeable props
[quo/activity-log [quo/activity-log
{:title (i18n/label :t/join-request) {:title (i18n/label :t/join-request)
@ -66,14 +75,13 @@
:icon :i/add-user :icon :i/add-user
:timestamp (datetime/timestamp->relative timestamp) :timestamp (datetime/timestamp->relative timestamp)
:unread? (not read) :unread? (not read)
:on-layout set-swipeable-height
:context [[common/user-avatar-tag author] :context [[common/user-avatar-tag author]
(i18n/label :t/wants-to-join) (i18n/label :t/wants-to-join)
[quo/context-tag [quo/context-tag
{:type :community {:type :community
:size 24 :size 24
:blur? true :blur? true
:community-logo community-image :community-logo community-logo
:community-name community-name}]] :community-name community-name}]]
:items (condp = membership-status :items (condp = membership-status
constants/activity-center-membership-status-accepted constants/activity-center-membership-status-accepted

View File

@ -4,6 +4,7 @@
(def swipe-action-width 80) (def swipe-action-width 80)
(def swipe-button-border-radius 16) (def swipe-button-border-radius 16)
(def swipe-button-margin 8)
(def user-avatar-tag (def user-avatar-tag
{:background-color colors/white-opa-10}) {:background-color colors/white-opa-10})

View File

@ -1,10 +1,12 @@
(ns status-im.contexts.shell.activity-center.notification.common.view (ns status-im.contexts.shell.activity-center.notification.common.view
(:require (:require
[oops.core :as oops]
[quo.core :as quo] [quo.core :as quo]
[quo.foundations.colors :as colors] [quo.foundations.colors :as colors]
[react-native.core :as rn] [react-native.core :as rn]
[react-native.gesture :as gesture] [react-native.gesture :as gesture]
[status-im.contexts.profile.utils :as profile.utils] [status-im.contexts.profile.utils :as profile.utils]
[status-im.contexts.shell.activity-center.context :as ac.context]
[status-im.contexts.shell.activity-center.notification.common.style :as style] [status-im.contexts.shell.activity-center.notification.common.style :as style]
[utils.i18n :as i18n] [utils.i18n :as i18n]
[utils.re-frame :as rf])) [utils.re-frame :as rf]))
@ -14,7 +16,7 @@
(let [profile (rf/sub [:contacts/contact-by-identity user-id])] (let [profile (rf/sub [:contacts/contact-by-identity user-id])]
[rn/view [rn/view
{:on-start-should-set-responder {:on-start-should-set-responder
(fn [_event] (fn []
(rf/dispatch [:navigate-back]) (rf/dispatch [:navigate-back])
(rf/dispatch [:chat.ui/show-profile user-id]) (rf/dispatch [:chat.ui/show-profile user-id])
true)} true)}
@ -33,35 +35,27 @@
swipe-button swipe-button
swipeable-ref swipeable-ref
style]}] style]}]
(fn [_ ^js drag-x] (fn [_ drag-x]
(let [{:keys [height] :as extra} (extra-fn) (let [extra (extra-fn)
opacity (.interpolate drag-x interpolation-opacity) opacity (oops/ocall drag-x :interpolate interpolation-opacity)
translate-x (.interpolate drag-x interpolation-translate-x)] translate-x (oops/ocall drag-x :interpolate interpolation-translate-x)]
[gesture/rect-button [gesture/rect-button
{:style (merge {:border-radius style/swipe-button-border-radius} style) {:style (merge {:border-radius style/swipe-button-border-radius} style)
:accessibility-label :notification-swipe-action-button :accessibility-label :notification-swipe-action-button
:on-press (fn [] :on-press (fn []
(when @swipeable-ref (when @swipeable-ref
(.close ^js @swipeable-ref) (oops/ocall @swipeable-ref :close)
(reset! active-swipeable nil)) (reset! active-swipeable nil))
(on-press extra))} (on-press extra))}
[swipe-button [swipe-button
{:style {:opacity opacity {:style {:opacity opacity
:transform [{:translateX translate-x}] :transform [{:translateX translate-x}]
:height height :flex 1
:width style/swipe-action-width}} :width style/swipe-action-width}}
extra]]))) extra]])))
(defn- close-active-swipeable
[active-swipeable swipeable]
(fn [_]
(when (and @active-swipeable
(not= @active-swipeable @swipeable))
(.close ^js @active-swipeable))
(reset! active-swipeable @swipeable)))
(defn swipe-button-container (defn swipe-button-container
[{:keys [style icon text]} _] [{:keys [style icon text]}]
[rn/animated-view [rn/animated-view
{:accessibility-label :notification-swipe {:accessibility-label :notification-swipe
:style style} :style style}
@ -103,47 +97,54 @@
(rf/dispatch [:activity-center.notifications/delete (:id notification)])) (rf/dispatch [:activity-center.notifications/delete (:id notification)]))
(defn swipeable (defn swipeable
[_] [{:keys [extra-fn
(let [swipeable-ref (atom nil)] left-button
(fn [{:keys [active-swipeable left-on-press
extra-fn right-button
left-button right-on-press]}
left-on-press child]
right-button (let [{:keys [active-swipeable]} (ac.context/use-context)
right-on-press]} this-swipeable (rn/use-ref-atom nil)
& children] set-this-swipeable (rn/use-callback #(reset! this-swipeable %)
(into [this-swipeable])
[gesture/swipeable on-swipeable-will-open (rn/use-callback
(merge (fn []
{:ref #(reset! swipeable-ref %) (when (and @active-swipeable
:accessibility-label :notification-swipeable (not= @active-swipeable @this-swipeable))
:friction 2 (oops/ocall @active-swipeable :close))
:on-swipeable-will-open (close-active-swipeable active-swipeable swipeable-ref) (reset! active-swipeable @this-swipeable))
:children-container-style {:padding-horizontal 20}} [@active-swipeable @this-swipeable])]
(when left-button [gesture/swipeable
{:overshoot-left false (cond-> {:ref set-this-swipeable
:left-threshold style/swipe-action-width :accessibility-label :notification-swipeable
:render-left-actions (render-swipe-action :friction 2
{:active-swipeable active-swipeable :on-swipeable-will-open on-swipeable-will-open
:extra-fn extra-fn :children-container-style {:padding-horizontal 20}}
:interpolation-opacity style/left-swipe-opacity-interpolation-js left-button
:interpolation-translate-x (assoc :overshoot-left false
style/left-swipe-translate-x-interpolation-js :left-threshold style/swipe-action-width
:on-press left-on-press :render-left-actions (render-swipe-action
:swipe-button left-button {:active-swipeable active-swipeable
:swipeable-ref swipeable-ref :extra-fn extra-fn
:style {:left 8}})}) :interpolation-opacity style/left-swipe-opacity-interpolation-js
(when right-button :interpolation-translate-x
{:overshoot-right false style/left-swipe-translate-x-interpolation-js
:right-threshold style/swipe-action-width :on-press left-on-press
:render-right-actions (render-swipe-action :swipe-button left-button
{:active-swipeable active-swipeable :swipeable-ref this-swipeable
:extra-fn extra-fn :style {:left style/swipe-button-margin}}))
:interpolation-opacity style/right-swipe-opacity-interpolation-js
:interpolation-translate-x right-button
style/right-swipe-translate-x-interpolation-js (assoc :overshoot-right false
:on-press right-on-press :right-threshold style/swipe-action-width
:swipe-button right-button :render-right-actions (render-swipe-action
:swipeable-ref swipeable-ref {:active-swipeable active-swipeable
:style {:right -8}})}))] :extra-fn extra-fn
children)))) :interpolation-opacity style/right-swipe-opacity-interpolation-js
:interpolation-translate-x
style/right-swipe-translate-x-interpolation-js
:on-press right-on-press
:swipe-button right-button
:swipeable-ref this-swipeable
:style {:right (- style/swipe-button-margin)}})))
child]))

View File

@ -1,6 +1,7 @@
(ns status-im.contexts.shell.activity-center.notification.community-kicked.view (ns status-im.contexts.shell.activity-center.notification.community-kicked.view
(:require (:require
[quo.core :as quo] [quo.core :as quo]
[react-native.core :as rn]
[react-native.gesture :as gesture] [react-native.gesture :as gesture]
[status-im.contexts.shell.activity-center.notification.common.style :as common-style] [status-im.contexts.shell.activity-center.notification.common.style :as common-style]
[status-im.contexts.shell.activity-center.notification.common.view :as common] [status-im.contexts.shell.activity-center.notification.common.view :as common]
@ -9,33 +10,34 @@
[utils.re-frame :as rf])) [utils.re-frame :as rf]))
(defn- swipeable (defn- swipeable
[{:keys [active-swipeable extra-fn]} child] [{:keys [extra-fn]} child]
[common/swipeable [common/swipeable
{:left-button common/swipe-button-read-or-unread {:left-button common/swipe-button-read-or-unread
:left-on-press common/swipe-on-press-toggle-read :left-on-press common/swipe-on-press-toggle-read
:right-button common/swipe-button-delete :right-button common/swipe-button-delete
:right-on-press common/swipe-on-press-delete :right-on-press common/swipe-on-press-delete
:active-swipeable active-swipeable :extra-fn extra-fn}
:extra-fn extra-fn}
child]) child])
(defn view (defn view
[{:keys [notification set-swipeable-height customization-color] :as props}] [{:keys [notification extra-fn]}]
(let [{:keys [community-id read (let [{:keys [id community-id read
timestamp]} notification timestamp]} notification
community (rf/sub [:communities/community community-id]) community (rf/sub [:communities/community community-id])
community-name (:name community) community-name (:name community)
community-image (get-in community [:images :thumbnail :uri])] community-image (get-in community [:images :thumbnail :uri])
[swipeable props customization-color (rf/sub [:profile/customization-color])
[gesture/touchable-without-feedback on-press (rn/use-callback
{:on-press (fn [] (fn []
(rf/dispatch [:navigate-back]) (rf/dispatch [:navigate-back])
(rf/dispatch [:activity-center.notifications/mark-as-read (:id notification)]))} (rf/dispatch [:activity-center.notifications/mark-as-read id]))
[id])]
[swipeable {:extra-fn extra-fn}
[gesture/touchable-without-feedback {:on-press on-press}
[quo/activity-log [quo/activity-log
{:title (i18n/label :t/community-kicked-heading) {:title (i18n/label :t/community-kicked-heading)
:customization-color customization-color :customization-color customization-color
:icon :i/placeholder :icon :i/placeholder
:on-layout set-swipeable-height
:timestamp (datetime/timestamp->relative timestamp) :timestamp (datetime/timestamp->relative timestamp)
:unread? (not read) :unread? (not read)
:context [[quo/text {:style common-style/user-avatar-tag-text} :context [[quo/text {:style common-style/user-avatar-tag-text}

View File

@ -1,6 +1,7 @@
(ns status-im.contexts.shell.activity-center.notification.community-request.view (ns status-im.contexts.shell.activity-center.notification.community-request.view
(:require (:require
[quo.core :as quo] [quo.core :as quo]
[react-native.core :as rn]
[react-native.gesture :as gesture] [react-native.gesture :as gesture]
[status-im.constants :as constants] [status-im.constants :as constants]
[status-im.contexts.shell.activity-center.notification.common.style :as common-style] [status-im.contexts.shell.activity-center.notification.common.style :as common-style]
@ -10,27 +11,23 @@
[utils.re-frame :as rf])) [utils.re-frame :as rf]))
(defn- swipeable (defn- swipeable
[{:keys [active-swipeable extra-fn]} child] [{:keys [extra-fn]} child]
[common/swipeable [common/swipeable
{:left-button common/swipe-button-read-or-unread {:left-button common/swipe-button-read-or-unread
:left-on-press common/swipe-on-press-toggle-read :left-on-press common/swipe-on-press-toggle-read
:right-button common/swipe-button-delete :right-button common/swipe-button-delete
:right-on-press common/swipe-on-press-delete :right-on-press common/swipe-on-press-delete
:active-swipeable active-swipeable :extra-fn extra-fn}
:extra-fn extra-fn}
child]) child])
(defn- get-header-text-and-context (defn- get-header-text-and-context
[community membership-status] [community-logo community-name community-permissions membership-status]
(let [community-name (:name community) (let [open? (not= 3 (:access community-permissions))
permissions (:permissions community)
open? (not= 3 (:access permissions))
community-image (get-in community [:images :thumbnail :uri])
community-context-tag [quo/context-tag community-context-tag [quo/context-tag
{:type :community {:type :community
:size 24 :size 24
:blur? true :blur? true
:community-logo community-image :community-logo community-logo
:community-name community-name}]] :community-name community-name}]]
(cond (cond
(= membership-status constants/activity-center-membership-status-idle) (= membership-status constants/activity-center-membership-status-idle)
@ -56,27 +53,33 @@
:t/joined-community :t/joined-community
:t/community-request-accepted-body-text) :t/community-request-accepted-body-text)
(when open? {:community community-name}))] (when open? {:community community-name}))]
community-context-tag]} community-context-tag]})))
:else nil)))
(defn view (defn view
[{:keys [notification set-swipeable-height customization-color] :as props}] [{:keys [notification extra-fn]}]
(let [{:keys [community-id membership-status read (let [{:keys [community-id membership-status read
timestamp]} notification timestamp]} notification
community (rf/sub [:communities/community community-id]) community-name (rf/sub [:communities/name community-id])
{:keys [header-text context]} (get-header-text-and-context community community-logo (rf/sub [:communities/logo community-id])
membership-status)] community-permissions (rf/sub [:communities/permissions community-id])
[swipeable props customization-color (rf/sub [:profile/customization-color])
[gesture/touchable-without-feedback {:keys [header-text
{:on-press (fn [] context]} (get-header-text-and-context community-logo
(rf/dispatch [:navigate-back]) community-name
(rf/dispatch [:communities/navigate-to-community-overview community-id]))} community-permissions
membership-status)
on-press (rn/use-callback
(fn []
(rf/dispatch [:navigate-back])
(rf/dispatch [:communities/navigate-to-community-overview
community-id]))
[community-id])]
[swipeable {:extra-fn extra-fn}
[gesture/touchable-without-feedback {:on-press on-press}
[quo/activity-log [quo/activity-log
{:title header-text {:title header-text
:customization-color customization-color :customization-color customization-color
:icon :i/communities :icon :i/communities
:on-layout set-swipeable-height
:timestamp (datetime/timestamp->relative timestamp) :timestamp (datetime/timestamp->relative timestamp)
:unread? (not read) :unread? (not read)
:context context}]]])) :context context}]]]))

View File

@ -1,6 +1,7 @@
(ns status-im.contexts.shell.activity-center.notification.contact-requests.view (ns status-im.contexts.shell.activity-center.notification.contact-requests.view
(:require (:require
[quo.core :as quo] [quo.core :as quo]
quo.theme
[react-native.core :as rn] [react-native.core :as rn]
[status-im.constants :as constants] [status-im.constants :as constants]
[status-im.contexts.shell.activity-center.notification.common.style :as common-style] [status-im.contexts.shell.activity-center.notification.common.style :as common-style]
@ -24,48 +25,52 @@
:text (i18n/label :t/decline)}]) :text (i18n/label :t/decline)}])
(defn- swipeable (defn- swipeable
[{:keys [active-swipeable extra-fn notification]} child] [{:keys [extra-fn notification]} child]
(let [{:keys [id author message]} notification (let [{:keys [id author message]} notification
{:keys [contact-request-state]} message {:keys [contact-request-state]} message
{:keys [public-key]} (rf/sub [:multiaccount/contact]) {:keys [public-key]} (rf/sub [:multiaccount/contact])
outgoing? (= public-key author)] outgoing? (= public-key author)
accept (rn/use-callback
#(rf/dispatch [:activity-center.contact-requests/accept id])
[id])
decline (rn/use-callback
#(rf/dispatch [:activity-center.contact-requests/decline id])
[id])]
(cond (cond
(or (#{constants/contact-request-message-state-accepted (or (#{constants/contact-request-message-state-accepted
constants/contact-request-message-state-declined} constants/contact-request-message-state-declined}
contact-request-state) contact-request-state)
(and outgoing? (= contact-request-state constants/contact-request-message-state-pending))) (and outgoing? (= contact-request-state constants/contact-request-message-state-pending)))
[common/swipeable [common/swipeable
{:left-button common/swipe-button-read-or-unread {:left-button common/swipe-button-read-or-unread
:left-on-press common/swipe-on-press-toggle-read :left-on-press common/swipe-on-press-toggle-read
:right-button common/swipe-button-delete :right-button common/swipe-button-delete
:right-on-press common/swipe-on-press-delete :right-on-press common/swipe-on-press-delete
:active-swipeable active-swipeable :extra-fn extra-fn}
:extra-fn extra-fn}
child] child]
(and (= contact-request-state constants/contact-request-message-state-pending) (and (= contact-request-state constants/contact-request-message-state-pending)
(not outgoing?)) (not outgoing?))
[common/swipeable [common/swipeable
{:left-button swipe-button-accept {:left-button swipe-button-accept
:left-on-press #(rf/dispatch [:activity-center.contact-requests/accept id]) :left-on-press accept
:right-button swipe-button-decline :right-button swipe-button-decline
:right-on-press #(rf/dispatch [:activity-center.contact-requests/decline id]) :right-on-press decline
:active-swipeable active-swipeable :extra-fn extra-fn}
:extra-fn extra-fn}
child] child]
:else :else
child))) child)))
(defn- outgoing-contact-request-view (defn- outgoing-contact-request-view
[{:keys [notification set-swipeable-height customization-color]} theme] [{:keys [notification]} theme]
(let [{:keys [chat-id message last-message accepted]} notification (let [{:keys [chat-id message last-message accepted]} notification
{:keys [contact-request-state] :as message} (or message last-message)] {:keys [contact-request-state] :as message} (or message last-message)
customization-color (rf/sub [:profile/customization-color])]
(if accepted (if accepted
[quo/activity-log [quo/activity-log
{:title (i18n/label :t/contact-request-was-accepted) {:title (i18n/label :t/contact-request-was-accepted)
:customization-color customization-color :customization-color customization-color
:on-layout set-swipeable-height
:icon :i/add-user :icon :i/add-user
:timestamp (datetime/timestamp->relative (:timestamp notification)) :timestamp (datetime/timestamp->relative (:timestamp notification))
:unread? (not (:read notification)) :unread? (not (:read notification))
@ -76,7 +81,6 @@
[quo/activity-log [quo/activity-log
{:title (i18n/label :t/contact-request) {:title (i18n/label :t/contact-request)
:customization-color customization-color :customization-color customization-color
:on-layout set-swipeable-height
:icon :i/add-user :icon :i/add-user
:timestamp (datetime/timestamp->relative (:timestamp notification)) :timestamp (datetime/timestamp->relative (:timestamp notification))
:unread? (not (:read notification)) :unread? (not (:read notification))
@ -102,13 +106,22 @@
nil)}]))) nil)}])))
(defn- incoming-contact-request-view (defn- incoming-contact-request-view
[{:keys [notification set-swipeable-height customization-color]} theme] [{:keys [notification]} theme]
(let [{:keys [id author message last-message]} notification (let [{:keys [id author message
message (or message last-message)] last-message]} notification
customization-color (rf/sub [:profile/customization-color])
message (or message last-message)
accept (rn/use-callback
(fn []
(rf/dispatch [:activity-center.contact-requests/accept id]))
[id])
decline (rn/use-callback
(fn []
(rf/dispatch [:activity-center.contact-requests/decline id]))
[id])]
[quo/activity-log [quo/activity-log
{:title (i18n/label :t/contact-request) {:title (i18n/label :t/contact-request)
:customization-color customization-color :customization-color customization-color
:on-layout set-swipeable-height
:icon :i/add-user :icon :i/add-user
:timestamp (datetime/timestamp->relative (:timestamp notification)) :timestamp (datetime/timestamp->relative (:timestamp notification))
:unread? (not (:read notification)) :unread? (not (:read notification))
@ -140,13 +153,13 @@
:key :button-decline :key :button-decline
:label (i18n/label :t/decline) :label (i18n/label :t/decline)
:accessibility-label :decline-contact-request :accessibility-label :decline-contact-request
:on-press #(rf/dispatch [:activity-center.contact-requests/decline id])} :on-press decline}
{:type :button {:type :button
:subtype :positive :subtype :positive
:key :button-accept :key :button-accept
:label (i18n/label :t/accept) :label (i18n/label :t/accept)
:accessibility-label :accept-contact-request :accessibility-label :accept-contact-request
:on-press #(rf/dispatch [:activity-center.contact-requests/accept id])}] :on-press accept}]
nil)}])) nil)}]))
@ -155,16 +168,16 @@
(let [{:keys [author message last-message]} notification (let [{:keys [author message last-message]} notification
{:keys [public-key]} (rf/sub [:multiaccount/contact]) {:keys [public-key]} (rf/sub [:multiaccount/contact])
{:keys [contact-request-state]} (or message last-message) {:keys [contact-request-state]} (or message last-message)
app-theme (rf/sub [:theme])] theme (quo.theme/use-theme)]
[swipeable props [swipeable props
(cond (cond
(= public-key author) (= public-key author)
[outgoing-contact-request-view props app-theme] [outgoing-contact-request-view props theme]
(= contact-request-state constants/contact-request-message-state-accepted) (= contact-request-state constants/contact-request-message-state-accepted)
[rn/pressable [rn/pressable
{:on-press #(rf/dispatch [:chat.ui/start-chat author])} {:on-press #(rf/dispatch [:chat.ui/start-chat author])}
[incoming-contact-request-view props app-theme]] [incoming-contact-request-view props theme]]
:else :else
[incoming-contact-request-view props app-theme])])) [incoming-contact-request-view props theme])]))

View File

@ -105,7 +105,7 @@
(rf/dispatch [:activity-center.notifications/mark-as-read id])) (rf/dispatch [:activity-center.notifications/mark-as-read id]))
(defn- swipeable (defn- swipeable
[{:keys [active-swipeable extra-fn notification replying?] :as props} child] [{:keys [extra-fn notification replying?] :as props} child]
(let [{:keys [id message (let [{:keys [id message
contact-verification-status]} notification contact-verification-status]} notification
challenger? (:outgoing message)] challenger? (:outgoing message)]
@ -116,23 +116,21 @@
(and (not challenger?) (and (not challenger?)
(= contact-verification-status constants/contact-verification-status-pending)) (= contact-verification-status constants/contact-verification-status-pending))
[common/swipeable [common/swipeable
{:left-button swipe-button-reply {:left-button swipe-button-reply
:left-on-press #(prepare-challenge-reply props) :left-on-press #(prepare-challenge-reply props)
:right-button swipe-button-decline :right-button swipe-button-decline
:right-on-press #(decline-challenge id) :right-on-press #(decline-challenge id)
:active-swipeable active-swipeable :extra-fn extra-fn}
:extra-fn extra-fn}
child] child]
(and challenger? (and challenger?
(= contact-verification-status constants/contact-verification-status-accepted)) (= contact-verification-status constants/contact-verification-status-accepted))
[common/swipeable [common/swipeable
{:left-button swipe-button-trust {:left-button swipe-button-trust
:left-on-press #(mark-challenge-trusted id) :left-on-press #(mark-challenge-trusted id)
:right-button swipe-button-untrustworthy :right-button swipe-button-untrustworthy
:right-on-press #(mark-challenge-untrustworthy id) :right-on-press #(mark-challenge-untrustworthy id)
:active-swipeable active-swipeable :extra-fn extra-fn}
:extra-fn extra-fn}
child] child]
(#{constants/contact-verification-status-accepted (#{constants/contact-verification-status-accepted
@ -140,12 +138,11 @@
constants/contact-verification-status-trusted} constants/contact-verification-status-trusted}
contact-verification-status) contact-verification-status)
[common/swipeable [common/swipeable
{:left-button common/swipe-button-read-or-unread {:left-button common/swipe-button-read-or-unread
:left-on-press common/swipe-on-press-toggle-read :left-on-press common/swipe-on-press-toggle-read
:right-button common/swipe-button-delete :right-button common/swipe-button-delete
:right-on-press common/swipe-on-press-delete :right-on-press common/swipe-on-press-delete
:active-swipeable active-swipeable :extra-fn extra-fn}
:extra-fn extra-fn}
child] child]
:else :else
@ -154,8 +151,9 @@
(defn view (defn view
[_] [_]
(let [reply (atom "")] (let [reply (atom "")]
(fn [{:keys [notification set-swipeable-height replying? customization-color] :as props}] (fn [{:keys [notification replying?] :as props}]
(let [{:keys [id message (let [customization-color (rf/sub [:profile/customization-color])
{:keys [id message
contact-verification-status]} notification contact-verification-status]} notification
challenger? (:outgoing message)] challenger? (:outgoing message)]
;; TODO(@ilmotta): Declined challenges should only be displayed for the challengee, not the ;; TODO(@ilmotta): Declined challenges should only be displayed for the challengee, not the
@ -165,86 +163,84 @@
(= contact-verification-status constants/contact-verification-status-declined)) (= contact-verification-status constants/contact-verification-status-declined))
[swipeable props [swipeable props
[quo/activity-log [quo/activity-log
(merge {:title (i18n/label :t/identity-verification-request)
(when-not replying? :customization-color customization-color
{:on-layout set-swipeable-height}) :icon :i/friend
{:title (i18n/label :t/identity-verification-request) :timestamp (datetime/timestamp->relative (:timestamp notification))
:customization-color customization-color :unread? (not (:read notification))
:icon :i/friend :on-update-reply #(reset! reply %)
:timestamp (datetime/timestamp->relative (:timestamp notification)) :replying? replying?
:unread? (not (:read notification)) :max-reply-length max-reply-length
:on-update-reply #(reset! reply %) :valid-reply? valid-reply?
:replying? replying? :context (context-tags challenger? notification)
:max-reply-length max-reply-length :message (activity-message challenger? notification)
:valid-reply? valid-reply? :items
:context (context-tags challenger? notification) (cond-> []
:message (activity-message challenger? notification) (and challenger?
:items (= contact-verification-status constants/contact-verification-status-accepted))
(cond-> [] (concat
(and challenger? [{:type :button
(= contact-verification-status constants/contact-verification-status-accepted)) :subtype :danger
(concat :key :button-mark-as-untrustworthy
[{:type :button :label (i18n/label :t/untrustworthy)
:subtype :danger :accessibility-label :mark-contact-verification-as-untrustworthy
:key :button-mark-as-untrustworthy :on-press #(mark-challenge-untrustworthy id)}
:label (i18n/label :t/untrustworthy) {:type :button
:accessibility-label :mark-contact-verification-as-untrustworthy :subtype :positive
:on-press #(mark-challenge-untrustworthy id)} :key :button-accept
{:type :button :label (i18n/label :t/accept)
:subtype :positive :accessibility-label :mark-contact-verification-as-trusted
:key :button-accept :on-press #(mark-challenge-trusted id)}])
:label (i18n/label :t/accept)
:accessibility-label :mark-contact-verification-as-trusted
:on-press #(mark-challenge-trusted id)}])
(and challenger? (and challenger?
(= contact-verification-status constants/contact-verification-status-trusted)) (= contact-verification-status constants/contact-verification-status-trusted))
(concat [{:type :status (concat [{:type :status
:subtype :positive :subtype :positive
:key :status-trusted :key :status-trusted
:label (i18n/label :t/status-confirmed)}]) :label (i18n/label :t/status-confirmed)}])
(and challenger? (and challenger?
(= contact-verification-status constants/contact-verification-status-untrustworthy)) (= contact-verification-status
(concat [{:type :status constants/contact-verification-status-untrustworthy))
:subtype :negative (concat [{:type :status
:key :status-untrustworthy :subtype :negative
:label (i18n/label :t/untrustworthy)}]) :key :status-untrustworthy
:label (i18n/label :t/untrustworthy)}])
(and (not challenger?) (and (not challenger?)
(= contact-verification-status constants/contact-verification-status-accepted)) (= contact-verification-status constants/contact-verification-status-accepted))
(concat [{:type :status (concat [{:type :status
:subtype :positive :subtype :positive
:key :status-accepted :key :status-accepted
:label (i18n/label :t/replied)}]) :label (i18n/label :t/replied)}])
(and (not challenger?) (and (not challenger?)
(= contact-verification-status constants/contact-verification-status-declined)) (= contact-verification-status constants/contact-verification-status-declined))
(concat [{:type :status (concat [{:type :status
:subtype :negative :subtype :negative
:key :status-declined :key :status-declined
:label (i18n/label :t/declined)}]) :label (i18n/label :t/declined)}])
(and (not challenger?) (and (not challenger?)
(= contact-verification-status constants/contact-verification-status-pending)) (= contact-verification-status constants/contact-verification-status-pending))
(concat (concat
[{:type :button [{:type :button
:subtype :danger :subtype :danger
:key :button-decline :key :button-decline
:label (i18n/label :t/decline) :label (i18n/label :t/decline)
:accessibility-label :decline-contact-verification :accessibility-label :decline-contact-verification
:on-press #(decline-challenge id)} :on-press #(decline-challenge id)}
(if replying? (if replying?
{:type :button {:type :button
:subtype :primary :subtype :primary
:key :button-reply :key :button-reply
:label (i18n/label :t/send-reply) :label (i18n/label :t/send-reply)
:accessibility-label :reply-to-contact-verification :accessibility-label :reply-to-contact-verification
:disable-when invalid-reply? :disable-when invalid-reply?
:on-press #(send-challenge-reply id @reply)} :on-press #(send-challenge-reply id @reply)}
{:type :button {:type :button
:subtype :primary :subtype :primary
:key :button-send-reply :key :button-send-reply
:label (i18n/label :t/message-reply) :label (i18n/label :t/message-reply)
:accessibility-label :send-reply-to-contact-verification :accessibility-label :send-reply-to-contact-verification
:on-press #(prepare-challenge-reply props)})]))})]]))))) :on-press #(prepare-challenge-reply props)})]))}]])))))

View File

@ -1,6 +1,7 @@
(ns status-im.contexts.shell.activity-center.notification.membership.view (ns status-im.contexts.shell.activity-center.notification.membership.view
(:require (:require
[quo.core :as quo] [quo.core :as quo]
[react-native.core :as rn]
[react-native.gesture :as gesture] [react-native.gesture :as gesture]
[status-im.contexts.shell.activity-center.notification.common.style :as common-style] [status-im.contexts.shell.activity-center.notification.common.style :as common-style]
[status-im.contexts.shell.activity-center.notification.common.view :as common] [status-im.contexts.shell.activity-center.notification.common.view :as common]
@ -33,35 +34,50 @@
:text (i18n/label :t/decline)}]) :text (i18n/label :t/decline)}])
(defn- swipeable (defn- swipeable
[{:keys [active-swipeable notification extra-fn]} child] [{:keys [notification extra-fn]} child]
(let [{:keys [accepted dismissed id]} notification] (let [{:keys [accepted dismissed
id]} notification
accept (rn/use-callback
(fn [] (rf/dispatch [:activity-center.notifications/accept id]))
[id])
dismiss (rn/use-callback
(fn [] (rf/dispatch [:activity-center.notifications/dismiss id]))
[id])]
(if (or accepted dismissed) (if (or accepted dismissed)
[common/swipeable [common/swipeable
{:left-button common/swipe-button-read-or-unread {:left-button common/swipe-button-read-or-unread
:left-on-press common/swipe-on-press-toggle-read :left-on-press common/swipe-on-press-toggle-read
:right-button common/swipe-button-delete :right-button common/swipe-button-delete
:right-on-press common/swipe-on-press-delete :right-on-press common/swipe-on-press-delete
:active-swipeable active-swipeable :extra-fn extra-fn}
:extra-fn extra-fn}
child] child]
[common/swipeable [common/swipeable
{:left-button swipe-button-accept {:left-button swipe-button-accept
:left-on-press #(rf/dispatch [:activity-center.notifications/accept id]) :left-on-press accept
:right-button swipe-button-decline :right-button swipe-button-decline
:right-on-press #(rf/dispatch [:activity-center.notifications/dismiss id]) :right-on-press dismiss
:active-swipeable active-swipeable :extra-fn extra-fn}
:extra-fn extra-fn}
child]))) child])))
(defn view (defn view
[{:keys [notification set-swipeable-height customization-color] :as props}] [{:keys [notification] :as props}]
(let [{:keys [id accepted dismissed author read timestamp chat-name chat-id]} notification] (let [{:keys [id accepted dismissed author read
timestamp chat-name
chat-id]} notification
customization-color (rf/sub [:profile/customization-color])
accept (rn/use-callback
(fn []
(rf/dispatch [:activity-center.notifications/accept id]))
[id])
dismiss (rn/use-callback
(fn []
(rf/dispatch [:activity-center.notifications/dismiss id]))
[id])]
[swipeable props [swipeable props
[pressable {:accepted accepted :chat-id chat-id} [pressable {:accepted accepted :chat-id chat-id}
[quo/activity-log [quo/activity-log
{:title (i18n/label :t/added-to-group-chat) {:title (i18n/label :t/added-to-group-chat)
:customization-color customization-color :customization-color customization-color
:on-layout set-swipeable-height
:icon :i/add-user :icon :i/add-user
:timestamp (datetime/timestamp->relative timestamp) :timestamp (datetime/timestamp->relative timestamp)
:unread? (not read) :unread? (not read)
@ -78,13 +94,10 @@
:key :button-accept :key :button-accept
:label (i18n/label :t/accept) :label (i18n/label :t/accept)
:accessibility-label :accept-group-chat-invitation :accessibility-label :accept-group-chat-invitation
:on-press #(rf/dispatch :on-press accept}
[:activity-center.notifications/accept id])}
{:type :button {:type :button
:subtype :danger :subtype :danger
:key :button-decline :key :button-decline
:label (i18n/label :t/decline) :label (i18n/label :t/decline)
:accessibility-label :decline-group-chat-invitation :accessibility-label :decline-group-chat-invitation
:on-press #(rf/dispatch :on-press dismiss}])}]]]))
[:activity-center.notifications/dismiss
id])}])}]]]))

View File

@ -30,25 +30,24 @@
parsed-text-children)))) parsed-text-children))))
(defn- swipeable (defn- swipeable
[{:keys [active-swipeable extra-fn]} child] [{:keys [extra-fn]} child]
[common/swipeable [common/swipeable
{:left-button common/swipe-button-read-or-unread {:left-button common/swipe-button-read-or-unread
:left-on-press common/swipe-on-press-toggle-read :left-on-press common/swipe-on-press-toggle-read
:right-button common/swipe-button-delete :right-button common/swipe-button-delete
:right-on-press common/swipe-on-press-delete :right-on-press common/swipe-on-press-delete
:active-swipeable active-swipeable :extra-fn extra-fn}
:extra-fn extra-fn}
child]) child])
(defn view (defn view
[{:keys [notification set-swipeable-height customization-color] :as props}] [{:keys [notification extra-fn]}]
(let [{:keys [author chat-name community-id chat-id (let [{:keys [author chat-name community-id chat-id
message read timestamp]} notification message read timestamp]} notification
community-chat? (not (string/blank? community-id)) community-chat? (not (string/blank? community-id))
community (rf/sub [:communities/community community-id]) community-name (rf/sub [:communities/name community-id])
community-name (:name community) community-logo (rf/sub [:communities/logo community-id])
community-image (get-in community [:images :thumbnail :uri])] customization-color (rf/sub [:profile/customization-color])]
[swipeable props [swipeable {:extra-fn extra-fn}
[gesture/touchable-without-feedback [gesture/touchable-without-feedback
{:on-press (fn [] {:on-press (fn []
(rf/dispatch [:hide-popover]) (rf/dispatch [:hide-popover])
@ -56,7 +55,6 @@
[quo/activity-log [quo/activity-log
{:title (i18n/label :t/mention) {:title (i18n/label :t/mention)
:customization-color customization-color :customization-color customization-color
:on-layout set-swipeable-height
:icon :i/mention :icon :i/mention
:timestamp (datetime/timestamp->relative timestamp) :timestamp (datetime/timestamp->relative timestamp)
:unread? (not read) :unread? (not read)
@ -68,7 +66,7 @@
{:type :channel {:type :channel
:blur? true :blur? true
:size 24 :size 24
:community-logo community-image :community-logo community-logo
:community-name community-name :community-name community-name
:channel-name chat-name}] :channel-name chat-name}]
[quo/context-tag [quo/context-tag

View File

@ -51,26 +51,26 @@
nil)) nil))
(defn- swipeable (defn- swipeable
[{:keys [active-swipeable extra-fn]} child] [{:keys [extra-fn]} child]
[common/swipeable [common/swipeable
{:left-button common/swipe-button-read-or-unread {:left-button common/swipe-button-read-or-unread
:left-on-press common/swipe-on-press-toggle-read :left-on-press common/swipe-on-press-toggle-read
:right-button common/swipe-button-delete :right-button common/swipe-button-delete
:right-on-press common/swipe-on-press-delete :right-on-press common/swipe-on-press-delete
:active-swipeable active-swipeable :extra-fn extra-fn}
:extra-fn extra-fn}
child]) child])
(defn view (defn view
[{:keys [notification set-swipeable-height customization-color] :as props}] [{:keys [notification extra-fn]}]
(let [{:keys [author chat-name community-id chat-id (let [{:keys [author chat-name community-id chat-id
message read timestamp album-messages]} notification message read timestamp
community-chat? (not (string/blank? community-id)) album-messages]} notification
community (rf/sub [:communities/community community-id]) community-chat? (not (string/blank? community-id))
community-name (:name community) community-name (rf/sub [:communities/name community-id])
community-image (get-in community [:images :thumbnail :uri]) community-logo (rf/sub [:communities/logo community-id])
media-server-port (rf/sub [:mediaserver/port])] customization-color (rf/sub [:profile/customization-color])
[swipeable props media-server-port (rf/sub [:mediaserver/port])]
[swipeable {:extra-fn extra-fn}
[gesture/touchable-without-feedback [gesture/touchable-without-feedback
{:on-press (fn [] {:on-press (fn []
(rf/dispatch [:hide-popover]) (rf/dispatch [:hide-popover])
@ -78,7 +78,6 @@
[quo/activity-log [quo/activity-log
{:title (i18n/label :t/message-reply) {:title (i18n/label :t/message-reply)
:customization-color customization-color :customization-color customization-color
:on-layout set-swipeable-height
:icon :i/reply :icon :i/reply
:timestamp (datetime/timestamp->relative timestamp) :timestamp (datetime/timestamp->relative timestamp)
:unread? (not read) :unread? (not read)
@ -89,7 +88,7 @@
{:type :channel {:type :channel
:blur? true :blur? true
:size 24 :size 24
:community-logo community-image :community-logo community-logo
:community-name community-name :community-name community-name
:channel-name chat-name}] :channel-name chat-name}]
[quo/context-tag [quo/context-tag
@ -113,10 +112,7 @@
(= (:content-type message) (= (:content-type message)
constants/content-type-gif) constants/content-type-gif)
:gif :gif)
:else
nil)
:body (get-message-content message :body (get-message-content message
album-messages album-messages
media-server-port)}}]]])) media-server-port)}}]]]))

View File

@ -1,9 +1,10 @@
(ns status-im.contexts.shell.activity-center.view (ns status-im.contexts.shell.activity-center.view
(:require (:require
[oops.core :as oops]
[quo.core :as quo] [quo.core :as quo]
[react-native.core :as rn] [react-native.core :as rn]
[react-native.navigation :as navigation] [react-native.navigation :as navigation]
[status-im.constants :as constants]
[status-im.contexts.shell.activity-center.context :as ac.context]
[status-im.contexts.shell.activity-center.header.view :as header] [status-im.contexts.shell.activity-center.header.view :as header]
[status-im.contexts.shell.activity-center.notification-types :as types] [status-im.contexts.shell.activity-center.notification-types :as types]
[status-im.contexts.shell.activity-center.notification.admin.view :as admin] [status-im.contexts.shell.activity-center.notification.admin.view :as admin]
@ -23,61 +24,68 @@
[utils.re-frame :as rf])) [utils.re-frame :as rf]))
(defn notification-component (defn notification-component
[{:keys [type] :as notification} index]
(let [extra-fn (rn/use-callback
(fn []
{:notification notification})
[notification])
props {:notification notification
:extra-fn extra-fn}]
;; Notifications are expensive to render. Without `delay-render` the opening
;; animation of the Activity Center can be clunky and the time to open the
;; AC after pressing the bell icon can be high.
[rn/view {:style (style/notification-container index)}
(cond
(= type types/contact-verification)
[contact-verification/view props]
(= type types/contact-request)
[contact-requests/view props]
(= type types/mention)
[mentions/view props]
(= type types/reply)
[reply/view props]
(= type types/admin)
[admin/view props]
(some types/membership [type])
(condp = type
types/private-group-chat [membership/view props]
types/community-request [community-request/view props]
types/community-kicked [community-kicked/view props]
nil))]))
(defn- fetch-next-page
[] []
(let [height (atom 0) (rf/dispatch [:activity-center.notifications/fetch-next-page]))
set-swipeable-height #(reset! height (oops/oget % "nativeEvent.layout.height"))]
(fn [{:keys [type] :as notification} index _ {:keys [active-swipeable customization-color]}]
(let [props {:height height
:customization-color customization-color
:active-swipeable active-swipeable
:set-swipeable-height set-swipeable-height
:notification notification
:extra-fn (fn [] {:height @height :notification notification})}]
[rn/view {:style (style/notification-container index)}
(cond
(= type types/contact-verification)
[contact-verification/view props]
(= type types/contact-request)
[contact-requests/view props]
(= type types/mention)
[mentions/view props]
(= type types/reply)
[reply/view props]
(= type types/admin)
[admin/view props]
(some types/membership [type])
(condp = type
types/private-group-chat [membership/view props]
types/community-request [community-request/view props]
types/community-kicked [community-kicked/view props]
nil)
:else
nil)]))))
(defn view (defn view
[] []
(let [active-swipeable (atom nil)] (let [notifications (rf/sub [:activity-center/notifications])
(rf/dispatch [:activity-center.notifications/fetch-first-page])
(fn [] ;; We globally control the active swipeable for all notifications
(let [notifications (rf/sub [:activity-center/notifications]) ;; because when a swipe left/right gesture initiates, the previously
customization-color (rf/sub [:profile/customization-color])] ;; active swiped notification (if any) must be removed & closed with
[quo/overlay {:type :shell} ;; animation.
[rn/view {:flex 1 :padding-top (navigation/status-bar-height)} active-swipeable (rn/use-ref-atom nil)]
[header/header] (rn/use-mount
[rn/flat-list (fn []
{:data notifications (rf/dispatch [:activity-center.notifications/fetch-first-page])))
:render-data {:active-swipeable active-swipeable
:customization-color customization-color} [ac.context/provider {:active-swipeable active-swipeable}
:content-container-style {:flex-grow 1} [quo/overlay {:type :shell}
:empty-component [empty-tab/empty-tab] [rn/view {:style {:flex 1 :padding-top (navigation/status-bar-height)}}
:key-fn :id [header/header]
:on-scroll-to-index-failed identity (rn/delay-render
:on-end-reached #(rf/dispatch [:activity-center.notifications/fetch-next-page]) [rn/flat-list
:render-fn notification-component}]] {:data notifications
])))) :initial-num-to-render constants/notifications-per-page
:content-container-style {:flex-grow 1}
:empty-component [empty-tab/empty-tab]
:key-fn :id
:on-scroll-to-index-failed identity
:on-end-reached fetch-next-page
:render-fn notification-component}])]]]))

View File

@ -16,12 +16,33 @@
(fn [info [_ id]] (fn [info [_ id]]
(get info id))) (get info id)))
;; Do not use this subscription directly in views. There is a significant risk
;; of re-rendering views too frequently because an active community can change
;; for numerous reasons.
(re-frame/reg-sub (re-frame/reg-sub
:communities/community :communities/community
:<- [:communities] :<- [:communities]
(fn [communities [_ id]] (fn [communities [_ id]]
(get communities id))) (get communities id)))
(re-frame/reg-sub :communities/logo
(fn [[_ community-id]]
[(re-frame/subscribe [:communities/community community-id])])
(fn [[community]]
(get-in community [:images :thumbnail :uri])))
(re-frame/reg-sub :communities/name
(fn [[_ community-id]]
[(re-frame/subscribe [:communities/community community-id])])
(fn [[{:keys [name]}]]
name))
(re-frame/reg-sub :communities/permissions
(fn [[_ community-id]]
[(re-frame/subscribe [:communities/community community-id])])
(fn [[{:keys [permissions]}]]
permissions))
(re-frame/reg-sub (re-frame/reg-sub
:communities/community-color :communities/community-color
(fn [[_ community-id]] (fn [[_ community-id]]