fix: action/reaction drawer UI issue (#19630)

This commit is contained in:
yqrashawn 2024-05-17 20:12:03 +08:00 committed by GitHub
parent afd92d2df1
commit cfe9d6e539
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 289 additions and 240 deletions

View File

@ -6,24 +6,26 @@
(h/describe "Drawers: drawer-action"
(h/test "default render"
(h/render-with-theme-provider [drawer-action/view {}])
(h/render-with-theme-provider [drawer-action/view {:accessibility-label :container}])
(h/is-truthy (h/query-by-label-text :container)))
(h/test "on-press-in changes internal state to :pressed"
(h/render-with-theme-provider [drawer-action/view {}])
(h/render-with-theme-provider [drawer-action/view {:accessibility-label :container}])
(h/fire-event :on-press-in (h/get-by-label-text :container))
(h/wait-for #(h/has-style (h/query-by-label-text :container)
{:backgroundColor (colors/resolve-color :blue :light 5)})))
(h/test "render default action with state :selected"
(h/render-with-theme-provider [drawer-action/view {:state :selected}])
(h/render-with-theme-provider [drawer-action/view
{:state :selected :accessibility-label :container}])
(h/has-style (h/query-by-label-text :container)
{:backgroundColor (colors/resolve-color :blue :light 5)})
(h/is-truthy (h/query-by-label-text :check-icon)))
(h/test "call on-press"
(let [on-press (h/mock-fn)]
(h/render-with-theme-provider [drawer-action/view {:on-press on-press}])
(h/render-with-theme-provider [drawer-action/view
{:on-press on-press :accessibility-label :container}])
(h/fire-event :on-press (h/get-by-label-text :container))
(h/was-called on-press)))
@ -38,7 +40,8 @@
(h/test "render :toggle action with state :selected"
(h/render-with-theme-provider [drawer-action/view
{:action :toggle
{:accessibility-label :container
:action :toggle
:state :selected}])
(h/is-truthy (h/query-by-label-text "toggle-on"))
(h/has-style (h/query-by-label-text :container)
@ -51,4 +54,5 @@
:description "Just a small desc"}])
(h/is-truthy (h/query-by-label-text :left-icon))
(h/is-truthy (h/query-by-text "Check contact"))
(h/has-style (h/query-by-text "Check contact") {:color colors/neutral-100})
(h/is-truthy (h/query-by-text "Just a small desc"))))

View File

@ -4,6 +4,8 @@
[:=>
[:cat
[:map {:closed true}
[:accessibility-label {:optional true} [:maybe :keyword]]
[:type {:optional true} [:maybe [:enum :main :danger]]]
[:action {:optional true} [:maybe [:enum :arrow :toggle]]]
[:icon {:optional true} [:maybe :keyword]]
[:description {:optional true} [:maybe :string]]

View File

@ -28,6 +28,23 @@
{:flex 1
:margin-right 12})
(defn text
[{:keys [theme blur? type]}]
(let [base {:weight :medium}
theme-with-blur (if blur? :blue theme)
matcher [theme-with-blur type]
color
(case matcher
([:dark :main] [:light :main]) (colors/theme-colors colors/neutral-100
colors/white
theme)
[:blur :main] colors/white-70-blur
([:dark :danger] [:light :danger]) (colors/theme-colors colors/danger-50
colors/danger-60
theme)
[:blur :danger] colors/danger-60)]
(assoc-in base [:style :color] color)))
(defn- neutral-color
[theme blur?]
(if blur?
@ -41,8 +58,14 @@
:margin-top 1})
(defn icon-color
[{:keys [theme blur?]}]
(neutral-color theme blur?))
[{:keys [theme blur? type]}]
(let [theme-with-blur (if blur? :blue theme)
matcher [theme-with-blur type]]
(case matcher
([:dark :main] [:light :main]) (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)
[:blur :main] colors/white-70-blur
([:dark :danger] [:light :danger]) (colors/theme-colors colors/danger-50 colors/danger-60 theme)
[:blur :danger] colors/danger-60)))
(defn desc
[{:keys [theme blur?]}]

View File

@ -10,11 +10,13 @@
[schema.core :as schema]))
(defn view-internal
[{:keys [action icon description state title on-press
customization-color blur?]
[{:keys [action icon description state title on-press customization-color
blur? accessibility-label]
action-type :type
:or {customization-color :blue
blur? false}}]
(let [theme (quo.theme/use-theme)
action-type (or action-type :main)
[pressed? set-pressed] (rn/use-state false)
on-press-in (rn/use-callback #(set-pressed true))
on-press-out (rn/use-callback #(set-pressed false))]
@ -29,16 +31,21 @@
:pressed? pressed?
:description? (not-empty description)
:blur? blur?})
:accessibility-label :container}
:accessibility-label accessibility-label}
(when icon
[icon/icon icon
{:accessibility-label :left-icon
:container-style (style/left-icon)
:color (style/icon-color {:theme theme
:type action-type
:blur? blur?})}])
[rn/view {:style (style/text-container)}
[text/text {:weight :medium}
[rn/view
{:style (style/text-container)}
[text/text
(style/text {:theme theme
:type action-type
:blur? blur?})
title]
(when (seq description)
@ -61,6 +68,7 @@
[icon/icon :i/chevron-right
{:accessibility-label :arrow-icon
:color (style/icon-color {:theme theme
:type action-type
:blur? blur?})}]
(= state :selected)

View File

@ -3,39 +3,14 @@
[quo.foundations.colors :as colors]
[react-native.platform :as platform]))
(defn- primary-name-top-offset
[size]
(when (= size 15)
(cond platform/ios? 1
platform/android? -0.5
:else 0)))
(defn- primary-name-margin-bottom-offset
[size]
(when (and (= size 15)
(or platform/ios? platform/android?))
-0.25))
(defn- primary-name-layout-offsets
[size]
;; NOTE(seanstrom): We need to sometimes offset the primary-name to align the baseline of the text
;; while avoiding shifting elements downward.
{:top (primary-name-top-offset size)
:margin-bottom (primary-name-margin-bottom-offset size)})
(defn container
[size]
(cond->
{:flex-shrink 1
:flex-wrap :nowrap
:flex-direction :row
:align-items :baseline
;; NOTE(seanstrom): Because we're offseting the primary-name we need to inverse the offset on the
;; container to avoid shifting elements downward
:top (* -1 (primary-name-top-offset size))})
(def details-container
{:flex-direction :row
:margin-left 8})
:align-items :center
:height (if (= size 15) 22 18)}))
(defn middle-dot
[theme]
@ -48,11 +23,15 @@
(defn primary-name
[muted? theme size]
(merge (primary-name-layout-offsets size)
{:color (if muted?
colors/neutral-50
(colors/theme-colors colors/neutral-100 colors/white theme))
:flex-shrink 1}))
;; iOS: primary-name height is 22.3 / 18.7
;; Android: primary-name height is 21.8 / 18.5
:margin-vertical (if (= size 15)
(if platform/ios? -0.15 0)
(if platform/ios? -0.35 -0.25))
:flex-shrink 1})
(defn secondary-name
[theme]
@ -60,13 +39,9 @@
:color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)})
(defn icon-container
[is-first?]
[is-first? size]
{:margin-left (if is-first? 4 2)
;; NOTE(seanstrom): Because we're using flex baseline to align elements
;; we need to offset the icon container to match the designs.
:top (cond platform/ios? 1
platform/android? 2
:else 0)})
:padding-top (if (= size 15) 6 4)})
(defn time-text
[theme]

View File

@ -13,7 +13,35 @@
[{:keys [primary-name secondary-name style short-chat-key time-str contact? verified? untrustworthy?
muted? size]
:or {size 13}}]
(let [theme (quo.theme/use-theme)]
(let [theme (quo.theme/use-theme)
short-chat-key-component
(when (and (not verified?) short-chat-key)
[text/text
{:weight :monospace
:size :label
:number-of-lines 1
:style (style/chat-key-text theme)}
short-chat-key])
time-str-component
(when time-str
[text/text
{:monospace true
:size :label
:accessibility-label :message-timestamp
:number-of-lines 1
:style (style/time-text theme)}
time-str])
middle-dot-seperator-component
(when (and short-chat-key-component time-str-component)
[text/text
{:monospace true
:size :label
:number-of-lines 1
:style (style/middle-dot theme)}
middle-dot])]
[rn/view
{:style (merge (style/container size) style)}
[text/text
@ -23,7 +51,7 @@
:accessibility-label :author-primary-name
:style (style/primary-name muted? theme size)}
primary-name]
(when (not (string/blank? secondary-name))
(when-not (string/blank? secondary-name)
[:<>
[text/text
{:size :label
@ -41,38 +69,22 @@
[icons/icon :main-icons2/contact
{:size 12
:no-color true
:container-style (style/icon-container true)}])
:container-style (style/icon-container true size)}])
(cond
verified?
[icons/icon :main-icons2/verified
{:size 12
:no-color true
:container-style (style/icon-container contact?)}]
:container-style (style/icon-container contact? size)}]
untrustworthy?
[icons/icon :main-icons2/untrustworthy
{:size 12
:no-color true
:container-style (style/icon-container contact?)}])
[rn/view {:style style/details-container}
(when (and (not verified?) short-chat-key)
[text/text
{:weight :monospace
:size :label
:number-of-lines 1
:style (style/chat-key-text theme)}
short-chat-key])
(when (and (not verified?) time-str short-chat-key)
[text/text
{:monospace true
:size :label
:number-of-lines 1
:style (style/middle-dot theme)}
middle-dot])
(when time-str
[text/text
{:monospace true
:size :label
:accessibility-label :message-timestamp
:number-of-lines 1
:style (style/time-text theme)}
time-str])]]))
:container-style (style/icon-container contact? size)}])
(when (or short-chat-key-component time-str-component)
[rn/view {:style {:width 8}}])
short-chat-key-component
middle-dot-seperator-component
time-str-component]))

View File

@ -5,7 +5,8 @@
[react-native.core :as rn]))
(defn view
[{:keys [reactions on-press on-long-press add-reaction? on-press-add use-case container-style]}]
[{:keys [reactions on-press on-long-press hide-new-reaction-button? on-press-add use-case
container-style]}]
[rn/view {:style (merge style/container container-style)}
(for [emoji-reaction reactions
:let [{:keys [emoji emoji-id emoji-reaction-id quantity own]} emoji-reaction]]
@ -19,7 +20,7 @@
:on-press #(on-press emoji-reaction)
:on-long-press #(on-long-press emoji-reaction)
:accessibility-label (str "emoji-reaction-" emoji-id)}])
(when add-reaction?
(when-not hide-new-reaction-button?
[react-selector/view
{:on-press on-press-add
:state :add-reaction

View File

@ -1,12 +1,31 @@
(ns quo.components.selectors.reactions-selector.style
(:require
[quo.foundations.colors :as colors]))
[quo.foundations.colors :as colors]
[react-native.platform :as platform]))
(defn container
[pressed? theme]
{:padding 10
:border-radius 12
(merge
{:border-radius 12
:border-width 1
:border-color (colors/theme-colors colors/neutral-20 colors/neutral-80 theme)
:background-color (when pressed?
(colors/theme-colors colors/neutral-10 colors/neutral-80-opa-40 theme))})
(colors/theme-colors colors/neutral-10 colors/neutral-80-opa-40 theme))}
(if platform/ios?
{:padding 9.5
:padding-horizontal 8}
{:padding 8})))
(def emoji-text-style
(merge
{:line-height 20
:text-align :center}
(if platform/ios?
{:line-height 20
:font-size 17
:text-align :center}
{:font-size 18
:height 24
:width 24
:text-align :center
:text-align-vertical :center})))

View File

@ -20,4 +20,6 @@
:style (merge (style/container pressed? theme)
container-style)
:on-press on-press}
[rn/text (reactions.resource/system-emojis emoji)]]))
[rn/text
{:style style/emoji-text-style}
(reactions.resource/system-emojis emoji)]]))

View File

@ -8,7 +8,6 @@
(defn sheet
[{:keys [max-height]}]
{:position :absolute
:overflow :hidden
:bottom 0
:left 0
:right 0

View File

@ -15,7 +15,7 @@
:emoji-id emoji-id}])))
(defn- on-long-press
[{:keys [message-id emoji-id user-message-content reactions-order theme]}]
[{:keys [message-id emoji-id user-message-content-render-fn reactions-order theme]}]
(rf/dispatch
[:reactions/get-authors-by-message-id
{:message-id message-id
@ -31,19 +31,19 @@
[drawers/reaction-authors
{:reactions-order reactions-order
:theme theme}])
:selected-item (fn [] user-message-content)
:selected-item (fn [] [user-message-content-render-fn
{:hide-reactions? true}])
:padding-bottom-override 0}]))}]))
(defn- on-press-add
[{:keys [chat-id message-id user-message-content]}]
[{:keys [chat-id message-id user-message-content-render-fn]}]
(rf/dispatch [:dismiss-keyboard])
(rf/dispatch
[:show-bottom-sheet
{:content (fn [] [drawers/reactions
{:chat-id chat-id
:message-id message-id}])
:selected-item (fn []
user-message-content)}]))
:selected-item (fn [] [user-message-content-render-fn])}]))
(defn- add-emoji-key
[reaction]
@ -52,23 +52,25 @@
(get constants/reactions (:emoji-id reaction))))
(defn message-reactions-row
[{:keys [message-id chat-id pinned-by preview?]} user-message-content]
[{:keys [message-id chat-id pinned-by hide-new-reaction-button?]} user-message-content-render-fn]
(let [theme (quo.theme/use-theme)
reactions (rf/sub [:chats/message-reactions message-id chat-id])]
[:<>
(when (seq reactions)
[quo/react
{:container-style {:margin-left 44
:margin-top 8}
:reactions (map add-emoji-key reactions)
:add-reaction? (not preview?)
:hide-new-reaction-button? hide-new-reaction-button?
:use-case (when pinned-by :pinned)
:on-press #(on-press (assoc % :message-id message-id))
:on-long-press #(on-long-press (assoc %
:message-id message-id
:theme theme
:reactions-order (map :emoji-id reactions)
:user-message-content user-message-content))
:reactions-order (map :emoji-id
reactions)
:user-message-content-render-fn
user-message-content-render-fn))
:on-press-add #(on-press-add {:chat-id chat-id
:message-id message-id
:user-message-content user-message-content})}])]))
:user-message-content-render-fn
user-message-content-render-fn})}])))

View File

@ -16,22 +16,20 @@
(assoc :margin-top 4)))
(defn user-message-content
[{:keys [outgoing outgoing-status six-reactions? window-scale small-screen? preview?]}]
[{:keys [outgoing outgoing-status six-reactions? window-scale small-screen?]}]
{:border-radius 16
:padding-horizontal (if preview? 12 8)
:padding-top (if preview? 8 4)
:padding-bottom (if preview?
12
(if (or small-screen?
:padding-horizontal 8
:padding-top 4
:padding-bottom (if (or small-screen?
(and
(> 3 window-scale)
six-reactions?))
(* message-padding-scaling-ratio window-scale)
4))
4)
:opacity (if (and outgoing (= outgoing-status :sending))
0.5
1)})
(def drawer-message-container
{:padding-top 4
:padding-bottom 8})
:padding-bottom 4})

View File

@ -35,19 +35,19 @@
(def delivery-state-showing-time-ms 3000)
(defn avatar-container
[{:keys [content last-in-group? pinned-by quoted-message from]} show-reactions?
in-reaction-and-action-menu? show-user-info? in-pinned-view?]
[{:keys [content last-in-group? pinned-by quoted-message from]} hide-reactions?
in-reaction-or-action-menu? show-user-info? in-pinned-view?]
(if (or (and (seq (:response-to content))
quoted-message)
last-in-group?
show-user-info?
pinned-by
(not show-reactions?)
in-reaction-and-action-menu?)
hide-reactions?
in-reaction-or-action-menu?)
[avatar/avatar
{:public-key from
:size :small
:hide-ring? (or in-pinned-view? in-reaction-and-action-menu?)}]
:hide-ring? (or in-pinned-view? in-reaction-or-action-menu?)}]
[rn/view {:padding-top 4 :width 32}]))
(defn author
@ -58,15 +58,15 @@
quoted-message
from
timestamp]}
show-reactions?
in-reaction-and-action-menu?
hide-reactions?
in-reaction-or-action-menu?
show-user-info?]
(when (or (and (seq (:response-to content)) quoted-message)
last-in-group?
pinned-by
show-user-info?
(not show-reactions?)
in-reaction-and-action-menu?)
hide-reactions?
in-reaction-or-action-menu?)
(let [[primary-name secondary-name] (rf/sub [:contacts/contact-two-names-by-identity from])
{:keys [ens-verified added?]} (rf/sub [:contacts/contact-by-address from])]
[quo/author
@ -158,8 +158,8 @@
(defn user-message-content
[]
(let [show-delivery-state? (reagent/atom false)]
(fn [{:keys [message-data context keyboard-shown? show-reactions? in-reaction-and-action-menu?
show-user-info? preview?]}]
(fn [{:keys [message-data context keyboard-shown? hide-reactions?
in-reaction-or-action-menu? show-user-info?]}]
(let [theme (quo.theme/use-theme)
{:keys [content-type quoted-message content outgoing outgoing-status pinned-by pinned
last-in-group? message-id chat-id]} message-data
@ -204,7 +204,7 @@
:small-screen? rn/small-screen?
:window-scale window-scale
:six-reactions? six-reactions?
:preview? preview?})
:in-reaction-or-action-menu? in-reaction-or-action-menu?})
:on-press (fn []
(if (and platform/ios? keyboard-shown?)
(do
@ -226,18 +226,16 @@
[rn/view
{:style {:padding-horizontal 4
:flex-direction :row}}
[avatar-container message-data show-reactions? in-reaction-and-action-menu? show-user-info?
[avatar-container message-data hide-reactions? in-reaction-or-action-menu? show-user-info?
(:in-pinned-view? context)]
(into
(if show-reactions?
[rn/view]
[gesture/scroll-view])
(if hide-reactions?
[gesture/scroll-view]
[rn/view])
[{:style {:margin-left 8
:flex 1
:gap 1
:max-height (when-not show-reactions?
(* 0.4 height))}}
[author message-data show-reactions? in-reaction-and-action-menu? show-user-info?]
:max-height (when hide-reactions? (* 0.4 height))}}
[author message-data hide-reactions? in-reaction-or-action-menu? show-user-info?]
(condp = content-type
constants/content-type-text
[content.text/text-content message-data context]
@ -272,17 +270,19 @@
(when @show-delivery-state?
[status/status outgoing-status])])]
(when show-reactions?
[reactions/message-reactions-row (assoc message-data :preview? preview?)
[rn/view {:pointer-events :none}
(when-not hide-reactions?
[reactions/message-reactions-row (assoc message-data :hide-new-reaction-button? true)
(fn [override-opts]
[rn/view
{:pointer-events :none
:style style/drawer-message-container}
[user-message-content
{:theme theme
(merge {:theme theme
:message-data message-data
:context context
:in-reaction-and-action-menu? true
:keyboard-shown? keyboard-shown?
:preview? true
:show-reactions? true}]]])]]))))
:in-reaction-or-action-menu? true
:keyboard-shown? keyboard-shown?}
override-opts)]])])]]))))
(defn on-long-press
[{:keys [deleted? deleted-for-me?] :as message-data} context keyboard-shown?]
@ -302,9 +302,8 @@
{:message-data message-data
:context context
:keyboard-shown? keyboard-shown?
:in-reaction-and-action-menu? true
:show-user-info? false
:show-reactions? true}]]))}]))
:in-reaction-or-action-menu? true
:show-user-info? false}]]))}]))
(defn check-if-system-message?
[content-type]
@ -349,5 +348,4 @@
[user-message-content
{:message-data message-data
:context context
:keyboard-shown? keyboard-shown?
:show-reactions? true}])]))
:keyboard-shown? keyboard-shown?}])]))

View File

@ -109,12 +109,14 @@
:on-press #(rf/dispatch [:chat.ui/edit-message message-data])
:label (i18n/label :t/edit-message)
:icon :i/edit
:accessibility-label :edit-message
:id :edit}])
(when (and able-to-send-message? (not= outgoing-status :sending) (not (or deleted? deleted-for-me?)))
[{:type :main
:on-press #(rf/dispatch [:chat.ui/reply-to-message message-data])
:label (i18n/label :t/message-reply)
:icon :i/reply
:accessibility-label :reply-message
:id :reply}])
(when (and (not (or deleted? deleted-for-me?))
(not= content-type constants/content-type-audio))
@ -123,6 +125,7 @@
(reply/get-quoted-text-with-mentions
(get content :parsed-text)))
:label (i18n/label :t/copy-text)
:accessibility-label :copy-text
:icon :i/copy
:id :copy}])
;; pinning images are temporarily disabled
@ -133,6 +136,7 @@
:label (i18n/label (if pinned-by
(if community? :t/unpin-from-channel :t/unpin-from-chat)
(if community? :t/pin-to-channel :t/pin-to-chat)))
:accessibility-label (if pinned-by :unpin-message :pin-message)
:icon :i/pin
:id (if pinned-by :unpin :pin)}])
(when-not (or deleted? deleted-for-me?)
@ -144,6 +148,7 @@
config/delete-message-for-me-undo-time-limit-ms]))
:label (i18n/label :t/delete-for-me)
:accessibility-label :delete-for-me
:icon :i/delete
:id :delete-for-me}])
(when (cond
@ -158,6 +163,7 @@
(rf/dispatch [:chat.ui/delete-message message-data
config/delete-message-undo-time-limit-ms]))
:label (i18n/label :t/delete-for-everyone)
:accessibility-label :delete-for-everyone
:icon :i/delete
:id :delete-for-all}])))
@ -180,7 +186,7 @@
[rn/view
{:style {:flex-direction :row
:justify-content :space-between
:padding-horizontal 30
:padding-horizontal 22
:padding-top 5
:padding-bottom 15}}
(for [[id reaction-name] constants/reactions
@ -221,10 +227,10 @@
(for [action main-actions]
(let [on-press (:on-press action)]
^{:key (:id action)}
[quo/menu-item
[quo/drawer-action
{:type :main
:title (:label action)
:accessibility-label (:label action)
:accessibility-label (:accessibility-label action)
:icon (:icon action)
:on-press (fn []
(rf/dispatch [:hide-bottom-sheet])
@ -238,10 +244,10 @@
(for [action danger-actions]
(let [on-press (:on-press action)]
^{:key (:id action)}
[quo/menu-item
[quo/drawer-action
{:type :danger
:title (:label action)
:accessibility-label (:label action)
:accessibility-label (:accessibility-label action)
:icon (:icon action)
:on-press (fn []
(rf/dispatch [:hide-bottom-sheet])
@ -255,10 +261,10 @@
(for [action admin-actions]
(let [on-press (:on-press action)]
^{:key (:id action)}
[quo/menu-item
[quo/drawer-action
{:type :danger
:title (:label action)
:accessibility-label (:label action)
:accessibility-label (:accessibility-label action)
:icon (:icon action)
:on-press (fn []
(rf/dispatch [:hide-bottom-sheet])

View File

@ -15,7 +15,7 @@
(def ^:private memo-gen-quantity (memoize gen-quantity))
(def ^:private descriptor
[{:key :add-reaction?
[{:key :hide-new-reaction-button?
:type :boolean}
{:label "Reactions"
:key :reaction-ids
@ -37,7 +37,7 @@
(defn preview-react
[]
(let [state (reagent/atom {:add-reaction? true
(let [state (reagent/atom {:hide-new-reaction-button? true
:max-count 1000
:reaction-ids [1 2 3]
:use-case :default})
@ -64,7 +64,7 @@
:align-items :flex-start}
[quo/react
{:reactions reactions
:add-reaction? (:add-reaction? @state)
:hide-new-reaction-button? (:hide-new-reaction-button? @state)
:use-case (:use-case @state)
:on-press (fn [reaction]
(let [reaction-id (:emoji-id reaction)