diff --git a/src/App.js b/src/App.js
index 8b794f5e20d8..581012838973 100644
--- a/src/App.js
+++ b/src/App.js
@@ -15,6 +15,7 @@ import ComposeProviders from './components/ComposeProviders';
import SafeArea from './components/SafeArea';
import * as Environment from './libs/Environment/Environment';
import {WindowDimensionsProvider} from './components/withWindowDimensions';
+import {KeyboardStateProvider} from './components/withKeyboardState';
// For easier debugging and development, when we are in web we expose Onyx to the window, so you can more easily set data into Onyx
if (window && Environment.isDevelopment()) {
@@ -41,6 +42,7 @@ const App = () => (
LocaleContextProvider,
HTMLEngineProvider,
WindowDimensionsProvider,
+ KeyboardStateProvider,
]}
>
diff --git a/src/components/HeaderWithCloseButton.js b/src/components/HeaderWithCloseButton.js
index 27ab5dbbe890..ee1970bca37d 100755
--- a/src/components/HeaderWithCloseButton.js
+++ b/src/components/HeaderWithCloseButton.js
@@ -9,14 +9,14 @@ import Navigation from '../libs/Navigation/Navigation';
import ROUTES from '../ROUTES';
import Icon from './Icon';
import * as Expensicons from './Icon/Expensicons';
-import withLocalize, {withLocalizePropTypes} from './withLocalize';
import Tooltip from './Tooltip';
-import ThreeDotsMenu, {ThreeDotsMenuItemPropTypes} from './ThreeDotsMenu';
-import VirtualKeyboard from '../libs/VirtualKeyboard';
import getButtonState from '../libs/getButtonState';
import * as StyleUtils from '../styles/StyleUtils';
-import withDelayToggleButtonState, {withDelayToggleButtonStatePropTypes} from './withDelayToggleButtonState';
import compose from '../libs/compose';
+import ThreeDotsMenu, {ThreeDotsMenuItemPropTypes} from './ThreeDotsMenu';
+import withDelayToggleButtonState, {withDelayToggleButtonStatePropTypes} from './withDelayToggleButtonState';
+import withLocalize, {withLocalizePropTypes} from './withLocalize';
+import withKeyboardState, {keyboardStatePropTypes} from './withKeyboardState';
const propTypes = {
/** Title of the Header */
@@ -79,8 +79,8 @@ const propTypes = {
}),
...withLocalizePropTypes,
-
...withDelayToggleButtonStatePropTypes,
+ ...keyboardStatePropTypes,
};
const defaultProps = {
@@ -142,7 +142,7 @@ class HeaderWithCloseButton extends Component {
{
- if (VirtualKeyboard.isOpen()) {
+ if (this.props.isKeyboardShown) {
Keyboard.dismiss();
}
this.props.onBackButtonPress();
@@ -224,4 +224,5 @@ HeaderWithCloseButton.defaultProps = defaultProps;
export default compose(
withLocalize,
withDelayToggleButtonState,
+ withKeyboardState,
)(HeaderWithCloseButton);
diff --git a/src/components/PDFView/index.native.js b/src/components/PDFView/index.native.js
index 46474eb73705..1029750b9ca5 100644
--- a/src/components/PDFView/index.native.js
+++ b/src/components/PDFView/index.native.js
@@ -45,7 +45,7 @@ class PDFView extends Component {
}
componentDidUpdate() {
- this.props.onToggleKeyboard(this.props.isShown);
+ this.props.onToggleKeyboard(this.props.isKeyboardShown);
}
handleFailureToLoadPDF(error) {
diff --git a/src/components/PDFView/pdfViewPropTypes.js b/src/components/PDFView/pdfViewPropTypes.js
index 281c135fe438..5f2c7e381dd0 100644
--- a/src/components/PDFView/pdfViewPropTypes.js
+++ b/src/components/PDFView/pdfViewPropTypes.js
@@ -1,6 +1,7 @@
import PropTypes from 'prop-types';
import stylePropTypes from '../../styles/stylePropTypes';
import {windowDimensionsPropTypes} from '../withWindowDimensions';
+import {keyboardStatePropTypes} from '../withKeyboardState';
const propTypes = {
/** URL to full-sized image */
@@ -13,6 +14,7 @@ const propTypes = {
onToggleKeyboard: PropTypes.func,
...windowDimensionsPropTypes,
+ ...keyboardStatePropTypes,
};
const defaultProps = {
diff --git a/src/components/withKeyboardState.js b/src/components/withKeyboardState.js
index b1159bdb376d..345ff5bb8f92 100755
--- a/src/components/withKeyboardState.js
+++ b/src/components/withKeyboardState.js
@@ -1,64 +1,79 @@
-import React, {Component} from 'react';
+/* eslint-disable react/no-unused-state */
+import React, {forwardRef, createContext} from 'react';
import PropTypes from 'prop-types';
import {Keyboard} from 'react-native';
import getComponentDisplayName from '../libs/getComponentDisplayName';
-const withKeyboardStatePropTypes = {
- /** Returns whether keyboard is open */
- isShown: PropTypes.bool.isRequired,
+const KeyboardStateContext = createContext(null);
+const keyboardStatePropTypes = {
+ /** Whether or not the keyboard is open */
+ isKeyboardShown: PropTypes.bool.isRequired,
};
-export default function withKeyboardState(WrappedComponent) {
- const WithKeyboardState = class extends Component {
- constructor(props) {
- super(props);
- this.state = {
- isShown: false,
- };
- }
+const keyboardStateProviderPropTypes = {
+ /* Actual content wrapped by this component */
+ children: PropTypes.node.isRequired,
+};
+
+class KeyboardStateProvider extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ isKeyboardShown: false,
+ };
+ }
- componentDidMount() {
- this.keyboardDidShowListener = Keyboard.addListener(
- 'keyboardDidShow',
- () => {
- this.setState({isShown: true});
- },
- );
- this.keyboardDidHideListener = Keyboard.addListener(
- 'keyboardDidHide',
- () => {
- this.setState({isShown: false});
- },
- );
- }
+ componentDidMount() {
+ this.keyboardDidShowListener = Keyboard.addListener(
+ 'keyboardDidShow',
+ () => {
+ this.setState({isKeyboardShown: true});
+ },
+ );
+ this.keyboardDidHideListener = Keyboard.addListener(
+ 'keyboardDidHide',
+ () => {
+ this.setState({isKeyboardShown: false});
+ },
+ );
+ }
- componentWillUnmount() {
- this.keyboardDidShowListener.remove();
- this.keyboardDidHideListener.remove();
- }
+ componentWillUnmount() {
+ this.keyboardDidShowListener.remove();
+ this.keyboardDidHideListener.remove();
+ }
- render() {
- // eslint-disable-next-line react/jsx-props-no-spreading
- return ;
- }
- };
+ render() {
+ return (
+
+ {this.props.children}
+
+ );
+ }
+}
+
+KeyboardStateProvider.propTypes = keyboardStateProviderPropTypes;
- WithKeyboardState.displayName = `WithKeyboardState(${getComponentDisplayName(WrappedComponent)})`;
- WithKeyboardState.propTypes = {
- forwardedRef: PropTypes.oneOfType([
- PropTypes.func,
- PropTypes.shape({current: PropTypes.instanceOf(React.Component)}),
- ]),
- };
- WithKeyboardState.defaultProps = {
- forwardedRef: undefined,
- };
- return React.forwardRef((props, ref) => (
- // eslint-disable-next-line react/jsx-props-no-spreading
-
+/**
+ * @param {React.Component} WrappedComponent
+ * @returns {React.Component}
+ */
+export default function withKeyboardState(WrappedComponent) {
+ const WithKeyboardState = forwardRef((props, ref) => (
+
+ {keyboardStateProps => (
+ // eslint-disable-next-line react/jsx-props-no-spreading
+
+ )}
+
));
+
+ WithKeyboardState.displayName = `withKeyboardState(${getComponentDisplayName(WrappedComponent)})`;
+ return WithKeyboardState;
}
export {
- withKeyboardStatePropTypes,
+ KeyboardStateProvider,
+ keyboardStatePropTypes,
};
diff --git a/src/libs/VirtualKeyboard/index.js b/src/libs/VirtualKeyboard/index.js
deleted file mode 100644
index 9b86a124efdc..000000000000
--- a/src/libs/VirtualKeyboard/index.js
+++ /dev/null
@@ -1,31 +0,0 @@
-import _ from 'underscore';
-import * as Browser from '../Browser';
-
-/**
- * Is the virtual keyboard open?
- *
- * @returns {Boolean|null} – null if the VirtualKeyboard API is unavailable
- */
-function isOpen() {
- if (!_.has(navigator, 'virtualKeyboard')) {
- return null;
- }
- return navigator.virtualKeyboard.boundingRect.y > 0;
-}
-
-/**
- * As of January 2022, the VirtualKeyboard web API is not available in all browsers yet
- * If it is unavailable, we default to assuming that the virtual keyboard is open on mobile devices.
- * See https://github.com/Expensify/App/issues/6767 for additional context.
- *
- * @returns {Boolean}
- */
-function shouldAssumeIsOpen() {
- const isOpened = isOpen();
- return _.isNull(isOpened) ? Browser.isMobile() : isOpened;
-}
-
-export default {
- isOpen,
- shouldAssumeIsOpen,
-};
diff --git a/src/libs/VirtualKeyboard/index.native.js b/src/libs/VirtualKeyboard/index.native.js
deleted file mode 100644
index dc3905f3d599..000000000000
--- a/src/libs/VirtualKeyboard/index.native.js
+++ /dev/null
@@ -1,33 +0,0 @@
-import {Keyboard} from 'react-native';
-
-let isVirtualKeyboardOpen = false;
-
-Keyboard.addListener(
- 'keyboardDidShow',
- () => {
- isVirtualKeyboardOpen = true;
- },
-);
-
-Keyboard.addListener(
- 'keyboardDidHide',
- () => {
- isVirtualKeyboardOpen = false;
- },
-);
-
-/**
- * Is the virtual keyboard open?
- *
- * Note – the web equivalent of this function may return null.
- *
- * @returns {Boolean}
- */
-function isOpen() {
- return isVirtualKeyboardOpen;
-}
-
-export default {
- isOpen,
- shouldAssumeIsOpen: isOpen,
-};
diff --git a/src/pages/home/report/ReportActionCompose.js b/src/pages/home/report/ReportActionCompose.js
index 7811d1c25ee1..35149943c78a 100644
--- a/src/pages/home/report/ReportActionCompose.js
+++ b/src/pages/home/report/ReportActionCompose.js
@@ -21,11 +21,8 @@ import ReportTypingIndicator from './ReportTypingIndicator';
import AttachmentModal from '../../../components/AttachmentModal';
import compose from '../../../libs/compose';
import PopoverMenu from '../../../components/PopoverMenu';
-import withWindowDimensions, {windowDimensionsPropTypes} from '../../../components/withWindowDimensions';
-import withDrawerState from '../../../components/withDrawerState';
import CONST from '../../../CONST';
import canFocusInputOnScreenFocus from '../../../libs/canFocusInputOnScreenFocus';
-import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize';
import Permissions from '../../../libs/Permissions';
import Navigation from '../../../libs/Navigation/Navigation';
import ROUTES from '../../../ROUTES';
@@ -39,7 +36,6 @@ import {withNetwork, withPersonalDetails} from '../../../components/OnyxProvider
import * as User from '../../../libs/actions/User';
import Tooltip from '../../../components/Tooltip';
import EmojiPickerButton from '../../../components/EmojiPicker/EmojiPickerButton';
-import VirtualKeyboard from '../../../libs/VirtualKeyboard';
import canUseTouchScreen from '../../../libs/canUseTouchscreen';
import toggleReportActionComposeView from '../../../libs/toggleReportActionComposeView';
import OfflineIndicator from '../../../components/OfflineIndicator';
@@ -49,6 +45,10 @@ import * as EmojiUtils from '../../../libs/EmojiUtils';
import reportPropTypes from '../../reportPropTypes';
import ReportDropUI from './ReportDropUI';
import DragAndDrop from '../../../components/DragAndDrop';
+import withWindowDimensions, {windowDimensionsPropTypes} from '../../../components/withWindowDimensions';
+import withDrawerState from '../../../components/withDrawerState';
+import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize';
+import withKeyboardState, {keyboardStatePropTypes} from '../../../components/withKeyboardState';
const propTypes = {
/** Beta features list */
@@ -99,6 +99,7 @@ const propTypes = {
...windowDimensionsPropTypes,
...withLocalizePropTypes,
...withCurrentUserPersonalDetailsPropTypes,
+ ...keyboardStatePropTypes,
};
const defaultProps = {
@@ -430,7 +431,8 @@ class ReportActionCompose extends React.Component {
* @param {Object} e
*/
triggerHotkeyActions(e) {
- if (!e || VirtualKeyboard.shouldAssumeIsOpen()) {
+ // Do not trigger actions for mobileWeb or native clients that have the keyboard open because for those devices, we want the return key to insert newlines rather than submit the form
+ if (!e || this.props.isSmallScreenWidth || this.props.isKeyboardShown) {
return;
}
@@ -741,6 +743,7 @@ export default compose(
withNetwork(),
withPersonalDetails(),
withCurrentUserPersonalDetails,
+ withKeyboardState,
withOnyx({
betas: {
key: ONYXKEYS.BETAS,
diff --git a/src/pages/home/report/ReportActionItemMessageEdit.js b/src/pages/home/report/ReportActionItemMessageEdit.js
index 67264ea02ed6..0f65c3634025 100644
--- a/src/pages/home/report/ReportActionItemMessageEdit.js
+++ b/src/pages/home/report/ReportActionItemMessageEdit.js
@@ -10,18 +10,18 @@ import Composer from '../../../components/Composer';
import * as Report from '../../../libs/actions/Report';
import * as ReportScrollManager from '../../../libs/ReportScrollManager';
import toggleReportActionComposeView from '../../../libs/toggleReportActionComposeView';
-import withWindowDimensions, {windowDimensionsPropTypes} from '../../../components/withWindowDimensions';
-import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize';
import Button from '../../../components/Button';
import ReportActionComposeFocusManager from '../../../libs/ReportActionComposeFocusManager';
import compose from '../../../libs/compose';
import EmojiPickerButton from '../../../components/EmojiPicker/EmojiPickerButton';
import * as ReportActionContextMenu from './ContextMenu/ReportActionContextMenu';
-import VirtualKeyboard from '../../../libs/VirtualKeyboard';
import * as EmojiUtils from '../../../libs/EmojiUtils';
import reportPropTypes from '../../reportPropTypes';
import ExceededCommentLength from '../../../components/ExceededCommentLength';
import CONST from '../../../CONST';
+import withWindowDimensions, {windowDimensionsPropTypes} from '../../../components/withWindowDimensions';
+import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize';
+import withKeyboardState, {keyboardStatePropTypes} from '../../../components/withKeyboardState';
const propTypes = {
/** All the data of the action */
@@ -43,14 +43,12 @@ const propTypes = {
// eslint-disable-next-line react/no-unused-prop-types
report: reportPropTypes,
- // Whether or not the emoji picker is disabled
+ /** Whether or not the emoji picker is disabled */
shouldDisableEmojiPicker: PropTypes.bool,
- /** Window Dimensions Props */
- ...windowDimensionsPropTypes,
-
- /** Localization props */
...withLocalizePropTypes,
+ ...windowDimensionsPropTypes,
+ ...keyboardStatePropTypes,
};
const defaultProps = {
@@ -203,7 +201,8 @@ class ReportActionItemMessageEdit extends React.Component {
* @param {Event} e
*/
triggerSaveOrCancel(e) {
- if (!e || VirtualKeyboard.shouldAssumeIsOpen()) {
+ // Do not trigger actions for mobileWeb or native clients that have the keyboard open because for those devices, we want the return key to insert newlines rather than submit the form
+ if (!e || this.props.isSmallScreenWidth || this.props.isKeyboardShown) {
return;
}
if (e.key === 'Enter' && !e.shiftKey) {
@@ -241,7 +240,7 @@ class ReportActionItemMessageEdit extends React.Component {
onFocus={() => {
this.setState({isFocused: true});
ReportScrollManager.scrollToIndex({animated: true, index: this.props.index}, true);
- toggleReportActionComposeView(false, VirtualKeyboard.shouldAssumeIsOpen());
+ toggleReportActionComposeView(false, this.props.isSmallScreenWidth);
}}
onBlur={(event) => {
// Return to prevent re-render when save/cancel button is pressed which cancels the onPress event by re-rendering
@@ -249,7 +248,7 @@ class ReportActionItemMessageEdit extends React.Component {
return;
}
this.setState({isFocused: false});
- toggleReportActionComposeView(true, VirtualKeyboard.shouldAssumeIsOpen());
+ toggleReportActionComposeView(true, this.props.isSmallScreenWidth);
}}
selection={this.state.selection}
onSelectionChange={this.onSelectionChange}
@@ -293,6 +292,7 @@ ReportActionItemMessageEdit.defaultProps = defaultProps;
export default compose(
withLocalize,
withWindowDimensions,
+ withKeyboardState,
)(React.forwardRef((props, ref) => (
/* eslint-disable-next-line react/jsx-props-no-spreading */
diff --git a/src/pages/signin/SignInPageLayout/SignInPageContent.js b/src/pages/signin/SignInPageLayout/SignInPageContent.js
index c692c93c868e..5f3ccb8a268a 100755
--- a/src/pages/signin/SignInPageLayout/SignInPageContent.js
+++ b/src/pages/signin/SignInPageLayout/SignInPageContent.js
@@ -10,7 +10,7 @@ import TermsAndLicenses from '../TermsAndLicenses';
import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize';
import SignInPageForm from '../../../components/SignInPageForm';
import compose from '../../../libs/compose';
-import withKeyboardState from '../../../components/withKeyboardState';
+import withKeyboardState, {keyboardStatePropTypes} from '../../../components/withKeyboardState';
import withWindowDimensions, {windowDimensionsPropTypes} from '../../../components/withWindowDimensions';
import KeyboardAvoidingView from '../../../components/KeyboardAvoidingView';
@@ -27,12 +27,12 @@ const propTypes = {
...withLocalizePropTypes,
...windowDimensionsPropTypes,
+ ...keyboardStatePropTypes,
};
const SignInPageContent = (props) => {
const dismissKeyboardWhenTappedOutsideOfInput = () => {
- // This prop comes from withKeyboardState
- if (!props.isShown) {
+ if (!props.isKeyboardShown) {
return;
}
Keyboard.dismiss();