@@ -2,12 +2,13 @@ import {useIsFocused} from '@react-navigation/native';
22import { format } from 'date-fns' ;
33import { Str } from 'expensify-common' ;
44import React , { useCallback , useContext , useEffect , useMemo , useReducer , useRef , useState } from 'react' ;
5- import { ActivityIndicator , PanResponder , PixelRatio , StyleSheet , View } from 'react-native' ;
5+ import { ActivityIndicator , InteractionManager , PanResponder , PixelRatio , StyleSheet , View } from 'react-native' ;
66import type { OnyxEntry } from 'react-native-onyx' ;
77import { useOnyx } from 'react-native-onyx' ;
88import { RESULTS } from 'react-native-permissions' ;
99import Animated , { useAnimatedStyle , useSharedValue , withTiming } from 'react-native-reanimated' ;
1010import type Webcam from 'react-webcam' ;
11+ import MultiScan from '@assets/images/educational-illustration__multi-scan.svg' ;
1112import TestReceipt from '@assets/images/fake-receipt.png' ;
1213import Hand from '@assets/images/hand.svg' ;
1314import ReceiptUpload from '@assets/images/receipt-upload.svg' ;
@@ -21,6 +22,7 @@ import DownloadAppBanner from '@components/DownloadAppBanner';
2122import DragAndDropConsumer from '@components/DragAndDrop/Consumer' ;
2223import { DragAndDropContext } from '@components/DragAndDrop/Provider' ;
2324import DropZoneUI from '@components/DropZone/DropZoneUI' ;
25+ import FeatureTrainingModal from '@components/FeatureTrainingModal' ;
2426import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator' ;
2527import Icon from '@components/Icon' ;
2628import * as Expensicons from '@components/Icon/Expensicons' ;
@@ -38,6 +40,7 @@ import useTheme from '@hooks/useTheme';
3840import useThemeStyles from '@hooks/useThemeStyles' ;
3941import setTestReceipt from '@libs/actions/setTestReceipt' ;
4042import { clearUserLocation , setUserLocation } from '@libs/actions/UserLocation' ;
43+ import { dismissProductTraining } from '@libs/actions/Welcome' ;
4144import { isMobile , isMobileWebKit } from '@libs/Browser' ;
4245import { base64ToFile , isLocalFile as isLocalFileFileUtils , resizeImageIfNeeded , validateReceipt } from '@libs/fileDownload/FileUtils' ;
4346import convertHeicImage from '@libs/fileDownload/heicConverter' ;
@@ -119,6 +122,7 @@ function IOURequestStepScan({
119122 const cameraRef = useRef < Webcam > ( null ) ;
120123 const trackRef = useRef < MediaStreamTrack | null > ( null ) ;
121124 const [ isQueriedPermissionState , setIsQueriedPermissionState ] = useState ( false ) ;
125+ const [ shouldShowMultiScanEducationalPopup , setShouldShowMultiScanEducationalPopup ] = useState ( false ) ;
122126
123127 const getScreenshotTimeoutRef = useRef < NodeJS . Timeout | null > ( null ) ;
124128 const [ reportNameValuePairs ] = useOnyx ( `${ ONYXKEYS . COLLECTION . REPORT_NAME_VALUE_PAIRS } ${ report ?. reportID } ` , { canBeMissing : true } ) ;
@@ -127,6 +131,7 @@ function IOURequestStepScan({
127131 const [ skipConfirmation ] = useOnyx ( `${ ONYXKEYS . COLLECTION . SKIP_CONFIRMATION } ${ initialTransactionID } ` , { canBeMissing : true } ) ;
128132 const [ activePolicyID ] = useOnyx ( ONYXKEYS . NVP_ACTIVE_POLICY_ID , { canBeMissing : false } ) ;
129133 const [ activePolicy ] = useOnyx ( `${ ONYXKEYS . COLLECTION . POLICY } ${ activePolicyID } ` , { canBeMissing : true } ) ;
134+ const [ dismissedProductTraining ] = useOnyx ( ONYXKEYS . NVP_DISMISSED_PRODUCT_TRAINING , { canBeMissing : true } ) ;
130135 const [ isLoadingReceipt , setIsLoadingReceipt ] = useState ( false ) ;
131136 const isEditing = action === CONST . IOU . ACTION . EDIT ;
132137 // TODO: use correct canUseMultiScan value when all multi-scan functionality is implemented
@@ -754,6 +759,9 @@ function IOURequestStepScan({
754759 ] ) ;
755760
756761 const toggleMultiScan = ( ) => {
762+ if ( ! dismissedProductTraining ?. [ CONST . PRODUCT_TRAINING_TOOLTIP_NAMES . MULTI_SCAN_EDUCATIONAL_MODAL ] ) {
763+ setShouldShowMultiScanEducationalPopup ( true ) ;
764+ }
757765 if ( isMultiScanEnabled ) {
758766 removeTransactionReceipt ( CONST . IOU . OPTIMISTIC_TRANSACTION_ID ) ;
759767 removeDraftTransactions ( true ) ;
@@ -831,6 +839,13 @@ function IOURequestStepScan({
831839 return translate ( attachmentInvalidReason ) ;
832840 } ;
833841
842+ const dismissMultiScanEducationalPopup = ( ) => {
843+ InteractionManager . runAfterInteractions ( ( ) => {
844+ dismissProductTraining ( CONST . PRODUCT_TRAINING_TOOLTIP_NAMES . MULTI_SCAN_EDUCATIONAL_MODAL ) ;
845+ setShouldShowMultiScanEducationalPopup ( false ) ;
846+ } ) ;
847+ } ;
848+
834849 const mobileCameraView = ( ) => (
835850 < >
836851 < View style = { [ styles . cameraView ] } >
@@ -977,7 +992,22 @@ function IOURequestStepScan({
977992 </ PressableWithFeedback >
978993 ) }
979994 </ View >
980-
995+ { canUseMultiScan && isMobile ( ) && shouldShowMultiScanEducationalPopup && (
996+ < FeatureTrainingModal
997+ title = { translate ( 'iou.scanMultipleReceipts' ) }
998+ image = { MultiScan }
999+ shouldRenderSVG
1000+ imageHeight = "auto"
1001+ imageWidth = "auto"
1002+ modalInnerContainerStyle = { styles . pt0 }
1003+ illustrationOuterContainerStyle = { styles . multiScanEducationalPopupImage }
1004+ onConfirm = { dismissMultiScanEducationalPopup }
1005+ titleStyles = { styles . mb2 }
1006+ confirmText = { translate ( 'common.buttonConfirm' ) }
1007+ description = { translate ( 'iou.scanMultipleReceiptsDescription' ) }
1008+ shouldGoBack = { false }
1009+ />
1010+ ) }
9811011 < ReceiptPreviews
9821012 isMultiScanEnabled = { isMultiScanEnabled }
9831013 submit = { submitReceipts }
0 commit comments