diff --git a/app/containers/ActionSheet/ActionSheet.tsx b/app/containers/ActionSheet/ActionSheet.tsx index 673e515a1..d578164ff 100644 --- a/app/containers/ActionSheet/ActionSheet.tsx +++ b/app/containers/ActionSheet/ActionSheet.tsx @@ -1,32 +1,21 @@ import { useBackHandler } from '@react-native-community/hooks'; import * as Haptics from 'expo-haptics'; -import React, { forwardRef, isValidElement, useEffect, useImperativeHandle, useRef, useState } from 'react'; -import { Keyboard, Text } from 'react-native'; -import { HandlerStateChangeEventPayload, State, TapGestureHandler } from 'react-native-gesture-handler'; -import Animated, { Easing, Extrapolate, interpolateNode, Value } from 'react-native-reanimated'; +import React, { forwardRef, isValidElement, useEffect, useImperativeHandle, useRef, useState, useCallback } from 'react'; +import { Keyboard } from 'react-native'; +import { Easing } from 'react-native-reanimated'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; -import ScrollBottomSheet from 'react-native-scroll-bottom-sheet'; +import BottomSheet, { BottomSheetBackdrop } from '@gorhom/bottom-sheet'; -import { themes } from '../../lib/constants'; import { useDimensions, useOrientation } from '../../dimensions'; -import I18n from '../../i18n'; import { useTheme } from '../../theme'; import { isIOS, isTablet } from '../../utils/deviceInfo'; -import * as List from '../List'; -import { Button } from './Button'; import { Handle } from './Handle'; -import { IActionSheetItem, Item } from './Item'; -import { TActionSheetOptions, TActionSheetOptionsItem } from './Provider'; +import { TActionSheetOptions } from './Provider'; +import BottomSheetContent from './BottomSheetContent'; import styles, { ITEM_HEIGHT } from './styles'; -const getItemLayout = (data: TActionSheetOptionsItem[] | null | undefined, index: number) => ({ - length: ITEM_HEIGHT, - offset: ITEM_HEIGHT * index, - index -}); - const HANDLE_HEIGHT = isIOS ? 40 : 56; -const MAX_SNAP_HEIGHT = 16; +const MIN_SNAP_HEIGHT = 16; const CANCEL_HEIGHT = 64; const ANIMATION_DURATION = 250; @@ -39,27 +28,26 @@ const ANIMATION_CONFIG = { const ActionSheet = React.memo( forwardRef(({ children }: { children: React.ReactElement }, ref) => { - const { theme } = useTheme(); - const bottomSheetRef = useRef>(null); + const { colors } = useTheme(); + const bottomSheetRef = useRef(null); const [data, setData] = useState({} as TActionSheetOptions); const [isVisible, setVisible] = useState(false); const { height } = useDimensions(); const { isLandscape } = useOrientation(); const insets = useSafeAreaInsets(); - const maxSnap = Math.max( - height - - // Items height - ITEM_HEIGHT * (data?.options?.length || 0) - + const maxSnap = Math.min( + // Items height + ITEM_HEIGHT * (data?.options?.length || 0) + // Handle height - HANDLE_HEIGHT - + HANDLE_HEIGHT + // Custom header height - (data?.headerHeight || 0) - + (data?.headerHeight || 0) + // Insets bottom height (Notch devices) - insets.bottom - + insets.bottom + // Cancel button height (data?.hasCancel ? CANCEL_HEIGHT : 0), - MAX_SNAP_HEIGHT + height - MIN_SNAP_HEIGHT ); /* @@ -69,14 +57,13 @@ const ActionSheet = React.memo( * we'll provide more one snap * that point 50% of the whole screen */ - const snaps = height - maxSnap > height * 0.6 && !isLandscape ? [maxSnap, height * 0.5, height] : [maxSnap, height]; - const openedSnapIndex = snaps.length > 2 ? 1 : 0; - const closedSnapIndex = snaps.length - 1; + const snaps = maxSnap > height * 0.6 && !isLandscape && !data.snaps ? [height * 0.5, maxSnap] : [maxSnap]; const toggleVisible = () => setVisible(!isVisible); const hide = () => { - bottomSheetRef.current?.snapTo(closedSnapIndex); + bottomSheetRef.current?.close(); + toggleVisible(); }; const show = (options: TActionSheetOptions) => { @@ -84,12 +71,6 @@ const ActionSheet = React.memo( toggleVisible(); }; - const onBackdropPressed = ({ nativeEvent }: { nativeEvent: HandlerStateChangeEventPayload }) => { - if (nativeEvent.oldState === State.ACTIVE) { - hide(); - } - }; - useBackHandler(() => { if (isVisible) { hide(); @@ -101,7 +82,6 @@ const ActionSheet = React.memo( if (isVisible) { Keyboard.dismiss(); Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); - bottomSheetRef.current?.snapTo(openedSnapIndex); } }, [isVisible]); @@ -122,26 +102,18 @@ const ActionSheet = React.memo( ); - const renderFooter = () => - data?.hasCancel ? ( - - ) : null; - - const renderItem = ({ item }: { item: IActionSheetItem['item'] }) => ; - - const animatedPosition = React.useRef(new Value(0)); - const opacity = interpolateNode(animatedPosition.current, { - inputRange: [0, 1], - outputRange: [0, themes[theme].backdropOpacity], - extrapolate: Extrapolate.CLAMP - }) as any; // The function's return differs from the expected type of opacity, however this problem is something related to lib, maybe when updating the types will be fixed. + const renderBackdrop = useCallback( + props => ( + + ), + [] + ); const bottomSheet = isLandscape || isTablet ? styles.bottomSheet : {}; @@ -149,42 +121,19 @@ const ActionSheet = React.memo( <> {children} {isVisible && ( - <> - - - - - testID='action-sheet' - ref={bottomSheetRef} - componentType='FlatList' - snapPoints={snaps} - initialSnapIndex={closedSnapIndex} - renderHandle={renderHandle} - onSettle={index => index === closedSnapIndex && toggleVisible()} - animatedPosition={animatedPosition.current} - containerStyle={{ ...styles.container, ...bottomSheet, backgroundColor: themes[theme].focusedBackground }} - animationConfig={ANIMATION_CONFIG} - data={data.options} - renderItem={renderItem} - keyExtractor={item => item.title} - style={{ backgroundColor: themes[theme].focusedBackground }} - contentContainerStyle={styles.content} - ItemSeparatorComponent={List.Separator} - ListHeaderComponent={List.Separator} - ListFooterComponent={renderFooter} - getItemLayout={getItemLayout} - removeClippedSubviews={isIOS} - /> - + index === -1 && toggleVisible()}> + + )} ); diff --git a/app/containers/ActionSheet/BottomSheetContent.tsx b/app/containers/ActionSheet/BottomSheetContent.tsx new file mode 100644 index 000000000..65a5b5b58 --- /dev/null +++ b/app/containers/ActionSheet/BottomSheetContent.tsx @@ -0,0 +1,58 @@ +import { Text } from 'react-native'; +import React from 'react'; +import { BottomSheetView, BottomSheetFlatList } from '@gorhom/bottom-sheet'; + +import { Button } from './Button'; +import I18n from '../../i18n'; +import { useTheme } from '../../theme'; +import { IActionSheetItem, Item } from './Item'; +import { TActionSheetOptionsItem } from './Provider'; +import styles from './styles'; +import * as List from '../List'; + +interface IBottomSheetContentProps { + hasCancel?: boolean; + options?: TActionSheetOptionsItem[]; + hide: () => void; + children?: React.ReactElement | null; +} + +const BottomSheetContent = React.memo(({ options, hasCancel, hide, children }: IBottomSheetContentProps) => { + const { theme, colors } = useTheme(); + + const renderFooter = () => + hasCancel ? ( + + ) : null; + + const renderItem = ({ item }: { item: IActionSheetItem['item'] }) => ; + + if (options) { + return ( + item.title} + bounces={true} + renderItem={renderItem} + style={{ backgroundColor: colors.focusedBackground }} + keyboardDismissMode='interactive' + indicatorStyle='black' + contentContainerStyle={styles.content} + ItemSeparatorComponent={List.Separator} + ListHeaderComponent={List.Separator} + ListFooterComponent={renderFooter} + /> + ); + } + return {children}; +}); + +export default BottomSheetContent; diff --git a/app/containers/ActionSheet/Provider.tsx b/app/containers/ActionSheet/Provider.tsx index 8706dc95a..43f444702 100644 --- a/app/containers/ActionSheet/Provider.tsx +++ b/app/containers/ActionSheet/Provider.tsx @@ -13,10 +13,13 @@ export type TActionSheetOptionsItem = { }; export type TActionSheetOptions = { - options: TActionSheetOptionsItem[]; + options?: TActionSheetOptionsItem[]; headerHeight?: number; customHeader?: React.ReactElement | null; hasCancel?: boolean; + type?: string; + children?: React.ReactElement | null; + snaps?: string[] | number[]; }; interface IActionSheetProvider { showActionSheet: (item: TActionSheetOptions) => void; diff --git a/app/containers/ActionSheet/styles.ts b/app/containers/ActionSheet/styles.ts index 1b9397dc9..68d371ea6 100644 --- a/app/containers/ActionSheet/styles.ts +++ b/app/containers/ActionSheet/styles.ts @@ -46,8 +46,7 @@ export default StyleSheet.create({ }, bottomSheet: { width: '50%', - alignSelf: 'center', - left: '25%' + marginHorizontal: '25%' }, button: { marginHorizontal: 16, diff --git a/package.json b/package.json index 31fef6eeb..19602ce4a 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "dependencies": { "@bugsnag/react-native": "^7.10.5", "@codler/react-native-keyboard-aware-scroll-view": "^1.0.1", + "@gorhom/bottom-sheet": "^4", "@nozbe/watermelondb": "0.23.0", "@react-native-clipboard/clipboard": "^1.8.5", "@react-native-community/art": "^1.2.0", @@ -111,7 +112,6 @@ "react-native-restart": "0.0.22", "react-native-safe-area-context": "3.2.0", "react-native-screens": "2.9.0", - "react-native-scroll-bottom-sheet": "0.6.2", "react-native-scrollable-tab-view": "^1.0.0", "react-native-simple-crypto": "RocketChat/react-native-simple-crypto#0.5.0", "react-native-slowlog": "^1.0.2", diff --git a/patches/react-native-scroll-bottom-sheet+0.6.2.patch b/patches/react-native-scroll-bottom-sheet+0.6.2.patch deleted file mode 100644 index 50fab7bdc..000000000 --- a/patches/react-native-scroll-bottom-sheet+0.6.2.patch +++ /dev/null @@ -1,42 +0,0 @@ -diff --git a/node_modules/react-native-scroll-bottom-sheet/src/index.tsx b/node_modules/react-native-scroll-bottom-sheet/src/index.tsx -index c4dbcce..b545c03 100644 ---- a/node_modules/react-native-scroll-bottom-sheet/src/index.tsx -+++ b/node_modules/react-native-scroll-bottom-sheet/src/index.tsx -@@ -518,6 +518,28 @@ export class ScrollBottomSheet extends Component> { - clockRunning(this.animationClock) - ), - [ -+ this.didScrollUpAndPullDown, -+ this.setTranslationY, -+ set(this.tempDestSnapPoint, add(snapPoints[0], this.extraOffset)), -+ cond(not(this.isManuallySetValue), set(this.nextSnapIndex, 0)), -+ set( -+ this.destSnapPoint, -+ cond( -+ this.isManuallySetValue, -+ this.manualYOffset, -+ this.calculateNextSnapPoint() -+ ) -+ ), -+ cond(this.isManuallySetValue, [ -+ set(this.animationFinished, 0) -+ ]), -+ set( -+ this.lastSnap, -+ sub( -+ this.destSnapPoint, -+ cond(eq(this.scrollUpAndPullDown, 1), this.lastStartScrollY, 0) -+ ) -+ ), - runTiming({ - clock: this.animationClock, - from: cond( -@@ -550,7 +572,7 @@ export class ScrollBottomSheet extends Component> { - ); - - this.position = interpolate(this.translateY, { -- inputRange: [openPosition, closedPosition], -+ inputRange: [snapPoints[snapPoints.length - 2], closedPosition], - outputRange: [1, 0], - extrapolate: Extrapolate.CLAMP, - }); diff --git a/yarn.lock b/yarn.lock index b731acf68..ff2fd9533 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2931,6 +2931,23 @@ base64-js "^1.2.3" xmlbuilder "^14.0.0" +"@gorhom/bottom-sheet@^4": + version "4.1.5" + resolved "https://registry.yarnpkg.com/@gorhom/bottom-sheet/-/bottom-sheet-4.1.5.tgz#35341d45799de28082c380db6639537b04fa0b26" + integrity sha512-3F5P8jK3NXwT2lGwkAdkdLwDVHaRvMZalUTXjK6Ogf0Tki6idffJ3TNlQZlg8k6+OnXAx0+i80f4XaI+J4GFrA== + dependencies: + "@gorhom/portal" "^1.0.11" + invariant "^2.2.4" + nanoid "^3.1.20" + react-native-redash "^16.1.1" + +"@gorhom/portal@^1.0.11": + version "1.0.12" + resolved "https://registry.yarnpkg.com/@gorhom/portal/-/portal-1.0.12.tgz#1c0deabb3f9057c736352a88bae9ca891a100346" + integrity sha512-JOYe85RUwiksgdMbhLWDCLpH3kgFFz+LCu1lnxOMMBQSfAKtL5kkTKVrhtmQ3Lq3lJM2paGnLc4wJrlVuaC5Jw== + dependencies: + nanoid "^3.1.23" + "@hapi/hoek@^9.0.0": version "9.2.1" resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.2.1.tgz#9551142a1980503752536b5050fd99f4a7f13b17" @@ -13067,6 +13084,11 @@ nanoid@3.1.23, nanoid@^3.1.15: resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.23.tgz#f744086ce7c2bc47ee0a8472574d5c78e4183a81" integrity sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw== +nanoid@^3.1.20, nanoid@^3.1.23: + version "3.3.1" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.1.tgz#6347a18cac88af88f58af0b3594b723d5e99bb35" + integrity sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw== + nanomatch@^1.2.9: version "1.2.13" resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" @@ -14972,6 +14994,15 @@ react-native-redash@^12.0.3: parse-svg-path "^0.1.2" use-memo-one "^1.1.1" +react-native-redash@^16.1.1: + version "16.2.3" + resolved "https://registry.yarnpkg.com/react-native-redash/-/react-native-redash-16.2.3.tgz#ee63e100c60f83275116e57d4e8bc79f26349db9" + integrity sha512-vSjHA6/mBY3IpDYPish3DlG06PKNLkb/b89hw7nsDM3yxAJ7Db+yMnEL3pp2YsoYblDc3s+0+wBRlvxay4X4vQ== + dependencies: + abs-svg-path "^0.1.1" + normalize-svg-path "^1.0.1" + parse-svg-path "^0.1.2" + react-native-restart@0.0.22: version "0.0.22" resolved "https://registry.yarnpkg.com/react-native-restart/-/react-native-restart-0.0.22.tgz#81fcb7f31e35951d85410c68b9556acf3ab88705" @@ -14987,13 +15018,6 @@ react-native-screens@2.9.0: resolved "https://registry.yarnpkg.com/react-native-screens/-/react-native-screens-2.9.0.tgz#ead2843107ba00fee259aa377582e457c74f1f3b" integrity sha512-5MaiUD6HA3nzY3JbVI8l3V7pKedtxQF3d8qktTVI0WmWXTI4QzqOU8r8fPVvfKo3MhOXwhWBjr+kQ7DZaIQQeg== -react-native-scroll-bottom-sheet@0.6.2: - version "0.6.2" - resolved "https://registry.yarnpkg.com/react-native-scroll-bottom-sheet/-/react-native-scroll-bottom-sheet-0.6.2.tgz#f68ea26cb10171ccc8eacf3b917a0e84c7508b82" - integrity sha512-VesAAzsv0xxtGKftFVRqbWiB1AaG3YttvilV16rpvbA45z0ow89tG5FZQtznH9ypKVY17O0bFFHocdji7bvZLg== - dependencies: - utility-types "^3.10.0" - react-native-scrollable-tab-view@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/react-native-scrollable-tab-view/-/react-native-scrollable-tab-view-1.0.0.tgz#87319896067f7bb643ecd7fba2cba4d6d8f9e18b" @@ -17748,11 +17772,6 @@ utila@^0.4.0, utila@~0.4: resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c" integrity sha1-ihagXURWV6Oupe7MWxKk+lN5dyw= -utility-types@^3.10.0: - version "3.10.0" - resolved "https://registry.yarnpkg.com/utility-types/-/utility-types-3.10.0.tgz#ea4148f9a741015f05ed74fd615e1d20e6bed82b" - integrity sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg== - utils-merge@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"