diff --git a/src/App.tsx b/src/App.tsx
index 3e6fc9ac2a27..789cac513431 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -12,6 +12,7 @@ import CustomStatusBarAndBackground from './components/CustomStatusBarAndBackgro
import CustomStatusBarAndBackgroundContextProvider from './components/CustomStatusBarAndBackground/CustomStatusBarAndBackgroundContextProvider';
import ErrorBoundary from './components/ErrorBoundary';
import FullScreenBlockingViewContextProvider from './components/FullScreenBlockingViewContextProvider';
+import FullScreenLoaderContextProvider from './components/FullScreenLoaderContext';
import HTMLEngineProvider from './components/HTMLEngineProvider';
import InitialURLContextProvider from './components/InitialURLContextProvider';
import {InputBlurContextProvider} from './components/InputBlurContext';
@@ -111,6 +112,7 @@ function App({url, hybridAppSettings, timestamp}: AppProps) {
ProductTrainingContextProvider,
InputBlurContextProvider,
FullScreenBlockingViewContextProvider,
+ FullScreenLoaderContextProvider,
]}
>
diff --git a/src/components/AttachmentPicker/index.native.tsx b/src/components/AttachmentPicker/index.native.tsx
index 6aa65e1f4977..bd0102d1fde7 100644
--- a/src/components/AttachmentPicker/index.native.tsx
+++ b/src/components/AttachmentPicker/index.native.tsx
@@ -116,6 +116,7 @@ function AttachmentPicker({
shouldValidateImage = true,
shouldHideGalleryOption = false,
fileLimit = 1,
+ onOpenPicker,
}: AttachmentPickerProps) {
const styles = useThemeStyles();
const [isVisible, setIsVisible] = useState(false);
@@ -398,6 +399,7 @@ function AttachmentPicker({
*/
const selectItem = useCallback(
(item: Item) => {
+ onOpenPicker?.();
/* setTimeout delays execution to the frame after the modal closes
* without this on iOS closing the modal closes the gallery/camera as well */
onModalHide.current = () => {
@@ -410,7 +412,7 @@ function AttachmentPicker({
};
close();
},
- [pickAttachment],
+ [pickAttachment, onOpenPicker],
);
useKeyboardShortcut(
diff --git a/src/components/AttachmentPicker/types.ts b/src/components/AttachmentPicker/types.ts
index 1e2e65761527..1958a94ac883 100644
--- a/src/components/AttachmentPicker/types.ts
+++ b/src/components/AttachmentPicker/types.ts
@@ -55,6 +55,9 @@ type AttachmentPickerProps = {
/** Whether to allow multiple files to be selected. */
fileLimit?: number;
+
+ /** A callback that will be called when the picker is opened. */
+ onOpenPicker?: () => void;
};
export default AttachmentPickerProps;
diff --git a/src/components/FullScreenLoaderContext.tsx b/src/components/FullScreenLoaderContext.tsx
new file mode 100644
index 000000000000..f06773992407
--- /dev/null
+++ b/src/components/FullScreenLoaderContext.tsx
@@ -0,0 +1,60 @@
+import React, {createContext, useContext, useMemo, useState} from 'react';
+import type {ReactNode} from 'react';
+import FullScreenLoadingIndicator from './FullscreenLoadingIndicator';
+
+type FullScreenLoaderContextType = {
+ /**
+ * Whether the full screen loader is visible.
+ */
+ isLoaderVisible: boolean;
+ /**
+ * Set the full screen loader visibility.
+ */
+ setIsLoaderVisible: React.Dispatch>;
+};
+
+const FullScreenLoaderContext = createContext({
+ isLoaderVisible: false,
+ setIsLoaderVisible: () => {},
+});
+
+type FullScreenLoaderContextProviderProps = {
+ /**
+ * The children of the full screen loader context provider.
+ */
+ children: ReactNode;
+};
+
+function FullScreenLoaderContextProvider({children}: FullScreenLoaderContextProviderProps) {
+ const [isLoaderVisible, setIsLoaderVisible] = useState(false);
+
+ const loaderContext = useMemo(
+ () => ({
+ isLoaderVisible,
+ setIsLoaderVisible,
+ }),
+ [isLoaderVisible],
+ );
+
+ return (
+
+ {children}
+ {isLoaderVisible && }
+
+ );
+}
+
+function useFullScreenLoader() {
+ const context = useContext(FullScreenLoaderContext);
+
+ if (!context) {
+ throw new Error('useFullScreenLoader must be used within a FullScreenLoaderContextProvider');
+ }
+
+ return context;
+}
+
+FullScreenLoaderContextProvider.displayName = 'FullScreenLoaderContextProvider';
+
+export default FullScreenLoaderContextProvider;
+export {FullScreenLoaderContext, useFullScreenLoader};
diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx
index 156c3c155466..60d34f6d0867 100644
--- a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx
+++ b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx
@@ -16,7 +16,7 @@ import Shutter from '@assets/images/shutter.svg';
import type {FileObject} from '@components/AttachmentModal';
import AttachmentPicker from '@components/AttachmentPicker';
import Button from '@components/Button';
-import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator';
+import {useFullScreenLoader} from '@components/FullScreenLoaderContext';
import Icon from '@components/Icon';
import * as Expensicons from '@components/Icon/Expensicons';
import ImageSVG from '@components/ImageSVG';
@@ -81,6 +81,7 @@ function IOURequestStepScan({
}: IOURequestStepScanProps) {
const theme = useTheme();
const styles = useThemeStyles();
+ const {isLoaderVisible, setIsLoaderVisible} = useFullScreenLoader();
const device = useCameraDevice('back', {
physicalDevices: ['wide-angle-camera', 'ultra-wide-angle-camera'],
});
@@ -105,7 +106,6 @@ function IOURequestStepScan({
const isPlatformMuted = mutedPlatforms[platform];
const [cameraPermissionStatus, setCameraPermissionStatus] = useState(null);
const [didCapturePhoto, setDidCapturePhoto] = useState(false);
- const [isLoadingReceipt, setIsLoadingReceipt] = useState(false);
const isTabActive = useIsFocused();
const [pdfFile, setPdfFile] = useState(null);
@@ -203,8 +203,12 @@ function IOURequestStepScan({
return () => {
subscription.remove();
+
+ if (isLoaderVisible) {
+ setIsLoaderVisible(false);
+ }
};
- }, []),
+ }, [isLoaderVisible, setIsLoaderVisible]),
);
const validateReceipt = (file: FileObject) => {
@@ -551,13 +555,7 @@ function IOURequestStepScan({
return;
}
- // With the image size > 24MB, we use manipulateAsync to resize the image.
- // It takes a long time so we should display a loading indicator while the resize image progresses.
- if (Str.isImage(originalFile.name ?? '') && (originalFile?.size ?? 0) > CONST.API_ATTACHMENT_VALIDATIONS.MAX_SIZE) {
- setIsLoadingReceipt(true);
- }
resizeImageIfNeeded(originalFile).then((file) => {
- setIsLoadingReceipt(false);
// Store the receipt on the transaction object in Onyx
// On Android devices, fetching blob for a file with name containing spaces fails to retrieve the type of file.
// So, let us also save the file type in receipt for later use during blob fetch
@@ -702,7 +700,6 @@ function IOURequestStepScan({
setElementTop(e.nativeEvent.layout.height - (variables.tabSelectorButtonHeight + variables.tabSelectorButtonPadding) * 2);
}}
>
- {isLoadingReceipt && }
{!!pdfFile && (
-
+ setIsLoaderVisible(true)}>
{({openPicker}) => (
{
openPicker({
onPicked: (data) => setReceiptAndNavigate(data.at(0) ?? {}),
+ onCanceled: () => setIsLoaderVisible(false),
});
}}
>