From 82fbeb076525e2d1fb1c9bf5ca06a23798cd3e6b Mon Sep 17 00:00:00 2001 From: Robert Kozik Date: Thu, 22 Jun 2023 15:28:09 +0200 Subject: [PATCH 01/17] fix parseReportRouteParams so it can be used with getActiveRoute function --- src/ROUTES.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/ROUTES.js b/src/ROUTES.js index a5fd5b4188c3..e2b2ba362ae4 100644 --- a/src/ROUTES.js +++ b/src/ROUTES.js @@ -173,11 +173,17 @@ export default { * @returns {Object} */ parseReportRouteParams: (route) => { - if (!route.startsWith(Url.addTrailingForwardSlash(REPORT))) { + let parsingRoute = route; + if (parsingRoute.at(0) === '/') { + // remove the first slash + parsingRoute = parsingRoute.slice(1); + } + + if (!parsingRoute.startsWith(Url.addTrailingForwardSlash(REPORT))) { return {reportID: '', isSubReportPageRoute: false}; } - const pathSegments = route.split('/'); + const pathSegments = parsingRoute.split('/'); return { reportID: lodashGet(pathSegments, 1), isSubReportPageRoute: Boolean(lodashGet(pathSegments, 2)), From 81695ef7f9090a77780d1a39f8f9aa9367bb7e02 Mon Sep 17 00:00:00 2001 From: Robert Kozik Date: Thu, 22 Jun 2023 15:29:11 +0200 Subject: [PATCH 02/17] create KeyDownPress listner lib --- src/libs/KeyboardShortcut/KeyDownPressListener/index.js | 9 +++++++++ .../KeyDownPressListener/index.native.js | 4 ++++ 2 files changed, 13 insertions(+) create mode 100644 src/libs/KeyboardShortcut/KeyDownPressListener/index.js create mode 100644 src/libs/KeyboardShortcut/KeyDownPressListener/index.native.js diff --git a/src/libs/KeyboardShortcut/KeyDownPressListener/index.js b/src/libs/KeyboardShortcut/KeyDownPressListener/index.js new file mode 100644 index 000000000000..4401beef1c59 --- /dev/null +++ b/src/libs/KeyboardShortcut/KeyDownPressListener/index.js @@ -0,0 +1,9 @@ +function addKeyDownPressListner(callbackFunction) { + document.addEventListener('keydown', callbackFunction); +} + +function removeKeyDownPressListner(callbackFunction) { + document.removeEventListener('keydown', callbackFunction); +} + +export {addKeyDownPressListner, removeKeyDownPressListner}; diff --git a/src/libs/KeyboardShortcut/KeyDownPressListener/index.native.js b/src/libs/KeyboardShortcut/KeyDownPressListener/index.native.js new file mode 100644 index 000000000000..aa1ded824d22 --- /dev/null +++ b/src/libs/KeyboardShortcut/KeyDownPressListener/index.native.js @@ -0,0 +1,4 @@ +function addKeyDownPressListner() {} +function removeKeyDownPressListner() {} + +export {addKeyDownPressListner, removeKeyDownPressListner}; From 4764004dd94eb2727efe6aac75acec8ec738b4e6 Mon Sep 17 00:00:00 2001 From: Robert Kozik Date: Thu, 22 Jun 2023 15:29:37 +0200 Subject: [PATCH 03/17] create isEmojiPickerVisible function --- src/libs/actions/EmojiPickerAction.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/libs/actions/EmojiPickerAction.js b/src/libs/actions/EmojiPickerAction.js index 9d4ef0b2e98c..484d0b55ca24 100644 --- a/src/libs/actions/EmojiPickerAction.js +++ b/src/libs/actions/EmojiPickerAction.js @@ -45,4 +45,11 @@ function isActiveReportAction(actionID) { return emojiPickerRef.current.isActiveReportAction(actionID); } -export {emojiPickerRef, showEmojiPicker, hideEmojiPicker, isActiveReportAction}; +function isEmojiPickerVisible() { + if (!emojiPickerRef.current) { + return; + } + return emojiPickerRef.current.state.isEmojiPickerVisible; +} + +export {emojiPickerRef, showEmojiPicker, hideEmojiPicker, isActiveReportAction, isEmojiPickerVisible}; From 0a5873cc03b2eac6e71c838641f5d792c9b88404 Mon Sep 17 00:00:00 2001 From: Robert Kozik Date: Thu, 22 Jun 2023 15:30:21 +0200 Subject: [PATCH 04/17] add keyDown and paste listners docuemnt-scope in order to focus composer --- src/components/Composer/index.js | 24 +++++++- src/pages/home/report/ReportActionCompose.js | 65 +++++++++++++++++--- 2 files changed, 79 insertions(+), 10 deletions(-) diff --git a/src/components/Composer/index.js b/src/components/Composer/index.js index eab4abe8a6fe..030f3e8eaea1 100755 --- a/src/components/Composer/index.js +++ b/src/components/Composer/index.js @@ -77,6 +77,9 @@ const propTypes = { /** Should we calculate the caret position */ shouldCalculateCaretPosition: PropTypes.bool, + /** Function to check whether composer is covered up or not */ + checkComposerVisibility: PropTypes.func, + ...withLocalizePropTypes, ...windowDimensionsPropTypes, @@ -104,6 +107,7 @@ const defaultProps = { setIsFullComposerAvailable: () => {}, isComposerFullSize: false, shouldCalculateCaretPosition: false, + checkComposerVisibility: () => false, }; const IMAGE_EXTENSIONS = { @@ -133,6 +137,7 @@ class Composer extends React.Component { end: initialValue.length, }, valueBeforeCaret: '', + isFocused: false, }; this.paste = this.paste.bind(this); @@ -159,7 +164,7 @@ class Composer extends React.Component { // There is no onPaste or onDrag for TextInput in react-native so we will add event // listeners here and unbind when the component unmounts if (this.textInput) { - this.textInput.addEventListener('paste', this.handlePaste); + document.addEventListener('paste', this.handlePaste); this.textInput.addEventListener('wheel', this.handleWheel); } } @@ -262,6 +267,7 @@ class Composer extends React.Component { */ paste(text) { try { + this.textInput.focus(); document.execCommand('insertText', false, text); this.updateNumberOfLines(); @@ -289,6 +295,10 @@ class Composer extends React.Component { * @param {ClipboardEvent} event */ handlePaste(event) { + if (!this.props.checkComposerVisibility() && !this.state.isFocused) { + return; + } + event.preventDefault(); const {files, types} = event.clipboardData; @@ -461,6 +471,18 @@ class Composer extends React.Component { numberOfLines={this.state.numberOfLines} disabled={this.props.isDisabled} onKeyPress={this.handleKeyPress} + onFocus={() => { + if (this.props.onFocus) { + this.props.onFocus(); + } + this.setState({isFocused: true}); + }} + onBlur={() => { + if (this.props.onBlur) { + this.props.onBlur(); + } + this.setState({isFocused: false}); + }} /> {this.props.shouldCalculateCaretPosition && renderElementForCaretPosition} diff --git a/src/pages/home/report/ReportActionCompose.js b/src/pages/home/report/ReportActionCompose.js index a1b9a2121fd5..89355f851310 100644 --- a/src/pages/home/report/ReportActionCompose.js +++ b/src/pages/home/report/ReportActionCompose.js @@ -52,6 +52,10 @@ import * as TaskUtils from '../../../libs/actions/Task'; import * as Browser from '../../../libs/Browser'; import * as IOU from '../../../libs/actions/IOU'; import PressableWithFeedback from '../../../components/Pressable/PressableWithFeedback'; +import * as KeyDownListener from '../../../libs/KeyboardShortcut/KeyDownPressListener'; +import * as EmojiPickerActions from '../../../libs/actions/EmojiPickerAction'; +import Navigation from '../../../libs/Navigation/Navigation'; +import ROUTES from '../../../ROUTES'; const propTypes = { /** Beta features list */ @@ -168,7 +172,9 @@ class ReportActionCompose extends React.Component { this.setIsFocused = this.setIsFocused.bind(this); this.setIsFullComposerAvailable = this.setIsFullComposerAvailable.bind(this); this.focus = this.focus.bind(this); - this.addEmojiToTextBox = this.addEmojiToTextBox.bind(this); + this.replaceSelectionWithText = this.replaceSelectionWithText.bind(this); + this.focusComposerOnKeyPress = this.focusComposerOnKeyPress.bind(this); + this.checkComposerVisibility = this.checkComposerVisibility.bind(this); this.onSelectionChange = this.onSelectionChange.bind(this); this.isEmojiCode = this.isEmojiCode.bind(this); this.isMentionCode = this.isMentionCode.bind(this); @@ -184,6 +190,8 @@ class ReportActionCompose extends React.Component { this.showPopoverMenu = this.showPopoverMenu.bind(this); this.comment = props.comment; + this.attachmentModalRef = React.createRef(); + // React Native will retain focus on an input for native devices but web/mWeb behave differently so we have some focus management // code that will refocus the compose input after a user closes a modal or some other actions, see usage of ReportActionComposeFocusManager this.willBlurTextInputOnTapOutside = willBlurTextInputOnTapOutside(); @@ -236,6 +244,8 @@ class ReportActionCompose extends React.Component { this.focus(false); }); + KeyDownListener.addKeyDownPressListner(this.focusComposerOnKeyPress); + this.updateComment(this.comment); // Shows Popover Menu on Workspace Chat at first sign-in @@ -270,6 +280,7 @@ class ReportActionCompose extends React.Component { componentWillUnmount() { ReportActionComposeFocusManager.clear(); + KeyDownListener.removeKeyDownPressListner(this.focusComposerOnKeyPress); } onSelectionChange(e) { @@ -658,20 +669,55 @@ class ReportActionCompose extends React.Component { } /** - * Callback for the emoji picker to add whatever emoji is chosen into the main input - * - * @param {String} emoji + * Callback to add whatever text is chosen into the main input (used f.e as callback for the emoji picker) + * @param {String} text */ - addEmojiToTextBox(emoji) { - this.updateComment(ComposerUtils.insertText(this.comment, this.state.selection, emoji)); + replaceSelectionWithText(text) { + this.updateComment(ComposerUtils.insertText(this.comment, this.state.selection, text)); this.setState((prevState) => ({ selection: { - start: prevState.selection.start + emoji.length, - end: prevState.selection.start + emoji.length, + start: prevState.selection.start + text.length, + end: prevState.selection.start + text.length, }, })); } + /** + * Check if the composer is visible. Returns true if the composer is not covered up by emoji picker or menu. False otherwise. + * @returns {Boolean} + */ + checkComposerVisibility() { + const {reportID, isSubReportPageRoute} = ROUTES.parseReportRouteParams(Navigation.getActiveRoute()); + const isMainReportScreen = Boolean(reportID) && !isSubReportPageRoute; + const isComposerCoveredUp = !isMainReportScreen || EmojiPickerActions.isEmojiPickerVisible() || this.state.isMenuVisible; + return !isComposerCoveredUp; + } + + focusComposerOnKeyPress(e) { + const isComposerVisible = this.checkComposerVisibility(); + if (!isComposerVisible) { + return; + } + + // If the key pressed is non-character keys like Enter, Shift, ... do not focus + if (e.key.length > 1) { + return; + } + + // If a key is pressed in combination with Meta, Control or Alt do not focus + if (e.metaKey || e.ctrlKey || e.altKey) { + return; + } + + // if we're typing on another input/text area, do not focus + if (['INPUT', 'TEXTAREA'].includes(e.target.nodeName)) { + return; + } + + this.focus(); + this.replaceSelectionWithText(e.key); + } + /** * Focus the composer text input * @param {Boolean} [shouldelay=false] Impose delay before focusing the composer @@ -1070,6 +1116,7 @@ class ReportActionCompose extends React.Component { disabled={this.props.disabled} > this.checkComposerVisibility()} autoFocus={this.shouldAutoFocus} multiline ref={this.setTextInputRef} @@ -1122,7 +1169,7 @@ class ReportActionCompose extends React.Component { onModalHide={() => { this.focus(true); }} - onEmojiSelected={this.addEmojiToTextBox} + onEmojiSelected={this.replaceSelectionWithText} /> )} Date: Thu, 29 Jun 2023 19:25:18 +0200 Subject: [PATCH 05/17] manage listeners on focus/blur basis --- src/components/Composer/index.js | 30 ++++++++++---------- src/pages/home/report/ReportActionCompose.js | 14 ++++++--- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/src/components/Composer/index.js b/src/components/Composer/index.js index 030f3e8eaea1..d4b6ba4a60f5 100755 --- a/src/components/Composer/index.js +++ b/src/components/Composer/index.js @@ -16,6 +16,7 @@ import styles from '../../styles/styles'; import Text from '../Text'; import isEnterWhileComposition from '../../libs/KeyboardShortcut/isEnterWhileComposition'; import CONST from '../../CONST'; +import withNavigation from '../withNavigation'; const propTypes = { /** Maximum number of lines in the text input */ @@ -137,7 +138,6 @@ class Composer extends React.Component { end: initialValue.length, }, valueBeforeCaret: '', - isFocused: false, }; this.paste = this.paste.bind(this); @@ -148,6 +148,8 @@ class Composer extends React.Component { this.shouldCallUpdateNumberOfLines = this.shouldCallUpdateNumberOfLines.bind(this); this.addCursorPositionToSelectionChange = this.addCursorPositionToSelectionChange.bind(this); this.textRef = React.createRef(null); + this.unsubscribeBlur = () => null; + this.unsubscribeFocus = () => null; } componentDidMount() { @@ -164,8 +166,15 @@ class Composer extends React.Component { // There is no onPaste or onDrag for TextInput in react-native so we will add event // listeners here and unbind when the component unmounts if (this.textInput) { - document.addEventListener('paste', this.handlePaste); this.textInput.addEventListener('wheel', this.handleWheel); + + // we need to handle listeners on navigation focus/blur as Composer is not unmounting + // when navigating away to different report + this.unsubscribeFocus = this.props.navigation.addListener('focus', () => document.addEventListener('paste', this.handlePaste)); + this.unsubscribeBlur = this.props.navigation.addListener('blur', () => document.removeEventListener('paste', this.handlePaste)); + + // focus is not triggered on component mount so we need to add listener manually + document.addEventListener('paste', this.handlePaste); } } @@ -198,7 +207,9 @@ class Composer extends React.Component { return; } - this.textInput.removeEventListener('paste', this.handlePaste); + document.removeEventListener('paste', this.handlePaste); + this.unsubscribeFocus(); + this.unsubscribeBlur(); this.textInput.removeEventListener('wheel', this.handleWheel); } @@ -471,18 +482,6 @@ class Composer extends React.Component { numberOfLines={this.state.numberOfLines} disabled={this.props.isDisabled} onKeyPress={this.handleKeyPress} - onFocus={() => { - if (this.props.onFocus) { - this.props.onFocus(); - } - this.setState({isFocused: true}); - }} - onBlur={() => { - if (this.props.onBlur) { - this.props.onBlur(); - } - this.setState({isFocused: false}); - }} /> {this.props.shouldCalculateCaretPosition && renderElementForCaretPosition} @@ -496,6 +495,7 @@ Composer.defaultProps = defaultProps; export default compose( withLocalize, withWindowDimensions, + withNavigation, )( React.forwardRef((props, ref) => ( null; + this.unsubscribeFocus = () => null; + this.state = { isFocused: this.shouldFocusInputOnScreenFocus && !this.props.modal.isVisible && !this.props.modal.willAlertModalBecomeVisible && this.props.shouldShowComposeInput, isFullComposerAvailable: props.isComposerFullSize, @@ -244,8 +247,10 @@ class ReportActionCompose extends React.Component { this.focus(false); }); + this.unsubscribeFocus = this.props.navigation.addListener('focus', () => KeyDownListener.addKeyDownPressListner(this.focusComposerOnKeyPress)); + this.unsubscribeBlur = this.props.navigation.addListener('blur', () => KeyDownListener.removeKeyDownPressListner(this.focusComposerOnKeyPress)); + KeyDownListener.addKeyDownPressListner(this.focusComposerOnKeyPress); - this.updateComment(this.comment); // Shows Popover Menu on Workspace Chat at first sign-in @@ -284,7 +289,10 @@ class ReportActionCompose extends React.Component { componentWillUnmount() { ReportActionComposeFocusManager.clear(); + KeyDownListener.removeKeyDownPressListner(this.focusComposerOnKeyPress); + this.unsubscribeBlur(); + this.unsubscribeFocus(); } onSelectionChange(e) { @@ -691,9 +699,7 @@ class ReportActionCompose extends React.Component { * @returns {Boolean} */ checkComposerVisibility() { - const {reportID, isSubReportPageRoute} = ROUTES.parseReportRouteParams(Navigation.getActiveRoute()); - const isMainReportScreen = Boolean(reportID) && !isSubReportPageRoute; - const isComposerCoveredUp = !isMainReportScreen || EmojiPickerActions.isEmojiPickerVisible() || this.state.isMenuVisible; + const isComposerCoveredUp = EmojiPickerActions.isEmojiPickerVisible() || this.state.isMenuVisible; return !isComposerCoveredUp; } From 28240d89cd6c2f0114cb1e50e49ec125eba9f732 Mon Sep 17 00:00:00 2001 From: Robert Kozik Date: Thu, 29 Jun 2023 20:08:32 +0200 Subject: [PATCH 06/17] prettier --- src/components/Composer/index.js | 6 +++--- src/pages/home/report/ReportActionCompose.js | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/Composer/index.js b/src/components/Composer/index.js index d4b6ba4a60f5..b5225c4c08f7 100755 --- a/src/components/Composer/index.js +++ b/src/components/Composer/index.js @@ -167,12 +167,12 @@ class Composer extends React.Component { // listeners here and unbind when the component unmounts if (this.textInput) { this.textInput.addEventListener('wheel', this.handleWheel); - + // we need to handle listeners on navigation focus/blur as Composer is not unmounting - // when navigating away to different report + // when navigating away to different report this.unsubscribeFocus = this.props.navigation.addListener('focus', () => document.addEventListener('paste', this.handlePaste)); this.unsubscribeBlur = this.props.navigation.addListener('blur', () => document.removeEventListener('paste', this.handlePaste)); - + // focus is not triggered on component mount so we need to add listener manually document.addEventListener('paste', this.handlePaste); } diff --git a/src/pages/home/report/ReportActionCompose.js b/src/pages/home/report/ReportActionCompose.js index 25561196a562..c47ff004801a 100644 --- a/src/pages/home/report/ReportActionCompose.js +++ b/src/pages/home/report/ReportActionCompose.js @@ -249,7 +249,7 @@ class ReportActionCompose extends React.Component { this.unsubscribeFocus = this.props.navigation.addListener('focus', () => KeyDownListener.addKeyDownPressListner(this.focusComposerOnKeyPress)); this.unsubscribeBlur = this.props.navigation.addListener('blur', () => KeyDownListener.removeKeyDownPressListner(this.focusComposerOnKeyPress)); - + KeyDownListener.addKeyDownPressListner(this.focusComposerOnKeyPress); this.updateComment(this.comment); @@ -289,7 +289,7 @@ class ReportActionCompose extends React.Component { componentWillUnmount() { ReportActionComposeFocusManager.clear(); - + KeyDownListener.removeKeyDownPressListner(this.focusComposerOnKeyPress); this.unsubscribeBlur(); this.unsubscribeFocus(); From 82700682235ceb9e34a9203cb9e49a0e175e5621 Mon Sep 17 00:00:00 2001 From: Robert Kozik Date: Fri, 30 Jun 2023 01:21:48 +0200 Subject: [PATCH 07/17] remove unused imports --- src/pages/home/report/ReportActionCompose.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/pages/home/report/ReportActionCompose.js b/src/pages/home/report/ReportActionCompose.js index c47ff004801a..421893d0bd89 100644 --- a/src/pages/home/report/ReportActionCompose.js +++ b/src/pages/home/report/ReportActionCompose.js @@ -54,8 +54,6 @@ import * as IOU from '../../../libs/actions/IOU'; import PressableWithFeedback from '../../../components/Pressable/PressableWithFeedback'; import * as KeyDownListener from '../../../libs/KeyboardShortcut/KeyDownPressListener'; import * as EmojiPickerActions from '../../../libs/actions/EmojiPickerAction'; -import Navigation from '../../../libs/Navigation/Navigation'; -import ROUTES from '../../../ROUTES'; const propTypes = { /** Beta features list */ From 8ae5a56639c37deb05085b64a7adb0673215b5d7 Mon Sep 17 00:00:00 2001 From: Robert Kozik Date: Fri, 30 Jun 2023 01:41:33 +0200 Subject: [PATCH 08/17] add emojiPicker visiblity state to imperative handle --- src/components/EmojiPicker/EmojiPicker.js | 2 +- src/libs/actions/EmojiPickerAction.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/EmojiPicker/EmojiPicker.js b/src/components/EmojiPicker/EmojiPicker.js index 35a96eb38477..1eb4b5a5c496 100644 --- a/src/components/EmojiPicker/EmojiPicker.js +++ b/src/components/EmojiPicker/EmojiPicker.js @@ -130,7 +130,7 @@ const EmojiPicker = forwardRef((props, ref) => { */ const isActiveReportAction = (actionID) => Boolean(actionID) && reportAction.reportActionID === actionID; - useImperativeHandle(ref, () => ({showEmojiPicker, isActiveReportAction, hideEmojiPicker})); + useImperativeHandle(ref, () => ({showEmojiPicker, isActiveReportAction, hideEmojiPicker, isEmojiPickerVisible})); // There is no way to disable animations, and they are really laggy, because there are so many // emojis. The best alternative is to set it to 1ms so it just "pops" in and out diff --git a/src/libs/actions/EmojiPickerAction.js b/src/libs/actions/EmojiPickerAction.js index 484d0b55ca24..458154913e3c 100644 --- a/src/libs/actions/EmojiPickerAction.js +++ b/src/libs/actions/EmojiPickerAction.js @@ -49,7 +49,7 @@ function isEmojiPickerVisible() { if (!emojiPickerRef.current) { return; } - return emojiPickerRef.current.state.isEmojiPickerVisible; + return emojiPickerRef.current.isEmojiPickerVisible; } export {emojiPickerRef, showEmojiPicker, hideEmojiPicker, isActiveReportAction, isEmojiPickerVisible}; From e979d2b41b65054579b48022d0e2fdfc27562bf2 Mon Sep 17 00:00:00 2001 From: Robert Kozik Date: Tue, 4 Jul 2023 12:47:20 +0200 Subject: [PATCH 09/17] conditionally add trailing space in the replaceSelectionWithText method --- src/pages/home/report/ReportActionCompose.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/pages/home/report/ReportActionCompose.js b/src/pages/home/report/ReportActionCompose.js index 421893d0bd89..f9c56bb83483 100644 --- a/src/pages/home/report/ReportActionCompose.js +++ b/src/pages/home/report/ReportActionCompose.js @@ -681,13 +681,16 @@ class ReportActionCompose extends React.Component { /** * Callback to add whatever text is chosen into the main input (used f.e as callback for the emoji picker) * @param {String} text + * @param {Boolean} shouldAddTrailSpace */ - replaceSelectionWithText(text) { - this.updateComment(ComposerUtils.insertText(this.comment, this.state.selection, `${text} `)); + replaceSelectionWithText(text, shouldAddTrailSpace = true) { + const updatedText = shouldAddTrailSpace ? `${text} ` : text; + const selectionSpaceLength = shouldAddTrailSpace ? CONST.SPACE_LENGTH : 0; + this.updateComment(ComposerUtils.insertText(this.comment, this.state.selection, updatedText)); this.setState((prevState) => ({ selection: { - start: prevState.selection.start + text.length + CONST.SPACE_LENGTH, - end: prevState.selection.start + text.length + CONST.SPACE_LENGTH, + start: prevState.selection.start + text.length + selectionSpaceLength, + end: prevState.selection.start + text.length + selectionSpaceLength, }, })); } @@ -723,7 +726,7 @@ class ReportActionCompose extends React.Component { } this.focus(); - this.replaceSelectionWithText(e.key); + this.replaceSelectionWithText(e.key, false); } /** From 3d12ed71518b980e7c2a1b6983c2189cf6089027 Mon Sep 17 00:00:00 2001 From: Robert Kozik Date: Tue, 4 Jul 2023 23:22:48 +0200 Subject: [PATCH 10/17] refactor navigation unsubscribers names --- src/pages/home/report/ReportActionCompose.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/pages/home/report/ReportActionCompose.js b/src/pages/home/report/ReportActionCompose.js index bc0f23993ce1..73213ad5d5ad 100644 --- a/src/pages/home/report/ReportActionCompose.js +++ b/src/pages/home/report/ReportActionCompose.js @@ -209,8 +209,8 @@ class ReportActionCompose extends React.Component { // so we need to ensure that it is only updated after focus. const isMobileSafari = Browser.isMobileSafari(); - this.unsubscribeNavBlur = () => null; - this.unsubscribeNavFocus = () => null; + this.unsubscribeNavigationBlur = () => null; + this.unsubscribeNavigationFocus = () => null; this.state = { isFocused: this.shouldFocusInputOnScreenFocus && !this.props.modal.isVisible && !this.props.modal.willAlertModalBecomeVisible && this.props.shouldShowComposeInput, @@ -245,10 +245,10 @@ class ReportActionCompose extends React.Component { this.focus(false); }); - this.unsubscribeNavBlur = this.props.navigation.addListener('blur', () => KeyDownListener.removeKeyDownPressListner(this.focusComposerOnKeyPress)); - + this.unsubscribeNavigationBlur = this.props.navigation.addListener('blur', () => KeyDownListener.removeKeyDownPressListner(this.focusComposerOnKeyPress)); + // This listener is used for focusing the composer again after going back to a report without remounting it and to focus on any key press. - this.unsubscribeNavFocus = this.props.navigation.addListener('focus', () => { + this.unsubscribeNavigationFocus = this.props.navigation.addListener('focus', () => { KeyDownListener.addKeyDownPressListner(this.focusComposerOnKeyPress); if (!this.willBlurTextInputOnTapOutside || this.props.isFocused || this.props.modal.isVisible) { return; @@ -297,8 +297,8 @@ class ReportActionCompose extends React.Component { ReportActionComposeFocusManager.clear(); KeyDownListener.removeKeyDownPressListner(this.focusComposerOnKeyPress); - this.unsubscribeNavBlur(); - this.unsubscribeNavFocus(); + this.unsubscribeNavigationBlur(); + this.unsubscribeNavigationFocus(); } onSelectionChange(e) { From 99edfa14580c3ec7eb1ece6be57aab2382eacaaf Mon Sep 17 00:00:00 2001 From: Robert Kozik Date: Wed, 5 Jul 2023 22:32:21 +0200 Subject: [PATCH 11/17] rephrase comment to me more precise --- src/components/Composer/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Composer/index.js b/src/components/Composer/index.js index b5225c4c08f7..af350ae48336 100755 --- a/src/components/Composer/index.js +++ b/src/components/Composer/index.js @@ -173,7 +173,7 @@ class Composer extends React.Component { this.unsubscribeFocus = this.props.navigation.addListener('focus', () => document.addEventListener('paste', this.handlePaste)); this.unsubscribeBlur = this.props.navigation.addListener('blur', () => document.removeEventListener('paste', this.handlePaste)); - // focus is not triggered on component mount so we need to add listener manually + // We need to add paste listener manually as well as navigation focus event is not triggered on component mount document.addEventListener('paste', this.handlePaste); } } From 889dd18f8913fb07d89bd54dd14750d68e1ce614 Mon Sep 17 00:00:00 2001 From: Robert Kozik Date: Thu, 6 Jul 2023 10:01:48 +0200 Subject: [PATCH 12/17] move FAB ref to separate file, create function to check create menu visibility --- .../FloatingActionButtonAndPopoverUtils.js | 12 ++++++++++++ src/pages/home/sidebar/SidebarScreen/index.js | 3 ++- 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopoverUtils.js diff --git a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopoverUtils.js b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopoverUtils.js new file mode 100644 index 000000000000..283bf1ce5b44 --- /dev/null +++ b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopoverUtils.js @@ -0,0 +1,12 @@ +import React from 'react'; + +const floatingActionButtonAndPopoverRef = React.createRef(null); + +function isFloatingActionButtonCreateMenuOpen() { + if (!floatingActionButtonAndPopoverRef.current) { + return; + } + return floatingActionButtonAndPopoverRef.current.state.isCreateMenuActive; +} + +export {floatingActionButtonAndPopoverRef, isFloatingActionButtonCreateMenuOpen} \ No newline at end of file diff --git a/src/pages/home/sidebar/SidebarScreen/index.js b/src/pages/home/sidebar/SidebarScreen/index.js index 75c28c2f069e..5efeb4051bb8 100755 --- a/src/pages/home/sidebar/SidebarScreen/index.js +++ b/src/pages/home/sidebar/SidebarScreen/index.js @@ -8,9 +8,10 @@ import FreezeWrapper from '../../../../libs/Navigation/FreezeWrapper'; import withWindowDimensions from '../../../../components/withWindowDimensions'; import StatusBar from '../../../../libs/StatusBar'; import themeColors from '../../../../styles/themes/default'; +import * as FloatingActionButton from './FloatingActionButtonAndPopoverUtils'; function SidebarScreen(props) { - const popoverModal = useRef(null); + const popoverModal = FloatingActionButton.floatingActionButtonAndPopoverRef; useFocusEffect( useCallback(() => { From eb261935adff5de0e52f1040ced4e9b5f3d01bbb Mon Sep 17 00:00:00 2001 From: Robert Kozik Date: Thu, 6 Jul 2023 10:22:00 +0200 Subject: [PATCH 13/17] Consider FAB popover menu status while checking composer visibility --- src/pages/home/report/ReportActionCompose.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pages/home/report/ReportActionCompose.js b/src/pages/home/report/ReportActionCompose.js index 73213ad5d5ad..60df7dbf32f2 100644 --- a/src/pages/home/report/ReportActionCompose.js +++ b/src/pages/home/report/ReportActionCompose.js @@ -54,6 +54,7 @@ import * as IOU from '../../../libs/actions/IOU'; import PressableWithFeedback from '../../../components/Pressable/PressableWithFeedback'; import * as KeyDownListener from '../../../libs/KeyboardShortcut/KeyDownPressListener'; import * as EmojiPickerActions from '../../../libs/actions/EmojiPickerAction'; +import * as FloatingActionButtonUtils from '../sidebar/SidebarScreen/FloatingActionButtonAndPopoverUtils'; const propTypes = { /** Beta features list */ @@ -708,7 +709,7 @@ class ReportActionCompose extends React.Component { * @returns {Boolean} */ checkComposerVisibility() { - const isComposerCoveredUp = EmojiPickerActions.isEmojiPickerVisible() || this.state.isMenuVisible; + const isComposerCoveredUp = EmojiPickerActions.isEmojiPickerVisible() || this.state.isMenuVisible || FloatingActionButtonUtils.isFloatingActionButtonCreateMenuOpen(); return !isComposerCoveredUp; } From 834fb7299d7d86df6f28f53f86f18cb9a3efe8c3 Mon Sep 17 00:00:00 2001 From: Robert Kozik Date: Thu, 6 Jul 2023 10:49:26 +0200 Subject: [PATCH 14/17] prettier --- .../SidebarScreen/FloatingActionButtonAndPopoverUtils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopoverUtils.js b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopoverUtils.js index 283bf1ce5b44..a00bb031f1f8 100644 --- a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopoverUtils.js +++ b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopoverUtils.js @@ -9,4 +9,4 @@ function isFloatingActionButtonCreateMenuOpen() { return floatingActionButtonAndPopoverRef.current.state.isCreateMenuActive; } -export {floatingActionButtonAndPopoverRef, isFloatingActionButtonCreateMenuOpen} \ No newline at end of file +export {floatingActionButtonAndPopoverRef, isFloatingActionButtonCreateMenuOpen}; From 1f4bf0bda9b9a0e42f7c9ceaac21fadd9e18434f Mon Sep 17 00:00:00 2001 From: Robert Kozik Date: Thu, 6 Jul 2023 10:53:21 +0200 Subject: [PATCH 15/17] remove unused useRef import --- src/pages/home/sidebar/SidebarScreen/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/sidebar/SidebarScreen/index.js b/src/pages/home/sidebar/SidebarScreen/index.js index 5efeb4051bb8..688113b7b273 100755 --- a/src/pages/home/sidebar/SidebarScreen/index.js +++ b/src/pages/home/sidebar/SidebarScreen/index.js @@ -1,4 +1,4 @@ -import React, {useRef, useCallback} from 'react'; +import React, {useCallback} from 'react'; import {InteractionManager} from 'react-native'; import {useFocusEffect} from '@react-navigation/native'; import sidebarPropTypes from './sidebarPropTypes'; From 2eac511b0239018b4a8fcc0b7c9f845d230fa29e Mon Sep 17 00:00:00 2001 From: Robert Kozik Date: Fri, 7 Jul 2023 22:19:58 +0200 Subject: [PATCH 16/17] move FloatingActionButtonAndPopoverUtils to libs directory --- .../FloatingActionButtonAndPopoverUtils.js | 0 src/pages/home/report/ReportActionCompose.js | 2 +- src/pages/home/sidebar/SidebarScreen/index.js | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename src/{pages/home/sidebar/SidebarScreen => libs}/FloatingActionButtonAndPopoverUtils.js (100%) diff --git a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopoverUtils.js b/src/libs/FloatingActionButtonAndPopoverUtils.js similarity index 100% rename from src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopoverUtils.js rename to src/libs/FloatingActionButtonAndPopoverUtils.js diff --git a/src/pages/home/report/ReportActionCompose.js b/src/pages/home/report/ReportActionCompose.js index 60df7dbf32f2..11437f6f9352 100644 --- a/src/pages/home/report/ReportActionCompose.js +++ b/src/pages/home/report/ReportActionCompose.js @@ -54,7 +54,7 @@ import * as IOU from '../../../libs/actions/IOU'; import PressableWithFeedback from '../../../components/Pressable/PressableWithFeedback'; import * as KeyDownListener from '../../../libs/KeyboardShortcut/KeyDownPressListener'; import * as EmojiPickerActions from '../../../libs/actions/EmojiPickerAction'; -import * as FloatingActionButtonUtils from '../sidebar/SidebarScreen/FloatingActionButtonAndPopoverUtils'; +import * as FloatingActionButtonUtils from '../../../libs/FloatingActionButtonAndPopoverUtils'; const propTypes = { /** Beta features list */ diff --git a/src/pages/home/sidebar/SidebarScreen/index.js b/src/pages/home/sidebar/SidebarScreen/index.js index 688113b7b273..f2c56319c742 100755 --- a/src/pages/home/sidebar/SidebarScreen/index.js +++ b/src/pages/home/sidebar/SidebarScreen/index.js @@ -8,7 +8,7 @@ import FreezeWrapper from '../../../../libs/Navigation/FreezeWrapper'; import withWindowDimensions from '../../../../components/withWindowDimensions'; import StatusBar from '../../../../libs/StatusBar'; import themeColors from '../../../../styles/themes/default'; -import * as FloatingActionButton from './FloatingActionButtonAndPopoverUtils'; +import * as FloatingActionButton from '../../../../libs/FloatingActionButtonAndPopoverUtils'; function SidebarScreen(props) { const popoverModal = FloatingActionButton.floatingActionButtonAndPopoverRef; From 8b49b86f18350391964d5d75ce3fccbde92c2780 Mon Sep 17 00:00:00 2001 From: Robert Kozik Date: Mon, 10 Jul 2023 13:12:28 +0200 Subject: [PATCH 17/17] detect popover status based on modal isVisible onyx state --- src/libs/FloatingActionButtonAndPopoverUtils.js | 12 ------------ src/pages/home/report/ReportActionCompose.js | 3 +-- src/pages/home/sidebar/SidebarScreen/index.js | 5 ++--- 3 files changed, 3 insertions(+), 17 deletions(-) delete mode 100644 src/libs/FloatingActionButtonAndPopoverUtils.js diff --git a/src/libs/FloatingActionButtonAndPopoverUtils.js b/src/libs/FloatingActionButtonAndPopoverUtils.js deleted file mode 100644 index a00bb031f1f8..000000000000 --- a/src/libs/FloatingActionButtonAndPopoverUtils.js +++ /dev/null @@ -1,12 +0,0 @@ -import React from 'react'; - -const floatingActionButtonAndPopoverRef = React.createRef(null); - -function isFloatingActionButtonCreateMenuOpen() { - if (!floatingActionButtonAndPopoverRef.current) { - return; - } - return floatingActionButtonAndPopoverRef.current.state.isCreateMenuActive; -} - -export {floatingActionButtonAndPopoverRef, isFloatingActionButtonCreateMenuOpen}; diff --git a/src/pages/home/report/ReportActionCompose.js b/src/pages/home/report/ReportActionCompose.js index 11437f6f9352..d38969468c00 100644 --- a/src/pages/home/report/ReportActionCompose.js +++ b/src/pages/home/report/ReportActionCompose.js @@ -54,7 +54,6 @@ import * as IOU from '../../../libs/actions/IOU'; import PressableWithFeedback from '../../../components/Pressable/PressableWithFeedback'; import * as KeyDownListener from '../../../libs/KeyboardShortcut/KeyDownPressListener'; import * as EmojiPickerActions from '../../../libs/actions/EmojiPickerAction'; -import * as FloatingActionButtonUtils from '../../../libs/FloatingActionButtonAndPopoverUtils'; const propTypes = { /** Beta features list */ @@ -709,7 +708,7 @@ class ReportActionCompose extends React.Component { * @returns {Boolean} */ checkComposerVisibility() { - const isComposerCoveredUp = EmojiPickerActions.isEmojiPickerVisible() || this.state.isMenuVisible || FloatingActionButtonUtils.isFloatingActionButtonCreateMenuOpen(); + const isComposerCoveredUp = EmojiPickerActions.isEmojiPickerVisible() || this.state.isMenuVisible || this.props.modal.isVisible; return !isComposerCoveredUp; } diff --git a/src/pages/home/sidebar/SidebarScreen/index.js b/src/pages/home/sidebar/SidebarScreen/index.js index f2c56319c742..27716995fc10 100755 --- a/src/pages/home/sidebar/SidebarScreen/index.js +++ b/src/pages/home/sidebar/SidebarScreen/index.js @@ -1,4 +1,4 @@ -import React, {useCallback} from 'react'; +import React, {useCallback, useRef} from 'react'; import {InteractionManager} from 'react-native'; import {useFocusEffect} from '@react-navigation/native'; import sidebarPropTypes from './sidebarPropTypes'; @@ -8,10 +8,9 @@ import FreezeWrapper from '../../../../libs/Navigation/FreezeWrapper'; import withWindowDimensions from '../../../../components/withWindowDimensions'; import StatusBar from '../../../../libs/StatusBar'; import themeColors from '../../../../styles/themes/default'; -import * as FloatingActionButton from '../../../../libs/FloatingActionButtonAndPopoverUtils'; function SidebarScreen(props) { - const popoverModal = FloatingActionButton.floatingActionButtonAndPopoverRef; + const popoverModal = useRef(null); useFocusEffect( useCallback(() => {