From 839ffbe962316a242345e4b6f4c75357499863c1 Mon Sep 17 00:00:00 2001 From: Adam Horodyski Date: Mon, 16 Mar 2026 11:24:53 +0100 Subject: [PATCH 1/2] Add useIsInSidePanel context to replace isInSidePanel prop drilling Introduce an IsInSidePanelContext and useIsInSidePanel hook that allows descendants to query whether they're rendered inside a side panel without prop drilling. SidePanelReport wraps its children with the provider. The existing isInSidePanel prop is kept for backward compatibility and will be removed in a follow-up PR that switches consumers to the hook. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../SidePanel/SidePanelReport/index.tsx | 17 ++++++++++------- src/hooks/useIsInSidePanel.ts | 10 ++++++++++ 2 files changed, 20 insertions(+), 7 deletions(-) create mode 100644 src/hooks/useIsInSidePanel.ts diff --git a/src/components/SidePanel/SidePanelReport/index.tsx b/src/components/SidePanel/SidePanelReport/index.tsx index a8c61b697a012..1bd884dc9945c 100644 --- a/src/components/SidePanel/SidePanelReport/index.tsx +++ b/src/components/SidePanel/SidePanelReport/index.tsx @@ -1,5 +1,6 @@ import {NavigationRouteContext} from '@react-navigation/native'; import React from 'react'; +import {IsInSidePanelContext} from '@hooks/useIsInSidePanel'; import type {ExtraContentProps, PlatformStackNavigationProp} from '@libs/Navigation/PlatformStackNavigation/types'; import type {ReportsSplitNavigatorParamList} from '@libs/Navigation/types'; import ReportScreen from '@pages/inbox/ReportScreen'; @@ -14,13 +15,15 @@ function SidePanelReport({navigation, reportID}: SidePanelReportProps) { const route = {name: SCREENS.REPORT, params: {reportID}, key: `Report-SidePanel-${reportID}`} as const; return ( - - } - isInSidePanel - /> - + + + } + isInSidePanel + /> + + ); } diff --git a/src/hooks/useIsInSidePanel.ts b/src/hooks/useIsInSidePanel.ts new file mode 100644 index 0000000000000..88df3c10beb14 --- /dev/null +++ b/src/hooks/useIsInSidePanel.ts @@ -0,0 +1,10 @@ +import {createContext, useContext} from 'react'; + +const IsInSidePanelContext = createContext(false); + +function useIsInSidePanel(): boolean { + return useContext(IsInSidePanelContext); +} + +export default useIsInSidePanel; +export {IsInSidePanelContext}; From dc6463c1cb99fb1b4a787afe4f344847b1c64daf Mon Sep 17 00:00:00 2001 From: Adam Horodyski Date: Mon, 16 Mar 2026 12:03:32 +0100 Subject: [PATCH 2/2] Replace isInSidePanel prop drilling with useIsInSidePanel context hook MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce an IsInSidePanelContext and useIsInSidePanel hook so that any descendant can query whether it is rendered inside the side panel without prop drilling through ReportScreen → HeaderView / ReportFooter → ReportActionCompose. SidePanelReport wraps its children with the context provider. All four consumers (ReportScreen, HeaderView, ReportFooter, ReportActionCompose) now call useIsInSidePanel() directly instead of receiving the prop. The Report/index.ts action-layer isInSidePanel parameter is a separate concern and is not changed by this commit. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/components/SidePanel/SidePanelReport/index.tsx | 1 - src/pages/inbox/HeaderView.tsx | 7 +++---- src/pages/inbox/ReportScreen.tsx | 12 ++++-------- .../ReportActionCompose/ReportActionCompose.tsx | 6 ++---- src/pages/inbox/report/ReportFooter.tsx | 7 ++----- 5 files changed, 11 insertions(+), 22 deletions(-) diff --git a/src/components/SidePanel/SidePanelReport/index.tsx b/src/components/SidePanel/SidePanelReport/index.tsx index 1bd884dc9945c..3ea7bb8619702 100644 --- a/src/components/SidePanel/SidePanelReport/index.tsx +++ b/src/components/SidePanel/SidePanelReport/index.tsx @@ -20,7 +20,6 @@ function SidePanelReport({navigation, reportID}: SidePanelReportProps) { } - isInSidePanel /> diff --git a/src/pages/inbox/HeaderView.tsx b/src/pages/inbox/HeaderView.tsx index 17e7c324f6743..dea0f76317c33 100644 --- a/src/pages/inbox/HeaderView.tsx +++ b/src/pages/inbox/HeaderView.tsx @@ -23,6 +23,7 @@ import Text from '@components/Text'; import Tooltip from '@components/Tooltip'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useHasTeam2025Pricing from '@hooks/useHasTeam2025Pricing'; +import useIsInSidePanel from '@hooks/useIsInSidePanel'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; @@ -100,15 +101,13 @@ type HeaderViewProps = { /** Whether we should display the header as in narrow layout */ shouldUseNarrowLayout?: boolean; - - /** Whether the header view is being displayed in the side panel */ - isInSidePanel?: boolean; }; -function HeaderView({report, parentReportAction, onNavigationMenuButtonClicked, shouldUseNarrowLayout = false, isInSidePanel}: HeaderViewProps) { +function HeaderView({report, parentReportAction, onNavigationMenuButtonClicked, shouldUseNarrowLayout = false}: HeaderViewProps) { const icons = useMemoizedLazyExpensifyIcons(['BackArrow', 'Close', 'DotIndicator'] as const); // eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth const {isSmallScreenWidth} = useResponsiveLayout(); + const isInSidePanel = useIsInSidePanel(); const route = useRoute(); const [parentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${getNonEmptyStringOnyxID(report?.parentReportID) ?? getNonEmptyStringOnyxID(report?.reportID)}`); const [grandParentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${getNonEmptyStringOnyxID(parentReport?.parentReportID)}`); diff --git a/src/pages/inbox/ReportScreen.tsx b/src/pages/inbox/ReportScreen.tsx index 41b14a9880ec9..cce3a62827b17 100644 --- a/src/pages/inbox/ReportScreen.tsx +++ b/src/pages/inbox/ReportScreen.tsx @@ -27,6 +27,7 @@ import {useCurrentReportIDState} from '@hooks/useCurrentReportID'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useDocumentTitle from '@hooks/useDocumentTitle'; import useIsAnonymousUser from '@hooks/useIsAnonymousUser'; +import useIsInSidePanel from '@hooks/useIsInSidePanel'; import useIsReportReadyToDisplay from '@hooks/useIsReportReadyToDisplay'; import useNetwork from '@hooks/useNetwork'; import useNewTransactions from '@hooks/useNewTransactions'; @@ -121,10 +122,7 @@ type ReportScreenNavigationProps = | PlatformStackScreenProps | PlatformStackScreenProps; -type ReportScreenProps = ReportScreenNavigationProps & { - /** Whether the report screen is being displayed in the side panel */ - isInSidePanel?: boolean; -}; +type ReportScreenProps = ReportScreenNavigationProps; const defaultReportMetadata = { hasOnceLoadedReportActions: false, @@ -157,7 +155,7 @@ function isEmpty(report: OnyxEntry): boolean { return !Object.values(report).some((value) => value !== undefined && value !== ''); } -function ReportScreen({route, navigation, isInSidePanel = false}: ReportScreenProps) { +function ReportScreen({route, navigation}: ReportScreenProps) { const styles = useThemeStyles(); const reportIDFromRoute = getNonEmptyStringOnyxID(route.params?.reportID); const reportActionIDFromRoute = route?.params?.reportActionID; @@ -169,6 +167,7 @@ function ReportScreen({route, navigation, isInSidePanel = false}: ReportScreenPr const {isBetaEnabled} = usePermissions(); const {isOffline} = useNetwork(); const {shouldUseNarrowLayout, isInNarrowPaneModal} = useResponsiveLayout(); + const isInSidePanel = useIsInSidePanel(); const {currentReportID: currentReportIDValue} = useCurrentReportIDState(); @@ -437,7 +436,6 @@ function ReportScreen({route, navigation, isInSidePanel = false}: ReportScreenPr report={report} parentReportAction={parentReportAction} shouldUseNarrowLayout={shouldUseNarrowLayout} - isInSidePanel={isInSidePanel} /> ); }, [ @@ -452,7 +450,6 @@ function ReportScreen({route, navigation, isInSidePanel = false}: ReportScreenPr reportActions, reportIDFromRoute, shouldUseNarrowLayout, - isInSidePanel, ]); useEffect(() => { @@ -1122,7 +1119,6 @@ function ReportScreen({route, navigation, isInSidePanel = false}: ReportScreenPr reportTransactions={reportTransactions} // If the report is from the 'Send Money' flow, we add the comment to the `iou` report because for these we don't combine reportActions even if there is a single transaction (they always have a single transaction) transactionThreadReportID={isSentMoneyReport ? undefined : transactionThreadReportID} - isInSidePanel={isInSidePanel} shouldHideStatusIndicators={isConciergeSidePanel && !hasUserSentMessage} kickoffWaitingIndicator={kickoffWaitingIndicator} /> diff --git a/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx b/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx index 29118046e8ddb..6ecf5dfef0a11 100644 --- a/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx +++ b/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx @@ -21,6 +21,7 @@ import useAncestors from '@hooks/useAncestors'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useHandleExceedMaxCommentLength from '@hooks/useHandleExceedMaxCommentLength'; import useHandleExceedMaxTaskTitleLength from '@hooks/useHandleExceedMaxTaskTitleLength'; +import useIsInSidePanel from '@hooks/useIsInSidePanel'; import useIsScrollLikelyLayoutTriggered from '@hooks/useIsScrollLikelyLayoutTriggered'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; @@ -112,9 +113,6 @@ type ReportActionComposeProps = Pick