Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automatically auto adjusting content WITH dynamic snap points #1418

Closed
karimcambridge opened this issue Jun 25, 2023 · 8 comments
Closed

Automatically auto adjusting content WITH dynamic snap points #1418

karimcambridge opened this issue Jun 25, 2023 · 8 comments
Labels
enhancement New feature or request no-issue-activity

Comments

@karimcambridge
Copy link

karimcambridge commented Jun 25, 2023

Feature Request

  • True dynamic snap points (the current feature is not 'dynamic snap points', it's more like 'dynamic container height')

So my main app screen as various 'states', where lets say there are 4 states and each state needs different dynamic content in the bottom sheet and have different snap points. I'm trying to figure out how to achieve this. Think rideshare applications.

Why it is needed

More adjustability, better UX.

Possible implementation

Not sure

Code sample

This is my current code, how I’m handling the situation currently:

// Bottom sheet setup

	const bottomSheetRef = useRef<BottomSheet>(null);
	const initialSnapPoints = useMemo(() => {
		switch(authState.requestingStep) {
			case actionTypes.ride.request.DRIVER:
				return ['16%', '40%'];
			default:
				return ['16%'];
		}
	}, [authState.requestingStep]); // CONTENT_HEIGHT
	const currentPosition = useSharedValue(0);

	const {
		animatedHandleHeight,
		animatedSnapPoints,
		animatedContentHeight,
		handleContentLayout,
	} = useBottomSheetDynamicSnapPoints(initialSnapPoints);

	const handleAnimateChanges = useCallback((fromIndex: number, toIndex: number) => {
		//console.log('handleAnimateChanges: fromIndex:', fromIndex, '. toIndex:', toIndex, '. animatedPosition:', currentPosition?.value);
		if(toIndex !== -1 && fromIndex === 0 && hasLocationBeenFound() && authState.requestingStep === actionTypes.ride.request.NONE) {
			openSearch();
		}
	}, [authState.requestingStep]);

// I manually change the mapheight then calculate the bottom sheet position from that.

const mapHeight = useMemo(() => {
		if(isRideComplete(authState.stage) && ride_stage.sub === rideConstants.stages.sub.payment) {
			return (potentiallyHasNotch() ? hp('40%') + insets.top : hp('40%'));
		}
		switch(authState.requestingStep) {
			case actionTypes.ride.request.NONE:
				if(!locationState.permissionState) {
					return (potentiallyHasNotch() ? hp('82%') + insets.top : hp('82%'));
				}
				if(!hasLocationBeenFound()) {
					return (potentiallyHasNotch() ? hp('88%') + insets.top : hp('88%'));
				}
				if(isValidPinPointingLocationState(pinPointingLocationState)) {
					return (potentiallyHasNotch() ? hp('85%') + insets.top : hp('85%'));
				}
				return (potentiallyHasNotch() ? hp('84%') + insets.top : hp('84%'));
			case actionTypes.ride.request.DETAILS:
				return (potentiallyHasNotch() ? hp('54%') + insets.top : hp('54%'));
			case actionTypes.ride.request.DRIVER:
				return (potentiallyHasNotch() ? hp('86%') + insets.top : hp('84%'));
			case actionTypes.ride.request.RIDING:
				return (potentiallyHasNotch() ? hp('75%') + insets.top : hp('75%'));
		}
		return (potentiallyHasNotch() ? hp('68%') + insets.top : hp('68%'));
	}, [authState.requestingStep, pinPointingLocationState]);

	useEffect(() => {
		//console.log(`bottomSheetPos: ${100 - calculatePercentage(mapHeight, hp('100%'))}%`, `screenHeight: ${hp('100%')}. mapHeight: ${mapHeight}. animatedPosition: ${currentPosition?.value}`);
		const
			curPercent = 100 - calculatePercentage(currentPosition.value, hp('100%')),
			mapPercent = 100 - calculatePercentage(mapHeight, hp('100%'))
		;
		if(mapPercent !== curPercent) {
			bottomSheetRef.current.snapToPosition(`${mapPercent}%`);
		}
		//console.log(`currentPosition: ${currentPosition?.value}. curPercent: ${curPercent}. mapPercent: ${mapPercent}`);
	}, [mapHeight]);

So here's the issue, I can't have real dynamic snappoints? I'd like when the app is in state actionTypes.ride.request.DRIVER, that the bottom sheet be able to open to a certain position (see initialSnapPoints) but then when it’s in other states, nada.

Suggestions:

Not sure how to solve this, but if it’s possible already, let me know, else let's talk.

@karimcambridge karimcambridge added the enhancement New feature or request label Jun 25, 2023
@maximus986
Copy link

@karimcambridge Hey, if you set the string 'CONTENT_HEIGHT' as the initial snap point, the component will use it as a placeholder value to add calculated snap points based on the content. You don't need to manually set snap points based on the app state.

Like this:
const initialSnapPoints = useMemo(() => ['CONTENT_HEIGHT'], [])

And then pass initialSnapPoints to useBottomSheetDynamicSnapPoints hook. That should be enough, hope it will help.

@karimcambridge
Copy link
Author

Hello. I tried that at first, it’s buggy. And that also doesn’t achieve having “multiple snap points”

@karimcambridge
Copy link
Author

For example @maximus986 this is with CONTENT_HEIGHT. This makes no sense for my UX. I want it to have 2 'DYNAMIC` snap points

CONTENT_HEIGHT
simulator_screenshot_25209785-87AE-42CE-A9C4-35F6CD9CA63B

My custom solution (with 2 snappoints)

simulator_screenshot_E793F502-81E0-4E01-AF98-FD436A8EE438

simulator_screenshot_EA4DD29C-9A1E-4A22-B0F4-F00EBA85F7EE

The issue with my solution is that it only works when you double save on simulator, doesn't work otherwise because of how the hooks work i guess.

So my suggestion is for actual dynamic snap points that you can change based on some variable. Not just automatically calculated bottom sheet height, which is what that feature is actually doing.

@karimcambridge
Copy link
Author

karimcambridge commented Jun 26, 2023

My solution so far is to place the highest snappoint first like this

const initialSnapPoints = useMemo(() => {
		switch(authState.requestingStep) {
			case actionTypes.ride.request.DRIVER:
				return ['30%', '16%'];
			default:
				return ['16%'];
		}
	}, [authState.requestingStep]); // CONTENT_HEIGHT

But it seems like this only works on initial render, like e.g setup. Whether or not you use CONTENT_HEIGHT it makes no difference.

Thus when the user tries to close the sheet, it will go to the smaller snappoint but with a higher index.

I hope y’all get what i'm trying to explain here.

@slutske22
Copy link

I have been able to achieve this with a small hack. In the View that changes height according to its content, I use the onLayout to set the snappoint:

// BottomSheet.tsx

import RNBottomSheet, {  useBottomSheetDynamicSnapPoints } from '@gorhom/bottom-sheet';

export const BottomSheet: React.FC = () => {
  const initialSnapPoints = useMemo(() => [75, 'CONTENT_HEIGHT'], []);

  const {
    animatedHandleHeight,
    animatedSnapPoints,
    animatedContentHeight,
    handleContentLayout,
  } = useBottomSheetDynamicSnapPoints(initialSnapPoints);

  return (
    <RNBottomSheet
      index={1}
      snapPoints={animatedSnapPoints}
      handleHeight={animatedHandleHeight}
      contentHeight={animatedContentHeight}
    >
      <StuffWithDynamicContent />
    </RNBottomSheet>
  );
};
// StuffWithDynamicContent.tsx

import { useBottomSheet } from '@gorhom/bottom-sheet';

export const StuffWithDynamicContent: React.FC = () => {
  const { snapToIndex } = useBottomSheet();

  return (
    <View
      onLayout={() => { // resnap to the same index that corresponds to CONTENT_HEIGHT
        setTimeout(() => {
          snapToIndex(1);
        }, 50); // needed tiny timout to make sure dynamic content fully rendered
      }}
    >
      // dynamic content here
    </View>
  );
};

When the children of the View in StuffWithDynamicContent changes, the onLayout function fires, every time. You then call snapToIndex, which sets the bottom sheet height to the height of the content. I use the tiny timeout to make sure the content is fully rendered before resetting the bottomsheet height. This is a bit hacky, but is working well for me.

@github-actions
Copy link

github-actions bot commented Aug 9, 2023

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.

@github-actions
Copy link

This issue was closed because it has been stalled for 5 days with no activity.

@karimcambridge
Copy link
Author

Let's not close this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request no-issue-activity
Projects
None yet
Development

No branches or pull requests

3 participants