Skip to content

Commit

Permalink
re-structure
Browse files Browse the repository at this point in the history
  • Loading branch information
chrispader committed Mar 7, 2025
1 parent 4fd759e commit 7c4a9a6
Show file tree
Hide file tree
Showing 18 changed files with 245 additions and 215 deletions.
55 changes: 30 additions & 25 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ 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 {ReportsSplitNavigatorParamList} from './libs/Navigation/types';
import type {ReimbursementAccountStepToOpen} from './libs/ReimbursementAccountUtils';
import type SCREENS from './SCREENS';
import type {ExitReason} from './types/form/ExitSurveyReasonForm';
import type {ConnectionName, SageIntacctMappingName} from './types/onyx/Policy';
import type AssertTypesNotEqual from './types/utils/AssertTypesNotEqual';
Expand All @@ -19,6 +21,25 @@ function getUrlWithBackToParam<TUrl extends string>(url: TUrl, backTo?: string,
return `${url}${backToParam}` as `${TUrl}`;
}

type AttachmentRouteParams = ReportsSplitNavigatorParamList[typeof SCREENS.ATTACHMENTS];
function getAttachmentRoute(url: string, params?: AttachmentRouteParams) {
if (!params?.source) {
return url;
}

const {source, type, reportID, accountID, isAuthTokenRequired, fileName, attachmentLink} = params;

const sourceParam = `?source=${encodeURIComponent(source)}`;
const typeParam = type ? `&type=${type as string}` : '';
const reportIDParam = reportID ? `&reportID=${reportID}` : '';
const accountIDParam = accountID ? `&accountID=${accountID}` : '';
const authTokenParam = isAuthTokenRequired ? '&isAuthTokenRequired=true' : '';
const fileNameParam = fileName ? `&fileName=${fileName}` : '';
const attachmentLinkParam = attachmentLink ? `&attachmentLink=${attachmentLink}` : '';

return `${url}${sourceParam}${typeParam}${reportIDParam}${accountIDParam}${authTokenParam}${fileNameParam}${attachmentLinkParam}` as const;
}

const PUBLIC_SCREENS_ROUTES = {
// If the user opens this route, we'll redirect them to the path saved in the last visited path or to the home page if the last visited path is empty.
ROOT: '',
Expand Down Expand Up @@ -326,6 +347,14 @@ const ROUTES = {
return `${baseRoute}${referrerParam}` as const;
},
},
REPORT_WITH_ID_ADD_ATTACHMENT: {
route: 'r/:reportID/attachment/add',
getRoute: (reportID: string, params?: AttachmentRouteParams) => {
// eslint-disable-next-line @typescript-eslint/naming-convention
const {reportID: _reportIDParam, ...restParams} = params ?? {};
return getAttachmentRoute(`r/${reportID}/attachment/add`, restParams);
},
},
REPORT_AVATAR: {
route: 'r/:reportID/avatar',
getRoute: (reportID: string, policyID?: string) => {
Expand Down Expand Up @@ -362,31 +391,7 @@ const ROUTES = {
},
ATTACHMENTS: {
route: 'attachment',
getRoute: (params?: {
source?: string;
type?: ValueOf<typeof CONST.ATTACHMENT_TYPE>;
reportID?: string | number;
accountID?: number;
isAuthTokenRequired?: boolean;
fileName?: string;
attachmentLink?: string;
}) => {
if (!params?.source) {
return `attachment`;
}

const {source, type, reportID, accountID, isAuthTokenRequired, fileName, attachmentLink} = params;

const sourceParam = `?source=${encodeURIComponent(source)}`;
const typeParam = type ? `&type=${type as string}` : '';
const reportIDParam = reportID ? `&reportID=${reportID}` : '';
const accountIDParam = accountID ? `&accountID=${accountID}` : '';
const authTokenParam = isAuthTokenRequired ? '&isAuthTokenRequired=true' : '';
const fileNameParam = fileName ? `&fileName=${fileName}` : '';
const attachmentLinkParam = attachmentLink ? `&attachmentLink=${attachmentLink}` : '';

return `attachment${sourceParam}${typeParam}${reportIDParam}${accountIDParam}${authTokenParam}${fileNameParam}${attachmentLinkParam}` as const;
},
getRoute: (params?: AttachmentRouteParams) => getAttachmentRoute('attachment', params),
},
REPORT_PARTICIPANTS: {
route: 'r/:reportID/participants',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import type ReactComponentModule from '@src/types/utils/ReactComponentModule';

const loadReportScreen = () => require<ReactComponentModule>('@pages/home/ReportScreen').default;
const loadSidebarScreen = () => require<ReactComponentModule>('@pages/home/sidebar/BaseSidebarScreen').default;

const loadAttachmentScreen = () => require<ReactComponentModule>('@pages/media/AttachmentModalScreen').default;
const Split = createSplitNavigator<ReportsSplitNavigatorParamList>();

/**
Expand Down Expand Up @@ -59,6 +59,11 @@ function ReportsSplitNavigator({route}: PlatformStackScreenProps<AuthScreensPara
initialParams={{reportID: initialReportID, openOnAdminRoom: shouldOpenOnAdminRoom() ? true : undefined}}
getComponent={loadReportScreen}
/>
<Split.Screen
name={SCREENS.ATTACHMENTS}
initialParams={{reportID: initialReportID}}
getComponent={loadAttachmentScreen}
/>
</Split.Navigator>
</FreezeWrapper>
);
Expand Down
1 change: 1 addition & 0 deletions src/libs/Navigation/linkingConfig/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1536,6 +1536,7 @@ const config: LinkingOptions<RootNavigatorParamList>['config'] = {
exact: true,
},
[SCREENS.REPORT]: ROUTES.REPORT_WITH_ID.route,
[SCREENS.ATTACHMENTS]: ROUTES.REPORT_WITH_ID_ADD_ATTACHMENT.route,
},
},

Expand Down
9 changes: 9 additions & 0 deletions src/libs/Navigation/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1592,6 +1592,15 @@ type ReportsSplitNavigatorParamList = {
openOnAdminRoom?: boolean;
referrer?: string;
};
[SCREENS.ATTACHMENTS]: {
source?: string;
type?: ValueOf<typeof CONST.ATTACHMENT_TYPE>;
reportID?: string | number;
accountID?: number;
isAuthTokenRequired?: boolean;
fileName?: string;
attachmentLink?: string;
};
};

type SettingsSplitNavigatorParamList = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -429,13 +429,13 @@ function ReportActionCompose({
file,
headerTitle: translate('reportActionCompose.sendAttachment'),
onConfirm: addAttachment,
onModalShow: () => setIsAttachmentPreviewActive(true),
onModalHide: onAttachmentPreviewClose,
onShow: () => setIsAttachmentPreviewActive(true),
onClose: onAttachmentPreviewClose,
shouldDisableSendButton: !!exceededMaxLength,
});
Navigation.navigate(ROUTES.ATTACHMENTS.getRoute());
Navigation.navigate(ROUTES.REPORT_WITH_ID_ADD_ATTACHMENT.getRoute(reportID));
},
[addAttachment, exceededMaxLength, onAttachmentPreviewClose, reportAttachmentsContext, translate],
[addAttachment, exceededMaxLength, onAttachmentPreviewClose, reportAttachmentsContext, reportID, translate],
);

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ import type {FileObject} from './types';
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;
/** 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 */
Expand All @@ -72,8 +76,6 @@ type AttachmentModalBaseContentProps = {
maybeIcon?: boolean;
/** Whether it is a receipt attachment or not */
isReceiptAttachment?: boolean;
/** Fallback source (URL, SVG function) for the image shown. */
fallbackSource?: AvatarSource;
/** Determines if the user can edit the receipt or not */
canEditReceipt?: boolean;
/** Determines if the user can delete the receipt or not */
Expand All @@ -82,22 +84,20 @@ type AttachmentModalBaseContentProps = {
shouldDisableSendButton?: boolean;
/** The link of the attachment */
attachmentLink?: string;
/** Optional callback to fire when we want to preview an image and approve it for use. */
onConfirm?: ((file: FileObject) => void) | null;
/** Fallback route when the modal is closed */
fallbackRoute?: Route;
/** Determines if the attachment is invalid or not */
isAttachmentInvalid?: boolean;
/** Determines if the attachment is invalid or not */
shouldLoadAttachment?: 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<View | HTMLElement>;
/** 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?: (shouldCallDirectly?: boolean) => void;
/** Callback triggered when the confirm modal is closed */
Expand All @@ -112,15 +112,18 @@ type AttachmentModalBaseContentProps = {
onInvalidReasonModalHide?: () => 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?: (file: FileObject | undefined, setFile: (file: FileObject | undefined) => void) => void;
};

function AttachmentModalBaseContent({
source = '',
fallbackSource,
file: fileProp,
originalFileName = '',
isAuthTokenRequired = false,
maybeIcon = false,
headerTitle: headerTitleProp,
fallbackSource,
type,
accountID,
attachmentLink = '',
Expand All @@ -137,7 +140,6 @@ function AttachmentModalBaseContent({
fallbackRoute,
isDeleteReceiptConfirmModalVisible = false,
isAttachmentInvalid = false,
shouldLoadAttachment = true,
attachmentInvalidReason,
attachmentInvalidReasonTitle,
submitRef,
Expand All @@ -149,6 +151,7 @@ function AttachmentModalBaseContent({
onPdfLoadError,
onInvalidReasonModalHide,
onCarouselAttachmentChange = () => {},
onValidateFile,
}: AttachmentModalBaseContentProps) {
const styles = useThemeStyles();

Expand All @@ -167,13 +170,20 @@ function AttachmentModalBaseContent({
const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`);
const [currentAttachmentLink, setCurrentAttachmentLink] = useState(attachmentLink);

const [file, setFile] = useState<FileObject | undefined>(
originalFileName
? {
name: originalFileName,
}
: undefined,
);
const fallbackFile = useMemo(() => (originalFileName ? {name: originalFileName} : undefined), [originalFileName]);
const [file, setFile] = useState<FileObject | undefined>(() => fileProp ?? fallbackFile);
useEffect(() => {
if (!fileProp) {
return;
}

if (onValidateFile) {
onValidateFile?.(fileProp, setFile);
} else {
setFile(fileProp ?? fallbackFile);
}
}, [fileProp, fallbackFile, onValidateFile]);

const {translate} = useLocalize();
const {isOffline} = useNetwork();

Expand Down Expand Up @@ -393,7 +403,6 @@ function AttachmentModalBaseContent({
/>
) : (
!!sourceForAttachmentView &&
shouldLoadAttachment &&
!isLoading && (
<AttachmentCarouselPagerContext.Provider value={context}>
<AttachmentView
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
import React, {memo, useCallback, useContext} from 'react';
import React, {memo, useCallback, useContext, useEffect} from 'react';
import ScreenWrapper from '@components/ScreenWrapper';
import Navigation from '@libs/Navigation/Navigation';
import AttachmentModalBaseContent from '@pages/media/AttachmentModalScreen/AttachmentModalBaseContent';
import AttachmentModalContext from '@pages/media/AttachmentModalScreen/AttachmentModalContext';
import type AttachmentModalContainerProps from './types';

function AttachmentModalContainer({contentProps, navigation}: AttachmentModalContainerProps) {
function AttachmentModalContainer({contentProps, navigation, onShow, onClose}: AttachmentModalContainerProps) {
const attachmentsContext = useContext(AttachmentModalContext);
const testID = typeof contentProps.source === 'string' ? contentProps.source : contentProps.source?.toString() ?? '';

const closeModal = useCallback(() => {
const handleClose = useCallback(() => {
attachmentsContext.setCurrentAttachment(undefined);
Navigation.goBack(contentProps.fallbackRoute);
}, [attachmentsContext, contentProps.fallbackRoute]);
onClose?.();
}, [attachmentsContext, contentProps.fallbackRoute, onClose]);

useEffect(() => {
onShow?.();
}, [onShow]);

return (
<ScreenWrapper
Expand All @@ -22,7 +27,7 @@ function AttachmentModalContainer({contentProps, navigation}: AttachmentModalCon
<AttachmentModalBaseContent
// eslint-disable-next-line react/jsx-props-no-spreading
{...contentProps}
onClose={closeModal}
onClose={handleClose}
/>
</ScreenWrapper>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,7 @@ import AttachmentModalContext from '@pages/media/AttachmentModalScreen/Attachmen
import CONST from '@src/CONST';
import type AttachmentModalContainerProps from './types';

function AttachmentModalContainer({
contentProps,
modalType,
closeConfirmModal,
setShouldLoadAttachment,
isOverlayModalVisible,
onModalShow,
onModalClose,
onModalHide,
}: AttachmentModalContainerProps) {
function AttachmentModalContainer({contentProps, modalType, closeConfirmModal, isOverlayModalVisible, onShow, onClose}: AttachmentModalContainerProps) {
const attachmentsContext = useContext(AttachmentModalContext);

/**
Expand All @@ -28,30 +19,29 @@ function AttachmentModalContainer({
*/
const closeModal = useCallback(
(shouldCallDirectly?: boolean) => {
if (typeof onModalClose === 'function') {
if (typeof onClose === 'function') {
if (shouldCallDirectly) {
onModalClose();
onClose();
return;
}
attachmentModalHandler.handleModalClose(onModalClose);
attachmentModalHandler.handleModalClose(onClose);
}

attachmentsContext.setCurrentAttachment(undefined);
Navigation.goBack(contentProps.fallbackRoute);
},
[attachmentsContext, contentProps.fallbackRoute, onModalClose],
[attachmentsContext, contentProps.fallbackRoute, onClose],
);

useEffect(() => {
onShow?.();
}, [onShow]);

return (
<Modal
isVisible
type={modalType ?? CONST.MODAL.MODAL_TYPE.CENTERED_UNSWIPEABLE}
onClose={isOverlayModalVisible ? () => closeConfirmModal?.() : () => closeModal?.()}
onModalHide={onModalHide}
onModalShow={() => {
onModalShow?.();
setShouldLoadAttachment?.(true);
}}
propagateSwipe
initialFocus={() => {
if (!contentProps.submitRef?.current) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import type {AttachmentModalBaseContentProps} from '@pages/media/AttachmentModalScreen/AttachmentModalBaseContent';
import type {AttachmentModalScreenModalCallbacks, AttachmentModalScreenProps} from '@pages/media/AttachmentModalScreen/types';
import type {AttachmentModalScreenCallbacks, AttachmentModalScreenProps} from '@pages/media/AttachmentModalScreen/types';
import type ModalType from '@src/types/utils/ModalType';

type AttachmentModalContainerProps = AttachmentModalScreenModalCallbacks & {
type AttachmentModalContainerProps = AttachmentModalScreenCallbacks & {
navigation: AttachmentModalScreenProps['navigation'];
contentProps: Partial<AttachmentModalBaseContentProps>;
modalType?: ModalType;
Expand Down
Loading

0 comments on commit 7c4a9a6

Please sign in to comment.