From 690aa7bd673024a6f6d4fbe55cfd05afd7c3f755 Mon Sep 17 00:00:00 2001 From: mvaivre Date: Wed, 16 Oct 2024 16:50:20 +0200 Subject: [PATCH] DRY things up with BottomModalBase --- .../src/features/modals/BottomModal.tsx | 149 +++--------------- .../src/features/modals/BottomModalBase.tsx | 142 +++++++++++++++++ .../features/modals/BottomModalFlashList.tsx | 128 ++------------- .../features/modals/useBottomModalState.tsx | 2 +- 4 files changed, 177 insertions(+), 244 deletions(-) create mode 100644 apps/mobile-wallet/src/features/modals/BottomModalBase.tsx diff --git a/apps/mobile-wallet/src/features/modals/BottomModal.tsx b/apps/mobile-wallet/src/features/modals/BottomModal.tsx index bb8e5ff7d..11f37f6b1 100644 --- a/apps/mobile-wallet/src/features/modals/BottomModal.tsx +++ b/apps/mobile-wallet/src/features/modals/BottomModal.tsx @@ -18,31 +18,17 @@ along with the library. If not, see . // HUGE THANKS TO JAI-ADAPPTOR @ https://gist.github.com/jai-adapptor/bc3650ab20232d8ab076fa73829caebb -import { ReactNode } from 'react' -import { KeyboardAvoidingView, Pressable, ScrollView, StyleProp, ViewStyle } from 'react-native' -import { GestureDetector } from 'react-native-gesture-handler' +import { ScrollView, StyleProp, ViewStyle } from 'react-native' import Animated from 'react-native-reanimated' -import styled from 'styled-components/native' -import AppText from '~/components/AppText' -import { CloseButton } from '~/components/buttons/Button' +import BottomModalBase, { BottomModalBaseProps } from '~/features/modals/BottomModalBase' import { useBottomModalState } from '~/features/modals/useBottomModalState' import { DEFAULT_MARGIN, VERTICAL_GAP } from '~/style/globalStyle' -export interface BottomModalProps { - modalId: number - children: ReactNode - onClose?: () => void - title?: string - maximisedContent?: boolean - minHeight?: number - navHeight?: number - noPadding?: boolean - contentVerticalGap?: boolean +export interface BottomModalProps extends BottomModalBaseProps { contentContainerStyle?: StyleProp } -const AnimatedPressable = Animated.createAnimatedComponent(Pressable) const AnimatedScrollView = Animated.createAnimatedComponent(ScrollView) const BottomModal = ({ @@ -57,16 +43,7 @@ const BottomModal = ({ contentVerticalGap, contentContainerStyle }: BottomModalProps) => { - const { - modalHeightAnimatedStyle, - handleAnimatedStyle, - backdropAnimatedStyle, - handleContentSizeChange, - panGesture, - handleClose, - contentScrollHandlers, - isScrollable - } = useBottomModalState({ + const modalState = useBottomModalState({ modalId, maximisedContent, minHeight, @@ -75,107 +52,25 @@ const BottomModal = ({ }) return ( - - - - - - - - - - - {title} - - - - - - - {children} - - - - + + + {children} + + ) } export default BottomModal - -const KeyboardAvoidingViewStyled = styled(KeyboardAvoidingView)` - position: absolute; - bottom: 0; - left: 0; - right: 0; - top: 0; -` - -const Container = styled.View` - position: absolute; - bottom: 0; - left: 0; - right: 0; -` - -const Backdrop = styled(AnimatedPressable)` - position: absolute; - top: 0; - bottom: 0; - left: 0; - right: 0; - background-color: rgba(0, 0, 0, 0.8); -` - -const ModalStyled = styled(Animated.View)` - justify-content: flex-start; - background-color: ${({ theme }) => (theme.name === 'light' ? theme.bg.back1 : theme.bg.secondary)}; - border-top-left-radius: 20px; - border-top-right-radius: 20px; - min-height: 80px; - overflow: hidden; -` - -const HandleContainer = styled.View` - align-items: center; - justify-content: center; - padding-top: 5px; -` - -const Handle = styled(Animated.View)` - width: 15%; - height: 4px; - border-radius: 8px; - background-color: ${({ theme }) => theme.border.primary}; - margin-top: -15px; -` - -const Title = styled(AppText)` - flex: 1; - text-align: center; -` - -const Navigation = styled(Animated.View)` - flex-direction: row; - align-items: center; - justify-content: flex-end; - padding: 0 ${DEFAULT_MARGIN - 1}px; -` - -const NavigationButtonContainer = styled.View<{ align: 'right' | 'left' }>` - width: 10%; - flex-direction: row; - justify-content: ${({ align }) => (align === 'right' ? 'flex-end' : 'flex-start')}; -` diff --git a/apps/mobile-wallet/src/features/modals/BottomModalBase.tsx b/apps/mobile-wallet/src/features/modals/BottomModalBase.tsx new file mode 100644 index 000000000..ac077c265 --- /dev/null +++ b/apps/mobile-wallet/src/features/modals/BottomModalBase.tsx @@ -0,0 +1,142 @@ +/* +Copyright 2018 - 2024 The Alephium Authors +This file is part of the alephium project. + +The library is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +The library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with the library. If not, see . +*/ + +import { ReactNode } from 'react' +import { KeyboardAvoidingView, Pressable } from 'react-native' +import { GestureDetector } from 'react-native-gesture-handler' +import Animated from 'react-native-reanimated' +import styled from 'styled-components/native' + +import AppText from '~/components/AppText' +import { CloseButton } from '~/components/buttons/Button' +import { useBottomModalState } from '~/features/modals/useBottomModalState' +import { DEFAULT_MARGIN } from '~/style/globalStyle' + +export interface BottomModalBaseProps extends ReturnType { + modalId: number + children: ReactNode + onClose?: () => void + title?: string + maximisedContent?: boolean + minHeight?: number + noPadding?: boolean + contentVerticalGap?: boolean + navHeight?: number +} + +const AnimatedPressable = Animated.createAnimatedComponent(Pressable) + +const BottomModalBase = ({ + maximisedContent, + backdropAnimatedStyle, + handleClose, + modalHeightAnimatedStyle, + handleAnimatedStyle, + panGesture, + navHeight, + title, + children +}: BottomModalBaseProps) => ( + + + + + + + + + + + {title} + + + + + + {children} + + + +) + +export default BottomModalBase + +const KeyboardAvoidingViewStyled = styled(KeyboardAvoidingView)` + position: absolute; + bottom: 0; + left: 0; + right: 0; + top: 0; +` + +const Container = styled.View` + position: absolute; + bottom: 0; + left: 0; + right: 0; +` + +const Backdrop = styled(AnimatedPressable)` + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + background-color: rgba(0, 0, 0, 0.8); +` + +const ModalStyled = styled(Animated.View)` + justify-content: flex-start; + background-color: ${({ theme }) => (theme.name === 'light' ? theme.bg.back1 : theme.bg.secondary)}; + border-top-left-radius: 20px; + border-top-right-radius: 20px; + min-height: 80px; + overflow: hidden; +` + +const HandleContainer = styled.View` + align-items: center; + justify-content: center; + padding-top: 5px; +` + +const Handle = styled(Animated.View)` + width: 15%; + height: 4px; + border-radius: 8px; + background-color: ${({ theme }) => theme.border.primary}; + margin-top: -15px; +` + +const Title = styled(AppText)` + flex: 1; + text-align: center; +` + +const Navigation = styled(Animated.View)` + flex-direction: row; + align-items: center; + justify-content: flex-end; + padding: 0 ${DEFAULT_MARGIN - 1}px; +` + +const NavigationButtonContainer = styled.View<{ align: 'right' | 'left' }>` + width: 10%; + flex-direction: row; + justify-content: ${({ align }) => (align === 'right' ? 'flex-end' : 'flex-start')}; +` diff --git a/apps/mobile-wallet/src/features/modals/BottomModalFlashList.tsx b/apps/mobile-wallet/src/features/modals/BottomModalFlashList.tsx index 72ea38727..b2a1c6722 100644 --- a/apps/mobile-wallet/src/features/modals/BottomModalFlashList.tsx +++ b/apps/mobile-wallet/src/features/modals/BottomModalFlashList.tsx @@ -17,24 +17,12 @@ along with the library. If not, see . */ import { ContentStyle } from '@shopify/flash-list' import { ReactNode } from 'react' -import { KeyboardAvoidingView, Pressable } from 'react-native' -import { GestureDetector } from 'react-native-gesture-handler' -import Animated from 'react-native-reanimated' -import styled from 'styled-components/native' -import AppText from '~/components/AppText' -import { CloseButton } from '~/components/buttons/Button' +import BottomModalBase, { BottomModalBaseProps } from '~/features/modals/BottomModalBase' import { ContentScrollHandlers, useBottomModalState } from '~/features/modals/useBottomModalState' import { DEFAULT_MARGIN } from '~/style/globalStyle' -export interface BottomModalFlashListProps { - modalId: number - onClose?: () => void - title?: string - maximisedContent?: boolean - minHeight?: number - navHeight?: number - noPadding?: boolean +export interface BottomModalFlashListProps extends Omit { children: (flashListProps: { contentContainerStyle: ContentStyle onScroll: ContentScrollHandlers['onScroll'] @@ -44,8 +32,6 @@ export interface BottomModalFlashListProps { }) => ReactNode } -const AnimatedPressable = Animated.createAnimatedComponent(Pressable) - const BottomModalFlashList = ({ modalId, onClose, @@ -56,15 +42,7 @@ const BottomModalFlashList = ({ noPadding, children }: BottomModalFlashListProps) => { - const { - modalHeightAnimatedStyle, - handleAnimatedStyle, - backdropAnimatedStyle, - handleContentSizeChange, - panGesture, - handleClose, - contentScrollHandlers - } = useBottomModalState({ + const modalState = useBottomModalState({ modalId, maximisedContent, minHeight, @@ -73,98 +51,16 @@ const BottomModalFlashList = ({ }) return ( - - - - - - - - - - - {title} - - - - - - {children({ - ...contentScrollHandlers, - onContentSizeChange: handleContentSizeChange, - contentContainerStyle: { - padding: noPadding ? 0 : DEFAULT_MARGIN - } - })} - - - + + {children({ + ...modalState.contentScrollHandlers, + onContentSizeChange: modalState.handleContentSizeChange, + contentContainerStyle: { + padding: noPadding ? 0 : DEFAULT_MARGIN + } + })} + ) } export default BottomModalFlashList - -const KeyboardAvoidingViewStyled = styled(KeyboardAvoidingView)` - position: absolute; - bottom: 0; - left: 0; - right: 0; - top: 0; -` - -const Container = styled.View` - position: absolute; - bottom: 0; - left: 0; - right: 0; -` - -const Backdrop = styled(AnimatedPressable)` - position: absolute; - top: 0; - bottom: 0; - left: 0; - right: 0; - background-color: rgba(0, 0, 0, 0.8); -` - -const ModalStyled = styled(Animated.View)` - justify-content: flex-start; - background-color: ${({ theme }) => (theme.name === 'light' ? theme.bg.back1 : theme.bg.secondary)}; - border-top-left-radius: 20px; - border-top-right-radius: 20px; - min-height: 80px; - overflow: hidden; -` - -const HandleContainer = styled.View` - align-items: center; - justify-content: center; - padding-top: 5px; -` - -const Handle = styled(Animated.View)` - width: 15%; - height: 4px; - border-radius: 8px; - background-color: ${({ theme }) => theme.border.primary}; - margin-top: -15px; -` - -const Title = styled(AppText)` - flex: 1; - text-align: center; -` - -const Navigation = styled(Animated.View)` - flex-direction: row; - align-items: center; - justify-content: flex-end; - padding: 0 ${DEFAULT_MARGIN - 1}px; -` - -const NavigationButtonContainer = styled.View<{ align: 'right' | 'left' }>` - width: 10%; - flex-direction: row; - justify-content: ${({ align }) => (align === 'right' ? 'flex-end' : 'flex-start')}; -` diff --git a/apps/mobile-wallet/src/features/modals/useBottomModalState.tsx b/apps/mobile-wallet/src/features/modals/useBottomModalState.tsx index a9ea435cc..75257e7fe 100644 --- a/apps/mobile-wallet/src/features/modals/useBottomModalState.tsx +++ b/apps/mobile-wallet/src/features/modals/useBottomModalState.tsx @@ -27,7 +27,7 @@ import { useAppDispatch, useAppSelector } from '~/hooks/redux' type BottomModalPositions = 'minimised' | 'maximised' | 'closing' -interface UseBottomModalStateParams { +export interface UseBottomModalStateParams { modalId: number navHeight: number maximisedContent?: boolean