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

fix: rewrite onTextChanged to onSelectionChanged event handler in KeyboardAwareScrollView #546

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion e2e/kit/004-aware-scroll-view.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ describe("AwareScrollView test cases", () => {
});

it("should auto-scroll when user types a text", async () => {
await element(by.id("aware_scroll_view_container")).scroll(80, "up");
await element(by.id("aware_scroll_view_container")).scroll(40, "up");
await typeText("TextInput#4", "1");
await waitForExpect(async () => {
await expectBitmapsToBeEqual(
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified e2e/kit/assets/ios/iPhone 13 Pro/AwareScrollViewTextChanged.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified e2e/kit/assets/ios/iPhone 14 Pro/AwareScrollViewTextChanged.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified e2e/kit/assets/ios/iPhone 15 Pro/AwareScrollViewTextChanged.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified e2e/kit/assets/ios/iPhone 16 Pro/AwareScrollViewTextChanged.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
76 changes: 56 additions & 20 deletions src/components/KeyboardAwareScrollView/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { forwardRef, useCallback, useMemo } from "react";
import { findNodeHandle } from "react-native";
import Reanimated, {
clamp,
interpolate,
scrollTo,
useAnimatedReaction,
Expand Down Expand Up @@ -111,6 +112,8 @@ const KeyboardAwareScrollView = forwardRef<
const scrollBeforeKeyboardMovement = useSharedValue(0);
const { input } = useReanimatedFocusedInput();
const layout = useSharedValue<FocusedInputLayoutChangedEvent | null>(null);
const lastSelection =
useSharedValue<FocusedInputSelectionChangedEvent | null>(null);

const { height } = useWindowDimensions();

Expand Down Expand Up @@ -153,9 +156,13 @@ const KeyboardAwareScrollView = forwardRef<
const inputHeight = layout.value?.layout.height || 0;
const point = absoluteY + inputHeight;

console.log({ absoluteY, inputHeight, point, visibleRect });

if (visibleRect - point <= bottomOffset) {
const relativeScrollTo =
keyboardHeight.value - (height - point) + bottomOffset;

console.log({ relativeScrollTo });
const interpolatedScrollTo = interpolate(
e,
[initialKeyboardSize.value, keyboardHeight.value],
Expand All @@ -170,6 +177,11 @@ const KeyboardAwareScrollView = forwardRef<
const targetScrollY =
Math.max(interpolatedScrollTo, 0) + scrollPosition.value;

console.log({
targetScrollY,
scrollPosition: scrollPosition.value,
interpolatedScrollTo,
});
scrollTo(scrollViewAnimatedRef, 0, targetScrollY, animated);

return interpolatedScrollTo;
Expand Down Expand Up @@ -207,7 +219,7 @@ const KeyboardAwareScrollView = forwardRef<
);

const scrollFromCurrentPosition = useCallback(
(customHeight?: number) => {
(customHeight: number) => {
"worklet";

const prevScrollPosition = scrollPosition.value;
Expand All @@ -222,49 +234,70 @@ const KeyboardAwareScrollView = forwardRef<
...input.value,
layout: {
...input.value.layout,
height: customHeight ?? input.value.layout.height,
// when we have multiline input with limited amount of lines, then custom height can be very big
// so we clamp it to max input height
height: clamp(customHeight, 0, input.value.layout.height),
},
};
scrollPosition.value = position.value;
maybeScroll(keyboardHeight.value, true);
scrollPosition.value = prevScrollPosition;
layout.value = prevLayout;

console.log({ customHeight });
},
[maybeScroll],
);
const onChangeText = useCallback(() => {
"worklet";

// if typing a text caused layout shift, then we need to ignore this handler
// because this event will be handled in `useAnimatedReaction` below
if (layout.value?.layout.height !== input.value?.layout.height) {
return;
}

scrollFromCurrentPosition();
}, [scrollFromCurrentPosition]);
const onSelectionChange = useCallback(
(e: FocusedInputSelectionChangedEvent) => {
const onChangeText = useCallback(
(customHeight: number) => {
"worklet";

if (e.selection.start.position !== e.selection.end.position) {
scrollFromCurrentPosition(e.selection.end.y);
// if typing a text caused layout shift, then we need to ignore this handler
// because this event will be handled in `useAnimatedReaction` below
if (layout.value?.layout.height !== input.value?.layout.height) {
return;
}

console.debug("maybeScroll - onChangeText");
scrollFromCurrentPosition(customHeight);
},
[scrollFromCurrentPosition],
);

const onChangeTextHandler = useMemo(
() => debounce(onChangeText, 200),
[onChangeText],
);
const onSelectionChange = useCallback(
(e: FocusedInputSelectionChangedEvent) => {
"worklet";

const lastTarget = lastSelection.value?.target;

lastSelection.value = e;

if (e.target !== lastTarget) {
// ignore this event, because "focus changed" event handled in `useSmoothKeyboardHandler`
return;
}

console.log(e);

if (e.selection.start.position !== e.selection.end.position) {
console.debug("onSelectionChange - onChangeText");

return scrollFromCurrentPosition(e.selection.end.y);
}

onChangeTextHandler(e.selection.end.y);
},
[scrollFromCurrentPosition, onChangeTextHandler],
);

useFocusedInputHandler(
{
onChangeText: onChangeTextHandler,
onSelectionChange: onSelectionChange,
},
[onChangeTextHandler, onSelectionChange],
[onSelectionChange],
);

useSmoothKeyboardHandler(
Expand Down Expand Up @@ -317,6 +350,7 @@ const KeyboardAwareScrollView = forwardRef<
if (focusWasChanged && !keyboardWillAppear.value) {
// update position on scroll value, so `onEnd` handler
// will pick up correct values
console.debug("maybeScroll - onStart");
position.value += maybeScroll(e.height, true);
}
},
Expand All @@ -327,6 +361,7 @@ const KeyboardAwareScrollView = forwardRef<

// if the user has set disableScrollOnKeyboardHide, only auto-scroll when the keyboard opens
if (!disableScrollOnKeyboardHide || keyboardWillAppear.value) {
console.debug("maybeScroll - onMove");
maybeScroll(e.height);
}
},
Expand All @@ -352,6 +387,7 @@ const KeyboardAwareScrollView = forwardRef<
const prevLayout = layout.value;

layout.value = input.value;
console.debug("maybeScroll - animated reaction");
scrollPosition.value += maybeScroll(keyboardHeight.value, true);
layout.value = prevLayout;
}
Expand Down
Loading