diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 63cf8532a4113..38b7bd4beaec1 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -10,8 +10,8 @@ import type CONST from './CONST'; import type {IOUAction, IOUType} from './CONST'; import type {IOURequestType} from './libs/actions/IOU'; import Log from './libs/Log'; +import type {RootNavigatorParamList} from './libs/Navigation/types'; import type {ReimbursementAccountStepToOpen} from './libs/ReimbursementAccountUtils'; -import type {AttachmentModalScreenParams} from './pages/media/AttachmentModalScreen/types'; import SCREENS from './SCREENS'; import type {Screen} from './SCREENS'; import type {ExitReason} from './types/form/ExitSurveyReasonForm'; @@ -422,6 +422,10 @@ const ROUTES = { return `r/${reportID}/avatar` as const; }, }, + ATTACHMENTS: { + route: 'attachment', + getRoute: (params?: ReportAttachmentsRouteParams) => getAttachmentModalScreenRoute('attachment', params), + }, EDIT_CURRENCY_REQUEST: { route: 'r/:threadReportID/edit/currency', getRoute: (threadReportID: string, currency: string, backTo: string) => `r/${threadReportID}/edit/currency?currency=${currency}&backTo=${backTo}` as const, @@ -447,10 +451,6 @@ const ROUTES = { return getUrlWithBackToParam(`r/${reportID}/details/shareCode` as const, backTo); }, }, - ATTACHMENTS: { - route: 'attachment', - getRoute: (params?: AttachmentRouteParams) => getAttachmentModalScreenRoute('attachment', params), - }, REPORT_PARTICIPANTS: { route: 'r/:reportID/participants', getRoute: (reportID: string, backTo?: string) => getUrlWithBackToParam(`r/${reportID}/participants` as const, backTo), @@ -2761,12 +2761,12 @@ const SHARED_ROUTE_PARAMS: Partial> = { export {getUrlWithBackToParam, PUBLIC_SCREENS_ROUTES, SHARED_ROUTE_PARAMS}; export default ROUTES; -type AttachmentsRoute = typeof ROUTES.ATTACHMENTS.route; -type ReportAddAttachmentRoute = `r/${string}/attachment/add`; -type AttachmentRoutes = AttachmentsRoute | ReportAddAttachmentRoute; -type AttachmentRouteParams = AttachmentModalScreenParams; +type ReportAttachmentsRoute = typeof ROUTES.ATTACHMENTS.route; +type AttachmentRoutes = ReportAttachmentsRoute; + +type ReportAttachmentsRouteParams = RootNavigatorParamList[typeof SCREENS.ATTACHMENTS]; -function getAttachmentModalScreenRoute(url: AttachmentRoutes, params?: AttachmentRouteParams) { +function getAttachmentModalScreenRoute(url: AttachmentRoutes, params?: ReportAttachmentsRouteParams) { if (!params?.source) { return url; } diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 1772fe9c06e5e..f1bc930775de7 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -12,11 +12,13 @@ import type { } from '@react-navigation/native'; import type {TupleToUnion, ValueOf} from 'type-fest'; import type {UpperCaseCharacters} from 'type-fest/source/internal'; +import type {FileObject} from '@components/AttachmentComposerModal'; import type {SearchQueryString} from '@components/Search/types'; import type {IOURequestType} from '@libs/actions/IOU'; import type {SaveSearchParams} from '@libs/API/parameters'; import type {ReimbursementAccountStepToOpen} from '@libs/ReimbursementAccountUtils'; -import type {AttachmentModalScreenParams} from '@pages/media/AttachmentModalScreen/types'; +import type {AvatarSource} from '@libs/UserUtils'; +import type {AttachmentModalContainerModalProps} from '@pages/media/AttachmentModalScreen/types'; import type CONST from '@src/CONST'; import type {Country, IOUAction, IOUType} from '@src/CONST'; import type NAVIGATORS from '@src/NAVIGATORS'; @@ -1869,7 +1871,7 @@ type ReportsSplitNavigatorParamList = { transactionID?: string; iouReportID?: string; }; - [SCREENS.ATTACHMENTS]: AttachmentModalScreenParams; + [SCREENS.ATTACHMENTS]: AttachmentModalScreensParamList[typeof SCREENS.ATTACHMENTS]; }; type SettingsSplitNavigatorParamList = { @@ -2125,65 +2127,87 @@ type PublicScreensParamList = SharedScreensParamList & { [NAVIGATORS.TEST_TOOLS_MODAL_NAVIGATOR]: NavigatorScreenParams; }; -type AuthScreensParamList = SharedScreensParamList & { - [SCREENS.CONCIERGE]: undefined; - [SCREENS.TRACK_EXPENSE]: undefined; - [SCREENS.SUBMIT_EXPENSE]: undefined; - [SCREENS.ATTACHMENTS]: AttachmentModalScreenParams; - [SCREENS.PROFILE_AVATAR]: { - accountID: string; +type AttachmentModalScreensParamList = { + [SCREENS.ATTACHMENTS]: AttachmentModalContainerModalProps & { + source?: AvatarSource; + reportID?: string; + accountID?: number; + attachmentID?: string; + type?: ValueOf; + fallbackSource?: AvatarSource; + isAuthTokenRequired?: boolean; + originalFileName?: string; + attachmentLink?: string; + headerTitle?: string; + hashKey?: number; + maybeIcon?: boolean; + file?: FileObject; + shouldDisableSendButton?: boolean; + onConfirm?: (file: FileObject) => void; + }; + [SCREENS.PROFILE_AVATAR]: AttachmentModalContainerModalProps & { + accountID: number; backTo?: Routes; }; - [SCREENS.WORKSPACE_AVATAR]: { + [SCREENS.WORKSPACE_AVATAR]: AttachmentModalContainerModalProps & { policyID: string; letter?: UpperCaseCharacters; }; - [SCREENS.WORKSPACE_JOIN_USER]: { - policyID: string; - email: string; - }; - [SCREENS.REPORT_AVATAR]: { + [SCREENS.REPORT_AVATAR]: AttachmentModalContainerModalProps & { reportID: string; policyID?: string; }; - [SCREENS.WORKSPACES_LIST]: { - backTo?: Routes; - }; - [SCREENS.NOT_FOUND]: undefined; - [SCREENS.REQUIRE_TWO_FACTOR_AUTH]: undefined; - [NAVIGATORS.REPORTS_SPLIT_NAVIGATOR]: NavigatorScreenParams; - [NAVIGATORS.SETTINGS_SPLIT_NAVIGATOR]: NavigatorScreenParams; - [NAVIGATORS.WORKSPACE_SPLIT_NAVIGATOR]: NavigatorScreenParams; - [NAVIGATORS.RIGHT_MODAL_NAVIGATOR]: NavigatorScreenParams; - [NAVIGATORS.ONBOARDING_MODAL_NAVIGATOR]: NavigatorScreenParams; - [NAVIGATORS.FEATURE_TRAINING_MODAL_NAVIGATOR]: NavigatorScreenParams; - [NAVIGATORS.WELCOME_VIDEO_MODAL_NAVIGATOR]: NavigatorScreenParams; - [NAVIGATORS.EXPLANATION_MODAL_NAVIGATOR]: NavigatorScreenParams; - [NAVIGATORS.MIGRATED_USER_MODAL_NAVIGATOR]: NavigatorScreenParams; - [NAVIGATORS.TEST_DRIVE_MODAL_NAVIGATOR]: NavigatorScreenParams; - [NAVIGATORS.TEST_DRIVE_DEMO_NAVIGATOR]: NavigatorScreenParams; - [NAVIGATORS.SEARCH_FULLSCREEN_NAVIGATOR]: NavigatorScreenParams; - [SCREENS.DESKTOP_SIGN_IN_REDIRECT]: undefined; - [SCREENS.TRANSACTION_RECEIPT]: { + [SCREENS.TRANSACTION_RECEIPT]: AttachmentModalContainerModalProps & { reportID: string; transactionID: string; readonly?: string; isFromReviewDuplicates?: string; + action?: IOUAction; + iouType?: IOUType; mergeTransactionID?: string; }; - [SCREENS.MONEY_REQUEST.RECEIPT_PREVIEW]: { + [SCREENS.MONEY_REQUEST.RECEIPT_PREVIEW]: AttachmentModalContainerModalProps & { reportID: string; transactionID: string; action: IOUAction; iouType: IOUType; readonly: string; }; - [SCREENS.CONNECTION_COMPLETE]: undefined; - [NAVIGATORS.SHARE_MODAL_NAVIGATOR]: NavigatorScreenParams; - [SCREENS.BANK_CONNECTION_COMPLETE]: undefined; - [NAVIGATORS.TEST_TOOLS_MODAL_NAVIGATOR]: NavigatorScreenParams; }; +type AuthScreensParamList = SharedScreensParamList & + AttachmentModalScreensParamList & { + [SCREENS.CONCIERGE]: undefined; + [SCREENS.TRACK_EXPENSE]: undefined; + [SCREENS.SUBMIT_EXPENSE]: undefined; + [SCREENS.WORKSPACES_LIST]: { + backTo?: Routes; + }; + [SCREENS.WORKSPACE_JOIN_USER]: { + policyID: string; + email: string; + }; + [SCREENS.NOT_FOUND]: undefined; + [SCREENS.REQUIRE_TWO_FACTOR_AUTH]: undefined; + [NAVIGATORS.REPORTS_SPLIT_NAVIGATOR]: NavigatorScreenParams; + [NAVIGATORS.SETTINGS_SPLIT_NAVIGATOR]: NavigatorScreenParams; + [NAVIGATORS.WORKSPACE_SPLIT_NAVIGATOR]: NavigatorScreenParams; + [NAVIGATORS.RIGHT_MODAL_NAVIGATOR]: NavigatorScreenParams; + [NAVIGATORS.ONBOARDING_MODAL_NAVIGATOR]: NavigatorScreenParams; + [NAVIGATORS.FEATURE_TRAINING_MODAL_NAVIGATOR]: NavigatorScreenParams; + [NAVIGATORS.WELCOME_VIDEO_MODAL_NAVIGATOR]: NavigatorScreenParams; + [NAVIGATORS.EXPLANATION_MODAL_NAVIGATOR]: NavigatorScreenParams; + [NAVIGATORS.MIGRATED_USER_MODAL_NAVIGATOR]: NavigatorScreenParams; + [NAVIGATORS.TEST_DRIVE_MODAL_NAVIGATOR]: NavigatorScreenParams; + [NAVIGATORS.TEST_DRIVE_DEMO_NAVIGATOR]: NavigatorScreenParams; + [NAVIGATORS.SEARCH_FULLSCREEN_NAVIGATOR]: NavigatorScreenParams; + [SCREENS.DESKTOP_SIGN_IN_REDIRECT]: undefined; + [SCREENS.CONNECTION_COMPLETE]: undefined; + [NAVIGATORS.SHARE_MODAL_NAVIGATOR]: NavigatorScreenParams; + [SCREENS.BANK_CONNECTION_COMPLETE]: undefined; + [NAVIGATORS.TEST_TOOLS_MODAL_NAVIGATOR]: NavigatorScreenParams; + }; + type SearchReportParamList = { [SCREENS.SEARCH.REPORT_RHP]: { reportID: string; @@ -2420,4 +2444,5 @@ export type { ReportChangeApproverParamList, TestToolsModalModalNavigatorParamList, MergeTransactionNavigatorParamList, + AttachmentModalScreensParamList, }; diff --git a/src/pages/media/AttachmentModalScreen/AttachmentModalBaseContent.tsx b/src/pages/media/AttachmentModalScreen/AttachmentModalBaseContent/index.tsx similarity index 81% rename from src/pages/media/AttachmentModalScreen/AttachmentModalBaseContent.tsx rename to src/pages/media/AttachmentModalScreen/AttachmentModalBaseContent/index.tsx index 4a607151fb40f..244233b0f2a4f 100644 --- a/src/pages/media/AttachmentModalScreen/AttachmentModalBaseContent.tsx +++ b/src/pages/media/AttachmentModalScreen/AttachmentModalBaseContent/index.tsx @@ -1,10 +1,7 @@ -import type {RefObject} from 'react'; import React, {memo, useCallback, useEffect, useMemo, useRef, useState} from 'react'; import {InteractionManager, Keyboard, View} from 'react-native'; import {GestureHandlerRootView} from 'react-native-gesture-handler'; -import type {OnyxEntry} from 'react-native-onyx'; import Animated, {FadeIn, LayoutAnimationConfig, useSharedValue} from 'react-native-reanimated'; -import type {ValueOf} from 'type-fest'; import AttachmentCarousel from '@components/Attachments/AttachmentCarousel'; import AttachmentCarouselPagerContext from '@components/Attachments/AttachmentCarousel/Pager/AttachmentCarouselPagerContext'; import AttachmentView from '@components/Attachments/AttachmentView'; @@ -32,140 +29,15 @@ import Navigation from '@libs/Navigation/Navigation'; import {getOriginalMessage, getReportAction, isMoneyRequestAction} from '@libs/ReportActionsUtils'; import {hasEReceipt, hasMissingSmartscanFields, hasReceipt, hasReceiptSource, isReceiptBeingScanned} from '@libs/TransactionUtils'; import type {AvatarSource} from '@libs/UserUtils'; +import type {FileObject} from '@pages/media/AttachmentModalScreen/types'; import variables from '@styles/variables'; import {detachReceipt} from '@userActions/IOU'; -import type {IOUAction, IOUType} from '@src/CONST'; import CONST from '@src/CONST'; -import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import type * as OnyxTypes from '@src/types/onyx'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import viewRef from '@src/types/utils/viewRef'; -import type {FileObject} from './types'; - -type OnValidateFileCallback = (file: FileObject | undefined, setFile: (file: FileObject | undefined) => void) => void; - -type OnCloseOptions = { - shouldCallDirectly?: boolean; - onAfterClose?: () => void; -}; - -type AttachmentModalBaseContentProps = { - /** Optional source (URL, SVG function) for the image shown. If not passed in via props must be specified when modal is opened. */ - source?: AvatarSource; - - /** The id of the attachment. */ - attachmentID?: string; - - /** Fallback source (URL, SVG function) for the image shown. */ - fallbackSource?: AvatarSource; - - /** Optional file object to be used for the attachment. If not passed in via props must be specified when modal is opened. */ - file?: FileObject; - - /** Optional original filename when uploading */ - originalFileName?: string; - - /** Whether source url requires authentication */ - isAuthTokenRequired?: boolean; - - /** Determines if download Button should be shown or not */ - allowDownload?: boolean; - - /** Determines if the receipt comes from track expense action */ - isTrackExpenseAction?: boolean; - - /** Title shown in the header of the modal */ - headerTitle?: string; - - /** The report that has this attachment */ - report?: OnyxEntry; - - /** The ID of the current report */ - reportID?: string; - - /** The type of the attachment */ - type?: ValueOf; - - /** The iou action of the expense creation flow of which we are displaying the receipt for. */ - iouAction?: IOUAction; - - /** The iou type of the expense creation flow of which we are displaying the receipt for. */ - iouType?: IOUType; - - /** The id of the draft transaction linked to the receipt. */ - draftTransactionID?: string; - - /** If the attachment originates from a note, the accountID will represent the author of that note. */ - accountID?: number; - - /** The data is loading or not */ - isLoading?: boolean; - - /** Should display not found page or not */ - shouldShowNotFoundPage?: boolean; - - /** Denotes whether it is a workspace avatar or not */ - isWorkspaceAvatar?: boolean; - - /** Denotes whether it can be an icon (ex: SVG) */ - maybeIcon?: boolean; - - /** Whether it is a receipt attachment or not */ - isReceiptAttachment?: boolean; - - /** Determines if the user can edit the receipt or not */ - canEditReceipt?: boolean; - - /** Determines if the user can delete the receipt or not */ - canDeleteReceipt?: boolean; - - /** Determines if the send button should be disabled or not */ - shouldDisableSendButton?: boolean; - - /** Determines if the help button should be displayed or not */ - shouldDisplayHelpButton?: boolean; - - /** The link of the attachment */ - attachmentLink?: string; - - /** Determines if the attachment is invalid or not */ - isAttachmentInvalid?: boolean; - - /** Determines if the attachment is invalid or not */ - attachmentInvalidReason?: TranslationPaths | null; - - /** Determines the title of the invalid reason modal */ - attachmentInvalidReasonTitle?: TranslationPaths | null; - - /** Ref to the submit button */ - submitRef?: RefObject; - - /** Determines if the delete receipt confirm modal is visible or not */ - isDeleteReceiptConfirmModalVisible?: boolean; - - /** Optional callback to fire when we want to preview an image and approve it for use. */ - onConfirm?: (file: FileObject) => void; - - /** Callback triggered when the modal is closed */ - onClose?: (options?: OnCloseOptions) => void; - - /** Callback triggered when the confirm modal is closed */ - onConfirmModalClose?: () => void; - - /** Callback triggered when the delete receipt modal is shown */ - onRequestDeleteReceipt?: () => void; - - /** Callback triggered when the delete receipt is confirmed */ - onDeleteReceipt?: () => void; - - /** Optional callback to fire when we want to do something after attachment carousel changes. */ - onCarouselAttachmentChange?: (attachment: Attachment) => void; - - /** Optional callback to fire when we want to validate the file. */ - onValidateFile?: OnValidateFileCallback; -}; +import type {AttachmentModalBaseContentProps} from './types'; function AttachmentModalBaseContent({ source = '', @@ -575,4 +447,4 @@ AttachmentModalBaseContent.displayName = 'AttachmentModalBaseContent'; export default memo(AttachmentModalBaseContent); -export type {AttachmentModalBaseContentProps, OnValidateFileCallback, OnCloseOptions}; +export type {AttachmentModalBaseContentProps}; diff --git a/src/pages/media/AttachmentModalScreen/AttachmentModalBaseContent/types.ts b/src/pages/media/AttachmentModalScreen/AttachmentModalBaseContent/types.ts new file mode 100644 index 0000000000000..bfa8892f9544c --- /dev/null +++ b/src/pages/media/AttachmentModalScreen/AttachmentModalBaseContent/types.ts @@ -0,0 +1,136 @@ +import type {RefObject} from 'react'; +import type {View} from 'react-native'; +import type {OnyxEntry} from 'react-native-onyx'; +import type {ValueOf} from 'type-fest'; +import type {Attachment} from '@components/Attachments/types'; +import type {AvatarSource} from '@libs/UserUtils'; +import type {FileObject} from '@pages/media/AttachmentModalScreen/types'; +import type {IOUAction, IOUType} from '@src/CONST'; +import type CONST from '@src/CONST'; +import type {TranslationPaths} from '@src/languages/types'; +import type * as OnyxTypes from '@src/types/onyx'; + +type OnValidateFileCallback = (file: FileObject | undefined, setFile: (file: FileObject | undefined) => void) => void; + +type AttachmentModalOnCloseOptions = { + shouldCallDirectly?: boolean; + onAfterClose?: () => void; +}; + +type AttachmentModalBaseContentProps = { + /** Optional source (URL, SVG function) for the image shown. If not passed in via props must be specified when modal is opened. */ + source?: AvatarSource; + + /** The id of the attachment. */ + attachmentID?: string; + + /** Fallback source (URL, SVG function) for the image shown. */ + fallbackSource?: AvatarSource; + + /** Optional file object to be used for the attachment. If not passed in via props must be specified when modal is opened. */ + file?: FileObject; + + /** Optional original filename when uploading */ + originalFileName?: string; + + /** Whether source url requires authentication */ + isAuthTokenRequired?: boolean; + + /** Determines if download Button should be shown or not */ + allowDownload?: boolean; + + /** Determines if the receipt comes from track expense action */ + isTrackExpenseAction?: boolean; + + /** Title shown in the header of the modal */ + headerTitle?: string; + + /** The report that has this attachment */ + report?: OnyxEntry; + + /** The ID of the current report */ + reportID?: string; + + /** The type of the attachment */ + type?: ValueOf; + + /** The iou action of the expense creation flow of which we are displaying the receipt for. */ + iouAction?: IOUAction; + + /** The iou type of the expense creation flow of which we are displaying the receipt for. */ + iouType?: IOUType; + + /** The id of the draft transaction linked to the receipt. */ + draftTransactionID?: string; + + /** If the attachment originates from a note, the accountID will represent the author of that note. */ + accountID?: number; + + /** The data is loading or not */ + isLoading?: boolean; + + /** Should display not found page or not */ + shouldShowNotFoundPage?: boolean; + + /** Denotes whether it is a workspace avatar or not */ + isWorkspaceAvatar?: boolean; + + /** Denotes whether it can be an icon (ex: SVG) */ + maybeIcon?: boolean; + + /** Whether it is a receipt attachment or not */ + isReceiptAttachment?: boolean; + + /** Determines if the user can edit the receipt or not */ + canEditReceipt?: boolean; + + /** Determines if the user can delete the receipt or not */ + canDeleteReceipt?: boolean; + + /** Determines if the send button should be disabled or not */ + shouldDisableSendButton?: boolean; + + /** Determines if the help button should be displayed or not */ + shouldDisplayHelpButton?: boolean; + + /** The link of the attachment */ + attachmentLink?: string; + + /** Determines if the attachment is invalid or not */ + isAttachmentInvalid?: boolean; + + /** Determines if the attachment is invalid or not */ + attachmentInvalidReason?: TranslationPaths | null; + + /** Determines the title of the invalid reason modal */ + attachmentInvalidReasonTitle?: TranslationPaths | null; + + /** Ref to the submit button */ + submitRef?: RefObject; + + /** Determines if the delete receipt confirm modal is visible or not */ + isDeleteReceiptConfirmModalVisible?: boolean; + + /** Optional callback to fire when we want to preview an image and approve it for use. */ + onConfirm?: (file: FileObject) => void; + + /** Callback triggered when the modal is closed */ + onClose?: (options?: AttachmentModalOnCloseOptions) => void; + + /** Callback triggered when the confirm modal is closed */ + onConfirmModalClose?: () => void; + + /** Callback triggered when the delete receipt modal is shown */ + onRequestDeleteReceipt?: () => void; + + /** Callback triggered when the delete receipt is confirmed */ + onDeleteReceipt?: () => void; + + /** Optional callback to fire when we want to do something after attachment carousel changes. */ + onCarouselAttachmentChange?: (attachment: Attachment) => void; + + /** Optional callback to fire when we want to validate the file. */ + onValidateFile?: OnValidateFileCallback; +}; + +export type {AttachmentModalBaseContentProps, AttachmentModalOnCloseOptions, OnValidateFileCallback}; diff --git a/src/pages/media/AttachmentModalScreen/AttachmentModalContainer/index.native.tsx b/src/pages/media/AttachmentModalScreen/AttachmentModalContainer/index.native.tsx index 92219432038ed..87a8c4fc1f00b 100644 --- a/src/pages/media/AttachmentModalScreen/AttachmentModalContainer/index.native.tsx +++ b/src/pages/media/AttachmentModalScreen/AttachmentModalContainer/index.native.tsx @@ -2,17 +2,18 @@ import React, {memo, useCallback, useContext, useEffect} from 'react'; import ScreenWrapper from '@components/ScreenWrapper'; import attachmentModalHandler from '@libs/AttachmentModalHandler'; import Navigation from '@libs/Navigation/Navigation'; -import type {OnCloseOptions} from '@pages/media/AttachmentModalScreen/AttachmentModalBaseContent'; import AttachmentModalBaseContent from '@pages/media/AttachmentModalScreen/AttachmentModalBaseContent'; +import type {AttachmentModalOnCloseOptions} from '@pages/media/AttachmentModalScreen/AttachmentModalBaseContent/types'; import AttachmentModalContext from '@pages/media/AttachmentModalScreen/AttachmentModalContext'; +import type {AttachmentModalScreenType} from '@pages/media/AttachmentModalScreen/types'; import type AttachmentModalContainerProps from './types'; -function AttachmentModalContainer({contentProps, navigation, onShow, onClose}: AttachmentModalContainerProps) { +function AttachmentModalContainer({contentProps, navigation, onShow, onClose}: AttachmentModalContainerProps) { const attachmentsContext = useContext(AttachmentModalContext); const testID = typeof contentProps.source === 'string' ? contentProps.source : (contentProps.source?.toString() ?? ''); const closeScreen = useCallback( - (options?: OnCloseOptions) => { + (options?: AttachmentModalOnCloseOptions) => { attachmentsContext.setCurrentAttachment(undefined); const close = () => { diff --git a/src/pages/media/AttachmentModalScreen/AttachmentModalContainer/index.tsx b/src/pages/media/AttachmentModalScreen/AttachmentModalContainer/index.tsx index fc4b0b7e7f278..c1f0c49c4ba72 100644 --- a/src/pages/media/AttachmentModalScreen/AttachmentModalContainer/index.tsx +++ b/src/pages/media/AttachmentModalScreen/AttachmentModalContainer/index.tsx @@ -2,13 +2,14 @@ import React, {useCallback, useContext, useEffect, useState} from 'react'; import {InteractionManager} from 'react-native'; import Modal from '@components/Modal'; import Navigation from '@libs/Navigation/Navigation'; -import type {OnCloseOptions} from '@pages/media/AttachmentModalScreen/AttachmentModalBaseContent'; import AttachmentModalBaseContent from '@pages/media/AttachmentModalScreen/AttachmentModalBaseContent'; +import type {AttachmentModalOnCloseOptions} from '@pages/media/AttachmentModalScreen/AttachmentModalBaseContent/types'; import AttachmentModalContext from '@pages/media/AttachmentModalScreen/AttachmentModalContext'; +import type {AttachmentModalScreenType} from '@pages/media/AttachmentModalScreen/types'; import CONST from '@src/CONST'; import type AttachmentModalContainerProps from './types'; -function AttachmentModalContainer({contentProps, modalType, onShow, onClose, shouldHandleNavigationBack}: AttachmentModalContainerProps) { +function AttachmentModalContainer({contentProps, modalType, onShow, onClose, shouldHandleNavigationBack}: AttachmentModalContainerProps) { const [isVisible, setIsVisible] = useState(true); const attachmentsContext = useContext(AttachmentModalContext); const [shouldDisableAnimationAfterInitialMount, setShouldDisableAnimationAfterInitialMount] = useState(false); @@ -21,7 +22,7 @@ function AttachmentModalContainer({contentProps, modalType, onShow, onClose, sho * This ensures smooth modal closing behavior without causing delays in closing. */ const closeModal = useCallback( - (options?: OnCloseOptions) => { + (options?: AttachmentModalOnCloseOptions) => { attachmentsContext.setCurrentAttachment(undefined); setIsVisible(false); diff --git a/src/pages/media/AttachmentModalScreen/AttachmentModalContainer/types.ts b/src/pages/media/AttachmentModalScreen/AttachmentModalContainer/types.ts index 372c8b551916a..d56988be8895f 100644 --- a/src/pages/media/AttachmentModalScreen/AttachmentModalContainer/types.ts +++ b/src/pages/media/AttachmentModalScreen/AttachmentModalContainer/types.ts @@ -1,8 +1,8 @@ -import type {AttachmentModalBaseContentProps} from '@pages/media/AttachmentModalScreen/AttachmentModalBaseContent'; -import type {AttachmentModalModalProps, AttachmentModalScreenProps} from '@pages/media/AttachmentModalScreen/types'; +import type {AttachmentModalBaseContentProps} from '@pages/media/AttachmentModalScreen/AttachmentModalBaseContent/types'; +import type {AttachmentModalContainerModalProps, AttachmentModalScreenProps, AttachmentModalScreenType} from '@pages/media/AttachmentModalScreen/types'; -type AttachmentModalContainerProps = AttachmentModalModalProps & { - navigation: AttachmentModalScreenProps['navigation']; +type AttachmentModalContainerProps = AttachmentModalContainerModalProps & { + navigation: AttachmentModalScreenProps['navigation']; contentProps: Partial; }; diff --git a/src/pages/media/AttachmentModalScreen/AttachmentModalContext.tsx b/src/pages/media/AttachmentModalScreen/AttachmentModalContext.tsx index 30c71f3cb2ad5..383cf1c3689e6 100644 --- a/src/pages/media/AttachmentModalScreen/AttachmentModalContext.tsx +++ b/src/pages/media/AttachmentModalScreen/AttachmentModalContext.tsx @@ -1,13 +1,16 @@ import React, {useCallback, useEffect, useMemo, useRef} from 'react'; import useCurrentReportID from '@hooks/useCurrentReportID'; import type ChildrenProps from '@src/types/utils/ChildrenProps'; -import type {AttachmentModalScreenParams} from './types'; +import type AttachmentModalScreenParams from './routes/types'; +import type {AttachmentModalScreenBaseParams, AttachmentModalScreenType} from './types'; type AttachmentModalContextValue = { isAttachmentHidden: (reportActionID: string) => boolean; updateHiddenAttachments: (reportActionID: string, isHidden: boolean) => void; - setCurrentAttachment: (attachmentProps: AttachmentModalScreenParams | undefined) => void; - getCurrentAttachment: () => AttachmentModalScreenParams | undefined; + setCurrentAttachment: = AttachmentModalScreenParams>( + attachmentParams: RouteParams | undefined, + ) => void; + getCurrentAttachment: = AttachmentModalScreenParams>() => RouteParams | undefined; }; const AttachmentModalContext = React.createContext({ @@ -27,11 +30,18 @@ function AttachmentModalContextProvider({children}: ChildrenProps) { hiddenAttachments.current = {}; }, [currentReportID?.currentReportID]); - const currentAttachment = useRef(undefined); - const setCurrentAttachment = useCallback((attachmentProps: AttachmentModalScreenParams | undefined) => { - currentAttachment.current = attachmentProps; - }, []); - const getCurrentAttachment = useCallback(() => currentAttachment.current, []); + const currentAttachment = useRef(undefined); + const setCurrentAttachment = useCallback( + = AttachmentModalScreenParams>(attachmentProps: RouteParams | undefined) => { + currentAttachment.current = attachmentProps; + }, + [], + ); + const getCurrentAttachment = useCallback( + = AttachmentModalScreenParams>() => + currentAttachment.current as RouteParams | undefined, + [], + ); const contextValue = useMemo( () => ({ isAttachmentHidden: (reportActionID: string) => hiddenAttachments.current[reportActionID], diff --git a/src/pages/media/AttachmentModalScreen/index.tsx b/src/pages/media/AttachmentModalScreen/index.tsx index cfd583bf5c584..f4ce3b3734816 100644 --- a/src/pages/media/AttachmentModalScreen/index.tsx +++ b/src/pages/media/AttachmentModalScreen/index.tsx @@ -6,30 +6,31 @@ import ReportAttachmentModalContent from './routes/ReportAttachmentModalContent' import ReportAvatarModalContent from './routes/ReportAvatarModalContent'; import TransactionReceiptModalContent from './routes/TransactionReceiptModalContent'; import WorkspaceAvatarModalContent from './routes/WorkspaceAvatarModalContent'; -import type {AttachmentModalScreenParams, AttachmentModalScreenProps} from './types'; +import type {AttachmentModalScreenProps, AttachmentModalScreenType} from './types'; + +type RouteType = AttachmentModalScreenProps['route']; +type NavigationType = AttachmentModalScreenProps['navigation']; /** * The attachment modal screen can take various different shapes. This is the main screen component that receives the route and * navigation props from the parent screen and renders the correct modal content based on the route. */ -function AttachmentModalScreen({route, navigation}: AttachmentModalScreenProps) { +function AttachmentModalScreen({route, navigation}: AttachmentModalScreenProps) { const attachmentsContext = useContext(AttachmentModalContext); - const paramsWithContext: AttachmentModalScreenParams = useMemo(() => { - const currentAttachment = attachmentsContext.getCurrentAttachment(); + const routeWithContext = useMemo(() => { + const currentAttachment = attachmentsContext.getCurrentAttachment(); if (currentAttachment) { - return {...route.params, ...currentAttachment}; + return {...route, params: {...route.params, ...currentAttachment}}; } - return route.params; - }, [attachmentsContext, route.params]); + return route; + }, [attachmentsContext, route]); if (route.name === SCREENS.ATTACHMENTS) { return ( } + navigation={navigation as NavigationType} /> ); } @@ -37,8 +38,8 @@ function AttachmentModalScreen({route, navigation}: AttachmentModalScreenProps) if (route.name === SCREENS.TRANSACTION_RECEIPT || route.name === SCREENS.MONEY_REQUEST.RECEIPT_PREVIEW) { return ( } + navigation={navigation as NavigationType} /> ); } @@ -46,8 +47,8 @@ function AttachmentModalScreen({route, navigation}: AttachmentModalScreenProps) if (route.name === SCREENS.PROFILE_AVATAR) { return ( } + navigation={navigation as NavigationType} /> ); } @@ -55,8 +56,8 @@ function AttachmentModalScreen({route, navigation}: AttachmentModalScreenProps) if (route.name === SCREENS.WORKSPACE_AVATAR) { return ( } + navigation={navigation as NavigationType} /> ); } @@ -64,8 +65,8 @@ function AttachmentModalScreen({route, navigation}: AttachmentModalScreenProps) if (route.name === SCREENS.REPORT_AVATAR) { return ( } + navigation={navigation as NavigationType} /> ); } diff --git a/src/pages/media/AttachmentModalScreen/routes/ProfileAvatarModalContent.tsx b/src/pages/media/AttachmentModalScreen/routes/ProfileAvatarModalContent.tsx index 0e94e91de5ee9..a9ae08607976d 100644 --- a/src/pages/media/AttachmentModalScreen/routes/ProfileAvatarModalContent.tsx +++ b/src/pages/media/AttachmentModalScreen/routes/ProfileAvatarModalContent.tsx @@ -5,13 +5,14 @@ import {openPublicProfilePage} from '@libs/actions/PersonalDetails'; import {getDisplayNameOrDefault} from '@libs/PersonalDetailsUtils'; import {getFullSizeAvatar} from '@libs/UserUtils'; import {isValidAccountRoute} from '@libs/ValidationUtils'; -import type {AttachmentModalBaseContentProps} from '@pages/media/AttachmentModalScreen/AttachmentModalBaseContent'; +import type {AttachmentModalBaseContentProps} from '@pages/media/AttachmentModalScreen/AttachmentModalBaseContent/types'; import AttachmentModalContainer from '@pages/media/AttachmentModalScreen/AttachmentModalContainer'; import type {AttachmentModalScreenProps} from '@pages/media/AttachmentModalScreen/types'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import type SCREENS from '@src/SCREENS'; -function ProfileAvatarModalContent({navigation, route}: AttachmentModalScreenProps) { +function ProfileAvatarModalContent({navigation, route}: AttachmentModalScreenProps) { const {accountID = CONST.DEFAULT_NUMBER_ID} = route.params; const {formatPhoneNumber} = useLocalize(); const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {canBeMissing: false}); @@ -27,16 +28,15 @@ function ProfileAvatarModalContent({navigation, route}: AttachmentModalScreenPro openPublicProfilePage(accountID); }, [accountID]); - const contentProps = useMemo( - () => - ({ - source: getFullSizeAvatar(avatarURL, accountID), - isLoading: !!(personalDetailsMetadata?.[accountID]?.isLoading ?? (isLoadingApp && !Object.keys(personalDetail ?? {}).length)), - headerTitle: formatPhoneNumber(displayName), - originalFileName: personalDetail?.originalFileName ?? '', - shouldShowNotFoundPage: !avatarURL, - maybeIcon: true, - }) satisfies Partial, + const contentProps = useMemo( + () => ({ + source: getFullSizeAvatar(avatarURL, accountID), + isLoading: !!(personalDetailsMetadata?.[accountID]?.isLoading ?? (isLoadingApp && !Object.keys(personalDetail ?? {}).length)), + headerTitle: formatPhoneNumber(displayName), + originalFileName: personalDetail?.originalFileName ?? '', + shouldShowNotFoundPage: !avatarURL, + maybeIcon: true, + }), [accountID, avatarURL, displayName, isLoadingApp, personalDetail, personalDetailsMetadata, formatPhoneNumber], ); diff --git a/src/pages/media/AttachmentModalScreen/routes/ReportAttachmentModalContent.tsx b/src/pages/media/AttachmentModalScreen/routes/ReportAttachmentModalContent.tsx index b87d6c17ec0a9..9b49c044395b1 100644 --- a/src/pages/media/AttachmentModalScreen/routes/ReportAttachmentModalContent.tsx +++ b/src/pages/media/AttachmentModalScreen/routes/ReportAttachmentModalContent.tsx @@ -11,17 +11,18 @@ import {translateLocal} from '@libs/Localize'; import Navigation from '@libs/Navigation/Navigation'; import {isReportNotFound} from '@libs/ReportUtils'; import tryResolveUrlFromApiRoot from '@libs/tryResolveUrlFromApiRoot'; -import type {AttachmentModalBaseContentProps, OnValidateFileCallback} from '@pages/media/AttachmentModalScreen/AttachmentModalBaseContent'; +import type {AttachmentModalBaseContentProps, OnValidateFileCallback} from '@pages/media/AttachmentModalScreen/AttachmentModalBaseContent/types'; import AttachmentModalContainer from '@pages/media/AttachmentModalScreen/AttachmentModalContainer'; import type {AttachmentModalScreenProps, FileObject} from '@pages/media/AttachmentModalScreen/types'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import type SCREENS from '@src/SCREENS'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import type ModalType from '@src/types/utils/ModalType'; -function ReportAttachmentModalContent({route, navigation}: AttachmentModalScreenProps) { +function ReportAttachmentModalContent({route, navigation}: AttachmentModalScreenProps) { const { attachmentID, type, @@ -164,7 +165,7 @@ function ReportAttachmentModalContent({route, navigation}: AttachmentModalScreen [getModalType], ); - const contentTypeProps = useMemo>( + const contentTypeProps = useMemo( () => fileParam ? { @@ -185,7 +186,7 @@ function ReportAttachmentModalContent({route, navigation}: AttachmentModalScreen [attachmentLink, fileParam, isAuthTokenRequired, isLoading, originalFileName, report, type, validateFile], ); - const contentProps = useMemo>( + const contentProps = useMemo( () => ({ ...contentTypeProps, source, diff --git a/src/pages/media/AttachmentModalScreen/routes/ReportAvatarModalContent.tsx b/src/pages/media/AttachmentModalScreen/routes/ReportAvatarModalContent.tsx index 5a9ac86bbdf36..e2c83a2730b7b 100644 --- a/src/pages/media/AttachmentModalScreen/routes/ReportAvatarModalContent.tsx +++ b/src/pages/media/AttachmentModalScreen/routes/ReportAvatarModalContent.tsx @@ -2,12 +2,13 @@ import React, {useMemo} from 'react'; import useOnyx from '@hooks/useOnyx'; import {getDefaultGroupAvatar, getPolicyName, getReportName, getWorkspaceIcon, isGroupChat, isThread} from '@libs/ReportUtils'; import {getFullSizeAvatar} from '@libs/UserUtils'; -import type {AttachmentModalBaseContentProps} from '@pages/media/AttachmentModalScreen/AttachmentModalBaseContent'; +import type {AttachmentModalBaseContentProps} from '@pages/media/AttachmentModalScreen/AttachmentModalBaseContent/types'; import AttachmentModalContainer from '@pages/media/AttachmentModalScreen/AttachmentModalContainer'; import type {AttachmentModalScreenProps} from '@pages/media/AttachmentModalScreen/types'; import ONYXKEYS from '@src/ONYXKEYS'; +import type SCREENS from '@src/SCREENS'; -function ReportAvatarModalContent({navigation, route}: AttachmentModalScreenProps) { +function ReportAvatarModalContent({navigation, route}: AttachmentModalScreenProps) { const {reportID, policyID} = route.params; const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, {canBeMissing: false}); @@ -32,13 +33,12 @@ function ReportAvatarModalContent({navigation, route}: AttachmentModalScreenProp }; }, [policy, report]); - const contentProps = useMemo( - () => - ({ - ...attachment, - shouldShowNotFoundPage: !report?.reportID && !isLoadingApp, - isLoading: (!report?.reportID || !policy?.id) && !!isLoadingApp, - }) satisfies Partial, + const contentProps = useMemo( + () => ({ + ...attachment, + shouldShowNotFoundPage: !report?.reportID && !isLoadingApp, + isLoading: (!report?.reportID || !policy?.id) && !!isLoadingApp, + }), [attachment, isLoadingApp, policy?.id, report?.reportID], ); diff --git a/src/pages/media/AttachmentModalScreen/routes/TransactionReceiptModalContent.tsx b/src/pages/media/AttachmentModalScreen/routes/TransactionReceiptModalContent.tsx index 513f86b5a57d1..60bb189fd13ee 100644 --- a/src/pages/media/AttachmentModalScreen/routes/TransactionReceiptModalContent.tsx +++ b/src/pages/media/AttachmentModalScreen/routes/TransactionReceiptModalContent.tsx @@ -9,15 +9,16 @@ import {getReportAction, isTrackExpenseAction} from '@libs/ReportActionsUtils'; import {canEditFieldOfMoneyRequest, isMoneyRequestReport, isTrackExpenseReport} from '@libs/ReportUtils'; import {getRequestType, hasEReceipt, hasReceiptSource} from '@libs/TransactionUtils'; import tryResolveUrlFromApiRoot from '@libs/tryResolveUrlFromApiRoot'; -import type {AttachmentModalBaseContentProps} from '@pages/media/AttachmentModalScreen/AttachmentModalBaseContent'; +import type {AttachmentModalBaseContentProps} from '@pages/media/AttachmentModalScreen/AttachmentModalBaseContent/types'; import AttachmentModalContainer from '@pages/media/AttachmentModalScreen/AttachmentModalContainer'; import type {AttachmentModalScreenProps} from '@pages/media/AttachmentModalScreen/types'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import type SCREENS from '@src/SCREENS'; -function TransactionReceiptModalContent({navigation, route}: AttachmentModalScreenProps) { - const {reportID = '', transactionID = '', iouAction, iouType, readonly: readonlyProp, isFromReviewDuplicates: isFromReviewDuplicatesProp} = route.params; +function TransactionReceiptModalContent({navigation, route}: AttachmentModalScreenProps) { + const {reportID = '', transactionID = '', action, iouType, readonly: readonlyProp, isFromReviewDuplicates: isFromReviewDuplicatesProp} = route.params; const {isBetaEnabled} = usePermissions(); const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, {canBeMissing: true}); @@ -25,13 +26,13 @@ function TransactionReceiptModalContent({navigation, route}: AttachmentModalScre const [transactionDraft] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {canBeMissing: true}); const [reportMetadata = CONST.DEFAULT_REPORT_METADATA] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportID}`, {canBeMissing: true}); - const isDraftTransaction = !!iouAction; + const isDraftTransaction = !!action; const transaction = isDraftTransaction ? transactionDraft : transactionMain; const receiptURIs = getThumbnailAndImageURIs(transaction); const isLocalFile = receiptURIs.isLocalFile; - const readonly = !!readonlyProp; - const isFromReviewDuplicates = !!isFromReviewDuplicatesProp; + const readonly = readonlyProp === 'true'; + const isFromReviewDuplicates = isFromReviewDuplicatesProp === 'true'; const imageSource = isDraftTransaction ? transactionDraft?.receipt?.source : tryResolveUrlFromApiRoot(receiptURIs.image ?? ''); const parentReportAction = getReportAction(report?.parentReportID, report?.parentReportActionID); @@ -77,7 +78,7 @@ function TransactionReceiptModalContent({navigation, route}: AttachmentModalScre iouType, transactionID, reportID, - ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(iouAction, iouType, transactionID, reportID), + ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(action, iouType, transactionID, reportID), ), ), ); @@ -95,32 +96,31 @@ function TransactionReceiptModalContent({navigation, route}: AttachmentModalScre ? !transaction : moneyRequestReportID !== transaction?.reportID; - const contentProps = useMemo( - () => - ({ - source: imageSource, - isAuthTokenRequired: !isLocalFile && !isDraftTransaction, - report, - isReceiptAttachment: true, - isDeleteReceiptConfirmModalVisible, - canEditReceipt: ((canEditReceipt && !readonly) || isDraftTransaction) && !transaction?.receipt?.isTestDriveReceipt, - canDeleteReceipt: canDeleteReceipt && !readonly && !isDraftTransaction && !transaction?.receipt?.isTestDriveReceipt, - allowDownload: !isEReceipt, - isTrackExpenseAction: isTrackExpenseActionValue, - originalFileName: isDraftTransaction ? transaction?.filename : receiptURIs?.filename, - isLoading: !transaction && reportMetadata?.isLoadingInitialReportActions, - iouAction, - iouType, - draftTransactionID: isDraftTransaction ? transactionID : undefined, - shouldShowNotFoundPage, - onRequestDeleteReceipt: () => setIsDeleteReceiptConfirmModalVisible?.(true), - onDeleteReceipt: () => setIsDeleteReceiptConfirmModalVisible?.(false), - }) satisfies Partial, + const contentProps = useMemo( + () => ({ + source: imageSource, + isAuthTokenRequired: !isLocalFile && !isDraftTransaction, + report, + isReceiptAttachment: true, + isDeleteReceiptConfirmModalVisible, + canEditReceipt: ((canEditReceipt && !readonly) || isDraftTransaction) && !transaction?.receipt?.isTestDriveReceipt, + canDeleteReceipt: canDeleteReceipt && !readonly && !isDraftTransaction && !transaction?.receipt?.isTestDriveReceipt, + allowDownload: !isEReceipt, + isTrackExpenseAction: isTrackExpenseActionValue, + originalFileName: isDraftTransaction ? transaction?.filename : receiptURIs?.filename, + isLoading: !transaction && reportMetadata?.isLoadingInitialReportActions, + action, + iouType, + draftTransactionID: isDraftTransaction ? transactionID : undefined, + shouldShowNotFoundPage, + onRequestDeleteReceipt: () => setIsDeleteReceiptConfirmModalVisible?.(true), + onDeleteReceipt: () => setIsDeleteReceiptConfirmModalVisible?.(false), + }), [ canDeleteReceipt, canEditReceipt, imageSource, - iouAction, + action, iouType, isDeleteReceiptConfirmModalVisible, isDraftTransaction, diff --git a/src/pages/media/AttachmentModalScreen/routes/WorkspaceAvatarModalContent.tsx b/src/pages/media/AttachmentModalScreen/routes/WorkspaceAvatarModalContent.tsx index 9dde2942f45a0..0faa06da22f55 100644 --- a/src/pages/media/AttachmentModalScreen/routes/WorkspaceAvatarModalContent.tsx +++ b/src/pages/media/AttachmentModalScreen/routes/WorkspaceAvatarModalContent.tsx @@ -2,12 +2,13 @@ import React, {useMemo} from 'react'; import useOnyx from '@hooks/useOnyx'; import {getDefaultWorkspaceAvatar} from '@libs/ReportUtils'; import {getFullSizeAvatar} from '@libs/UserUtils'; -import type {AttachmentModalBaseContentProps} from '@pages/media/AttachmentModalScreen/AttachmentModalBaseContent'; +import type {AttachmentModalBaseContentProps} from '@pages/media/AttachmentModalScreen/AttachmentModalBaseContent/types'; import AttachmentModalContainer from '@pages/media/AttachmentModalScreen/AttachmentModalContainer'; import type {AttachmentModalScreenProps} from '@pages/media/AttachmentModalScreen/types'; import ONYXKEYS from '@src/ONYXKEYS'; +import type SCREENS from '@src/SCREENS'; -function WorkspaceAvatarModalContent({navigation, route}: AttachmentModalScreenProps) { +function WorkspaceAvatarModalContent({navigation, route}: AttachmentModalScreenProps) { const {policyID} = route.params; const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {canBeMissing: false}); @@ -15,17 +16,16 @@ function WorkspaceAvatarModalContent({navigation, route}: AttachmentModalScreenP const avatarURL = policy?.avatarURL ?? getDefaultWorkspaceAvatar(policy?.name ?? ''); - const contentProps = useMemo( - () => - ({ - source: getFullSizeAvatar(avatarURL, 0), - headerTitle: policy?.name, - isWorkspaceAvatar: true, - originalFileName: policy?.originalFileName ?? policy?.id, - shouldShowNotFoundPage: !Object.keys(policy ?? {}).length && !isLoadingApp, - isLoading: !Object.keys(policy ?? {}).length && !!isLoadingApp, - maybeIcon: true, - }) satisfies Partial, + const contentProps = useMemo( + () => ({ + source: getFullSizeAvatar(avatarURL, 0), + headerTitle: policy?.name, + isWorkspaceAvatar: true, + originalFileName: policy?.originalFileName ?? policy?.id, + shouldShowNotFoundPage: !Object.keys(policy ?? {}).length && !isLoadingApp, + isLoading: !Object.keys(policy ?? {}).length && !!isLoadingApp, + maybeIcon: true, + }), [avatarURL, isLoadingApp, policy], ); diff --git a/src/pages/media/AttachmentModalScreen/routes/types.ts b/src/pages/media/AttachmentModalScreen/routes/types.ts new file mode 100644 index 0000000000000..d56524e11f07b --- /dev/null +++ b/src/pages/media/AttachmentModalScreen/routes/types.ts @@ -0,0 +1,7 @@ +import type {AttachmentModalScreensParamList} from '@libs/Navigation/types'; +import type {AttachmentModalScreenBaseParams, AttachmentModalScreenType} from '@pages/media/AttachmentModalScreen/types'; +import type Modify from '@src/types/utils/Modify'; + +type AttachmentModalScreenParams = Modify; + +export default AttachmentModalScreenParams; diff --git a/src/pages/media/AttachmentModalScreen/types.ts b/src/pages/media/AttachmentModalScreen/types.ts index 960595c1e69e6..7d79bc40c22a8 100644 --- a/src/pages/media/AttachmentModalScreen/types.ts +++ b/src/pages/media/AttachmentModalScreen/types.ts @@ -1,8 +1,9 @@ +import type {TupleToUnion} from 'type-fest'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {RootNavigatorParamList} from '@libs/Navigation/types'; -import type SCREENS from '@src/SCREENS'; +import SCREENS from '@src/SCREENS'; import type ModalType from '@src/types/utils/ModalType'; -import type {AttachmentModalBaseContentProps} from './AttachmentModalBaseContent'; +import type {AttachmentModalBaseContentProps} from './AttachmentModalBaseContent/types'; /** * Modal render prop component that exposes modal launching triggers that can be used @@ -20,24 +21,25 @@ type ImagePickerResponse = { type FileObject = Partial; -type AttachmentModalModalProps = { +type AttachmentModalContainerModalProps = { modalType?: ModalType; onShow?: () => void; onClose?: () => void; shouldHandleNavigationBack?: boolean; }; -type AttachmentModalScreenParams = AttachmentModalBaseContentProps & - AttachmentModalModalProps & { - file?: FileObject; - reportID?: string; - policyID?: string; - transactionID?: string; - readonly?: boolean; - isFromReviewDuplicates?: boolean; - hashKey?: number; - }; +const ATTACHMENT_MODAL_SCREENS = [ + SCREENS.ATTACHMENTS, + SCREENS.REPORT_AVATAR, + SCREENS.PROFILE_AVATAR, + SCREENS.WORKSPACE_AVATAR, + SCREENS.TRANSACTION_RECEIPT, + SCREENS.MONEY_REQUEST.RECEIPT_PREVIEW, +]; +type AttachmentModalScreenType = TupleToUnion; -type AttachmentModalScreenProps = PlatformStackScreenProps; +type AttachmentModalScreenBaseParams = AttachmentModalBaseContentProps & AttachmentModalContainerModalProps; -export type {AttachmentModalScreenParams, AttachmentModalModalProps, AttachmentModalScreenProps, FileObject, ImagePickerResponse}; +type AttachmentModalScreenProps = PlatformStackScreenProps; + +export type {AttachmentModalScreenType, AttachmentModalScreenBaseParams, AttachmentModalContainerModalProps, AttachmentModalScreenProps, FileObject, ImagePickerResponse}; diff --git a/src/types/utils/Modify.ts b/src/types/utils/Modify.ts new file mode 100644 index 0000000000000..d1a30faab616a --- /dev/null +++ b/src/types/utils/Modify.ts @@ -0,0 +1,6 @@ +/** + * Merges A with B, while properties from B override ones from A. + */ +type Modify = Omit & B; + +export default Modify;