From e660851677605b525d75aa503c905edec0543755 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Qu=E1=BB=91c=20Kh=C3=A1nh?= Date: Wed, 14 Aug 2024 18:06:16 +0700 Subject: [PATCH] feat(mobile): optimize bottom sheet to prevent re-trigger --- .../category/select-category-icon-field.tsx | 30 ++-------- .../mobile/components/common/bottom-sheet.tsx | 50 +++++++++++++++++ apps/mobile/components/common/date-picker.tsx | 33 ++--------- .../components/common/date-range-picker.tsx | 55 ++----------------- .../components/form-fields/currency-field.tsx | 32 ++--------- .../setting/select-default-currency.tsx | 32 ++--------- .../transaction/select-account-field.tsx | 31 ++--------- .../transaction/select-category-field.tsx | 30 ++-------- .../wallet/select-account-icon-field.tsx | 29 ++-------- 9 files changed, 86 insertions(+), 236 deletions(-) create mode 100644 apps/mobile/components/common/bottom-sheet.tsx diff --git a/apps/mobile/components/category/select-category-icon-field.tsx b/apps/mobile/components/category/select-category-icon-field.tsx index 4362455d..dd62d5d5 100644 --- a/apps/mobile/components/category/select-category-icon-field.tsx +++ b/apps/mobile/components/category/select-category-icon-field.tsx @@ -1,17 +1,15 @@ -import { BottomSheetBackdrop, BottomSheetModal } from '@gorhom/bottom-sheet' +import type { BottomSheetModal } from '@gorhom/bottom-sheet' import { useMemo, useRef } from 'react' -import { useColorScheme } from '@/hooks/useColorScheme' import { CATEGORY_EXPENSE_ICONS, CATEGORY_INCOME_ICONS, } from '@/lib/icons/category-icons' -import { theme } from '@/lib/theme' import { sleep } from '@/lib/utils' import type { CategoryTypeType } from '@6pm/validation' import { useController } from 'react-hook-form' import { Keyboard } from 'react-native' -import { FullWindowOverlay } from 'react-native-screens' +import { BottomSheet } from '../common/bottom-sheet' import GenericIcon from '../common/generic-icon' import { IconGridSheet } from '../common/icon-grid-sheet' import { Button } from '../ui/button' @@ -26,7 +24,6 @@ export function SelectCategoryIconField({ type: CategoryTypeType }) { const sheetRef = useRef(null) - const { colorScheme } = useColorScheme() const { field: { onChange, onBlur, value }, @@ -51,26 +48,7 @@ export function SelectCategoryIconField({ > - ( - - )} - containerComponent={(props) => ( - {props.children} - )} - > + - + ) } diff --git a/apps/mobile/components/common/bottom-sheet.tsx b/apps/mobile/components/common/bottom-sheet.tsx new file mode 100644 index 00000000..9774955e --- /dev/null +++ b/apps/mobile/components/common/bottom-sheet.tsx @@ -0,0 +1,50 @@ +import { useColorScheme } from '@/hooks/useColorScheme' +import { theme } from '@/lib/theme' +import { + BottomSheetBackdrop, + type BottomSheetBackdropProps, + BottomSheetModal, + type BottomSheetModalProps, +} from '@gorhom/bottom-sheet' +import type { BottomSheetModalMethods } from '@gorhom/bottom-sheet/lib/typescript/types' +import { forwardRef, useCallback } from 'react' +import { FullWindowOverlay } from 'react-native-screens' + +export const BottomSheet = forwardRef< + BottomSheetModalMethods, + BottomSheetModalProps +>((props, ref) => { + const { colorScheme } = useColorScheme() + + const backdropComponent = useCallback( + (props: BottomSheetBackdropProps) => ( + + ), + [], + ) + + const containerComponent = useCallback( + (props: { children?: React.ReactNode }) => ( + {props.children} + ), + [], + ) + + return ( + + ) +}) diff --git a/apps/mobile/components/common/date-picker.tsx b/apps/mobile/components/common/date-picker.tsx index 5009aff3..464985e3 100644 --- a/apps/mobile/components/common/date-picker.tsx +++ b/apps/mobile/components/common/date-picker.tsx @@ -1,12 +1,6 @@ -import { useColorScheme } from '@/hooks/useColorScheme' import { formatDateShort } from '@/lib/date' -import { theme } from '@/lib/theme' import { sleep } from '@/lib/utils' -import { - BottomSheetBackdrop, - BottomSheetModal, - BottomSheetView, -} from '@gorhom/bottom-sheet' +import { type BottomSheetModal, BottomSheetView } from '@gorhom/bottom-sheet' import { t } from '@lingui/macro' import { useLingui } from '@lingui/react' import DateTimePicker from '@react-native-community/datetimepicker' @@ -15,9 +9,9 @@ import { Calendar } from 'lucide-react-native' import { useRef, useState } from 'react' import { Keyboard, View } from 'react-native' import { useSafeAreaInsets } from 'react-native-safe-area-context' -import { FullWindowOverlay } from 'react-native-screens' import { Button } from '../ui/button' import { Text } from '../ui/text' +import { BottomSheet } from './bottom-sheet' function SpinnerDatePicker({ value, @@ -70,7 +64,6 @@ export function DatePicker({ minimumDate?: Date }) { const { bottom } = useSafeAreaInsets() - const { colorScheme } = useColorScheme() const sheetRef = useRef(null) return ( @@ -87,25 +80,7 @@ export function DatePicker({ {formatDateShort(value)} - ( - - )} - containerComponent={(props) => ( - {props.children} - )} - > + - + ) } diff --git a/apps/mobile/components/common/date-range-picker.tsx b/apps/mobile/components/common/date-range-picker.tsx index 7d3664bf..192a128b 100644 --- a/apps/mobile/components/common/date-range-picker.tsx +++ b/apps/mobile/components/common/date-range-picker.tsx @@ -1,12 +1,6 @@ -import { useColorScheme } from '@/hooks/useColorScheme' import { formatDateShort } from '@/lib/date' -import { theme } from '@/lib/theme' import { cn, sleep } from '@/lib/utils' -import { - BottomSheetBackdrop, - BottomSheetModal, - BottomSheetView, -} from '@gorhom/bottom-sheet' +import { type BottomSheetModal, BottomSheetView } from '@gorhom/bottom-sheet' import { t } from '@lingui/macro' import { useLingui } from '@lingui/react' import DateTimePicker from '@react-native-community/datetimepicker' @@ -17,9 +11,9 @@ import { ArrowRightIcon } from 'lucide-react-native' import { useRef, useState } from 'react' import { Keyboard, View } from 'react-native' import { useSafeAreaInsets } from 'react-native-safe-area-context' -import { FullWindowOverlay } from 'react-native-screens' import { Button } from '../ui/button' import { Text } from '../ui/text' +import { BottomSheet } from './bottom-sheet' function SpinnerDatePicker({ value, @@ -78,7 +72,6 @@ export function DateRangePicker({ }) { const { i18n } = useLingui() const { bottom } = useSafeAreaInsets() - const { colorScheme } = useColorScheme() const sheetFromRef = useRef(null) const sheetToRef = useRef(null) const [fromDate, toDate] = value ?? [] @@ -118,25 +111,7 @@ export function DateRangePicker({ - ( - - )} - containerComponent={(props) => ( - {props.children} - )} - > + - - ( - - )} - containerComponent={(props) => ( - {props.children} - )} - > + + - + ) } diff --git a/apps/mobile/components/form-fields/currency-field.tsx b/apps/mobile/components/form-fields/currency-field.tsx index a1697e79..1a776f3c 100644 --- a/apps/mobile/components/form-fields/currency-field.tsx +++ b/apps/mobile/components/form-fields/currency-field.tsx @@ -1,11 +1,8 @@ -import { BottomSheetBackdrop, BottomSheetModal } from '@gorhom/bottom-sheet' -import { useRef } from 'react' - -import { useColorScheme } from '@/hooks/useColorScheme' -import { theme } from '@/lib/theme' import { cn } from '@/lib/utils' +import type { BottomSheetModal } from '@gorhom/bottom-sheet' +import { useRef } from 'react' import { Keyboard } from 'react-native' -import { FullWindowOverlay } from 'react-native-screens' +import { BottomSheet } from '../common/bottom-sheet' import { CurrencySheetList } from '../common/currency-sheet' import { Button } from '../ui/button' import { Text } from '../ui/text' @@ -20,7 +17,6 @@ export function CurrencyField({ className?: string }) { const sheetRef = useRef(null) - const { colorScheme } = useColorScheme() return ( <> - ( - - )} - containerComponent={(props) => ( - {props.children} - )} - > + { @@ -62,7 +40,7 @@ export function CurrencyField({ setTimeout(() => sheetRef.current?.close(), 200) }} /> - + ) } diff --git a/apps/mobile/components/setting/select-default-currency.tsx b/apps/mobile/components/setting/select-default-currency.tsx index 22e6a6d1..04e915f5 100644 --- a/apps/mobile/components/setting/select-default-currency.tsx +++ b/apps/mobile/components/setting/select-default-currency.tsx @@ -1,16 +1,13 @@ -import { BottomSheetBackdrop, BottomSheetModal } from '@gorhom/bottom-sheet' -import { useRef } from 'react' - -import { useColorScheme } from '@/hooks/useColorScheme' -import { theme } from '@/lib/theme' import { sleep } from '@/lib/utils' import { useDefaultCurrency } from '@/stores/user-settings/hooks' import { useUserSettingsStore } from '@/stores/user-settings/store' +import type { BottomSheetModal } from '@gorhom/bottom-sheet' import { t } from '@lingui/macro' import { useLingui } from '@lingui/react' import { ChevronRightIcon, CurrencyIcon } from 'lucide-react-native' +import { useRef } from 'react' import { Keyboard, View } from 'react-native' -import { FullWindowOverlay } from 'react-native-screens' +import { BottomSheet } from '../common/bottom-sheet' import { CurrencySheetList } from '../common/currency-sheet' import { MenuItem } from '../common/menu-item' import { Text } from '../ui/text' @@ -20,7 +17,6 @@ export function SelectDefaultCurrency() { const sheetRef = useRef(null) const defaultCurrency = useDefaultCurrency() const setPreferredCurrency = useUserSettingsStore().setPreferredCurrency - const { colorScheme } = useColorScheme() return ( <> } /> - ( - - )} - containerComponent={(props) => ( - {props.children} - )} - > + { @@ -66,7 +44,7 @@ export function SelectDefaultCurrency() { setPreferredCurrency?.(currency.code) }} /> - + ) } diff --git a/apps/mobile/components/transaction/select-account-field.tsx b/apps/mobile/components/transaction/select-account-field.tsx index bcc51f29..9a71152c 100644 --- a/apps/mobile/components/transaction/select-account-field.tsx +++ b/apps/mobile/components/transaction/select-account-field.tsx @@ -2,9 +2,8 @@ import { sleep } from '@/lib/utils' import { useWallets } from '@/queries/wallet' import type { WalletAccountWithBalance } from '@6pm/validation' import { - BottomSheetBackdrop, BottomSheetFlatList, - BottomSheetModal, + type BottomSheetModal, } from '@gorhom/bottom-sheet' import { t } from '@lingui/macro' import { useLingui } from '@lingui/react' @@ -13,10 +12,7 @@ import { useRef } from 'react' import { useController } from 'react-hook-form' import { Keyboard, View } from 'react-native' import { useSafeAreaInsets } from 'react-native-safe-area-context' -import { FullWindowOverlay } from 'react-native-screens' - -import { useColorScheme } from '@/hooks/useColorScheme' -import { theme } from '@/lib/theme' +import { BottomSheet } from '../common/bottom-sheet' import GenericIcon from '../common/generic-icon' import { Button } from '../ui/button' import { Text } from '../ui/text' @@ -28,7 +24,6 @@ export function SelectAccountField({ }) { const { bottom } = useSafeAreaInsets() const { data: walletAccounts, isLoading } = useWallets() - const { colorScheme } = useColorScheme() const sheetRef = useRef(null) const { i18n } = useLingui() @@ -63,25 +58,7 @@ export function SelectAccountField({ {selectedWalletAccount?.name || t(i18n)`Select account`} - ( - - )} - containerComponent={(props) => ( - {props.children} - )} - > + )} /> - + ) } diff --git a/apps/mobile/components/transaction/select-category-field.tsx b/apps/mobile/components/transaction/select-category-field.tsx index e587fecd..c3673a4c 100644 --- a/apps/mobile/components/transaction/select-category-field.tsx +++ b/apps/mobile/components/transaction/select-category-field.tsx @@ -1,8 +1,7 @@ import { sleep } from '@/lib/utils' import type { Category } from '@6pm/validation' import { - BottomSheetBackdrop, - BottomSheetModal, + type BottomSheetModal, BottomSheetSectionList, } from '@gorhom/bottom-sheet' import { t } from '@lingui/macro' @@ -12,13 +11,11 @@ import { useCallback, useRef, useState } from 'react' import { useController } from 'react-hook-form' import { FlatList, Keyboard, View } from 'react-native' -import { useColorScheme } from '@/hooks/useColorScheme' -import { theme } from '@/lib/theme' import { useCategoryList } from '@/stores/category/hooks' import { Link, useFocusEffect } from 'expo-router' import { PlusIcon } from 'lucide-react-native' import { useSafeAreaInsets } from 'react-native-safe-area-context' -import { FullWindowOverlay } from 'react-native-screens' +import { BottomSheet } from '../common/bottom-sheet' import GenericIcon from '../common/generic-icon' import { Button } from '../ui/button' import { Text } from '../ui/text' @@ -30,7 +27,6 @@ export function SelectCategoryField({ }) { const { bottom } = useSafeAreaInsets() const { categories = [], isLoading } = useCategoryList() - const { colorScheme } = useColorScheme() const [shouldReOpen, setShouldReOpen] = useState(false) const sheetRef = useRef(null) @@ -83,25 +79,7 @@ export function SelectCategoryField({ {selectedCategory?.name || t(i18n)`Uncategorized`} - ( - - )} - containerComponent={(props) => ( - {props.children} - )} - > + i.id} @@ -177,7 +155,7 @@ export function SelectCategoryField({ ) : null } /> - + ) } diff --git a/apps/mobile/components/wallet/select-account-icon-field.tsx b/apps/mobile/components/wallet/select-account-icon-field.tsx index 4fa51280..b4208cb4 100644 --- a/apps/mobile/components/wallet/select-account-icon-field.tsx +++ b/apps/mobile/components/wallet/select-account-icon-field.tsx @@ -1,12 +1,10 @@ -import { BottomSheetBackdrop, BottomSheetModal } from '@gorhom/bottom-sheet' +import type { BottomSheetModal } from '@gorhom/bottom-sheet' import { useRef } from 'react' -import { useColorScheme } from '@/hooks/useColorScheme' import { WALLET_ICONS } from '@/lib/icons/wallet-icons' -import { theme } from '@/lib/theme' import { useController } from 'react-hook-form' import { Keyboard } from 'react-native' -import { FullWindowOverlay } from 'react-native-screens' +import { BottomSheet } from '../common/bottom-sheet' import GenericIcon from '../common/generic-icon' import { IconGridSheet } from '../common/icon-grid-sheet' import { Button } from '../ui/button' @@ -16,7 +14,6 @@ export function SelectAccountIconField({ }: { onSelect?: (currency: string) => void }) { - const { colorScheme } = useColorScheme() const sheetRef = useRef(null) const { field: { onChange, onBlur, value }, @@ -35,25 +32,7 @@ export function SelectAccountIconField({ > - ( - - )} - containerComponent={(props) => ( - {props.children} - )} - > + sheetRef.current?.close(), 200) }} /> - + ) }