diff --git a/src/libs/DistanceRequestUtils.ts b/src/libs/DistanceRequestUtils.ts index c41b33873a8a..94167b382d49 100644 --- a/src/libs/DistanceRequestUtils.ts +++ b/src/libs/DistanceRequestUtils.ts @@ -289,11 +289,15 @@ function convertToDistanceInMeters(distance: number, unit: Unit): number { /** * Returns custom unit rate ID for the distance transaction */ -function getCustomUnitRateID(reportID: string) { +function getCustomUnitRateID(reportID?: string) { + let customUnitRateID: string = CONST.CUSTOM_UNITS.FAKE_P2P_ID; + + if (!reportID) { + return customUnitRateID; + } const report = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; const parentReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID}`]; const policy = PolicyUtils.getPolicy(report?.policyID ?? parentReport?.policyID); - let customUnitRateID: string = CONST.CUSTOM_UNITS.FAKE_P2P_ID; if (isEmptyObject(policy)) { return customUnitRateID; diff --git a/src/pages/iou/request/step/IOURequestStepParticipants.tsx b/src/pages/iou/request/step/IOURequestStepParticipants.tsx index ca41764260c9..6a67a1040f1b 100644 --- a/src/pages/iou/request/step/IOURequestStepParticipants.tsx +++ b/src/pages/iou/request/step/IOURequestStepParticipants.tsx @@ -5,7 +5,9 @@ import FormHelpMessage from '@components/FormHelpMessage'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import {READ_COMMANDS} from '@libs/API/types'; +import * as Browser from '@libs/Browser'; import DistanceRequestUtils from '@libs/DistanceRequestUtils'; +import getPlatform from '@libs/getPlatform'; import HttpUtils from '@libs/HttpUtils'; import * as IOUUtils from '@libs/IOUUtils'; import Navigation from '@libs/Navigation/Navigation'; @@ -15,9 +17,11 @@ import MoneyRequestParticipantsSelector from '@pages/iou/request/MoneyRequestPar import * as IOU from '@userActions/IOU'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import type {Route} from '@src/ROUTES'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import type {Participant} from '@src/types/onyx/IOU'; +import KeyboardUtils from '@src/utils/keyboard'; import StepScreenWrapper from './StepScreenWrapper'; import type {WithFullTransactionOrNotFoundProps} from './withFullTransactionOrNotFound'; import withFullTransactionOrNotFound from './withFullTransactionOrNotFound'; @@ -37,7 +41,7 @@ function IOURequestStepParticipants({ const {translate} = useLocalize(); const styles = useThemeStyles(); const isFocused = useIsFocused(); - const [skipConfirmation] = useOnyx(`${ONYXKEYS.COLLECTION.SKIP_CONFIRMATION}${transactionID ?? -1}`); + const [skipConfirmation] = useOnyx(`${ONYXKEYS.COLLECTION.SKIP_CONFIRMATION}${transactionID ?? CONST.DEFAULT_NUMBER_ID}`); // We need to set selectedReportID if user has navigated back from confirmation page and navigates to confirmation page with already selected participant const selectedReportID = useRef(participants?.length === 1 ? participants.at(0)?.reportID ?? reportID : reportID); @@ -70,6 +74,8 @@ function IOURequestStepParticipants({ const receiptFilename = transaction?.filename; const receiptPath = transaction?.receipt?.source; const receiptType = transaction?.receipt?.type; + const isAndroidNative = getPlatform() === CONST.PLATFORM.ANDROID; + const isMobileSafari = Browser.isMobileSafari(); // When the component mounts, if there is a receipt, see if the image can be read from the disk. If not, redirect the user to the starting step of the flow. // This is because until the expense is saved, the receipt file is only stored in the browsers memory as a blob:// and if the browser is refreshed, then @@ -86,7 +92,7 @@ function IOURequestStepParticipants({ (val: Participant[]) => { HttpUtils.cancelPendingRequests(READ_COMMANDS.SEARCH_FOR_REPORTS); - const firstParticipantReportID = val.at(0)?.reportID ?? ''; + const firstParticipantReportID = val.at(0)?.reportID; const rateID = DistanceRequestUtils.getCustomUnitRateID(firstParticipantReportID); const isInvoice = iouType === CONST.IOU.TYPE.INVOICE && ReportUtils.isInvoiceRoomWithID(firstParticipantReportID); numberOfParticipants.current = val.length; @@ -102,11 +108,24 @@ function IOURequestStepParticipants({ } // When a participant is selected, the reportID needs to be saved because that's the reportID that will be used in the confirmation step. - selectedReportID.current = firstParticipantReportID || reportID; + selectedReportID.current = firstParticipantReportID ?? reportID; }, [iouType, reportID, transactionID], ); + const handleNavigation = useCallback( + (route: Route) => { + if (isAndroidNative || isMobileSafari) { + KeyboardUtils.dismiss().then(() => { + Navigation.navigate(route); + }); + } else { + Navigation.navigate(route); + } + }, + [isAndroidNative, isMobileSafari], + ); + const goToNextStep = useCallback(() => { const isCategorizing = action === CONST.IOU.ACTION.CATEGORIZE; const isShareAction = action === CONST.IOU.ACTION.SHARE; @@ -132,12 +151,13 @@ function IOURequestStepParticipants({ transactionID, selectedReportID.current || reportID, ); - if (isCategorizing) { - Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_CATEGORY.getRoute(action, iouType, transactionID, selectedReportID.current || reportID, iouConfirmationPageRoute)); - } else { - Navigation.navigate(iouConfirmationPageRoute); - } - }, [iouType, transactionID, transaction, reportID, action, participants]); + + const route = isCategorizing + ? ROUTES.MONEY_REQUEST_STEP_CATEGORY.getRoute(action, iouType, transactionID, selectedReportID.current || reportID, iouConfirmationPageRoute) + : iouConfirmationPageRoute; + + handleNavigation(route); + }, [action, participants, iouType, transaction, transactionID, reportID, handleNavigation]); const navigateBack = useCallback(() => { IOUUtils.navigateToStartMoneyRequestStep(iouRequestType, iouType, transactionID, reportID, action); @@ -154,7 +174,8 @@ function IOURequestStepParticipants({ IOU.setCustomUnitRateID(transactionID, rateID); IOU.setMoneyRequestParticipantsFromReport(transactionID, selfDMReport); const iouConfirmationPageRoute = ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(action, CONST.IOU.TYPE.TRACK, transactionID, selfDMReportID); - Navigation.navigate(iouConfirmationPageRoute); + + handleNavigation(iouConfirmationPageRoute); }; useEffect(() => { diff --git a/src/utils/keyboard.ts b/src/utils/keyboard/index.ts similarity index 100% rename from src/utils/keyboard.ts rename to src/utils/keyboard/index.ts diff --git a/src/utils/keyboard/index.website.ts b/src/utils/keyboard/index.website.ts new file mode 100644 index 000000000000..f2ea9c673fdf --- /dev/null +++ b/src/utils/keyboard/index.website.ts @@ -0,0 +1,48 @@ +import {Keyboard} from 'react-native'; + +let isVisible = false; +const initialViewportHeight = window?.visualViewport?.height; + +const handleResize = () => { + const currentHeight = window?.visualViewport?.height; + + if (!currentHeight || !initialViewportHeight) { + return; + } + + if (currentHeight < initialViewportHeight) { + isVisible = true; + return; + } + + if (currentHeight === initialViewportHeight) { + isVisible = false; + } +}; + +window.visualViewport?.addEventListener('resize', handleResize); + +const dismiss = (): Promise => { + return new Promise((resolve) => { + if (!isVisible) { + resolve(); + return; + } + + const handleDismissResize = () => { + if (window.visualViewport?.height !== initialViewportHeight) { + return; + } + + window.visualViewport?.removeEventListener('resize', handleDismissResize); + return resolve(); + }; + + window.visualViewport?.addEventListener('resize', handleDismissResize); + Keyboard.dismiss(); + }); +}; + +const utils = {dismiss}; + +export default utils;