Swipe gestures for Activity Center notifications with CTA (#15284)
Implements swipe actions for notifications with call to action (e.g. pending contact requests, unverified identity verifications, etc). Fixes https://github.com/status-im/status-mobile/issues/15118 According to the Design team, the goal is to deliver a consistent experience to users, so whenever the user sees a notification with buttons, the same actions can be taken via the swipe buttons. Note: swipe buttons are using placeholder icons while the Design team works out which ones to use Additionally, a bunch of fixes: - Fix: outgoing pending contact requests were not being removed from the UI when cancelled. - Fix: Membership tab not showing unread indicator. - Fix: dismissed membership notification not marked as read. - Fix: dismissed membership notification was displaying decline/accept buttons. Regression came from changes in status-go related to soft deletion of notifications. - Fix: incorrect check for the pending state of a contact request. - Fixed lots of bugs for identity verification notifications, as it was completely broken. Unfortunately, somebody made lots of changes without actually testing the flows. - Add basic error handling and log if accepting, declining or canceling contact requests fail. The demo shows an identity verification with swipe actions to reply or decline. [identity-verification-swipe-to-reply.webm](https://user-images.githubusercontent.com/46027/223565755-b2ca3f68-12e2-4e1e-9e52-edd52cfcc971.webm) Out of scope: The old quo input is still in use in the identity verification notification. This will eventually be solved by issue https://github.com/status-im/status-mobile/issues/14364 ### Steps to test Notifications with one or more buttons (actions) are affected by this change, because now the user can also swipe left/right to act on them. - Membership notifications: private group chat. The following PR explains how to generate them https://github.com/status-im/status-mobile/pull/14785 - Contact requests, and community gated requests to join (Admin tab). - Identity verifications. I believe the only way to test identity verification flows at the moment is to use the Desktop app, since initiating the challenge is not implemented in Mobile yet. - Mentions and replies don't have new swipe buttons because they don't have call to action buttons throughout their lifecycle. Steps to test identity verification flows: #### Identity verification flow 1 - `A` and `B` are mutual contacts. - `A` sends a verification request to `B`. - `A` should not see any notification yet. - `B` should receive an identity verification notification. `B` can either decline or reply. - `B` declines and the status `Declined` is shown instead of buttons. - `B` can now either swipe to toggle read/unread or swipe delete the notification. - `A` should not receive any notification after `A` declined. #### Identity verification flow 2 - `A` and `B` are mutual contacts. - `A` sends a verification request to `B`. - `A` should not see any notification yet. - `B` should receive an identity verification notification. `B` can either decline or reply. - `B` press `Reply` and a bottom sheet is displayed with a text input. - `B` sends the reply/answer message and the status `Replied` is shown instead of buttons. - `B` can now either swipe to toggle read/unread or swipe to delete the notification. - `A` should receive a notification with the reply from `B`. - `A` can either mark the answer as untrustworthy or accept it (trust it) via the normal buttons, as well as via the swipe left/right buttons. - If `A` accepts the answer, then the status `Confirmed` is shown instead of buttons. On the other hand, if `A` marks as untrustworthy, then the status `Untrustworthy` is shown instead of buttons. - `B` should receive no further notifications due to `A`s actions. - `A` can now either swipe to toggle read/unread or swipe delete the notification.
This commit is contained in:
parent
e98ce54830
commit
9473d3f40c
|
@ -37,7 +37,7 @@
|
|||
(when on-update-reply
|
||||
(on-update-reply %)))
|
||||
:auto-capitalize :none
|
||||
:auto-focus true
|
||||
:auto-focus false
|
||||
:accessibility-label :identity-verification-reply-text-input
|
||||
:placeholder (i18n/label :t/type-something)
|
||||
:return-key-type :none
|
||||
|
@ -129,7 +129,7 @@
|
|||
(-> button
|
||||
(assoc :size size)
|
||||
(assoc :type subtype)
|
||||
(assoc :disabled (and replying? (disable-when @reply-input)))
|
||||
(assoc :disabled (and replying? disable-when (disable-when @reply-input)))
|
||||
(update :style merge common-style {:margin-right 8}))
|
||||
label]))
|
||||
|
||||
|
@ -142,7 +142,7 @@
|
|||
:blur? blur?}])
|
||||
|
||||
(defn- footer
|
||||
[_ _]
|
||||
[_]
|
||||
(let [reply-input (reagent/atom "")]
|
||||
(fn [{:keys [replying? items] :as props}]
|
||||
[:<>
|
||||
|
|
|
@ -55,6 +55,7 @@
|
|||
[status-im.wallet.core :as wallet]
|
||||
status-im.wallet.custom-tokens.core
|
||||
status-im2.contexts.activity-center.events
|
||||
status-im2.contexts.activity-center.notification.contact-requests.events
|
||||
status-im2.contexts.shell.events
|
||||
status-im.chat.models.gaps
|
||||
[status-im2.navigation.events :as navigation]))
|
||||
|
|
|
@ -177,7 +177,7 @@
|
|||
(reset! expanded? true))
|
||||
(and @keyboard-was-shown? (not keyboard-shown))
|
||||
(reset! expanded? false))))
|
||||
[@show-bottom-sheet? @keyboard-was-shown?])
|
||||
[@show-bottom-sheet? @keyboard-was-shown? keyboard-shown])
|
||||
(react/effect! #(do
|
||||
(when-not @gesture-running?
|
||||
(cond
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
[status-im2.common.toasts.events :as toasts]
|
||||
[status-im2.constants :as constants]
|
||||
[status-im2.contexts.activity-center.notification-types :as types]
|
||||
status-im2.contexts.activity-center.notification.contact-requests.events
|
||||
[status-im2.contexts.chat.events :as chat.events]
|
||||
[taoensso.timbre :as log]
|
||||
[utils.collection :as collection]
|
||||
|
@ -43,6 +42,12 @@
|
|||
(log/warn (str "Failed to " action)
|
||||
{:notification-id notification-id :error error}))
|
||||
|
||||
(defn get-notification
|
||||
[db notification-id]
|
||||
(->> (get-in db [:activity-center :notifications])
|
||||
(filter #(= notification-id (:id %)))
|
||||
first))
|
||||
|
||||
;;;; Notification reconciliation
|
||||
|
||||
(defn- update-notifications
|
||||
|
@ -94,12 +99,6 @@
|
|||
|
||||
;;;; Status changes (read/dismissed/deleted)
|
||||
|
||||
(defn- get-notification
|
||||
[db notification-id]
|
||||
(->> (get-in db [:activity-center :notifications])
|
||||
(filter #(= notification-id (:id %)))
|
||||
first))
|
||||
|
||||
(rf/defn mark-as-read
|
||||
{:events [:activity-center.notifications/mark-as-read]}
|
||||
[{:keys [db]} notification-id]
|
||||
|
@ -235,7 +234,7 @@
|
|||
{:events [:activity-center.notifications/dismiss-success]}
|
||||
[{:keys [db] :as cofx} notification-id]
|
||||
(let [notification (get-notification db notification-id)]
|
||||
(notifications-reconcile cofx [(assoc notification :dismissed true)])))
|
||||
(notifications-reconcile cofx [(assoc notification :read true :dismissed true)])))
|
||||
|
||||
(rf/defn delete-notification
|
||||
{:events [:activity-center.notifications/delete]}
|
||||
|
|
|
@ -137,15 +137,16 @@
|
|||
:action :notification/accept})))
|
||||
|
||||
(deftest notification-dismissal-test
|
||||
(testing "dismisses notification, but keep it in the app db"
|
||||
(testing "dismisses & mark notification as read, and 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}
|
||||
dismissed-notif-1 (assoc notif-1 :dismissed true)]
|
||||
dismissed-notif-1 (assoc notif-1 :dismissed true :read true)]
|
||||
(h/stub-fx-with-callbacks :json-rpc/call :on-success (constantly notif-2))
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications]
|
||||
[notif-2 notif-1]])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center]
|
||||
{:filter {:type types/no-type :status :all}
|
||||
:notifications [notif-2 notif-1]}])
|
||||
|
||||
(rf/dispatch [:activity-center.notifications/dismiss (:id notif-1)])
|
||||
|
||||
|
@ -520,7 +521,7 @@
|
|||
:type types/mention}]
|
||||
(get-in (h/db) [:activity-center :notifications]))))))
|
||||
|
||||
(testing "resets loading flag after an error"
|
||||
(testing "resets loading state after error"
|
||||
(h/run-test-sync
|
||||
(setup)
|
||||
(let [spy-queue (atom [])]
|
||||
|
|
|
@ -2,79 +2,111 @@
|
|||
(:require [quo2.core :as quo]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[status-im2.constants :as constants]
|
||||
[status-im2.contexts.activity-center.notification.common.style :as style]
|
||||
[status-im2.contexts.activity-center.notification.common.style :as common-style]
|
||||
[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]
|
||||
(if (#{constants/activity-center-membership-status-accepted
|
||||
(defn- swipe-button-accept
|
||||
[{:keys [style]} _]
|
||||
[common/swipe-button-container
|
||||
{:style (common-style/swipe-success-container style)
|
||||
:icon :i/placeholder
|
||||
:text (i18n/label :t/accept)}])
|
||||
|
||||
(defn- swipe-button-decline
|
||||
[{:keys [style]} _]
|
||||
[common/swipe-button-container
|
||||
{:style (common-style/swipe-danger-container style)
|
||||
:icon :i/placeholder
|
||||
:text (i18n/label :t/decline)}])
|
||||
|
||||
(defn- swipeable
|
||||
[{:keys [active-swipeable notification extra-fn]} child]
|
||||
(let [{:keys [community-id id membership-status]} notification]
|
||||
(cond
|
||||
(#{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))
|
||||
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}
|
||||
child]
|
||||
|
||||
(= membership-status constants/activity-center-membership-status-pending)
|
||||
[common/swipeable
|
||||
{:left-button swipe-button-accept
|
||||
:left-on-press #(rf/dispatch [:communities.ui/accept-request-to-join-pressed community-id id])
|
||||
:right-button swipe-button-decline
|
||||
:right-on-press #(rf/dispatch [:communities.ui/decline-request-to-join-pressed community-id
|
||||
id])
|
||||
:active-swipeable active-swipeable
|
||||
:extra-fn extra-fn}
|
||||
child]
|
||||
|
||||
:else
|
||||
child)))
|
||||
|
||||
(defn view
|
||||
[{: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])]
|
||||
[quo/activity-log
|
||||
{:title (i18n/label :t/join-request)
|
||||
: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
|
||||
{:size :small
|
||||
:override-theme :dark
|
||||
:color colors/primary-50
|
||||
:style style/user-avatar-tag
|
||||
:text-style style/user-avatar-tag-text}
|
||||
{:uri community-image} community-name]]
|
||||
:items (case membership-status
|
||||
constants/activity-center-membership-status-accepted
|
||||
[{:type :status
|
||||
:subtype :positive
|
||||
:key :status-accepted
|
||||
:blur? true
|
||||
:label (i18n/label :t/accepted)}]
|
||||
[{:keys [notification set-swipeable-height] :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])]
|
||||
[swipeable props
|
||||
[quo/activity-log
|
||||
{:title (i18n/label :t/join-request)
|
||||
: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
|
||||
{:size :small
|
||||
:override-theme :dark
|
||||
:color colors/primary-50
|
||||
:style common-style/user-avatar-tag
|
||||
:text-style common-style/user-avatar-tag-text}
|
||||
{:uri community-image} community-name]]
|
||||
:items (case membership-status
|
||||
constants/activity-center-membership-status-accepted
|
||||
[{:type :status
|
||||
:subtype :positive
|
||||
:key :status-accepted
|
||||
:blur? true
|
||||
:label (i18n/label :t/accepted)}]
|
||||
|
||||
constants/activity-center-membership-status-declined
|
||||
[{:type :status
|
||||
:subtype :negative
|
||||
:key :status-declined
|
||||
:blur? true
|
||||
:label (i18n/label :t/declined)}]
|
||||
constants/activity-center-membership-status-declined
|
||||
[{:type :status
|
||||
:subtype :negative
|
||||
:key :status-declined
|
||||
:blur? true
|
||||
:label (i18n/label :t/declined)}]
|
||||
|
||||
constants/activity-center-membership-status-pending
|
||||
[{:type :button
|
||||
:subtype :danger
|
||||
:key :button-decline
|
||||
:label (i18n/label :t/decline)
|
||||
:accessibility-label :decline-join-request
|
||||
:on-press (fn []
|
||||
(rf/dispatch [:communities.ui/decline-request-to-join-pressed
|
||||
community-id id]))}
|
||||
{:type :button
|
||||
:subtype :positive
|
||||
:key :button-accept
|
||||
:label (i18n/label :t/accept)
|
||||
:accessibility-label :accept-join-request
|
||||
:on-press (fn []
|
||||
(rf/dispatch [:communities.ui/accept-request-to-join-pressed
|
||||
community-id id]))}]
|
||||
constants/activity-center-membership-status-pending
|
||||
[{:type :button
|
||||
:subtype :danger
|
||||
:key :button-decline
|
||||
:label (i18n/label :t/decline)
|
||||
:accessibility-label :decline-join-request
|
||||
:on-press (fn []
|
||||
(rf/dispatch
|
||||
[:communities.ui/decline-request-to-join-pressed
|
||||
community-id id]))}
|
||||
{:type :button
|
||||
:subtype :positive
|
||||
:key :button-accept
|
||||
:label (i18n/label :t/accept)
|
||||
:accessibility-label :accept-join-request
|
||||
:on-press (fn []
|
||||
(rf/dispatch [:communities.ui/accept-request-to-join-pressed
|
||||
community-id id]))}]
|
||||
|
||||
nil)}]))
|
||||
nil)}]]))
|
||||
|
|
|
@ -30,23 +30,29 @@
|
|||
: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))
|
||||
(def swipe-base
|
||||
{:align-items :center
|
||||
:justify-content :center
|
||||
:border-radius swipe-button-border-radius
|
||||
:width swipe-action-width})
|
||||
|
||||
(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))
|
||||
(defn swipe-success-container
|
||||
[style]
|
||||
(merge swipe-base
|
||||
{:background-color colors/success-60}
|
||||
style))
|
||||
|
||||
(defn swipe-danger-container
|
||||
[style]
|
||||
(merge swipe-base
|
||||
{:background-color colors/danger-60}
|
||||
style))
|
||||
|
||||
(defn swipe-primary-container
|
||||
[style]
|
||||
(merge swipe-base
|
||||
{:background-color colors/primary-60}
|
||||
style))
|
||||
|
||||
(def swipe-text
|
||||
{:margin-top 5
|
||||
|
|
|
@ -54,39 +54,42 @@
|
|||
(.close ^js @active-swipeable))
|
||||
(reset! active-swipeable @swipeable)))
|
||||
|
||||
(defn left-swipe-button
|
||||
[{:keys [style]} {:keys [notification]}]
|
||||
(defn swipe-button-container
|
||||
[{:keys [style icon text]} _]
|
||||
[rn/animated-view
|
||||
{:accessibility-label :notification-left-swipe
|
||||
:style (style/left-swipe-container style)}
|
||||
{:accessibility-label :notification-swipe
|
||||
:style style}
|
||||
[rn/view {:style style/swipe-text-wrapper}
|
||||
[quo/icon
|
||||
(if (:read notification)
|
||||
:i/notifications
|
||||
:i/check)
|
||||
[quo/icon icon
|
||||
{:color colors/white}]
|
||||
[quo/text {:style style/swipe-text}
|
||||
(if (:read notification)
|
||||
(i18n/label :t/unread)
|
||||
(i18n/label :t/read))]]])
|
||||
text]]])
|
||||
|
||||
(defn right-swipe-button
|
||||
(defn swipe-button-read-or-unread
|
||||
[{:keys [style]} {:keys [notification]}]
|
||||
[swipe-button-container
|
||||
{:style (style/swipe-primary-container style)
|
||||
:icon (if (:read notification)
|
||||
:i/notifications
|
||||
:i/check)
|
||||
:text (if (:read notification)
|
||||
(i18n/label :t/unread)
|
||||
(i18n/label :t/read))}])
|
||||
|
||||
(defn swipe-button-delete
|
||||
[{: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)]]])
|
||||
[swipe-button-container
|
||||
{:style (style/swipe-danger-container style)
|
||||
:icon :i/delete
|
||||
:text (i18n/label :t/delete)}])
|
||||
|
||||
(defn left-swipe-on-press
|
||||
(defn swipe-on-press-toggle-read
|
||||
[{: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
|
||||
(defn swipe-on-press-delete
|
||||
[{:keys [notification]}]
|
||||
(rf/dispatch [:activity-center.notifications/delete (:id notification)]))
|
||||
|
||||
|
@ -102,15 +105,15 @@
|
|||
& 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
|
||||
(merge
|
||||
{:ref #(reset! swipeable-ref %)
|
||||
:accessibility-label :notification-swipeable
|
||||
:friction 2
|
||||
:on-swipeable-will-open (close-active-swipeable active-swipeable swipeable-ref)}
|
||||
(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
|
||||
|
@ -118,14 +121,17 @@
|
|||
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})}]
|
||||
:swipeable-ref swipeable-ref})})
|
||||
(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})}))]
|
||||
children))))
|
||||
|
|
|
@ -1,26 +1,69 @@
|
|||
(ns status-im2.contexts.activity-center.notification.contact-requests.events
|
||||
(:require [utils.re-frame :as rf]))
|
||||
(:require [status-im2.contexts.activity-center.events :as ac-events]
|
||||
[taoensso.timbre :as log]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(rf/defn accept-contact-request
|
||||
{:events [:activity-center.contact-requests/accept-request]}
|
||||
[{:keys [db]} id]
|
||||
{:json-rpc/call [{:method "wakuext_acceptContactRequest"
|
||||
:params [{:id id}]
|
||||
:js-response true
|
||||
:on-success #(rf/dispatch [:sanitize-messages-and-process-response %])}]})
|
||||
{:events [:activity-center.contact-requests/accept]}
|
||||
[_ contact-id]
|
||||
{:json-rpc/call
|
||||
[{:method "wakuext_acceptContactRequest"
|
||||
:params [{:id contact-id}]
|
||||
:js-response true
|
||||
:on-success #(rf/dispatch [:sanitize-messages-and-process-response %])
|
||||
:on-error #(rf/dispatch [:activity-center.contact-requests/accept-error contact-id %])}]})
|
||||
|
||||
(rf/defn accept-contact-request-error
|
||||
{:events [:activity-center.contact-requests/accept-error]}
|
||||
[_ contact-id error]
|
||||
(log/error "Failed to accept contact-request"
|
||||
{:error error
|
||||
:event :activity-center.contact-requests/accept
|
||||
:contact-id contact-id})
|
||||
nil)
|
||||
|
||||
(rf/defn decline-contact-request
|
||||
{:events [:activity-center.contact-requests/decline-request]}
|
||||
[{:keys [db]} id]
|
||||
{:json-rpc/call [{:method "wakuext_declineContactRequest"
|
||||
:params [{:id id}]
|
||||
:js-response true
|
||||
:on-success #(rf/dispatch [:sanitize-messages-and-process-response %])}]})
|
||||
{:events [:activity-center.contact-requests/decline]}
|
||||
[_ contact-id]
|
||||
{:json-rpc/call
|
||||
[{:method "wakuext_declineContactRequest"
|
||||
:params [{:id contact-id}]
|
||||
:js-response true
|
||||
:on-success #(rf/dispatch [:sanitize-messages-and-process-response %])
|
||||
:on-error #(rf/dispatch [:activity-center.contact-requests/decline-error contact-id %])}]})
|
||||
|
||||
(rf/defn decline-contact-request-error
|
||||
{:events [:activity-center.contact-requests/decline-error]}
|
||||
[_ contact-id error]
|
||||
(log/error "Failed to decline contact-request"
|
||||
{:error error
|
||||
:event :activity-center.contact-requests/decline
|
||||
:contact-id contact-id})
|
||||
nil)
|
||||
|
||||
(rf/defn cancel-outgoing-contact-request
|
||||
{:events [:activity-center.contact-requests/cancel-outgoing-request]}
|
||||
[{:keys [db]} id]
|
||||
{:json-rpc/call [{:method "wakuext_cancelOutgoingContactRequest"
|
||||
:params [{:id id}]
|
||||
:js-response true
|
||||
:on-success #(rf/dispatch [:sanitize-messages-and-process-response %])}]})
|
||||
{:events [:activity-center.contact-requests/cancel-outgoing]}
|
||||
[{:keys [db]} {:keys [contact-id notification-id]}]
|
||||
(when-let [notification (ac-events/get-notification db notification-id)]
|
||||
{:json-rpc/call
|
||||
[{:method "wakuext_cancelOutgoingContactRequest"
|
||||
:params [{:id contact-id}]
|
||||
:on-success #(rf/dispatch [:activity-center.contact-requests/cancel-outgoing-success
|
||||
notification])
|
||||
:on-error #(rf/dispatch [:activity-center.contact-requests/cancel-outgoing-error contact-id
|
||||
%])}]}))
|
||||
|
||||
(rf/defn cancel-outgoing-contact-request-success
|
||||
{:events [:activity-center.contact-requests/cancel-outgoing-success]}
|
||||
[_ notification]
|
||||
{:dispatch [:activity-center.notifications/reconcile
|
||||
[(assoc notification :deleted true)]]})
|
||||
|
||||
(rf/defn cancel-outgoing-contact-request-error
|
||||
{:events [:activity-center.contact-requests/cancel-outgoing-error]}
|
||||
[_ contact-id error]
|
||||
(log/error "Failed to cancel outgoing contact-request"
|
||||
{:error error
|
||||
:event :activity-center.contact-requests/cancel-outgoing
|
||||
:contact-id contact-id})
|
||||
nil)
|
||||
|
|
|
@ -2,31 +2,81 @@
|
|||
(:require [quo2.core :as quo]
|
||||
[react-native.gesture :as gesture]
|
||||
[status-im2.constants :as constants]
|
||||
[status-im2.contexts.activity-center.notification.common.style :as common-style]
|
||||
[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))
|
||||
(defn- swipe-button-accept
|
||||
[{:keys [style]} _]
|
||||
[common/swipe-button-container
|
||||
{:style (common-style/swipe-success-container style)
|
||||
:icon :i/placeholder
|
||||
:text (i18n/label :t/accept)}])
|
||||
|
||||
(defn- swipe-button-decline
|
||||
[{:keys [style]} _]
|
||||
[common/swipe-button-container
|
||||
{:style (common-style/swipe-danger-container style)
|
||||
:icon :i/placeholder
|
||||
:text (i18n/label :t/decline)}])
|
||||
|
||||
(defn- swipe-button-cancel-pending
|
||||
[{:keys [style]} _]
|
||||
[common/swipe-button-container
|
||||
{:style (common-style/swipe-danger-container style)
|
||||
:icon :i/placeholder
|
||||
:text (i18n/label :t/cancel)}])
|
||||
|
||||
(defn- swipeable
|
||||
[{:keys [active-swipeable extra-fn notification]} child]
|
||||
(let [{:keys [id author message last-message]} notification
|
||||
{:keys [contact-request-state]} (or (:message notification)
|
||||
(:last-message notification))
|
||||
{:keys [public-key]} (rf/sub [:multiaccount/contact])
|
||||
message (or message last-message)]
|
||||
(cond
|
||||
(#{constants/contact-request-message-state-accepted
|
||||
constants/contact-request-message-state-declined}
|
||||
contact-request-state)
|
||||
[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
|
||||
{: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 (fn [] {:height @height :notification notification})}
|
||||
:extra-fn extra-fn}
|
||||
child]
|
||||
|
||||
(= contact-request-state constants/contact-request-message-state-pending)
|
||||
(if (= public-key author)
|
||||
[common/swipeable
|
||||
{:right-button swipe-button-cancel-pending
|
||||
:right-on-press (fn []
|
||||
(rf/dispatch
|
||||
[:activity-center.contact-requests/cancel-outgoing
|
||||
{:contact-id (:from message)
|
||||
:notification-id id}]))
|
||||
:active-swipeable active-swipeable
|
||||
:extra-fn extra-fn}
|
||||
child]
|
||||
[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}
|
||||
child])
|
||||
|
||||
:else
|
||||
child)))
|
||||
|
||||
(defn outgoing-contact-request-view
|
||||
[{:keys [id chat-id message last-message] :as notification}
|
||||
set-swipeable-height]
|
||||
(let [{:keys [contact-request-state] :as message} (or message last-message)]
|
||||
(defn- outgoing-contact-request-view
|
||||
[{:keys [notification set-swipeable-height]}]
|
||||
(let [{:keys [id chat-id message last-message]} notification
|
||||
{: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)
|
||||
|
@ -48,7 +98,7 @@
|
|||
[common/user-avatar-tag chat-id]]
|
||||
:message {:body (get-in message [:content :text])}
|
||||
:items (case contact-request-state
|
||||
constants/contact-request-state-mutual
|
||||
constants/contact-request-message-state-pending
|
||||
[{:type :button
|
||||
:subtype :danger
|
||||
:key :button-cancel
|
||||
|
@ -56,10 +106,9 @@
|
|||
:accessibility-label :cancel-contact-request
|
||||
:on-press (fn []
|
||||
(rf/dispatch
|
||||
[:activity-center.contact-requests/cancel-outgoing-request
|
||||
(:from message)])
|
||||
(rf/dispatch [:activity-center.notifications/mark-as-read
|
||||
id]))}
|
||||
[:activity-center.contact-requests/cancel-outgoing
|
||||
{:contact-id (:from message)
|
||||
:notification-id id}]))}
|
||||
{:type :status
|
||||
:subtype :pending
|
||||
:key :status-pending
|
||||
|
@ -75,10 +124,10 @@
|
|||
|
||||
nil)}])))
|
||||
|
||||
(defn incoming-contact-request-view
|
||||
[{:keys [id author message last-message] :as notification}
|
||||
set-swipeable-height]
|
||||
(let [message (or message last-message)]
|
||||
(defn- incoming-contact-request-view
|
||||
[{:keys [notification set-swipeable-height]}]
|
||||
(let [{:keys [id author message last-message]} notification
|
||||
message (or message last-message)]
|
||||
[quo/activity-log
|
||||
{:title (i18n/label :t/contact-request)
|
||||
:on-layout set-swipeable-height
|
||||
|
@ -110,36 +159,31 @@
|
|||
:key :button-decline
|
||||
:label (i18n/label :t/decline)
|
||||
:accessibility-label :decline-contact-request
|
||||
:on-press (fn []
|
||||
(rf/dispatch [:activity-center.contact-requests/decline-request id])
|
||||
(rf/dispatch [:activity-center.notifications/mark-as-read
|
||||
id]))}
|
||||
:on-press #(rf/dispatch [:activity-center.contact-requests/decline id])}
|
||||
{:type :button
|
||||
:subtype :positive
|
||||
:key :button-accept
|
||||
:label (i18n/label :t/accept)
|
||||
:accessibility-label :accept-contact-request
|
||||
:on-press (fn []
|
||||
(rf/dispatch [:activity-center.contact-requests/accept-request id])
|
||||
(rf/dispatch [:activity-center.notifications/mark-as-read
|
||||
id]))}]
|
||||
:on-press #(rf/dispatch [:activity-center.contact-requests/accept id])}]
|
||||
nil)}]))
|
||||
|
||||
(defn view
|
||||
[{: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 set-swipeable-height]
|
||||
[{:keys [notification] :as props}]
|
||||
(let [{:keys [author message last-message]} notification
|
||||
{:keys [public-key]} (rf/sub [:multiaccount/contact])
|
||||
{:keys [contact-request-state]} (or message last-message)]
|
||||
[swipeable props
|
||||
(cond
|
||||
(= public-key author)
|
||||
[outgoing-contact-request-view props]
|
||||
|
||||
(= contact-request-state constants/contact-request-message-state-accepted)
|
||||
[gesture/touchable-without-feedback
|
||||
{:on-press (fn []
|
||||
(rf/dispatch [:hide-popover])
|
||||
(rf/dispatch [:chat.ui/start-chat author]))}
|
||||
[incoming-contact-request-view notification set-swipeable-height]]
|
||||
(= contact-request-state constants/contact-request-message-state-accepted)
|
||||
[gesture/touchable-without-feedback
|
||||
{:on-press (fn []
|
||||
(rf/dispatch [:hide-popover])
|
||||
(rf/dispatch [:chat.ui/start-chat author]))}
|
||||
[incoming-contact-request-view props]]
|
||||
|
||||
:else
|
||||
[incoming-contact-request-view notification set-swipeable-height])))
|
||||
:else
|
||||
[incoming-contact-request-view props])]))
|
||||
|
|
|
@ -1,16 +1,40 @@
|
|||
(ns status-im2.contexts.activity-center.notification.contact-verification.view
|
||||
(:require [clojure.string :as string]
|
||||
[utils.i18n :as i18n]
|
||||
[quo2.core :as quo]
|
||||
[status-im2.constants :as constants]
|
||||
[utils.datetime :as datetime]
|
||||
[status-im2.contexts.activity-center.notification.common.style :as common-style]
|
||||
[status-im2.contexts.activity-center.notification.common.view :as common]
|
||||
[utils.datetime :as datetime]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(defn- hide-bottom-sheet-and-dispatch
|
||||
[event]
|
||||
(rf/dispatch [:bottom-sheet/hide])
|
||||
(rf/dispatch event))
|
||||
(defn- swipe-button-decline
|
||||
[{:keys [style]} _]
|
||||
[common/swipe-button-container
|
||||
{:style (common-style/swipe-danger-container style)
|
||||
:icon :i/placeholder
|
||||
:text (i18n/label :t/decline)}])
|
||||
|
||||
(defn- swipe-button-reply
|
||||
[{:keys [style]} _]
|
||||
[common/swipe-button-container
|
||||
{:style (common-style/swipe-primary-container style)
|
||||
:icon :i/placeholder
|
||||
:text (i18n/label :t/message-reply)}])
|
||||
|
||||
(defn- swipe-button-untrustworthy
|
||||
[{:keys [style]} _]
|
||||
[common/swipe-button-container
|
||||
{:style (common-style/swipe-danger-container style)
|
||||
:icon :i/placeholder
|
||||
:text (i18n/label :t/untrustworthy)}])
|
||||
|
||||
(defn- swipe-button-trust
|
||||
[{:keys [style]} _]
|
||||
[common/swipe-button-container
|
||||
{:style (common-style/swipe-success-container style)
|
||||
:icon :i/placeholder
|
||||
:text (i18n/label :t/accept)}])
|
||||
|
||||
(defn- context-tags
|
||||
[challenger? {:keys [author contact-verification-status]}]
|
||||
|
@ -38,18 +62,6 @@
|
|||
(= contact-verification-status constants/contact-verification-status-declined))
|
||||
{:body (get-in message [:content :text])})))
|
||||
|
||||
(defn- activity-status
|
||||
[challenger? contact-verification-status]
|
||||
(if challenger?
|
||||
(cond (= contact-verification-status constants/contact-verification-status-trusted)
|
||||
{:type :positive :label (i18n/label :t/status-confirmed)}
|
||||
(= contact-verification-status constants/contact-verification-status-untrustworthy)
|
||||
{:type :negative :label (i18n/label :t/untrustworthy)})
|
||||
(cond (= contact-verification-status constants/contact-verification-status-accepted)
|
||||
{:type :positive :label (i18n/label :t/replied)}
|
||||
(= contact-verification-status constants/contact-verification-status-declined)
|
||||
{:type :negative :label (i18n/label :t/declined)})))
|
||||
|
||||
(def ^:private max-reply-length
|
||||
280)
|
||||
|
||||
|
@ -57,84 +69,181 @@
|
|||
[reply]
|
||||
(<= (count reply) max-reply-length))
|
||||
|
||||
(def ^:private invalid-reply?
|
||||
(comp not valid-reply?))
|
||||
|
||||
(declare view)
|
||||
|
||||
(defn- decline-challenge
|
||||
[id]
|
||||
(rf/dispatch [:bottom-sheet/hide])
|
||||
(rf/dispatch [:activity-center.contact-verification/decline id])
|
||||
(rf/dispatch [:activity-center.notifications/mark-as-read id]))
|
||||
|
||||
(defn- prepare-challenge-reply
|
||||
[props]
|
||||
(rf/dispatch [:bottom-sheet/show-sheet
|
||||
{:content view
|
||||
:override-theme :dark}
|
||||
(assoc props :replying? true)]))
|
||||
|
||||
(defn- send-challenge-reply
|
||||
[id reply]
|
||||
(rf/dispatch [:bottom-sheet/hide])
|
||||
(rf/dispatch [:activity-center.contact-verification/reply id reply])
|
||||
(rf/dispatch [:activity-center.notifications/mark-as-read id]))
|
||||
|
||||
(defn- mark-challenge-untrustworthy
|
||||
[id]
|
||||
(rf/dispatch [:activity-center.contact-verification/mark-as-untrustworthy id])
|
||||
(rf/dispatch [:activity-center.notifications/mark-as-read id]))
|
||||
|
||||
(defn- mark-challenge-trusted
|
||||
[id]
|
||||
(rf/dispatch [:activity-center.contact-verification/mark-as-trusted id])
|
||||
(rf/dispatch [:activity-center.notifications/mark-as-read id]))
|
||||
|
||||
(defn- swipeable
|
||||
[{:keys [active-swipeable extra-fn notification replying?] :as props} child]
|
||||
(let [{:keys [id message
|
||||
contact-verification-status]} notification
|
||||
challenger? (:outgoing message)]
|
||||
(cond
|
||||
replying?
|
||||
child
|
||||
|
||||
(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}
|
||||
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}
|
||||
child]
|
||||
|
||||
(#{constants/contact-verification-status-accepted
|
||||
constants/contact-verification-status-declined
|
||||
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}
|
||||
child]
|
||||
|
||||
:else
|
||||
child)))
|
||||
|
||||
(defn view
|
||||
[_ _]
|
||||
[_]
|
||||
(let [reply (atom "")]
|
||||
(fn [{:keys [id message contact-verification-status] :as notification} {:keys [replying?]}]
|
||||
(let [challenger? (:outgoing message)]
|
||||
(fn [{:keys [notification set-swipeable-height replying?] :as props}]
|
||||
(let [{:keys [id message
|
||||
contact-verification-status]} notification
|
||||
challenger? (:outgoing message)]
|
||||
;; TODO(@ilmotta): Declined challenges should only be displayed for the
|
||||
;; challengee, not the challenger.
|
||||
;; https://github.com/status-im/status-mobile/issues/14354
|
||||
(when-not (and challenger?
|
||||
(= contact-verification-status constants/contact-verification-status-declined))
|
||||
[quo/activity-log
|
||||
(merge
|
||||
{:title (i18n/label :t/identity-verification-request)
|
||||
: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)
|
||||
:status (activity-status challenger? contact-verification-status)
|
||||
:items
|
||||
(if challenger?
|
||||
(when (= contact-verification-status constants/contact-verification-status-accepted)
|
||||
(when-not
|
||||
(and challenger?
|
||||
(= 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)
|
||||
: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 (fn []
|
||||
(rf/dispatch
|
||||
[:activity-center.contact-verification/mark-as-untrustworthy
|
||||
id])
|
||||
(rf/dispatch [:activity-center.notifications/mark-as-read
|
||||
id]))}
|
||||
: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 (fn []
|
||||
(rf/dispatch
|
||||
[:activity-center.contact-verification/mark-as-trusted id])
|
||||
(rf/dispatch [:activity-center.notifications/mark-as-read
|
||||
id]))}])
|
||||
(when (= contact-verification-status constants/contact-verification-status-pending)
|
||||
: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-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-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 (fn []
|
||||
(hide-bottom-sheet-and-dispatch
|
||||
[:activity-center.contact-verification/decline id])
|
||||
(rf/dispatch
|
||||
[:activity-center.notifications/mark-as-read id]))}
|
||||
: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 #(not (valid-reply? %))
|
||||
:on-press (fn []
|
||||
(hide-bottom-sheet-and-dispatch
|
||||
[:activity-center.contact-verification/reply id
|
||||
@reply])
|
||||
(rf/dispatch
|
||||
[:activity-center.notifications/mark-as-read id]))}
|
||||
: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 (fn []
|
||||
(rf/dispatch [:bottom-sheet/show-sheet
|
||||
{:content view}
|
||||
{:notification notification
|
||||
:replying? true}]))})]))})])))))
|
||||
:on-press #(prepare-challenge-reply props)})]))})]])))))
|
||||
|
|
|
@ -1,45 +1,84 @@
|
|||
(ns status-im2.contexts.activity-center.notification.membership.view
|
||||
(:require [quo2.core :as quo]
|
||||
[react-native.core :as rn]
|
||||
[react-native.gesture :as gesture]
|
||||
[status-im2.contexts.activity-center.notification.common.style :as common-style]
|
||||
[status-im2.contexts.activity-center.notification.common.view :as common]
|
||||
[utils.datetime :as datetime]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(defn pressable
|
||||
[{:keys [accepted chat-id]} & children]
|
||||
(defn- pressable
|
||||
[{:keys [accepted chat-id]} child]
|
||||
(if accepted
|
||||
(into [rn/touchable-opacity
|
||||
{:on-press (fn []
|
||||
(rf/dispatch [:hide-popover])
|
||||
(rf/dispatch [:chat/navigate-to-chat chat-id]))}]
|
||||
children)
|
||||
(into [:<>] children)))
|
||||
[gesture/touchable-without-feedback
|
||||
{:on-press (fn []
|
||||
(rf/dispatch [:hide-popover])
|
||||
(rf/dispatch [:chat/navigate-to-chat chat-id]))}
|
||||
child]
|
||||
child))
|
||||
|
||||
(defn- swipe-button-accept
|
||||
[{:keys [style]} _]
|
||||
[common/swipe-button-container
|
||||
{:style (common-style/swipe-success-container style)
|
||||
:icon :i/placeholder
|
||||
:text (i18n/label :t/accept)}])
|
||||
|
||||
(defn- swipe-button-decline
|
||||
[{:keys [style]} _]
|
||||
[common/swipe-button-container
|
||||
{:style (common-style/swipe-danger-container style)
|
||||
:icon :i/placeholder
|
||||
:text (i18n/label :t/decline)}])
|
||||
|
||||
(defn- swipeable
|
||||
[{:keys [active-swipeable notification extra-fn]} child]
|
||||
(let [{:keys [accepted dismissed id]} notification]
|
||||
(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}
|
||||
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}
|
||||
child])))
|
||||
|
||||
(defn view
|
||||
[{:keys [id accepted author read timestamp chat-name chat-id]}]
|
||||
[pressable {:accepted accepted :chat-id chat-id}
|
||||
[quo/activity-log
|
||||
{:title (i18n/label :t/added-to-group-chat)
|
||||
:icon :i/add-user
|
||||
:timestamp (datetime/timestamp->relative timestamp)
|
||||
:unread? (not read)
|
||||
:context [[common/user-avatar-tag author]
|
||||
(i18n/label :t/added-you-to)
|
||||
[quo/group-avatar-tag chat-name
|
||||
{:size :small
|
||||
:color :purple}]]
|
||||
:items (when-not accepted
|
||||
[{:type :button
|
||||
:subtype :positive
|
||||
:key :button-accept
|
||||
:label (i18n/label :t/accept)
|
||||
:accessibility-label :accept-group-chat-invitation
|
||||
:on-press #(rf/dispatch [:activity-center.notifications/accept id])}
|
||||
{: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])}])}]])
|
||||
[{:keys [notification set-swipeable-height] :as props}]
|
||||
(let [{:keys [id accepted dismissed author read timestamp chat-name chat-id]} notification]
|
||||
[swipeable props
|
||||
[pressable {:accepted accepted :chat-id chat-id}
|
||||
[quo/activity-log
|
||||
{:title (i18n/label :t/added-to-group-chat)
|
||||
:on-layout set-swipeable-height
|
||||
:icon :i/add-user
|
||||
:timestamp (datetime/timestamp->relative timestamp)
|
||||
:unread? (not read)
|
||||
:context [[common/user-avatar-tag author]
|
||||
(i18n/label :t/added-you-to)
|
||||
[quo/group-avatar-tag chat-name
|
||||
{:size :small
|
||||
:color :purple}]]
|
||||
:items (when-not (or accepted dismissed)
|
||||
[{:type :button
|
||||
:subtype :positive
|
||||
:key :button-accept
|
||||
:label (i18n/label :t/accept)
|
||||
:accessibility-label :accept-group-chat-invitation
|
||||
:on-press #(rf/dispatch [:activity-center.notifications/accept id])}
|
||||
{: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])}])}]]]))
|
||||
|
|
|
@ -9,14 +9,14 @@
|
|||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(def tag-params
|
||||
(def ^:private tag-params
|
||||
{:size :small
|
||||
:override-theme :dark
|
||||
:color colors/primary-50
|
||||
:style style/tag
|
||||
:text-style style/tag-text})
|
||||
|
||||
(defn message-body
|
||||
(defn- message-body
|
||||
[message]
|
||||
(let [parsed-text (get-in message [:content :parsed-text])
|
||||
parsed-text-children (:children (first parsed-text))]
|
||||
|
@ -35,37 +35,39 @@
|
|||
literal))
|
||||
parsed-text-children))))
|
||||
|
||||
(defn swipeable
|
||||
[{:keys [height active-swipeable notification]} child]
|
||||
(defn- swipeable
|
||||
[{:keys [active-swipeable extra-fn]} 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
|
||||
{: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 (fn [] {:height @height :notification notification})}
|
||||
:extra-fn extra-fn}
|
||||
child])
|
||||
|
||||
(defn view
|
||||
[{: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])]
|
||||
[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)
|
||||
:context [[common/user-avatar-tag author]
|
||||
[quo/text {:style style/tag-text} (string/lower-case (i18n/label :t/on))]
|
||||
(if community-chat?
|
||||
[quo/context-tag tag-params {:uri community-image} community-name chat-name]
|
||||
[quo/group-avatar-tag chat-name tag-params])]
|
||||
:message {:body (message-body message)}}]]))
|
||||
[{:keys [notification set-swipeable-height] :as props}]
|
||||
(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
|
||||
[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)
|
||||
:context [[common/user-avatar-tag author]
|
||||
[quo/text {:style style/tag-text} (string/lower-case (i18n/label :t/on))]
|
||||
(if community-chat?
|
||||
[quo/context-tag tag-params {:uri community-image} community-name chat-name]
|
||||
[quo/group-avatar-tag chat-name tag-params])]
|
||||
:message {:body (message-body message)}}]]]))
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(def tag-params
|
||||
(def ^:private tag-params
|
||||
{:size :small
|
||||
:override-theme :dark
|
||||
:color colors/primary-50
|
||||
|
@ -20,7 +20,7 @@
|
|||
:text-style style/tag-text})
|
||||
|
||||
;; NOTE: Replies support text, image and stickers only.
|
||||
(defn get-message-content
|
||||
(defn- get-message-content
|
||||
[{:keys [content-type] :as message}]
|
||||
(case content-type
|
||||
constants/content-type-text (get-in message [:content :text])
|
||||
|
@ -37,38 +37,40 @@
|
|||
|
||||
nil))
|
||||
|
||||
(defn swipeable
|
||||
[{:keys [height active-swipeable notification]} child]
|
||||
(defn- swipeable
|
||||
[{:keys [active-swipeable extra-fn]} 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
|
||||
{: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 (fn [] {:height @height :notification notification})}
|
||||
:extra-fn extra-fn}
|
||||
child])
|
||||
|
||||
(defn view
|
||||
[{: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])]
|
||||
[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)
|
||||
:context [[common/user-avatar-tag author]
|
||||
[quo/text {:style style/lowercase-text} (i18n/label :t/on)]
|
||||
(if community-chat?
|
||||
[quo/context-tag tag-params {:uri community-image} community-name chat-name]
|
||||
[quo/group-avatar-tag chat-name tag-params])]
|
||||
:message {:body-number-of-lines 1
|
||||
:body (get-message-content message)}}]]))
|
||||
[{:keys [notification set-swipeable-height] :as props}]
|
||||
(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
|
||||
[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)
|
||||
:context [[common/user-avatar-tag author]
|
||||
[quo/text {:style style/lowercase-text} (i18n/label :t/on)]
|
||||
(if community-chat?
|
||||
[quo/context-tag tag-params {:uri community-image} community-name chat-name]
|
||||
[quo/group-avatar-tag chat-name tag-params])]
|
||||
:message {:body-number-of-lines 1
|
||||
:body (get-message-content message)}}]]]))
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
(ns status-im2.contexts.activity-center.view
|
||||
(:require [oops.core :as oops]
|
||||
(:require [clojure.set :as set]
|
||||
[oops.core :as oops]
|
||||
[quo2.core :as quo]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[react-native.core :as rn]
|
||||
|
@ -127,7 +128,7 @@
|
|||
:label (i18n/label :t/membership)
|
||||
:accessibility-label :tab-membership
|
||||
:notification-dot? (when-not is-mark-all-as-read-undoable?
|
||||
(contains? types-with-unread types/membership))}
|
||||
(set/subset? types/membership types-with-unread))}
|
||||
{:id types/system
|
||||
:label (i18n/label :t/system)
|
||||
:accessibility-label :tab-system
|
||||
|
@ -172,32 +173,30 @@
|
|||
(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}]
|
||||
(let [props {:height height
|
||||
: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 notification {}]
|
||||
[contact-verification/view props]
|
||||
|
||||
(= type types/contact-request)
|
||||
[contact-requests/swipeable swipeable-args
|
||||
[contact-requests/view notification set-swipeable-height]]
|
||||
[contact-requests/view props]
|
||||
|
||||
(= type types/mention)
|
||||
[mentions/swipeable swipeable-args
|
||||
[mentions/view notification set-swipeable-height]]
|
||||
[mentions/view props]
|
||||
|
||||
(= type types/reply)
|
||||
[reply/swipeable swipeable-args
|
||||
[reply/view notification set-swipeable-height]]
|
||||
[reply/view props]
|
||||
|
||||
(= type types/admin)
|
||||
[admin/swipeable swipeable-args
|
||||
[admin/view notification set-swipeable-height]]
|
||||
[admin/view props]
|
||||
|
||||
(some types/membership [type])
|
||||
[membership/view notification]
|
||||
[membership/view props]
|
||||
|
||||
:else
|
||||
nil)]))))
|
||||
|
|
|
@ -286,7 +286,7 @@ class TestActivityMultipleDevicePR(MultipleSharedDeviceTestCase):
|
|||
|
||||
self.home_1.just_fyi("Mark it as read and check filter")
|
||||
reply_element.swipe_right_on_element()
|
||||
self.home_1.activity_left_swipe_button.click()
|
||||
self.home_1.activity_notification_swipe_button.click()
|
||||
if reply_element.is_element_displayed(2):
|
||||
self.errors.append("Message is not marked as read!")
|
||||
self.home_1.activity_unread_filter_button.click()
|
||||
|
@ -295,7 +295,7 @@ class TestActivityMultipleDevicePR(MultipleSharedDeviceTestCase):
|
|||
|
||||
self.home_1.just_fyi("Mark it as unread and check filter via right swipe")
|
||||
reply_element.swipe_right_on_element()
|
||||
self.home_1.activity_left_swipe_button.click()
|
||||
self.home_1.activity_notification_swipe_button.click()
|
||||
if not reply_element.unread_indicator.is_element_displayed():
|
||||
self.errors.append("No unread dot is shown on activity center element after marking it as unread!")
|
||||
|
||||
|
@ -310,7 +310,7 @@ class TestActivityMultipleDevicePR(MultipleSharedDeviceTestCase):
|
|||
self.home_1.just_fyi("Delete it from unread via left swipe")
|
||||
self.home_1.open_activity_center_button.click()
|
||||
reply_element.swipe_left_on_element()
|
||||
self.home_1.activity_right_swipe_button.click()
|
||||
self.home_1.activity_notification_swipe_button.click()
|
||||
if reply_element.is_element_displayed():
|
||||
self.errors.append("Reply is still shown after removing from activity centre!")
|
||||
|
||||
|
|
|
@ -254,8 +254,7 @@ class HomeView(BaseView):
|
|||
# Activity centre
|
||||
self.mention_activity_tab_button = ActivityTabButton(self.driver, accessibility_id="tab-mention")
|
||||
self.reply_activity_tab_button = ActivityTabButton(self.driver, accessibility_id="tab-reply")
|
||||
self.activity_right_swipe_button = Button(self.driver, accessibility_id="notification-right-swipe")
|
||||
self.activity_left_swipe_button = Button(self.driver, accessibility_id="notification-left-swipe")
|
||||
self.activity_notification_swipe_button = Button(self.driver, accessibility_id="notification-swipe")
|
||||
self.activity_unread_filter_button = Button(self.driver, accessibility_id="selector-filter")
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue