Skip to content

Commit a8600a1

Browse files
committed
feat: remove KeyboardAvoidingView compat layer
1 parent b2dca65 commit a8600a1

File tree

4 files changed

+15
-166
lines changed

4 files changed

+15
-166
lines changed

src/components/HeaderWithBackButton/index.tsx

+1-3
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeed
1010
import SearchButton from '@components/Search/SearchRouter/SearchButton';
1111
import ThreeDotsMenu from '@components/ThreeDotsMenu';
1212
import Tooltip from '@components/Tooltip';
13-
import useKeyboardState from '@hooks/useKeyboardState';
1413
import useLocalize from '@hooks/useLocalize';
1514
import useStyleUtils from '@hooks/useStyleUtils';
1615
import useTheme from '@hooks/useTheme';
@@ -72,7 +71,6 @@ function HeaderWithBackButton({
7271
const StyleUtils = useStyleUtils();
7372
const [isDownloadButtonActive, temporarilyDisableDownloadButton] = useThrottledButtonState();
7473
const {translate} = useLocalize();
75-
const {isKeyboardShown} = useKeyboardState();
7674

7775
// If the icon is present, the header bar should be taller and use different font.
7876
const isCentralPaneSettings = !!icon;
@@ -155,7 +153,7 @@ function HeaderWithBackButton({
155153
<Tooltip text={translate('common.back')}>
156154
<PressableWithoutFeedback
157155
onPress={() => {
158-
if (isKeyboardShown) {
156+
if (Keyboard.isVisible()) {
159157
Keyboard.dismiss();
160158
}
161159
const topmostReportId = Navigation.getTopmostReportId();
Original file line numberDiff line numberDiff line change
@@ -1,133 +1,15 @@
1-
import React, {forwardRef, useCallback, useMemo, useState} from 'react';
2-
import type {LayoutRectangle, View, ViewProps} from 'react-native';
3-
import {useKeyboardContext, useKeyboardHandler} from 'react-native-keyboard-controller';
4-
import Reanimated, {interpolate, runOnUI, useAnimatedStyle, useDerivedValue, useSharedValue} from 'react-native-reanimated';
5-
import {useSafeAreaFrame} from 'react-native-safe-area-context';
6-
import type {KeyboardAvoidingViewProps} from './types';
7-
8-
const useKeyboardAnimation = () => {
9-
const {reanimated} = useKeyboardContext();
10-
11-
// calculate it only once on mount, to avoid `SharedValue` reads during a render
12-
const [initialHeight] = useState(() => -reanimated.height.get());
13-
const [initialProgress] = useState(() => reanimated.progress.get());
14-
15-
const heightWhenOpened = useSharedValue(initialHeight);
16-
const height = useSharedValue(initialHeight);
17-
const progress = useSharedValue(initialProgress);
18-
const isClosed = useSharedValue(initialProgress === 0);
19-
20-
useKeyboardHandler(
21-
{
22-
onStart: (e) => {
23-
'worklet';
24-
25-
progress.set(e.progress);
26-
height.set(e.height);
27-
28-
if (e.height > 0) {
29-
isClosed.set(false);
30-
heightWhenOpened.set(e.height);
31-
}
32-
},
33-
onEnd: (e) => {
34-
'worklet';
35-
36-
isClosed.set(e.height === 0);
37-
height.set(e.height);
38-
progress.set(e.progress);
39-
},
40-
},
41-
[],
42-
);
43-
44-
return {height, progress, heightWhenOpened, isClosed};
45-
};
46-
47-
const defaultLayout: LayoutRectangle = {
48-
x: 0,
49-
y: 0,
50-
width: 0,
51-
height: 0,
52-
};
53-
54-
/**
55-
* View that moves out of the way when the keyboard appears by automatically
56-
* adjusting its height, position, or bottom padding.
57-
*
58-
* This `KeyboardAvoidingView` acts as a backward compatible layer for the previous Android behavior (prior to edge-to-edge mode).
59-
* We can use `KeyboardAvoidingView` directly from the `react-native-keyboard-controller` package, but in this case animations are stuttering and it's better to handle as a separate task.
1+
/*
2+
* The KeyboardAvoidingView is only used on ios
603
*/
61-
const KeyboardAvoidingView = forwardRef<View, React.PropsWithChildren<KeyboardAvoidingViewProps>>(
62-
({behavior, children, contentContainerStyle, enabled = true, keyboardVerticalOffset = 0, style, onLayout: onLayoutProps, ...props}, ref) => {
63-
const initialFrame = useSharedValue<LayoutRectangle | null>(null);
64-
const frame = useDerivedValue(() => initialFrame.get() ?? defaultLayout);
65-
66-
const keyboard = useKeyboardAnimation();
67-
const {height: screenHeight} = useSafeAreaFrame();
68-
69-
const relativeKeyboardHeight = useCallback(() => {
70-
'worklet';
71-
72-
const keyboardY = screenHeight - keyboard.heightWhenOpened.get() - keyboardVerticalOffset;
73-
74-
return Math.max(frame.get().y + frame.get().height - keyboardY, 0);
75-
}, [screenHeight, keyboard.heightWhenOpened, keyboardVerticalOffset, frame]);
76-
77-
const onLayoutWorklet = useCallback(
78-
(layout: LayoutRectangle) => {
79-
'worklet';
80-
81-
if (keyboard.isClosed.get() || initialFrame.get() === null) {
82-
initialFrame.set(layout);
83-
}
84-
},
85-
[initialFrame, keyboard.isClosed],
86-
);
87-
const onLayout = useCallback<NonNullable<ViewProps['onLayout']>>(
88-
(e) => {
89-
runOnUI(onLayoutWorklet)(e.nativeEvent.layout);
90-
onLayoutProps?.(e);
91-
},
92-
[onLayoutProps, onLayoutWorklet],
93-
);
94-
95-
const animatedStyle = useAnimatedStyle(() => {
96-
const bottom = interpolate(keyboard.progress.get(), [0, 1], [0, relativeKeyboardHeight()]);
97-
const bottomHeight = enabled ? bottom : 0;
98-
99-
switch (behavior) {
100-
case 'height':
101-
if (!keyboard.isClosed.get()) {
102-
return {
103-
height: frame.get().height - bottomHeight,
104-
flex: 0,
105-
};
106-
}
107-
108-
return {};
109-
110-
case 'padding':
111-
return {paddingBottom: bottomHeight};
4+
import React from 'react';
5+
import {KeyboardAvoidingView as KeyboardAvoidingViewComponent} from 'react-native-keyboard-controller';
6+
import type {KeyboardAvoidingViewProps} from './types';
1127

113-
default:
114-
return {};
115-
}
116-
}, [behavior, enabled, relativeKeyboardHeight]);
117-
const combinedStyles = useMemo(() => [style, animatedStyle], [style, animatedStyle]);
8+
function KeyboardAvoidingView(props: KeyboardAvoidingViewProps) {
9+
// eslint-disable-next-line react/jsx-props-no-spreading
10+
return <KeyboardAvoidingViewComponent {...props} />;
11+
}
11812

119-
return (
120-
<Reanimated.View
121-
ref={ref}
122-
style={combinedStyles}
123-
onLayout={onLayout}
124-
// eslint-disable-next-line react/jsx-props-no-spreading
125-
{...props}
126-
>
127-
{children}
128-
</Reanimated.View>
129-
);
130-
},
131-
);
13+
KeyboardAvoidingView.displayName = 'KeyboardAvoidingView';
13214

13315
export default KeyboardAvoidingView;

src/components/ScreenWrapper.tsx

+2-10
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import {PickerAvoidingView} from 'react-native-picker-select';
77
import type {EdgeInsets} from 'react-native-safe-area-context';
88
import useEnvironment from '@hooks/useEnvironment';
99
import useInitialDimensions from '@hooks/useInitialWindowDimensions';
10-
import useKeyboardState from '@hooks/useKeyboardState';
1110
import useNetwork from '@hooks/useNetwork';
1211
import useResponsiveLayout from '@hooks/useResponsiveLayout';
1312
import useStyledSafeAreaInsets from '@hooks/useStyledSafeAreaInsets';
@@ -158,18 +157,11 @@ function ScreenWrapper(
158157
const {isSmallScreenWidth, shouldUseNarrowLayout} = useResponsiveLayout();
159158
const {initialHeight} = useInitialDimensions();
160159
const styles = useThemeStyles();
161-
const keyboardState = useKeyboardState();
162160
const {isDevelopment} = useEnvironment();
163161
const {isOffline} = useNetwork();
164162
const [didScreenTransitionEnd, setDidScreenTransitionEnd] = useState(false);
165163
const maxHeight = shouldEnableMaxHeight ? windowHeight : undefined;
166164
const minHeight = shouldEnableMinHeight && !Browser.isSafari() ? initialHeight : undefined;
167-
const isKeyboardShown = keyboardState?.isKeyboardShown ?? false;
168-
169-
const isKeyboardShownRef = useRef<boolean>(false);
170-
171-
// eslint-disable-next-line react-compiler/react-compiler
172-
isKeyboardShownRef.current = keyboardState?.isKeyboardShown ?? false;
173165

174166
const route = useRoute();
175167
const shouldReturnToOldDot = useMemo(() => {
@@ -191,7 +183,7 @@ function ScreenWrapper(
191183
PanResponder.create({
192184
onMoveShouldSetPanResponderCapture: (_e, gestureState) => {
193185
const isHorizontalSwipe = Math.abs(gestureState.dx) > Math.abs(gestureState.dy);
194-
const shouldDismissKeyboard = shouldDismissKeyboardBeforeClose && isKeyboardShown && Browser.isMobile();
186+
const shouldDismissKeyboard = shouldDismissKeyboardBeforeClose && Keyboard.isVisible() && Browser.isMobile();
195187

196188
return isHorizontalSwipe && shouldDismissKeyboard;
197189
},
@@ -221,7 +213,7 @@ function ScreenWrapper(
221213
// described here https://reactnavigation.org/docs/preventing-going-back/#limitations
222214
const beforeRemoveSubscription = shouldDismissKeyboardBeforeClose
223215
? navigation.addListener('beforeRemove', () => {
224-
if (!isKeyboardShownRef.current) {
216+
if (!Keyboard.isVisible()) {
225217
return;
226218
}
227219
Keyboard.dismiss();

src/components/withKeyboardState.tsx

+2-25
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
import type {ComponentType, ForwardedRef, ReactElement, RefAttributes} from 'react';
2-
import React, {createContext, forwardRef, useEffect, useMemo, useState} from 'react';
1+
import type {ReactElement} from 'react';
2+
import React, {createContext, useEffect, useMemo, useState} from 'react';
33
import {Keyboard} from 'react-native';
4-
import getComponentDisplayName from '@libs/getComponentDisplayName';
54
import type ChildrenProps from '@src/types/utils/ChildrenProps';
65

76
type KeyboardStateContextValue = {
@@ -44,27 +43,5 @@ function KeyboardStateProvider({children}: ChildrenProps): ReactElement | null {
4443
return <KeyboardStateContext.Provider value={contextValue}>{children}</KeyboardStateContext.Provider>;
4544
}
4645

47-
export default function withKeyboardState<TProps extends KeyboardStateContextValue, TRef>(
48-
WrappedComponent: ComponentType<TProps & RefAttributes<TRef>>,
49-
): (props: Omit<TProps, keyof KeyboardStateContextValue> & React.RefAttributes<TRef>) => ReactElement | null {
50-
function WithKeyboardState(props: Omit<TProps, keyof KeyboardStateContextValue>, ref: ForwardedRef<TRef>) {
51-
return (
52-
<KeyboardStateContext.Consumer>
53-
{(keyboardStateProps) => (
54-
<WrappedComponent
55-
// eslint-disable-next-line react/jsx-props-no-spreading
56-
{...keyboardStateProps}
57-
// eslint-disable-next-line react/jsx-props-no-spreading
58-
{...(props as TProps)}
59-
ref={ref}
60-
/>
61-
)}
62-
</KeyboardStateContext.Consumer>
63-
);
64-
}
65-
WithKeyboardState.displayName = `withKeyboardState(${getComponentDisplayName(WrappedComponent)})`;
66-
return forwardRef(WithKeyboardState);
67-
}
68-
6946
export type {KeyboardStateContextValue};
7047
export {KeyboardStateProvider, KeyboardStateContext};

0 commit comments

Comments
 (0)