diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 603a3507b0b04..1389290e962b1 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -67,9 +67,6 @@ const ONYXKEYS = { // draft status in custom mode (date and time) STATUS_DRAFT_CUSTOM_CLEAR_AFTER_DATE: 'statusDraftCustomClearAfterDate', - // keep edit message focus state - INPUT_FOCUSED: 'inputFocused', - /** Contains all the personalDetails the user has access to, keyed by accountID */ PERSONAL_DETAILS_LIST: 'personalDetailsList', @@ -1190,7 +1187,6 @@ type OnyxValuesMapping = { [ONYXKEYS.NEW_GROUP_CHAT_DRAFT]: OnyxTypes.NewGroupChatDraft; [ONYXKEYS.CUSTOM_STATUS_DRAFT]: OnyxTypes.CustomStatusDraft; [ONYXKEYS.STATUS_DRAFT_CUSTOM_CLEAR_AFTER_DATE]: string; - [ONYXKEYS.INPUT_FOCUSED]: boolean; [ONYXKEYS.PERSONAL_DETAILS_LIST]: OnyxTypes.PersonalDetailsList; [ONYXKEYS.PRIVATE_PERSONAL_DETAILS]: OnyxTypes.PrivatePersonalDetails; [ONYXKEYS.PERSONAL_DETAILS_METADATA]: Record; diff --git a/src/components/AddPaymentMethodMenu.tsx b/src/components/AddPaymentMethodMenu.tsx index 26801e3a0a464..65e5692e2fc3e 100644 --- a/src/components/AddPaymentMethodMenu.tsx +++ b/src/components/AddPaymentMethodMenu.tsx @@ -133,7 +133,6 @@ function AddPaymentMethodMenu({ // }, // ], ]} - shouldEnableNewFocusManagement restoreFocusType={restoreFocusType} /> ); diff --git a/src/components/BaseMiniContextMenuItem.tsx b/src/components/BaseMiniContextMenuItem.tsx index 23a4520eae8a1..350d41220fdc8 100644 --- a/src/components/BaseMiniContextMenuItem.tsx +++ b/src/components/BaseMiniContextMenuItem.tsx @@ -3,9 +3,9 @@ import type {PressableStateCallbackType} from 'react-native'; import {View} from 'react-native'; import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; +import ComposerFocusManager from '@libs/ComposerFocusManager'; import DomUtils from '@libs/DomUtils'; import getButtonState from '@libs/getButtonState'; -import ReportActionComposeFocusManager from '@libs/ReportActionComposeFocusManager'; import variables from '@styles/variables'; import CONST from '@src/CONST'; import type WithSentryLabel from '@src/types/utils/SentryLabel'; @@ -68,7 +68,7 @@ function BaseMiniContextMenuItem({ ref={ref} onPress={onPress} onMouseDown={(event) => { - if (!ReportActionComposeFocusManager.isFocused() && !ReportActionComposeFocusManager.isEditFocused()) { + if (!ComposerFocusManager.isComposerFocused() && !ComposerFocusManager.isEditComposerFocused()) { const activeElement = DomUtils.getActiveElement(); if (activeElement instanceof HTMLElement) { activeElement?.blur(); diff --git a/src/components/ConfirmModal.tsx b/src/components/ConfirmModal.tsx index 02be40520fb3d..6db308a9ca5e3 100755 --- a/src/components/ConfirmModal.tsx +++ b/src/components/ConfirmModal.tsx @@ -98,12 +98,6 @@ type ConfirmModalProps = { /** Styles for the image */ imageStyles?: StyleProp; - /** - * Whether the modal should enable the new focus manager. - * We are attempting to migrate to a new refocus manager, adding this property for gradual migration. - * */ - shouldEnableNewFocusManagement?: boolean; - /** How to re-focus after the modal is dismissed */ restoreFocusType?: BaseModalProps['restoreFocusType']; @@ -147,7 +141,6 @@ function ConfirmModal({ shouldShowDismissIcon, titleContainerStyles, shouldReverseStackedButtons, - shouldEnableNewFocusManagement, restoreFocusType, isConfirmLoading, shouldHandleNavigationBack, @@ -175,7 +168,6 @@ function ConfirmModal({ onModalHide={onModalHide} type={isSmallScreenWidth ? CONST.MODAL.MODAL_TYPE.BOTTOM_DOCKED : CONST.MODAL.MODAL_TYPE.CONFIRM} innerContainerStyle={styles.pv0} - shouldEnableNewFocusManagement={shouldEnableNewFocusManagement} restoreFocusType={restoreFocusType} shouldHandleNavigationBack={shouldHandleNavigationBack} shouldIgnoreBackHandlerDuringTransition={shouldIgnoreBackHandlerDuringTransition} diff --git a/src/components/CurrencyPicker.tsx b/src/components/CurrencyPicker.tsx index d0dcebcdc38ff..148c206e663ca 100644 --- a/src/components/CurrencyPicker.tsx +++ b/src/components/CurrencyPicker.tsx @@ -71,7 +71,6 @@ function CurrencyPicker({label, value, errorText, headerContent, excludeCurrenci isVisible={isPickerVisible} onClose={hidePickerModal} onModalHide={hidePickerModal} - shouldEnableNewFocusManagement onBackdropPress={Navigation.dismissModal} shouldUseModalPaddingStyle={false} shouldHandleNavigationBack diff --git a/src/components/DatePicker/DatePickerModal.tsx b/src/components/DatePicker/DatePickerModal.tsx index 88f71d53cabf2..5254e3012abf9 100644 --- a/src/components/DatePicker/DatePickerModal.tsx +++ b/src/components/DatePicker/DatePickerModal.tsx @@ -72,7 +72,6 @@ function DatePickerModal({ anchorAlignment={DEFAULT_ANCHOR_ORIGIN} restoreFocusType={CONST.MODAL.RESTORE_FOCUS_TYPE.DELETE} shouldSwitchPositionIfOverflow - shouldEnableNewFocusManagement shouldMeasureAnchorPositionFromTop={shouldPositionFromTop} shouldSkipRemeasurement forwardedFSClass={forwardedFSClass} diff --git a/src/components/EmojiPicker/EmojiPicker.tsx b/src/components/EmojiPicker/EmojiPicker.tsx index 8d759fc14f868..0cd1951e057b4 100644 --- a/src/components/EmojiPicker/EmojiPicker.tsx +++ b/src/components/EmojiPicker/EmojiPicker.tsx @@ -15,10 +15,9 @@ import blurActiveElement from '@libs/Accessibility/blurActiveElement'; import type {AnchorOrigin, EmojiPickerOnModalHide, EmojiPickerRef, EmojiPopoverAnchor, OnEmojiSelected, ShowEmojiPickerOptions} from '@libs/actions/EmojiPickerAction'; import {isMobileChrome} from '@libs/Browser'; import calculateAnchorPosition from '@libs/calculateAnchorPosition'; +import ComposerFocusManager from '@libs/ComposerFocusManager'; +import type {ComposerType} from '@libs/ComposerFocusManager'; import DomUtils from '@libs/DomUtils'; -import refocusComposerAfterPreventFirstResponder from '@libs/refocusComposerAfterPreventFirstResponder'; -import type {ComposerType} from '@libs/ReportActionComposeFocusManager'; -import ReportActionComposeFocusManager from '@libs/ReportActionComposeFocusManager'; import {close} from '@userActions/Modal'; import CONST from '@src/CONST'; import KeyboardUtils from '@src/utils/keyboard'; @@ -97,11 +96,7 @@ function EmojiPicker({viewportOffsetTop, ref}: EmojiPickerProps) { }); composerToRefocusOnClose.current = composerToRefocusOnCloseValue; - if (composerToRefocusOnCloseValue === 'main') { - ReportActionComposeFocusManager.preventComposerFocusOnFirstResponderOnce(); - } else if (composerToRefocusOnCloseValue === 'edit') { - ReportActionComposeFocusManager.preventEditComposerFocusOnFirstResponderOnce(); - } + ComposerFocusManager.preventComposerFocusOnFirstResponderOnce(composerToRefocusOnCloseValue); onModalHide.current = onModalHideValue; onEmojiSelected.current = onEmojiSelectedValue; @@ -164,7 +159,7 @@ function EmojiPicker({viewportOffsetTop, ref}: EmojiPickerProps) { const handleModalHide = () => { onModalHide.current(); - refocusComposerAfterPreventFirstResponder(composerToRefocusOnClose.current).then(() => { + ComposerFocusManager.refocusComposerAfterPreventFirstResponder(composerToRefocusOnClose.current).then(() => { composerToRefocusOnClose.current = undefined; }); }; @@ -259,7 +254,6 @@ function EmojiPicker({viewportOffsetTop, ref}: EmojiPickerProps) { anchorDimensions={emojiAnchorDimension.current} avoidKeyboard shouldSwitchPositionIfOverflow - shouldEnableNewFocusManagement restoreFocusType={CONST.MODAL.RESTORE_FOCUS_TYPE.DELETE} shouldSkipRemeasurement > diff --git a/src/components/FocusTrap/FocusTrapForModal/index.web.tsx b/src/components/FocusTrap/FocusTrapForModal/index.web.tsx index a36bb03c3fe8f..73997441c4df7 100644 --- a/src/components/FocusTrap/FocusTrapForModal/index.web.tsx +++ b/src/components/FocusTrap/FocusTrapForModal/index.web.tsx @@ -2,7 +2,7 @@ import {FocusTrap} from 'focus-trap-react'; import React from 'react'; import sharedTrapStack from '@components/FocusTrap/sharedTrapStack'; import blurActiveElement from '@libs/Accessibility/blurActiveElement'; -import ReportActionComposeFocusManager from '@libs/ReportActionComposeFocusManager'; +import ComposerFocusManager from '@libs/ComposerFocusManager'; import type FocusTrapForModalProps from './FocusTrapForModalProps'; function FocusTrapForModal({children, active, initialFocus = false, shouldPreventScroll = false, shouldReturnFocus = true}: FocusTrapForModalProps) { @@ -17,7 +17,7 @@ function FocusTrapForModal({children, active, initialFocus = false, shouldPreven initialFocus, fallbackFocus: document.body, setReturnFocus: (element) => { - if (ReportActionComposeFocusManager.isFocused()) { + if (ComposerFocusManager.isComposerFocused()) { return false; } if (shouldReturnFocus) { diff --git a/src/components/LHNOptionsList/OptionRowLHN.tsx b/src/components/LHNOptionsList/OptionRowLHN.tsx index 3b13e96da68f2..6f5729f0c13ba 100644 --- a/src/components/LHNOptionsList/OptionRowLHN.tsx +++ b/src/components/LHNOptionsList/OptionRowLHN.tsx @@ -20,13 +20,13 @@ import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; +import ComposerFocusManager from '@libs/ComposerFocusManager'; import DateUtils from '@libs/DateUtils'; import DomUtils from '@libs/DomUtils'; import {containsCustomEmoji as containsCustomEmojiUtils, containsOnlyCustomEmoji} from '@libs/EmojiUtils'; import FS from '@libs/Fullstory'; import {shouldOptionShowTooltip, shouldUseBoldText} from '@libs/OptionsListUtils'; import Performance from '@libs/Performance'; -import ReportActionComposeFocusManager from '@libs/ReportActionComposeFocusManager'; import {isAdminRoom, isChatUsedForOnboarding as isChatUsedForOnboardingReportUtils, isConciergeChatReport, isGroupChat, isOneOnOneChat, isSystemChat} from '@libs/ReportUtils'; import {startSpan} from '@libs/telemetry/activeSpans'; import TextWithEmojiFragment from '@pages/home/report/comment/TextWithEmojiFragment'; @@ -184,7 +184,7 @@ function OptionRowLHN({ event?.preventDefault(); // Enable Composer to focus on clicking the same chat after opening the context menu. - ReportActionComposeFocusManager.focus(); + ComposerFocusManager.focusComposer(); hideProductTrainingTooltip(); onSelectRow(optionItem, popoverAnchor); }; diff --git a/src/components/Modal/BaseModal.tsx b/src/components/Modal/BaseModal.tsx index 3d5751b53dd45..4e9f4e93ff9f6 100644 --- a/src/components/Modal/BaseModal.tsx +++ b/src/components/Modal/BaseModal.tsx @@ -53,7 +53,6 @@ function BaseModal({ shouldUseCustomBackdrop = false, onBackdropPress, modalId, - shouldEnableNewFocusManagement = false, restoreFocusType, shouldUseModalPaddingStyle = true, initialFocus = false, @@ -97,11 +96,9 @@ function BaseModal({ const uniqueModalId = useMemo(() => modalId ?? ComposerFocusManager.getId(), [modalId]); const saveFocusState = useCallback(() => { - if (shouldEnableNewFocusManagement) { - ComposerFocusManager.saveFocusState(uniqueModalId); - } + ComposerFocusManager.saveFocusState(uniqueModalId); ComposerFocusManager.resetReadyToFocus(uniqueModalId); - }, [shouldEnableNewFocusManagement, uniqueModalId]); + }, [uniqueModalId]); /** * Hides modal * @param callHideCallback - Should we call the onModalHide callback @@ -367,7 +364,6 @@ function BaseModal({ customBackdrop={shouldUseCustomBackdrop ? : undefined} type={type} shouldIgnoreBackHandlerDuringTransition={shouldIgnoreBackHandlerDuringTransition} - shouldEnableNewFocusManagement={shouldEnableNewFocusManagement} > {isVisibleState && containerView} diff --git a/src/components/Modal/ReanimatedModal/types.ts b/src/components/Modal/ReanimatedModal/types.ts index 6fbe23dc3ab27..f6ef33396da32 100644 --- a/src/components/Modal/ReanimatedModal/types.ts +++ b/src/components/Modal/ReanimatedModal/types.ts @@ -137,12 +137,6 @@ type ReanimatedModalProps = ViewProps & /** Whether to use a custom backdrop for the modal? (This prevents focus issues on desktop) */ initialFocus?: FocusTrapOptions['initialFocus']; - /** - * Whether the modal should enable the new focus manager. - * We are attempting to migrate to a new refocus manager, adding this property for gradual migration. - * */ - shouldEnableNewFocusManagement?: boolean; - /** Whether to ignore the back handler during transition */ shouldIgnoreBackHandlerDuringTransition?: boolean; }; diff --git a/src/components/Modal/types.ts b/src/components/Modal/types.ts index 4db73a9bd1195..fcb8bb6742894 100644 --- a/src/components/Modal/types.ts +++ b/src/components/Modal/types.ts @@ -73,12 +73,6 @@ type BaseModalProps = Partial & /** Unique id for the modal */ modalId?: number; - /** - * Whether the modal should enable the new focus manager. - * We are attempting to migrate to a new refocus manager, adding this property for gradual migration. - * */ - shouldEnableNewFocusManagement?: boolean; - /** How to re-focus after the modal is dismissed */ restoreFocusType?: ValueOf; diff --git a/src/components/MoneyRequestHeader.tsx b/src/components/MoneyRequestHeader.tsx index aae620b376683..2bc3cfaea6fa3 100644 --- a/src/components/MoneyRequestHeader.tsx +++ b/src/components/MoneyRequestHeader.tsx @@ -576,7 +576,6 @@ function MoneyRequestHeader({report, parentReportAction, policy, onBackButtonPre confirmText={translate('common.delete')} cancelText={translate('common.cancel')} danger - shouldEnableNewFocusManagement /> {!!rejectModalAction && ( )} diff --git a/src/components/PopoverMenu.tsx b/src/components/PopoverMenu.tsx index eea11c2081a79..69670b5a4e8bb 100644 --- a/src/components/PopoverMenu.tsx +++ b/src/components/PopoverMenu.tsx @@ -126,12 +126,6 @@ type PopoverMenuProps = Partial & { /** Whether we want to show the popover on the right side of the screen */ fromSidebarMediumScreen?: boolean; - /** - * Whether the modal should enable the new focus manager. - * We are attempting to migrate to a new refocus manager, adding this property for gradual migration. - * */ - shouldEnableNewFocusManagement?: boolean; - /** How to re-focus after the modal is dismissed */ restoreFocusType?: BaseModalProps['restoreFocusType']; @@ -270,7 +264,6 @@ function BasePopoverMenu({ disableAnimation = true, withoutOverlay = false, shouldSetModalVisibility = true, - shouldEnableNewFocusManagement, restoreFocusType, shouldShowSelectedItemCheck = false, containerStyles, @@ -559,7 +552,6 @@ function BasePopoverMenu({ fromSidebarMediumScreen={fromSidebarMediumScreen} withoutOverlay={withoutOverlay} shouldSetModalVisibility={shouldSetModalVisibility} - shouldEnableNewFocusManagement={shouldEnableNewFocusManagement} restoreFocusType={restoreFocusType} innerContainerStyle={{...styles.pv0, ...innerContainerStyle}} shouldUseModalPaddingStyle={shouldUseModalPaddingStyle} @@ -567,7 +559,7 @@ function BasePopoverMenu({ > 0 && popoverDimensions.height > 0; const modalId = useMemo(() => ComposerFocusManager.getId(), []); - useEffect(() => { - if (prevIsVisible || !isVisible || !shouldEnableNewFocusManagement) { - return; - } - ComposerFocusManager.saveFocusState(modalId); - }, [isVisible, shouldEnableNewFocusManagement, prevIsVisible, modalId]); - if (!prevIsVisible && isVisible && isContentMeasured && !shouldSkipRemeasurement) { // Check if anything significant changed that would require re-measurement const hasAnchorPositionChanged = !deepEqual(prevAnchorPosition, anchorPosition); @@ -203,7 +195,6 @@ function PopoverWithMeasuredContentBase({ avoidKeyboard={avoidKeyboard} hideModalContentWhileAnimating={hideModalContentWhileAnimating} modalId={modalId} - shouldEnableNewFocusManagement={shouldEnableNewFocusManagement} // eslint-disable-next-line react/jsx-props-no-spreading {...props} anchorPosition={shiftedAnchorPosition} diff --git a/src/components/RequireTwoFactorAuthenticationModal.tsx b/src/components/RequireTwoFactorAuthenticationModal.tsx index fe156c15080f4..0f397625a8a1b 100644 --- a/src/components/RequireTwoFactorAuthenticationModal.tsx +++ b/src/components/RequireTwoFactorAuthenticationModal.tsx @@ -23,15 +23,9 @@ type RequireTwoFactorAuthenticationModalProps = { /** Describe what is showing */ description: string; - - /** - * Whether the modal should enable the new focus manager. - * We are attempting to migrate to a new refocus manager, adding this property for gradual migration. - * */ - shouldEnableNewFocusManagement?: boolean; }; -function RequireTwoFactorAuthenticationModal({onCancel = () => {}, description, isVisible, onSubmit, shouldEnableNewFocusManagement}: RequireTwoFactorAuthenticationModalProps) { +function RequireTwoFactorAuthenticationModal({onCancel = () => {}, description, isVisible, onSubmit}: RequireTwoFactorAuthenticationModalProps) { const {shouldUseNarrowLayout} = useResponsiveLayout(); const styles = useThemeStyles(); const {translate} = useLocalize(); @@ -43,7 +37,6 @@ function RequireTwoFactorAuthenticationModal({onCancel = () => {}, description, isVisible={isVisible} type={shouldUseNarrowLayout ? CONST.MODAL.MODAL_TYPE.BOTTOM_DOCKED : CONST.MODAL.MODAL_TYPE.CONFIRM} innerContainerStyle={{...styles.pb5, ...styles.pt0, ...styles.boxShadowNone}} - shouldEnableNewFocusManagement={shouldEnableNewFocusManagement} > diff --git a/src/components/Search/FilterDropdowns/DropdownButton.tsx b/src/components/Search/FilterDropdowns/DropdownButton.tsx index a2b54a29d4494..47d8fa1b035a2 100644 --- a/src/components/Search/FilterDropdowns/DropdownButton.tsx +++ b/src/components/Search/FilterDropdowns/DropdownButton.tsx @@ -160,7 +160,6 @@ function DropdownButton({label, value, viewportOffsetTop, PopoverComponent, medi anchorPosition={popoverTriggerPosition} anchorAlignment={ANCHOR_ORIGIN} restoreFocusType={CONST.MODAL.RESTORE_FOCUS_TYPE.DELETE} - shouldEnableNewFocusManagement shouldMeasureAnchorPositionFromTop={false} outerStyle={{...StyleUtils.getOuterModalStyle(windowHeight, viewportOffsetTop), ...containerStyles}} // This must be false because we dont want the modal to close if we open the RHP for selections diff --git a/src/components/SidePanel/Concierge/index.tsx b/src/components/SidePanel/Concierge/index.tsx index d9fc9eb8f7b72..ae34d1ee6131a 100644 --- a/src/components/SidePanel/Concierge/index.tsx +++ b/src/components/SidePanel/Concierge/index.tsx @@ -12,7 +12,7 @@ const CONCIERGE_REPORT_KEY = 'Report-Concierge-Key'; function Concierge({navigation}: Pick) { const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID, {canBeMissing: true}); // eslint-disable-next-line react/jsx-no-constructed-context-values - const route = !!conciergeReportID && ({name: SCREENS.REPORT, params: {reportID: conciergeReportID}, key: CONCIERGE_REPORT_KEY} as const); + const route = !!conciergeReportID && ({name: SCREENS.REPORT, params: {reportID: '803938620872427'}, key: CONCIERGE_REPORT_KEY} as const); if (!route) { return null; diff --git a/src/components/SidePanel/SidePanelContextProvider.tsx b/src/components/SidePanel/SidePanelContextProvider.tsx index 1a50aa54aac01..26ed8cfcff52f 100644 --- a/src/components/SidePanel/SidePanelContextProvider.tsx +++ b/src/components/SidePanel/SidePanelContextProvider.tsx @@ -7,8 +7,8 @@ import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useSidePanelDisplayStatus from '@hooks/useSidePanelDisplayStatus'; import useWindowDimensions from '@hooks/useWindowDimensions'; import SidePanelActions from '@libs/actions/SidePanel'; +import ComposerFocusManager from '@libs/ComposerFocusManager'; import focusComposerWithDelay from '@libs/focusComposerWithDelay'; -import ReportActionComposeFocusManager from '@libs/ReportActionComposeFocusManager'; import variables from '@styles/variables'; import CONST from '@src/CONST'; import type {SidePanel} from '@src/types/onyx'; @@ -84,8 +84,8 @@ function SidePanelContextProvider({children}: PropsWithChildren) { setIsSidePanelTransitionEnded(false); SidePanelActions.closeSidePanel(!isExtraLargeScreenWidth || shouldUpdateNarrow); - // Focus the composer after closing the Side Panel - focusComposerWithDelay(ReportActionComposeFocusManager.composerRef.current, CONST.SIDE_PANEL_ANIMATED_TRANSITION + CONST.COMPOSER_FOCUS_DELAY)(true); + // Focus the main composer after closing the Side Panel + focusComposerWithDelay(ComposerFocusManager.getComposerRef('main').current, CONST.SIDE_PANEL_ANIMATED_TRANSITION + CONST.COMPOSER_FOCUS_DELAY)(true); }, [isExtraLargeScreenWidth, sidePanelNVP], ); diff --git a/src/components/ThreeDotsMenu/index.tsx b/src/components/ThreeDotsMenu/index.tsx index 372cf9066ff88..e0edd2f49b4b3 100644 --- a/src/components/ThreeDotsMenu/index.tsx +++ b/src/components/ThreeDotsMenu/index.tsx @@ -177,7 +177,6 @@ function ThreeDotsMenu({ withoutOverlay={!shouldOverlay} shouldSetModalVisibility={shouldSetModalVisibility} anchorRef={buttonRef} - shouldEnableNewFocusManagement restoreFocusType={restoreFocusType} /> diff --git a/src/libs/ComposerFocusManager.ts b/src/libs/ComposerFocusManager.ts index 5e392f2a5deb9..af5c57a415679 100644 --- a/src/libs/ComposerFocusManager.ts +++ b/src/libs/ComposerFocusManager.ts @@ -1,7 +1,18 @@ +import {findFocusedRoute} from '@react-navigation/native'; +import type {RefObject} from 'react'; +import React from 'react'; import {TextInput} from 'react-native'; import type {ValueOf} from 'type-fest'; import CONST from '@src/CONST'; +import SCREENS from '@src/SCREENS'; import isWindowReadyToFocus from './isWindowReadyToFocus'; +import isReportOpenInRHP from './Navigation/helpers/isReportOpenInRHP'; +import navigationRef from './Navigation/navigationRef'; +import preventTextInputFocusOnFirstResponderOnce from './preventTextInputFocusOnFirstResponderOnce'; + +// ============================================ +// SECTION 1: TYPES +// ============================================ type ModalId = number | undefined; @@ -9,6 +20,12 @@ type InputElement = (TextInput & HTMLElement) | null; type RestoreFocusType = ValueOf | undefined; +type FocusContext = 'main' | 'sidePanel'; + +type ComposerType = 'main' | 'edit'; + +type FocusCallback = (shouldFocusForNonBlurInputOnTapOutside?: boolean) => void; + /** * So far, modern browsers only support the file cancel event in some newer versions * (i.e., Chrome: 113+ / Firefox: 91+ / Safari 16.4+), and there is no standard feature detection method available. @@ -24,6 +41,10 @@ type PromiseMapValue = { resolve: () => void; }; +// ============================================ +// SECTION 2: MODAL FOCUS MANAGEMENT +// ============================================ + let focusedInput: InputElement = null; let uniqueModalId = 1; const focusMap = new Map(); @@ -39,47 +60,6 @@ function getActiveInput() { return (TextInput.State.currentlyFocusedInput ? TextInput.State.currentlyFocusedInput() : TextInput.State.currentlyFocusedField()) as InputElement; } -/** - * On web platform, if the modal is displayed by a click, the blur event is fired before the modal appears, - * so we need to cache the focused input in the pointerdown handler, which is fired before the blur event. - */ -function saveFocusedInput() { - focusedInput = getActiveInput(); -} - -/** - * If a click does not display the modal, we also should clear the cached value to avoid potential issues. - */ -function clearFocusedInput() { - if (!focusedInput) { - return; - } - - // For the PopoverWithMeasuredContent component, Modal is only mounted after onLayout event is triggered, - // this event is placed within a setTimeout in react-native-web, - // so we can safely clear the cached value only after this event. - setTimeout(() => (focusedInput = null), CONST.ANIMATION_IN_TIMING); -} - -/** - * When a TextInput is unmounted, we also should release the reference here to avoid potential issues. - * - */ -function releaseInput(input: InputElement) { - if (!input) { - return; - } - if (input === focusedInput) { - focusedInput = null; - } - for (const [key, value] of focusMap.entries()) { - if (value.input !== input) { - continue; - } - focusMap.delete(key); - } -} - function getId() { return uniqueModalId++; } @@ -115,7 +95,7 @@ function saveFocusState(id: ModalId, isInUploadingContext = false, shouldClearFo * On web platform, if we intentionally click on another input box, there is no need to restore focus. * Additionally, if we are closing the RHP, we can ignore the focused input. */ -function focus(input: InputElement, shouldIgnoreFocused = false) { +function focusInput(input: InputElement, shouldIgnoreFocused = false) { const activeInput = getActiveInput(); if (!input || (activeInput && !shouldIgnoreFocused)) { return; @@ -134,7 +114,7 @@ function tryRestoreTopmostFocus(shouldIgnoreFocused: boolean, isInUploadingConte if (activeModals.indexOf(modalId) >= 0) { return; } - focus(input, shouldIgnoreFocused); + focusInput(input, shouldIgnoreFocused); focusMap.delete(modalId); } @@ -171,7 +151,7 @@ function restoreFocusState(id: ModalId, shouldIgnoreFocused = false, restoreFocu return; } if (input) { - focus(input, shouldIgnoreFocused); + focusInput(input, shouldIgnoreFocused); return; } @@ -223,16 +203,184 @@ function refocusAfterModalFullyClosed(id: ModalId, restoreType: RestoreFocusType isReadyToFocus(id)?.then(() => restoreFocusState(id, false, restoreType, isInUploadingContext)); } +// ============================================ +// SECTION 3: REPORT COMPOSER FOCUS +// ============================================ + +const composerRefs: Record> = { + main: React.createRef(), + sidePanel: React.createRef(), +}; + +const editComposerRefs: Record> = { + main: React.createRef(), + sidePanel: React.createRef(), +}; + +const focusCallbacks: Record = { + main: null, + sidePanel: null, +}; + +const priorityFocusCallbacks: Record = { + main: null, + sidePanel: null, +}; + +/** + * Get the composer ref for a specific context + */ +function getComposerRef(context: FocusContext): RefObject { + return composerRefs[context]; +} + +/** + * Get the edit composer ref for a specific context + */ +function getEditComposerRef(context: FocusContext): RefObject { + return editComposerRefs[context]; +} + +/** + * Register a callback to be called when focus is requested. + * Typical uses of this would be call the focus on the ReportActionComposer. + * + * @param callback callback to register + * @param isPriorityCallback whether this is a priority callback (edit composer) + * @param context the focus context ('main' or 'sidePanel') + */ +function onComposerFocus(callback: FocusCallback | null, isPriorityCallback = false, context: FocusContext = 'main') { + if (isPriorityCallback) { + priorityFocusCallbacks[context] = callback; + } else { + focusCallbacks[context] = callback; + } +} + +/** + * Request focus on the ReportActionComposer + * @param context the focus context ('main' or 'sidePanel') + */ +function focusComposer(context: FocusContext = 'main') { + /** Do not trigger the refocusing when the active route is not the report screen */ + const navigationState = navigationRef.getState(); + const focusedRoute = findFocusedRoute(navigationState); + if (!navigationState || (!isReportOpenInRHP(navigationState) && focusedRoute?.name !== SCREENS.REPORT && focusedRoute?.name !== SCREENS.SEARCH.MONEY_REQUEST_REPORT)) { + return; + } + + const priorityCallback = priorityFocusCallbacks[context]; + const regularCallback = focusCallbacks[context]; + + if (typeof priorityCallback !== 'function' && typeof regularCallback !== 'function') { + return; + } + + if (typeof priorityCallback === 'function') { + priorityCallback(); + return; + } + + if (typeof regularCallback === 'function') { + regularCallback(); + } +} + +/** + * Clear the registered focus callback + * @param isPriorityCallback whether to clear the priority callback + * @param context the focus context ('main' or 'sidePanel') + */ +function clearComposerFocus(isPriorityCallback = false, context: FocusContext = 'main') { + if (isPriorityCallback) { + const editRef = editComposerRefs[context]; + if (editRef) { + editRef.current = null; + } + priorityFocusCallbacks[context] = null; + } else { + focusCallbacks[context] = null; + } +} + +/** + * Exposes the current focus state of the report action composer. + */ +function isComposerFocused(): boolean { + // Check if any composer in any context is focused + for (const ref of Object.values(composerRefs)) { + if (ref?.current?.isFocused()) { + return true; + } + } + + return false; +} + +/** + * Exposes the current focus state of the edit message composer. + */ +function isEditComposerFocused(): boolean { + // Check if any edit composer in any context is focused + for (const ref of Object.values(editComposerRefs)) { + if (ref?.current?.isFocused()) { + return true; + } + } + + return false; +} + +/** + * This will prevent the composer's text input from focusing the next time it becomes the + * first responder in the UIResponder chain. (iOS only, no-op on Android) + */ +function preventComposerFocusOnFirstResponderOnce(composerType: ComposerType = 'main') { + const ref = composerType === 'main' ? composerRefs.main : editComposerRefs.main; + + if (!ref) { + return; + } + + preventTextInputFocusOnFirstResponderOnce(ref); +} + +/** + * Refocus the composer after preventing the first responder. + * @param composerToRefocusOnClose the composer to refocus on close + * @returns a promise that resolves when the composer is refocused + */ +function refocusComposerAfterPreventFirstResponder(composerToRefocusOnClose?: ComposerType) { + return isWindowReadyToFocus().then(() => { + if (composerToRefocusOnClose === 'edit') { + editComposerRefs.main?.current?.focus(); + return; + } + + composerRefs.main?.current?.focus(); + }); +} + export default { + // Modal focus management getId, - saveFocusedInput, - clearFocusedInput, - releaseInput, saveFocusState, restoreFocusState, resetReadyToFocus, setReadyToFocus, isReadyToFocus, refocusAfterModalFullyClosed, - tryRestoreTopmostFocus, + + // Report composer focus + getComposerRef, + getEditComposerRef, + onComposerFocus, + focusComposer, + clearComposerFocus, + isComposerFocused, + isEditComposerFocused, + preventComposerFocusOnFirstResponderOnce, + refocusComposerAfterPreventFirstResponder, }; + +export type {FocusContext, ComposerType}; diff --git a/src/libs/ReportActionComposeFocusManager.ts b/src/libs/ReportActionComposeFocusManager.ts deleted file mode 100644 index 6f9194ac60651..0000000000000 --- a/src/libs/ReportActionComposeFocusManager.ts +++ /dev/null @@ -1,117 +0,0 @@ -import {findFocusedRoute} from '@react-navigation/native'; -import type {RefObject} from 'react'; -import React from 'react'; -import type {TextInput} from 'react-native'; -import SCREENS from '@src/SCREENS'; -import isReportOpenInRHP from './Navigation/helpers/isReportOpenInRHP'; -import navigationRef from './Navigation/navigationRef'; -import preventTextInputFocusOnFirstResponderOnce from './preventTextInputFocusOnFirstResponderOnce'; - -type ComposerType = 'main' | 'edit'; - -type FocusCallback = (shouldFocusForNonBlurInputOnTapOutside?: boolean) => void; - -const composerRef: RefObject = React.createRef(); - -// There are two types of composer: general composer (edit composer) and main composer. -// The general composer callback will take priority if it exists. -const editComposerRef: RefObject = React.createRef(); -// There are two types of focus callbacks: priority and general -// Priority callback would take priority if it existed -let priorityFocusCallback: FocusCallback | null = null; -let focusCallback: FocusCallback | null = null; - -/** - * Register a callback to be called when focus is requested. - * Typical uses of this would be call the focus on the ReportActionComposer. - * - * @param callback callback to register - */ -function onComposerFocus(callback: FocusCallback | null, isPriorityCallback = false) { - if (isPriorityCallback) { - priorityFocusCallback = callback; - } else { - focusCallback = callback; - } -} - -/** - * Request focus on the ReportActionComposer - */ -function focus(shouldFocusForNonBlurInputOnTapOutside?: boolean) { - /** Do not trigger the refocusing when the active route is not the report screen */ - const navigationState = navigationRef.getState(); - const focusedRoute = findFocusedRoute(navigationState); - if (!navigationState || (!isReportOpenInRHP(navigationState) && focusedRoute?.name !== SCREENS.REPORT && focusedRoute?.name !== SCREENS.SEARCH.MONEY_REQUEST_REPORT)) { - return; - } - - if (typeof priorityFocusCallback !== 'function' && typeof focusCallback !== 'function') { - return; - } - - if (typeof priorityFocusCallback === 'function') { - priorityFocusCallback(shouldFocusForNonBlurInputOnTapOutside); - return; - } - - if (typeof focusCallback === 'function') { - focusCallback(); - } -} - -/** - * Clear the registered focus callback - */ -function clear(isPriorityCallback = false) { - if (isPriorityCallback) { - editComposerRef.current = null; - priorityFocusCallback = null; - } else { - focusCallback = null; - } -} - -/** - * Exposes the current focus state of the report action composer. - */ -function isFocused(): boolean { - return !!composerRef.current?.isFocused(); -} - -/** - * Exposes the current focus state of the edit message composer. - */ -function isEditFocused(): boolean { - return !!editComposerRef.current?.isFocused(); -} - -/** - * This will prevent the composer's text input from focusing the next time it becomes the - * first responder in the UIResponder chain. (iOS only, no-op on Android) - */ -function preventComposerFocusOnFirstResponderOnce() { - preventTextInputFocusOnFirstResponderOnce(composerRef); -} - -/** - * This will prevent the edit composer's text input from focusing the next time it becomes the - * first responder in the UIResponder chain. (iOS only, no-op on Android) - */ -function preventEditComposerFocusOnFirstResponderOnce() { - preventTextInputFocusOnFirstResponderOnce(editComposerRef); -} - -export default { - composerRef, - onComposerFocus, - focus, - clear, - isFocused, - editComposerRef, - isEditFocused, - preventComposerFocusOnFirstResponderOnce, - preventEditComposerFocusOnFirstResponderOnce, -}; - -export type {ComposerType}; diff --git a/src/libs/actions/EmojiPickerAction.ts b/src/libs/actions/EmojiPickerAction.ts index 4c691e63d5ab3..513eb6ba19713 100644 --- a/src/libs/actions/EmojiPickerAction.ts +++ b/src/libs/actions/EmojiPickerAction.ts @@ -4,7 +4,7 @@ import type {TextInput, View} from 'react-native'; import type {ValueOf} from 'type-fest'; import type {Emoji} from '@assets/emojis/types'; import type {CloseContextMenuCallback} from '@components/Reactions/QuickEmojiReactions/types'; -import type {ComposerType} from '@libs/ReportActionComposeFocusManager'; +import type {ComposerType} from '@libs/ComposerFocusManager'; import type CONST from '@src/CONST'; type AnchorOrigin = { diff --git a/src/libs/actions/InputFocus/index.ts b/src/libs/actions/InputFocus/index.ts deleted file mode 100644 index 658ac2977d78a..0000000000000 --- a/src/libs/actions/InputFocus/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -import type {Modal} from '@src/types/onyx'; - -// eslint-disable-next-line @typescript-eslint/no-unused-vars -function inputFocusChange(focus: boolean) {} -// eslint-disable-next-line @typescript-eslint/no-unused-vars -function composerFocusKeepFocusOn(ref: HTMLElement, isFocused: boolean, modal: Modal, onyxFocused: boolean) {} - -export {composerFocusKeepFocusOn, inputFocusChange}; diff --git a/src/libs/actions/InputFocus/index.website.ts b/src/libs/actions/InputFocus/index.website.ts deleted file mode 100644 index a6bfe03c93202..0000000000000 --- a/src/libs/actions/InputFocus/index.website.ts +++ /dev/null @@ -1,36 +0,0 @@ -import {InteractionManager} from 'react-native'; -import Onyx from 'react-native-onyx'; -import * as Browser from '@libs/Browser'; -import ReportActionComposeFocusManager from '@libs/ReportActionComposeFocusManager'; -import ONYXKEYS from '@src/ONYXKEYS'; -import type {Modal} from '@src/types/onyx'; - -function inputFocusChange(focus: boolean) { - Onyx.set(ONYXKEYS.INPUT_FOCUSED, focus); -} - -let refSave: HTMLElement | undefined; -function composerFocusKeepFocusOn(ref: HTMLElement, isFocused: boolean, modal: Modal, onyxFocused: boolean) { - if (isFocused && !onyxFocused) { - inputFocusChange(true); - ref.focus(); - } - if (isFocused) { - refSave = ref; - } - if (!isFocused && !onyxFocused && !modal.willAlertModalBecomeVisible && !modal.isVisible && refSave) { - if (!ReportActionComposeFocusManager.isFocused()) { - // Focusing will fail when it is called immediately after closing modal so we call it after interaction. - // eslint-disable-next-line @typescript-eslint/no-deprecated - InteractionManager.runAfterInteractions(() => { - refSave?.focus(); - }); - } else { - refSave = undefined; - } - } -} - -const callback = (method: () => void) => !Browser.isMobile() && method(); - -export {composerFocusKeepFocusOn, inputFocusChange, callback}; diff --git a/src/libs/refocusComposerAfterPreventFirstResponder.ts b/src/libs/refocusComposerAfterPreventFirstResponder.ts deleted file mode 100644 index 6ac3fca6dcbc4..0000000000000 --- a/src/libs/refocusComposerAfterPreventFirstResponder.ts +++ /dev/null @@ -1,15 +0,0 @@ -import isWindowReadyToFocus from './isWindowReadyToFocus'; -import type {ComposerType} from './ReportActionComposeFocusManager'; -import ReportActionComposeFocusManager from './ReportActionComposeFocusManager'; - -function refocusComposerAfterPreventFirstResponder(composerToRefocusOnClose: ComposerType | undefined) { - return isWindowReadyToFocus().then(() => { - if (composerToRefocusOnClose === 'main') { - ReportActionComposeFocusManager.composerRef.current?.focus(); - } else if (composerToRefocusOnClose === 'edit') { - ReportActionComposeFocusManager.editComposerRef.current?.focus(); - } - }); -} - -export default refocusComposerAfterPreventFirstResponder; diff --git a/src/pages/EditReportFieldPage.tsx b/src/pages/EditReportFieldPage.tsx index ab5a919dfe5e5..ea51445cadd78 100644 --- a/src/pages/EditReportFieldPage.tsx +++ b/src/pages/EditReportFieldPage.tsx @@ -148,7 +148,6 @@ function EditReportFieldPage({route}: EditReportFieldPageProps) { confirmText={translate('common.delete')} cancelText={translate('common.cancel')} danger - shouldEnableNewFocusManagement /> {(reportField.type === CONST.REPORT_FIELD_TYPES.TEXT || isReportFieldTitle) && ( diff --git a/src/pages/ReportDetailsPage.tsx b/src/pages/ReportDetailsPage.tsx index ce3ab6b0db795..7644411b2f726 100644 --- a/src/pages/ReportDetailsPage.tsx +++ b/src/pages/ReportDetailsPage.tsx @@ -1043,7 +1043,6 @@ function ReportDetailsPage({policy, report, route, reportMetadata}: ReportDetail confirmText={translate('common.delete')} cancelText={translate('common.cancel')} danger - shouldEnableNewFocusManagement /> diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx index 99e14c82338a5..2dc5a229faf3a 100644 --- a/src/pages/home/ReportScreen.tsx +++ b/src/pages/home/ReportScreen.tsx @@ -1021,6 +1021,7 @@ function ReportScreen({route, navigation, isInSidePanel = false}: ReportScreenPr parentReportAction={parentReportAction} transactionThreadReportID={transactionThreadReportID} isReportTransactionThread={isTransactionThreadView} + isInSidePanel={isInSidePanel} /> ) : null} {!!report && shouldDisplayMoneyRequestActionsList && !shouldWaitForTransactions ? ( diff --git a/src/pages/home/report/ContextMenu/ContextMenuActions.tsx b/src/pages/home/report/ContextMenu/ContextMenuActions.tsx index 1ab0a98c3093e..067ded1ba69f4 100644 --- a/src/pages/home/report/ContextMenu/ContextMenuActions.tsx +++ b/src/pages/home/report/ContextMenu/ContextMenuActions.tsx @@ -13,6 +13,7 @@ import QuickEmojiReactions from '@components/Reactions/QuickEmojiReactions'; import addEncryptedAuthTokenToURL from '@libs/addEncryptedAuthTokenToURL'; import {isMobileSafari} from '@libs/Browser'; import Clipboard from '@libs/Clipboard'; +import ComposerFocusManager from '@libs/ComposerFocusManager'; import EmailUtils from '@libs/EmailUtils'; import {getEnvironmentURL} from '@libs/Environment/Environment'; import fileDownload from '@libs/fileDownload'; @@ -22,7 +23,6 @@ import {getForReportActionTemp} from '@libs/ModifiedExpenseMessage'; import Navigation from '@libs/Navigation/Navigation'; import Parser from '@libs/Parser'; import {getCleanedTagName} from '@libs/PolicyUtils'; -import ReportActionComposeFocusManager from '@libs/ReportActionComposeFocusManager'; import { getActionableCardFraudAlertMessage, getActionableMentionWhisperMessage, @@ -386,7 +386,7 @@ const ContextMenuActions: ContextMenuAction[] = [ onPress: (closePopover, {reportAction, reportID}) => { markCommentAsUnread(reportID, reportAction); if (closePopover) { - hideContextMenu(true, ReportActionComposeFocusManager.focus); + hideContextMenu(true, ComposerFocusManager.focusComposer); } }, getDescription: () => {}, @@ -401,7 +401,7 @@ const ContextMenuActions: ContextMenuAction[] = [ onPress: (closePopover, {reportID}) => { readNewestAction(reportID, true); if (closePopover) { - hideContextMenu(true, ReportActionComposeFocusManager.focus); + hideContextMenu(true, ComposerFocusManager.focusComposer); } }, getDescription: () => {}, @@ -523,13 +523,13 @@ const ContextMenuActions: ContextMenuAction[] = [ const originalReportID = getOriginalReportID(reportID, reportAction); if (closePopover) { hideContextMenu(false, () => { - ReportActionComposeFocusManager.focus(); + ComposerFocusManager.focusComposer(); toggleSubscribeToChildReport(reportAction?.childReportID, reportAction, originalReportID, childReportNotificationPreference); }); return; } - ReportActionComposeFocusManager.focus(); + ComposerFocusManager.focusComposer(); toggleSubscribeToChildReport(reportAction?.childReportID, reportAction, originalReportID, childReportNotificationPreference); }, getDescription: () => {}, @@ -563,13 +563,13 @@ const ContextMenuActions: ContextMenuAction[] = [ const originalReportID = getOriginalReportID(reportID, reportAction); if (closePopover) { hideContextMenu(false, () => { - ReportActionComposeFocusManager.focus(); + ComposerFocusManager.focusComposer(); toggleSubscribeToChildReport(reportAction?.childReportID, reportAction, originalReportID, childReportNotificationPreference); }); return; } - ReportActionComposeFocusManager.focus(); + ComposerFocusManager.focusComposer(); toggleSubscribeToChildReport(reportAction?.childReportID, reportAction, originalReportID, childReportNotificationPreference); }, getDescription: () => {}, @@ -584,7 +584,7 @@ const ContextMenuActions: ContextMenuAction[] = [ shouldShow: ({type}) => type === CONST.CONTEXT_MENU_TYPES.LINK, onPress: (closePopover, {selection}) => { Clipboard.setString(selection); - hideContextMenu(true, ReportActionComposeFocusManager.focus); + hideContextMenu(true, ComposerFocusManager.focusComposer); }, getDescription: (selection) => selection, sentryLabel: CONST.SENTRY_LABEL.CONTEXT_MENU.COPY_URL, @@ -598,7 +598,7 @@ const ContextMenuActions: ContextMenuAction[] = [ shouldShow: ({type}) => type === CONST.CONTEXT_MENU_TYPES.TEXT, onPress: (closePopover, {selection}) => { Clipboard.setString(selection); - hideContextMenu(true, ReportActionComposeFocusManager.focus); + hideContextMenu(true, ComposerFocusManager.focusComposer); }, getDescription: () => undefined, sentryLabel: CONST.SENTRY_LABEL.CONTEXT_MENU.COPY_TO_CLIPBOARD, @@ -612,7 +612,7 @@ const ContextMenuActions: ContextMenuAction[] = [ shouldShow: ({type}) => type === CONST.CONTEXT_MENU_TYPES.EMAIL, onPress: (closePopover, {selection}) => { Clipboard.setString(EmailUtils.trimMailTo(selection)); - hideContextMenu(true, ReportActionComposeFocusManager.focus); + hideContextMenu(true, ComposerFocusManager.focusComposer); }, getDescription: (selection) => EmailUtils.prefixMailSeparatorsWithBreakOpportunities(EmailUtils.trimMailTo(selection ?? '')), sentryLabel: CONST.SENTRY_LABEL.CONTEXT_MENU.COPY_EMAIL, @@ -907,7 +907,7 @@ const ContextMenuActions: ContextMenuAction[] = [ } if (closePopover) { - hideContextMenu(true, ReportActionComposeFocusManager.focus); + hideContextMenu(true, ComposerFocusManager.focusComposer); } }, getDescription: () => {}, @@ -933,7 +933,7 @@ const ContextMenuActions: ContextMenuAction[] = [ const reportActionID = reportAction?.reportActionID; Clipboard.setString(`${environmentURL}/r/${originalReportID}/${reportActionID}`); }); - hideContextMenu(true, ReportActionComposeFocusManager.focus); + hideContextMenu(true, ComposerFocusManager.focusComposer); }, getDescription: () => {}, sentryLabel: CONST.SENTRY_LABEL.CONTEXT_MENU.COPY_LINK, @@ -946,7 +946,7 @@ const ContextMenuActions: ContextMenuAction[] = [ onPress: (closePopover, {reportID}) => { togglePinnedState(reportID, false); if (closePopover) { - hideContextMenu(false, ReportActionComposeFocusManager.focus); + hideContextMenu(false, ComposerFocusManager.focusComposer); } }, getDescription: () => {}, @@ -960,7 +960,7 @@ const ContextMenuActions: ContextMenuAction[] = [ onPress: (closePopover, {reportID}) => { togglePinnedState(reportID, true); if (closePopover) { - hideContextMenu(false, ReportActionComposeFocusManager.focus); + hideContextMenu(false, ComposerFocusManager.focusComposer); } }, getDescription: () => {}, @@ -1018,7 +1018,7 @@ const ContextMenuActions: ContextMenuAction[] = [ const isAnchorTag = anchorRegex.test(html); fileDownload(translate, sourceURLWithAuth, originalFileName ?? '', '', isAnchorTag && isMobileSafari()).then(() => setDownload(sourceID, false)); if (closePopover) { - hideContextMenu(true, ReportActionComposeFocusManager.focus); + hideContextMenu(true, ComposerFocusManager.focusComposer); } }, getDescription: () => {}, @@ -1034,7 +1034,7 @@ const ContextMenuActions: ContextMenuAction[] = [ shouldShow: ({type, isProduction}) => type === CONST.CONTEXT_MENU_TYPES.REPORT && !isProduction, onPress: (closePopover, {report}) => { Clipboard.setString(JSON.stringify(report, null, 4)); - hideContextMenu(true, ReportActionComposeFocusManager.focus); + hideContextMenu(true, ComposerFocusManager.focusComposer); }, getDescription: () => {}, sentryLabel: CONST.SENTRY_LABEL.CONTEXT_MENU.COPY_ONYX_DATA, @@ -1050,7 +1050,7 @@ const ContextMenuActions: ContextMenuAction[] = [ } else { Navigation.navigate(ROUTES.DEBUG_REPORT.getRoute(reportID)); } - hideContextMenu(false, ReportActionComposeFocusManager.focus); + hideContextMenu(false, ComposerFocusManager.focusComposer); }, getDescription: () => {}, sentryLabel: CONST.SENTRY_LABEL.CONTEXT_MENU.DEBUG, diff --git a/src/pages/home/report/ContextMenu/PopoverReportActionContextMenu.tsx b/src/pages/home/report/ContextMenu/PopoverReportActionContextMenu.tsx index 52843af4f1c17..121247c464cb4 100644 --- a/src/pages/home/report/ContextMenu/PopoverReportActionContextMenu.tsx +++ b/src/pages/home/report/ContextMenu/PopoverReportActionContextMenu.tsx @@ -20,9 +20,8 @@ import useTransactionsAndViolationsForReport from '@hooks/useTransactionsAndViol import {deleteTrackExpense} from '@libs/actions/IOU'; import {deleteAppReport, deleteReportComment} from '@libs/actions/Report'; import calculateAnchorPosition from '@libs/calculateAnchorPosition'; -import refocusComposerAfterPreventFirstResponder from '@libs/refocusComposerAfterPreventFirstResponder'; -import type {ComposerType} from '@libs/ReportActionComposeFocusManager'; -import ReportActionComposeFocusManager from '@libs/ReportActionComposeFocusManager'; +import ComposerFocusManager from '@libs/ComposerFocusManager'; +import type {ComposerType} from '@libs/ComposerFocusManager'; import {getOriginalMessage, isMoneyRequestAction, isReportPreviewAction, isTrackExpenseAction} from '@libs/ReportActionsUtils'; import {getOriginalReportID} from '@libs/ReportUtils'; import CONST from '@src/CONST'; @@ -191,9 +190,9 @@ function PopoverReportActionContextMenu({ref}: PopoverReportActionContextMenuPro isOverflowMenu = false, withoutOverlay = true, } = showContextMenuParams; - if (ReportActionComposeFocusManager.isFocused()) { + if (ComposerFocusManager.isComposerFocused()) { setComposerToRefocusOnClose('main'); - } else if (ReportActionComposeFocusManager.isEditFocused()) { + } else if (ComposerFocusManager.isEditComposerFocused()) { setComposerToRefocusOnClose('edit'); } @@ -305,7 +304,7 @@ function PopoverReportActionContextMenu({ref}: PopoverReportActionContextMenuPro type: Actions.CLOSE_POPOVER, }); - refocusComposerAfterPreventFirstResponder(composerToRefocusOnClose).then(() => { + ComposerFocusManager.refocusComposerAfterPreventFirstResponder(composerToRefocusOnClose).then(() => { setComposerToRefocusOnClose(undefined); }); }; diff --git a/src/pages/home/report/ContextMenu/ReportActionContextMenu.ts b/src/pages/home/report/ContextMenu/ReportActionContextMenu.ts index 2da4ac2bbffc8..0c9e1a922e2ea 100644 --- a/src/pages/home/report/ContextMenu/ReportActionContextMenu.ts +++ b/src/pages/home/report/ContextMenu/ReportActionContextMenu.ts @@ -4,7 +4,7 @@ import type {RefObject} from 'react'; import type {GestureResponderEvent, Text as RNText, TextInput, View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; -import type {ComposerType} from '@libs/ReportActionComposeFocusManager'; +import type {ComposerType} from '@libs/ComposerFocusManager'; import type CONST from '@src/CONST'; import type {ReportAction} from '@src/types/onyx'; import type {ContextMenuAction} from './ContextMenuActions'; diff --git a/src/pages/home/report/PureReportActionItem.tsx b/src/pages/home/report/PureReportActionItem.tsx index 1a4a304bea68f..654b43a4e9238 100644 --- a/src/pages/home/report/PureReportActionItem.tsx +++ b/src/pages/home/report/PureReportActionItem.tsx @@ -430,6 +430,9 @@ type PureReportActionItemProps = { /** Report metadata for the report */ reportMetadata?: OnyxEntry; + + /** Whether this component is inside the side panel */ + isInSidePanel?: boolean; }; // This is equivalent to returning a negative boolean in normal functions, but we can keep the element return type @@ -502,6 +505,7 @@ function PureReportActionItem({ reportNameValuePairsOrigin, reportNameValuePairsOriginalID, reportMetadata, + isInSidePanel = false, }: PureReportActionItemProps) { const actionSheetAwareScrollViewContext = useContext(ActionSheetAwareScrollView.ActionSheetAwareScrollViewContext); const {translate, formatPhoneNumber, localeCompare, formatTravelDate, getLocalDateFromDatetime} = useLocalize(); @@ -1650,6 +1654,7 @@ function PureReportActionItem({ (chatIncludesConcierge(report) && isBlockedFromConcierge(blockedFromConcierge)) || isArchivedNonExpenseReport(report, isArchivedRoom) } isGroupPolicyReport={!!report?.policyID && report.policyID !== CONST.POLICY.ID_FAKE} + isInSidePanel={isInSidePanel} /> )} diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx index 944099f89ebf6..0c58492990346 100644 --- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx +++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx @@ -23,6 +23,8 @@ import useThemeStyles from '@hooks/useThemeStyles'; import {isMobileSafari} from '@libs/Browser'; import canFocusInputOnScreenFocus from '@libs/canFocusInputOnScreenFocus'; import {forceClearInput} from '@libs/ComponentUtils'; +import ComposerFocusManager from '@libs/ComposerFocusManager'; +import type {FocusContext} from '@libs/ComposerFocusManager'; import {canSkipTriggerHotkeys, findCommonSuffixLength, insertText, insertWhiteSpaceAtIndex} from '@libs/ComposerUtils'; import convertToLTRForComposer from '@libs/convertToLTRForComposer'; import {containsOnlyEmojis, extractEmojis, getAddedEmojis, getZWNJCursorOffset, insertZWNJBetweenDigitAndEmoji, replaceAndExtractEmojis} from '@libs/EmojiUtils'; @@ -32,7 +34,6 @@ import getPlatform from '@libs/getPlatform'; import {addKeyDownPressListener, removeKeyDownPressListener} from '@libs/KeyboardShortcut/KeyDownPressListener'; import {detectAndRewritePaste} from '@libs/MarkdownLinkHelpers'; import Parser from '@libs/Parser'; -import ReportActionComposeFocusManager from '@libs/ReportActionComposeFocusManager'; import {isValidReportIDFromPath, shouldAutoFocusOnKeyPress} from '@libs/ReportUtils'; import updateMultilineInputRange from '@libs/updateMultilineInputRange'; import willBlurTextInputOnTapOutsideFunc from '@libs/willBlurTextInputOnTapOutside'; @@ -43,7 +44,6 @@ import SilentCommentUpdater from '@pages/home/report/ReportActionCompose/SilentC import Suggestions from '@pages/home/report/ReportActionCompose/Suggestions'; import {isEmojiPickerVisible} from '@userActions/EmojiPickerAction'; import type {OnEmojiSelected} from '@userActions/EmojiPickerAction'; -import {inputFocusChange} from '@userActions/InputFocus'; import {areAllModalsHidden} from '@userActions/Modal'; import {broadcastUserIsTyping, saveReportActionDraft, saveReportDraftComment} from '@userActions/Report'; import CONST from '@src/CONST'; @@ -138,6 +138,9 @@ type ComposerWithSuggestionsProps = Partial & /** Whether the main composer was hidden */ didHideComposerInput?: boolean; + /** Whether this composer is inside the side panel */ + isInSidePanel?: boolean; + /** Reference to the outer element */ ref?: ForwardedRef; }; @@ -230,6 +233,9 @@ function ComposerWithSuggestions({ // Fullstory forwardedFSClass, + + // Side panel + isInSidePanel = false, }: ComposerWithSuggestionsProps) { const {isKeyboardShown} = useKeyboardState(); const theme = useTheme(); @@ -237,6 +243,7 @@ function ComposerWithSuggestions({ const StyleUtils = useStyleUtils(); const {preferredLocale} = useLocalize(); const {isSidePanelHiddenOrLargeScreen} = useSidePanel(); + const focusContext: FocusContext = isInSidePanel ? 'sidePanel' : 'main'; const isFocused = useIsFocused(); const navigation = useNavigation(); const emojisPresentBefore = useRef([]); @@ -255,7 +262,6 @@ function ComposerWithSuggestions({ const [modal] = useOnyx(ONYXKEYS.MODAL, {canBeMissing: true}); const [preferredSkinTone = CONST.EMOJI_DEFAULT_SKIN_TONE] = useOnyx(ONYXKEYS.PREFERRED_EMOJI_SKIN_TONE, {canBeMissing: true}); - const [editFocused] = useOnyx(ONYXKEYS.INPUT_FOCUSED, {canBeMissing: true}); const lastTextRef = useRef(value); useEffect(() => { @@ -264,7 +270,8 @@ function ComposerWithSuggestions({ const {shouldUseNarrowLayout} = useResponsiveLayout(); const maxComposerLines = shouldUseNarrowLayout ? CONST.COMPOSER.MAX_LINES_SMALL_SCREEN : CONST.COMPOSER.MAX_LINES; - const shouldAutoFocus = (shouldFocusInputOnScreenFocus || !!draftComment) && shouldShowComposeInput && areAllModalsHidden() && isFocused && !didHideComposerInput; + // Side panel composer should never auto-focus - main composer takes priority on page load + const shouldAutoFocus = !isInSidePanel && (shouldFocusInputOnScreenFocus || !!draftComment) && shouldShowComposeInput && areAllModalsHidden() && isFocused && !didHideComposerInput; const valueRef = useRef(value); valueRef.current = value; @@ -286,13 +293,13 @@ function ComposerWithSuggestions({ */ const setTextInputRef = useCallback( (el: TextInput) => { - ReportActionComposeFocusManager.composerRef.current = el; + ComposerFocusManager.getComposerRef(focusContext).current = el; textInputRef.current = el; if (typeof animatedRef === 'function') { animatedRef(el); } }, - [animatedRef], + [animatedRef, focusContext], ); const resetKeyboardInput = useCallback(() => { @@ -580,7 +587,6 @@ function ComposerWithSuggestions({ if (!suggestionsRef.current) { return false; } - inputFocusChange(false); return suggestionsRef.current.setShouldBlockSuggestionCalc(false); }, [suggestionsRef]); @@ -598,15 +604,19 @@ function ComposerWithSuggestions({ */ const setUpComposeFocusManager = useCallback( (shouldTakeOverFocus = false) => { - ReportActionComposeFocusManager.onComposerFocus((shouldFocusForNonBlurInputOnTapOutside = false) => { - if ((!willBlurTextInputOnTapOutside && !shouldFocusForNonBlurInputOnTapOutside) || !isFocused || !isSidePanelHiddenOrLargeScreen) { - return; - } + ComposerFocusManager.onComposerFocus( + (shouldFocusForNonBlurInputOnTapOutside = false) => { + if ((!willBlurTextInputOnTapOutside && !shouldFocusForNonBlurInputOnTapOutside) || !isFocused || !isSidePanelHiddenOrLargeScreen) { + return; + } - focus(true); - }, shouldTakeOverFocus); + focus(true); + }, + shouldTakeOverFocus, + focusContext, + ); }, - [focus, isFocused, isSidePanelHiddenOrLargeScreen], + [focus, isFocused, isSidePanelHiddenOrLargeScreen, focusContext], ); /** @@ -667,7 +677,7 @@ function ComposerWithSuggestions({ const unsubscribeNavigationFocus = navigation.addListener('focus', () => { addKeyDownPressListener(focusComposerOnKeyPress); // The report isn't unmounted and can be focused again after going back from another report so we should update the composerRef again - ReportActionComposeFocusManager.composerRef.current = textInputRef.current; + ComposerFocusManager.getComposerRef(focusContext).current = textInputRef.current; setUpComposeFocusManager(); }); addKeyDownPressListener(focusComposerOnKeyPress); @@ -675,13 +685,13 @@ function ComposerWithSuggestions({ setUpComposeFocusManager(); return () => { - ReportActionComposeFocusManager.clear(); + ComposerFocusManager.clearComposerFocus(false, focusContext); removeKeyDownPressListener(focusComposerOnKeyPress); unsubscribeNavigationBlur(); unsubscribeNavigationFocus(); }; - }, [focusComposerOnKeyPress, navigation, setUpComposeFocusManager, isSidePanelHiddenOrLargeScreen]); + }, [focusComposerOnKeyPress, navigation, setUpComposeFocusManager, isSidePanelHiddenOrLargeScreen, focusContext]); const prevIsModalVisible = usePrevious(modal?.isVisible); const prevIsFocused = usePrevious(isFocused); @@ -711,12 +721,11 @@ function ComposerWithSuggestions({ return; } - if (editFocused) { - inputFocusChange(false); + if (ComposerFocusManager.isEditComposerFocused()) { return; } focus(true); - }, [focus, prevIsFocused, editFocused, prevIsModalVisible, isFocused, modal?.isVisible, isNextModalWillOpenRef, shouldAutoFocus, isSidePanelHiddenOrLargeScreen]); + }, [focus, prevIsFocused, prevIsModalVisible, isFocused, modal?.isVisible, isNextModalWillOpenRef, shouldAutoFocus, isSidePanelHiddenOrLargeScreen]); useEffect(() => { // Scrolls the composer to the bottom and sets the selection to the end, so that longer drafts are easier to edit diff --git a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx index ff5d573c478b7..dc01ae44efba4 100644 --- a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx +++ b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx @@ -592,6 +592,7 @@ function ReportActionCompose({ onValueChange={onValueChange} didHideComposerInput={didHideComposerInput} forwardedFSClass={fsClass} + isInSidePanel={isInSidePanel} /> {shouldDisplayDualDropZone && ( ; }; @@ -94,11 +97,6 @@ const shouldUseForcedSelectionRange = shouldUseEmojiPickerSelection(); // video source -> video attributes const draftMessageVideoAttributeCache = new Map(); -const DEFAULT_MODAL_VALUE = { - willAlertModalBecomeVisible: false, - isVisible: false, -}; - function ReportActionItemMessageEdit({ action, draftMessage, @@ -108,8 +106,10 @@ function ReportActionItemMessageEdit({ index, isGroupPolicyReport, shouldDisableEmojiPicker = false, + isInSidePanel = false, ref, }: ReportActionItemMessageEditProps) { + const focusContext: FocusContext = isInSidePanel ? 'sidePanel' : 'main'; const [preferredSkinTone = CONST.EMOJI_DEFAULT_SKIN_TONE] = useOnyx(ONYXKEYS.PREFERRED_EMOJI_SKIN_TONE, {canBeMissing: true}); const {email} = useCurrentUserPersonalDetails(); const theme = useTheme(); @@ -138,10 +138,6 @@ function ReportActionItemMessageEdit({ const debouncedValidateCommentMaxLength = useMemo(() => lodashDebounce(validateCommentMaxLength, CONST.TIMING.COMMENT_LENGTH_DEBOUNCE_TIME), [validateCommentMaxLength]); const {isScrollLayoutTriggered, raiseIsScrollLayoutTriggered} = useIsScrollLikelyLayoutTriggered(); - - const [modal = DEFAULT_MODAL_VALUE] = useOnyx(ONYXKEYS.MODAL, {canBeMissing: true}); - const [onyxInputFocused = false] = useOnyx(ONYXKEYS.INPUT_FOCUSED, {canBeMissing: true}); - const {isScrolling, startScrollBlock, endScrollBlock} = useScrollBlocker(); const textInputRef = useRef<(HTMLTextAreaElement & TextInput) | null>(null); @@ -168,24 +164,20 @@ function ReportActionItemMessageEdit({ setDraft(draftMessage); }, [draftMessage, action, prevDraftMessage]); - useEffect(() => { - composerFocusKeepFocusOn(textInputRef.current as HTMLElement, isFocused, modal, onyxInputFocused); - }, [isFocused, modal, onyxInputFocused]); - useEffect( // Remove focus callback on unmount to avoid stale callbacks () => { if (textInputRef.current) { - ReportActionComposeFocusManager.editComposerRef.current = textInputRef.current; + ComposerFocusManager.getEditComposerRef(focusContext).current = textInputRef.current; } return () => { - if (ReportActionComposeFocusManager.editComposerRef.current !== textInputRef.current) { + if (ComposerFocusManager.getEditComposerRef(focusContext).current !== textInputRef.current) { return; } - ReportActionComposeFocusManager.clear(true); + ComposerFocusManager.clearComposerFocus(true, focusContext); }; }, - [], + [focusContext], ); // We consider the report action active if it's focused, its emoji picker is open or its context menu is open @@ -204,11 +196,15 @@ function ReportActionItemMessageEdit({ // Take over focus priority const setUpComposeFocusManager = useCallback(() => { - ReportActionComposeFocusManager.onComposerFocus(() => { - focus(true, emojiPickerSelectionRef.current ? {...emojiPickerSelectionRef.current} : undefined); - emojiPickerSelectionRef.current = undefined; - }, true); - }, [focus]); + ComposerFocusManager.onComposerFocus( + () => { + focus(true, emojiPickerSelectionRef.current ? {...emojiPickerSelectionRef.current} : undefined); + emojiPickerSelectionRef.current = undefined; + }, + true, + focusContext, + ); + }, [focus, focusContext]); // show the composer after editing is complete for devices that hide the composer during editing. useEffect(() => () => setShouldShowComposeInput(true), []); @@ -283,10 +279,10 @@ function ReportActionItemMessageEdit({ deleteReportActionDraft(reportID, action); if (isActive()) { - ReportActionComposeFocusManager.clear(true); + ComposerFocusManager.clearComposerFocus(true, focusContext); // Wait for report action compose re-mounting on mWeb // eslint-disable-next-line @typescript-eslint/no-deprecated - InteractionManager.runAfterInteractions(() => ReportActionComposeFocusManager.focus()); + InteractionManager.runAfterInteractions(() => ComposerFocusManager.focusComposer(focusContext)); } // Scroll to the last comment after editing to make sure the whole comment is clearly visible in the report. @@ -295,7 +291,7 @@ function ReportActionItemMessageEdit({ reportScrollManager.scrollToIndex(index, false); }); } - }, [action, index, reportID, reportScrollManager, isActive]); + }, [action, index, reportID, reportScrollManager, isActive, focusContext]); /** * Save the draft of the comment to be the new comment message. This will take the comment out of "edit mode" with @@ -527,6 +523,7 @@ function ReportActionItemMessageEdit({ ref.current = el; } }} + autoFocus onChangeText={updateDraft} // Debounced saveDraftComment onKeyPress={triggerSaveOrCancel} value={draft} @@ -535,7 +532,7 @@ function ReportActionItemMessageEdit({ onFocus={() => { setIsFocused(true); if (textInputRef.current) { - ReportActionComposeFocusManager.editComposerRef.current = textInputRef.current; + ComposerFocusManager.getEditComposerRef(focusContext).current = textInputRef.current; } startScrollBlock(); // eslint-disable-next-line @typescript-eslint/no-deprecated @@ -603,7 +600,7 @@ function ReportActionItemMessageEdit({ if (activeElementId === CONST.COMPOSER.NATIVE_ID || activeElementId === CONST.EMOJI_PICKER_BUTTON_NATIVE_ID) { return; } - ReportActionComposeFocusManager.focus(); + ComposerFocusManager.focusComposer(focusContext); }} onEmojiSelected={addEmojiToTextBox} emojiPickerID={action.reportActionID} diff --git a/src/pages/home/report/ReportActionsList.tsx b/src/pages/home/report/ReportActionsList.tsx index 367db0380bfeb..c07bb6a2fba1a 100644 --- a/src/pages/home/report/ReportActionsList.tsx +++ b/src/pages/home/report/ReportActionsList.tsx @@ -121,6 +121,9 @@ type ReportActionsListProps = { /** Whether the optimistic CREATED report action was added */ hasCreatedActionAdded?: boolean; + + /** Whether this list is inside the side panel */ + isInSidePanel?: boolean; }; // In the component we are subscribing to the arrival of new actions. @@ -160,6 +163,7 @@ function ReportActionsList({ shouldEnableAutoScrollToTopThreshold, parentReportActionForTransactionThread, hasCreatedActionAdded, + isInSidePanel = false, }: ReportActionsListProps) { const prevHasCreatedActionAdded = usePrevious(hasCreatedActionAdded); const currentUserPersonalDetails = useCurrentUserPersonalDetails(); @@ -733,6 +737,7 @@ function ReportActionsList({ isTryNewDotNVPDismissed={isTryNewDotNVPDismissed} reportNameValuePairsOrigin={reportNameValuePairs?.origin} reportNameValuePairsOriginalID={reportNameValuePairs?.originalID} + isInSidePanel={isInSidePanel} /> ); }, @@ -762,6 +767,7 @@ function ReportActionsList({ isReportArchived, reportNameValuePairs?.origin, reportNameValuePairs?.originalID, + isInSidePanel, ], ); diff --git a/src/pages/home/report/ReportActionsListItemRenderer.tsx b/src/pages/home/report/ReportActionsListItemRenderer.tsx index d0e564ec3d2e6..b638dfd50fd2f 100644 --- a/src/pages/home/report/ReportActionsListItemRenderer.tsx +++ b/src/pages/home/report/ReportActionsListItemRenderer.tsx @@ -103,6 +103,9 @@ type ReportActionsListItemRendererProps = { /** Report name value pairs originalID */ reportNameValuePairsOriginalID?: string; + + /** Whether this component is inside the side panel */ + isInSidePanel?: boolean; }; function ReportActionsListItemRenderer({ @@ -138,6 +141,7 @@ function ReportActionsListItemRenderer({ isReportArchived = false, reportNameValuePairsOrigin, reportNameValuePairsOriginalID, + isInSidePanel = false, }: ReportActionsListItemRendererProps) { const originalMessage = useMemo(() => getOriginalMessage(reportAction), [reportAction]); @@ -270,6 +274,7 @@ function ReportActionsListItemRenderer({ isTryNewDotNVPDismissed={isTryNewDotNVPDismissed} reportNameValuePairsOrigin={reportNameValuePairsOrigin} reportNameValuePairsOriginalID={reportNameValuePairsOriginalID} + isInSidePanel={isInSidePanel} /> ); } diff --git a/src/pages/home/report/ReportActionsView.tsx b/src/pages/home/report/ReportActionsView.tsx index a1e2c00310d79..7b866dac63fbb 100755 --- a/src/pages/home/report/ReportActionsView.tsx +++ b/src/pages/home/report/ReportActionsView.tsx @@ -66,6 +66,9 @@ type ReportActionsViewProps = { /** If the report is a transaction thread report */ isReportTransactionThread?: boolean; + + /** Whether this view is inside the side panel */ + isInSidePanel?: boolean; }; let listOldID = Math.round(Math.random() * 100); @@ -79,6 +82,7 @@ function ReportActionsView({ hasNewerActions, hasOlderActions, isReportTransactionThread, + isInSidePanel = false, }: ReportActionsViewProps) { useCopySelectionHelper(); const route = useRoute>(); @@ -331,6 +335,7 @@ function ReportActionsView({ listID={listID} shouldEnableAutoScrollToTopThreshold={shouldEnableAutoScroll} hasCreatedActionAdded={shouldAddCreatedAction} + isInSidePanel={isInSidePanel} /> diff --git a/src/pages/iou/request/step/IOURequestStepCurrencyModal.tsx b/src/pages/iou/request/step/IOURequestStepCurrencyModal.tsx index 0c3c1a3090807..b9f22822361f0 100644 --- a/src/pages/iou/request/step/IOURequestStepCurrencyModal.tsx +++ b/src/pages/iou/request/step/IOURequestStepCurrencyModal.tsx @@ -47,7 +47,6 @@ function IOURequestStepCurrencyModal({isPickerVisible, hidePickerModal, headerTe isVisible={isPickerVisible} onClose={hidePickerModal} onModalHide={hidePickerModal} - shouldEnableNewFocusManagement onBackdropPress={Navigation.dismissModal} shouldUseModalPaddingStyle={false} shouldHandleNavigationBack diff --git a/src/pages/iou/request/step/IOURequestStepSubrate.tsx b/src/pages/iou/request/step/IOURequestStepSubrate.tsx index e27def6ae5e3c..b52a8902404dd 100644 --- a/src/pages/iou/request/step/IOURequestStepSubrate.tsx +++ b/src/pages/iou/request/step/IOURequestStepSubrate.tsx @@ -164,7 +164,6 @@ function IOURequestStepSubrate({ prompt: translate('iou.deleteSubrateConfirmation'), confirmText: translate('common.delete'), cancelText: translate('common.cancel'), - shouldEnableNewFocusManagement: true, danger: true, }); if (result.action !== ModalActions.CONFIRM) { diff --git a/src/pages/iou/request/step/IOURequestStepWaypoint.tsx b/src/pages/iou/request/step/IOURequestStepWaypoint.tsx index 0f2f0b7f1c697..ff6ea01ee9948 100644 --- a/src/pages/iou/request/step/IOURequestStepWaypoint.tsx +++ b/src/pages/iou/request/step/IOURequestStepWaypoint.tsx @@ -164,7 +164,6 @@ function IOURequestStepWaypoint({ prompt: translate('distance.deleteWaypointConfirmation'), confirmText: translate('common.delete'), cancelText: translate('common.cancel'), - shouldEnableNewFocusManagement: true, danger: true, }); if (result.action !== ModalActions.CONFIRM) { diff --git a/src/pages/workspace/accounting/intacct/import/SageIntacctEditUserDimensionsPage.tsx b/src/pages/workspace/accounting/intacct/import/SageIntacctEditUserDimensionsPage.tsx index d25d10f022e34..216c7d2ba6ad9 100644 --- a/src/pages/workspace/accounting/intacct/import/SageIntacctEditUserDimensionsPage.tsx +++ b/src/pages/workspace/accounting/intacct/import/SageIntacctEditUserDimensionsPage.tsx @@ -145,7 +145,6 @@ function SageIntacctEditUserDimensionsPage({route}: SageIntacctEditUserDimension confirmText={translate('common.remove')} cancelText={translate('common.cancel')} danger - shouldEnableNewFocusManagement /> diff --git a/src/pages/workspace/expensifyCard/WorkspaceEditCardLimitPage.tsx b/src/pages/workspace/expensifyCard/WorkspaceEditCardLimitPage.tsx index ed02de2f517bc..607f229b0d06a 100644 --- a/src/pages/workspace/expensifyCard/WorkspaceEditCardLimitPage.tsx +++ b/src/pages/workspace/expensifyCard/WorkspaceEditCardLimitPage.tsx @@ -170,7 +170,6 @@ function WorkspaceEditCardLimitPage({route}: WorkspaceEditCardLimitPageProps) { confirmText={translate('workspace.expensifyCard.changeLimit')} cancelText={translate('common.cancel')} danger - shouldEnableNewFocusManagement /> )} diff --git a/src/pages/workspace/expensifyCard/WorkspaceEditCardLimitTypePage.tsx b/src/pages/workspace/expensifyCard/WorkspaceEditCardLimitTypePage.tsx index 13420f3fb68f2..d9fb713a41b83 100644 --- a/src/pages/workspace/expensifyCard/WorkspaceEditCardLimitTypePage.tsx +++ b/src/pages/workspace/expensifyCard/WorkspaceEditCardLimitTypePage.tsx @@ -187,7 +187,6 @@ function WorkspaceEditCardLimitTypePage({route}: WorkspaceEditCardLimitTypePageP confirmText={translate('workspace.expensifyCard.changeLimitType')} cancelText={translate('common.cancel')} danger - shouldEnableNewFocusManagement />