diff --git a/apps/wallet-mobile/.storybook/storybook.requires.js b/apps/wallet-mobile/.storybook/storybook.requires.js
index 746352407c..f1fc7e7b43 100644
--- a/apps/wallet-mobile/.storybook/storybook.requires.js
+++ b/apps/wallet-mobile/.storybook/storybook.requires.js
@@ -69,6 +69,7 @@ const getStories = () => {
"./src/components/AmountItem/AmountItem.stories.tsx": require("../src/components/AmountItem/AmountItem.stories.tsx"),
"./src/components/Analytics/Analytics.stories.tsx": require("../src/components/Analytics/Analytics.stories.tsx"),
"./src/components/BlueCheckbox/BlueCheckbox.stories.tsx": require("../src/components/BlueCheckbox/BlueCheckbox.stories.tsx"),
+ "./src/components/BottomSheet/BottomSheet.stories.tsx": require("../src/components/BottomSheet/BottomSheet.stories.tsx"),
"./src/components/BottomSheetModal/BottomSheetModal.stories.tsx": require("../src/components/BottomSheetModal/BottomSheetModal.stories.tsx"),
"./src/components/Boundary/Boundary.stories.tsx": require("../src/components/Boundary/Boundary.stories.tsx"),
"./src/components/Button/Button.stories.tsx": require("../src/components/Button/Button.stories.tsx"),
diff --git a/apps/wallet-mobile/src/components/BottomSheet/BottomSheet.stories.tsx b/apps/wallet-mobile/src/components/BottomSheet/BottomSheet.stories.tsx
new file mode 100644
index 0000000000..e285acda3f
--- /dev/null
+++ b/apps/wallet-mobile/src/components/BottomSheet/BottomSheet.stories.tsx
@@ -0,0 +1,114 @@
+import {storiesOf} from '@storybook/react-native'
+import React from 'react'
+import {Button, ScrollView, StyleSheet, Text, TextInput, View} from 'react-native'
+
+import {Spacer} from '../Spacer'
+import {BottomSheet, BottomSheetRef} from './BottomSheet'
+
+storiesOf('BottomSheet', module)
+ .add('Default', () => )
+ .add('Scroll + input (Keyboard) + extendable', () => )
+
+const ComponentDefault = () => {
+ const bottomSheetRef = React.useRef(null)
+
+ const openBottomSheet = () => {
+ bottomSheetRef.current?.openBottomSheet()
+ }
+
+ const closeBottomSheet = () => {
+ bottomSheetRef.current?.closeBottomSheet()
+ }
+
+ const handleClick = () => {
+ if (bottomSheetRef.current?.isOpen) closeBottomSheet()
+ else openBottomSheet()
+ }
+
+ return (
+
+
+
+
+
+ FAke content
+
+
+
+ )
+}
+
+const ComponentWithScrollAndInput = () => {
+ const bottomSheetRef = React.useRef(null)
+
+ const openBottomSheet = () => {
+ bottomSheetRef.current?.openBottomSheet()
+ }
+
+ const closeBottomSheet = () => {
+ bottomSheetRef.current?.closeBottomSheet()
+ }
+
+ const handleClick = () => {
+ if (bottomSheetRef.current?.isOpen) closeBottomSheet()
+ else openBottomSheet()
+ }
+
+ return (
+
+
+
+
+
+
+ FAke content
+
+ FAke content
+
+ FAke content
+
+ FAke content
+
+ FAke content
+
+ FAke content
+
+ FAke content
+
+ FAke content
+
+ FAke content
+
+ FAke content
+
+ FAke content
+
+ FAke content
+
+ FAke content
+
+ FAke content
+
+ FAke content
+
+ FAke content
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+const styles = StyleSheet.create({
+ input: {
+ alignSelf: 'stretch',
+ height: 40,
+ borderWidth: 1,
+ },
+})
diff --git a/apps/wallet-mobile/src/components/BottomSheet/BottomSheet.tsx b/apps/wallet-mobile/src/components/BottomSheet/BottomSheet.tsx
new file mode 100644
index 0000000000..b7fb484b05
--- /dev/null
+++ b/apps/wallet-mobile/src/components/BottomSheet/BottomSheet.tsx
@@ -0,0 +1,145 @@
+import React from 'react'
+import {KeyboardAvoidingView, Modal, NativeTouchEvent, Platform, StyleSheet, Text, View} from 'react-native'
+
+import {Spacer} from '../Spacer'
+
+type BottomSheetProps = {
+ children: React.ReactNode
+ height?: number
+ debug?: boolean
+ isExtendable?: boolean
+ maxHeight?: `${number}%` | number
+ title: string
+}
+
+export type BottomSheetRef = {
+ openBottomSheet: () => void
+ closeBottomSheet: () => void
+ isOpen: boolean
+ onResponderMove: ({nativeEvent}: {nativeEvent: NativeTouchEvent}) => void // in case it needs to be used outside
+}
+
+export const BottomSheet = React.forwardRef(
+ ({children, height = 300, debug = false, maxHeight = '80%', title, isExtendable = true}, ref) => {
+ const [isOpen, setIsOpen] = React.useState(false)
+ const [isExtended, setExtended] = React.useState(false)
+ const [swipeLocationY, setSwipeLocationY] = React.useState(height)
+ const [upDirectionCount, setUpDirectionCount] = React.useState(0)
+ const [downDirectionCount, setDownDirectionCount] = React.useState(0)
+
+ React.useImperativeHandle(ref, () => ({
+ openBottomSheet,
+ closeBottomSheet,
+ isOpen,
+ onResponderMove,
+ }))
+
+ const openBottomSheet = () => {
+ setIsOpen(true)
+ }
+
+ const closeBottomSheet = () => {
+ if (isExtended) setExtended(false)
+ setIsOpen(false)
+ }
+
+ const extendBottomSheet = () => {
+ setExtended(true)
+ }
+
+ const cleanDirectionCount = () => {
+ setDownDirectionCount(0)
+ setUpDirectionCount(0)
+ }
+
+ const onResponderMove = ({nativeEvent}: {nativeEvent: NativeTouchEvent}) => {
+ if (swipeLocationY < nativeEvent.locationY) {
+ const newState = downDirectionCount + 1
+ if (newState > 4) {
+ closeBottomSheet()
+ cleanDirectionCount()
+ } else setDownDirectionCount(newState)
+ } else if (swipeLocationY > nativeEvent.locationY) {
+ const newState = upDirectionCount + 1
+ if (newState > 4) {
+ extendBottomSheet()
+ cleanDirectionCount()
+ } else setUpDirectionCount(newState)
+ }
+ setSwipeLocationY(nativeEvent.locationY)
+ }
+
+ return (
+ <>
+ {isOpen && }
+
+
+
+
+
+
+ {children}
+
+
+
+ >
+ )
+ },
+)
+
+const Header = ({
+ onResponderMove,
+ title,
+}: {
+ onResponderMove: ({nativeEvent}: {nativeEvent: NativeTouchEvent}) => void
+ title: string
+}) => {
+ return (
+ true} style={styles.header}>
+
+
+
+
+
+
+ {title}
+
+ )
+}
+
+const SliderIndicator = () =>
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ justifyContent: 'flex-end',
+ alignItems: 'center',
+ },
+ backdrop: {
+ ...StyleSheet.absoluteFillObject,
+ backgroundColor: 'rgba(0,0,0,0.5)',
+ },
+ sheet: {
+ backgroundColor: 'white',
+ borderTopRightRadius: 20,
+ borderTopLeftRadius: 20,
+ alignItems: 'center',
+ alignSelf: 'stretch',
+ },
+ title: {
+ fontWeight: '500',
+ fontFamily: 'Rubik-Medium',
+ fontSize: 20,
+ padding: 16,
+ },
+ header: {
+ alignItems: 'center',
+ alignSelf: 'stretch',
+ },
+ slider: {
+ height: 4,
+ backgroundColor: 'black',
+ width: 32,
+ borderRadius: 10,
+ },
+})
diff --git a/apps/wallet-mobile/src/components/BottomSheet/index.ts b/apps/wallet-mobile/src/components/BottomSheet/index.ts
new file mode 100644
index 0000000000..06b6ba56fd
--- /dev/null
+++ b/apps/wallet-mobile/src/components/BottomSheet/index.ts
@@ -0,0 +1 @@
+export * from './BottomSheet'
diff --git a/apps/wallet-mobile/src/components/index.ts b/apps/wallet-mobile/src/components/index.ts
index 762c9dd573..d4ed6ba8c4 100644
--- a/apps/wallet-mobile/src/components/index.ts
+++ b/apps/wallet-mobile/src/components/index.ts
@@ -1,5 +1,6 @@
export * from './Analytics'
export * from './Banner'
+export * from './BottomSheet'
export * from './BottomSheetModal'
export * from './Boundary'
export * from './BulletPointItem'
diff --git a/apps/wallet-mobile/src/features/Swap/useCases/ConfirmTxScreen/ConfirmTxScreen.tsx b/apps/wallet-mobile/src/features/Swap/useCases/ConfirmTxScreen/ConfirmTxScreen.tsx
index d7fc085395..446a91cd21 100644
--- a/apps/wallet-mobile/src/features/Swap/useCases/ConfirmTxScreen/ConfirmTxScreen.tsx
+++ b/apps/wallet-mobile/src/features/Swap/useCases/ConfirmTxScreen/ConfirmTxScreen.tsx
@@ -3,8 +3,7 @@ import React from 'react'
import {StyleSheet, View, ViewProps} from 'react-native'
import {SafeAreaView} from 'react-native-safe-area-context'
-import {Button} from '../../../../components'
-import {BottomSheetModal} from '../../../../components/BottomSheetModal'
+import {BottomSheet, BottomSheetRef, Button, Spacer} from '../../../../components'
import {LoadingOverlay} from '../../../../components/LoadingOverlay'
import {useWalletNavigation} from '../../../../navigation'
import {useSelectedWallet} from '../../../../SelectedWallet'
@@ -17,7 +16,15 @@ import {ConfirmTx} from './ConfirmTx'
import {TransactionSummary} from './TransactionSummary'
export const ConfirmTxScreen = () => {
- const [confirmationModal, setConfirmationModal] = React.useState(false)
+ const bottomSheetRef = React.useRef(null)
+
+ const openBottomSheet = () => {
+ bottomSheetRef.current?.openBottomSheet()
+ }
+
+ const closeBottomSheet = () => {
+ bottomSheetRef.current?.closeBottomSheet()
+ }
const strings = useStrings()
const wallet = useSelectedWallet()
@@ -62,27 +69,29 @@ export const ConfirmTxScreen = () => {
authWithOs()
return
}
- setConfirmationModal(true)
+ openBottomSheet()
}}
/>
- {
- setConfirmationModal(false)
- }}
- contentContainerStyle={{justifyContent: 'space-between'}}
+ isExtendable={false}
>
- resetToTxHistory()}
- onCancel={() => setConfirmationModal(false)}
- />
-
+
+
+
+
+
+
@@ -102,4 +111,9 @@ const styles = StyleSheet.create({
actions: {
paddingVertical: 16,
},
+ modalContent: {
+ flex: 1,
+ alignSelf: 'stretch',
+ paddingHorizontal: 16,
+ },
})