+ {!shouldRenderOnlyCurrentPage ? (
+
+ ) : (
+ // Only the visible page is loaded. PDFPreviewer measures every page before showing any, which takes
+ // tens of seconds on long PDFs and isn't needed when one page is shown at a time.
+ oversampleWidth > 0 && (
+
+ {renderedPage !== undefined && (
+
+ )}
+ {page !== renderedPage && (
+ setRenderedPageState({fileURL, page})}
+ onError={handleLoadError}
+ {...canvasLimits}
+ />
+ )}
+
+ )
+ )}
);
diff --git a/src/components/ReportActionItem/ReceiptPDFOverlay/types.ts b/src/components/ReportActionItem/ReceiptPDFOverlay/types.ts
index 4e88be683bc9..a1b8a7a24bc3 100644
--- a/src/components/ReportActionItem/ReceiptPDFOverlay/types.ts
+++ b/src/components/ReportActionItem/ReceiptPDFOverlay/types.ts
@@ -7,6 +7,12 @@ type ReceiptPDFOverlayProps = {
/** Called when the PDF fails to load so the caller can fall back to the thumbnail */
onLoadFailure?: () => void;
+
+ /** Called once the PDF has loaded and its pages can be shown. Only fires when `page` is set. */
+ onLoadSuccess?: () => void;
+
+ /** 1-indexed page to show at the top of the overlay */
+ page?: number;
};
export default ReceiptPDFOverlayProps;
diff --git a/src/components/ReportActionItem/ReceiptPageNavigator.tsx b/src/components/ReportActionItem/ReceiptPageNavigator.tsx
new file mode 100644
index 000000000000..00df1e8b9eb5
--- /dev/null
+++ b/src/components/ReportActionItem/ReceiptPageNavigator.tsx
@@ -0,0 +1,131 @@
+import Icon from '@components/Icon';
+import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback';
+import Text from '@components/Text';
+import Tooltip from '@components/Tooltip';
+
+import useHover from '@hooks/useHover';
+import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset';
+import useLocalize from '@hooks/useLocalize';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
+
+import variables from '@styles/variables';
+
+import CONST from '@src/CONST';
+import type IconAsset from '@src/types/utils/IconAsset';
+
+import React from 'react';
+import {View} from 'react-native';
+
+type PageButtonProps = {
+ /** Arrow icon to show */
+ icon: IconAsset;
+
+ /** Tooltip and accessibility label */
+ label: string;
+
+ /** Whether the button can be pressed */
+ isDisabled: boolean;
+
+ /** Called when the button is pressed */
+ onPress: () => void;
+
+ /** Label used to identify the button in Sentry */
+ sentryLabel: string;
+};
+
+function PageButton({icon, label, isDisabled, onPress, sentryLabel}: PageButtonProps) {
+ const styles = useThemeStyles();
+ const theme = useTheme();
+ const {hovered, bind} = useHover();
+
+ // Static: icon color. Hover: text color, to read as more prominent/active. Disabled: icon color, dimmed.
+ const iconFill = !isDisabled && hovered ? theme.text : theme.icon;
+
+ return (
+