diff --git a/src/status_im/constants.cljs b/src/status_im/constants.cljs index 7c92dccf2b..f9aad3c896 100644 --- a/src/status_im/constants.cljs +++ b/src/status_im/constants.cljs @@ -45,6 +45,11 @@ (def ^:const activity-center-membership-status-accepted 2) (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-1-hour-type 2) (def ^:const mute-for-8-hours-type 3) diff --git a/src/status_im/contexts/shell/activity_center/context.cljs b/src/status_im/contexts/shell/activity_center/context.cljs new file mode 100644 index 0000000000..696708c438 --- /dev/null +++ b/src/status_im/contexts/shell/activity_center/context.cljs @@ -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)})) diff --git a/src/status_im/contexts/shell/activity_center/events.cljs b/src/status_im/contexts/shell/activity_center/events.cljs index 34c1cc832f..d68f16bdd4 100644 --- a/src/status_im/contexts/shell/activity_center/events.cljs +++ b/src/status_im/contexts/shell/activity_center/events.cljs @@ -15,10 +15,7 @@ (def defaults {:filter-status :unread :filter-type types/no-type - ;; Choose the maximum number of notifications that *usually/safely* fit on - ;; most screens, so that the UI doesn't have to needlessly render - ;; notifications. - :notifications-per-page 8}) + :notifications-per-page constants/notifications-per-page}) ;;;; Navigation diff --git a/src/status_im/contexts/shell/activity_center/notification/admin/view.cljs b/src/status_im/contexts/shell/activity_center/notification/admin/view.cljs index 7bfe39151e..c06633e7f8 100644 --- a/src/status_im/contexts/shell/activity_center/notification/admin/view.cljs +++ b/src/status_im/contexts/shell/activity_center/notification/admin/view.cljs @@ -1,6 +1,7 @@ (ns status-im.contexts.shell.activity-center.notification.admin.view (:require [quo.core :as quo] + [react-native.core :as rn] [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.view :as common] @@ -23,42 +24,50 @@ :text (i18n/label :t/decline)}]) (defn- swipeable - [{:keys [active-swipeable notification extra-fn]} child] - (let [{:keys [community-id id membership-status]} notification] + [{:keys [notification extra-fn]} child] + (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 (#{constants/activity-center-membership-status-accepted constants/activity-center-membership-status-declined} membership-status) [common/swipeable - {:left-button common/swipe-button-read-or-unread - :left-on-press common/swipe-on-press-toggle-read - :right-button common/swipe-button-delete - :right-on-press common/swipe-on-press-delete - :active-swipeable active-swipeable - :extra-fn extra-fn} + {:left-button common/swipe-button-read-or-unread + :left-on-press common/swipe-on-press-toggle-read + :right-button common/swipe-button-delete + :right-on-press common/swipe-on-press-delete + :extra-fn extra-fn} child] (= membership-status constants/activity-center-membership-status-pending) [common/swipeable - {:left-button swipe-button-accept - :left-on-press #(rf/dispatch [:communities/accept-request-to-join-pressed community-id id]) - :right-button swipe-button-decline - :right-on-press #(rf/dispatch [:communities/decline-request-to-join-pressed community-id - id]) - :active-swipeable active-swipeable - :extra-fn extra-fn} + {:left-button swipe-button-accept + :left-on-press accept + :right-button swipe-button-decline + :right-on-press decline + :extra-fn extra-fn} child] :else child))) (defn view - [{:keys [notification set-swipeable-height customization-color] :as props}] + [{:keys [notification] :as props}] (let [{:keys [author community-id id membership-status read timestamp]} notification - community (rf/sub [:communities/community community-id]) - community-name (:name community) - community-image (get-in community [:images :thumbnail :uri])] + community-name (rf/sub [:communities/name community-id]) + community-logo (rf/sub [:communities/logo community-id]) + customization-color (rf/sub [:profile/customization-color])] [swipeable props [quo/activity-log {:title (i18n/label :t/join-request) @@ -66,14 +75,13 @@ :icon :i/add-user :timestamp (datetime/timestamp->relative timestamp) :unread? (not read) - :on-layout set-swipeable-height :context [[common/user-avatar-tag author] (i18n/label :t/wants-to-join) [quo/context-tag {:type :community :size 24 :blur? true - :community-logo community-image + :community-logo community-logo :community-name community-name}]] :items (condp = membership-status constants/activity-center-membership-status-accepted diff --git a/src/status_im/contexts/shell/activity_center/notification/common/style.cljs b/src/status_im/contexts/shell/activity_center/notification/common/style.cljs index 1a54e11691..b1ce7a44da 100644 --- a/src/status_im/contexts/shell/activity_center/notification/common/style.cljs +++ b/src/status_im/contexts/shell/activity_center/notification/common/style.cljs @@ -4,6 +4,7 @@ (def swipe-action-width 80) (def swipe-button-border-radius 16) +(def swipe-button-margin 8) (def user-avatar-tag {:background-color colors/white-opa-10}) diff --git a/src/status_im/contexts/shell/activity_center/notification/common/view.cljs b/src/status_im/contexts/shell/activity_center/notification/common/view.cljs index 245f34344b..8b0508ee8e 100644 --- a/src/status_im/contexts/shell/activity_center/notification/common/view.cljs +++ b/src/status_im/contexts/shell/activity_center/notification/common/view.cljs @@ -1,10 +1,12 @@ (ns status-im.contexts.shell.activity-center.notification.common.view (:require + [oops.core :as oops] [quo.core :as quo] [quo.foundations.colors :as colors] [react-native.core :as rn] [react-native.gesture :as gesture] [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] [utils.i18n :as i18n] [utils.re-frame :as rf])) @@ -14,7 +16,7 @@ (let [profile (rf/sub [:contacts/contact-by-identity user-id])] [rn/view {:on-start-should-set-responder - (fn [_event] + (fn [] (rf/dispatch [:navigate-back]) (rf/dispatch [:chat.ui/show-profile user-id]) true)} @@ -33,35 +35,27 @@ swipe-button swipeable-ref style]}] - (fn [_ ^js drag-x] - (let [{:keys [height] :as extra} (extra-fn) - opacity (.interpolate drag-x interpolation-opacity) - translate-x (.interpolate drag-x interpolation-translate-x)] + (fn [_ drag-x] + (let [extra (extra-fn) + opacity (oops/ocall drag-x :interpolate interpolation-opacity) + translate-x (oops/ocall drag-x :interpolate interpolation-translate-x)] [gesture/rect-button {:style (merge {:border-radius style/swipe-button-border-radius} style) :accessibility-label :notification-swipe-action-button :on-press (fn [] (when @swipeable-ref - (.close ^js @swipeable-ref) + (oops/ocall @swipeable-ref :close) (reset! active-swipeable nil)) (on-press extra))} [swipe-button {:style {:opacity opacity :transform [{:translateX translate-x}] - :height height + :flex 1 :width style/swipe-action-width}} 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 - [{:keys [style icon text]} _] + [{:keys [style icon text]}] [rn/animated-view {:accessibility-label :notification-swipe :style style} @@ -103,47 +97,54 @@ (rf/dispatch [:activity-center.notifications/delete (:id notification)])) (defn swipeable - [_] - (let [swipeable-ref (atom nil)] - (fn [{:keys [active-swipeable - extra-fn - left-button - left-on-press - right-button - right-on-press]} - & children] - (into - [gesture/swipeable - (merge - {:ref #(reset! swipeable-ref %) - :accessibility-label :notification-swipeable - :friction 2 - :on-swipeable-will-open (close-active-swipeable active-swipeable swipeable-ref) - :children-container-style {:padding-horizontal 20}} - (when left-button - {:overshoot-left false - :left-threshold style/swipe-action-width - :render-left-actions (render-swipe-action - {:active-swipeable active-swipeable - :extra-fn extra-fn - :interpolation-opacity style/left-swipe-opacity-interpolation-js - :interpolation-translate-x - style/left-swipe-translate-x-interpolation-js - :on-press left-on-press - :swipe-button left-button - :swipeable-ref swipeable-ref - :style {:left 8}})}) - (when right-button - {:overshoot-right false - :right-threshold style/swipe-action-width - :render-right-actions (render-swipe-action - {:active-swipeable active-swipeable - :extra-fn extra-fn - :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 swipeable-ref - :style {:right -8}})}))] - children)))) + [{:keys [extra-fn + left-button + left-on-press + right-button + right-on-press]} + child] + (let [{:keys [active-swipeable]} (ac.context/use-context) + this-swipeable (rn/use-ref-atom nil) + set-this-swipeable (rn/use-callback #(reset! this-swipeable %) + [this-swipeable]) + on-swipeable-will-open (rn/use-callback + (fn [] + (when (and @active-swipeable + (not= @active-swipeable @this-swipeable)) + (oops/ocall @active-swipeable :close)) + (reset! active-swipeable @this-swipeable)) + [@active-swipeable @this-swipeable])] + [gesture/swipeable + (cond-> {:ref set-this-swipeable + :accessibility-label :notification-swipeable + :friction 2 + :on-swipeable-will-open on-swipeable-will-open + :children-container-style {:padding-horizontal 20}} + left-button + (assoc :overshoot-left false + :left-threshold style/swipe-action-width + :render-left-actions (render-swipe-action + {:active-swipeable active-swipeable + :extra-fn extra-fn + :interpolation-opacity style/left-swipe-opacity-interpolation-js + :interpolation-translate-x + style/left-swipe-translate-x-interpolation-js + :on-press left-on-press + :swipe-button left-button + :swipeable-ref this-swipeable + :style {:left style/swipe-button-margin}})) + + right-button + (assoc :overshoot-right false + :right-threshold style/swipe-action-width + :render-right-actions (render-swipe-action + {:active-swipeable active-swipeable + :extra-fn extra-fn + :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])) diff --git a/src/status_im/contexts/shell/activity_center/notification/community_kicked/view.cljs b/src/status_im/contexts/shell/activity_center/notification/community_kicked/view.cljs index 0fc0021d6a..598d2ba094 100644 --- a/src/status_im/contexts/shell/activity_center/notification/community_kicked/view.cljs +++ b/src/status_im/contexts/shell/activity_center/notification/community_kicked/view.cljs @@ -1,6 +1,7 @@ (ns status-im.contexts.shell.activity-center.notification.community-kicked.view (:require [quo.core :as quo] + [react-native.core :as rn] [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.view :as common] @@ -9,33 +10,34 @@ [utils.re-frame :as rf])) (defn- swipeable - [{:keys [active-swipeable extra-fn]} child] + [{:keys [extra-fn]} child] [common/swipeable - {:left-button common/swipe-button-read-or-unread - :left-on-press common/swipe-on-press-toggle-read - :right-button common/swipe-button-delete - :right-on-press common/swipe-on-press-delete - :active-swipeable active-swipeable - :extra-fn extra-fn} + {:left-button common/swipe-button-read-or-unread + :left-on-press common/swipe-on-press-toggle-read + :right-button common/swipe-button-delete + :right-on-press common/swipe-on-press-delete + :extra-fn extra-fn} child]) (defn view - [{:keys [notification set-swipeable-height customization-color] :as props}] - (let [{:keys [community-id read + [{:keys [notification extra-fn]}] + (let [{:keys [id community-id read timestamp]} notification community (rf/sub [:communities/community community-id]) community-name (:name community) - community-image (get-in community [:images :thumbnail :uri])] - [swipeable props - [gesture/touchable-without-feedback - {:on-press (fn [] - (rf/dispatch [:navigate-back]) - (rf/dispatch [:activity-center.notifications/mark-as-read (:id notification)]))} + community-image (get-in community [:images :thumbnail :uri]) + customization-color (rf/sub [:profile/customization-color]) + on-press (rn/use-callback + (fn [] + (rf/dispatch [:navigate-back]) + (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 {:title (i18n/label :t/community-kicked-heading) :customization-color customization-color :icon :i/placeholder - :on-layout set-swipeable-height :timestamp (datetime/timestamp->relative timestamp) :unread? (not read) :context [[quo/text {:style common-style/user-avatar-tag-text} diff --git a/src/status_im/contexts/shell/activity_center/notification/community_request/view.cljs b/src/status_im/contexts/shell/activity_center/notification/community_request/view.cljs index dc2cbe9230..905ab5803e 100644 --- a/src/status_im/contexts/shell/activity_center/notification/community_request/view.cljs +++ b/src/status_im/contexts/shell/activity_center/notification/community_request/view.cljs @@ -1,6 +1,7 @@ (ns status-im.contexts.shell.activity-center.notification.community-request.view (:require [quo.core :as quo] + [react-native.core :as rn] [react-native.gesture :as gesture] [status-im.constants :as constants] [status-im.contexts.shell.activity-center.notification.common.style :as common-style] @@ -10,27 +11,23 @@ [utils.re-frame :as rf])) (defn- swipeable - [{:keys [active-swipeable extra-fn]} child] + [{:keys [extra-fn]} child] [common/swipeable - {:left-button common/swipe-button-read-or-unread - :left-on-press common/swipe-on-press-toggle-read - :right-button common/swipe-button-delete - :right-on-press common/swipe-on-press-delete - :active-swipeable active-swipeable - :extra-fn extra-fn} + {:left-button common/swipe-button-read-or-unread + :left-on-press common/swipe-on-press-toggle-read + :right-button common/swipe-button-delete + :right-on-press common/swipe-on-press-delete + :extra-fn extra-fn} child]) (defn- get-header-text-and-context - [community membership-status] - (let [community-name (:name community) - permissions (:permissions community) - open? (not= 3 (:access permissions)) - community-image (get-in community [:images :thumbnail :uri]) + [community-logo community-name community-permissions membership-status] + (let [open? (not= 3 (:access community-permissions)) community-context-tag [quo/context-tag {:type :community :size 24 :blur? true - :community-logo community-image + :community-logo community-logo :community-name community-name}]] (cond (= membership-status constants/activity-center-membership-status-idle) @@ -56,27 +53,33 @@ :t/joined-community :t/community-request-accepted-body-text) (when open? {:community community-name}))] - community-context-tag]} - - :else nil))) + community-context-tag]}))) (defn view - [{:keys [notification set-swipeable-height customization-color] :as props}] + [{:keys [notification extra-fn]}] (let [{:keys [community-id membership-status read - timestamp]} notification - community (rf/sub [:communities/community community-id]) - {:keys [header-text context]} (get-header-text-and-context community - membership-status)] - [swipeable props - [gesture/touchable-without-feedback - {:on-press (fn [] - (rf/dispatch [:navigate-back]) - (rf/dispatch [:communities/navigate-to-community-overview community-id]))} + timestamp]} notification + community-name (rf/sub [:communities/name community-id]) + community-logo (rf/sub [:communities/logo community-id]) + community-permissions (rf/sub [:communities/permissions community-id]) + customization-color (rf/sub [:profile/customization-color]) + {:keys [header-text + context]} (get-header-text-and-context community-logo + community-name + 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 {:title header-text :customization-color customization-color :icon :i/communities - :on-layout set-swipeable-height :timestamp (datetime/timestamp->relative timestamp) :unread? (not read) :context context}]]])) diff --git a/src/status_im/contexts/shell/activity_center/notification/contact_requests/view.cljs b/src/status_im/contexts/shell/activity_center/notification/contact_requests/view.cljs index ce50ca2c13..37a58f666b 100644 --- a/src/status_im/contexts/shell/activity_center/notification/contact_requests/view.cljs +++ b/src/status_im/contexts/shell/activity_center/notification/contact_requests/view.cljs @@ -1,6 +1,7 @@ (ns status-im.contexts.shell.activity-center.notification.contact-requests.view (:require [quo.core :as quo] + quo.theme [react-native.core :as rn] [status-im.constants :as constants] [status-im.contexts.shell.activity-center.notification.common.style :as common-style] @@ -24,48 +25,52 @@ :text (i18n/label :t/decline)}]) (defn- swipeable - [{:keys [active-swipeable extra-fn notification]} child] + [{:keys [extra-fn notification]} child] (let [{:keys [id author message]} notification {:keys [contact-request-state]} message {: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 (or (#{constants/contact-request-message-state-accepted constants/contact-request-message-state-declined} contact-request-state) (and outgoing? (= contact-request-state constants/contact-request-message-state-pending))) [common/swipeable - {:left-button common/swipe-button-read-or-unread - :left-on-press common/swipe-on-press-toggle-read - :right-button common/swipe-button-delete - :right-on-press common/swipe-on-press-delete - :active-swipeable active-swipeable - :extra-fn extra-fn} + {:left-button common/swipe-button-read-or-unread + :left-on-press common/swipe-on-press-toggle-read + :right-button common/swipe-button-delete + :right-on-press common/swipe-on-press-delete + :extra-fn extra-fn} child] (and (= contact-request-state constants/contact-request-message-state-pending) (not outgoing?)) [common/swipeable - {:left-button swipe-button-accept - :left-on-press #(rf/dispatch [:activity-center.contact-requests/accept id]) - :right-button swipe-button-decline - :right-on-press #(rf/dispatch [:activity-center.contact-requests/decline id]) - :active-swipeable active-swipeable - :extra-fn extra-fn} + {:left-button swipe-button-accept + :left-on-press accept + :right-button swipe-button-decline + :right-on-press decline + :extra-fn extra-fn} child] :else child))) (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 - {: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 [quo/activity-log {:title (i18n/label :t/contact-request-was-accepted) :customization-color customization-color - :on-layout set-swipeable-height :icon :i/add-user :timestamp (datetime/timestamp->relative (:timestamp notification)) :unread? (not (:read notification)) @@ -76,7 +81,6 @@ [quo/activity-log {:title (i18n/label :t/contact-request) :customization-color customization-color - :on-layout set-swipeable-height :icon :i/add-user :timestamp (datetime/timestamp->relative (:timestamp notification)) :unread? (not (:read notification)) @@ -102,13 +106,22 @@ nil)}]))) (defn- incoming-contact-request-view - [{:keys [notification set-swipeable-height customization-color]} theme] - (let [{:keys [id author message last-message]} notification - message (or message last-message)] + [{:keys [notification]} theme] + (let [{:keys [id author 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 {:title (i18n/label :t/contact-request) :customization-color customization-color - :on-layout set-swipeable-height :icon :i/add-user :timestamp (datetime/timestamp->relative (:timestamp notification)) :unread? (not (:read notification)) @@ -140,13 +153,13 @@ :key :button-decline :label (i18n/label :t/decline) :accessibility-label :decline-contact-request - :on-press #(rf/dispatch [:activity-center.contact-requests/decline id])} + :on-press decline} {:type :button :subtype :positive :key :button-accept :label (i18n/label :t/accept) :accessibility-label :accept-contact-request - :on-press #(rf/dispatch [:activity-center.contact-requests/accept id])}] + :on-press accept}] nil)}])) @@ -155,16 +168,16 @@ (let [{:keys [author message last-message]} notification {:keys [public-key]} (rf/sub [:multiaccount/contact]) {:keys [contact-request-state]} (or message last-message) - app-theme (rf/sub [:theme])] + theme (quo.theme/use-theme)] [swipeable props (cond (= 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) [rn/pressable {:on-press #(rf/dispatch [:chat.ui/start-chat author])} - [incoming-contact-request-view props app-theme]] + [incoming-contact-request-view props theme]] :else - [incoming-contact-request-view props app-theme])])) + [incoming-contact-request-view props theme])])) diff --git a/src/status_im/contexts/shell/activity_center/notification/contact_verification/view.cljs b/src/status_im/contexts/shell/activity_center/notification/contact_verification/view.cljs index cb3a2827e9..9e6ab82d99 100644 --- a/src/status_im/contexts/shell/activity_center/notification/contact_verification/view.cljs +++ b/src/status_im/contexts/shell/activity_center/notification/contact_verification/view.cljs @@ -105,7 +105,7 @@ (rf/dispatch [:activity-center.notifications/mark-as-read id])) (defn- swipeable - [{:keys [active-swipeable extra-fn notification replying?] :as props} child] + [{:keys [extra-fn notification replying?] :as props} child] (let [{:keys [id message contact-verification-status]} notification challenger? (:outgoing message)] @@ -116,23 +116,21 @@ (and (not challenger?) (= contact-verification-status constants/contact-verification-status-pending)) [common/swipeable - {:left-button swipe-button-reply - :left-on-press #(prepare-challenge-reply props) - :right-button swipe-button-decline - :right-on-press #(decline-challenge id) - :active-swipeable active-swipeable - :extra-fn extra-fn} + {:left-button swipe-button-reply + :left-on-press #(prepare-challenge-reply props) + :right-button swipe-button-decline + :right-on-press #(decline-challenge id) + :extra-fn extra-fn} child] (and challenger? (= contact-verification-status constants/contact-verification-status-accepted)) [common/swipeable - {:left-button swipe-button-trust - :left-on-press #(mark-challenge-trusted id) - :right-button swipe-button-untrustworthy - :right-on-press #(mark-challenge-untrustworthy id) - :active-swipeable active-swipeable - :extra-fn extra-fn} + {:left-button swipe-button-trust + :left-on-press #(mark-challenge-trusted id) + :right-button swipe-button-untrustworthy + :right-on-press #(mark-challenge-untrustworthy id) + :extra-fn extra-fn} child] (#{constants/contact-verification-status-accepted @@ -140,12 +138,11 @@ constants/contact-verification-status-trusted} contact-verification-status) [common/swipeable - {:left-button common/swipe-button-read-or-unread - :left-on-press common/swipe-on-press-toggle-read - :right-button common/swipe-button-delete - :right-on-press common/swipe-on-press-delete - :active-swipeable active-swipeable - :extra-fn extra-fn} + {:left-button common/swipe-button-read-or-unread + :left-on-press common/swipe-on-press-toggle-read + :right-button common/swipe-button-delete + :right-on-press common/swipe-on-press-delete + :extra-fn extra-fn} child] :else @@ -154,8 +151,9 @@ (defn view [_] (let [reply (atom "")] - (fn [{:keys [notification set-swipeable-height replying? customization-color] :as props}] - (let [{:keys [id message + (fn [{:keys [notification replying?] :as props}] + (let [customization-color (rf/sub [:profile/customization-color]) + {:keys [id message contact-verification-status]} notification challenger? (:outgoing message)] ;; 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)) [swipeable props [quo/activity-log - (merge - (when-not replying? - {:on-layout set-swipeable-height}) - {:title (i18n/label :t/identity-verification-request) - :customization-color customization-color - :icon :i/friend - :timestamp (datetime/timestamp->relative (:timestamp notification)) - :unread? (not (:read notification)) - :on-update-reply #(reset! reply %) - :replying? replying? - :max-reply-length max-reply-length - :valid-reply? valid-reply? - :context (context-tags challenger? notification) - :message (activity-message challenger? notification) - :items - (cond-> [] - (and challenger? - (= contact-verification-status constants/contact-verification-status-accepted)) - (concat - [{:type :button - :subtype :danger - :key :button-mark-as-untrustworthy - :label (i18n/label :t/untrustworthy) - :accessibility-label :mark-contact-verification-as-untrustworthy - :on-press #(mark-challenge-untrustworthy id)} - {:type :button - :subtype :positive - :key :button-accept - :label (i18n/label :t/accept) - :accessibility-label :mark-contact-verification-as-trusted - :on-press #(mark-challenge-trusted id)}]) + {:title (i18n/label :t/identity-verification-request) + :customization-color customization-color + :icon :i/friend + :timestamp (datetime/timestamp->relative (:timestamp notification)) + :unread? (not (:read notification)) + :on-update-reply #(reset! reply %) + :replying? replying? + :max-reply-length max-reply-length + :valid-reply? valid-reply? + :context (context-tags challenger? notification) + :message (activity-message challenger? notification) + :items + (cond-> [] + (and challenger? + (= contact-verification-status constants/contact-verification-status-accepted)) + (concat + [{:type :button + :subtype :danger + :key :button-mark-as-untrustworthy + :label (i18n/label :t/untrustworthy) + :accessibility-label :mark-contact-verification-as-untrustworthy + :on-press #(mark-challenge-untrustworthy id)} + {:type :button + :subtype :positive + :key :button-accept + :label (i18n/label :t/accept) + :accessibility-label :mark-contact-verification-as-trusted + :on-press #(mark-challenge-trusted id)}]) - (and challenger? - (= contact-verification-status constants/contact-verification-status-trusted)) - (concat [{:type :status - :subtype :positive - :key :status-trusted - :label (i18n/label :t/status-confirmed)}]) + (and challenger? + (= contact-verification-status constants/contact-verification-status-trusted)) + (concat [{:type :status + :subtype :positive + :key :status-trusted + :label (i18n/label :t/status-confirmed)}]) - (and challenger? - (= contact-verification-status constants/contact-verification-status-untrustworthy)) - (concat [{:type :status - :subtype :negative - :key :status-untrustworthy - :label (i18n/label :t/untrustworthy)}]) + (and challenger? + (= contact-verification-status + constants/contact-verification-status-untrustworthy)) + (concat [{:type :status + :subtype :negative + :key :status-untrustworthy + :label (i18n/label :t/untrustworthy)}]) - (and (not challenger?) - (= contact-verification-status constants/contact-verification-status-accepted)) - (concat [{:type :status - :subtype :positive - :key :status-accepted - :label (i18n/label :t/replied)}]) + (and (not challenger?) + (= contact-verification-status constants/contact-verification-status-accepted)) + (concat [{:type :status + :subtype :positive + :key :status-accepted + :label (i18n/label :t/replied)}]) - (and (not challenger?) - (= contact-verification-status constants/contact-verification-status-declined)) - (concat [{:type :status - :subtype :negative - :key :status-declined - :label (i18n/label :t/declined)}]) + (and (not challenger?) + (= contact-verification-status constants/contact-verification-status-declined)) + (concat [{:type :status + :subtype :negative + :key :status-declined + :label (i18n/label :t/declined)}]) - (and (not challenger?) - (= contact-verification-status constants/contact-verification-status-pending)) - (concat - [{:type :button - :subtype :danger - :key :button-decline - :label (i18n/label :t/decline) - :accessibility-label :decline-contact-verification - :on-press #(decline-challenge id)} - (if replying? - {:type :button - :subtype :primary - :key :button-reply - :label (i18n/label :t/send-reply) - :accessibility-label :reply-to-contact-verification - :disable-when invalid-reply? - :on-press #(send-challenge-reply id @reply)} - {:type :button - :subtype :primary - :key :button-send-reply - :label (i18n/label :t/message-reply) - :accessibility-label :send-reply-to-contact-verification - :on-press #(prepare-challenge-reply props)})]))})]]))))) + (and (not challenger?) + (= contact-verification-status constants/contact-verification-status-pending)) + (concat + [{:type :button + :subtype :danger + :key :button-decline + :label (i18n/label :t/decline) + :accessibility-label :decline-contact-verification + :on-press #(decline-challenge id)} + (if replying? + {:type :button + :subtype :primary + :key :button-reply + :label (i18n/label :t/send-reply) + :accessibility-label :reply-to-contact-verification + :disable-when invalid-reply? + :on-press #(send-challenge-reply id @reply)} + {:type :button + :subtype :primary + :key :button-send-reply + :label (i18n/label :t/message-reply) + :accessibility-label :send-reply-to-contact-verification + :on-press #(prepare-challenge-reply props)})]))}]]))))) diff --git a/src/status_im/contexts/shell/activity_center/notification/membership/view.cljs b/src/status_im/contexts/shell/activity_center/notification/membership/view.cljs index 11cd253bde..811fc02126 100644 --- a/src/status_im/contexts/shell/activity_center/notification/membership/view.cljs +++ b/src/status_im/contexts/shell/activity_center/notification/membership/view.cljs @@ -1,6 +1,7 @@ (ns status-im.contexts.shell.activity-center.notification.membership.view (:require [quo.core :as quo] + [react-native.core :as rn] [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.view :as common] @@ -33,35 +34,50 @@ :text (i18n/label :t/decline)}]) (defn- swipeable - [{:keys [active-swipeable notification extra-fn]} child] - (let [{:keys [accepted dismissed id]} notification] + [{:keys [notification extra-fn]} child] + (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) [common/swipeable - {:left-button common/swipe-button-read-or-unread - :left-on-press common/swipe-on-press-toggle-read - :right-button common/swipe-button-delete - :right-on-press common/swipe-on-press-delete - :active-swipeable active-swipeable - :extra-fn extra-fn} + {:left-button common/swipe-button-read-or-unread + :left-on-press common/swipe-on-press-toggle-read + :right-button common/swipe-button-delete + :right-on-press common/swipe-on-press-delete + :extra-fn extra-fn} child] [common/swipeable - {:left-button swipe-button-accept - :left-on-press #(rf/dispatch [:activity-center.notifications/accept id]) - :right-button swipe-button-decline - :right-on-press #(rf/dispatch [:activity-center.notifications/dismiss id]) - :active-swipeable active-swipeable - :extra-fn extra-fn} + {:left-button swipe-button-accept + :left-on-press accept + :right-button swipe-button-decline + :right-on-press dismiss + :extra-fn extra-fn} child]))) (defn view - [{:keys [notification set-swipeable-height customization-color] :as props}] - (let [{:keys [id accepted dismissed author read timestamp chat-name chat-id]} notification] + [{:keys [notification] :as props}] + (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 [pressable {:accepted accepted :chat-id chat-id} [quo/activity-log {:title (i18n/label :t/added-to-group-chat) :customization-color customization-color - :on-layout set-swipeable-height :icon :i/add-user :timestamp (datetime/timestamp->relative timestamp) :unread? (not read) @@ -78,13 +94,10 @@ :key :button-accept :label (i18n/label :t/accept) :accessibility-label :accept-group-chat-invitation - :on-press #(rf/dispatch - [:activity-center.notifications/accept id])} + :on-press accept} {:type :button :subtype :danger :key :button-decline :label (i18n/label :t/decline) :accessibility-label :decline-group-chat-invitation - :on-press #(rf/dispatch - [:activity-center.notifications/dismiss - id])}])}]]])) + :on-press dismiss}])}]]])) diff --git a/src/status_im/contexts/shell/activity_center/notification/mentions/view.cljs b/src/status_im/contexts/shell/activity_center/notification/mentions/view.cljs index fe0300dc6c..0f2e7f89c6 100644 --- a/src/status_im/contexts/shell/activity_center/notification/mentions/view.cljs +++ b/src/status_im/contexts/shell/activity_center/notification/mentions/view.cljs @@ -30,25 +30,24 @@ parsed-text-children)))) (defn- swipeable - [{:keys [active-swipeable extra-fn]} child] + [{:keys [extra-fn]} child] [common/swipeable - {:left-button common/swipe-button-read-or-unread - :left-on-press common/swipe-on-press-toggle-read - :right-button common/swipe-button-delete - :right-on-press common/swipe-on-press-delete - :active-swipeable active-swipeable - :extra-fn extra-fn} + {:left-button common/swipe-button-read-or-unread + :left-on-press common/swipe-on-press-toggle-read + :right-button common/swipe-button-delete + :right-on-press common/swipe-on-press-delete + :extra-fn extra-fn} child]) (defn view - [{:keys [notification set-swipeable-height customization-color] :as props}] + [{:keys [notification extra-fn]}] (let [{:keys [author chat-name community-id chat-id message read timestamp]} notification community-chat? (not (string/blank? community-id)) - community (rf/sub [:communities/community community-id]) - community-name (:name community) - community-image (get-in community [:images :thumbnail :uri])] - [swipeable props + community-name (rf/sub [:communities/name community-id]) + community-logo (rf/sub [:communities/logo community-id]) + customization-color (rf/sub [:profile/customization-color])] + [swipeable {:extra-fn extra-fn} [gesture/touchable-without-feedback {:on-press (fn [] (rf/dispatch [:hide-popover]) @@ -56,7 +55,6 @@ [quo/activity-log {:title (i18n/label :t/mention) :customization-color customization-color - :on-layout set-swipeable-height :icon :i/mention :timestamp (datetime/timestamp->relative timestamp) :unread? (not read) @@ -68,7 +66,7 @@ {:type :channel :blur? true :size 24 - :community-logo community-image + :community-logo community-logo :community-name community-name :channel-name chat-name}] [quo/context-tag diff --git a/src/status_im/contexts/shell/activity_center/notification/reply/view.cljs b/src/status_im/contexts/shell/activity_center/notification/reply/view.cljs index f0bbc201b4..a319cbd564 100644 --- a/src/status_im/contexts/shell/activity_center/notification/reply/view.cljs +++ b/src/status_im/contexts/shell/activity_center/notification/reply/view.cljs @@ -51,26 +51,26 @@ nil)) (defn- swipeable - [{:keys [active-swipeable extra-fn]} child] + [{:keys [extra-fn]} child] [common/swipeable - {:left-button common/swipe-button-read-or-unread - :left-on-press common/swipe-on-press-toggle-read - :right-button common/swipe-button-delete - :right-on-press common/swipe-on-press-delete - :active-swipeable active-swipeable - :extra-fn extra-fn} + {:left-button common/swipe-button-read-or-unread + :left-on-press common/swipe-on-press-toggle-read + :right-button common/swipe-button-delete + :right-on-press common/swipe-on-press-delete + :extra-fn extra-fn} child]) (defn view - [{:keys [notification set-swipeable-height customization-color] :as props}] + [{:keys [notification extra-fn]}] (let [{:keys [author chat-name community-id chat-id - message read timestamp album-messages]} notification - community-chat? (not (string/blank? community-id)) - community (rf/sub [:communities/community community-id]) - community-name (:name community) - community-image (get-in community [:images :thumbnail :uri]) - media-server-port (rf/sub [:mediaserver/port])] - [swipeable props + message read timestamp + album-messages]} notification + community-chat? (not (string/blank? community-id)) + community-name (rf/sub [:communities/name community-id]) + community-logo (rf/sub [:communities/logo community-id]) + customization-color (rf/sub [:profile/customization-color]) + media-server-port (rf/sub [:mediaserver/port])] + [swipeable {:extra-fn extra-fn} [gesture/touchable-without-feedback {:on-press (fn [] (rf/dispatch [:hide-popover]) @@ -78,7 +78,6 @@ [quo/activity-log {:title (i18n/label :t/message-reply) :customization-color customization-color - :on-layout set-swipeable-height :icon :i/reply :timestamp (datetime/timestamp->relative timestamp) :unread? (not read) @@ -89,7 +88,7 @@ {:type :channel :blur? true :size 24 - :community-logo community-image + :community-logo community-logo :community-name community-name :channel-name chat-name}] [quo/context-tag @@ -113,10 +112,7 @@ (= (:content-type message) constants/content-type-gif) - :gif - - :else - nil) + :gif) :body (get-message-content message album-messages media-server-port)}}]]])) diff --git a/src/status_im/contexts/shell/activity_center/view.cljs b/src/status_im/contexts/shell/activity_center/view.cljs index ca7d3f7ef6..25f9ca6e8e 100644 --- a/src/status_im/contexts/shell/activity_center/view.cljs +++ b/src/status_im/contexts/shell/activity_center/view.cljs @@ -1,9 +1,10 @@ (ns status-im.contexts.shell.activity-center.view (:require - [oops.core :as oops] [quo.core :as quo] [react-native.core :as rn] [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.notification-types :as types] [status-im.contexts.shell.activity-center.notification.admin.view :as admin] @@ -23,61 +24,68 @@ [utils.re-frame :as rf])) (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) - 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)])))) + (rf/dispatch [:activity-center.notifications/fetch-next-page])) (defn view [] - (let [active-swipeable (atom nil)] - (rf/dispatch [:activity-center.notifications/fetch-first-page]) - (fn [] - (let [notifications (rf/sub [:activity-center/notifications]) - customization-color (rf/sub [:profile/customization-color])] - [quo/overlay {:type :shell} - [rn/view {:flex 1 :padding-top (navigation/status-bar-height)} - [header/header] - [rn/flat-list - {:data notifications - :render-data {:active-swipeable active-swipeable - :customization-color customization-color} - :content-container-style {:flex-grow 1} - :empty-component [empty-tab/empty-tab] - :key-fn :id - :on-scroll-to-index-failed identity - :on-end-reached #(rf/dispatch [:activity-center.notifications/fetch-next-page]) - :render-fn notification-component}]] - ])))) + (let [notifications (rf/sub [:activity-center/notifications]) + + ;; We globally control the active swipeable for all notifications + ;; because when a swipe left/right gesture initiates, the previously + ;; active swiped notification (if any) must be removed & closed with + ;; animation. + active-swipeable (rn/use-ref-atom nil)] + (rn/use-mount + (fn [] + (rf/dispatch [:activity-center.notifications/fetch-first-page]))) + + [ac.context/provider {:active-swipeable active-swipeable} + [quo/overlay {:type :shell} + [rn/view {:style {:flex 1 :padding-top (navigation/status-bar-height)}} + [header/header] + (rn/delay-render + [rn/flat-list + {: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}])]]])) diff --git a/src/status_im/subs/communities.cljs b/src/status_im/subs/communities.cljs index 61e5dfb15b..5336cc418b 100644 --- a/src/status_im/subs/communities.cljs +++ b/src/status_im/subs/communities.cljs @@ -16,12 +16,33 @@ (fn [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 :communities/community :<- [:communities] (fn [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 :communities/community-color (fn [[_ community-id]]