Skip to content

Commit f9f3863

Browse files
committed
Guard contentOffsetY path behind new arch and skip rAF on non-Android
1 parent 836b547 commit f9f3863

3 files changed

Lines changed: 81 additions & 3 deletions

File tree

src/components/KeyboardChatScrollView/useExtraContentPadding/__fixtures__/setup.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,24 @@ export const createRender = () => {
4848
beforeEach(() => {
4949
mockScrollTo.mockClear();
5050
});
51+
52+
/**
53+
* Temporarily removes `nativeFabricUIManager` from global to simulate legacy
54+
* (bridge) architecture for the duration of the provided callback.
55+
*
56+
* @param fn - Callback to run under legacy arch conditions.
57+
*/
58+
export function withLegacyArch(fn: () => void) {
59+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
60+
const original = (global as any).nativeFabricUIManager;
61+
62+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
63+
(global as any).nativeFabricUIManager = undefined;
64+
65+
try {
66+
fn();
67+
} finally {
68+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
69+
(global as any).nativeFabricUIManager = original;
70+
}
71+
}

src/components/KeyboardChatScrollView/useExtraContentPadding/__tests__/contentOffsetY.spec.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
flushRAF,
55
mockScrollTo,
66
reactionEffect,
7+
withLegacyArch,
78
} from "../__fixtures__/setup";
89

910
describe("useExtraContentPadding — contentOffsetY (iOS atomic path)", () => {
@@ -140,3 +141,53 @@ describe("useExtraContentPadding — contentOffsetY (iOS atomic path)", () => {
140141
expect(mockScrollTo).toHaveBeenCalledWith(expect.anything(), 0, 120, false);
141142
});
142143
});
144+
145+
describe("useExtraContentPadding — iOS legacy arch (scrollTo path)", () => {
146+
it("should use scrollTo directly when on legacy arch even if contentOffsetY is provided (non-inverted)", () => {
147+
const render = createRender();
148+
const contentOffsetY = sv(100);
149+
150+
withLegacyArch(() => {
151+
render({
152+
extraContentPadding: sv(20),
153+
keyboardPadding: sv(300),
154+
scroll: sv(100),
155+
layout: sv({ width: 390, height: 800 }),
156+
size: sv({ width: 390, height: 2000 }),
157+
contentOffsetY,
158+
inverted: false,
159+
keyboardLiftBehavior: "always",
160+
freeze: false,
161+
});
162+
});
163+
164+
reactionEffect(20, 0);
165+
166+
expect(contentOffsetY.value).toBe(100);
167+
expect(mockScrollTo).toHaveBeenCalledWith(expect.anything(), 0, 120, false);
168+
});
169+
170+
it("should use scrollTo directly when on legacy arch even if contentOffsetY is provided (inverted)", () => {
171+
const render = createRender();
172+
const contentOffsetY = sv(5);
173+
174+
withLegacyArch(() => {
175+
render({
176+
extraContentPadding: sv(20),
177+
keyboardPadding: sv(300),
178+
scroll: sv(5),
179+
layout: sv({ width: 390, height: 800 }),
180+
size: sv({ width: 390, height: 2000 }),
181+
contentOffsetY,
182+
inverted: true,
183+
keyboardLiftBehavior: "always",
184+
freeze: false,
185+
});
186+
});
187+
188+
reactionEffect(20, 0);
189+
190+
expect(contentOffsetY.value).toBe(5);
191+
expect(mockScrollTo).toHaveBeenCalledWith(expect.anything(), 0, -15, false);
192+
});
193+
});

src/components/KeyboardChatScrollView/useExtraContentPadding/index.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { useCallback } from "react";
2+
import { Platform } from "react-native";
23
import { scrollTo, useAnimatedReaction } from "react-native-reanimated";
34

45
import { isScrollAtEnd, shouldShiftContent } from "../useChatKeyboard/helpers";
@@ -56,22 +57,27 @@ function useExtraContentPadding(options: UseExtraContentPaddingOptions): void {
5657
freeze,
5758
} = options;
5859

60+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
61+
const isNewArch = !!(global as any).nativeFabricUIManager;
62+
5963
const scrollToTarget = useCallback(
6064
(target: number) => {
6165
"worklet";
6266

63-
if (contentOffsetY) {
67+
if (contentOffsetY && isNewArch) {
6468
// eslint-disable-next-line react-compiler/react-compiler
6569
contentOffsetY.value = target;
66-
} else {
70+
} else if (Platform.OS === "android") {
6771
// Defer scrollTo so the animatedProps inset commit lands first;
6872
// otherwise the native ScrollView clamps to the old range.
6973
requestAnimationFrame(() => {
7074
scrollTo(scrollViewRef, 0, target, false);
7175
});
76+
} else {
77+
scrollTo(scrollViewRef, 0, target, false);
7278
}
7379
},
74-
[scrollViewRef, contentOffsetY],
80+
[scrollViewRef, contentOffsetY, isNewArch],
7581
);
7682

7783
useAnimatedReaction(

0 commit comments

Comments
 (0)