Skip to content

Commit

Permalink
fix(View): Restore scroll position on swipe back cancel (#6325)
Browse files Browse the repository at this point in the history
Мы очень хитро восстанавливаем позицию скролла, надеясь на то, что это всегда будет работать в useEffect, который сработает перед обработкой события transition (перед завершением анимации), где окончательно сбросятся все состояния.
Но в случае, если пользователь вернул панель назад, мы просто сбрасываем состояние компонента, отвечающее, за свайп. Логика, отвечающая за восстановление скролла не срабатывает, потому что это не считается как `failure`.

Вот тут мы решаем `swipeBack` был успешным (`success`), был отменён (`fail`), то есть свайп не закончен, либо это вообще не считается свайпом, потому что панель по завершении жеста осталась на той же позиции.
https://github.com/VKCOM/VKUI/blob/a4719b49f887c2584eec6655d72e373e62409c59/packages/vkui/src/components/View/View.tsx#L301-L316
Если это не свайп вовсе (пользователь вернул панель на место), то мы просто сбрасываем состояние свайпа с помощью функции `onSwipeBackCancel`.

Но в такой ситуации не сработает условие для восстановления скролла при отмене свайпа.
https://github.com/VKCOM/VKUI/blob/8dbb1de9855af8c772abcb719848175654e39a8a/src/components/View/View.tsx#L494-L500

- caused by #5725

-- Изменения
Вынес логику по восстановлению скролла при отмене свайпа в отдельный useEffect, потому что изначальный слишком большой.
Смотрю на переменные `prevSwipingBack`, `swipingBack`, чтобы понять был ли всё же свайп, потому что `swipeBackResult` нам ни о чем не скажет, даже если бы мы его устанавливали, то он не был бы тут же очищен в том же рендере из-за вызова `onSwipeBackCancel`. Также проверяю `prevSwipeBackShift`, который равен нулю, то есть смещения после жеста у панели нет, что значит, что пользователь жестом свайпа вернул панель туда откуда взял.
  • Loading branch information
mendrew authored Dec 26, 2023
1 parent 611a55b commit 03d7a58
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 7 deletions.
19 changes: 17 additions & 2 deletions packages/vkui/src/components/View/View.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,8 @@ describe('View', () => {
expect(document.getElementById('p1')).toBeTruthy();
expect(document.getElementById('p2')).toBeNull();
});
it('restores scroll after swipeBack', () => {

it('restores scroll after cancelled swipeBack (mouse up during the move)', () => {
let y = 101;
scrollsCache['scroll']['p1'] = 22;
const [MockScroll, scrollTo] = mockScrollContext(() => y);
Expand All @@ -262,7 +263,21 @@ describe('View', () => {
});
fireEvent.mouseUp(view);
rerender(<SwipeBack activePanel="p1" history={['p1']} />);
expect(scrollTo).toBeCalledWith(0, 22);
expect(scrollTo).toHaveBeenCalledWith(0, 22);
});

it('restores scroll when swipeBack cancelled because user moves panel back to starting point', () => {
const currentScrollPosition = 22;
const startPosition = { clientX: 0, clientY: 100 };
const [MockScroll, scrollTo] = mockScrollContext(() => currentScrollPosition);
const { view, rerender, SwipeBack } = setupSwipeBack({ Wrapper: MockScroll });
fireEvent.mouseDown(view, startPosition);
fireEvent.mouseMove(view, { clientX: SWIPE_BACK_SHIFT_THRESHOLD, clientY: 100 });
fireEvent.mouseMove(view, startPosition);
fireEvent.mouseUp(view);

rerender(<SwipeBack activePanel="p2" history={['p2']} />);
expect(scrollTo).toHaveBeenCalledWith(0, currentScrollPosition);
});

describe('horizontal scrollable elements', () => {
Expand Down
33 changes: 28 additions & 5 deletions packages/vkui/src/components/View/View.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ export const View = ({
const prevSwipingBack = usePrevious(swipingBack);
const prevBrowserSwipe = usePrevious(browserSwipe);
const prevSwipeBackResult = usePrevious(swipeBackResult);
const prevSwipeBackShift = usePrevious(swipeBackShift);
const prevSwipeBackPrevPanel = usePrevious(swipeBackPrevPanel);
const prevOnTransition = usePrevious(onTransition);

Expand Down Expand Up @@ -439,11 +440,6 @@ export const View = ({
);
}

// Если свайп назад отменился (когда пользователь недостаточно сильно свайпнул)
if (prevSwipeBackResult === 'fail' && !swipeBackResult && activePanel !== null) {
scroll?.scrollTo(0, scrolls.current[activePanel]);
}

// Закончился Safari свайп
if (prevActivePanel !== activePanelProp && browserSwipe) {
setBrowserSwipe(false);
Expand Down Expand Up @@ -476,6 +472,33 @@ export const View = ({
waitTransitionFinish,
]);

React.useEffect(
function restoreScrollPositionWhenSwipeBackIsCancelled() {
// Если свайп назад отменился (когда пользователь недостаточно сильно свайпнул)
const swipeBackCancelledInTheMiddleOfAction =
prevSwipeBackResult === 'fail' && !swipeBackResult;
const swipeBackCancelledByMovingPanelBackToInitialPoint =
prevSwipingBack && !swipingBack && prevSwipeBackShift === 0;

if (
(swipeBackCancelledInTheMiddleOfAction ||
swipeBackCancelledByMovingPanelBackToInitialPoint) &&
activePanel !== null
) {
scroll?.scrollTo(0, scrolls.current[activePanel]);
}
},
[
prevSwipeBackResult,
swipeBackResult,
prevSwipingBack,
swipingBack,
prevSwipeBackShift,
activePanel,
scroll,
],
);

return (
<NavViewIdContext.Provider value={id}>
<Touch
Expand Down

0 comments on commit 03d7a58

Please sign in to comment.