Add chat actions in home screen

This commit adds the following chat actions on the home screen:

- Mute chat
- Delete chat
- View profile (one to ones only)
- Clear history

It adds also integration tests for muting and deleting a chat.

To accommodate multiple dividers in the bottom sheet, the interface has
been changed to accept a sequence of sequences, instead of a map.
This commit is contained in:
Andrea Maria Piana 2022-10-26 17:08:40 +01:00
parent b492ed2969
commit b774ecbcb4
No known key found for this signature in database
GPG Key ID: AA6CCA6DE0E06424
7 changed files with 279 additions and 79 deletions

View File

@ -5,64 +5,70 @@
[quo2.components.icon :as icon]
[quo2.foundations.colors :as colors]))
(defn- get-icon-color [section]
(if (= section :bottom)
(defn- get-icon-color [danger?]
(if danger?
(colors/theme-colors colors/danger-50 colors/danger-60)
(colors/theme-colors colors/neutral-50 colors/neutral-40)))
(defn action [section]
(fn [{:keys [icon label sub-label right-icon on-press]}]
[rn/touchable-opacity {:on-press on-press}
[react/view {:style
{:flex 1
:height (if sub-label 56 47)
:margin-horizontal 20
:flex-direction :row}}
[react/view {:style
{:height 20
:margin-top :auto
:margin-bottom :auto
:margin-right 12
:width 20}}
[icon/icon icon
{:color (get-icon-color section)
:size 20}]]
[react/view
{:style
{:flex 1
:justify-content :center}}
(defn action [{:keys [icon
label
sub-label
right-icon
danger?
on-press]}]
[rn/touchable-opacity {:on-press on-press}
[react/view {:style
{:height (if sub-label 56 47)
:margin-horizontal 20
:flex-direction :row}}
[react/view {:style
{:height 20
:margin-top :auto
:margin-bottom :auto
:margin-right 12
:width 20}}
[icon/icon icon
{:color (get-icon-color danger?)
:size 20}]]
[react/view
{:style
{:flex 1
:justify-content :center}}
[text/text
{:size :paragraph-1
:weight :medium
:style {:color
(when danger?
(colors/theme-colors colors/danger-50 colors/danger-60))}}
label]
(when sub-label
[text/text
{:size :paragraph-1
:weight :medium
:style
{:color (when (= section :bottom)
(colors/theme-colors colors/danger-50 colors/danger-60))}}
label]
(when sub-label [text/text
{:size :paragraph-2
:style
{:color (colors/theme-colors colors/neutral-50 colors/neutral-40)}}
sub-label])]
(when right-icon
[react/view {:style
{:height 20
:margin-top :auto
:margin-bottom :auto
:width 20}}
[icon/icon right-icon
{:color (get-icon-color section)
:size 20}]])]]))
{:size :paragraph-2
:style {:color
(colors/theme-colors colors/neutral-50 colors/neutral-40)}}
sub-label])]
(when right-icon
[react/view {:style
{:height 20
:margin-top :auto
:margin-bottom :auto
:width 20}}
[icon/icon right-icon
{:color (get-icon-color danger?)
:size 20}]])]])
(defn action-drawer [{:keys [actions actions-with-consequence]}]
(defn divider []
[rn/view {:style {:border-top-width 1
:border-top-color (colors/theme-colors colors/neutral-10 colors/neutral-80)
:margin-top 8
:margin-bottom 7
:align-items :center
:flex-direction :row}}])
(defn action-drawer [sections]
[:<> {:style
{:flex 1}}
(map (action :top) actions)
(when actions-with-consequence
[:<>
[rn/view {:style {:border-top-width 1
:border-top-color (colors/theme-colors colors/neutral-10 colors/neutral-80)
:margin-top 8
:margin-bottom 7
:align-items :center
:flex-direction :row}}]
(map (action :bottom) actions-with-consequence)])])
(interpose
[divider]
(for [actions sections]
(map action actions)))])

View File

@ -14,24 +14,27 @@
:key :show-red-options?
:type :boolean}])
(def options-with-consequences [{:icon :main-icons2/share-context
:label "Clear history"}])
(def options-with-consequences [{:icon :main-icons2/delete
:danger? true
:label "Clear history"}])
(defn render-action-sheet [state]
[quo2/action-drawer {:actions-with-consequence (when (:show-red-options? @state) options-with-consequences)
:actions [{:icon :main-icons2/share-context
:label "View channel members and details"}
{:icon :main-icons2/communities
:label "Mark as read"}
{:icon :main-icons2/muted
:label (if (:muted? @state) "Unmute channel" "Mute channel")
:right-icon :main-icons2/chevron-right
:sub-label (when (:muted? @state) "Muted for 15 min")}
{:icon :main-icons2/scan
:right-icon :main-icons2/chevron-right
:label "Fetch messages"}
{:icon :main-icons2/add-user
:label "Share link to the channel"}]}])
[quo2/action-drawer (cond-> [[{:icon :main-icons2/friend
:label "View channel members and details"}
{:icon :main-icons2/communities
:label "Mark as read"}
{:icon :main-icons2/muted
:label (if (:muted? @state) "Unmute channel" "Mute channel")
:right-icon :main-icons2/chevron-right
:sub-label (when (:muted? @state) "Muted for 15 min")}
{:icon :main-icons2/scan
:right-icon :main-icons2/chevron-right
:label "Fetch messages"}
{:icon :main-icons2/add-user
:label "Share link to the channel"}]]
(:show-red-options? @state)
(conj options-with-consequences))])
(defn cool-preview []
(let [state (reagent/atom {:muted? true

View File

@ -176,6 +176,11 @@
:on-error #(log/error "failed to clear history " chat-id %)}]}
(clear-history chat-id remove-chat?)))
(fx/defn chat-deactivated
{:events [::chat-deactivated]}
[_ chat-id]
(log/debug "chat deactivated" chat-id))
(fx/defn deactivate-chat
"Deactivate chat in db, no side effects"
[{:keys [db now] :as cofx} chat-id]
@ -185,10 +190,10 @@
(assoc-in db [:chats chat-id :active] false)
(update db :chats dissoc chat-id))
(update :chats-home-list disj chat-id)
(assoc-in [:current-chat-id] nil))
(assoc :current-chat-id nil))
::json-rpc/call [{:method "wakuext_deactivateChat"
:params [{:id chat-id}]
:on-success #(log/debug "chat deactivated" chat-id)
:on-success #(re-frame/dispatch [::chat-deactivated chat-id])
:on-error #(log/error "failed to create public chat" chat-id %)}]}
(clear-history chat-id true)))
@ -227,9 +232,7 @@
{:clear-message-notifications
[[chat-id] (get-in db [:multiaccount :remote-push-notifications-enabled?])]}
(deactivate-chat chat-id)
(offload-messages chat-id)
(when (not (= (:view-id db) :home))
(navigation/pop-to-root-tab :chat-stack))))
(offload-messages chat-id)))
(fx/defn show-more-chats
{:events [:chat.ui/show-more-chats]}
@ -385,6 +388,11 @@
(log/error "mute chat failed" chat-id error)
{:db (assoc-in db [:chats chat-id :muted] (not muted?))})
(fx/defn mute-chat-toggled-successfully
{:events [::mute-chat-toggled-successfully]}
[_ chat-id]
(log/debug "muted chat successfully" chat-id))
(fx/defn mute-chat
{:events [::mute-chat-toggled]}
[{:keys [db] :as cofx} chat-id muted?]
@ -393,7 +401,7 @@
::json-rpc/call [{:method method
:params [chat-id]
:on-error #(re-frame/dispatch [::mute-chat-failed chat-id muted? %])
:on-success #(log/debug "muted chat successfully")}]}))
:on-success #(re-frame/dispatch [::mute-chat-toggled-successfully chat-id])}]}))
(fx/defn show-profile
{:events [:chat.ui/show-profile]}

View File

@ -4,6 +4,7 @@
[clojure.string :as string]
[re-frame.core :as rf]
status-im.events
[status-im.chat.models :as chat.models]
[status-im.utils.security :as security]
[status-im.multiaccounts.logout.core :as logout]
[status-im.transport.core :as transport]
@ -231,5 +232,65 @@
(logout!) (rf-test/wait-for [::logout/logout-method] ; we need to logout to make sure the node is not in an inconsistent state between tests
(assert-logout))))))))
(deftest delete-chat-test
(log/info "========= delete-chat-test ==================")
(rf-test/run-test-async
(initialize-app!)
(rf-test/wait-for
[:status-im.init.core/initialize-view]
(generate-and-derive-addresses!)
(rf-test/wait-for
[:multiaccount-generate-and-derive-addresses-success] ; wait for the keys
(assert-multiaccount-loaded)
(create-multiaccount!)
(rf-test/wait-for
[::transport/messenger-started]
(assert-messenger-started)
(rf/dispatch-sync [:chat.ui/start-chat chat-id]) ;; start a new chat
(rf-test/wait-for
[:status-im.chat.models/one-to-one-chat-created]
(rf/dispatch-sync [:chat.ui/navigate-to-chat chat-id])
(is (= chat-id @(rf/subscribe [:chats/current-chat-id])))
(is @(rf/subscribe [:chats/chat chat-id]))
(rf/dispatch-sync [:chat.ui/remove-chat-pressed chat-id])
(rf/dispatch-sync [:chat.ui/remove-chat chat-id])
(rf-test/wait-for
[::chat.models/chat-deactivated]
(is (not @(rf/subscribe [:chats/chat chat-id])))
(logout!) (rf-test/wait-for [::logout/logout-method] ; we need to logout to make sure the node is not in an inconsistent state between tests
(assert-logout)))))))))
(deftest mute-chat-test
(log/info "========= mute-chat-test ==================")
(rf-test/run-test-async
(initialize-app!)
(rf-test/wait-for
[:status-im.init.core/initialize-view]
(generate-and-derive-addresses!)
(rf-test/wait-for
[:multiaccount-generate-and-derive-addresses-success] ; wait for the keys
(assert-multiaccount-loaded)
(create-multiaccount!)
(rf-test/wait-for
[::transport/messenger-started]
(assert-messenger-started)
(rf/dispatch-sync [:chat.ui/start-chat chat-id]) ;; start a new chat
(rf-test/wait-for
[:status-im.chat.models/one-to-one-chat-created]
(rf/dispatch-sync [:chat.ui/navigate-to-chat chat-id])
(is (= chat-id @(rf/subscribe [:chats/current-chat-id])))
(is @(rf/subscribe [:chats/chat chat-id]))
(rf/dispatch-sync [::chat.models/mute-chat-toggled chat-id true])
(rf-test/wait-for
[::chat.models/mute-chat-toggled-successfully]
(is @(rf/subscribe [:chats/muted chat-id]))
(rf/dispatch-sync [::chat.models/mute-chat-toggled chat-id false])
(rf-test/wait-for
[::chat.models/mute-chat-toggled-successfully]
(is (not @(rf/subscribe [:chats/muted chat-id])))
(logout!) (rf-test/wait-for [::logout/logout-method] ; we need to logout to make sure the node is not in an inconsistent state between tests
(assert-logout))))))))))
(comment
(run-tests))

View File

@ -0,0 +1,117 @@
(ns status-im.ui2.screens.chat.actions
(:require
[status-im.chat.models :as chat.models]
[status-im.chat.models.pin-message :as models.pin-message]
[status-im.i18n.i18n :as i18n]
[status-im.constants :as constants]
[status-im.utils.handlers :refer [<sub >evt]]
[quo2.components.drawers.action-drawers :as drawer]))
(defn- entry [icon label on-press danger?]
{:pre [(keyword? icon)
(string? label)
(fn? on-press)
(boolean? danger?)]}
{:icon icon
:label label
:on-press on-press
:danger? danger?})
(defn hide-sheet-and-dispatch [event]
(>evt [:bottom-sheet/hide])
(>evt event))
(defn show-profile-action [chat-id]
(hide-sheet-and-dispatch [:chat.ui/show-profile chat-id])
(>evt [::models.pin-message/load-pin-messages chat-id]))
(defn mark-all-read-action [chat-id]
(hide-sheet-and-dispatch [:chat/mark-all-as-read chat-id]))
(defn edit-nickname-action [chat-id]
(hide-sheet-and-dispatch [:chat.ui/edit-nickname chat-id]))
(defn mute-chat-action [chat-id]
(hide-sheet-and-dispatch [::chat.models/mute-chat-toggled chat-id true]))
(defn unmute-chat-action [chat-id]
(hide-sheet-and-dispatch [::chat.models/mute-chat-toggled chat-id false]))
(defn clear-history-action [chat-id]
(hide-sheet-and-dispatch [:chat.ui/clear-history-pressed chat-id]))
(defn delete-chat-action [chat-id]
(hide-sheet-and-dispatch [:chat.ui/remove-chat-pressed chat-id]))
(defn mute-chat-entry [muted? chat-id]
(entry :main-icons2/muted
(i18n/label
(if muted?
:unmute-chat
:mute-chat))
(if muted?
#(unmute-chat-action chat-id)
#(mute-chat-action chat-id))
false))
(defn mark-as-read-entry [chat-id]
(entry :main-icons2/check
(i18n/label :mark-as-read)
#(mark-all-read-action chat-id)
false))
(defn clear-history-entry [chat-id]
(entry :main-icons2/delete
(i18n/label :clear-history)
#(clear-history-action chat-id)
true))
(defn delete-chat-entry [chat-id]
(entry :main-icons2/delete
(i18n/label :delete-chat)
#(delete-chat-action chat-id)
true))
(defn view-profile-entry [chat-id]
(entry :main-icons2/friend
(i18n/label :view-profile)
#(show-profile-action chat-id)
false))
(defn edit-nickname-entry [chat-id]
(entry :main-icons2/edit
(i18n/label :edit-nickname)
#(edit-nickname-action chat-id)
false))
(defn destructive-actions [chat-id]
[(clear-history-entry chat-id)
(delete-chat-entry chat-id)])
(defn notification-actions [muted? chat-id]
[(mute-chat-entry muted? chat-id)
(mark-as-read-entry chat-id)])
(defn one-to-one-actions [muted? chat-id]
[drawer/action-drawer [[(view-profile-entry chat-id)
(edit-nickname-entry chat-id)]
(notification-actions muted? chat-id)
(destructive-actions chat-id)]])
(defn public-chat-actions [muted? chat-id]
[drawer/action-drawer [(notification-actions muted? chat-id)
(destructive-actions chat-id)]])
(defn private-group-chat-actions [muted? chat-id]
[drawer/action-drawer [(notification-actions muted? chat-id)
(destructive-actions chat-id)]])
(defn actions [chat-type chat-id]
(let [muted? (<sub [:chats/muted chat-id])]
(case chat-type
constants/one-to-one-chat-type
[one-to-one-actions muted? chat-id]
constants/public-chat-type
[public-chat-actions muted? chat-id]
constants/private-group-chat-type
[private-group-chat-actions muted? chat-id])))

View File

@ -8,6 +8,7 @@
[status-im.ui.components.list.views :as list]
[status-im.ui.components.react :as react]
[status-im.ui.screens.home.styles :as styles]
[status-im.ui2.screens.chat.actions :as actions]
[status-im.ui.screens.home.views.inner-item :refer [home-list-item]]
[quo.design-system.colors :as quo.colors]
[quo.core :as quo]
@ -19,7 +20,6 @@
[status-im.utils.utils :as utils]
[status-im.ui.components.topbar :as topbar]
[status-im.ui.components.plus-button :as components.plus-button]
[status-im.ui.screens.chat.sheets :as sheets]
[status-im.ui.components.tabbar.core :as tabbar]
[status-im.ui.components.invite.views :as invite]
[status-im.utils.handlers :refer [<sub >evt]]
@ -117,7 +117,7 @@
(re-frame/dispatch [:set :public-group-topic nil])
(re-frame/dispatch [:search/home-filter-changed nil]))}])])))
(defn render-fn [{:keys [chat-id] :as home-item}]
(defn render-fn [{:keys [chat-id chat-type] :as home-item}]
[home-list-item
home-item
{:on-press (fn []
@ -129,7 +129,9 @@
(re-frame/dispatch [:accept-all-activity-center-notifications-from-chat chat-id]))
:on-long-press #(re-frame/dispatch [:bottom-sheet/show-sheet
{:content (fn []
[sheets/actions home-item])}])}])
[actions/actions
chat-type
chat-id])}])}])
(defn- render-contact [{:keys [public-key] :as row}]
(let [[first-name second-name] (multiaccounts/contact-two-names row true)

View File

@ -1480,6 +1480,7 @@
"page-camera-request-blocked": "camera requests blocked. To enable camera requests go to Settings",
"nickname": "Nickname",
"add-nickname": "Add a nickname (optional)",
"edit-nickname": "Edit nickname",
"nickname-description": "Nicknames help you identify others in Status.\nOnly you can see the nicknames youve added",
"accept": "Accept",
"group-invite": "Group invite",
@ -1510,6 +1511,8 @@
"name-optional": "Name (optional)",
"mute": "Mute",
"unmute": "Unmute",
"mute-chat": "Mute chat",
"unmute-chat": "Unmute chat",
"mute-community": "Mute community",
"unmute-community": "Unmute community",
"scan-tokens": "Scan tokens",