diff --git a/src/mocks/js_dependencies.cljs b/src/mocks/js_dependencies.cljs index d63af6661f..c9cae410db 100644 --- a/src/mocks/js_dependencies.cljs +++ b/src/mocks/js_dependencies.cljs @@ -265,7 +265,15 @@ globalThis.__STATUS_MOBILE_JS_IDENTITY_PROXY__ = new Proxy({}, {get() { return ( (def react-native-gesture-handler #js - {:default #js {} + {:FlatList #js {} + :Gesture #js {:Pan nil} + :GestureDetector #js {} + :LongPressGestureHandler #js {} + :NativeViewGestureHandler #js {} + :PanGestureHandler #js {} + :PureNativeButton #js {} + :RectButton #js {} + :ScrollView #js {} :State #js {:BEGAN nil :ACTIVE nil @@ -273,19 +281,13 @@ globalThis.__STATUS_MOBILE_JS_IDENTITY_PROXY__ = new Proxy({}, {get() { return ( :END nil :FAILED nil :UNDETERMINED nil} - :PureNativeButton #js {} + :Swipeable #js {} :TapGestureHandler #js {} - :PanGestureHandler #js {} :TouchableHighlight #js {} - :LongPressGestureHandler #js {} - :TouchableWithoutFeedback #js {} - :NativeViewGestureHandler #js {} - :FlatList #js {} - :ScrollView #js {} :TouchableOpacity #js {} - :GestureDetector #js {} - :Gesture #js {:Pan nil} - :createNativeWrapper identity}) + :TouchableWithoutFeedback #js {} + :createNativeWrapper identity + :default #js {}}) (def react-native-redash #js {:clamp nil}) diff --git a/src/quo2/components/notifications/activity_log/view.cljs b/src/quo2/components/notifications/activity_log/view.cljs index 249f2276d7..1d2c229b0d 100644 --- a/src/quo2/components/notifications/activity_log/view.cljs +++ b/src/quo2/components/notifications/activity_log/view.cljs @@ -164,7 +164,8 @@ :as props}] [rn/view {:accessibility-label :activity - :style style/container} + :style style/container + :on-layout (:on-layout props)} (when-not replying? [activity-icon icon]) [rn/view diff --git a/src/quo2/components/tabs/tabs.cljs b/src/quo2/components/tabs/tabs.cljs index 6ed9c2c638..e0e19f5fae 100644 --- a/src/quo2/components/tabs/tabs.cljs +++ b/src/quo2/components/tabs/tabs.cljs @@ -159,7 +159,7 @@ ;; {:overflow :visible} doesn't work on components inheriting ;; from ScrollView (e.g. FlatList). There are open issues, here's ;; just one about this topic: - ;; https://github.com/facebook/react-native/issues/3121 + ;; https://github.com/facebook/react-native/issues/31218 :content-container-style {:padding-top (dec unread-count-offset)} :extra-data (str @active-tab-id) :horizontal true diff --git a/src/react_native/core.cljs b/src/react_native/core.cljs index 720f617bc3..c1e37652ef 100644 --- a/src/react_native/core.cljs +++ b/src/react_native/core.cljs @@ -10,6 +10,10 @@ (def app-state ^js (.-AppState ^js react-native)) +;; Only use this component for exceptional cases, otherwise use Reanimated, e.g. +;; when using interpolated values exposed by RN Gesture Handler > Swipeable. +(def animated-view (reagent/adapt-react-class react-native/Animated.View)) + (def view (reagent/adapt-react-class (.-View ^js react-native))) (def scroll-view (reagent/adapt-react-class (.-ScrollView ^js react-native))) (def image (reagent/adapt-react-class (.-Image ^js react-native))) diff --git a/src/react_native/gesture.cljs b/src/react_native/gesture.cljs index b1669b0f72..6428b0e422 100644 --- a/src/react_native/gesture.cljs +++ b/src/react_native/gesture.cljs @@ -1,6 +1,11 @@ (ns react-native.gesture (:require ["react-native-gesture-handler" :refer - (GestureDetector Gesture gestureHandlerRootHOC)] + (Gesture + GestureDetector + RectButton + Swipeable + TouchableWithoutFeedback + gestureHandlerRootHOC)] [reagent.core :as reagent])) (def gesture-detector (reagent/adapt-react-class GestureDetector)) @@ -32,3 +37,27 @@ ([g1 g2 g3] (.Simultaneous ^js Gesture g1 g2 g3))) (defn exclusive [g1 g2] (.Exclusive ^js Gesture g1 g2)) + +;; RN Gesture Handler touchables are drop-in replacements for the RN ones. In +;; some cases, it's the only touchable that works with Swipeable components. +(def touchable-without-feedback (reagent/adapt-react-class TouchableWithoutFeedback)) + +(def rect-button (reagent/adapt-react-class RectButton)) + +(def ^:private swipeable-component + (reagent/adapt-react-class Swipeable)) + +(defn swipeable + [{:keys [render-left-actions render-right-actions] :as props} & children] + (into [swipeable-component + (cond-> props + render-left-actions + (assoc :render-left-actions + (fn [& args] + (reagent/as-element (apply render-left-actions args)))) + + render-right-actions + (assoc :render-right-actions + (fn [& args] + (reagent/as-element (apply render-right-actions args)))))] + children)) diff --git a/src/status_im2/contexts/activity_center/events.cljs b/src/status_im2/contexts/activity_center/events.cljs index cc8f11e366..8ddc821535 100644 --- a/src/status_im2/contexts/activity_center/events.cljs +++ b/src/status_im2/contexts/activity_center/events.cljs @@ -77,7 +77,7 @@ (update-in $ [types/no-type :all :data] remove-notification) (update-in $ [filter-type :unread :data] remove-notification) (update-in $ [types/no-type :unread :data] remove-notification) - (if (:dismissed notification) + (if (:deleted notification) $ (cond-> (-> $ (update-in [filter-type :all :data] insert-and-sort) @@ -159,7 +159,7 @@ remove-pending-contact-request contact-id))}) -;;;; Mark notifications as read +;;;; Status changes (read/dismissed/deleted) (defn- get-notification [db notification-id] @@ -190,6 +190,24 @@ [cofx notification] (notifications-reconcile cofx [(assoc notification :read true)])) +(rf/defn mark-as-unread + {:events [:activity-center.notifications/mark-as-unread]} + [{:keys [db]} notification-id] + (when-let [notification (get-notification db notification-id)] + {:json-rpc/call [{:method "wakuext_markActivityCenterNotificationsUnread" + :params [[notification-id]] + :on-success #(rf/dispatch [:activity-center.notifications/mark-as-unread-success + notification]) + :on-error #(rf/dispatch [:activity-center/process-notification-failure + notification-id + :notification/mark-as-unread + %])}]})) + +(rf/defn mark-as-unread-success + {:events [:activity-center.notifications/mark-as-unread-success]} + [cofx notification] + (notifications-reconcile cofx [(assoc notification :read false)])) + (rf/defn mark-all-as-read {:events [:activity-center.notifications/mark-all-as-read]} [{:keys [db now]}] @@ -250,8 +268,6 @@ :utils/dispatch-later [{:dispatch [:activity-center.notifications/mark-all-as-read] :ms undo-time-limit-ms}]})) -;;;; Acceptance/dismissal - (rf/defn accept-notification {:events [:activity-center.notifications/accept]} [{:keys [db]} notification-id] @@ -290,6 +306,24 @@ (let [notification (get-notification db notification-id)] (notifications-reconcile cofx [(assoc notification :dismissed true)]))) +(rf/defn delete-notification + {:events [:activity-center.notifications/delete]} + [{:keys [db]} notification-id] + {:json-rpc/call [{:method "wakuext_deleteActivityCenterNotifications" + :params [[notification-id]] + :on-success #(rf/dispatch [:activity-center.notifications/delete-success + notification-id]) + :on-error #(rf/dispatch [:activity-center/process-notification-failure + notification-id + :notification/delete + %])}]}) + +(rf/defn delete-notification-success + {:events [:activity-center.notifications/delete-success]} + [{:keys [db] :as cofx} notification-id] + (let [notification (get-notification db notification-id)] + (notifications-reconcile cofx [(assoc notification :deleted true)]))) + ;;;; Contact verification (rf/defn contact-verification-decline diff --git a/src/status_im2/contexts/activity_center/events_test.cljs b/src/status_im2/contexts/activity_center/events_test.cljs index ff2581ea1d..d6ad9c4172 100644 --- a/src/status_im2/contexts/activity_center/events_test.cljs +++ b/src/status_im2/contexts/activity_center/events_test.cljs @@ -177,11 +177,12 @@ :action :notification/accept}))) (deftest notification-dismissal-test - (testing "dismisses notification and removes from app db" + (testing "dismisses notification, but keep it in the app db" (h/run-test-sync (setup) - (let [notif-1 {:id "0x1" :type types/private-group-chat} - notif-2 {:id "0x2" :type types/admin}] + (let [notif-1 {:id "0x1" :type types/private-group-chat} + notif-2 {:id "0x2" :type types/admin} + dismissed-notif-1 (assoc notif-1 :dismissed true)] (h/stub-fx-with-callbacks :json-rpc/call :on-success (constantly notif-2)) (rf/dispatch [:test/assoc-in [:activity-center] {:notifications {types/no-type @@ -195,12 +196,12 @@ (rf/dispatch [:activity-center.notifications/dismiss (:id notif-1)]) (is (= {types/no-type - {:all {:cursor "" :data [notif-2]} - :unread {:data []}} + {:all {:cursor "" :data [notif-2 dismissed-notif-1]} + :unread {:data [dismissed-notif-1]}} types/membership - {:all {:data []} - :unread {:cursor "" :data []}}} + {:all {:data [dismissed-notif-1]} + :unread {:cursor "" :data [dismissed-notif-1]}}} (get-in (h/db) [:activity-center :notifications])))))) (testing "logs on failure" @@ -355,12 +356,12 @@ (is (= notifications (get-in (h/db) [:activity-center :notifications])))))) - (testing "removes dismissed notifications" + (testing "removes deleted notifications" (h/run-test-sync (setup) (let [notif-1 {:id "0x1" :read true :type types/one-to-one-chat} notif-2 {:id "0x2" :read false :type types/one-to-one-chat} - notif-3 {:id "0x3" :read false :type types/system} + notif-3 {:id "0x3" :read false :type types/system :dismissed true} notif-4 {:id "0x4" :read true :type types/system} notif-5 {:id "0x5" :read false :type types/system :accepted true}] (rf/dispatch [:test/assoc-in [:activity-center :notifications] @@ -372,8 +373,8 @@ :unread {:cursor "" :data [notif-3 notif-5]}}}]) (rf/dispatch [:activity-center.notifications/reconcile - [(assoc notif-1 :dismissed true) - (assoc notif-4 :dismissed true) + [(assoc notif-1 :deleted true) + (assoc notif-4 :deleted true) notif-5]]) (is (= {types/no-type diff --git a/src/status_im2/contexts/activity_center/notification/admin/view.cljs b/src/status_im2/contexts/activity_center/notification/admin/view.cljs index d935b15aa3..46cd18c86d 100644 --- a/src/status_im2/contexts/activity_center/notification/admin/view.cljs +++ b/src/status_im2/contexts/activity_center/notification/admin/view.cljs @@ -8,8 +8,24 @@ [utils.i18n :as i18n] [utils.re-frame :as rf])) +(defn swipeable + [{:keys [height active-swipeable notification]} child] + (if (#{constants/activity-center-membership-status-accepted + constants/activity-center-membership-status-declined} + (:membership-status notification)) + [common/swipeable + {:left-button common/left-swipe-button + :left-on-press common/left-swipe-on-press + :right-button common/right-swipe-button + :right-on-press common/right-swipe-on-press + :active-swipeable active-swipeable + :extra-fn (fn [] {:height @height :notification notification})} + child] + child)) + (defn view - [{:keys [author community-id id membership-status read timestamp]}] + [{:keys [author community-id id membership-status read timestamp]} + set-swipeable-height] (let [community (rf/sub [:communities/community community-id]) community-name (:name community) community-image (get-in community [:images :thumbnail :uri])] @@ -18,6 +34,7 @@ :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 diff --git a/src/status_im2/contexts/activity_center/notification/common/style.cljs b/src/status_im2/contexts/activity_center/notification/common/style.cljs index ee2bd8c886..f790ec0f48 100644 --- a/src/status_im2/contexts/activity_center/notification/common/style.cljs +++ b/src/status_im2/contexts/activity_center/notification/common/style.cljs @@ -1,8 +1,56 @@ (ns status-im2.contexts.activity-center.notification.common.style (:require [quo2.foundations.colors :as colors])) +(def swipe-action-width 72) +(def swipe-button-border-radius 16) + (def user-avatar-tag {:background-color colors/white-opa-10}) (def user-avatar-tag-text {:color colors/white}) + +(def left-swipe-opacity-interpolation-js + (clj->js {:inputRange [0 swipe-action-width] + :outputRange [0 1] + :extrapolate :clamp})) + +(def left-swipe-translate-x-interpolation-js + (clj->js {:inputRange [0 swipe-action-width] + :outputRange [(- swipe-action-width) 0] + :extrapolate :clamp})) + +(def right-swipe-opacity-interpolation-js + (clj->js {:inputRange [(- swipe-action-width) 0] + :outputRange [1 0] + :extrapolate :clamp})) + +(def right-swipe-translate-x-interpolation-js + (clj->js {:inputRange [(- swipe-action-width) 0] + :outputRange [0 swipe-action-width] + :extrapolate :clamp})) + +(defn left-swipe-container + [style-props] + (merge {:background-color colors/primary-60 + :align-items :center + :justify-content :center + :border-radius swipe-button-border-radius + :width swipe-action-width} + style-props)) + +(defn right-swipe-container + [style-props] + (merge {:background-color colors/danger-60 + :align-items :center + :justify-content :center + :border-radius swipe-button-border-radius + :width swipe-action-width} + style-props)) + +(def swipe-text + {:margin-top 5 + :color colors/white}) + +(def swipe-text-wrapper + {:align-items :center}) diff --git a/src/status_im2/contexts/activity_center/notification/common/view.cljs b/src/status_im2/contexts/activity_center/notification/common/view.cljs index 09041390c2..2dfb9ede16 100644 --- a/src/status_im2/contexts/activity_center/notification/common/view.cljs +++ b/src/status_im2/contexts/activity_center/notification/common/view.cljs @@ -1,8 +1,12 @@ (ns status-im2.contexts.activity-center.notification.common.view (:require [quo2.core :as quo] + [react-native.core :as rn] + [react-native.gesture :as gesture] + [quo2.foundations.colors :as colors] [status-im.multiaccounts.core :as multiaccounts] [status-im2.contexts.activity-center.notification.common.style :as style] [status-im2.contexts.activity-center.utils :as activity-center.utils] + [utils.i18n :as i18n] [utils.re-frame :as rf])) (defn user-avatar-tag @@ -16,3 +20,113 @@ :text-style style/user-avatar-tag-text} (activity-center.utils/contact-name contact) (multiaccounts/displayed-photo contact)])) + +(defn- render-swipe-action + [{:keys [active-swipeable + extra-fn + interpolation-opacity + interpolation-translate-x + on-press + swipe-button + swipeable-ref]}] + (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)] + [gesture/rect-button + {:style {:border-radius style/swipe-button-border-radius} + :accessibility-label :notification-swipe-action-button + :on-press (fn [] + (when @swipeable-ref + (.close ^js @swipeable-ref) + (reset! active-swipeable nil)) + (on-press extra))} + [swipe-button + {:style {:opacity opacity + :transform [{:translateX translate-x}] + :height height}} + 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 left-swipe-button + [{:keys [style]} {:keys [notification]}] + [rn/animated-view + {:accessibility-label :notification-left-swipe + :style (style/left-swipe-container style)} + [rn/view {:style style/swipe-text-wrapper} + [quo/icon + (if (:read notification) + :i/notifications + :i/check) + {:color colors/white}] + [quo/text {:style style/swipe-text} + (if (:read notification) + (i18n/label :t/unread) + (i18n/label :t/read))]]]) + +(defn right-swipe-button + [{:keys [style]}] + [rn/animated-view + {:accessibility-label :notification-right-swipe + :style (style/right-swipe-container style)} + [rn/view {:style style/swipe-text-wrapper} + [quo/icon :i/delete {:color colors/white}] + [quo/text {:style style/swipe-text} + (i18n/label :t/delete)]]]) + +(defn left-swipe-on-press + [{:keys [notification]}] + (if (:read notification) + (rf/dispatch [:activity-center.notifications/mark-as-unread (:id notification)]) + (rf/dispatch [:activity-center.notifications/mark-as-read (:id notification)]))) + +(defn right-swipe-on-press + [{:keys [notification]}] + (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 + {:ref #(reset! swipeable-ref %) + :accessibility-label :notification-swipeable + :friction 2 + :left-threshold style/swipe-action-width + :right-threshold style/swipe-action-width + :overshoot-left false + :overshoot-right false + :on-swipeable-will-open (close-active-swipeable active-swipeable swipeable-ref) + :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}) + :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})}] + children)))) diff --git a/src/status_im2/contexts/activity_center/notification/contact_requests/view.cljs b/src/status_im2/contexts/activity_center/notification/contact_requests/view.cljs index 1a6198a1e9..6472c94a48 100644 --- a/src/status_im2/contexts/activity_center/notification/contact_requests/view.cljs +++ b/src/status_im2/contexts/activity_center/notification/contact_requests/view.cljs @@ -1,18 +1,36 @@ (ns status-im2.contexts.activity-center.notification.contact-requests.view (:require [quo2.core :as quo] - [react-native.core :as rn] + [react-native.gesture :as gesture] [status-im2.constants :as constants] [status-im2.contexts.activity-center.notification.common.view :as common] [utils.datetime :as datetime] [utils.i18n :as i18n] [utils.re-frame :as rf])) +(defn swipeable + [{:keys [height active-swipeable notification]} child] + (let [message (or (:message notification) (:last-message notification))] + (if (#{constants/contact-request-message-state-accepted + constants/contact-request-message-state-declined} + (:contact-request-state message)) + [common/swipeable + {:left-button common/left-swipe-button + :left-on-press common/left-swipe-on-press + :right-button common/right-swipe-button + :right-on-press common/right-swipe-on-press + :active-swipeable active-swipeable + :extra-fn (fn [] {:height @height :notification notification})} + child] + child))) + (defn outgoing-contact-request-view - [{:keys [id chat-id message last-message] :as notification}] + [{:keys [id chat-id message last-message] :as notification} + set-swipeable-height] (let [{:keys [contact-request-state] :as message} (or message last-message)] (if (= contact-request-state constants/contact-request-message-state-accepted) [quo/activity-log {:title (i18n/label :t/contact-request-was-accepted) + :on-layout set-swipeable-height :icon :i/add-user :timestamp (datetime/timestamp->relative (:timestamp notification)) :unread? (not (:read notification)) @@ -22,6 +40,7 @@ :items []] [quo/activity-log {:title (i18n/label :t/contact-request) + :on-layout set-swipeable-height :icon :i/add-user :timestamp (datetime/timestamp->relative (:timestamp notification)) :unread? (not (:read notification)) @@ -55,10 +74,12 @@ nil)}]))) (defn incoming-contact-request-view - [{:keys [id author message last-message] :as notification}] + [{:keys [id author message last-message] :as notification} + set-swipeable-height] (let [message (or message last-message)] [quo/activity-log {:title (i18n/label :t/contact-request) + :on-layout set-swipeable-height :icon :i/add-user :timestamp (datetime/timestamp->relative (:timestamp notification)) :unread? (not (:read notification)) @@ -79,7 +100,7 @@ :key :status-declined :label (i18n/label :t/declined)}] - constants/contact-request-state-mutual + constants/contact-request-message-state-pending [{:type :button :subtype :danger :key :button-decline @@ -101,19 +122,20 @@ nil)}])) (defn view - [{:keys [author message last-message] :as notification}] + [{:keys [author message last-message] :as notification} + set-swipeable-height] (let [{:keys [public-key]} (rf/sub [:multiaccount/contact]) {:keys [contact-request-state]} (or message last-message)] (cond (= public-key author) - [outgoing-contact-request-view notification] + [outgoing-contact-request-view notification set-swipeable-height] (= contact-request-state constants/contact-request-message-state-accepted) - [rn/touchable-opacity + [gesture/touchable-without-feedback {:on-press (fn [] (rf/dispatch [:hide-popover]) (rf/dispatch [:chat.ui/start-chat {:public-key author}]))} - [incoming-contact-request-view notification]] + [incoming-contact-request-view notification set-swipeable-height]] :else - [incoming-contact-request-view notification]))) + [incoming-contact-request-view notification set-swipeable-height]))) diff --git a/src/status_im2/contexts/activity_center/notification/mentions/view.cljs b/src/status_im2/contexts/activity_center/notification/mentions/view.cljs index aea31f2e96..0fd1bf323a 100644 --- a/src/status_im2/contexts/activity_center/notification/mentions/view.cljs +++ b/src/status_im2/contexts/activity_center/notification/mentions/view.cljs @@ -2,7 +2,7 @@ (:require [clojure.string :as string] [quo2.core :as quo] [quo2.foundations.colors :as colors] - [react-native.core :as rn] + [react-native.gesture :as gesture] [status-im2.contexts.activity-center.notification.common.view :as common] [status-im2.contexts.activity-center.notification.mentions.style :as style] [utils.datetime :as datetime] @@ -35,18 +35,31 @@ literal)) parsed-text-children)))) +(defn swipeable + [{:keys [height active-swipeable notification]} child] + [common/swipeable + {:left-button common/left-swipe-button + :left-on-press common/left-swipe-on-press + :right-button common/right-swipe-button + :right-on-press common/right-swipe-on-press + :active-swipeable active-swipeable + :extra-fn (fn [] {:height @height :notification notification})} + child]) + (defn view - [{:keys [author chat-name community-id chat-id message read timestamp]}] + [{:keys [author chat-name community-id chat-id message read timestamp]} + set-swipeable-height] (let [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])] - [rn/touchable-opacity + [gesture/touchable-without-feedback {:on-press (fn [] (rf/dispatch [:hide-popover]) (rf/dispatch [:chat/navigate-to-chat chat-id]))} [quo/activity-log {:title (i18n/label :t/mention) + :on-layout set-swipeable-height :icon :i/mention :timestamp (datetime/timestamp->relative timestamp) :unread? (not read) diff --git a/src/status_im2/contexts/activity_center/notification/reply/view.cljs b/src/status_im2/contexts/activity_center/notification/reply/view.cljs index 378668892e..3d216ab214 100644 --- a/src/status_im2/contexts/activity_center/notification/reply/view.cljs +++ b/src/status_im2/contexts/activity_center/notification/reply/view.cljs @@ -2,7 +2,7 @@ (:require [clojure.string :as string] [quo2.core :as quo] [quo2.foundations.colors :as colors] - [react-native.core :as rn] + [react-native.gesture :as gesture] [status-im.ui2.screens.chat.messages.message :as old-message] [status-im2.common.not-implemented :as not-implemented] [status-im2.constants :as constants] @@ -37,18 +37,31 @@ nil)) +(defn swipeable + [{:keys [height active-swipeable notification]} child] + [common/swipeable + {:left-button common/left-swipe-button + :left-on-press common/left-swipe-on-press + :right-button common/right-swipe-button + :right-on-press common/right-swipe-on-press + :active-swipeable active-swipeable + :extra-fn (fn [] {:height @height :notification notification})} + child]) + (defn view - [{:keys [author chat-name community-id chat-id message read timestamp]}] + [{:keys [author chat-name community-id chat-id message read timestamp]} + set-swipeable-height] (let [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])] - [rn/touchable-opacity + [gesture/touchable-without-feedback {:on-press (fn [] (rf/dispatch [:hide-popover]) (rf/dispatch [:chat/navigate-to-chat chat-id]))} [quo/activity-log {:title (i18n/label :t/message-reply) + :on-layout set-swipeable-height :icon :i/reply :timestamp (datetime/timestamp->relative timestamp) :unread? (not read) diff --git a/src/status_im2/contexts/activity_center/view.cljs b/src/status_im2/contexts/activity_center/view.cljs index f1080c025f..7bd9d2f5a2 100644 --- a/src/status_im2/contexts/activity_center/view.cljs +++ b/src/status_im2/contexts/activity_center/view.cljs @@ -1,5 +1,5 @@ (ns status-im2.contexts.activity-center.view - (:require [quo.react :as react] + (:require [oops.core :as oops] [quo2.core :as quo] [quo2.foundations.colors :as colors] [react-native.core :as rn] @@ -166,47 +166,59 @@ [rn/view {:style style/filter-toggle-container} [filter-selector-read-toggle]]]]) -(defn render-notification - [{:keys [type] :as notification} index] - [rn/view {:style (style/notification-container index)} - (cond - (= type types/contact-verification) - [contact-verification/view notification {}] +(defn notification-component + [] + (let [height (atom 0) + set-swipeable-height #(reset! height (oops/oget % "nativeEvent.layout.height"))] + (fn [{:keys [type] :as notification} index _ active-swipeable] + (let [swipeable-args {:height height + :active-swipeable active-swipeable + :notification notification}] + [rn/view {:style (style/notification-container index)} + (cond + (= type types/contact-verification) + [contact-verification/view notification {}] - (= type types/mention) - [mentions/view notification] + (= type types/contact-request) + [contact-requests/swipeable swipeable-args + [contact-requests/view notification set-swipeable-height]] - (= type types/reply) - [reply/view notification] + (= type types/mention) + [mentions/swipeable swipeable-args + [mentions/view notification set-swipeable-height]] - (= type types/contact-request) - [contact-requests/view notification] + (= type types/reply) + [reply/swipeable swipeable-args + [reply/view notification set-swipeable-height]] - (= type types/admin) - [admin/view notification] + (= type types/admin) + [admin/swipeable swipeable-args + [admin/view notification set-swipeable-height]] - (some types/membership [type]) - [membership/view notification] + (some types/membership [type]) + [membership/view notification] - :else - nil)]) + :else + nil)])))) (defn view [] - [:f> - (fn [] - (react/effect! #(rf/dispatch [:activity-center.notifications/fetch-first-page])) - [safe-area/consumer - (fn [{:keys [top bottom]}] - (let [notifications (rf/sub [:activity-center/filtered-notifications]) - window-width (rf/sub [:dimensions/window-width])] - [rn/view {:style (style/screen-container window-width top bottom)} - [header] - [rn/flat-list - {:data notifications - :content-container-style {:flex-grow 1} - :empty-component [empty-tab] - :key-fn :id - :on-scroll-to-index-failed identity - :on-end-reached #(rf/dispatch [:activity-center.notifications/fetch-next-page]) - :render-fn render-notification}]]))])]) + (let [active-swipeable (atom nil)] + [:f> + (fn [] + (rn/use-effect-once #(rf/dispatch [:activity-center.notifications/fetch-first-page])) + [safe-area/consumer + (fn [{:keys [top bottom]}] + (let [notifications (rf/sub [:activity-center/filtered-notifications]) + window-width (rf/sub [:dimensions/window-width])] + [rn/view {:style (style/screen-container window-width top bottom)} + [header] + [rn/flat-list + {:data notifications + :render-data active-swipeable + :content-container-style {:flex-grow 1} + :empty-component [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}]]))])])) diff --git a/src/status_im2/contexts/chat/home/view.cljs b/src/status_im2/contexts/chat/home/view.cljs index c6e650b96d..b5cd8703aa 100644 --- a/src/status_im2/contexts/chat/home/view.cljs +++ b/src/status_im2/contexts/chat/home/view.cljs @@ -51,13 +51,13 @@ [quo/text (i18n/label :t/blank-contacts-text)]]) (defn contacts - [contact-requests] + [pending-contact-requests] (let [items (rf/sub [:contacts/active-sections])] - (if (and (empty? items) (empty? contact-requests)) + (if (and (empty? items) (empty? pending-contact-requests)) [welcome-blank-contacts] [:<> - (when (seq contact-requests) - [contact-request/contact-requests contact-requests]) + (when (seq pending-contact-requests) + [contact-request/contact-requests pending-contact-requests]) (when (seq items) [contact-list/contact-list {:icon :options}])]))) @@ -65,7 +65,7 @@ [] (let [selected-tab (reagent/atom :recent)] (fn [] - (let [contact-requests (rf/sub [:activity-center/pending-contact-requests])] + (let [pending-contact-requests (rf/sub [:activity-center/pending-contact-requests])] [:<> [quo/discover-card {:title (i18n/label :t/invite-friends-to-status) @@ -86,9 +86,9 @@ {:id :contacts :label (i18n/label :t/contacts) :accessibility-label :tab-contacts - :notification-dot? (pos? (count contact-requests))}]}] + :notification-dot? (pos? (count pending-contact-requests))}]}] (if (= @selected-tab :contacts) - [contacts contact-requests] + [contacts pending-contact-requests] [chats @selected-tab])])))) (defn home diff --git a/src/status_im2/subs/activity_center.cljs b/src/status_im2/subs/activity_center.cljs index 67ac46b4df..2694e55e77 100644 --- a/src/status_im2/subs/activity_center.cljs +++ b/src/status_im2/subs/activity_center.cljs @@ -1,5 +1,6 @@ (ns status-im2.subs.activity-center (:require [re-frame.core :as re-frame] + [status-im2.constants :as constants] [status-im2.contexts.activity-center.notification-types :as types])) (re-frame/reg-sub @@ -70,4 +71,7 @@ :activity-center/pending-contact-requests :<- [:activity-center/notifications] (fn [notifications] - (get-in notifications [types/contact-request :unread :data]))) + (filter (fn [{:keys [message]}] + (= constants/contact-request-message-state-pending + (:contact-request-state message))) + (get-in notifications [types/contact-request :unread :data])))) diff --git a/src/status_im2/subs/activity_center_test.cljs b/src/status_im2/subs/activity_center_test.cljs index cd03600adc..7d93b25df3 100644 --- a/src/status_im2/subs/activity_center_test.cljs +++ b/src/status_im2/subs/activity_center_test.cljs @@ -1,6 +1,7 @@ (ns status-im2.subs.activity-center-test (:require [cljs.test :refer [is testing]] [re-frame.db :as rf-db] + [status-im2.constants :as constants] [status-im2.contexts.activity-center.notification-types :as types] status-im2.subs.activity-center [test-helpers.unit :as h] @@ -63,3 +64,27 @@ types/admin 7}) (is (= 28 (rf/sub [sub-name])))) + +(h/deftest-sub :activity-center/pending-contact-requests + [sub-name] + (testing "returns only contact request notifications in the pending state" + (let [pending {:id "0x2" + :type types/contact-request + :message {:contact-request-state + constants/contact-request-message-state-pending}}] + (swap! rf-db/app-db assoc-in + [:activity-center :notifications types/contact-request :unread :data] + [{:id "0x1" + :type types/contact-request + :message {:contact-request-state constants/contact-request-message-state-none}} + pending + {:id "0x3" + :type types/contact-request + :message {:contact-request-state constants/contact-request-message-state-accepted}} + {:id "0x4" + :type types/contact-request + :message {:contact-request-state constants/contact-request-message-state-declined}} + {:id "0x5" + :type types/mention}]) + + (is (= [pending] (rf/sub [sub-name])))))) diff --git a/status-go-version.json b/status-go-version.json index dd79aaf8f7..a584537349 100644 --- a/status-go-version.json +++ b/status-go-version.json @@ -3,7 +3,7 @@ "_comment": "Instead use: scripts/update-status-go.sh ", "owner": "status-im", "repo": "status-go", - "version": "v0.131.12", - "commit-sha1": "3c88a9fd7f0e95aad237d125625b109815af0f73", - "src-sha256": "04zcwgayd70rsdx7pjr05fjnv5qbzvih7fr5a63a4a2bxvajpkms" + "version": "v0.132.3", + "commit-sha1": "999d8c0ee0f2274c58b5a2de3beddab7feaabd16", + "src-sha256": "0qcpbhkhy8l3cq1055gnxghi7292jnd7xznl6s6h92pcg37y1jzc" } diff --git a/translations/en.json b/translations/en.json index 8a3865f4c6..859ca21196 100644 --- a/translations/en.json +++ b/translations/en.json @@ -1018,6 +1018,8 @@ "push-failed-transaction-body": "{{value}} {{currency}} to {{to}}", "allow-mention-notifications": "Show @ mentions", "server": "Server", + "read": "Read", + "unread": "Unread", "specify-server-public-key": "Enter server public key", "notify": "Notify", "off": "Off",