Skip to content

Commit

Permalink
"Good enough" solution for inner scroll mgmt in BottomModal
Browse files Browse the repository at this point in the history
Allows to close the modal by swiping down
  • Loading branch information
mvaivre committed Oct 2, 2024
1 parent bebc315 commit 74af13d
Showing 1 changed file with 65 additions and 34 deletions.
99 changes: 65 additions & 34 deletions apps/mobile-wallet/src/features/modals/BottomModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import Animated, {
interpolate,
runOnJS,
runOnUI,
useAnimatedScrollHandler,
useAnimatedStyle,
useSharedValue,
withSpring,
Expand Down Expand Up @@ -95,6 +96,7 @@ const BottomModal = ({

const contentHeight = useSharedValue(0)
const [isScrollable, setIsScrollable] = useState(false)
const contentScrollY = useSharedValue(0)

const maxHeight = dimensions.height

Expand Down Expand Up @@ -180,10 +182,36 @@ const BottomModal = ({
position.value = 'minimised'
}, [minHeight.value, modalHeight, navHeight, position])

// Modal panning gesture handler 👇
// eslint-disable-next-line react-hooks/exhaustive-deps
const handleDragEnd = () => {
'worklet'

const shouldMinimise = position.value === 'maximised' && -modalHeight.value < dimensions.height - DRAG_BUFFER

const shouldMaximise =
canMaximize.value && position.value === 'minimised' && -modalHeight.value > minHeight.value + DRAG_BUFFER

const shouldClose =
['minimised', 'closing'].includes(position.value) && -modalHeight.value < minHeight.value - DRAG_BUFFER

if (shouldMaximise) {
handleMaximize()
} else if (shouldMinimise) {
shouldMaximizeOnOpen.value ? handleClose() : handleMinimize()
} else if (shouldClose) {
handleClose()
} else {
modalHeight.value =
position.value === 'maximised'
? withSpring(-maxHeight, springConfig)
: withSpring(-minHeight.value, springConfig)
}
}

const panGesture = useMemo(
() =>
Gesture.Pan()
.activeOffsetY(5)
.onStart(() => {
offsetY.value = modalHeight.value
})
Expand All @@ -193,42 +221,44 @@ const BottomModal = ({
}
})
.onEnd(() => {
const shouldMinimise = position.value === 'maximised' && -modalHeight.value < dimensions.height - DRAG_BUFFER

const shouldMaximise =
canMaximize.value && position.value === 'minimised' && -modalHeight.value > minHeight.value + DRAG_BUFFER

const shouldClose =
['minimised', 'closing'].includes(position.value) && -modalHeight.value < minHeight.value - DRAG_BUFFER

if (shouldMaximise) {
handleMaximize()
} else if (shouldMinimise) {
shouldMaximizeOnOpen.value ? handleClose() : handleMinimize()
} else if (shouldClose) {
handleClose()
} else {
modalHeight.value =
position.value === 'maximised'
? withSpring(-maxHeight, springConfig)
: withSpring(-minHeight.value, springConfig)
}
handleDragEnd()
}),
[
offsetY,
modalHeight,
position.value,
dimensions.height,
canMaximize.value,
minHeight.value,
handleMaximize,
shouldMaximizeOnOpen.value,
handleClose,
handleMinimize,
maxHeight
]
[offsetY, modalHeight, position.value, handleDragEnd]
)

// Content scroll management 👇
const previousContentScrollY = useSharedValue(0)
const isContentDragged = useSharedValue(false)
const scrollDelta = useSharedValue(0)

const contentScrollHandler = useAnimatedScrollHandler({
onScroll: (e) => {
contentScrollY.value = e.contentOffset.y

scrollDelta.value = Math.round(previousContentScrollY.value - contentScrollY.value)

if (!isContentDragged.value) return

if (contentScrollY.value <= 0) {
// move the whole modal
if (contentScrollY.value < previousContentScrollY.value) {
modalHeight.value -= contentScrollY.value
}
} else if (-modalHeight.value < maxHeight) {
modalHeight.value += scrollDelta.value * 1.3
}
previousContentScrollY.value = contentScrollY.value
},
onBeginDrag: () => {
isContentDragged.value = true
},
onEndDrag: () => {
isContentDragged.value = false

handleDragEnd()
}
})

// Trigger handle close when modal entity is closed from outside
useEffect(() => {
if (position.value !== 'closing' && isModalClosing) {
Expand Down Expand Up @@ -258,6 +288,7 @@ const BottomModal = ({
keyboardShouldPersistTaps="handled"
scrollEnabled={isScrollable}
scrollEventThrottle={16}
onScroll={contentScrollHandler}
contentContainerStyle={[
contentContainerStyle,
{
Expand Down

0 comments on commit 74af13d

Please sign in to comment.