From 2a84bbd697a74a5b16fc1d78e24ede09ce283c16 Mon Sep 17 00:00:00 2001 From: staszekscp Date: Fri, 27 Feb 2026 13:40:36 +0100 Subject: [PATCH 1/4] Migrate ReportActionItemContext --- .../ReportActionItem/MoneyRequestAction.tsx | 7 +-- .../DuplicateTransactionItem.tsx | 51 ++++++++++--------- .../inbox/report/ReportActionItemContext.tsx | 10 ---- .../report/ReportActionItemContext/default.ts | 11 ++++ .../report/ReportActionItemContext/index.tsx | 17 +++++++ .../report/ReportActionItemContext/types.ts | 9 ++++ 6 files changed, 68 insertions(+), 37 deletions(-) delete mode 100644 src/pages/inbox/report/ReportActionItemContext.tsx create mode 100644 src/pages/inbox/report/ReportActionItemContext/default.ts create mode 100644 src/pages/inbox/report/ReportActionItemContext/index.tsx create mode 100644 src/pages/inbox/report/ReportActionItemContext/types.ts diff --git a/src/components/ReportActionItem/MoneyRequestAction.tsx b/src/components/ReportActionItem/MoneyRequestAction.tsx index 7edc41301348..1f41300d8b84 100644 --- a/src/components/ReportActionItem/MoneyRequestAction.tsx +++ b/src/components/ReportActionItem/MoneyRequestAction.tsx @@ -1,6 +1,6 @@ import {useRoute} from '@react-navigation/native'; import lodashIsEmpty from 'lodash/isEmpty'; -import React, {useContext, useMemo} from 'react'; +import React, {useMemo} from 'react'; import type {StyleProp, ViewStyle} from 'react-native'; import RenderHTML from '@components/RenderHTML'; import useLocalize from '@hooks/useLocalize'; @@ -24,7 +24,7 @@ import { } from '@libs/ReportActionsUtils'; import type {ContextMenuAnchor} from '@pages/inbox/report/ContextMenu/ReportActionContextMenu'; import {contextMenuRef} from '@pages/inbox/report/ContextMenu/ReportActionContextMenu'; -import ReportActionItemContext from '@pages/inbox/report/ReportActionItemContext'; +import {useReportActionItemActions, useReportActionItemState} from '@pages/inbox/report/ReportActionItemContext'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -82,7 +82,8 @@ function MoneyRequestAction({ isWhisper = false, shouldDisplayContextMenu = true, }: MoneyRequestActionProps) { - const {shouldOpenReportInRHP, onPreviewPressed} = useContext(ReportActionItemContext); + const {shouldOpenReportInRHP} = useReportActionItemState(); + const {onPreviewPressed} = useReportActionItemActions(); const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`); const [iouReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${requestReportID}`); const [reportActions] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${chatReportID}`, {canEvict: false}); diff --git a/src/pages/TransactionDuplicate/DuplicateTransactionItem.tsx b/src/pages/TransactionDuplicate/DuplicateTransactionItem.tsx index 2d468b6bd7db..d293b94ddb22 100644 --- a/src/pages/TransactionDuplicate/DuplicateTransactionItem.tsx +++ b/src/pages/TransactionDuplicate/DuplicateTransactionItem.tsx @@ -10,7 +10,7 @@ import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; import {getOriginalMessage, getReportAction, isMoneyRequestAction} from '@libs/ReportActionsUtils'; import {getOriginalReportID} from '@libs/ReportUtils'; import ReportActionItem from '@pages/inbox/report/ReportActionItem'; -import ReportActionItemContext from '@pages/inbox/report/ReportActionItemContext'; +import {ReportActionItemActionsContext, ReportActionItemStateContext} from '@pages/inbox/report/ReportActionItemContext'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Policy, Transaction} from '@src/types/onyx'; @@ -56,7 +56,8 @@ function DuplicateTransactionItem({transaction, index, policies, onPreviewPresse }, ); - const contextValue = useMemo(() => ({shouldOpenReportInRHP: true, onPreviewPressed}), [onPreviewPressed]); + const stateValue = useMemo(() => ({shouldOpenReportInRHP: true}), []); + const actionsValue = useMemo(() => ({onPreviewPressed}), [onPreviewPressed]); if (!action || !report) { return null; @@ -67,28 +68,30 @@ function DuplicateTransactionItem({transaction, index, policies, onPreviewPresse return ( - - - + + + + + ); } diff --git a/src/pages/inbox/report/ReportActionItemContext.tsx b/src/pages/inbox/report/ReportActionItemContext.tsx deleted file mode 100644 index 0ed3bfef9e40..000000000000 --- a/src/pages/inbox/report/ReportActionItemContext.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import {createContext} from 'react'; - -type ReportActionItemContextType = { - shouldOpenReportInRHP: boolean; - onPreviewPressed?: (reportID: string) => void; -}; - -const ReportActionItemContext = createContext({shouldOpenReportInRHP: false}); - -export default ReportActionItemContext; diff --git a/src/pages/inbox/report/ReportActionItemContext/default.ts b/src/pages/inbox/report/ReportActionItemContext/default.ts new file mode 100644 index 000000000000..3e771ff5326a --- /dev/null +++ b/src/pages/inbox/report/ReportActionItemContext/default.ts @@ -0,0 +1,11 @@ +import type {ReportActionItemActionsContextType, ReportActionItemStateContextType} from './types'; + +const defaultReportActionItemStateContextValue: ReportActionItemStateContextType = { + shouldOpenReportInRHP: false, +}; + +const defaultReportActionItemActionsContextValue: ReportActionItemActionsContextType = { + onPreviewPressed: undefined, +}; + +export {defaultReportActionItemStateContextValue, defaultReportActionItemActionsContextValue}; diff --git a/src/pages/inbox/report/ReportActionItemContext/index.tsx b/src/pages/inbox/report/ReportActionItemContext/index.tsx new file mode 100644 index 000000000000..f302bad8d24c --- /dev/null +++ b/src/pages/inbox/report/ReportActionItemContext/index.tsx @@ -0,0 +1,17 @@ +import {createContext, useContext} from 'react'; +import {defaultReportActionItemActionsContextValue, defaultReportActionItemStateContextValue} from './default'; +import type {ReportActionItemActionsContextType, ReportActionItemStateContextType} from './types'; + +const ReportActionItemStateContext = createContext(defaultReportActionItemStateContextValue); +const ReportActionItemActionsContext = createContext(defaultReportActionItemActionsContextValue); + +function useReportActionItemState(): ReportActionItemStateContextType { + return useContext(ReportActionItemStateContext); +} + +function useReportActionItemActions(): ReportActionItemActionsContextType { + return useContext(ReportActionItemActionsContext); +} + +export {ReportActionItemStateContext, ReportActionItemActionsContext, useReportActionItemState, useReportActionItemActions}; +export type {ReportActionItemActionsContextType, ReportActionItemStateContextType} from './types'; diff --git a/src/pages/inbox/report/ReportActionItemContext/types.ts b/src/pages/inbox/report/ReportActionItemContext/types.ts new file mode 100644 index 000000000000..c506bf97bd0a --- /dev/null +++ b/src/pages/inbox/report/ReportActionItemContext/types.ts @@ -0,0 +1,9 @@ +type ReportActionItemStateContextType = { + shouldOpenReportInRHP: boolean; +}; + +type ReportActionItemActionsContextType = { + onPreviewPressed?: (reportID: string) => void; +}; + +export type {ReportActionItemStateContextType, ReportActionItemActionsContextType}; From db80b0d9fa7a781c251c668b833d90c2e58b00eb Mon Sep 17 00:00:00 2001 From: staszekscp Date: Fri, 27 Feb 2026 16:00:58 +0100 Subject: [PATCH 2/4] MigrateShowContextMenuContext --- .../BaseAnchorForAttachmentsOnly.tsx | 77 +++--- .../HTMLRenderers/ImageRenderer.tsx | 85 +++--- .../MentionReportRenderer/index.tsx | 49 ++-- .../HTMLRenderers/MentionUserRenderer.tsx | 92 ++++--- .../HTMLRenderers/PreRenderer.tsx | 54 ++-- .../HTMLRenderers/VideoRenderer.tsx | 66 +++-- .../MoneyRequestConfirmationListFooter.tsx | 70 ++--- .../ReportActionItemImage.tsx | 55 ++-- src/components/ReportActionItem/TaskView.tsx | 248 +++++++++--------- .../ShowContextMenuContext/default.ts | 18 ++ .../index.tsx} | 41 ++- .../ShowContextMenuContext/types.ts | 20 ++ .../VideoPlayerThumbnail.tsx | 62 +++-- .../TransactionDuplicate/Confirmation.tsx | 38 +-- .../inbox/report/PureReportActionItem.tsx | 163 ++++++------ .../report/ReportActionItemContentCreated.tsx | 76 +++--- tests/unit/MentionUserRendererTest.tsx | 17 +- tests/unit/VideoRendererTest.tsx | 23 +- 18 files changed, 662 insertions(+), 592 deletions(-) create mode 100644 src/components/ShowContextMenuContext/default.ts rename src/components/{ShowContextMenuContext.ts => ShowContextMenuContext/index.tsx} (61%) create mode 100644 src/components/ShowContextMenuContext/types.ts diff --git a/src/components/AnchorForAttachmentsOnly/BaseAnchorForAttachmentsOnly.tsx b/src/components/AnchorForAttachmentsOnly/BaseAnchorForAttachmentsOnly.tsx index 22ad207ba9e8..d43c203b50d3 100644 --- a/src/components/AnchorForAttachmentsOnly/BaseAnchorForAttachmentsOnly.tsx +++ b/src/components/AnchorForAttachmentsOnly/BaseAnchorForAttachmentsOnly.tsx @@ -2,7 +2,7 @@ import React from 'react'; import AttachmentView from '@components/Attachments/AttachmentView'; import {useSession} from '@components/OnyxListItemProvider'; import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback'; -import {ShowContextMenuContext, showContextMenuForReport} from '@components/ShowContextMenuContext'; +import {showContextMenuForReport, useShowContextMenuActions, useShowContextMenuState} from '@components/ShowContextMenuContext'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useOnyx from '@hooks/useOnyx'; @@ -39,45 +39,44 @@ function BaseAnchorForAttachmentsOnly({style, source = '', displayName = '', onP const encryptedAuthToken = session?.encryptedAuthToken ?? ''; const sourceURLWithAuth = addEncryptedAuthTokenToURL(source, encryptedAuthToken); + const {anchor, report, isReportArchived, action, isDisabled, shouldDisplayContextMenu} = useShowContextMenuState(); + const {checkIfContextMenuActive} = useShowContextMenuActions(); + return ( - - {({anchor, report, isReportArchived, action, checkIfContextMenuActive, isDisabled, shouldDisplayContextMenu}) => ( - { - if (isDownloading || isOffline || !sourceID) { - return; - } - setDownload(sourceID, true); - fileDownload(translate, sourceURLWithAuth, displayName, '', isMobileSafari()).then(() => setDownload(sourceID, false)); - }} - onPressIn={onPressIn} - onPressOut={onPressOut} - onLongPress={(event) => { - if (isDisabled || !shouldDisplayContextMenu) { - return; - } - showContextMenuForReport(event, anchor, report?.reportID, action, checkIfContextMenuActive, isArchivedNonExpenseReport(report, isReportArchived)); - }} - shouldUseHapticsOnLongPress - accessibilityLabel={displayName} - role={CONST.ROLE.BUTTON} - sentryLabel={CONST.SENTRY_LABEL.BASE_ANCHOR_FOR_ATTACHMENTS_ONLY.DOWNLOAD_BUTTON} - > - - - )} - + { + if (isDownloading || isOffline || !sourceID) { + return; + } + setDownload(sourceID, true); + fileDownload(translate, sourceURLWithAuth, displayName, '', isMobileSafari()).then(() => setDownload(sourceID, false)); + }} + onPressIn={onPressIn} + onPressOut={onPressOut} + onLongPress={(event) => { + if (isDisabled || !shouldDisplayContextMenu) { + return; + } + showContextMenuForReport(event, anchor, report?.reportID, action, checkIfContextMenuActive, isArchivedNonExpenseReport(report, isReportArchived)); + }} + shouldUseHapticsOnLongPress + accessibilityLabel={displayName} + role={CONST.ROLE.BUTTON} + sentryLabel={CONST.SENTRY_LABEL.BASE_ANCHOR_FOR_ATTACHMENTS_ONLY.DOWNLOAD_BUTTON} + > + + ); } diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.tsx index 56247d66db95..c2ae94febd35 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.tsx +++ b/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.tsx @@ -4,7 +4,7 @@ import {AttachmentContext} from '@components/AttachmentContext'; import {getButtonRole} from '@components/Button/utils'; import {isDeletedNode} from '@components/HTMLEngineProvider/htmlEngineUtils'; import PressableWithoutFocus from '@components/Pressable/PressableWithoutFocus'; -import {ShowContextMenuContext, showContextMenuForReport} from '@components/ShowContextMenuContext'; +import {showContextMenuForReport, useShowContextMenuActions, useShowContextMenuState} from '@components/ShowContextMenuContext'; import ThumbnailImage from '@components/ThumbnailImage'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; @@ -90,53 +90,52 @@ function ImageRenderer({tnode}: CustomRendererProps) { /> ); + const {anchor, report, isReportArchived, action, isDisabled, shouldDisplayContextMenu} = useShowContextMenuState(); + const {onShowContextMenu, checkIfContextMenuActive} = useShowContextMenuActions(); + return imagePreviewModalDisabled ? ( thumbnailImageComponent ) : ( - - {({onShowContextMenu, anchor, report, isReportArchived, action, checkIfContextMenuActive, isDisabled, shouldDisplayContextMenu}) => ( - - {({reportID, accountID, type}) => ( - { - if (!source || !type) { - return; - } + + {({reportID, accountID, type}) => ( + { + if (!source || !type) { + return; + } - const attachmentLink = tnode.parent?.attributes?.href; - const route = ROUTES.REPORT_ATTACHMENTS?.getRoute({ - attachmentID, - reportID, - type, - source, - accountID, - isAuthTokenRequired: isAttachmentOrReceipt, - originalFileName: fileName, - attachmentLink, - }); - Navigation.navigate(route); - }} - onLongPress={(event) => { - if (isDisabled || !shouldDisplayContextMenu) { - return; - } - return onShowContextMenu(() => - showContextMenuForReport(event, anchor, report?.reportID, action, checkIfContextMenuActive, isArchivedNonExpenseReport(report, isReportArchived)), - ); - }} - isNested - shouldUseHapticsOnLongPress - role={getButtonRole(true)} - accessibilityLabel={translate('accessibilityHints.viewAttachment')} - sentryLabel={CONST.SENTRY_LABEL.HTML_RENDERER.IMAGE} - > - {thumbnailImageComponent} - - )} - + const attachmentLink = tnode.parent?.attributes?.href; + const route = ROUTES.REPORT_ATTACHMENTS?.getRoute({ + attachmentID, + reportID, + type, + source, + accountID, + isAuthTokenRequired: isAttachmentOrReceipt, + originalFileName: fileName, + attachmentLink, + }); + Navigation.navigate(route); + }} + onLongPress={(event) => { + if (isDisabled || !shouldDisplayContextMenu) { + return; + } + return onShowContextMenu(() => + showContextMenuForReport(event, anchor, report?.reportID, action, checkIfContextMenuActive, isArchivedNonExpenseReport(report, isReportArchived)), + ); + }} + isNested + shouldUseHapticsOnLongPress + role={getButtonRole(true)} + accessibilityLabel={translate('accessibilityHints.viewAttachment')} + sentryLabel={CONST.SENTRY_LABEL.HTML_RENDERER.IMAGE} + > + {thumbnailImageComponent} + )} - + ); } diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer/index.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer/index.tsx index acbfb286920a..c0a17325bb89 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer/index.tsx +++ b/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer/index.tsx @@ -2,7 +2,6 @@ import React, {useContext, useMemo} from 'react'; import type {TextStyle} from 'react-native'; import {StyleSheet} from 'react-native'; import type {CustomRendererProps, TPhrasing, TText} from 'react-native-render-html'; -import {ShowContextMenuContext} from '@components/ShowContextMenuContext'; import Text from '@components/Text'; import {useCurrentReportIDState} from '@hooks/useCurrentReportID'; import useOnyx from '@hooks/useOnyx'; @@ -52,32 +51,28 @@ function MentionReportRenderer({style, tnode, TDefaultRenderer, ...defaultRender const {color, ...styleWithoutColor} = flattenStyle; return ( - - {() => ( - { - event.preventDefault(); - Navigation.navigate(navigationRoute); - } - : undefined - } - role={isGroupPolicyReport ? CONST.ROLE.LINK : undefined} - accessibilityLabel={isGroupPolicyReport ? `/${navigationRoute}` : undefined} - > - #{mentionDisplayText} - - )} - + { + event.preventDefault(); + Navigation.navigate(navigationRoute); + } + : undefined + } + role={isGroupPolicyReport ? CONST.ROLE.LINK : undefined} + accessibilityLabel={isGroupPolicyReport ? `/${navigationRoute}` : undefined} + > + #{mentionDisplayText} + ); } diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.tsx index fac38983df18..540d0b66cab9 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.tsx +++ b/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.tsx @@ -7,7 +7,7 @@ import type {TextStyle} from 'react-native'; import type {CustomRendererProps, TPhrasing, TText} from 'react-native-render-html'; import {TNodeChildrenRenderer} from 'react-native-render-html'; import {usePersonalDetails} from '@components/OnyxListItemProvider'; -import {ShowContextMenuContext, showContextMenuForReport} from '@components/ShowContextMenuContext'; +import {showContextMenuForReport, useShowContextMenuActions, useShowContextMenuState} from '@components/ShowContextMenuContext'; import Text from '@components/Text'; import UserDetailsTooltip from '@components/UserDetailsTooltip'; import withCurrentUserPersonalDetails from '@components/withCurrentUserPersonalDetails'; @@ -33,6 +33,8 @@ function MentionUserRenderer({style, tnode, TDefaultRenderer, currentUserPersona const htmlAttribAccountID = tnode.attributes.accountid; const personalDetails = usePersonalDetails(); const htmlAttributeAccountID = tnode.attributes.accountid; + const {anchor, report, isReportArchived, action, isDisabled, shouldDisplayContextMenu} = useShowContextMenuState(); + const {onShowContextMenu, checkIfContextMenuActive} = useShowContextMenuActions(); let accountID: number; let mentionDisplayText: string; @@ -74,56 +76,52 @@ function MentionUserRenderer({style, tnode, TDefaultRenderer, currentUserPersona const {color, ...styleWithoutColor} = flattenStyle; return ( - - {({onShowContextMenu, anchor, report, isReportArchived, action, checkIfContextMenuActive, isDisabled, shouldDisplayContextMenu}) => ( + { + if (isDisabled || !shouldDisplayContextMenu) { + return; + } + return onShowContextMenu(() => + showContextMenuForReport(event, anchor, report?.reportID, action, checkIfContextMenuActive, isArchivedNonExpenseReport(report, isReportArchived)), + ); + }} + onPress={(event) => { + event.preventDefault(); + if (!isEmpty(htmlAttribAccountID)) { + Navigation.navigate(ROUTES.PROFILE.getRoute(parseInt(htmlAttribAccountID, 10), Navigation.getReportRHPActiveRoute())); + return; + } + Navigation.navigate(ROUTES.PROFILE.getRoute(accountID, Navigation.getReportRHPActiveRoute(), mentionDisplayText)); + }} + role={CONST.ROLE.LINK} + accessibilityLabel={`/${navigationRoute}`} + > + { - if (isDisabled || !shouldDisplayContextMenu) { - return; - } - return onShowContextMenu(() => - showContextMenuForReport(event, anchor, report?.reportID, action, checkIfContextMenuActive, isArchivedNonExpenseReport(report, isReportArchived)), - ); - }} - onPress={(event) => { - event.preventDefault(); - if (!isEmpty(htmlAttribAccountID)) { - Navigation.navigate(ROUTES.PROFILE.getRoute(parseInt(htmlAttribAccountID, 10), Navigation.getReportRHPActiveRoute())); - return; - } - Navigation.navigate(ROUTES.PROFILE.getRoute(accountID, Navigation.getReportRHPActiveRoute(), mentionDisplayText)); - }} + // eslint-disable-next-line react/jsx-props-no-spreading + {...defaultRendererProps} + style={[ + styles.link, + styleWithoutColor, + StyleUtils.getMentionStyle(isOurMention), + {color: StyleUtils.getMentionTextColor(isOurMention)}, + styles.breakWord, + styles.textWrap, + ]} role={CONST.ROLE.LINK} - accessibilityLabel={`/${navigationRoute}`} + testID="mention-user" + href={`/${navigationRoute}`} > - - - {htmlAttribAccountID ? `@${mentionDisplayText}` : } - - + {htmlAttribAccountID ? `@${mentionDisplayText}` : } - )} - + + ); } diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/PreRenderer.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/PreRenderer.tsx index 8392bfefc4cf..4330be01a190 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/PreRenderer.tsx +++ b/src/components/HTMLEngineProvider/HTMLRenderers/PreRenderer.tsx @@ -4,7 +4,7 @@ import type {GestureResponderEvent} from 'react-native'; import type {CustomRendererProps, TBlock} from 'react-native-render-html'; import * as HTMLEngineUtils from '@components/HTMLEngineProvider/htmlEngineUtils'; import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback'; -import {ShowContextMenuContext, showContextMenuForReport} from '@components/ShowContextMenuContext'; +import {showContextMenuForReport, useShowContextMenuActions, useShowContextMenuState} from '@components/ShowContextMenuContext'; import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useStyleUtils from '@hooks/useStyleUtils'; @@ -33,6 +33,8 @@ function PreRenderer({TDefaultRenderer, onPressIn, onPressOut, onLongPress, ...d const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); const {translate} = useLocalize(); + const {anchor, report, isReportArchived, action, isDisabled, shouldDisplayContextMenu} = useShowContextMenuState(); + const {onShowContextMenu, checkIfContextMenuActive} = useShowContextMenuActions(); const isLast = defaultRendererProps.renderIndex === defaultRendererProps.renderLength - 1; const isChildOfTaskTitle = HTMLEngineUtils.isChildOfTaskTitle(defaultRendererProps.tnode); @@ -51,33 +53,29 @@ function PreRenderer({TDefaultRenderer, onPressIn, onPressOut, onLongPress, ...d return ( - - {({onShowContextMenu, anchor, report, isReportArchived, action, checkIfContextMenuActive, isDisabled, shouldDisplayContextMenu}) => ( - {})} - onPressIn={onPressIn} - onPressOut={onPressOut} - onLongPress={(event) => { - onShowContextMenu(() => { - if (isDisabled || !shouldDisplayContextMenu) { - return; - } - return showContextMenuForReport(event, anchor, report?.reportID, action, checkIfContextMenuActive, isArchivedNonExpenseReport(report, isReportArchived)); - }); - }} - shouldUseHapticsOnLongPress - role={CONST.ROLE.PRESENTATION} - accessibilityLabel={translate('accessibilityHints.preStyledText')} - > - - - {/* eslint-disable-next-line react/jsx-props-no-spreading */} - - - - - )} - + {})} + onPressIn={onPressIn} + onPressOut={onPressOut} + onLongPress={(event) => { + onShowContextMenu(() => { + if (isDisabled || !shouldDisplayContextMenu) { + return; + } + return showContextMenuForReport(event, anchor, report?.reportID, action, checkIfContextMenuActive, isArchivedNonExpenseReport(report, isReportArchived)); + }); + }} + shouldUseHapticsOnLongPress + role={CONST.ROLE.PRESENTATION} + accessibilityLabel={translate('accessibilityHints.preStyledText')} + > + + + {/* eslint-disable-next-line react/jsx-props-no-spreading */} + + + + ); } diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/VideoRenderer.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/VideoRenderer.tsx index 2fd4075892b2..0eb1536b2770 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/VideoRenderer.tsx +++ b/src/components/HTMLEngineProvider/HTMLRenderers/VideoRenderer.tsx @@ -2,7 +2,7 @@ import React from 'react'; import type {CustomRendererProps, TBlock} from 'react-native-render-html'; import {AttachmentContext} from '@components/AttachmentContext'; import {isDeletedNode} from '@components/HTMLEngineProvider/htmlEngineUtils'; -import {ShowContextMenuContext} from '@components/ShowContextMenuContext'; +import {useShowContextMenuState} from '@components/ShowContextMenuContext'; import VideoPlayerPreview from '@components/VideoPlayerPreview'; import {getFileName} from '@libs/fileDownload/FileUtils'; import tryResolveUrlFromApiRoot from '@libs/tryResolveUrlFromApiRoot'; @@ -27,41 +27,39 @@ function VideoRenderer({tnode, key}: VideoRendererProps) { const isDeleted = isDeletedNode(tnode); const attachmentID = htmlAttribs[CONST.ATTACHMENT_ID_ATTRIBUTE]; + const {report} = useShowContextMenuState(); + return ( - - {({report}) => ( - - {({accountID, type, hashKey, reportID}) => ( - { - if (!sourceURL || !type) { - return; - } - const isAuthTokenRequired = !!htmlAttribs[CONST.ATTACHMENT_SOURCE_ATTRIBUTE]; - const route = ROUTES.REPORT_ATTACHMENTS.getRoute({ - attachmentID, - reportID: report?.reportID, - type, - source: sourceURL, - accountID, - isAuthTokenRequired, - hashKey, - }); - Navigation.navigate(route); - }} - /> - )} - + + {({accountID, type, hashKey, reportID}) => ( + { + if (!sourceURL || !type) { + return; + } + const isAuthTokenRequired = !!htmlAttribs[CONST.ATTACHMENT_SOURCE_ATTRIBUTE]; + const route = ROUTES.REPORT_ATTACHMENTS.getRoute({ + attachmentID, + reportID: report?.reportID, + type, + source: sourceURL, + accountID, + isAuthTokenRequired, + hashKey, + }); + Navigation.navigate(route); + }} + /> )} - + ); } diff --git a/src/components/MoneyRequestConfirmationListFooter.tsx b/src/components/MoneyRequestConfirmationListFooter.tsx index 61810fb55d90..eae2100bd7bb 100644 --- a/src/components/MoneyRequestConfirmationListFooter.tsx +++ b/src/components/MoneyRequestConfirmationListFooter.tsx @@ -68,7 +68,7 @@ import PDFThumbnail from './PDFThumbnail'; import PressableWithoutFocus from './Pressable/PressableWithoutFocus'; import ReceiptEmptyState from './ReceiptEmptyState'; import ReceiptImage from './ReceiptImage'; -import {ShowContextMenuContext} from './ShowContextMenuContext'; +import {ShowContextMenuActionsContext, ShowContextMenuStateContext} from './ShowContextMenuContext'; import Text from './Text'; type MoneyRequestConfirmationListFooterProps = { @@ -445,20 +445,26 @@ function MoneyRequestConfirmationListFooter({ // Time requests appear as regular expenses after they're created, with editable amount and merchant, not hours and rate const shouldShowTimeRequestFields = isTimeRequest && action === CONST.IOU.ACTION.CREATE; - const contextMenuContextValue = useMemo( + const contextMenuStateValue = useMemo( () => ({ anchor: null, report: undefined, isReportArchived: false, action: undefined, - checkIfContextMenuActive: () => {}, - onShowContextMenu: () => {}, isDisabled: true, shouldDisplayContextMenu: false, }), [], ); + const contextMenuActionsValue = useMemo( + () => ({ + checkIfContextMenuActive: () => {}, + onShowContextMenu: () => {}, + }), + [], + ); + const tagVisibility = useMemo( () => getTagVisibility({ @@ -517,33 +523,35 @@ function MoneyRequestConfirmationListFooter({ { item: ( - - - { - if (!transactionID) { - return; - } - - Navigation.navigate( - ROUTES.MONEY_REQUEST_STEP_DESCRIPTION.getRoute(action, iouType, transactionID, reportID, Navigation.getActiveRoute(), reportActionID), - ); - }} - style={[styles.moneyRequestMenuItem]} - titleStyle={styles.flex1} - disabled={didConfirm} - interactive={!isReadOnly} - numberOfLinesTitle={2} - rightLabel={isDescriptionRequired ? translate('common.required') : ''} - sentryLabel={CONST.SENTRY_LABEL.REQUEST_CONFIRMATION_LIST.DESCRIPTION_FIELD} - /> - - + + + + { + if (!transactionID) { + return; + } + + Navigation.navigate( + ROUTES.MONEY_REQUEST_STEP_DESCRIPTION.getRoute(action, iouType, transactionID, reportID, Navigation.getActiveRoute(), reportActionID), + ); + }} + style={[styles.moneyRequestMenuItem]} + titleStyle={styles.flex1} + disabled={didConfirm} + interactive={!isReadOnly} + numberOfLinesTitle={2} + rightLabel={isDescriptionRequired ? translate('common.required') : ''} + sentryLabel={CONST.SENTRY_LABEL.REQUEST_CONFIRMATION_LIST.DESCRIPTION_FIELD} + /> + + + ), shouldShow: true, diff --git a/src/components/ReportActionItem/ReportActionItemImage.tsx b/src/components/ReportActionItem/ReportActionItemImage.tsx index 31d61306dc56..3b756aa774ed 100644 --- a/src/components/ReportActionItem/ReportActionItemImage.tsx +++ b/src/components/ReportActionItem/ReportActionItemImage.tsx @@ -9,7 +9,7 @@ import type {IconSize} from '@components/EReceiptThumbnail'; import PressableWithoutFocus from '@components/Pressable/PressableWithoutFocus'; import type {ReceiptImageProps} from '@components/ReceiptImage'; import ReceiptImage from '@components/ReceiptImage'; -import {ShowContextMenuContext} from '@components/ShowContextMenuContext'; +import {useShowContextMenuState} from '@components/ShowContextMenuContext'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -114,6 +114,7 @@ function ReportActionItemImage({ const styles = useThemeStyles(); const {translate} = useLocalize(); const icons = useMemoizedLazyExpensifyIcons(['Receipt']); + const {report: contextReport, transactionThreadReport} = useShowContextMenuState(); const isMapDistanceRequest = !!transaction && isDistanceRequest(transaction) && !isManualDistanceRequest(transaction); const hasPendingWaypoints = transaction && isFetchingWaypointsFromServer(transaction); const hasErrors = !isEmptyObject(transaction?.errors) || !isEmptyObject(transaction?.errorFields?.route) || !isEmptyObject(transaction?.errorFields?.waypoints); @@ -178,34 +179,30 @@ function ReportActionItemImage({ if (enablePreviewModal) { return ( - - {({report, transactionThreadReport}) => ( - - Navigation.navigate( - ROUTES.TRANSACTION_RECEIPT.getRoute( - transactionThreadReport?.reportID ?? report?.reportID ?? reportProp?.reportID ?? getReportIDForExpense(transaction), - transaction?.transactionID, - readonly, - mergeTransactionID, - ), - ) - } - accessibilityLabel={translate('accessibilityHints.viewAttachment')} - accessibilityRole={CONST.ROLE.BUTTON} - sentryLabel={CONST.SENTRY_LABEL.RECEIPT.IMAGE} - > - - - )} - + + Navigation.navigate( + ROUTES.TRANSACTION_RECEIPT.getRoute( + transactionThreadReport?.reportID ?? contextReport?.reportID ?? reportProp?.reportID ?? getReportIDForExpense(transaction), + transaction?.transactionID, + readonly, + mergeTransactionID, + ), + ) + } + accessibilityLabel={translate('accessibilityHints.viewAttachment')} + accessibilityRole={CONST.ROLE.BUTTON} + sentryLabel={CONST.SENTRY_LABEL.RECEIPT.IMAGE} + > + + ); } diff --git a/src/components/ReportActionItem/TaskView.tsx b/src/components/ReportActionItem/TaskView.tsx index 45b41beb1b0a..ddb27ec7d4b2 100644 --- a/src/components/ReportActionItem/TaskView.tsx +++ b/src/components/ReportActionItem/TaskView.tsx @@ -11,7 +11,7 @@ import OfflineWithFeedback from '@components/OfflineWithFeedback'; import {usePersonalDetails} from '@components/OnyxListItemProvider'; import PressableWithSecondaryInteraction from '@components/PressableWithSecondaryInteraction'; import RenderHTML from '@components/RenderHTML'; -import {ShowContextMenuContext} from '@components/ShowContextMenuContext'; +import {ShowContextMenuActionsContext, ShowContextMenuStateContext} from '@components/ShowContextMenuContext'; import Text from '@components/Text'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useHasOutstandingChildTask from '@hooks/useHasOutstandingChildTask'; @@ -82,151 +82,159 @@ function TaskView({report, parentReport, action}: TaskViewProps) { const disableState = !isTaskModifiable; const isDisableInteractive = disableState || !isOpen; const accountID = currentUserPersonalDetails?.accountID ?? CONST.DEFAULT_NUMBER_ID; - const contextValue = useMemo( + const contextMenuStateValue = useMemo( () => ({ anchor: null, report, isReportArchived: false, action, transactionThreadReport: undefined, - checkIfContextMenuActive: () => {}, isDisabled: true, - onShowContextMenu: (callback: () => void) => callback(), shouldDisplayContextMenu: false, }), [report, action], ); + const contextMenuActionsValue = useMemo( + () => ({ + checkIfContextMenuActive: () => {}, + onShowContextMenu: (callback: () => void) => callback(), + }), + [], + ); + const attachmentContextValue = useMemo(() => ({type: CONST.ATTACHMENT_TYPE.ONBOARDING, accountID}), [accountID]); return ( - - - clearTaskErrors(report, conciergeReportID)} - errorRowStyles={styles.ph5} - > - - {(hovered) => ( - { - if (isDisableInteractive) { - return; - } - if (e && e.type === 'click') { - (e.currentTarget as HTMLElement).blur(); - } + + + + clearTaskErrors(report, conciergeReportID)} + errorRowStyles={styles.ph5} + > + + {(hovered) => ( + { + if (isDisableInteractive) { + return; + } + if (e && e.type === 'click') { + (e.currentTarget as HTMLElement).blur(); + } - Navigation.navigate(ROUTES.TASK_TITLE.getRoute(report?.reportID, Navigation.getReportRHPActiveRoute())); - })} - style={({pressed}) => [ - styles.ph5, - styles.pv2, - StyleUtils.getButtonBackgroundColorStyle(getButtonState(hovered, pressed, false, disableState, !isDisableInteractive), true), - isDisableInteractive && styles.cursorDefault, - ]} - accessibilityLabel={taskTitle || translate('task.task')} - disabled={isDisableInteractive} - sentryLabel={CONST.SENTRY_LABEL.TASK.VIEW_TITLE} - > - {({pressed}) => ( - - {translate('task.title')} - - { - // If we're already navigating to these task editing pages, early return not to mark as completed, otherwise we would have not found page. - if (isActiveTaskEditRoute(report?.reportID)) { - return; - } - if (isCompleted) { - reopenTask(report, parentReport, currentUserPersonalDetails.accountID); - } else { - completeTask(report, parentReport?.hasOutstandingChildTask ?? false, hasOutstandingChildTask, parentReportAction); - } - })} - isChecked={isCompleted} - style={styles.taskMenuItemCheckbox} - containerSize={24} - containerBorderRadius={8} - caretSize={16} - accessibilityLabel={taskTitle || translate('task.task')} - disabled={!isTaskActionable} - sentryLabel={CONST.SENTRY_LABEL.TASK.VIEW_CHECKBOX} - /> + Navigation.navigate(ROUTES.TASK_TITLE.getRoute(report?.reportID, Navigation.getReportRHPActiveRoute())); + })} + style={({pressed}) => [ + styles.ph5, + styles.pv2, + StyleUtils.getButtonBackgroundColorStyle(getButtonState(hovered, pressed, false, disableState, !isDisableInteractive), true), + isDisableInteractive && styles.cursorDefault, + ]} + accessibilityLabel={taskTitle || translate('task.task')} + disabled={isDisableInteractive} + sentryLabel={CONST.SENTRY_LABEL.TASK.VIEW_TITLE} + > + {({pressed}) => ( + + {translate('task.title')} - - - {!isDisableInteractive && ( - - + { + // If we're already navigating to these task editing pages, early return not to mark as completed, otherwise we would have not found page. + if (isActiveTaskEditRoute(report?.reportID)) { + return; + } + if (isCompleted) { + reopenTask(report, parentReport, currentUserPersonalDetails.accountID); + } else { + completeTask(report, parentReport?.hasOutstandingChildTask ?? false, hasOutstandingChildTask, parentReportAction); + } + })} + isChecked={isCompleted} + style={styles.taskMenuItemCheckbox} + containerSize={24} + containerBorderRadius={8} + caretSize={16} + accessibilityLabel={taskTitle || translate('task.task')} + disabled={!isTaskActionable} + sentryLabel={CONST.SENTRY_LABEL.TASK.VIEW_CHECKBOX} + /> + + - )} - - - )} - - )} - - - Navigation.navigate(ROUTES.REPORT_DESCRIPTION.getRoute(report?.reportID, Navigation.getReportRHPActiveRoute()))} - shouldShowRightIcon={!isDisableInteractive} - disabled={disableState} - wrapperStyle={[styles.pv2, styles.taskDescriptionMenuItem]} - shouldGreyOutWhenDisabled={false} - numberOfLinesTitle={0} - interactive={!isDisableInteractive} - shouldUseDefaultCursorWhenDisabled - sentryLabel={CONST.SENTRY_LABEL.TASK.VIEW_DESCRIPTION} - /> - - - {report?.managerID ? ( - Navigation.navigate(ROUTES.TASK_ASSIGNEE.getRoute(report?.reportID, Navigation.getReportRHPActiveRoute()))} - shouldShowRightIcon={!isDisableInteractive} - disabled={disableState} - wrapperStyle={[styles.pv2]} - isSmallAvatarSubscriptMenu - shouldGreyOutWhenDisabled={false} - interactive={!isDisableInteractive} - titleWithTooltips={assigneeTooltipDetails} - shouldUseDefaultCursorWhenDisabled - sentryLabel={CONST.SENTRY_LABEL.TASK.VIEW_ASSIGNEE} - /> - ) : ( + {!isDisableInteractive && ( + + + + )} + + + )} + + )} + + Navigation.navigate(ROUTES.TASK_ASSIGNEE.getRoute(report?.reportID, Navigation.getReportRHPActiveRoute()))} + shouldRenderAsHTML + description={translate('task.description')} + title={report?.description ?? ''} + onPress={() => Navigation.navigate(ROUTES.REPORT_DESCRIPTION.getRoute(report?.reportID, Navigation.getReportRHPActiveRoute()))} shouldShowRightIcon={!isDisableInteractive} disabled={disableState} - wrapperStyle={[styles.pv2]} + wrapperStyle={[styles.pv2, styles.taskDescriptionMenuItem]} shouldGreyOutWhenDisabled={false} + numberOfLinesTitle={0} interactive={!isDisableInteractive} shouldUseDefaultCursorWhenDisabled - sentryLabel={CONST.SENTRY_LABEL.TASK.VIEW_ASSIGNEE} + sentryLabel={CONST.SENTRY_LABEL.TASK.VIEW_DESCRIPTION} /> - )} + + + {report?.managerID ? ( + Navigation.navigate(ROUTES.TASK_ASSIGNEE.getRoute(report?.reportID, Navigation.getReportRHPActiveRoute()))} + shouldShowRightIcon={!isDisableInteractive} + disabled={disableState} + wrapperStyle={[styles.pv2]} + isSmallAvatarSubscriptMenu + shouldGreyOutWhenDisabled={false} + interactive={!isDisableInteractive} + titleWithTooltips={assigneeTooltipDetails} + shouldUseDefaultCursorWhenDisabled + sentryLabel={CONST.SENTRY_LABEL.TASK.VIEW_ASSIGNEE} + /> + ) : ( + Navigation.navigate(ROUTES.TASK_ASSIGNEE.getRoute(report?.reportID, Navigation.getReportRHPActiveRoute()))} + shouldShowRightIcon={!isDisableInteractive} + disabled={disableState} + wrapperStyle={[styles.pv2]} + shouldGreyOutWhenDisabled={false} + interactive={!isDisableInteractive} + shouldUseDefaultCursorWhenDisabled + sentryLabel={CONST.SENTRY_LABEL.TASK.VIEW_ASSIGNEE} + /> + )} + - - - + + + ); } diff --git a/src/components/ShowContextMenuContext/default.ts b/src/components/ShowContextMenuContext/default.ts new file mode 100644 index 000000000000..fb961ad6b127 --- /dev/null +++ b/src/components/ShowContextMenuContext/default.ts @@ -0,0 +1,18 @@ +import type {ShowContextMenuActionsContextType, ShowContextMenuStateContextType} from './types'; + +const defaultShowContextMenuStateContextValue: ShowContextMenuStateContextType = { + anchor: null, + report: undefined, + isReportArchived: false, + action: undefined, + transactionThreadReport: undefined, + isDisabled: true, + shouldDisplayContextMenu: true, +}; + +const defaultShowContextMenuActionsContextValue: ShowContextMenuActionsContextType = { + checkIfContextMenuActive: () => {}, + onShowContextMenu: (callback) => callback(), +}; + +export {defaultShowContextMenuStateContextValue, defaultShowContextMenuActionsContextValue}; diff --git a/src/components/ShowContextMenuContext.ts b/src/components/ShowContextMenuContext/index.tsx similarity index 61% rename from src/components/ShowContextMenuContext.ts rename to src/components/ShowContextMenuContext/index.tsx index f56e6e62e8ec..d6e7aa6183ee 100644 --- a/src/components/ShowContextMenuContext.ts +++ b/src/components/ShowContextMenuContext/index.tsx @@ -1,4 +1,4 @@ -import {createContext} from 'react'; +import {createContext, useContext} from 'react'; // eslint-disable-next-line no-restricted-imports import type {GestureResponderEvent} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; @@ -7,31 +7,20 @@ import {getOriginalReportID} from '@libs/ReportUtils'; import {showContextMenu} from '@pages/inbox/report/ContextMenu/ReportActionContextMenu'; import type {ContextMenuAnchor} from '@pages/inbox/report/ContextMenu/ReportActionContextMenu'; import CONST from '@src/CONST'; -import type {Report, ReportAction} from '@src/types/onyx'; +import type {ReportAction} from '@src/types/onyx'; +import {defaultShowContextMenuActionsContextValue, defaultShowContextMenuStateContextValue} from './default'; +import type {ShowContextMenuActionsContextType, ShowContextMenuStateContextType} from './types'; -type ShowContextMenuContextProps = { - anchor: ContextMenuAnchor; - report: OnyxEntry; - isReportArchived: boolean; - action: OnyxEntry; - transactionThreadReport?: OnyxEntry; - checkIfContextMenuActive: () => void; - onShowContextMenu: (callback: () => void) => void; - isDisabled: boolean; - shouldDisplayContextMenu?: boolean; -}; +const ShowContextMenuStateContext = createContext(defaultShowContextMenuStateContextValue); +const ShowContextMenuActionsContext = createContext(defaultShowContextMenuActionsContextValue); -const ShowContextMenuContext = createContext({ - anchor: null, - onShowContextMenu: (callback) => callback(), - report: undefined, - isReportArchived: false, - action: undefined, - transactionThreadReport: undefined, - checkIfContextMenuActive: () => {}, - isDisabled: true, - shouldDisplayContextMenu: true, -}); +function useShowContextMenuState(): ShowContextMenuStateContextType { + return useContext(ShowContextMenuStateContext); +} + +function useShowContextMenuActions(): ShowContextMenuActionsContextType { + return useContext(ShowContextMenuActionsContext); +} /** * Show the report action context menu. @@ -75,5 +64,5 @@ function showContextMenuForReport( }); } -export {ShowContextMenuContext, showContextMenuForReport}; -export type {ShowContextMenuContextProps}; +export {ShowContextMenuStateContext, ShowContextMenuActionsContext, useShowContextMenuState, useShowContextMenuActions, showContextMenuForReport}; +export type {ShowContextMenuActionsContextType, ShowContextMenuStateContextType} from './types'; diff --git a/src/components/ShowContextMenuContext/types.ts b/src/components/ShowContextMenuContext/types.ts new file mode 100644 index 000000000000..a75c3376bde7 --- /dev/null +++ b/src/components/ShowContextMenuContext/types.ts @@ -0,0 +1,20 @@ +import type {OnyxEntry} from 'react-native-onyx'; +import type {ContextMenuAnchor} from '@pages/inbox/report/ContextMenu/ReportActionContextMenu'; +import type {Report, ReportAction} from '@src/types/onyx'; + +type ShowContextMenuStateContextType = { + anchor: ContextMenuAnchor; + report: OnyxEntry; + isReportArchived: boolean; + action: OnyxEntry; + transactionThreadReport?: OnyxEntry; + isDisabled: boolean; + shouldDisplayContextMenu?: boolean; +}; + +type ShowContextMenuActionsContextType = { + checkIfContextMenuActive: () => void; + onShowContextMenu: (callback: () => void) => void; +}; + +export type {ShowContextMenuStateContextType, ShowContextMenuActionsContextType}; diff --git a/src/components/VideoPlayerPreview/VideoPlayerThumbnail.tsx b/src/components/VideoPlayerPreview/VideoPlayerThumbnail.tsx index a7a031dd372f..285ffe861f6d 100644 --- a/src/components/VideoPlayerPreview/VideoPlayerThumbnail.tsx +++ b/src/components/VideoPlayerPreview/VideoPlayerThumbnail.tsx @@ -5,7 +5,7 @@ import AttachmentDeletedIndicator from '@components/AttachmentDeletedIndicator'; import Icon from '@components/Icon'; import Image from '@components/Image'; import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback'; -import {ShowContextMenuContext, showContextMenuForReport} from '@components/ShowContextMenuContext'; +import {showContextMenuForReport, useShowContextMenuActions, useShowContextMenuState} from '@components/ShowContextMenuContext'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useThemeStyles from '@hooks/useThemeStyles'; import ControlSelection from '@libs/ControlSelection'; @@ -31,6 +31,8 @@ type VideoPlayerThumbnailProps = { function VideoPlayerThumbnail({thumbnailUrl, onPress, accessibilityLabel, isDeleted}: VideoPlayerThumbnailProps) { const styles = useThemeStyles(); const icons = useMemoizedLazyExpensifyIcons(['Play'] as const); + const {anchor, report, isReportArchived, action, isDisabled, shouldDisplayContextMenu} = useShowContextMenuState(); + const {onShowContextMenu, checkIfContextMenuActive} = useShowContextMenuActions(); return ( @@ -45,37 +47,33 @@ function VideoPlayerThumbnail({thumbnailUrl, onPress, accessibilityLabel, isDele )} {!isDeleted ? ( - - {({anchor, report, isReportArchived, action, checkIfContextMenuActive, isDisabled, onShowContextMenu, shouldDisplayContextMenu}) => ( - canUseTouchScreen() && ControlSelection.block()} - onPressOut={() => ControlSelection.unblock()} - onLongPress={(event) => { - if (isDisabled || !shouldDisplayContextMenu) { - return; - } - onShowContextMenu(() => { - showContextMenuForReport(event, anchor, report?.reportID, action, checkIfContextMenuActive, isArchivedNonExpenseReport(report, isReportArchived)); - }); - }} - shouldUseHapticsOnLongPress - sentryLabel={CONST.SENTRY_LABEL.VIDEO_PLAYER.THUMBNAIL} - > - - - - - )} - + canUseTouchScreen() && ControlSelection.block()} + onPressOut={() => ControlSelection.unblock()} + onLongPress={(event) => { + if (isDisabled || !shouldDisplayContextMenu) { + return; + } + onShowContextMenu(() => { + showContextMenuForReport(event, anchor, report?.reportID, action, checkIfContextMenuActive, isArchivedNonExpenseReport(report, isReportArchived)); + }); + }} + shouldUseHapticsOnLongPress + sentryLabel={CONST.SENTRY_LABEL.VIDEO_PLAYER.THUMBNAIL} + > + + + + ) : ( )} diff --git a/src/pages/TransactionDuplicate/Confirmation.tsx b/src/pages/TransactionDuplicate/Confirmation.tsx index 6afebaa856b8..04f7538d1043 100644 --- a/src/pages/TransactionDuplicate/Confirmation.tsx +++ b/src/pages/TransactionDuplicate/Confirmation.tsx @@ -10,7 +10,7 @@ import HeaderWithBackButton from '@components/HeaderWithBackButton'; import MoneyRequestView from '@components/ReportActionItem/MoneyRequestView'; import ScreenWrapper from '@components/ScreenWrapper'; import ScrollView from '@components/ScrollView'; -import {ShowContextMenuContext} from '@components/ShowContextMenuContext'; +import {ShowContextMenuActionsContext, ShowContextMenuStateContext} from '@components/ShowContextMenuContext'; import Text from '@components/Text'; import {useWideRHPState} from '@components/WideRHPContextProvider'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; @@ -92,13 +92,11 @@ function Confirmation() { Navigation.dismissToSuperWideRHP(); }, [transactionsMergeParams]); - const contextValue = useMemo( + const contextMenuStateValue = useMemo( () => ({ transactionThreadReport: report, action: reportAction, report, - checkIfContextMenuActive: () => {}, - onShowContextMenu: () => {}, isReportArchived: false, anchor: null, isDisabled: false, @@ -106,6 +104,14 @@ function Confirmation() { [report, reportAction], ); + const contextMenuActionsValue = useMemo( + () => ({ + checkIfContextMenuActive: () => {}, + onShowContextMenu: () => {}, + }), + [], + ); + const doesTransactionBelongToReport = reviewDuplicates?.transactionID === transactionID || (transactionID && reviewDuplicates?.duplicates.includes(transactionID)); const isDismissingRef = useRef(false); @@ -144,17 +150,19 @@ function Confirmation() { {translate('violations.confirmDuplicatesInfo')} {/* We need that provider here because MoneyRequestView component requires that */} - - } - isFromReviewDuplicates - /> - + + + } + isFromReviewDuplicates + /> + + - )} - {/** + + {isHidden ? translate('moderation.revealMessage') : translate('moderation.hideMessage')} + + + )} + {/** These are the actionable buttons that appear at the bottom of a Concierge message for example: Invite a user mentioned but not a member of the room https://github.com/Expensify/App/issues/32741 */} - {actionableItemButtons.length > 0 && ( - - )} - - ) : ( - - )} - - + {actionableItemButtons.length > 0 && ( + + )} + + ) : ( + + )} + + + ); } @@ -1935,7 +1945,8 @@ function PureReportActionItem({ return ( ); - }, [contextValue, parentReportAction, parentReport, draftMessage, shouldHideThreadDividerLine, parentReportActionForTransactionThread]); + }, [contextMenuStateValue, contextMenuActionsValue, parentReportAction, parentReport, draftMessage, shouldHideThreadDividerLine, parentReportActionForTransactionThread]); if (action.actionName === CONST.REPORT.ACTIONS.TYPE.CREATED && !isHarvestCreatedExpenseReport) { return createdActionContent; diff --git a/src/pages/inbox/report/ReportActionItemContentCreated.tsx b/src/pages/inbox/report/ReportActionItemContentCreated.tsx index f52aa1317c76..1ad8af1f8ab0 100644 --- a/src/pages/inbox/report/ReportActionItemContentCreated.tsx +++ b/src/pages/inbox/report/ReportActionItemContentCreated.tsx @@ -6,8 +6,8 @@ import RenderHTML from '@components/RenderHTML'; import MoneyReportView from '@components/ReportActionItem/MoneyReportView'; import MoneyRequestView from '@components/ReportActionItem/MoneyRequestView'; import TaskView from '@components/ReportActionItem/TaskView'; -import {ShowContextMenuContext} from '@components/ShowContextMenuContext'; -import type {ShowContextMenuContextProps} from '@components/ShowContextMenuContext'; +import type {ShowContextMenuActionsContextType, ShowContextMenuStateContextType} from '@components/ShowContextMenuContext'; +import {ShowContextMenuActionsContext, ShowContextMenuStateContext} from '@components/ShowContextMenuContext'; import SpacerView from '@components/SpacerView'; import UnreadActionIndicator from '@components/UnreadActionIndicator'; import useLocalize from '@hooks/useLocalize'; @@ -28,8 +28,11 @@ import ReportActionItemCreated from './ReportActionItemCreated'; import ReportActionItemSingle from './ReportActionItemSingle'; type ReportActionItemContentCreatedProps = { - /** The context value containing the report and action data, along with the show context menu props */ - contextValue: ShowContextMenuContextProps; + /** The state context value containing the report and action data */ + contextMenuStateValue: ShowContextMenuStateContextType; + + /** The actions context value containing the show context menu callbacks */ + contextMenuActionsValue: ShowContextMenuActionsContextType; /** The parent report */ parentReport: OnyxEntry; @@ -47,10 +50,18 @@ type ReportActionItemContentCreatedProps = { shouldHideThreadDividerLine: boolean; }; -function ReportActionItemContentCreated({contextValue, parentReport, parentReportAction, transactionID, draftMessage, shouldHideThreadDividerLine}: ReportActionItemContentCreatedProps) { +function ReportActionItemContentCreated({ + contextMenuStateValue, + contextMenuActionsValue, + parentReport, + parentReportAction, + transactionID, + draftMessage, + shouldHideThreadDividerLine, +}: ReportActionItemContentCreatedProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); - const {report, action, transactionThreadReport} = contextValue; + const {report, action, transactionThreadReport} = contextMenuStateValue; const policy = usePolicy(report?.policyID === CONST.POLICY.OWNER_EMAIL_FAKE ? undefined : report?.policyID); const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${getNonEmptyStringOnyxID(transactionID)}`); @@ -72,7 +83,7 @@ function ReportActionItemContentCreated({contextValue, parentReport, parentRepor [shouldHideThreadDividerLine, report?.reportID, styles.reportHorizontalRule], ); - const contextMenuValue = useMemo(() => ({...contextValue, isDisabled: true}), [contextValue]); + const disabledStateValue = useMemo(() => ({...contextMenuStateValue, isDisabled: true}), [contextMenuStateValue]); if (isTransactionThread(parentReportAction)) { const isReversedTransaction = isReversedTransactionReportActionsUtils(parentReportAction); @@ -105,17 +116,19 @@ function ReportActionItemContentCreated({contextValue, parentReport, parentRepor return ( - - - - {renderThreadDivider} - - + + + + + {renderThreadDivider} + + + ); } @@ -167,17 +180,19 @@ function ReportActionItemContentCreated({contextValue, parentReport, parentRepor shouldShowTotal={transaction ? transactionCurrency !== report?.currency : false} shouldHideThreadDividerLine={false} /> - - - - {renderThreadDivider} - - + + + + + {renderThreadDivider} + + + ) : ( - prevProps.contextValue === nextProps.contextValue && + prevProps.contextMenuStateValue === nextProps.contextMenuStateValue && + prevProps.contextMenuActionsValue === nextProps.contextMenuActionsValue && prevProps.parentReportAction === nextProps.parentReportAction && prevProps.transactionID === nextProps.transactionID && prevProps.draftMessage === nextProps.draftMessage && diff --git a/tests/unit/MentionUserRendererTest.tsx b/tests/unit/MentionUserRendererTest.tsx index 626345a63026..738bf00be64c 100644 --- a/tests/unit/MentionUserRendererTest.tsx +++ b/tests/unit/MentionUserRendererTest.tsx @@ -6,7 +6,7 @@ import Onyx from 'react-native-onyx'; import type {TText} from 'react-native-render-html'; import MentionUserRenderer from '@components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer'; import OnyxListItemProvider from '@components/OnyxListItemProvider'; -import {ShowContextMenuContext} from '@components/ShowContextMenuContext'; +import {ShowContextMenuActionsContext, ShowContextMenuStateContext} from '@components/ShowContextMenuContext'; import type {WithCurrentUserPersonalDetailsProps} from '@components/withCurrentUserPersonalDetails'; import Navigation from '@libs/Navigation/Navigation'; import CONST from '@src/CONST'; @@ -95,20 +95,25 @@ jest.mock('@libs/Log', () => ({ function withProvider(children: ReactNode) { return ( - void) => fn(), anchor: null, report: undefined, isReportArchived: false, action: undefined, - checkIfContextMenuActive: () => false, isDisabled: true, shouldDisplayContextMenu: false, }} > - {children} - + void) => fn(), + checkIfContextMenuActive: () => false, + }} + > + {children} + + ); } diff --git a/tests/unit/VideoRendererTest.tsx b/tests/unit/VideoRendererTest.tsx index 7fa62477bd05..7752d40852f0 100644 --- a/tests/unit/VideoRendererTest.tsx +++ b/tests/unit/VideoRendererTest.tsx @@ -3,7 +3,7 @@ import React from 'react'; import {AttachmentContext} from '@components/AttachmentContext'; import VideoRenderer from '@components/HTMLEngineProvider/HTMLRenderers/VideoRenderer'; import type PressableProps from '@components/Pressable/GenericPressable/types'; -import {ShowContextMenuContext} from '@components/ShowContextMenuContext'; +import {ShowContextMenuActionsContext, ShowContextMenuStateContext} from '@components/ShowContextMenuContext'; import Navigation from '@libs/Navigation/Navigation'; import CONST from '@src/CONST'; @@ -34,14 +34,17 @@ jest.mock('@components/VideoPlayerPreview', () => { }; }); -const mockShowContextMenuValue = { +const mockShowContextMenuStateValue = { anchor: null, report: undefined, isReportArchived: false, action: undefined, transactionThreadReport: undefined, - checkIfContextMenuActive: () => {}, isDisabled: true, +}; + +const mockShowContextMenuActionsValue = { + checkIfContextMenuActive: () => {}, onShowContextMenu: (callback: () => void) => callback(), }; const mockTNodeAttributes = { @@ -60,12 +63,14 @@ describe('VideoRenderer', () => { it('should open the report attachment with isAuthTokenRequired=true', () => { // Given a VideoRenderer component with a valid attributes render( - - - {/* @ts-expect-error - Ignoring type errors for testing purposes */} - - - , + + + + {/* @ts-expect-error - Ignoring type errors for testing purposes */} + + + + , ); // When the user presses the show modal button From 9a7c821f3ad8b0c2b10c093f3f0fa1f3b5ea4e17 Mon Sep 17 00:00:00 2001 From: staszekscp Date: Fri, 27 Feb 2026 16:07:39 +0100 Subject: [PATCH 3/4] Migrate AccountingContext --- .../ConnectToNetSuiteFlow/index.tsx | 4 +- .../accounting/AccountingContext/default.ts | 26 ++++ .../index.tsx} | 114 ++++++++---------- .../accounting/AccountingContext/types.ts | 22 ++++ .../workspace/accounting/ClaimOfferPage.tsx | 4 +- .../accounting/PolicyAccountingPage.tsx | 5 +- 6 files changed, 102 insertions(+), 73 deletions(-) create mode 100644 src/pages/workspace/accounting/AccountingContext/default.ts rename src/pages/workspace/accounting/{AccountingContext.tsx => AccountingContext/index.tsx} (57%) create mode 100644 src/pages/workspace/accounting/AccountingContext/types.ts diff --git a/src/components/ConnectToNetSuiteFlow/index.tsx b/src/components/ConnectToNetSuiteFlow/index.tsx index fd45055656b6..e0c48c442c9d 100644 --- a/src/components/ConnectToNetSuiteFlow/index.tsx +++ b/src/components/ConnectToNetSuiteFlow/index.tsx @@ -7,7 +7,7 @@ import useResponsiveLayout from '@hooks/useResponsiveLayout'; import {isAuthenticationError} from '@libs/actions/connections'; import {getAdminPoliciesConnectedToNetSuite} from '@libs/actions/Policy/Policy'; import Navigation from '@libs/Navigation/Navigation'; -import {useAccountingContext} from '@pages/workspace/accounting/AccountingContext'; +import {useAccountingState} from '@pages/workspace/accounting/AccountingContext'; import {getInitialSubPageForNetsuiteTokenInput} from '@pages/workspace/accounting/netsuite/utils'; import type {AnchorPosition} from '@styles/index'; import CONST from '@src/CONST'; @@ -26,7 +26,7 @@ function ConnectToNetSuiteFlow({policyID}: ConnectToNetSuiteFlowProps) { const [isReuseConnectionsPopoverOpen, setIsReuseConnectionsPopoverOpen] = useState(false); const [reuseConnectionPopoverPosition, setReuseConnectionPopoverPosition] = useState({horizontal: 0, vertical: 0}); - const {popoverAnchorRefs} = useAccountingContext(); + const {popoverAnchorRefs} = useAccountingState(); const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`); const icons = useMemoizedLazyExpensifyIcons(['Copy', 'LinkCopy'] as const); diff --git a/src/pages/workspace/accounting/AccountingContext/default.ts b/src/pages/workspace/accounting/AccountingContext/default.ts new file mode 100644 index 000000000000..b79523a0ec57 --- /dev/null +++ b/src/pages/workspace/accounting/AccountingContext/default.ts @@ -0,0 +1,26 @@ +import type {RefObject} from 'react'; +import type {View} from 'react-native'; +import CONST from '@src/CONST'; +import type {ConnectionName} from '@src/types/onyx/Policy'; +import type {AccountingActionsContextType, AccountingStateContextType} from './types'; + +const popoverAnchorRefsInitialValue = Object.values(CONST.POLICY.CONNECTIONS.NAME).reduce( + (acc, key) => { + acc[key] = {current: null}; + return acc; + }, + {} as Record>, +); + +const defaultAccountingStateContextValue: AccountingStateContextType = { + activeIntegration: undefined, + popoverAnchorRefs: { + current: popoverAnchorRefsInitialValue, + }, +}; + +const defaultAccountingActionsContextValue: AccountingActionsContextType = { + startIntegrationFlow: () => {}, +}; + +export {defaultAccountingStateContextValue, defaultAccountingActionsContextValue, popoverAnchorRefsInitialValue}; diff --git a/src/pages/workspace/accounting/AccountingContext.tsx b/src/pages/workspace/accounting/AccountingContext/index.tsx similarity index 57% rename from src/pages/workspace/accounting/AccountingContext.tsx rename to src/pages/workspace/accounting/AccountingContext/index.tsx index 1a9b44bb122e..c4e2c43bb29f 100644 --- a/src/pages/workspace/accounting/AccountingContext.tsx +++ b/src/pages/workspace/accounting/AccountingContext/index.tsx @@ -1,5 +1,5 @@ +import React, {createContext, useCallback, useContext, useMemo, useRef, useState} from 'react'; import type {RefObject} from 'react'; -import React, {useContext, useMemo, useRef, useState} from 'react'; import type {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import AccountingConnectionConfirmationModal from '@components/AccountingConnectionConfirmationModal'; @@ -8,61 +8,28 @@ import useLocalize from '@hooks/useLocalize'; import {removePolicyConnection} from '@libs/actions/connections'; import Navigation from '@libs/Navigation/Navigation'; import {isControlPolicy} from '@libs/PolicyUtils'; -import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; -import type {ConnectionName} from '@src/types/onyx/Policy'; import type Policy from '@src/types/onyx/Policy'; import type ChildrenProps from '@src/types/utils/ChildrenProps'; -import {getAccountingIntegrationData} from './utils'; +import {getAccountingIntegrationData} from '../utils'; +import {defaultAccountingActionsContextValue, defaultAccountingStateContextValue, popoverAnchorRefsInitialValue} from './default'; +import type {AccountingActionsContextType, AccountingStateContextType, ActiveIntegration, ActiveIntegrationState} from './types'; -type ActiveIntegration = { - name: ConnectionName; - shouldDisconnectIntegrationBeforeConnecting?: boolean; - integrationToDisconnect?: ConnectionName; -}; - -type ActiveIntegrationState = ActiveIntegration & {key: number}; - -type AccountingContextType = { - activeIntegration?: ActiveIntegration; - startIntegrationFlow: (activeIntegration: ActiveIntegration) => void; - - /* - * This stores refs to integration buttons, so the PopoverMenu can be positioned correctly - */ - popoverAnchorRefs: RefObject>>; -}; - -const popoverAnchorRefsInitialValue = Object.values(CONST.POLICY.CONNECTIONS.NAME).reduce( - (acc, key) => { - acc[key] = {current: null}; - return acc; - }, - {} as Record>, -); - -const defaultAccountingContext = { - activeIntegration: undefined, - startIntegrationFlow: () => {}, - popoverAnchorRefs: { - current: popoverAnchorRefsInitialValue, - }, -}; - -const AccountingContext = React.createContext(defaultAccountingContext); +const AccountingStateContext = createContext(defaultAccountingStateContextValue); +const AccountingActionsContext = createContext(defaultAccountingActionsContextValue); type AccountingContextProviderProps = ChildrenProps & { policy: OnyxEntry; }; function AccountingContextProvider({children, policy}: AccountingContextProviderProps) { - const popoverAnchorRefs = useRef>>(defaultAccountingContext.popoverAnchorRefs.current); + const popoverAnchorRefs = useRef>>(popoverAnchorRefsInitialValue); const [activeIntegration, setActiveIntegration] = useState(); const {translate} = useLocalize(); const policyID = policy?.id; const accountingIcons = useMemoizedLazyExpensifyIcons(['IntacctSquare', 'QBOSquare', 'XeroSquare', 'NetSuiteSquare', 'QBDSquare']); - const startIntegrationFlow = React.useCallback( + const startIntegrationFlow = useCallback( (newActiveIntegration: ActiveIntegration) => { if (!policyID) { return; @@ -107,13 +74,19 @@ function AccountingContextProvider({children, policy}: AccountingContextProvider }); }; - const accountingContext = useMemo( + const stateValue = useMemo( () => ({ activeIntegration, - startIntegrationFlow, popoverAnchorRefs, }), - [activeIntegration, startIntegrationFlow], + [activeIntegration], + ); + + const actionsValue = useMemo( + () => ({ + startIntegrationFlow, + }), + [startIntegrationFlow], ); const renderActiveIntegration = () => { @@ -128,31 +101,38 @@ function AccountingContextProvider({children, policy}: AccountingContextProvider const shouldShowConfirmationModal = !!activeIntegration?.shouldDisconnectIntegrationBeforeConnecting && !!activeIntegration?.integrationToDisconnect; return ( - - {children} - {!shouldShowConfirmationModal && renderActiveIntegration()} - {shouldShowConfirmationModal && ( - { - if (!policyID || !activeIntegration?.integrationToDisconnect) { - return; - } - removePolicyConnection(policy, activeIntegration?.integrationToDisconnect); - closeConfirmationModal(); - }} - integrationToConnect={activeIntegration?.name} - onCancel={() => { - setActiveIntegration(undefined); - }} - /> - )} - + + + {children} + {!shouldShowConfirmationModal && renderActiveIntegration()} + {shouldShowConfirmationModal && ( + { + if (!policyID || !activeIntegration?.integrationToDisconnect) { + return; + } + removePolicyConnection(policy, activeIntegration?.integrationToDisconnect); + closeConfirmationModal(); + }} + integrationToConnect={activeIntegration?.name} + onCancel={() => { + setActiveIntegration(undefined); + }} + /> + )} + + ); } -function useAccountingContext() { - return useContext(AccountingContext); +function useAccountingState(): AccountingStateContextType { + return useContext(AccountingStateContext); +} + +function useAccountingActions(): AccountingActionsContextType { + return useContext(AccountingActionsContext); } -export default AccountingContext; -export {AccountingContextProvider, useAccountingContext}; +export default AccountingContextProvider; +export {AccountingStateContext, AccountingActionsContext, AccountingContextProvider, useAccountingState, useAccountingActions}; +export type {AccountingActionsContextType, AccountingStateContextType, ActiveIntegration} from './types'; diff --git a/src/pages/workspace/accounting/AccountingContext/types.ts b/src/pages/workspace/accounting/AccountingContext/types.ts new file mode 100644 index 000000000000..9499e98cae1f --- /dev/null +++ b/src/pages/workspace/accounting/AccountingContext/types.ts @@ -0,0 +1,22 @@ +import type {RefObject} from 'react'; +import type {View} from 'react-native'; +import type {ConnectionName} from '@src/types/onyx/Policy'; + +type ActiveIntegration = { + name: ConnectionName; + shouldDisconnectIntegrationBeforeConnecting?: boolean; + integrationToDisconnect?: ConnectionName; +}; + +type ActiveIntegrationState = ActiveIntegration & {key: number}; + +type AccountingStateContextType = { + activeIntegration?: ActiveIntegration; + popoverAnchorRefs: RefObject>>; +}; + +type AccountingActionsContextType = { + startIntegrationFlow: (activeIntegration: ActiveIntegration) => void; +}; + +export type {ActiveIntegration, ActiveIntegrationState, AccountingStateContextType, AccountingActionsContextType}; diff --git a/src/pages/workspace/accounting/ClaimOfferPage.tsx b/src/pages/workspace/accounting/ClaimOfferPage.tsx index aebcb604675b..09c6939642c0 100644 --- a/src/pages/workspace/accounting/ClaimOfferPage.tsx +++ b/src/pages/workspace/accounting/ClaimOfferPage.tsx @@ -26,7 +26,7 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; import type {PolicyFeatureName} from '@src/types/onyx/Policy'; -import {AccountingContextProvider, useAccountingContext} from './AccountingContext'; +import {AccountingContextProvider, useAccountingActions} from './AccountingContext'; type IntegrationType = typeof CONST.POLICY.CONNECTIONS.NAME.XERO | typeof CONST.POLICY.RECEIPT_PARTNERS.NAME.UBER; @@ -48,7 +48,7 @@ function ClaimOfferPage({route, policy}: ClaimOfferPageProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); const {isOffline} = useNetwork(); - const {startIntegrationFlow} = useAccountingContext(); + const {startIntegrationFlow} = useAccountingActions(); const expensifyIcons = useMemoizedLazyExpensifyIcons(['TreasureChestGreenWithSparkle'] as const); const integrations = policy?.receiptPartners; const {isUberConnected} = useGetReceiptPartnersIntegrationData(policyID); diff --git a/src/pages/workspace/accounting/PolicyAccountingPage.tsx b/src/pages/workspace/accounting/PolicyAccountingPage.tsx index 108162cbc4d3..f5888144333b 100644 --- a/src/pages/workspace/accounting/PolicyAccountingPage.tsx +++ b/src/pages/workspace/accounting/PolicyAccountingPage.tsx @@ -56,7 +56,7 @@ import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {ConnectionName} from '@src/types/onyx/Policy'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; -import {AccountingContextProvider, useAccountingContext} from './AccountingContext'; +import {AccountingContextProvider, useAccountingActions, useAccountingState} from './AccountingContext'; import type {MenuItemData, PolicyAccountingPageProps} from './types'; import {getAccountingIntegrationData, getSynchronizationErrorMessage} from './utils'; @@ -79,7 +79,8 @@ function PolicyAccountingPage({policy}: PolicyAccountingPageProps) { const {shouldUseNarrowLayout} = useResponsiveLayout(); const [isDisconnectModalOpen, setIsDisconnectModalOpen] = useState(false); const [datetimeToRelative, setDateTimeToRelative] = useState(''); - const {startIntegrationFlow, popoverAnchorRefs} = useAccountingContext(); + const {popoverAnchorRefs} = useAccountingState(); + const {startIntegrationFlow} = useAccountingActions(); const [account] = useOnyx(ONYXKEYS.ACCOUNT); const {isLargeScreenWidth} = useResponsiveLayout(); const route = useRoute(); From 29634bfcb7d512f7c7e025fb58fd55e92675eb78 Mon Sep 17 00:00:00 2001 From: staszekscp Date: Wed, 4 Mar 2026 15:40:40 +0100 Subject: [PATCH 4/4] Fix lint --- src/CONST/index.ts | 1 + src/components/HTMLEngineProvider/HTMLRenderers/PreRenderer.tsx | 1 + tests/unit/VideoRendererTest.tsx | 1 + 3 files changed, 3 insertions(+) diff --git a/src/CONST/index.ts b/src/CONST/index.ts index 89577aa736c7..623a3f3494b5 100644 --- a/src/CONST/index.ts +++ b/src/CONST/index.ts @@ -8349,6 +8349,7 @@ const CONST = { }, HTML_RENDERER: { IMAGE: 'HTMLRenderer-Image', + PRE: 'HTMLRenderer-Pre', }, RECEIPT: { IMAGE: 'Receipt-Image', diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/PreRenderer.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/PreRenderer.tsx index 4330be01a190..122666021263 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/PreRenderer.tsx +++ b/src/components/HTMLEngineProvider/HTMLRenderers/PreRenderer.tsx @@ -54,6 +54,7 @@ function PreRenderer({TDefaultRenderer, onPressIn, onPressOut, onLongPress, ...d return ( {})} onPressIn={onPressIn} onPressOut={onPressOut} diff --git a/tests/unit/VideoRendererTest.tsx b/tests/unit/VideoRendererTest.tsx index 7752d40852f0..6ba2b2516d05 100644 --- a/tests/unit/VideoRendererTest.tsx +++ b/tests/unit/VideoRendererTest.tsx @@ -29,6 +29,7 @@ jest.mock('@components/VideoPlayerPreview', () => { onPress={handlePress} accessibilityRole="button" accessibilityLabel={fileName} + sentryLabel="VideoRendererTest-ShowModalButton" /> ); };