diff --git a/apps/mobile/package.json b/apps/mobile/package.json index d1bd5c810894f48a3e5c88c824ff75be41104311..b5f0d49d2bc1b0d766c75071ed1963bb6e074a55 100644 --- a/apps/mobile/package.json +++ b/apps/mobile/package.json @@ -114,7 +114,7 @@ "react-native": "0.88.0-rc.3", "react-native-gesture-handler": "~3.2.1", "react-native-image-viewing": "^0.2.2", - "react-native-keyboard-controller": "1.22.4", + "react-native-keyboard-controller": "1.22.6", "react-native-nitro-markdown": "^0.5.0", "react-native-nitro-modules": "0.35.9", "react-native-reanimated": "4.7.0", diff --git a/patches/react-native-keyboard-controller@1.22.4.patch b/patches/react-native-keyboard-controller@1.22.4.patch deleted file mode 100644 index 01c1760bf33aaa2cb7648895ff5a66487ee9ca78..0000000000000000000000000000000000000000 --- a/patches/react-native-keyboard-controller@1.22.4.patch +++ /dev/null @@ -1,933 +0,0 @@ -diff --git a/lib/commonjs/components/KeyboardChatScrollView/index.js b/lib/commonjs/components/KeyboardChatScrollView/index.js -index 5617394..4f2222a 100644 ---- a/lib/commonjs/components/KeyboardChatScrollView/index.js -+++ b/lib/commonjs/components/KeyboardChatScrollView/index.js -@@ -27,9 +27,12 @@ const KeyboardChatScrollView = /*#__PURE__*/(0, _react.forwardRef)(({ - offset = 0, - extraContentPadding = ZERO_CONTENT_PADDING, - blankSpace = ZERO_BLANK_SPACE, -+ adjustedInsetCompensation = 0, -+ adjustedStartInsetCompensation = 0, - applyWorkaroundForContentInsetHitTestBug = false, - onLayout: onLayoutProp, - onContentSizeChange: onContentSizeChangeProp, -+ onContentInsetChange, - onEndVisible, - ...rest - }, ref) => { -@@ -51,13 +54,17 @@ const KeyboardChatScrollView = /*#__PURE__*/(0, _react.forwardRef)(({ - freeze: freezeSV, - offset, - blankSpace, -- extraContentPadding -+ extraContentPadding, -+ adjustedInsetCompensation, -+ adjustedStartInsetCompensation - }); - (0, _useExtraContentPadding.useExtraContentPadding)({ - scrollViewRef, - extraContentPadding, - keyboardPadding: padding, - blankSpace, -+ adjustedInsetCompensation, -+ adjustedStartInsetCompensation, - scroll, - layout, - size, -@@ -88,10 +95,26 @@ const KeyboardChatScrollView = /*#__PURE__*/(0, _react.forwardRef)(({ - // a bug for you, please open an issue. - const totalPadding = (0, _reactNativeReanimated.useDerivedValue)(() => Math.min(layout.value.height, Math.max(blankSpace.value, padding.value + extraContentPadding.value))); - -+ // iOS applies the destination contentInset and contentOffset together at -+ // keyboard-animation start. Keep that native target behavior, but report -+ // the presentation height so virtualized-list layout follows the keyboard. -+ const reportedPadding = (0, _reactNativeReanimated.useDerivedValue)(() => _reactNative.Platform.OS === "ios" ? Math.min(layout.value.height, Math.max(blankSpace.value, currentHeight.value + extraContentPadding.value)) : totalPadding.value); -+ -+ // Mirror the effective bottom padding (keyboard + composer + blank floor) -+ // to the consumer - a virtualized list needs it in its own scroll math or -+ // its end/maintain targets point at the under-the-keyboard resting offset. -+ (0, _reactNativeReanimated.useAnimatedReaction)(() => reportedPadding.value, (current, previous) => { -+ if (onContentInsetChange && current !== previous) { -+ (0, _reactNativeReanimated.runOnJS)(onContentInsetChange)({ -+ bottom: current -+ }); -+ } -+ }, [onContentInsetChange]); -+ - // Scroll indicator inset = keyboard + extraContentPadding (excludes blankSpace). - // Apps that render into the unsafe area can supply a negative - // scrollIndicatorInsets adjustment at the application layer. -- const indicatorPadding = (0, _reactNativeReanimated.useDerivedValue)(() => padding.value + extraContentPadding.value); -+ const indicatorPadding = (0, _reactNativeReanimated.useDerivedValue)(() => padding.value); - const onLayout = (0, _react.useCallback)(e => { - onLayoutInternal(e); - onLayoutProp === null || onLayoutProp === void 0 || onLayoutProp(e); -diff --git a/lib/commonjs/components/KeyboardChatScrollView/useChatKeyboard/helpers.js b/lib/commonjs/components/KeyboardChatScrollView/useChatKeyboard/helpers.js -index 4d33b3f..6a787d3 100644 ---- a/lib/commonjs/components/KeyboardChatScrollView/useChatKeyboard/helpers.js -+++ b/lib/commonjs/components/KeyboardChatScrollView/useChatKeyboard/helpers.js -@@ -206,7 +206,7 @@ const clampedScrollTarget = (offsetBeforeScroll, keyboardHeight, contentHeight, - * ``` - */ - exports.clampedScrollTarget = clampedScrollTarget; --const computeIOSContentOffset = (relativeScroll, keyboardHeight, contentHeight, layoutHeight, inverted, totalPaddingForMaxScroll) => { -+const computeIOSContentOffset = (relativeScroll, keyboardHeight, contentHeight, layoutHeight, inverted, totalPaddingForMaxScroll, startInsetCompensation = 0) => { - "worklet"; - - const paddingForMax = totalPaddingForMaxScroll !== undefined ? totalPaddingForMaxScroll : keyboardHeight; -@@ -214,8 +214,9 @@ const computeIOSContentOffset = (relativeScroll, keyboardHeight, contentHeight, - const maxScroll = Math.max(contentHeight - layoutHeight, 0); - return Math.max(Math.min(relativeScroll - keyboardHeight, maxScroll), -paddingForMax); - } -- const maxScroll = Math.max(contentHeight - layoutHeight + paddingForMax, 0); -- return Math.min(Math.max(keyboardHeight + relativeScroll, 0), maxScroll); -+ const minScroll = -startInsetCompensation; -+ const maxScroll = Math.max(contentHeight - layoutHeight + paddingForMax, minScroll); -+ return Math.min(Math.max(keyboardHeight + relativeScroll, minScroll), maxScroll); - }; - exports.computeIOSContentOffset = computeIOSContentOffset; - //# sourceMappingURL=helpers.js.map -\ No newline at end of file -diff --git a/lib/commonjs/components/KeyboardChatScrollView/useChatKeyboard/index.ios.js b/lib/commonjs/components/KeyboardChatScrollView/useChatKeyboard/index.ios.js -index cecee07..533a9e9 100644 ---- a/lib/commonjs/components/KeyboardChatScrollView/useChatKeyboard/index.ios.js -+++ b/lib/commonjs/components/KeyboardChatScrollView/useChatKeyboard/index.ios.js -@@ -32,7 +32,9 @@ function useChatKeyboard(scrollViewRef, options) { - freeze, - offset, - blankSpace, -- extraContentPadding -+ extraContentPadding, -+ adjustedInsetCompensation, -+ adjustedStartInsetCompensation - } = options; - const padding = (0, _reactNativeReanimated.useSharedValue)(0); - const currentHeight = (0, _reactNativeReanimated.useSharedValue)(0); -@@ -59,6 +61,9 @@ function useChatKeyboard(scrollViewRef, options) { - targetKeyboardHeight.value = e.height; - } - const effective = (0, _helpers.getEffectiveHeight)(e.height, targetKeyboardHeight.value, offset); -+ if (e.duration === 0) { -+ currentHeight.value = effective; -+ } - const atEnd = (0, _helpers.isScrollAtEnd)(scroll.value, layout.value.height, size.value.height, inverted); - - // Scale minimum padding absorption by how much of it is visible. -@@ -67,7 +72,7 @@ function useChatKeyboard(scrollViewRef, options) { - const visiblePadding = visibleFraction * blankSpace.value; - const minimumPaddingAbsorbed = Math.max(0, visiblePadding - extraContentPadding.value); - const scrollEffective = (0, _helpers.getScrollEffective)(effective, minimumPaddingAbsorbed); -- const actualTotalPadding = Math.max(blankSpace.value, effective + extraContentPadding.value); -+ const actualTotalPadding = Math.max(blankSpace.value, effective + extraContentPadding.value) + adjustedInsetCompensation; - - // `onInteractive` owns the native scroll offset. When its final - // keyboardWillHide event arrives, consume the marker so this -@@ -87,8 +92,8 @@ function useChatKeyboard(scrollViewRef, options) { - const maxScroll = Math.max(size.value.height - layout.value.height, 0); - contentOffsetY.value = Math.max(-actualTotalPadding, Math.min(scroll.value, maxScroll)); - } else { -- const maxScroll = Math.max(size.value.height - layout.value.height + actualTotalPadding, 0); -- contentOffsetY.value = Math.max(0, Math.min(scroll.value, maxScroll)); -+ const maxScroll = Math.max(size.value.height - layout.value.height + actualTotalPadding, -adjustedStartInsetCompensation); -+ contentOffsetY.value = Math.max(-adjustedStartInsetCompensation, Math.min(scroll.value, maxScroll)); - } - return; - } -@@ -102,8 +107,8 @@ function useChatKeyboard(scrollViewRef, options) { - const maxScroll = Math.max(size.value.height - layout.value.height, 0); - contentOffsetY.value = Math.max(-actualTotalPadding, Math.min(scroll.value, maxScroll)); - } else { -- const maxScroll = Math.max(size.value.height - layout.value.height + actualTotalPadding, 0); -- contentOffsetY.value = Math.max(0, Math.min(scroll.value, maxScroll)); -+ const maxScroll = Math.max(size.value.height - layout.value.height + actualTotalPadding, -adjustedStartInsetCompensation); -+ contentOffsetY.value = Math.max(-adjustedStartInsetCompensation, Math.min(scroll.value, maxScroll)); - } - return; - } -@@ -129,17 +134,25 @@ function useChatKeyboard(scrollViewRef, options) { - contentOffsetY.value = scroll.value; - return; - } -- contentOffsetY.value = (0, _helpers.computeIOSContentOffset)(relativeScroll, scrollEffective, size.value.height, layout.value.height, inverted, actualTotalPadding); -+ contentOffsetY.value = (0, _helpers.computeIOSContentOffset)(relativeScroll, scrollEffective, size.value.height, layout.value.height, inverted, actualTotalPadding, adjustedStartInsetCompensation); - }, -- onInteractive: () => { -+ onMove: e => { - "worklet"; - -- isInteractiveDismissal.value = true; -+ if (freeze.value) { -+ return; -+ } -+ currentHeight.value = (0, _helpers.getEffectiveHeight)(e.height, targetKeyboardHeight.value, offset); - }, -- onMove: () => { -+ onInteractive: e => { - "worklet"; - -- // iOS doesn't need per-frame updates (contentOffset handles it) -+ isInteractiveDismissal.value = true; -+ -+ if (freeze.value) { -+ return; -+ } -+ currentHeight.value = (0, _helpers.getEffectiveHeight)(e.height, targetKeyboardHeight.value, offset); - }, - onEnd: e => { - "worklet"; -@@ -148,9 +161,10 @@ function useChatKeyboard(scrollViewRef, options) { - return; - } - const effective = (0, _helpers.getEffectiveHeight)(e.height, targetKeyboardHeight.value, offset); -+ currentHeight.value = effective; - padding.value = effective; - } -- }, [inverted, keyboardLiftBehavior, offset, extraContentPadding]); -+ }, [inverted, keyboardLiftBehavior, offset, extraContentPadding, adjustedInsetCompensation, adjustedStartInsetCompensation]); - return { - padding, - currentHeight, -diff --git a/lib/commonjs/components/KeyboardChatScrollView/useExtraContentPadding/index.js b/lib/commonjs/components/KeyboardChatScrollView/useExtraContentPadding/index.js -index 54a1c57..00c2941 100644 ---- a/lib/commonjs/components/KeyboardChatScrollView/useExtraContentPadding/index.js -+++ b/lib/commonjs/components/KeyboardChatScrollView/useExtraContentPadding/index.js -@@ -31,6 +31,8 @@ function useExtraContentPadding(options) { - extraContentPadding, - keyboardPadding, - blankSpace, -+ adjustedInsetCompensation, -+ adjustedStartInsetCompensation, - scroll, - layout, - size, -@@ -50,8 +52,13 @@ function useExtraContentPadding(options) { - // otherwise the native ScrollView clamps to the old range. - requestAnimationFrame(() => { - // check that view is still mounted and ref is actual -- // otherwise it may lead to a crash -- if (!scrollViewRef()) { -+ // otherwise it may lead to a crash. Reanimated 4.7+ passes the -+ // ref to the UI runtime as a shareable holding the node in `.value`. -+ const node = -+ typeof scrollViewRef === "function" -+ ? scrollViewRef() -+ : scrollViewRef.value; -+ if (!node) { - return; - } - (0, _reactNativeReanimated.scrollTo)(scrollViewRef, 0, target, false); -@@ -70,8 +77,8 @@ function useExtraContentPadding(options) { - } - - // Compute effective delta considering blankSpace floor -- const previousTotal = Math.max(blankSpace.value, keyboardPadding.value + previous); -- const currentTotal = Math.max(blankSpace.value, keyboardPadding.value + current); -+ const previousTotal = Math.max(blankSpace.value, keyboardPadding.value + previous) + adjustedInsetCompensation; -+ const currentTotal = Math.max(blankSpace.value, keyboardPadding.value + current) + adjustedInsetCompensation; - const effectiveDelta = currentTotal - previousTotal; - if (effectiveDelta === 0) { - // blankSpace absorbed the change -@@ -90,10 +97,11 @@ function useExtraContentPadding(options) { - const target = Math.max(scroll.value - effectiveDelta, -currentTotal); - scrollToTarget(target); - } else { -- const maxScroll = Math.max(size.value.height - layout.value.height + currentTotal, 0); -- const target = Math.min(scroll.value + effectiveDelta, maxScroll); -+ const minScroll = -adjustedStartInsetCompensation; -+ const maxScroll = Math.max(size.value.height - layout.value.height + currentTotal, minScroll); -+ const target = Math.max(minScroll, Math.min(scroll.value + effectiveDelta, maxScroll)); - scrollToTarget(target); - } -- }, [inverted, keyboardLiftBehavior]); -+ }, [inverted, keyboardLiftBehavior, adjustedInsetCompensation, adjustedStartInsetCompensation]); - } - //# sourceMappingURL=index.js.map -\ No newline at end of file -diff --git a/lib/module/components/KeyboardChatScrollView/index.js b/lib/module/components/KeyboardChatScrollView/index.js -index d085394..a57c2f9 100644 ---- a/lib/module/components/KeyboardChatScrollView/index.js -+++ b/lib/module/components/KeyboardChatScrollView/index.js -@@ -1,7 +1,7 @@ - function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); } - import React, { forwardRef, useCallback, useMemo } from "react"; --import { StyleSheet } from "react-native"; --import { makeMutable, useAnimatedRef, useAnimatedStyle, useDerivedValue } from "react-native-reanimated"; -+import { Platform, StyleSheet } from "react-native"; -+import { makeMutable, runOnJS, useAnimatedReaction, useAnimatedRef, useAnimatedStyle, useDerivedValue } from "react-native-reanimated"; - import Reanimated from "react-native-reanimated"; - import useCombinedRef from "../hooks/useCombinedRef"; - import ScrollViewWithBottomPadding from "../ScrollViewWithBottomPadding"; -@@ -20,9 +20,12 @@ const KeyboardChatScrollView = /*#__PURE__*/forwardRef(({ - offset = 0, - extraContentPadding = ZERO_CONTENT_PADDING, - blankSpace = ZERO_BLANK_SPACE, -+ adjustedInsetCompensation = 0, -+ adjustedStartInsetCompensation = 0, - applyWorkaroundForContentInsetHitTestBug = false, - onLayout: onLayoutProp, - onContentSizeChange: onContentSizeChangeProp, -+ onContentInsetChange, - onEndVisible, - ...rest - }, ref) => { -@@ -44,13 +47,17 @@ const KeyboardChatScrollView = /*#__PURE__*/forwardRef(({ - freeze: freezeSV, - offset, - blankSpace, -- extraContentPadding -+ extraContentPadding, -+ adjustedInsetCompensation, -+ adjustedStartInsetCompensation - }); - useExtraContentPadding({ - scrollViewRef, - extraContentPadding, - keyboardPadding: padding, - blankSpace, -+ adjustedInsetCompensation, -+ adjustedStartInsetCompensation, - scroll, - layout, - size, -@@ -81,10 +88,26 @@ const KeyboardChatScrollView = /*#__PURE__*/forwardRef(({ - // a bug for you, please open an issue. - const totalPadding = useDerivedValue(() => Math.min(layout.value.height, Math.max(blankSpace.value, padding.value + extraContentPadding.value))); - -+ // iOS applies the destination contentInset and contentOffset together at -+ // keyboard-animation start. Keep that native target behavior, but report -+ // the presentation height so virtualized-list layout follows the keyboard. -+ const reportedPadding = useDerivedValue(() => Platform.OS === "ios" ? Math.min(layout.value.height, Math.max(blankSpace.value, currentHeight.value + extraContentPadding.value)) : totalPadding.value); -+ -+ // Mirror the effective bottom padding (keyboard + composer + blank floor) -+ // to the consumer - a virtualized list needs it in its own scroll math or -+ // its end/maintain targets point at the under-the-keyboard resting offset. -+ useAnimatedReaction(() => reportedPadding.value, (current, previous) => { -+ if (onContentInsetChange && current !== previous) { -+ runOnJS(onContentInsetChange)({ -+ bottom: current -+ }); -+ } -+ }, [onContentInsetChange]); -+ - // Scroll indicator inset = keyboard + extraContentPadding (excludes blankSpace). - // Apps that render into the unsafe area can supply a negative - // scrollIndicatorInsets adjustment at the application layer. -- const indicatorPadding = useDerivedValue(() => padding.value + extraContentPadding.value); -+ const indicatorPadding = useDerivedValue(() => padding.value); - const onLayout = useCallback(e => { - onLayoutInternal(e); - onLayoutProp === null || onLayoutProp === void 0 || onLayoutProp(e); -diff --git a/lib/module/components/KeyboardChatScrollView/useChatKeyboard/helpers.js b/lib/module/components/KeyboardChatScrollView/useChatKeyboard/helpers.js -index 295e221..2f7b87e 100644 ---- a/lib/module/components/KeyboardChatScrollView/useChatKeyboard/helpers.js -+++ b/lib/module/components/KeyboardChatScrollView/useChatKeyboard/helpers.js -@@ -193,7 +193,7 @@ export const clampedScrollTarget = (offsetBeforeScroll, keyboardHeight, contentH - * computeIOSContentOffset(100, 300, 1000, 800, false); // 400 - * ``` - */ --export const computeIOSContentOffset = (relativeScroll, keyboardHeight, contentHeight, layoutHeight, inverted, totalPaddingForMaxScroll) => { -+export const computeIOSContentOffset = (relativeScroll, keyboardHeight, contentHeight, layoutHeight, inverted, totalPaddingForMaxScroll, startInsetCompensation = 0) => { - "worklet"; - - const paddingForMax = totalPaddingForMaxScroll !== undefined ? totalPaddingForMaxScroll : keyboardHeight; -@@ -201,7 +201,8 @@ export const computeIOSContentOffset = (relativeScroll, keyboardHeight, contentH - const maxScroll = Math.max(contentHeight - layoutHeight, 0); - return Math.max(Math.min(relativeScroll - keyboardHeight, maxScroll), -paddingForMax); - } -- const maxScroll = Math.max(contentHeight - layoutHeight + paddingForMax, 0); -- return Math.min(Math.max(keyboardHeight + relativeScroll, 0), maxScroll); -+ const minScroll = -startInsetCompensation; -+ const maxScroll = Math.max(contentHeight - layoutHeight + paddingForMax, minScroll); -+ return Math.min(Math.max(keyboardHeight + relativeScroll, minScroll), maxScroll); - }; - //# sourceMappingURL=helpers.js.map -\ No newline at end of file -diff --git a/lib/module/components/KeyboardChatScrollView/useChatKeyboard/index.ios.js b/lib/module/components/KeyboardChatScrollView/useChatKeyboard/index.ios.js -index 407275d..19427d7 100644 ---- a/lib/module/components/KeyboardChatScrollView/useChatKeyboard/index.ios.js -+++ b/lib/module/components/KeyboardChatScrollView/useChatKeyboard/index.ios.js -@@ -25,7 +25,9 @@ function useChatKeyboard(scrollViewRef, options) { - freeze, - offset, - blankSpace, -- extraContentPadding -+ extraContentPadding, -+ adjustedInsetCompensation, -+ adjustedStartInsetCompensation - } = options; - const padding = useSharedValue(0); - const currentHeight = useSharedValue(0); -@@ -52,6 +54,9 @@ function useChatKeyboard(scrollViewRef, options) { - targetKeyboardHeight.value = e.height; - } - const effective = getEffectiveHeight(e.height, targetKeyboardHeight.value, offset); -+ if (e.duration === 0) { -+ currentHeight.value = effective; -+ } - const atEnd = isScrollAtEnd(scroll.value, layout.value.height, size.value.height, inverted); - - // Scale minimum padding absorption by how much of it is visible. -@@ -60,7 +65,7 @@ function useChatKeyboard(scrollViewRef, options) { - const visiblePadding = visibleFraction * blankSpace.value; - const minimumPaddingAbsorbed = Math.max(0, visiblePadding - extraContentPadding.value); - const scrollEffective = getScrollEffective(effective, minimumPaddingAbsorbed); -- const actualTotalPadding = Math.max(blankSpace.value, effective + extraContentPadding.value); -+ const actualTotalPadding = Math.max(blankSpace.value, effective + extraContentPadding.value) + adjustedInsetCompensation; - - // `onInteractive` owns the native scroll offset. When its final - // keyboardWillHide event arrives, consume the marker so this -@@ -80,8 +85,8 @@ function useChatKeyboard(scrollViewRef, options) { - const maxScroll = Math.max(size.value.height - layout.value.height, 0); - contentOffsetY.value = Math.max(-actualTotalPadding, Math.min(scroll.value, maxScroll)); - } else { -- const maxScroll = Math.max(size.value.height - layout.value.height + actualTotalPadding, 0); -- contentOffsetY.value = Math.max(0, Math.min(scroll.value, maxScroll)); -+ const maxScroll = Math.max(size.value.height - layout.value.height + actualTotalPadding, -adjustedStartInsetCompensation); -+ contentOffsetY.value = Math.max(-adjustedStartInsetCompensation, Math.min(scroll.value, maxScroll)); - } - return; - } -@@ -95,8 +100,8 @@ function useChatKeyboard(scrollViewRef, options) { - const maxScroll = Math.max(size.value.height - layout.value.height, 0); - contentOffsetY.value = Math.max(-actualTotalPadding, Math.min(scroll.value, maxScroll)); - } else { -- const maxScroll = Math.max(size.value.height - layout.value.height + actualTotalPadding, 0); -- contentOffsetY.value = Math.max(0, Math.min(scroll.value, maxScroll)); -+ const maxScroll = Math.max(size.value.height - layout.value.height + actualTotalPadding, -adjustedStartInsetCompensation); -+ contentOffsetY.value = Math.max(-adjustedStartInsetCompensation, Math.min(scroll.value, maxScroll)); - } - return; - } -@@ -122,17 +127,25 @@ function useChatKeyboard(scrollViewRef, options) { - contentOffsetY.value = scroll.value; - return; - } -- contentOffsetY.value = computeIOSContentOffset(relativeScroll, scrollEffective, size.value.height, layout.value.height, inverted, actualTotalPadding); -+ contentOffsetY.value = computeIOSContentOffset(relativeScroll, scrollEffective, size.value.height, layout.value.height, inverted, actualTotalPadding, adjustedStartInsetCompensation); - }, -- onInteractive: () => { -+ onMove: e => { - "worklet"; - -- isInteractiveDismissal.value = true; -+ if (freeze.value) { -+ return; -+ } -+ currentHeight.value = getEffectiveHeight(e.height, targetKeyboardHeight.value, offset); - }, -- onMove: () => { -+ onInteractive: e => { - "worklet"; - -- // iOS doesn't need per-frame updates (contentOffset handles it) -+ isInteractiveDismissal.value = true; -+ -+ if (freeze.value) { -+ return; -+ } -+ currentHeight.value = getEffectiveHeight(e.height, targetKeyboardHeight.value, offset); - }, - onEnd: e => { - "worklet"; -@@ -141,9 +154,10 @@ function useChatKeyboard(scrollViewRef, options) { - return; - } - const effective = getEffectiveHeight(e.height, targetKeyboardHeight.value, offset); -+ currentHeight.value = effective; - padding.value = effective; - } -- }, [inverted, keyboardLiftBehavior, offset, extraContentPadding]); -+ }, [inverted, keyboardLiftBehavior, offset, extraContentPadding, adjustedInsetCompensation, adjustedStartInsetCompensation]); - return { - padding, - currentHeight, -diff --git a/lib/module/components/KeyboardChatScrollView/useExtraContentPadding/index.js b/lib/module/components/KeyboardChatScrollView/useExtraContentPadding/index.js -index 5885415..8b3c4f0 100644 ---- a/lib/module/components/KeyboardChatScrollView/useExtraContentPadding/index.js -+++ b/lib/module/components/KeyboardChatScrollView/useExtraContentPadding/index.js -@@ -25,6 +25,8 @@ function useExtraContentPadding(options) { - extraContentPadding, - keyboardPadding, - blankSpace, -+ adjustedInsetCompensation, -+ adjustedStartInsetCompensation, - scroll, - layout, - size, -@@ -44,8 +46,13 @@ function useExtraContentPadding(options) { - // otherwise the native ScrollView clamps to the old range. - requestAnimationFrame(() => { - // check that view is still mounted and ref is actual -- // otherwise it may lead to a crash -- if (!scrollViewRef()) { -+ // otherwise it may lead to a crash. Reanimated 4.7+ passes the -+ // ref to the UI runtime as a shareable holding the node in `.value`. -+ const node = -+ typeof scrollViewRef === "function" -+ ? scrollViewRef() -+ : scrollViewRef.value; -+ if (!node) { - return; - } - scrollTo(scrollViewRef, 0, target, false); -@@ -64,8 +71,8 @@ function useExtraContentPadding(options) { - } - - // Compute effective delta considering blankSpace floor -- const previousTotal = Math.max(blankSpace.value, keyboardPadding.value + previous); -- const currentTotal = Math.max(blankSpace.value, keyboardPadding.value + current); -+ const previousTotal = Math.max(blankSpace.value, keyboardPadding.value + previous) + adjustedInsetCompensation; -+ const currentTotal = Math.max(blankSpace.value, keyboardPadding.value + current) + adjustedInsetCompensation; - const effectiveDelta = currentTotal - previousTotal; - if (effectiveDelta === 0) { - // blankSpace absorbed the change -@@ -84,11 +91,12 @@ function useExtraContentPadding(options) { - const target = Math.max(scroll.value - effectiveDelta, -currentTotal); - scrollToTarget(target); - } else { -- const maxScroll = Math.max(size.value.height - layout.value.height + currentTotal, 0); -- const target = Math.min(scroll.value + effectiveDelta, maxScroll); -+ const minScroll = -adjustedStartInsetCompensation; -+ const maxScroll = Math.max(size.value.height - layout.value.height + currentTotal, minScroll); -+ const target = Math.max(minScroll, Math.min(scroll.value + effectiveDelta, maxScroll)); - scrollToTarget(target); - } -- }, [inverted, keyboardLiftBehavior]); -+ }, [inverted, keyboardLiftBehavior, adjustedInsetCompensation, adjustedStartInsetCompensation]); - } - export { useExtraContentPadding }; - //# sourceMappingURL=index.js.map -\ No newline at end of file -diff --git a/lib/typescript/components/KeyboardChatScrollView/types.d.ts b/lib/typescript/components/KeyboardChatScrollView/types.d.ts -index eba1514..6597d80 100644 ---- a/lib/typescript/components/KeyboardChatScrollView/types.d.ts -+++ b/lib/typescript/components/KeyboardChatScrollView/types.d.ts -@@ -88,6 +88,10 @@ export type KeyboardChatScrollViewProps = { - * Default is `undefined` (equivalent to `0` — no minimum floor). - */ - blankSpace?: SharedValue; -+ /** Extra bottom inset UIKit adds beyond the raw contentInset (safe area). Offset math only. */ -+ adjustedInsetCompensation?: number; -+ /** Leading inset UIKit adds outside raw contentInset. Offset math only. */ -+ adjustedStartInsetCompensation?: number; - /** - * Fires whenever the effective content inset changes — the static `contentInset` - * prop combined with the dynamic keyboard-driven padding. -diff --git a/lib/typescript/components/KeyboardChatScrollView/useChatKeyboard/helpers.d.ts b/lib/typescript/components/KeyboardChatScrollView/useChatKeyboard/helpers.d.ts -index 2b51cc4..3acec1f 100644 ---- a/lib/typescript/components/KeyboardChatScrollView/useChatKeyboard/helpers.d.ts -+++ b/lib/typescript/components/KeyboardChatScrollView/useChatKeyboard/helpers.d.ts -@@ -129,4 +129,4 @@ export declare const clampedScrollTarget: (offsetBeforeScroll: number, keyboardH - * computeIOSContentOffset(100, 300, 1000, 800, false); // 400 - * ``` - */ --export declare const computeIOSContentOffset: (relativeScroll: number, keyboardHeight: number, contentHeight: number, layoutHeight: number, inverted: boolean, totalPaddingForMaxScroll?: number) => number; -+export declare const computeIOSContentOffset: (relativeScroll: number, keyboardHeight: number, contentHeight: number, layoutHeight: number, inverted: boolean, totalPaddingForMaxScroll?: number, startInsetCompensation?: number) => number; -diff --git a/lib/typescript/components/KeyboardChatScrollView/useChatKeyboard/types.d.ts b/lib/typescript/components/KeyboardChatScrollView/useChatKeyboard/types.d.ts -index aff9b5a..fdd8395 100644 ---- a/lib/typescript/components/KeyboardChatScrollView/useChatKeyboard/types.d.ts -+++ b/lib/typescript/components/KeyboardChatScrollView/useChatKeyboard/types.d.ts -@@ -9,6 +9,10 @@ type UseChatKeyboardOptions = { - blankSpace: SharedValue; - /** Extra content padding shared value — needed on iOS to correctly clamp contentOffset. */ - extraContentPadding: SharedValue; -+ /** Safe-area extra beyond raw contentInset. Offset math only. */ -+ adjustedInsetCompensation: number; -+ /** Leading inset UIKit adds beyond the raw contentInset. Offset math only. */ -+ adjustedStartInsetCompensation: number; - }; - type UseChatKeyboardReturn = { - /** Extra scrollable space (= keyboard height). Used as contentInset on iOS, contentInsetBottom/contentInsetTop on Android. */ -diff --git a/lib/typescript/components/KeyboardChatScrollView/useExtraContentPadding/index.d.ts b/lib/typescript/components/KeyboardChatScrollView/useExtraContentPadding/index.d.ts -index ec73f70..45b3874 100644 ---- a/lib/typescript/components/KeyboardChatScrollView/useExtraContentPadding/index.d.ts -+++ b/lib/typescript/components/KeyboardChatScrollView/useExtraContentPadding/index.d.ts -@@ -8,6 +8,10 @@ type UseExtraContentPaddingOptions = { - keyboardPadding: SharedValue; - /** Minimum inset floor — used to absorb keyboard and extraContentPadding changes. */ - blankSpace: SharedValue; -+ /** Safe-area extra beyond raw contentInset. Offset math only. */ -+ adjustedInsetCompensation: number; -+ /** Leading inset UIKit adds beyond raw contentInset. Offset math only. */ -+ adjustedStartInsetCompensation: number; - /** Current vertical scroll offset. */ - scroll: SharedValue; - /** Visible viewport dimensions. */ -diff --git a/src/components/KeyboardChatScrollView/index.tsx b/src/components/KeyboardChatScrollView/index.tsx -index 554b38c..725d546 100644 ---- a/src/components/KeyboardChatScrollView/index.tsx -+++ b/src/components/KeyboardChatScrollView/index.tsx -@@ -1,7 +1,9 @@ - import React, { forwardRef, useCallback, useMemo } from "react"; --import { StyleSheet } from "react-native"; -+import { Platform, StyleSheet } from "react-native"; - import { - makeMutable, -+ runOnJS, -+ useAnimatedReaction, - useAnimatedRef, - useAnimatedStyle, - useDerivedValue, -@@ -39,9 +41,12 @@ const KeyboardChatScrollView: React.ForwardRefExoticComponent< - offset = 0, - extraContentPadding = ZERO_CONTENT_PADDING, - blankSpace = ZERO_BLANK_SPACE, -+ adjustedInsetCompensation = 0, -+ adjustedStartInsetCompensation = 0, - applyWorkaroundForContentInsetHitTestBug = false, - onLayout: onLayoutProp, - onContentSizeChange: onContentSizeChangeProp, -+ onContentInsetChange, - onEndVisible, - ...rest - }, -@@ -68,6 +73,8 @@ const KeyboardChatScrollView: React.ForwardRefExoticComponent< - offset, - blankSpace, - extraContentPadding, -+ adjustedInsetCompensation, -+ adjustedStartInsetCompensation, - }); - - useExtraContentPadding({ -@@ -75,6 +82,8 @@ const KeyboardChatScrollView: React.ForwardRefExoticComponent< - extraContentPadding, - keyboardPadding: padding, - blankSpace, -+ adjustedInsetCompensation, -+ adjustedStartInsetCompensation, - scroll, - layout, - size, -@@ -112,13 +121,40 @@ const KeyboardChatScrollView: React.ForwardRefExoticComponent< - ), - ); - -- // Scroll indicator inset = keyboard + extraContentPadding (excludes blankSpace). -- // Apps that render into the unsafe area can supply a negative -- // scrollIndicatorInsets adjustment at the application layer. -- const indicatorPadding = useDerivedValue( -- () => padding.value + extraContentPadding.value, -+ // iOS applies the destination contentInset and contentOffset together at -+ // keyboard-animation start. Keep that native target behavior, but report -+ // the presentation height so virtualized-list layout follows the keyboard. -+ const reportedPadding = useDerivedValue(() => -+ Platform.OS === "ios" -+ ? Math.min( -+ layout.value.height, -+ Math.max( -+ blankSpace.value, -+ currentHeight.value + extraContentPadding.value, -+ ), -+ ) -+ : totalPadding.value, - ); - -+ // Mirror the effective bottom padding (keyboard + composer + blank floor) -+ // to the consumer — a virtualized list needs it in its own scroll math or -+ // its end/maintain targets point at the under-the-keyboard resting offset. -+ useAnimatedReaction( -+ () => reportedPadding.value, -+ (current, previous) => { -+ if (onContentInsetChange && current !== previous) { -+ runOnJS(onContentInsetChange)({ bottom: current }); -+ } -+ }, -+ [onContentInsetChange], -+ ); -+ -+ // Scroll indicator inset = keyboard only (excludes extraContentPadding and -+ // blankSpace): with a floating composer the indicator track should run the -+ // full height of the scroll view, behind the composer, like iOS Messages. -+ // The keyboard still lifts it so it never disappears under the keyboard. -+ const indicatorPadding = useDerivedValue(() => padding.value); -+ - const onLayout = useCallback( - (e: LayoutChangeEvent) => { - onLayoutInternal(e); -diff --git a/src/components/KeyboardChatScrollView/types.ts b/src/components/KeyboardChatScrollView/types.ts -index b7c98c8..e1012a8 100644 ---- a/src/components/KeyboardChatScrollView/types.ts -+++ b/src/components/KeyboardChatScrollView/types.ts -@@ -93,6 +93,17 @@ export type KeyboardChatScrollViewProps = { - * Default is `undefined` (equivalent to `0` — no minimum floor). - */ - blankSpace?: SharedValue; -+ /** -+ * Extra bottom inset UIKit adds on top of the raw `contentInset` this -+ * component writes (e.g. the home-indicator safe area under -+ * `contentInsetAdjustmentBehavior="automatic"`). Used ONLY in scroll-offset -+ * math (max scroll / end pinning) — never written into `contentInset`. -+ * -+ * Default is `0`. -+ */ -+ adjustedInsetCompensation?: number; -+ /** Leading inset UIKit adds outside raw contentInset. Offset math only. */ -+ adjustedStartInsetCompensation?: number; - /** - * Fires whenever the effective content inset changes — the static `contentInset` - * prop combined with the dynamic keyboard-driven padding. -diff --git a/src/components/KeyboardChatScrollView/useChatKeyboard/helpers.ts b/src/components/KeyboardChatScrollView/useChatKeyboard/helpers.ts -index 37eacd1..3d77ab2 100644 ---- a/src/components/KeyboardChatScrollView/useChatKeyboard/helpers.ts -+++ b/src/components/KeyboardChatScrollView/useChatKeyboard/helpers.ts -@@ -245,6 +245,7 @@ export const computeIOSContentOffset = ( - layoutHeight: number, - inverted: boolean, - totalPaddingForMaxScroll?: number, -+ startInsetCompensation: number = 0, - ): number => { - "worklet"; - -@@ -262,7 +263,14 @@ export const computeIOSContentOffset = ( - ); - } - -- const maxScroll = Math.max(contentHeight - layoutHeight + paddingForMax, 0); -+ const minScroll = -startInsetCompensation; -+ const maxScroll = Math.max( -+ contentHeight - layoutHeight + paddingForMax, -+ minScroll, -+ ); - -- return Math.min(Math.max(keyboardHeight + relativeScroll, 0), maxScroll); -+ return Math.min( -+ Math.max(keyboardHeight + relativeScroll, minScroll), -+ maxScroll, -+ ); - }; -diff --git a/src/components/KeyboardChatScrollView/useChatKeyboard/index.ios.ts b/src/components/KeyboardChatScrollView/useChatKeyboard/index.ios.ts -index 129a74b..168ce1a 100644 ---- a/src/components/KeyboardChatScrollView/useChatKeyboard/index.ios.ts -+++ b/src/components/KeyboardChatScrollView/useChatKeyboard/index.ios.ts -@@ -43,6 +43,8 @@ function useChatKeyboard( - offset, - blankSpace, - extraContentPadding, -+ adjustedInsetCompensation, -+ adjustedStartInsetCompensation, - } = options; - - const padding = useSharedValue(0); -@@ -80,6 +82,10 @@ function useChatKeyboard( - offset, - ); - -+ if (e.duration === 0) { -+ currentHeight.value = effective; -+ } -+ - const atEnd = isScrollAtEnd( - scroll.value, - layout.value.height, -@@ -105,10 +111,12 @@ function useChatKeyboard( - effective, - minimumPaddingAbsorbed, - ); -- const actualTotalPadding = Math.max( -- blankSpace.value, -- effective + extraContentPadding.value, -- ); -+ // UIKit adds adjustedInsetCompensation (safe area) on top of the raw -+ // inset; include it here so end/max-scroll targets match the real -+ // resting offsets. -+ const actualTotalPadding = -+ Math.max(blankSpace.value, effective + extraContentPadding.value) + -+ adjustedInsetCompensation; - - // `onInteractive` owns the native scroll offset. When its final - // keyboardWillHide event arrives, consume the marker so this -@@ -142,11 +150,11 @@ function useChatKeyboard( - } else { - const maxScroll = Math.max( - size.value.height - layout.value.height + actualTotalPadding, -- 0, -+ -adjustedStartInsetCompensation, - ); - - contentOffsetY.value = Math.max( -- 0, -+ -adjustedStartInsetCompensation, - Math.min(scroll.value, maxScroll), - ); - } -@@ -177,11 +185,11 @@ function useChatKeyboard( - } else { - const maxScroll = Math.max( - size.value.height - layout.value.height + actualTotalPadding, -- 0, -+ -adjustedStartInsetCompensation, - ); - - contentOffsetY.value = Math.max( -- 0, -+ -adjustedStartInsetCompensation, - Math.min(scroll.value, maxScroll), - ); - } -@@ -231,17 +239,36 @@ function useChatKeyboard( - layout.value.height, - inverted, - actualTotalPadding, -+ adjustedStartInsetCompensation, - ); - }, -- onInteractive: () => { -+ onMove: (e) => { - "worklet"; - -- isInteractiveDismissal.value = true; -+ if (freeze.value) { -+ return; -+ } -+ -+ currentHeight.value = getEffectiveHeight( -+ e.height, -+ targetKeyboardHeight.value, -+ offset, -+ ); - }, -- onMove: () => { -+ onInteractive: (e) => { - "worklet"; - -- // iOS doesn't need per-frame updates (contentOffset handles it) -+ isInteractiveDismissal.value = true; -+ -+ if (freeze.value) { -+ return; -+ } -+ -+ currentHeight.value = getEffectiveHeight( -+ e.height, -+ targetKeyboardHeight.value, -+ offset, -+ ); - }, - onEnd: (e) => { - "worklet"; -@@ -256,10 +283,18 @@ function useChatKeyboard( - offset, - ); - -+ currentHeight.value = effective; - padding.value = effective; - }, - }, -- [inverted, keyboardLiftBehavior, offset, extraContentPadding], -+ [ -+ inverted, -+ keyboardLiftBehavior, -+ offset, -+ extraContentPadding, -+ adjustedInsetCompensation, -+ adjustedStartInsetCompensation, -+ ], - ); - - return { -diff --git a/src/components/KeyboardChatScrollView/useChatKeyboard/types.ts b/src/components/KeyboardChatScrollView/useChatKeyboard/types.ts -index 02abf5c..4ee0ddd 100644 ---- a/src/components/KeyboardChatScrollView/useChatKeyboard/types.ts -+++ b/src/components/KeyboardChatScrollView/useChatKeyboard/types.ts -@@ -11,6 +11,10 @@ type UseChatKeyboardOptions = { - blankSpace: SharedValue; - /** Extra content padding shared value — needed on iOS to correctly clamp contentOffset. */ - extraContentPadding: SharedValue; -+ /** Extra bottom inset UIKit adds beyond the raw contentInset (safe area). Offset math only. */ -+ adjustedInsetCompensation: number; -+ /** Leading inset UIKit adds beyond the raw contentInset. Offset math only. */ -+ adjustedStartInsetCompensation: number; - }; - - type UseChatKeyboardReturn = { -diff --git a/src/components/KeyboardChatScrollView/useExtraContentPadding/index.ts b/src/components/KeyboardChatScrollView/useExtraContentPadding/index.ts -index c9c93f3..02f95c0 100644 ---- a/src/components/KeyboardChatScrollView/useExtraContentPadding/index.ts -+++ b/src/components/KeyboardChatScrollView/useExtraContentPadding/index.ts -@@ -16,6 +16,10 @@ type UseExtraContentPaddingOptions = { - keyboardPadding: SharedValue; - /** Minimum inset floor — used to absorb keyboard and extraContentPadding changes. */ - blankSpace: SharedValue; -+ /** Extra bottom inset UIKit adds beyond the raw contentInset (safe area). Offset math only. */ -+ adjustedInsetCompensation: number; -+ /** Leading inset UIKit adds beyond raw contentInset. Offset math only. */ -+ adjustedStartInsetCompensation: number; - /** Current vertical scroll offset. */ - scroll: SharedValue; - /** Visible viewport dimensions. */ -@@ -51,6 +55,8 @@ function useExtraContentPadding(options: UseExtraContentPaddingOptions): void { - extraContentPadding, - keyboardPadding, - blankSpace, -+ adjustedInsetCompensation, -+ adjustedStartInsetCompensation, - scroll, - layout, - size, -@@ -72,8 +78,13 @@ function useExtraContentPadding(options: UseExtraContentPaddingOptions): void { - // otherwise the native ScrollView clamps to the old range. - requestAnimationFrame(() => { - // check that view is still mounted and ref is actual -- // otherwise it may lead to a crash -- if (!scrollViewRef()) { -+ // otherwise it may lead to a crash. Reanimated 4.7+ passes the -+ // ref to the UI runtime as a shareable holding the node in `.value`. -+ const node = -+ typeof scrollViewRef === "function" -+ ? scrollViewRef() -+ : (scrollViewRef as unknown as { value: unknown }).value; -+ if (!node) { - return; - } - scrollTo(scrollViewRef, 0, target, false); -@@ -99,14 +110,12 @@ function useExtraContentPadding(options: UseExtraContentPaddingOptions): void { - } - - // Compute effective delta considering blankSpace floor -- const previousTotal = Math.max( -- blankSpace.value, -- keyboardPadding.value + previous, -- ); -- const currentTotal = Math.max( -- blankSpace.value, -- keyboardPadding.value + current, -- ); -+ const previousTotal = -+ Math.max(blankSpace.value, keyboardPadding.value + previous) + -+ adjustedInsetCompensation; -+ const currentTotal = -+ Math.max(blankSpace.value, keyboardPadding.value + current) + -+ adjustedInsetCompensation; - const effectiveDelta = currentTotal - previousTotal; - - if (effectiveDelta === 0) { -@@ -139,16 +148,25 @@ function useExtraContentPadding(options: UseExtraContentPaddingOptions): void { - - scrollToTarget(target); - } else { -+ const minScroll = -adjustedStartInsetCompensation; - const maxScroll = Math.max( - size.value.height - layout.value.height + currentTotal, -- 0, -+ minScroll, -+ ); -+ const target = Math.max( -+ minScroll, -+ Math.min(scroll.value + effectiveDelta, maxScroll), - ); -- const target = Math.min(scroll.value + effectiveDelta, maxScroll); - - scrollToTarget(target); - } - }, -- [inverted, keyboardLiftBehavior], -+ [ -+ inverted, -+ keyboardLiftBehavior, -+ adjustedInsetCompensation, -+ adjustedStartInsetCompensation, -+ ], - ); - } - diff --git a/patches/react-native-keyboard-controller@1.22.6.patch b/patches/react-native-keyboard-controller@1.22.6.patch new file mode 100644 index 0000000000000000000000000000000000000000..8b9170240d4e58ae37bf4231d6c891aa96039ba2 --- /dev/null +++ b/patches/react-native-keyboard-controller@1.22.6.patch @@ -0,0 +1,952 @@ +diff --git a/lib/commonjs/components/KeyboardChatScrollView/index.js b/lib/commonjs/components/KeyboardChatScrollView/index.js +index 0b78ff3..98b3b6c 100644 +--- a/lib/commonjs/components/KeyboardChatScrollView/index.js ++++ b/lib/commonjs/components/KeyboardChatScrollView/index.js +@@ -28,9 +28,12 @@ const KeyboardChatScrollView = /*#__PURE__*/(0, _react.forwardRef)(({ + offset = 0, + extraContentPadding = ZERO_CONTENT_PADDING, + blankSpace = ZERO_BLANK_SPACE, ++ adjustedInsetCompensation = 0, ++ adjustedStartInsetCompensation = 0, + applyWorkaroundForContentInsetHitTestBug = false, + onLayout: onLayoutProp, + onContentSizeChange: onContentSizeChangeProp, ++ onContentInsetChange, + onEndVisible, + ...rest + }, ref) => { +@@ -54,6 +57,8 @@ const KeyboardChatScrollView = /*#__PURE__*/(0, _react.forwardRef)(({ + offset, + blankSpace, + extraContentPadding, ++ adjustedInsetCompensation, ++ adjustedStartInsetCompensation, + initialContentOffsetY: (_rest$contentOffset = rest.contentOffset) === null || _rest$contentOffset === void 0 ? void 0 : _rest$contentOffset.y + }); + (0, _useExtraContentPadding.useExtraContentPadding)({ +@@ -61,6 +66,8 @@ const KeyboardChatScrollView = /*#__PURE__*/(0, _react.forwardRef)(({ + extraContentPadding, + keyboardPadding: padding, + blankSpace, ++ adjustedInsetCompensation, ++ adjustedStartInsetCompensation, + scroll, + layout, + size, +@@ -91,10 +98,26 @@ const KeyboardChatScrollView = /*#__PURE__*/(0, _react.forwardRef)(({ + // a bug for you, please open an issue. + const totalPadding = (0, _reanimated.useDerivedValue)(() => Math.min(layout.value.height, Math.max(blankSpace.value, padding.value + extraContentPadding.value))); + ++ // iOS applies the destination contentInset and contentOffset together at ++ // keyboard-animation start. Keep that native target behavior, but report ++ // the presentation height so virtualized-list layout follows the keyboard. ++ const reportedPadding = (0, _reanimated.useDerivedValue)(() => _reactNative.Platform.OS === "ios" ? Math.min(layout.value.height, Math.max(blankSpace.value, currentHeight.value + extraContentPadding.value)) : totalPadding.value); ++ ++ // Mirror the effective bottom padding (keyboard + composer + blank floor) ++ // to the consumer - a virtualized list needs it in its own scroll math or ++ // its end/maintain targets point at the under-the-keyboard resting offset. ++ (0, _reanimated.useAnimatedReaction)(() => reportedPadding.value, (current, previous) => { ++ if (onContentInsetChange && current !== previous) { ++ (0, _reactNativeReanimated.runOnJS)(onContentInsetChange)({ ++ bottom: current ++ }); ++ } ++ }, [onContentInsetChange]); ++ + // Scroll indicator inset = keyboard + extraContentPadding (excludes blankSpace). + // Apps that render into the unsafe area can supply a negative + // scrollIndicatorInsets adjustment at the application layer. +- const indicatorPadding = (0, _reanimated.useDerivedValue)(() => padding.value + extraContentPadding.value); ++ const indicatorPadding = (0, _reanimated.useDerivedValue)(() => padding.value); + const onLayout = (0, _react.useCallback)(e => { + onLayoutInternal(e); + onLayoutProp === null || onLayoutProp === void 0 || onLayoutProp(e); +diff --git a/lib/commonjs/components/KeyboardChatScrollView/useChatKeyboard/helpers.js b/lib/commonjs/components/KeyboardChatScrollView/useChatKeyboard/helpers.js +index 4d33b3f..6a787d3 100644 +--- a/lib/commonjs/components/KeyboardChatScrollView/useChatKeyboard/helpers.js ++++ b/lib/commonjs/components/KeyboardChatScrollView/useChatKeyboard/helpers.js +@@ -206,7 +206,7 @@ const clampedScrollTarget = (offsetBeforeScroll, keyboardHeight, contentHeight, + * ``` + */ + exports.clampedScrollTarget = clampedScrollTarget; +-const computeIOSContentOffset = (relativeScroll, keyboardHeight, contentHeight, layoutHeight, inverted, totalPaddingForMaxScroll) => { ++const computeIOSContentOffset = (relativeScroll, keyboardHeight, contentHeight, layoutHeight, inverted, totalPaddingForMaxScroll, startInsetCompensation = 0) => { + "worklet"; + + const paddingForMax = totalPaddingForMaxScroll !== undefined ? totalPaddingForMaxScroll : keyboardHeight; +@@ -214,8 +214,9 @@ const computeIOSContentOffset = (relativeScroll, keyboardHeight, contentHeight, + const maxScroll = Math.max(contentHeight - layoutHeight, 0); + return Math.max(Math.min(relativeScroll - keyboardHeight, maxScroll), -paddingForMax); + } +- const maxScroll = Math.max(contentHeight - layoutHeight + paddingForMax, 0); +- return Math.min(Math.max(keyboardHeight + relativeScroll, 0), maxScroll); ++ const minScroll = -startInsetCompensation; ++ const maxScroll = Math.max(contentHeight - layoutHeight + paddingForMax, minScroll); ++ return Math.min(Math.max(keyboardHeight + relativeScroll, minScroll), maxScroll); + }; + exports.computeIOSContentOffset = computeIOSContentOffset; + //# sourceMappingURL=helpers.js.map +\ No newline at end of file +diff --git a/lib/commonjs/components/KeyboardChatScrollView/useChatKeyboard/index.ios.js b/lib/commonjs/components/KeyboardChatScrollView/useChatKeyboard/index.ios.js +index 6481228..0dc5dd1 100644 +--- a/lib/commonjs/components/KeyboardChatScrollView/useChatKeyboard/index.ios.js ++++ b/lib/commonjs/components/KeyboardChatScrollView/useChatKeyboard/index.ios.js +@@ -32,7 +32,9 @@ function useChatKeyboard(scrollViewRef, options) { + freeze, + offset, + blankSpace, +- extraContentPadding ++ extraContentPadding, ++ adjustedInsetCompensation, ++ adjustedStartInsetCompensation + } = options; + const padding = (0, _reactNativeReanimated.useSharedValue)(0); + const currentHeight = (0, _reactNativeReanimated.useSharedValue)(0); +@@ -59,6 +61,9 @@ function useChatKeyboard(scrollViewRef, options) { + targetKeyboardHeight.value = e.height; + } + const effective = (0, _helpers.getEffectiveHeight)(e.height, targetKeyboardHeight.value, offset); ++ if (e.duration === 0) { ++ currentHeight.value = effective; ++ } + const atEnd = (0, _helpers.isScrollAtEnd)(scroll.value, layout.value.height, size.value.height, inverted); + + // Scale minimum padding absorption by how much of it is visible. +@@ -67,7 +72,7 @@ function useChatKeyboard(scrollViewRef, options) { + const visiblePadding = visibleFraction * blankSpace.value; + const minimumPaddingAbsorbed = Math.max(0, visiblePadding - extraContentPadding.value); + const scrollEffective = (0, _helpers.getScrollEffective)(effective, minimumPaddingAbsorbed); +- const actualTotalPadding = Math.max(blankSpace.value, effective + extraContentPadding.value); ++ const actualTotalPadding = Math.max(blankSpace.value, effective + extraContentPadding.value) + adjustedInsetCompensation; + + // `onInteractive` owns the native scroll offset. When its final + // keyboardWillHide event arrives, consume the marker so this +@@ -87,8 +92,8 @@ function useChatKeyboard(scrollViewRef, options) { + const maxScroll = Math.max(size.value.height - layout.value.height, 0); + contentOffsetY.value = Math.max(-actualTotalPadding, Math.min(scroll.value, maxScroll)); + } else { +- const maxScroll = Math.max(size.value.height - layout.value.height + actualTotalPadding, 0); +- contentOffsetY.value = Math.max(0, Math.min(scroll.value, maxScroll)); ++ const maxScroll = Math.max(size.value.height - layout.value.height + actualTotalPadding, -adjustedStartInsetCompensation); ++ contentOffsetY.value = Math.max(-adjustedStartInsetCompensation, Math.min(scroll.value, maxScroll)); + } + return; + } +@@ -102,8 +107,8 @@ function useChatKeyboard(scrollViewRef, options) { + const maxScroll = Math.max(size.value.height - layout.value.height, 0); + contentOffsetY.value = Math.max(-actualTotalPadding, Math.min(scroll.value, maxScroll)); + } else { +- const maxScroll = Math.max(size.value.height - layout.value.height + actualTotalPadding, 0); +- contentOffsetY.value = Math.max(0, Math.min(scroll.value, maxScroll)); ++ const maxScroll = Math.max(size.value.height - layout.value.height + actualTotalPadding, -adjustedStartInsetCompensation); ++ contentOffsetY.value = Math.max(-adjustedStartInsetCompensation, Math.min(scroll.value, maxScroll)); + } + return; + } +@@ -129,17 +134,25 @@ function useChatKeyboard(scrollViewRef, options) { + contentOffsetY.value = scroll.value; + return; + } +- contentOffsetY.value = (0, _helpers.computeIOSContentOffset)(relativeScroll, scrollEffective, size.value.height, layout.value.height, inverted, actualTotalPadding); ++ contentOffsetY.value = (0, _helpers.computeIOSContentOffset)(relativeScroll, scrollEffective, size.value.height, layout.value.height, inverted, actualTotalPadding, adjustedStartInsetCompensation); + }, +- onInteractive: () => { ++ onMove: e => { + "worklet"; + +- isInteractiveDismissal.value = true; ++ if (freeze.value) { ++ return; ++ } ++ currentHeight.value = (0, _helpers.getEffectiveHeight)(e.height, targetKeyboardHeight.value, offset); + }, +- onMove: () => { ++ onInteractive: e => { + "worklet"; + +- // iOS doesn't need per-frame updates (contentOffset handles it) ++ isInteractiveDismissal.value = true; ++ ++ if (freeze.value) { ++ return; ++ } ++ currentHeight.value = (0, _helpers.getEffectiveHeight)(e.height, targetKeyboardHeight.value, offset); + }, + onEnd: e => { + "worklet"; +@@ -148,9 +161,10 @@ function useChatKeyboard(scrollViewRef, options) { + return; + } + const effective = (0, _helpers.getEffectiveHeight)(e.height, targetKeyboardHeight.value, offset); ++ currentHeight.value = effective; + padding.value = effective; + } +- }, [inverted, keyboardLiftBehavior, offset, extraContentPadding]); ++ }, [inverted, keyboardLiftBehavior, offset, extraContentPadding, adjustedInsetCompensation, adjustedStartInsetCompensation]); + return { + padding, + currentHeight, +diff --git a/lib/commonjs/components/KeyboardChatScrollView/useExtraContentPadding/index.js b/lib/commonjs/components/KeyboardChatScrollView/useExtraContentPadding/index.js +index 0215bef..5307446 100644 +--- a/lib/commonjs/components/KeyboardChatScrollView/useExtraContentPadding/index.js ++++ b/lib/commonjs/components/KeyboardChatScrollView/useExtraContentPadding/index.js +@@ -32,6 +32,8 @@ function useExtraContentPadding(options) { + extraContentPadding, + keyboardPadding, + blankSpace, ++ adjustedInsetCompensation, ++ adjustedStartInsetCompensation, + scroll, + layout, + size, +@@ -51,8 +53,13 @@ function useExtraContentPadding(options) { + // otherwise the native ScrollView clamps to the old range. + requestAnimationFrame(() => { + // check that view is still mounted and ref is actual +- // otherwise it may lead to a crash +- if (!scrollViewRef()) { ++ // otherwise it may lead to a crash. Reanimated 4.7+ passes the ++ // ref to the UI runtime as a shareable holding the node in `.value`. ++ const node = ++ typeof scrollViewRef === "function" ++ ? scrollViewRef() ++ : scrollViewRef.value; ++ if (!node) { + return; + } + (0, _reactNativeReanimated.scrollTo)(scrollViewRef, 0, target, false); +@@ -71,8 +78,8 @@ function useExtraContentPadding(options) { + } + + // Compute effective delta considering blankSpace floor +- const previousTotal = Math.max(blankSpace.value, keyboardPadding.value + previous); +- const currentTotal = Math.max(blankSpace.value, keyboardPadding.value + current); ++ const previousTotal = Math.max(blankSpace.value, keyboardPadding.value + previous) + adjustedInsetCompensation; ++ const currentTotal = Math.max(blankSpace.value, keyboardPadding.value + current) + adjustedInsetCompensation; + const effectiveDelta = currentTotal - previousTotal; + if (effectiveDelta === 0) { + // blankSpace absorbed the change +@@ -91,13 +98,11 @@ function useExtraContentPadding(options) { + const target = Math.max(scroll.value - effectiveDelta, -currentTotal); + scrollToTarget(target); + } else { +- const maxScroll = Math.max(size.value.height - layout.value.height + currentTotal, 0); +- // Clamp at 0 as well: when the content is shorter than the viewport a +- // shrinking padding makes `effectiveDelta` negative while `scroll.value` +- // is already 0, so the target would go below the top of the content. +- const target = Math.max(Math.min(scroll.value + effectiveDelta, maxScroll), 0); ++ const minScroll = -adjustedStartInsetCompensation; ++ const maxScroll = Math.max(size.value.height - layout.value.height + currentTotal, minScroll); ++ const target = Math.max(minScroll, Math.min(scroll.value + effectiveDelta, maxScroll)); + scrollToTarget(target); + } +- }, [inverted, keyboardLiftBehavior]); ++ }, [inverted, keyboardLiftBehavior, adjustedInsetCompensation, adjustedStartInsetCompensation]); + } + //# sourceMappingURL=index.js.map +\ No newline at end of file +diff --git a/lib/module/components/KeyboardChatScrollView/index.js b/lib/module/components/KeyboardChatScrollView/index.js +index e0c8391..8df802a 100644 +--- a/lib/module/components/KeyboardChatScrollView/index.js ++++ b/lib/module/components/KeyboardChatScrollView/index.js +@@ -1,9 +1,9 @@ + function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); } + import React, { forwardRef, useCallback, useMemo } from "react"; +-import { StyleSheet } from "react-native"; +-import { makeMutable, useAnimatedRef } from "react-native-reanimated"; ++import { Platform, StyleSheet } from "react-native"; ++import { makeMutable, runOnJS, useAnimatedRef } from "react-native-reanimated"; + import Reanimated from "react-native-reanimated"; +-import { useAnimatedStyle, useDerivedValue } from "../../reanimated"; ++import { useAnimatedReaction, useAnimatedStyle, useDerivedValue } from "../../reanimated"; + import useCombinedRef from "../hooks/useCombinedRef"; + import ScrollViewWithBottomPadding from "../ScrollViewWithBottomPadding"; + import { useChatKeyboard } from "./useChatKeyboard"; +@@ -21,9 +21,12 @@ const KeyboardChatScrollView = /*#__PURE__*/forwardRef(({ + offset = 0, + extraContentPadding = ZERO_CONTENT_PADDING, + blankSpace = ZERO_BLANK_SPACE, ++ adjustedInsetCompensation = 0, ++ adjustedStartInsetCompensation = 0, + applyWorkaroundForContentInsetHitTestBug = false, + onLayout: onLayoutProp, + onContentSizeChange: onContentSizeChangeProp, ++ onContentInsetChange, + onEndVisible, + ...rest + }, ref) => { +@@ -47,6 +50,8 @@ const KeyboardChatScrollView = /*#__PURE__*/forwardRef(({ + offset, + blankSpace, + extraContentPadding, ++ adjustedInsetCompensation, ++ adjustedStartInsetCompensation, + initialContentOffsetY: (_rest$contentOffset = rest.contentOffset) === null || _rest$contentOffset === void 0 ? void 0 : _rest$contentOffset.y + }); + useExtraContentPadding({ +@@ -54,6 +59,8 @@ const KeyboardChatScrollView = /*#__PURE__*/forwardRef(({ + extraContentPadding, + keyboardPadding: padding, + blankSpace, ++ adjustedInsetCompensation, ++ adjustedStartInsetCompensation, + scroll, + layout, + size, +@@ -84,10 +91,26 @@ const KeyboardChatScrollView = /*#__PURE__*/forwardRef(({ + // a bug for you, please open an issue. + const totalPadding = useDerivedValue(() => Math.min(layout.value.height, Math.max(blankSpace.value, padding.value + extraContentPadding.value))); + ++ // iOS applies the destination contentInset and contentOffset together at ++ // keyboard-animation start. Keep that native target behavior, but report ++ // the presentation height so virtualized-list layout follows the keyboard. ++ const reportedPadding = useDerivedValue(() => Platform.OS === "ios" ? Math.min(layout.value.height, Math.max(blankSpace.value, currentHeight.value + extraContentPadding.value)) : totalPadding.value); ++ ++ // Mirror the effective bottom padding (keyboard + composer + blank floor) ++ // to the consumer - a virtualized list needs it in its own scroll math or ++ // its end/maintain targets point at the under-the-keyboard resting offset. ++ useAnimatedReaction(() => reportedPadding.value, (current, previous) => { ++ if (onContentInsetChange && current !== previous) { ++ runOnJS(onContentInsetChange)({ ++ bottom: current ++ }); ++ } ++ }, [onContentInsetChange]); ++ + // Scroll indicator inset = keyboard + extraContentPadding (excludes blankSpace). + // Apps that render into the unsafe area can supply a negative + // scrollIndicatorInsets adjustment at the application layer. +- const indicatorPadding = useDerivedValue(() => padding.value + extraContentPadding.value); ++ const indicatorPadding = useDerivedValue(() => padding.value); + const onLayout = useCallback(e => { + onLayoutInternal(e); + onLayoutProp === null || onLayoutProp === void 0 || onLayoutProp(e); +diff --git a/lib/module/components/KeyboardChatScrollView/useChatKeyboard/helpers.js b/lib/module/components/KeyboardChatScrollView/useChatKeyboard/helpers.js +index 295e221..2f7b87e 100644 +--- a/lib/module/components/KeyboardChatScrollView/useChatKeyboard/helpers.js ++++ b/lib/module/components/KeyboardChatScrollView/useChatKeyboard/helpers.js +@@ -193,7 +193,7 @@ export const clampedScrollTarget = (offsetBeforeScroll, keyboardHeight, contentH + * computeIOSContentOffset(100, 300, 1000, 800, false); // 400 + * ``` + */ +-export const computeIOSContentOffset = (relativeScroll, keyboardHeight, contentHeight, layoutHeight, inverted, totalPaddingForMaxScroll) => { ++export const computeIOSContentOffset = (relativeScroll, keyboardHeight, contentHeight, layoutHeight, inverted, totalPaddingForMaxScroll, startInsetCompensation = 0) => { + "worklet"; + + const paddingForMax = totalPaddingForMaxScroll !== undefined ? totalPaddingForMaxScroll : keyboardHeight; +@@ -201,7 +201,8 @@ export const computeIOSContentOffset = (relativeScroll, keyboardHeight, contentH + const maxScroll = Math.max(contentHeight - layoutHeight, 0); + return Math.max(Math.min(relativeScroll - keyboardHeight, maxScroll), -paddingForMax); + } +- const maxScroll = Math.max(contentHeight - layoutHeight + paddingForMax, 0); +- return Math.min(Math.max(keyboardHeight + relativeScroll, 0), maxScroll); ++ const minScroll = -startInsetCompensation; ++ const maxScroll = Math.max(contentHeight - layoutHeight + paddingForMax, minScroll); ++ return Math.min(Math.max(keyboardHeight + relativeScroll, minScroll), maxScroll); + }; + //# sourceMappingURL=helpers.js.map +\ No newline at end of file +diff --git a/lib/module/components/KeyboardChatScrollView/useChatKeyboard/index.ios.js b/lib/module/components/KeyboardChatScrollView/useChatKeyboard/index.ios.js +index 043b973..df5a408 100644 +--- a/lib/module/components/KeyboardChatScrollView/useChatKeyboard/index.ios.js ++++ b/lib/module/components/KeyboardChatScrollView/useChatKeyboard/index.ios.js +@@ -25,7 +25,9 @@ function useChatKeyboard(scrollViewRef, options) { + freeze, + offset, + blankSpace, +- extraContentPadding ++ extraContentPadding, ++ adjustedInsetCompensation, ++ adjustedStartInsetCompensation + } = options; + const padding = useSharedValue(0); + const currentHeight = useSharedValue(0); +@@ -52,6 +54,9 @@ function useChatKeyboard(scrollViewRef, options) { + targetKeyboardHeight.value = e.height; + } + const effective = getEffectiveHeight(e.height, targetKeyboardHeight.value, offset); ++ if (e.duration === 0) { ++ currentHeight.value = effective; ++ } + const atEnd = isScrollAtEnd(scroll.value, layout.value.height, size.value.height, inverted); + + // Scale minimum padding absorption by how much of it is visible. +@@ -60,7 +65,7 @@ function useChatKeyboard(scrollViewRef, options) { + const visiblePadding = visibleFraction * blankSpace.value; + const minimumPaddingAbsorbed = Math.max(0, visiblePadding - extraContentPadding.value); + const scrollEffective = getScrollEffective(effective, minimumPaddingAbsorbed); +- const actualTotalPadding = Math.max(blankSpace.value, effective + extraContentPadding.value); ++ const actualTotalPadding = Math.max(blankSpace.value, effective + extraContentPadding.value) + adjustedInsetCompensation; + + // `onInteractive` owns the native scroll offset. When its final + // keyboardWillHide event arrives, consume the marker so this +@@ -80,8 +85,8 @@ function useChatKeyboard(scrollViewRef, options) { + const maxScroll = Math.max(size.value.height - layout.value.height, 0); + contentOffsetY.value = Math.max(-actualTotalPadding, Math.min(scroll.value, maxScroll)); + } else { +- const maxScroll = Math.max(size.value.height - layout.value.height + actualTotalPadding, 0); +- contentOffsetY.value = Math.max(0, Math.min(scroll.value, maxScroll)); ++ const maxScroll = Math.max(size.value.height - layout.value.height + actualTotalPadding, -adjustedStartInsetCompensation); ++ contentOffsetY.value = Math.max(-adjustedStartInsetCompensation, Math.min(scroll.value, maxScroll)); + } + return; + } +@@ -95,8 +100,8 @@ function useChatKeyboard(scrollViewRef, options) { + const maxScroll = Math.max(size.value.height - layout.value.height, 0); + contentOffsetY.value = Math.max(-actualTotalPadding, Math.min(scroll.value, maxScroll)); + } else { +- const maxScroll = Math.max(size.value.height - layout.value.height + actualTotalPadding, 0); +- contentOffsetY.value = Math.max(0, Math.min(scroll.value, maxScroll)); ++ const maxScroll = Math.max(size.value.height - layout.value.height + actualTotalPadding, -adjustedStartInsetCompensation); ++ contentOffsetY.value = Math.max(-adjustedStartInsetCompensation, Math.min(scroll.value, maxScroll)); + } + return; + } +@@ -122,17 +127,25 @@ function useChatKeyboard(scrollViewRef, options) { + contentOffsetY.value = scroll.value; + return; + } +- contentOffsetY.value = computeIOSContentOffset(relativeScroll, scrollEffective, size.value.height, layout.value.height, inverted, actualTotalPadding); ++ contentOffsetY.value = computeIOSContentOffset(relativeScroll, scrollEffective, size.value.height, layout.value.height, inverted, actualTotalPadding, adjustedStartInsetCompensation); + }, +- onInteractive: () => { ++ onMove: e => { + "worklet"; + +- isInteractiveDismissal.value = true; ++ if (freeze.value) { ++ return; ++ } ++ currentHeight.value = getEffectiveHeight(e.height, targetKeyboardHeight.value, offset); + }, +- onMove: () => { ++ onInteractive: e => { + "worklet"; + +- // iOS doesn't need per-frame updates (contentOffset handles it) ++ isInteractiveDismissal.value = true; ++ ++ if (freeze.value) { ++ return; ++ } ++ currentHeight.value = getEffectiveHeight(e.height, targetKeyboardHeight.value, offset); + }, + onEnd: e => { + "worklet"; +@@ -141,9 +154,10 @@ function useChatKeyboard(scrollViewRef, options) { + return; + } + const effective = getEffectiveHeight(e.height, targetKeyboardHeight.value, offset); ++ currentHeight.value = effective; + padding.value = effective; + } +- }, [inverted, keyboardLiftBehavior, offset, extraContentPadding]); ++ }, [inverted, keyboardLiftBehavior, offset, extraContentPadding, adjustedInsetCompensation, adjustedStartInsetCompensation]); + return { + padding, + currentHeight, +diff --git a/lib/module/components/KeyboardChatScrollView/useExtraContentPadding/index.js b/lib/module/components/KeyboardChatScrollView/useExtraContentPadding/index.js +index 5a9b91d..a11bfdb 100644 +--- a/lib/module/components/KeyboardChatScrollView/useExtraContentPadding/index.js ++++ b/lib/module/components/KeyboardChatScrollView/useExtraContentPadding/index.js +@@ -26,6 +26,8 @@ function useExtraContentPadding(options) { + extraContentPadding, + keyboardPadding, + blankSpace, ++ adjustedInsetCompensation, ++ adjustedStartInsetCompensation, + scroll, + layout, + size, +@@ -45,8 +47,13 @@ function useExtraContentPadding(options) { + // otherwise the native ScrollView clamps to the old range. + requestAnimationFrame(() => { + // check that view is still mounted and ref is actual +- // otherwise it may lead to a crash +- if (!scrollViewRef()) { ++ // otherwise it may lead to a crash. Reanimated 4.7+ passes the ++ // ref to the UI runtime as a shareable holding the node in `.value`. ++ const node = ++ typeof scrollViewRef === "function" ++ ? scrollViewRef() ++ : scrollViewRef.value; ++ if (!node) { + return; + } + scrollTo(scrollViewRef, 0, target, false); +@@ -65,8 +72,8 @@ function useExtraContentPadding(options) { + } + + // Compute effective delta considering blankSpace floor +- const previousTotal = Math.max(blankSpace.value, keyboardPadding.value + previous); +- const currentTotal = Math.max(blankSpace.value, keyboardPadding.value + current); ++ const previousTotal = Math.max(blankSpace.value, keyboardPadding.value + previous) + adjustedInsetCompensation; ++ const currentTotal = Math.max(blankSpace.value, keyboardPadding.value + current) + adjustedInsetCompensation; + const effectiveDelta = currentTotal - previousTotal; + if (effectiveDelta === 0) { + // blankSpace absorbed the change +@@ -85,14 +92,12 @@ function useExtraContentPadding(options) { + const target = Math.max(scroll.value - effectiveDelta, -currentTotal); + scrollToTarget(target); + } else { +- const maxScroll = Math.max(size.value.height - layout.value.height + currentTotal, 0); +- // Clamp at 0 as well: when the content is shorter than the viewport a +- // shrinking padding makes `effectiveDelta` negative while `scroll.value` +- // is already 0, so the target would go below the top of the content. +- const target = Math.max(Math.min(scroll.value + effectiveDelta, maxScroll), 0); ++ const minScroll = -adjustedStartInsetCompensation; ++ const maxScroll = Math.max(size.value.height - layout.value.height + currentTotal, minScroll); ++ const target = Math.max(minScroll, Math.min(scroll.value + effectiveDelta, maxScroll)); + scrollToTarget(target); + } +- }, [inverted, keyboardLiftBehavior]); ++ }, [inverted, keyboardLiftBehavior, adjustedInsetCompensation, adjustedStartInsetCompensation]); + } + export { useExtraContentPadding }; + //# sourceMappingURL=index.js.map +\ No newline at end of file +diff --git a/lib/typescript/components/KeyboardChatScrollView/types.d.ts b/lib/typescript/components/KeyboardChatScrollView/types.d.ts +index eba1514..6597d80 100644 +--- a/lib/typescript/components/KeyboardChatScrollView/types.d.ts ++++ b/lib/typescript/components/KeyboardChatScrollView/types.d.ts +@@ -88,6 +88,10 @@ export type KeyboardChatScrollViewProps = { + * Default is `undefined` (equivalent to `0` — no minimum floor). + */ + blankSpace?: SharedValue; ++ /** Extra bottom inset UIKit adds beyond the raw contentInset (safe area). Offset math only. */ ++ adjustedInsetCompensation?: number; ++ /** Leading inset UIKit adds outside raw contentInset. Offset math only. */ ++ adjustedStartInsetCompensation?: number; + /** + * Fires whenever the effective content inset changes — the static `contentInset` + * prop combined with the dynamic keyboard-driven padding. +diff --git a/lib/typescript/components/KeyboardChatScrollView/useChatKeyboard/helpers.d.ts b/lib/typescript/components/KeyboardChatScrollView/useChatKeyboard/helpers.d.ts +index 2b51cc4..3acec1f 100644 +--- a/lib/typescript/components/KeyboardChatScrollView/useChatKeyboard/helpers.d.ts ++++ b/lib/typescript/components/KeyboardChatScrollView/useChatKeyboard/helpers.d.ts +@@ -129,4 +129,4 @@ export declare const clampedScrollTarget: (offsetBeforeScroll: number, keyboardH + * computeIOSContentOffset(100, 300, 1000, 800, false); // 400 + * ``` + */ +-export declare const computeIOSContentOffset: (relativeScroll: number, keyboardHeight: number, contentHeight: number, layoutHeight: number, inverted: boolean, totalPaddingForMaxScroll?: number) => number; ++export declare const computeIOSContentOffset: (relativeScroll: number, keyboardHeight: number, contentHeight: number, layoutHeight: number, inverted: boolean, totalPaddingForMaxScroll?: number, startInsetCompensation?: number) => number; +diff --git a/lib/typescript/components/KeyboardChatScrollView/useChatKeyboard/types.d.ts b/lib/typescript/components/KeyboardChatScrollView/useChatKeyboard/types.d.ts +index 8d358bc..911f3d9 100644 +--- a/lib/typescript/components/KeyboardChatScrollView/useChatKeyboard/types.d.ts ++++ b/lib/typescript/components/KeyboardChatScrollView/useChatKeyboard/types.d.ts +@@ -11,6 +11,10 @@ type UseChatKeyboardOptions = { + blankSpace: SharedValue; + /** Extra content padding shared value — needed on iOS to correctly clamp contentOffset. */ + extraContentPadding: SharedValue; ++ /** Safe-area extra beyond raw contentInset. Offset math only. */ ++ adjustedInsetCompensation: number; ++ /** Leading inset UIKit adds beyond the raw contentInset. Offset math only. */ ++ adjustedStartInsetCompensation: number; + }; + type UseChatKeyboardReturn = { + /** Extra scrollable space (= keyboard height). Used as contentInset on iOS, contentInsetBottom/contentInsetTop on Android. */ +diff --git a/lib/typescript/components/KeyboardChatScrollView/useExtraContentPadding/index.d.ts b/lib/typescript/components/KeyboardChatScrollView/useExtraContentPadding/index.d.ts +index ec73f70..45b3874 100644 +--- a/lib/typescript/components/KeyboardChatScrollView/useExtraContentPadding/index.d.ts ++++ b/lib/typescript/components/KeyboardChatScrollView/useExtraContentPadding/index.d.ts +@@ -8,6 +8,10 @@ type UseExtraContentPaddingOptions = { + keyboardPadding: SharedValue; + /** Minimum inset floor — used to absorb keyboard and extraContentPadding changes. */ + blankSpace: SharedValue; ++ /** Safe-area extra beyond raw contentInset. Offset math only. */ ++ adjustedInsetCompensation: number; ++ /** Leading inset UIKit adds beyond raw contentInset. Offset math only. */ ++ adjustedStartInsetCompensation: number; + /** Current vertical scroll offset. */ + scroll: SharedValue; + /** Visible viewport dimensions. */ +diff --git a/src/components/KeyboardChatScrollView/index.tsx b/src/components/KeyboardChatScrollView/index.tsx +index 935bc71..d411dc4 100644 +--- a/src/components/KeyboardChatScrollView/index.tsx ++++ b/src/components/KeyboardChatScrollView/index.tsx +@@ -1,9 +1,13 @@ + import React, { forwardRef, useCallback, useMemo } from "react"; +-import { StyleSheet } from "react-native"; +-import { makeMutable, useAnimatedRef } from "react-native-reanimated"; ++import { Platform, StyleSheet } from "react-native"; ++import { makeMutable, runOnJS, useAnimatedRef } from "react-native-reanimated"; + import Reanimated from "react-native-reanimated"; + +-import { useAnimatedStyle, useDerivedValue } from "../../reanimated"; ++import { ++ useAnimatedReaction, ++ useAnimatedStyle, ++ useDerivedValue, ++} from "../../reanimated"; + import useCombinedRef from "../hooks/useCombinedRef"; + import ScrollViewWithBottomPadding from "../ScrollViewWithBottomPadding"; + +@@ -35,9 +39,12 @@ const KeyboardChatScrollView: React.ForwardRefExoticComponent< + offset = 0, + extraContentPadding = ZERO_CONTENT_PADDING, + blankSpace = ZERO_BLANK_SPACE, ++ adjustedInsetCompensation = 0, ++ adjustedStartInsetCompensation = 0, + applyWorkaroundForContentInsetHitTestBug = false, + onLayout: onLayoutProp, + onContentSizeChange: onContentSizeChangeProp, ++ onContentInsetChange, + onEndVisible, + ...rest + }, +@@ -64,6 +71,8 @@ const KeyboardChatScrollView: React.ForwardRefExoticComponent< + offset, + blankSpace, + extraContentPadding, ++ adjustedInsetCompensation, ++ adjustedStartInsetCompensation, + initialContentOffsetY: rest.contentOffset?.y, + }); + +@@ -72,6 +81,8 @@ const KeyboardChatScrollView: React.ForwardRefExoticComponent< + extraContentPadding, + keyboardPadding: padding, + blankSpace, ++ adjustedInsetCompensation, ++ adjustedStartInsetCompensation, + scroll, + layout, + size, +@@ -109,13 +120,40 @@ const KeyboardChatScrollView: React.ForwardRefExoticComponent< + ), + ); + +- // Scroll indicator inset = keyboard + extraContentPadding (excludes blankSpace). +- // Apps that render into the unsafe area can supply a negative +- // scrollIndicatorInsets adjustment at the application layer. +- const indicatorPadding = useDerivedValue( +- () => padding.value + extraContentPadding.value, ++ // iOS applies the destination contentInset and contentOffset together at ++ // keyboard-animation start. Keep that native target behavior, but report ++ // the presentation height so virtualized-list layout follows the keyboard. ++ const reportedPadding = useDerivedValue(() => ++ Platform.OS === "ios" ++ ? Math.min( ++ layout.value.height, ++ Math.max( ++ blankSpace.value, ++ currentHeight.value + extraContentPadding.value, ++ ), ++ ) ++ : totalPadding.value, + ); + ++ // Mirror the effective bottom padding (keyboard + composer + blank floor) ++ // to the consumer — a virtualized list needs it in its own scroll math or ++ // its end/maintain targets point at the under-the-keyboard resting offset. ++ useAnimatedReaction( ++ () => reportedPadding.value, ++ (current, previous) => { ++ if (onContentInsetChange && current !== previous) { ++ runOnJS(onContentInsetChange)({ bottom: current }); ++ } ++ }, ++ [onContentInsetChange], ++ ); ++ ++ // Scroll indicator inset = keyboard only (excludes extraContentPadding and ++ // blankSpace): with a floating composer the indicator track should run the ++ // full height of the scroll view, behind the composer, like iOS Messages. ++ // The keyboard still lifts it so it never disappears under the keyboard. ++ const indicatorPadding = useDerivedValue(() => padding.value); ++ + const onLayout = useCallback( + (e: LayoutChangeEvent) => { + onLayoutInternal(e); +diff --git a/src/components/KeyboardChatScrollView/types.ts b/src/components/KeyboardChatScrollView/types.ts +index b7c98c8..e1012a8 100644 +--- a/src/components/KeyboardChatScrollView/types.ts ++++ b/src/components/KeyboardChatScrollView/types.ts +@@ -93,6 +93,17 @@ export type KeyboardChatScrollViewProps = { + * Default is `undefined` (equivalent to `0` — no minimum floor). + */ + blankSpace?: SharedValue; ++ /** ++ * Extra bottom inset UIKit adds on top of the raw `contentInset` this ++ * component writes (e.g. the home-indicator safe area under ++ * `contentInsetAdjustmentBehavior="automatic"`). Used ONLY in scroll-offset ++ * math (max scroll / end pinning) — never written into `contentInset`. ++ * ++ * Default is `0`. ++ */ ++ adjustedInsetCompensation?: number; ++ /** Leading inset UIKit adds outside raw contentInset. Offset math only. */ ++ adjustedStartInsetCompensation?: number; + /** + * Fires whenever the effective content inset changes — the static `contentInset` + * prop combined with the dynamic keyboard-driven padding. +diff --git a/src/components/KeyboardChatScrollView/useChatKeyboard/helpers.ts b/src/components/KeyboardChatScrollView/useChatKeyboard/helpers.ts +index 37eacd1..3d77ab2 100644 +--- a/src/components/KeyboardChatScrollView/useChatKeyboard/helpers.ts ++++ b/src/components/KeyboardChatScrollView/useChatKeyboard/helpers.ts +@@ -245,6 +245,7 @@ export const computeIOSContentOffset = ( + layoutHeight: number, + inverted: boolean, + totalPaddingForMaxScroll?: number, ++ startInsetCompensation: number = 0, + ): number => { + "worklet"; + +@@ -262,7 +263,14 @@ export const computeIOSContentOffset = ( + ); + } + +- const maxScroll = Math.max(contentHeight - layoutHeight + paddingForMax, 0); ++ const minScroll = -startInsetCompensation; ++ const maxScroll = Math.max( ++ contentHeight - layoutHeight + paddingForMax, ++ minScroll, ++ ); + +- return Math.min(Math.max(keyboardHeight + relativeScroll, 0), maxScroll); ++ return Math.min( ++ Math.max(keyboardHeight + relativeScroll, minScroll), ++ maxScroll, ++ ); + }; +diff --git a/src/components/KeyboardChatScrollView/useChatKeyboard/index.ios.ts b/src/components/KeyboardChatScrollView/useChatKeyboard/index.ios.ts +index 1ae9b81..208505b 100644 +--- a/src/components/KeyboardChatScrollView/useChatKeyboard/index.ios.ts ++++ b/src/components/KeyboardChatScrollView/useChatKeyboard/index.ios.ts +@@ -43,6 +43,8 @@ function useChatKeyboard( + offset, + blankSpace, + extraContentPadding, ++ adjustedInsetCompensation, ++ adjustedStartInsetCompensation, + } = options; + + const padding = useSharedValue(0); +@@ -80,6 +82,10 @@ function useChatKeyboard( + offset, + ); + ++ if (e.duration === 0) { ++ currentHeight.value = effective; ++ } ++ + const atEnd = isScrollAtEnd( + scroll.value, + layout.value.height, +@@ -105,10 +111,12 @@ function useChatKeyboard( + effective, + minimumPaddingAbsorbed, + ); +- const actualTotalPadding = Math.max( +- blankSpace.value, +- effective + extraContentPadding.value, +- ); ++ // UIKit adds adjustedInsetCompensation (safe area) on top of the raw ++ // inset; include it here so end/max-scroll targets match the real ++ // resting offsets. ++ const actualTotalPadding = ++ Math.max(blankSpace.value, effective + extraContentPadding.value) + ++ adjustedInsetCompensation; + + // `onInteractive` owns the native scroll offset. When its final + // keyboardWillHide event arrives, consume the marker so this +@@ -142,11 +150,11 @@ function useChatKeyboard( + } else { + const maxScroll = Math.max( + size.value.height - layout.value.height + actualTotalPadding, +- 0, ++ -adjustedStartInsetCompensation, + ); + + contentOffsetY.value = Math.max( +- 0, ++ -adjustedStartInsetCompensation, + Math.min(scroll.value, maxScroll), + ); + } +@@ -177,11 +185,11 @@ function useChatKeyboard( + } else { + const maxScroll = Math.max( + size.value.height - layout.value.height + actualTotalPadding, +- 0, ++ -adjustedStartInsetCompensation, + ); + + contentOffsetY.value = Math.max( +- 0, ++ -adjustedStartInsetCompensation, + Math.min(scroll.value, maxScroll), + ); + } +@@ -231,17 +239,36 @@ function useChatKeyboard( + layout.value.height, + inverted, + actualTotalPadding, ++ adjustedStartInsetCompensation, + ); + }, +- onInteractive: () => { ++ onMove: (e) => { + "worklet"; + +- isInteractiveDismissal.value = true; ++ if (freeze.value) { ++ return; ++ } ++ ++ currentHeight.value = getEffectiveHeight( ++ e.height, ++ targetKeyboardHeight.value, ++ offset, ++ ); + }, +- onMove: () => { ++ onInteractive: (e) => { + "worklet"; + +- // iOS doesn't need per-frame updates (contentOffset handles it) ++ isInteractiveDismissal.value = true; ++ ++ if (freeze.value) { ++ return; ++ } ++ ++ currentHeight.value = getEffectiveHeight( ++ e.height, ++ targetKeyboardHeight.value, ++ offset, ++ ); + }, + onEnd: (e) => { + "worklet"; +@@ -256,10 +283,18 @@ function useChatKeyboard( + offset, + ); + ++ currentHeight.value = effective; + padding.value = effective; + }, + }, +- [inverted, keyboardLiftBehavior, offset, extraContentPadding], ++ [ ++ inverted, ++ keyboardLiftBehavior, ++ offset, ++ extraContentPadding, ++ adjustedInsetCompensation, ++ adjustedStartInsetCompensation, ++ ], + ); + + return { +diff --git a/src/components/KeyboardChatScrollView/useChatKeyboard/types.ts b/src/components/KeyboardChatScrollView/useChatKeyboard/types.ts +index f89b7c8..02e0e19 100644 +--- a/src/components/KeyboardChatScrollView/useChatKeyboard/types.ts ++++ b/src/components/KeyboardChatScrollView/useChatKeyboard/types.ts +@@ -13,6 +13,10 @@ type UseChatKeyboardOptions = { + blankSpace: SharedValue; + /** Extra content padding shared value — needed on iOS to correctly clamp contentOffset. */ + extraContentPadding: SharedValue; ++ /** Extra bottom inset UIKit adds beyond the raw contentInset (safe area). Offset math only. */ ++ adjustedInsetCompensation: number; ++ /** Leading inset UIKit adds beyond the raw contentInset. Offset math only. */ ++ adjustedStartInsetCompensation: number; + }; + + type UseChatKeyboardReturn = { +diff --git a/src/components/KeyboardChatScrollView/useExtraContentPadding/index.ts b/src/components/KeyboardChatScrollView/useExtraContentPadding/index.ts +index de7d6f7..631cdfb 100644 +--- a/src/components/KeyboardChatScrollView/useExtraContentPadding/index.ts ++++ b/src/components/KeyboardChatScrollView/useExtraContentPadding/index.ts +@@ -17,6 +17,10 @@ type UseExtraContentPaddingOptions = { + keyboardPadding: SharedValue; + /** Minimum inset floor — used to absorb keyboard and extraContentPadding changes. */ + blankSpace: SharedValue; ++ /** Extra bottom inset UIKit adds beyond the raw contentInset (safe area). Offset math only. */ ++ adjustedInsetCompensation: number; ++ /** Leading inset UIKit adds beyond raw contentInset. Offset math only. */ ++ adjustedStartInsetCompensation: number; + /** Current vertical scroll offset. */ + scroll: SharedValue; + /** Visible viewport dimensions. */ +@@ -52,6 +56,8 @@ function useExtraContentPadding(options: UseExtraContentPaddingOptions): void { + extraContentPadding, + keyboardPadding, + blankSpace, ++ adjustedInsetCompensation, ++ adjustedStartInsetCompensation, + scroll, + layout, + size, +@@ -73,8 +79,13 @@ function useExtraContentPadding(options: UseExtraContentPaddingOptions): void { + // otherwise the native ScrollView clamps to the old range. + requestAnimationFrame(() => { + // check that view is still mounted and ref is actual +- // otherwise it may lead to a crash +- if (!scrollViewRef()) { ++ // otherwise it may lead to a crash. Reanimated 4.7+ passes the ++ // ref to the UI runtime as a shareable holding the node in `.value`. ++ const node = ++ typeof scrollViewRef === "function" ++ ? scrollViewRef() ++ : (scrollViewRef as unknown as { value: unknown }).value; ++ if (!node) { + return; + } + scrollTo(scrollViewRef, 0, target, false); +@@ -100,14 +111,12 @@ function useExtraContentPadding(options: UseExtraContentPaddingOptions): void { + } + + // Compute effective delta considering blankSpace floor +- const previousTotal = Math.max( +- blankSpace.value, +- keyboardPadding.value + previous, +- ); +- const currentTotal = Math.max( +- blankSpace.value, +- keyboardPadding.value + current, +- ); ++ const previousTotal = ++ Math.max(blankSpace.value, keyboardPadding.value + previous) + ++ adjustedInsetCompensation; ++ const currentTotal = ++ Math.max(blankSpace.value, keyboardPadding.value + current) + ++ adjustedInsetCompensation; + const effectiveDelta = currentTotal - previousTotal; + + if (effectiveDelta === 0) { +@@ -140,22 +149,28 @@ function useExtraContentPadding(options: UseExtraContentPaddingOptions): void { + + scrollToTarget(target); + } else { ++ const minScroll = -adjustedStartInsetCompensation; + const maxScroll = Math.max( + size.value.height - layout.value.height + currentTotal, +- 0, ++ minScroll, + ); +- // Clamp at 0 as well: when the content is shorter than the viewport a +- // shrinking padding makes `effectiveDelta` negative while `scroll.value` +- // is already 0, so the target would go below the top of the content. ++ // Clamp at the top as well: when the content is shorter than the ++ // viewport a shrinking padding makes `effectiveDelta` negative while ++ // `scroll.value` is already at the top. + const target = Math.max( ++ minScroll, + Math.min(scroll.value + effectiveDelta, maxScroll), +- 0, + ); + + scrollToTarget(target); + } + }, +- [inverted, keyboardLiftBehavior], ++ [ ++ inverted, ++ keyboardLiftBehavior, ++ adjustedInsetCompensation, ++ adjustedStartInsetCompensation, ++ ], + ); + } + diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 092af1eaeeb7c7211cc1140816751ebe1fe4fdf0..f21be615171032d2fdb1ab06298ff64be4a905d5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -111,7 +111,7 @@ patchedDependencies: expo-widgets@58.0.11: 94de7964c5a67ab40e6b79d9bdf4b7bfc50ae97b70722ee8e5a35105ca83386b node-pty@1.2.0-beta.15: f2fe901c61cde17986240002d05c172d5d0272d83ffaab8d0ebeb922763be414 react-native-gesture-handler@3.2.1: d518a937e3cb2df5e7d67f1ab6b265c74a8639e4572d1b032b7b747e9b1c01a9 - react-native-keyboard-controller@1.22.4: c631278492f0113f42fa8bed43c7d8ed5779424dd12af0cefc55132c2ee1d84c + react-native-keyboard-controller@1.22.6: 6e448411781347cd0b87868ee279117514a399801c18f233ddca4fa3935be75d react-native-nitro-markdown@0.5.8: 642b47830730acff3761fe29cf54271d18b493c0070c750097850b7ea032e00c react-native-nitro-modules@0.35.9: daf4a639ce3f951af630a3711e4082d58e3a11e5a79f45efb0b59925327ebde2 react-native-screens@4.28.0: cc1301436ee62c39860acc9ee1a1e60b7d47e6abd211c42effa62c056d165483 @@ -448,8 +448,8 @@ importers: specifier: ^0.2.2 version: 0.2.2(react-native@0.88.0-rc.3(@babel/core@7.29.7)(@react-native/metro-config@0.88.0-rc.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.3.0)(bufferutil@4.1.0)(react@19.3.0)(utf-8-validate@6.0.6))(react@19.3.0) react-native-keyboard-controller: - specifier: 1.22.4 - version: 1.22.4(patch_hash=c631278492f0113f42fa8bed43c7d8ed5779424dd12af0cefc55132c2ee1d84c)(react-native-reanimated@4.7.0(react-native-worklets@0.13.0(@babel/core@7.29.7)(@react-native/metro-config@0.88.0-rc.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(react-native@0.88.0-rc.3(@babel/core@7.29.7)(@react-native/metro-config@0.88.0-rc.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.3.0)(bufferutil@4.1.0)(react@19.3.0)(utf-8-validate@6.0.6))(react@19.3.0))(react-native@0.88.0-rc.3(@babel/core@7.29.7)(@react-native/metro-config@0.88.0-rc.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.3.0)(bufferutil@4.1.0)(react@19.3.0)(utf-8-validate@6.0.6))(react@19.3.0))(react-native@0.88.0-rc.3(@babel/core@7.29.7)(@react-native/metro-config@0.88.0-rc.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.3.0)(bufferutil@4.1.0)(react@19.3.0)(utf-8-validate@6.0.6))(react@19.3.0) + specifier: 1.22.6 + version: 1.22.6(patch_hash=6e448411781347cd0b87868ee279117514a399801c18f233ddca4fa3935be75d)(react-native-reanimated@4.7.0(react-native-worklets@0.13.0(@babel/core@7.29.7)(@react-native/metro-config@0.88.0-rc.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(react-native@0.88.0-rc.3(@babel/core@7.29.7)(@react-native/metro-config@0.88.0-rc.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.3.0)(bufferutil@4.1.0)(react@19.3.0)(utf-8-validate@6.0.6))(react@19.3.0))(react-native@0.88.0-rc.3(@babel/core@7.29.7)(@react-native/metro-config@0.88.0-rc.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.3.0)(bufferutil@4.1.0)(react@19.3.0)(utf-8-validate@6.0.6))(react@19.3.0))(react-native@0.88.0-rc.3(@babel/core@7.29.7)(@react-native/metro-config@0.88.0-rc.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.3.0)(bufferutil@4.1.0)(react@19.3.0)(utf-8-validate@6.0.6))(react@19.3.0) react-native-nitro-markdown: specifier: ^0.5.0 version: 0.5.8(patch_hash=642b47830730acff3761fe29cf54271d18b493c0070c750097850b7ea032e00c)(react-native-nitro-modules@0.35.9(patch_hash=daf4a639ce3f951af630a3711e4082d58e3a11e5a79f45efb0b59925327ebde2)(react-native@0.88.0-rc.3(@babel/core@7.29.7)(@react-native/metro-config@0.88.0-rc.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.3.0)(bufferutil@4.1.0)(react@19.3.0)(utf-8-validate@6.0.6))(react@19.3.0))(react-native-svg@15.15.5(react-native@0.88.0-rc.3(@babel/core@7.29.7)(@react-native/metro-config@0.88.0-rc.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.3.0)(bufferutil@4.1.0)(react@19.3.0)(utf-8-validate@6.0.6))(react@19.3.0))(react-native@0.88.0-rc.3(@babel/core@7.29.7)(@react-native/metro-config@0.88.0-rc.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.3.0)(bufferutil@4.1.0)(react@19.3.0)(utf-8-validate@6.0.6))(react@19.3.0) @@ -9561,8 +9561,8 @@ packages: react: '*' react-native: '*' - react-native-keyboard-controller@1.22.4: - resolution: {integrity: sha512-2by3oJBQqKH9UAydlBeiEpivxIyTbt345bmverLym7vbEp4NXckWQLF0STnVuXYccRak5/sd3wMYDJPhv/9+3g==} + react-native-keyboard-controller@1.22.6: + resolution: {integrity: sha512-gwJAI3Bhbqs/Ja3uaeBz7EFQSFcem8q4wsXkrC9sy1CL14gjWhBICLu2Ecn7HmezpIu33uhLbirLGyqSawxRtg==} peerDependencies: react: '*' react-native: '*' @@ -20049,7 +20049,7 @@ snapshots: react: 19.3.0 react-native: 0.88.0-rc.3(@babel/core@7.29.7)(@react-native/metro-config@0.88.0-rc.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.3.0)(bufferutil@4.1.0)(react@19.3.0)(utf-8-validate@6.0.6) - react-native-keyboard-controller@1.22.4(patch_hash=c631278492f0113f42fa8bed43c7d8ed5779424dd12af0cefc55132c2ee1d84c)(react-native-reanimated@4.7.0(react-native-worklets@0.13.0(@babel/core@7.29.7)(@react-native/metro-config@0.88.0-rc.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(react-native@0.88.0-rc.3(@babel/core@7.29.7)(@react-native/metro-config@0.88.0-rc.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.3.0)(bufferutil@4.1.0)(react@19.3.0)(utf-8-validate@6.0.6))(react@19.3.0))(react-native@0.88.0-rc.3(@babel/core@7.29.7)(@react-native/metro-config@0.88.0-rc.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.3.0)(bufferutil@4.1.0)(react@19.3.0)(utf-8-validate@6.0.6))(react@19.3.0))(react-native@0.88.0-rc.3(@babel/core@7.29.7)(@react-native/metro-config@0.88.0-rc.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.3.0)(bufferutil@4.1.0)(react@19.3.0)(utf-8-validate@6.0.6))(react@19.3.0): + react-native-keyboard-controller@1.22.6(patch_hash=6e448411781347cd0b87868ee279117514a399801c18f233ddca4fa3935be75d)(react-native-reanimated@4.7.0(react-native-worklets@0.13.0(@babel/core@7.29.7)(@react-native/metro-config@0.88.0-rc.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(react-native@0.88.0-rc.3(@babel/core@7.29.7)(@react-native/metro-config@0.88.0-rc.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.3.0)(bufferutil@4.1.0)(react@19.3.0)(utf-8-validate@6.0.6))(react@19.3.0))(react-native@0.88.0-rc.3(@babel/core@7.29.7)(@react-native/metro-config@0.88.0-rc.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.3.0)(bufferutil@4.1.0)(react@19.3.0)(utf-8-validate@6.0.6))(react@19.3.0))(react-native@0.88.0-rc.3(@babel/core@7.29.7)(@react-native/metro-config@0.88.0-rc.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.3.0)(bufferutil@4.1.0)(react@19.3.0)(utf-8-validate@6.0.6))(react@19.3.0): dependencies: react: 19.3.0 react-native: 0.88.0-rc.3(@babel/core@7.29.7)(@react-native/metro-config@0.88.0-rc.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.3.0)(bufferutil@4.1.0)(react@19.3.0)(utf-8-validate@6.0.6) diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 3c0ef065701e9852daca8e0585ae8fec27c5030d..5bd65fcacbbb04c5d7a2c0a19bc6d0fb7bfe7bdf 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -143,7 +143,7 @@ minimumReleaseAgeExclude: - "react@19.3.0" - "react-dom@19.3.0" - "react-native@0.88.0-rc.3" - - "react-native-keyboard-controller@1.22.4" + - "react-native-keyboard-controller@1.22.6" - "react-native-worklets@0.13.0" - "react-native-reanimated@4.7.0" - "react-native-screens@4.28.0" @@ -260,7 +260,7 @@ patchedDependencies: # Link http(s) URLs with a port or a single-label host. md4c otherwise requires a dotted host and stops at ':'. react-native-nitro-markdown@0.5.8: patches/react-native-nitro-markdown@0.5.8.patch react-native-gesture-handler@3.2.1: patches/react-native-gesture-handler@3.2.1.patch - react-native-keyboard-controller@1.22.4: patches/react-native-keyboard-controller@1.22.4.patch + react-native-keyboard-controller@1.22.6: patches/react-native-keyboard-controller@1.22.6.patch react-native-nitro-modules@0.35.9: patches/react-native-nitro-modules@0.35.9.patch react-native-screens@4.28.0: patches/react-native-screens@4.28.0.patch uniwind@1.11.0: patches/uniwind@1.11.0.patch