Account selection: Use bottom sheet component (#18919)

Use bottom sheet component as designed in
Figma https://www.figma.com/file/h9wo4GipgZURbqqr1vShFN/Communities-for-Mobile?type=design&node-id=16409-98076&mode=design&t=jrVNb3JnWNurJwP8-0

- Fixes no animation when closing Addresses For Permissions modal:
  https://github.com/status-im/status-mobile/issues/18617
- Fixes drawer is always expanded:
  https://github.com/status-im/status-mobile/issues/18619

Additionally:

- Fixed incorrect code passing a function instance to a style prop, which leads
  to incorrect appearance for settings.category.reorder.view/view.
- Optimized Reagent a little bit by extracting functions that need to close over
  the community ID to the mount phase. That way, we avoid regenerating function
  instances on every re-render and Reagent can avoid a bit of re-work.

The challenge was to make the scrollable content work inside a
bottom sheet, while supporting the designs for a fixed (always visible) header
and footer.
This commit is contained in:
Icaro Motta 2024-03-04 09:23:30 -03:00 committed by GitHub
parent f8cd14296f
commit 27e177f18b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 143 additions and 140 deletions

View File

@ -16,9 +16,9 @@
(reagent/flush)) (reagent/flush))
(defn- reorder-category-internal (defn- reorder-category-internal
[{:keys [label data blur? theme]}] [{:keys [label data blur? theme container-style]}]
(reagent/with-let [atom-data (reagent/atom data)] (reagent/with-let [atom-data (reagent/atom data)]
[rn/view {:style style/container} [rn/view {:style (merge (style/container label) container-style)}
[text/text [text/text
{:weight :medium {:weight :medium
:size :paragraph-2 :size :paragraph-2

View File

@ -3,18 +3,18 @@
[quo.foundations.colors :as colors] [quo.foundations.colors :as colors]
[react-native.platform :as platform])) [react-native.platform :as platform]))
(def ^:private sheet-border-radius 20)
(defn sheet (defn sheet
[{:keys [top]} window-height selected-item] [{:keys [max-height]}]
{:position :absolute {:position :absolute
:max-height (- window-height top)
:z-index 1
:bottom 0 :bottom 0
:left 0 :left 0
:right 0 :right 0
:border-top-left-radius 20 :z-index 1
:border-top-right-radius 20 :max-height max-height
:overflow (when-not selected-item :hidden) :border-top-left-radius sheet-border-radius
:flex 1}) :border-top-right-radius sheet-border-radius})
(def gradient-bg (def gradient-bg
{:position :absolute {:position :absolute
@ -31,10 +31,11 @@
:bottom 0}) :bottom 0})
(defn sheet-content (defn sheet-content
[theme padding-bottom-override {:keys [bottom]} shell? bottom-margin] [{:keys [theme padding-bottom shell?]}]
{:border-top-left-radius 20 {:overflow :scroll
:border-top-right-radius 20 :padding-bottom padding-bottom
:padding-bottom (or padding-bottom-override (+ bottom bottom-margin)) :border-top-left-radius sheet-border-radius
:border-top-right-radius sheet-border-radius
:background-color (if shell? :background-color (if shell?
:transparent :transparent
(colors/theme-colors colors/white colors/neutral-95 theme))}) (colors/theme-colors colors/white colors/neutral-95 theme))})

View File

@ -56,6 +56,10 @@
(show translate-y bg-opacity) (show translate-y bg-opacity)
(hide translate-y bg-opacity window-height on-close)))))) (hide translate-y bg-opacity window-height on-close))))))
(defn- get-layout-height
[event]
(oops/oget event "nativeEvent.layout.height"))
(defn view (defn view
[{:keys [hide? insets]} [{:keys [hide? insets]}
{:keys [content selected-item padding-bottom-override border-radius on-close shell? {:keys [content selected-item padding-bottom-override border-radius on-close shell?
@ -64,6 +68,8 @@
(let [theme (quo.theme/use-theme-value) (let [theme (quo.theme/use-theme-value)
sheet-height (rn/use-ref-atom 0) sheet-height (rn/use-ref-atom 0)
item-height (rn/use-ref-atom 0) item-height (rn/use-ref-atom 0)
set-sheet-height (rn/use-callback #(reset! sheet-height (get-layout-height %)))
set-item-height (rn/use-callback #(reset! item-height (get-layout-height %)))
{window-height :height} (rn/get-window) {window-height :height} (rn/get-window)
bg-opacity (reanimated/use-shared-value 0) bg-opacity (reanimated/use-shared-value 0)
translate-y (reanimated/use-shared-value window-height) translate-y (reanimated/use-shared-value window-height)
@ -80,7 +86,10 @@
top (- window-height (:top insets) @sheet-height) top (- window-height (:top insets) @sheet-height)
bottom (if selected-item-smaller-than-sheet? bottom (if selected-item-smaller-than-sheet?
(+ @sheet-height bottom-margin) (+ @sheet-height bottom-margin)
(:bottom insets))] (:bottom insets))
sheet-max-height (- window-height (:top insets))
content-padding-bottom (or padding-bottom-override
(+ (:bottom insets) bottom-margin))]
(rn/use-effect (rn/use-effect
#(if hide? #(if hide?
(hide translate-y bg-opacity window-height on-close) (hide translate-y bg-opacity window-height on-close)
@ -105,7 +114,7 @@
[reanimated/view [reanimated/view
{:style (reanimated/apply-animations-to-style {:style (reanimated/apply-animations-to-style
{:transform [{:translateY translate-y}]} {:transform [{:translateY translate-y}]}
(style/sheet insets window-height selected-item))} (style/sheet {:max-height sheet-max-height}))}
(when shell? (when shell?
[blur/ios-view [blur/ios-view
{:style style/shell-bg {:style style/shell-bg
@ -114,14 +123,14 @@
:overlay-color :transparent}]) :overlay-color :transparent}])
(when selected-item (when selected-item
[rn/view [rn/view
{:on-layout #(reset! item-height (.-nativeEvent.layout.height ^js %)) {:on-layout set-item-height
:style :style (style/selected-item theme top bottom selected-item-smaller-than-sheet? border-radius)}
(style/selected-item theme top bottom selected-item-smaller-than-sheet? border-radius)}
[selected-item]]) [selected-item]])
[rn/view [rn/view
{:style (style/sheet-content theme padding-bottom-override insets shell? bottom-margin) {:on-layout set-sheet-height
:on-layout #(reset! sheet-height (.-nativeEvent.layout.height ^js %))} :style (style/sheet-content {:theme theme
:shell? shell?
:padding-bottom content-padding-bottom})}
(when (and gradient-cover? customization-color) (when (and gradient-cover? customization-color)
[rn/view {:style style/gradient-bg} [rn/view {:style style/gradient-bg}
[quo/gradient-cover [quo/gradient-cover

View File

@ -5,81 +5,92 @@
[react-native.gesture :as gesture] [react-native.gesture :as gesture]
[status-im.common.password-authentication.view :as password-authentication] [status-im.common.password-authentication.view :as password-authentication]
[status-im.contexts.communities.actions.accounts-selection.style :as style] [status-im.contexts.communities.actions.accounts-selection.style :as style]
[status-im.contexts.communities.actions.addresses-for-permissions.view :as addresses-for-permissions]
[status-im.contexts.communities.actions.airdrop-addresses.view :as airdrop-addresses]
[status-im.contexts.communities.actions.community-rules.view :as community-rules] [status-im.contexts.communities.actions.community-rules.view :as community-rules]
[status-im.contexts.communities.utils :as communities.utils] [status-im.contexts.communities.utils :as communities.utils]
[utils.i18n :as i18n] [utils.i18n :as i18n]
[utils.re-frame :as rf])) [utils.re-frame :as rf]))
(defn- join-community-and-navigate-back
[id]
(rf/dispatch [:password-authentication/show {:content (fn [] [password-authentication/view])}
{:label (i18n/label :t/join-open-community)
:on-press #(rf/dispatch [:communities/request-to-join-with-addresses
{:community-id id :password %}])}])
(rf/dispatch [:navigate-back]))
(defn view (defn view
[] []
(let [{id :community-id} (rf/sub [:get-screen-params]) (let [{id :community-id} (rf/sub [:get-screen-params])
{:keys [name color images]} (rf/sub [:communities/community id]) show-addresses-for-permissions (fn []
airdrop-account (rf/sub [:communities/airdrop-account id]) (rf/dispatch [:show-bottom-sheet
selected-accounts (rf/sub [:communities/selected-permission-accounts id]) {:community-id id
{:keys [highest-permission-role]} (rf/sub [:community/token-gated-overview id]) :content addresses-for-permissions/view}]))
highest-role-text (i18n/label (communities.utils/role->translation-key show-airdrop-addresses (fn []
highest-permission-role (rf/dispatch [:show-bottom-sheet
:t/member))] {:community-id id
[rn/safe-area-view {:style style/container} :content airdrop-addresses/view}]))
[quo/page-nav navigate-back #(rf/dispatch [:navigate-back])
{:text-align :left join-and-go-back (fn []
:icon-name :i/close (rf/dispatch [:password-authentication/show
:on-press #(rf/dispatch [:navigate-back]) {:content (fn [] [password-authentication/view])}
:accessibility-label :back-button}] {:label (i18n/label :t/join-open-community)
[quo/page-top :on-press
{:title (i18n/label :t/request-to-join) #(rf/dispatch
:description :context-tag [:communities/request-to-join-with-addresses
:context-tag {:type :community {:community-id id :password %}])}])
:size 24 (navigate-back))]
:community-logo (get-in images [:thumbnail :uri]) (fn []
:community-name name}}] (let [{:keys [name color images]} (rf/sub [:communities/community id])
[gesture/scroll-view airdrop-account (rf/sub [:communities/airdrop-account id])
[:<> selected-accounts (rf/sub [:communities/selected-permission-accounts id])
[quo/text {:keys [highest-permission-role]} (rf/sub [:community/token-gated-overview id])
{:style style/section-title highest-role-text (i18n/label (communities.utils/role->translation-key
:accessibility-label :community-rules-title highest-permission-role
:weight :semi-bold :t/member))]
:size :paragraph-1} [rn/safe-area-view {:style style/container}
(i18n/label :t/address-to-share)] [quo/page-nav
[quo/category {:text-align :left
{:list-type :settings :icon-name :i/close
:data [{:title (i18n/label :t/join-as-a {:role highest-role-text}) :on-press navigate-back
:on-press #(rf/dispatch [:open-modal :addresses-for-permissions :accessibility-label :back-button}]
{:community-id id}]) [quo/page-top
:description :text {:title (i18n/label :t/request-to-join)
:action :arrow :description :context-tag
:label :preview :context-tag {:type :community
:label-props {:type :accounts :size 24
:data selected-accounts} :community-logo (get-in images [:thumbnail :uri])
:description-props {:text (i18n/label :t/all-addresses)}} :community-name name}}]
{:title (i18n/label :t/for-airdrops) [gesture/scroll-view
:on-press #(rf/dispatch [:open-modal :airdrop-addresses [:<>
{:community-id id}]) [quo/text
:description :text {:style style/section-title
:action :arrow :accessibility-label :community-rules-title
:label :preview :weight :semi-bold
:label-props {:type :accounts :size :paragraph-1}
:data [airdrop-account]} (i18n/label :t/address-to-share)]
:description-props {:text (:name airdrop-account)}}]}] [quo/category
[quo/text {:list-type :settings
{:style style/section-title :data [{:title (i18n/label :t/join-as-a {:role highest-role-text})
:accessibility-label :community-rules-title :on-press show-addresses-for-permissions
:weight :semi-bold :description :text
:size :paragraph-1} :action :arrow
(i18n/label :t/community-rules)] :label :preview
[community-rules/view id]]] :label-props {:type :accounts
[rn/view {:style (style/bottom-actions)} :data selected-accounts}
[quo/slide-button :description-props {:text (i18n/label :t/all-addresses)}}
{:size :size-48 {:title (i18n/label :t/for-airdrops)
:track-text (i18n/label :t/slide-to-request-to-join) :on-press show-airdrop-addresses
:track-icon :i/face-id :description :text
:customization-color color :action :arrow
:on-complete #(join-community-and-navigate-back id)}]]])) :label :preview
:label-props {:type :accounts
:data [airdrop-account]}
:description-props {:text (:name airdrop-account)}}]}]
[quo/text
{:style style/section-title
:accessibility-label :community-rules-title
:weight :semi-bold
:size :paragraph-1}
(i18n/label :t/community-rules)]
[community-rules/view id]]]
[rn/view {:style (style/bottom-actions)}
[quo/slide-button
{:size :size-48
:track-text (i18n/label :t/slide-to-request-to-join)
:track-icon :i/face-id
:customization-color color
:on-complete join-and-go-back}]]]))))

View File

@ -1,3 +0,0 @@
(ns status-im.contexts.communities.actions.addresses-for-permissions.style)
(def container {:flex 1})

View File

@ -1,11 +1,9 @@
(ns status-im.contexts.communities.actions.addresses-for-permissions.view (ns status-im.contexts.communities.actions.addresses-for-permissions.view
(:require [quo.core :as quo] (:require [quo.core :as quo]
[react-native.core :as rn]
[react-native.gesture :as gesture] [react-native.gesture :as gesture]
[status-im.common.not-implemented :as not-implemented] [status-im.common.not-implemented :as not-implemented]
[status-im.common.resources :as resources] [status-im.common.resources :as resources]
[status-im.constants :as constants] [status-im.constants :as constants]
[status-im.contexts.communities.actions.addresses-for-permissions.style :as style]
[utils.i18n :as i18n] [utils.i18n :as i18n]
[utils.money :as money] [utils.money :as money]
[utils.re-frame :as rf])) [utils.re-frame :as rf]))
@ -56,8 +54,15 @@
:customization-color community-color}])) :customization-color community-color}]))
(defn view (defn view
[{:keys [scroll-enabled? on-scroll]}] []
(let [{id :community-id} (rf/sub [:get-screen-params])] (let [{id :community-id} (rf/sub [:get-screen-params])
toggle-share-all-addresses #(rf/dispatch [:communities/toggle-share-all-addresses id])
update-previous-addresses (fn []
(rf/dispatch [:communities/update-previous-permission-addresses id])
(rf/dispatch [:hide-bottom-sheet]))
reset-selected-addresses (fn []
(rf/dispatch [:communities/reset-selected-permission-addresses id])
(rf/dispatch [:hide-bottom-sheet]))]
(rf/dispatch [:communities/get-permissioned-balances id]) (rf/dispatch [:communities/get-permissioned-balances id])
(fn [] (fn []
(let [{:keys [name color images]} (rf/sub [:communities/community id]) (let [{:keys [name color images]} (rf/sub [:communities/community id])
@ -67,7 +72,7 @@
selected-addresses (rf/sub [:communities/selected-permission-addresses id]) selected-addresses (rf/sub [:communities/selected-permission-addresses id])
share-all-addresses? (rf/sub [:communities/share-all-addresses? id]) share-all-addresses? (rf/sub [:communities/share-all-addresses? id])
unsaved-address-changes? (rf/sub [:communities/unsaved-address-changes? id])] unsaved-address-changes? (rf/sub [:communities/unsaved-address-changes? id])]
[rn/safe-area-view {:style style/container} [:<>
[quo/drawer-top [quo/drawer-top
{:type :context-tag {:type :context-tag
:title (i18n/label :t/addresses-for-permissions) :title (i18n/label :t/addresses-for-permissions)
@ -79,26 +84,24 @@
:community-logo (get-in images [:thumbnail :uri]) :community-logo (get-in images [:thumbnail :uri])
:customization-color color}] :customization-color color}]
[quo/category
{:list-type :settings
:data [{:title (i18n/label :t/share-all-current-and-future-addresses)
:action :selector
:action-props {:on-change #(rf/dispatch
[:communities/toggle-share-all-addresses
id])
:customization-color color
:checked? share-all-addresses?}}]
:container-style {:padding-bottom 16}}]
[gesture/flat-list [gesture/flat-list
{:render-fn account-item {:render-fn account-item
:render-data {:selected-addresses selected-addresses :render-data {:selected-addresses selected-addresses
:community-id id :community-id id
:share-all-addresses? share-all-addresses? :share-all-addresses? share-all-addresses?
:community-color color} :community-color color}
:header [quo/category
{:list-type :settings
:data [{:title
(i18n/label
:t/share-all-current-and-future-addresses)
:action :selector
:action-props
{:on-change toggle-share-all-addresses
:customization-color color
:checked? share-all-addresses?}}]
:container-style {:padding-bottom 16 :padding-horizontal 0}}]
:content-container-style {:padding-horizontal 20} :content-container-style {:padding-horizontal 20}
:scroll-enabled @scroll-enabled?
:on-scroll on-scroll
:key-fn :address :key-fn :address
:data accounts}] :data accounts}]
@ -110,25 +113,18 @@
(empty? selected-addresses) (empty? selected-addresses)
(not highest-permission-role) (not highest-permission-role)
(not unsaved-address-changes?)) (not unsaved-address-changes?))
:on-press (fn [] :on-press update-previous-addresses}
(rf/dispatch
[:communities/update-previous-permission-addresses
id])
(rf/dispatch [:navigate-back]))}
:button-two-label (i18n/label :t/cancel) :button-two-label (i18n/label :t/cancel)
:button-two-props {:type :grey :button-two-props {:type :grey
:on-press (fn [] :on-press reset-selected-addresses}
(rf/dispatch
[:communities/reset-selected-permission-addresses id])
(rf/dispatch [:navigate-back]))}
:description (if (or (empty? selected-addresses) :description (if (or (empty? selected-addresses)
(not highest-permission-role)) (not highest-permission-role))
:top-error :top-error
:top) :top)
:role (when-not checking? (role-keyword highest-permission-role)) :role (when-not checking? (role-keyword highest-permission-role))
:error-message (cond :error-message (cond
(empty? selected-addresses) (i18n/label :t/no-addresses-selected) (empty? selected-addresses)
(not highest-permission-role) (i18n/label (i18n/label :t/no-addresses-selected)
:t/addresses-dont-contain-tokens-needed)
:else nil)}]]))))
(not highest-permission-role)
(i18n/label :t/addresses-dont-contain-tokens-needed))}]]))))

View File

@ -1,7 +1,7 @@
(ns status-im.contexts.communities.actions.airdrop-addresses.view (ns status-im.contexts.communities.actions.airdrop-addresses.view
(:require (:require
[quo.core :as quo] [quo.core :as quo]
[react-native.core :as rn] [react-native.gesture :as gesture]
[status-im.common.not-implemented :as not-implemented] [status-im.common.not-implemented :as not-implemented]
[status-im.contexts.communities.actions.airdrop-addresses.style :as style] [status-im.contexts.communities.actions.airdrop-addresses.style :as style]
[utils.i18n :as i18n] [utils.i18n :as i18n]
@ -14,7 +14,7 @@
:state (when (= airdrop-address (:address item)) :selected) :state (when (= airdrop-address (:address item)) :selected)
:on-press (fn [] :on-press (fn []
(rf/dispatch [:communities/set-airdrop-address (:address item) community-id]) (rf/dispatch [:communities/set-airdrop-address (:address item) community-id])
(rf/dispatch [:navigate-back])) (rf/dispatch [:hide-bottom-sheet]))
:emoji (:emoji item)}]) :emoji (:emoji item)}])
(defn view (defn view
@ -33,7 +33,7 @@
:on-button-press not-implemented/alert :on-button-press not-implemented/alert
:community-logo (get-in images [:thumbnail :uri]) :community-logo (get-in images [:thumbnail :uri])
:customization-color color}] :customization-color color}]
[rn/flat-list [gesture/flat-list
{:data selected-accounts {:data selected-accounts
:render-fn render-item :render-fn render-item
:render-data [airdrop-address id] :render-data [airdrop-address id]

View File

@ -13,9 +13,6 @@
[status-im.contexts.chat.messenger.messages.view :as chat] [status-im.contexts.chat.messenger.messages.view :as chat]
[status-im.contexts.chat.messenger.photo-selector.view :as photo-selector] [status-im.contexts.chat.messenger.photo-selector.view :as photo-selector]
[status-im.contexts.communities.actions.accounts-selection.view :as communities.accounts-selection] [status-im.contexts.communities.actions.accounts-selection.view :as communities.accounts-selection]
[status-im.contexts.communities.actions.addresses-for-permissions.view :as
addresses-for-permissions]
[status-im.contexts.communities.actions.airdrop-addresses.view :as airdrop-addresses]
[status-im.contexts.communities.actions.request-to-join.view :as join-menu] [status-im.contexts.communities.actions.request-to-join.view :as join-menu]
[status-im.contexts.communities.actions.share-community-channel.view :as share-community-channel] [status-im.contexts.communities.actions.share-community-channel.view :as share-community-channel]
[status-im.contexts.communities.discover.view :as communities.discover] [status-im.contexts.communities.discover.view :as communities.discover]
@ -126,14 +123,6 @@
:options {:sheet? true} :options {:sheet? true}
:component communities.accounts-selection/view} :component communities.accounts-selection/view}
{:name :addresses-for-permissions
:options {:sheet? true}
:component addresses-for-permissions/view}
{:name :airdrop-addresses
:options {:sheet? true}
:component airdrop-addresses/view}
{:name :lightbox {:name :lightbox
:options options/lightbox :options options/lightbox
:component lightbox/lightbox} :component lightbox/lightbox}