diff --git a/native/account/auth-components/auth-content-container.react.js b/native/account/auth-components/auth-content-container.react.js --- a/native/account/auth-components/auth-content-container.react.js +++ b/native/account/auth-components/auth-content-container.react.js @@ -3,8 +3,7 @@ import { useHeaderHeight } from '@react-navigation/elements'; import * as React from 'react'; import { ScrollView } from 'react-native'; - -import KeyboardAvoidingView from '../../components/keyboard-avoiding-view.react.js'; +import { KeyboardAvoidingView } from 'react-native-keyboard-controller'; type ViewProps = React.ElementConfig; type Props = { diff --git a/native/account/logged-out-modal.react.js b/native/account/logged-out-modal.react.js --- a/native/account/logged-out-modal.react.js +++ b/native/account/logged-out-modal.react.js @@ -13,6 +13,7 @@ BackHandler, ActivityIndicator, } from 'react-native'; +import { KeyboardAvoidingView } from 'react-native-keyboard-controller'; import { Easing, useSharedValue, @@ -35,7 +36,6 @@ import type { LogInState } from './log-in-panel.react.js'; import LoggedOutStaffInfo from './logged-out-staff-info.react.js'; import PromptButton from './prompt-button.react.js'; -import KeyboardAvoidingView from '../components/keyboard-avoiding-view.react.js'; import ConnectedStatusBar from '../connected-status-bar.react.js'; import { useRatchetingKeyboardHeight } from '../keyboard/animated-keyboard.js'; import { createIsForegroundSelector } from '../navigation/nav-selectors.js'; diff --git a/native/calendar/calendar.react.js b/native/calendar/calendar.react.js --- a/native/calendar/calendar.react.js +++ b/native/calendar/calendar.react.js @@ -7,9 +7,9 @@ import { createStackNavigator } from '@react-navigation/stack'; import * as React from 'react'; import { View } from 'react-native'; +import { KeyboardAvoidingView } from 'react-native-keyboard-controller'; import CalendarScreen from './calendar-screen.react.js'; -import KeyboardAvoidingView from '../components/keyboard-avoiding-view.react.js'; import CommunityDrawerButton from '../navigation/community-drawer-button.react.js'; import { CalendarScreenRouteName, diff --git a/native/chat/chat.react.js b/native/chat/chat.react.js --- a/native/chat/chat.react.js +++ b/native/chat/chat.react.js @@ -30,6 +30,7 @@ useWindowDimensions, type MeasureOnSuccessCallback, } from 'react-native'; +import { KeyboardAvoidingView } from 'react-native-keyboard-controller'; import MessageStorePruner from 'lib/components/message-store-pruner.react.js'; import ThreadDraftUpdater from 'lib/components/thread-draft-updater.react.js'; @@ -63,7 +64,6 @@ import ThreadScreenPruner from './thread-screen-pruner.react.js'; import ThreadSettingsButton from './thread-settings-button.react.js'; import ThreadSettingsHeaderTitle from './thread-settings-header-title.react.js'; -import KeyboardAvoidingView from '../components/keyboard-avoiding-view.react.js'; import { NUXHandler } from '../components/nux-handler.react.js'; import { nuxTip, diff --git a/native/community-creation/community-creation-content-container.react.js b/native/community-creation/community-creation-content-container.react.js --- a/native/community-creation/community-creation-content-container.react.js +++ b/native/community-creation/community-creation-content-container.react.js @@ -3,8 +3,7 @@ import { useHeaderHeight } from '@react-navigation/elements'; import * as React from 'react'; import { View } from 'react-native'; - -import KeyboardAvoidingView from '../components/keyboard-avoiding-view.react.js'; +import { KeyboardAvoidingView } from 'react-native-keyboard-controller'; type ViewProps = React.ElementConfig; type Props = ViewProps; diff --git a/native/components/keyboard-avoiding-view.react.js b/native/components/keyboard-avoiding-view.react.js deleted file mode 100644 --- a/native/components/keyboard-avoiding-view.react.js +++ /dev/null @@ -1,199 +0,0 @@ -// @flow - -import invariant from 'invariant'; -import * as React from 'react'; -import { - View, - Keyboard, - Platform, - LayoutAnimation, - StyleSheet, -} from 'react-native'; - -import { - type KeyboardState, - KeyboardContext, -} from '../keyboard/keyboard-state.js'; -import type { ScreenRect } from '../keyboard/keyboard.js'; -import type { - LayoutRectangle, - LayoutChangeEvent, - EventSubscription, - KeyboardEvent, -} from '../types/react-native.js'; -import type { ViewStyle } from '../types/styles.js'; - -type ViewProps = React.ElementConfig; -type BaseProps = { - ...ViewProps, - +behavior: 'height' | 'position' | 'padding', - +contentContainerStyle?: ?ViewStyle, -}; -const KeyboardAvoidingView: React.ComponentType = React.memo( - function KeyboardAvoidingView(props: BaseProps) { - const keyboardState = React.useContext(KeyboardContext); - return ( - - ); - }, -); - -type Props = { - ...BaseProps, - // withKeyboardState - +keyboardState: ?KeyboardState, -}; -type State = { - +bottom: number, -}; -class InnerKeyboardAvoidingView extends React.PureComponent { - state: State = { - bottom: 0, - }; - subscriptions: EventSubscription[] = []; - viewFrame: ?LayoutRectangle; - keyboardFrame: ?ScreenRect; - defaultViewFrameHeight = 0; - waitingForLayout: Array<() => mixed> = []; - - componentDidMount() { - if (Platform.OS === 'ios') { - this.subscriptions.push( - Keyboard.addListener('keyboardWillChangeFrame', this.onKeyboardChange), - ); - } else { - this.subscriptions.push( - Keyboard.addListener('keyboardDidHide', this.onKeyboardChange), - Keyboard.addListener('keyboardDidShow', this.onKeyboardChange), - ); - } - } - - componentWillUnmount() { - for (const subscription of this.subscriptions) { - subscription.remove(); - } - } - - onKeyboardChange = (event: ?KeyboardEvent) => { - if (!event) { - this.keyboardFrame = null; - this.setState({ bottom: 0 }); - return; - } - - if (!this.viewFrame) { - this.waitingForLayout.push(() => this.onKeyboardChange(event)); - return; - } - - const { duration, easing, endCoordinates } = event; - this.keyboardFrame = endCoordinates; - - const { keyboardState } = this.props; - const mediaGalleryOpen = keyboardState && keyboardState.mediaGalleryOpen; - if ( - Platform.OS === 'android' && - mediaGalleryOpen && - this.keyboardFrame.height > 0 && - this.viewFrame - ) { - this.viewFrame = { - ...this.viewFrame, - height: this.defaultViewFrameHeight, - }; - } - - const height = this.relativeKeyboardHeight; - if (height === this.state.bottom) { - return; - } - this.setState({ bottom: height }); - - if (duration && easing) { - LayoutAnimation.configureNext({ - duration: duration > 10 ? duration : 10, - update: { - duration: duration > 10 ? duration : 10, - type: LayoutAnimation.Types[easing] || 'keyboard', - }, - }); - } - }; - - get relativeKeyboardHeight(): number { - const { viewFrame, keyboardFrame } = this; - if (!viewFrame || !keyboardFrame || keyboardFrame.screenY === 0) { - return 0; - } - return Math.max(viewFrame.y + viewFrame.height - keyboardFrame.screenY, 0); - } - - onLayout = (event: LayoutChangeEvent) => { - this.viewFrame = event.nativeEvent.layout; - - const { keyboardState } = this.props; - const keyboardShowing = keyboardState && keyboardState.keyboardShowing; - if (!keyboardShowing) { - this.defaultViewFrameHeight = this.viewFrame.height; - } - - for (const callback of this.waitingForLayout) { - callback(); - } - this.waitingForLayout = []; - }; - - // ESLint doesn't recognize that invariant always throws - // eslint-disable-next-line consistent-return - render(): React.Node { - const { - behavior, - children, - contentContainerStyle, - style, - keyboardState, - ...props - } = this.props; - const { bottom } = this.state; - if (behavior === 'height') { - let heightStyle; - if (this.viewFrame && bottom > 0) { - heightStyle = { - height: this.defaultViewFrameHeight - bottom, - flex: 0, - }; - } - const composedStyle = StyleSheet.compose(style, heightStyle); - return ( - - {children} - - ); - } else if (behavior === 'position') { - const composedStyle = StyleSheet.compose(contentContainerStyle, { - bottom, - }); - const { pointerEvents } = props; - return ( - - - {children} - - - ); - } else if (behavior === 'padding') { - const composedStyle = StyleSheet.compose(style, { - paddingBottom: bottom, - }); - return ( - - {children} - - ); - } - invariant(false, `invalid KeyboardAvoidingView behavior ${behavior}`); - } -} - -export default KeyboardAvoidingView; diff --git a/native/components/modal.react.js b/native/components/modal.react.js --- a/native/components/modal.react.js +++ b/native/components/modal.react.js @@ -3,9 +3,9 @@ import { useNavigation } from '@react-navigation/native'; import * as React from 'react'; import { View, TouchableWithoutFeedback, StyleSheet } from 'react-native'; +import { KeyboardAvoidingView } from 'react-native-keyboard-controller'; import { SafeAreaView } from 'react-native-safe-area-context'; -import KeyboardAvoidingView from './keyboard-avoiding-view.react.js'; import { useStyles } from '../themes/colors.js'; import type { ViewStyle } from '../types/styles.js'; import { diff --git a/native/ios/Podfile.lock b/native/ios/Podfile.lock --- a/native/ios/Podfile.lock +++ b/native/ios/Podfile.lock @@ -2089,6 +2089,35 @@ - SocketRocket - react-native-in-app-message (1.0.2): - React + - react-native-keyboard-controller (1.18.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-hermes + - React-ImageManager + - React-jsi + - React-NativeModulesApple + - React-RCTFabric + - React-renderercss + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - SocketRocket + - Yoga - react-native-netinfo (11.4.1): - React-Core - react-native-orientation-locker (1.5.0): @@ -3041,6 +3070,7 @@ - React-Mapbuffer (from `../../node_modules/react-native/ReactCommon`) - React-microtasksnativemodule (from `../../node_modules/react-native/ReactCommon/react/nativemodule/microtasks`) - react-native-in-app-message (from `../node_modules/react-native-in-app-message`) + - react-native-keyboard-controller (from `../node_modules/react-native-keyboard-controller`) - "react-native-netinfo (from `../node_modules/@react-native-community/netinfo`)" - react-native-orientation-locker (from `../node_modules/react-native-orientation-locker`) - react-native-pager-view (from `../node_modules/react-native-pager-view`) @@ -3247,6 +3277,8 @@ :path: "../../node_modules/react-native/ReactCommon/react/nativemodule/microtasks" react-native-in-app-message: :path: "../node_modules/react-native-in-app-message" + react-native-keyboard-controller: + :path: "../node_modules/react-native-keyboard-controller" react-native-netinfo: :path: "../node_modules/@react-native-community/netinfo" react-native-orientation-locker: @@ -3428,6 +3460,7 @@ React-Mapbuffer: 9a7c65078c6851397c1999068989e4fc239d0c80 React-microtasksnativemodule: 4f1ef719ba6c7ebbd2d75346ffa2916f9b4771c9 react-native-in-app-message: f91de5009620af01456531118264c93e249b83ec + react-native-keyboard-controller: 67ade97aef69b23d03c4e46ef0889de00b6056a2 react-native-netinfo: f0a9899081c185db1de5bb2fdc1c88c202a059ac react-native-orientation-locker: 851f6510d8046ea2f14aa169b1e01fcd309a94ba react-native-pager-view: fb71fc7749da79d194e0e3e455d02fc0211c0042 diff --git a/native/package.json b/native/package.json --- a/native/package.json +++ b/native/package.json @@ -100,6 +100,7 @@ "react-native-fs": "^2.20.0", "react-native-gesture-handler": "~2.26.0", "react-native-in-app-message": "^1.0.2", + "react-native-keyboard-controller": "^1.17.5", "react-native-keyboard-input": "6.0.1", "react-native-keychain": "^8.0.0", "react-native-orientation-locker": "^1.5.0", diff --git a/native/profile/profile.react.js b/native/profile/profile.react.js --- a/native/profile/profile.react.js +++ b/native/profile/profile.react.js @@ -9,6 +9,7 @@ import { createStackNavigator } from '@react-navigation/stack'; import * as React from 'react'; import { View, useWindowDimensions } from 'react-native'; +import { KeyboardAvoidingView } from 'react-native-keyboard-controller'; import AddKeyserver from './add-keyserver.react.js'; import AppearancePreferences from './appearance-preferences.react.js'; @@ -30,7 +31,6 @@ import ProfileScreen from './profile-screen.react.js'; import RelationshipList from './relationship-list.react.js'; import TunnelbrokerMenu from './tunnelbroker-menu.react.js'; -import KeyboardAvoidingView from '../components/keyboard-avoiding-view.react.js'; import CommunityDrawerButton from '../navigation/community-drawer-button.react.js'; import HeaderBackButton from '../navigation/header-back-button.react.js'; import { diff --git a/native/root.react.js b/native/root.react.js --- a/native/root.react.js +++ b/native/root.react.js @@ -15,6 +15,7 @@ import * as React from 'react'; import { StyleSheet } from 'react-native'; import { GestureHandlerRootView } from 'react-native-gesture-handler'; +import { KeyboardProvider } from 'react-native-keyboard-controller'; import Orientation from 'react-native-orientation-locker'; import { SafeAreaProvider, @@ -353,101 +354,103 @@ } return ( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {gated} - - - - - - - - - - - - - - - - {navigation} - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {gated} + + + + + + + + + + + + + + + + {navigation} + + + + + + + + + + + + + + + + + + + + + + + + + + ); } diff --git a/yarn.lock b/yarn.lock --- a/yarn.lock +++ b/yarn.lock @@ -21386,6 +21386,13 @@ resolved "https://registry.yarnpkg.com/react-native-is-edge-to-edge/-/react-native-is-edge-to-edge-1.2.1.tgz#64e10851abd9d176cbf2b40562f751622bde3358" integrity sha512-FLbPWl/MyYQWz+KwqOZsSyj2JmLKglHatd3xLZWskXOpRaio4LfEDEz8E/A6uD8QoTHW6Aobw1jbEwK7KMgR7Q== +react-native-keyboard-controller@^1.17.5: + version "1.18.1" + resolved "https://registry.yarnpkg.com/react-native-keyboard-controller/-/react-native-keyboard-controller-1.18.1.tgz#aca911f3321925dbff152bc2029fb62fa0b5046c" + integrity sha512-5zBRLNq4BDmMIs7G/fe4udcmCu9uxnRIQ+WciGPwL2TGaIrAC+mngiFzPO8rm8DzN1ghDrDPzmy8wV2GEwYLMw== + dependencies: + react-native-is-edge-to-edge "^1.2.1" + react-native-keyboard-input@6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/react-native-keyboard-input/-/react-native-keyboard-input-6.0.1.tgz#996a0c00c2232b09e30cdee37dd8c086c688e1f7"