diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 4d977ea8e219..89a98992c5eb 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -248,7 +248,12 @@ const ROUTES = { }, REPORT_AVATAR: { route: 'r/:reportID/avatar', - getRoute: (reportID: string) => `r/${reportID}/avatar` as const, + getRoute: (reportID: string, isNewGroupChat?: boolean) => { + if (isNewGroupChat) { + return `r/${reportID}/avatar?isNewGroupChat=${isNewGroupChat}` as const; + } + return `r/${reportID}/avatar` as const; + }, }, EDIT_CURRENCY_REQUEST: { route: 'r/:threadReportID/edit/currency', diff --git a/src/components/AvatarWithImagePicker.tsx b/src/components/AvatarWithImagePicker.tsx index 15a004ba9b87..ffd95bd6a680 100644 --- a/src/components/AvatarWithImagePicker.tsx +++ b/src/components/AvatarWithImagePicker.tsx @@ -121,9 +121,6 @@ type AvatarWithImagePickerProps = { /** Allows to open an image without Attachment Picker. */ enablePreview?: boolean; - /** Hard disables the "View photo" option */ - shouldDisableViewPhoto?: boolean; - /** Optionally override the default "Edit" icon */ editIcon?: IconAsset; @@ -157,7 +154,6 @@ function AvatarWithImagePicker({ disabled = false, onViewPhotoPress, enablePreview = false, - shouldDisableViewPhoto = false, editIcon = Expensicons.Pencil, shouldUseStyleUtilityForAnchorPosition = false, }: AvatarWithImagePickerProps) { @@ -370,7 +366,7 @@ function AvatarWithImagePicker({ const menuItems = createMenuItems(openPicker); // If the current avatar isn't a default avatar and we are not overriding this behavior allow the "View Photo" option - if (!shouldDisableViewPhoto && !isUsingDefaultAvatar) { + if (!isUsingDefaultAvatar) { menuItems.push({ icon: Expensicons.Eye, text: translate('avatarWithImagePicker.viewPhoto'), diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index 386bbe51d2d9..f2d25ede9534 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -22,7 +22,12 @@ const config: LinkingOptions['config'] = { [SCREENS.ATTACHMENTS]: ROUTES.ATTACHMENTS.route, [SCREENS.PROFILE_AVATAR]: ROUTES.PROFILE_AVATAR.route, [SCREENS.WORKSPACE_AVATAR]: ROUTES.WORKSPACE_AVATAR.route, - [SCREENS.REPORT_AVATAR]: ROUTES.REPORT_AVATAR.route, + [SCREENS.REPORT_AVATAR]: { + path: ROUTES.REPORT_AVATAR.route, + parse: { + isNewGroupChat: (isNewGroupChat: string) => isNewGroupChat === 'true', + }, + }, [SCREENS.TRANSACTION_RECEIPT]: ROUTES.TRANSACTION_RECEIPT.route, [SCREENS.WORKSPACE_JOIN_USER]: ROUTES.WORKSPACE_JOIN_USER.route, [SCREENS.REPORT]: ROUTES.REPORT_WITH_ID.route, diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index c31bf6dcddc9..cb2247f817b4 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -1230,6 +1230,7 @@ type AuthScreensParamList = CentralPaneScreensParamList & }; [SCREENS.REPORT_AVATAR]: { reportID: string; + isNewGroupChat: boolean; }; [SCREENS.NOT_FOUND]: undefined; [NAVIGATORS.LEFT_MODAL_NAVIGATOR]: NavigatorScreenParams; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index ace8f547b75c..62197840378b 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -277,6 +277,7 @@ type OptimisticChatReport = Pick< | 'description' | 'writeCapability' | 'avatarUrl' + | 'avatarFileName' | 'invoiceReceiver' | 'isHidden' > & { @@ -4609,6 +4610,7 @@ function buildOptimisticChatReport( parentReportID = '', description = '', avatarUrl = '', + avatarFileName = '', optimisticReportID = '', shouldShowParticipants = true, ): OptimisticChatReport { @@ -4650,6 +4652,7 @@ function buildOptimisticChatReport( description, writeCapability, avatarUrl, + avatarFileName, }; if (chatType === CONST.REPORT.CHAT_TYPE.INVOICE) { @@ -4667,6 +4670,7 @@ function buildOptimisticGroupChatReport( participantAccountIDs: number[], reportName: string, avatarUri: string, + avatarFileName: string, optimisticReportID?: string, notificationPreference?: NotificationPreference, ) { @@ -4685,6 +4689,7 @@ function buildOptimisticGroupChatReport( undefined, undefined, avatarUri, + avatarFileName, optimisticReportID, ); } @@ -5064,6 +5069,7 @@ function buildOptimisticWorkspaceChats(policyID: string, policyName: string, exp undefined, undefined, undefined, + undefined, expenseReportId, ); const expenseChatReportID = expenseChatData.reportID; @@ -5177,6 +5183,7 @@ function buildTransactionThread( '', '', '', + '', false, ); } diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 035d3c13d137..0dec5d4a91e6 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -676,6 +676,7 @@ function updateGroupChatAvatar(reportID: string, file?: File | CustomRNImageMani key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, value: { avatarUrl: file?.uri ?? '', + avatarFileName: file?.name ?? '', pendingFields: { avatar: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, }, @@ -686,12 +687,15 @@ function updateGroupChatAvatar(reportID: string, file?: File | CustomRNImageMani }, ]; + const fetchedReport = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; + const failureData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, value: { - avatarUrl: ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]?.avatarUrl ?? null, + avatarFileName: fetchedReport?.avatarFileName ?? null, + avatarUrl: fetchedReport?.avatarUrl ?? null, pendingFields: { avatar: null, }, @@ -989,7 +993,14 @@ function navigateToAndOpenReport( if (isEmptyObject(chat)) { if (isGroupChat) { // If we are creating a group chat then participantAccountIDs is expected to contain currentUserAccountID - newChat = ReportUtils.buildOptimisticGroupChatReport(participantAccountIDs, reportName ?? '', avatarUri ?? '', optimisticReportID, CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN); + newChat = ReportUtils.buildOptimisticGroupChatReport( + participantAccountIDs, + reportName ?? '', + avatarUri ?? '', + avatarFile?.name ?? '', + optimisticReportID, + CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN, + ); } else { newChat = ReportUtils.buildOptimisticChatReport( [...participantAccountIDs, currentUserAccountID], diff --git a/src/pages/NewChatConfirmPage.tsx b/src/pages/NewChatConfirmPage.tsx index f72850c8f7bd..84381d2020bc 100644 --- a/src/pages/NewChatConfirmPage.tsx +++ b/src/pages/NewChatConfirmPage.tsx @@ -152,9 +152,9 @@ function NewChatConfirmPage({newGroupDraft, allPersonalDetails}: NewChatConfirmP }} size={CONST.AVATAR_SIZE.XLARGE} avatarStyle={styles.avatarXLarge} - shouldDisableViewPhoto editIcon={Expensicons.Camera} editIconStyle={styles.smallEditIconAccount} + onViewPhotoPress={() => Navigation.navigate(ROUTES.REPORT_AVATAR.getRoute(optimisticReportID.current, true))} shouldUseStyleUtilityForAnchorPosition style={styles.w100} /> diff --git a/src/pages/ReportAvatar.tsx b/src/pages/ReportAvatar.tsx index 5ec34e3b71dc..0da0f22863cd 100644 --- a/src/pages/ReportAvatar.tsx +++ b/src/pages/ReportAvatar.tsx @@ -1,7 +1,7 @@ import type {StackScreenProps} from '@react-navigation/stack'; import React from 'react'; import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; -import {withOnyx} from 'react-native-onyx'; +import {useOnyx, withOnyx} from 'react-native-onyx'; import AttachmentModal from '@components/AttachmentModal'; import Navigation from '@libs/Navigation/Navigation'; import type {AuthScreensParamList} from '@libs/Navigation/types'; @@ -20,14 +20,37 @@ type ReportAvatarOnyxProps = { type ReportAvatarProps = ReportAvatarOnyxProps & StackScreenProps; -function ReportAvatar({report = {} as Report, policies, isLoadingApp = true}: ReportAvatarProps) { +function ReportAvatar({report = {} as Report, policies, isLoadingApp = true, route}: ReportAvatarProps) { const policy = policies?.[`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID ?? '-1'}`]; - const policyName = ReportUtils.getPolicyName(report, false, policy); - const avatarURL = ReportUtils.getWorkspaceAvatar(report); + let title; + let avatarURL; + let fileName; + // eslint-disable-next-line rulesdir/no-negated-variables + let shouldShowNotFoundPage; + + const shouldUseGroupChatDraft = !!route.params.isNewGroupChat; + + const [groupChatDraft] = useOnyx(ONYXKEYS.NEW_GROUP_CHAT_DRAFT, {initWithStoredValues: shouldUseGroupChatDraft}); + + if (shouldUseGroupChatDraft) { + avatarURL = groupChatDraft?.avatarUri ?? undefined; + fileName = groupChatDraft?.avatarFileName ?? undefined; + // When user enters custom group name, it typically stored in groupChatDraft.reportName + // If that is null then we will use ReportUtils.getGroupChatName to get the name + /* eslint-disable @typescript-eslint/prefer-nullish-coalescing */ + title = groupChatDraft?.reportName || ReportUtils.getGroupChatName(groupChatDraft?.participants.map((participant) => participant.accountID) ?? [], true); + shouldShowNotFoundPage = !isLoadingApp && !groupChatDraft; + } else { + avatarURL = policy ? ReportUtils.getWorkspaceAvatar(report) : report?.avatarUrl; + // In the case of default workspace avatar, originalFileName prop takes policyID as value to get the color of the avatar + fileName = policy ? policy?.originalFileName ?? policy?.id ?? report?.policyID : report?.avatarFileName; + title = policy ? ReportUtils.getPolicyName(report, false, policy) : ReportUtils.getReportName(report); + shouldShowNotFoundPage = !isLoadingApp && !report?.reportID; + } return ( { @@ -35,9 +58,8 @@ function ReportAvatar({report = {} as Report, policies, isLoadingApp = true}: Re }} isWorkspaceAvatar maybeIcon - // In the case of default workspace avatar, originalFileName prop takes policyID as value to get the color of the avatar - originalFileName={policy?.originalFileName ?? policy?.id ?? report?.policyID} - shouldShowNotFoundPage={!report?.reportID && !isLoadingApp} + originalFileName={fileName} + shouldShowNotFoundPage={shouldShowNotFoundPage} isLoading={(!report?.reportID || !policy?.id) && !!isLoadingApp} /> ); diff --git a/src/pages/ReportDetailsPage.tsx b/src/pages/ReportDetailsPage.tsx index 284004bc29ca..828965b4e188 100644 --- a/src/pages/ReportDetailsPage.tsx +++ b/src/pages/ReportDetailsPage.tsx @@ -487,7 +487,6 @@ function ReportDetailsPage({policies, report, session, personalDetails}: ReportD isUsingDefaultAvatar={!report.avatarUrl} size={CONST.AVATAR_SIZE.XLARGE} avatarStyle={styles.avatarXLarge} - shouldDisableViewPhoto onImageRemoved={() => { // Calling this without a file will remove the avatar Report.updateGroupChatAvatar(report.reportID ?? ''); @@ -501,6 +500,7 @@ function ReportDetailsPage({policies, report, session, personalDetails}: ReportD onErrorClose={() => Report.clearAvatarErrors(report.reportID ?? '-1')} shouldUseStyleUtilityForAnchorPosition style={[styles.w100, styles.mb3]} + onViewPhotoPress={() => Navigation.navigate(ROUTES.REPORT_AVATAR.getRoute(report.reportID))} /> ); } diff --git a/src/types/onyx/Report.ts b/src/types/onyx/Report.ts index b5d0a0a15d13..9f468044e98f 100644 --- a/src/types/onyx/Report.ts +++ b/src/types/onyx/Report.ts @@ -71,6 +71,11 @@ type Report = OnyxCommon.OnyxValueWithOfflineFeedback< /** The URL of the Group Chat report custom avatar */ avatarUrl?: string; + /** The file name of the Group Chat report custom avatar + * This field is not coming from backend, so it's for client side only + */ + avatarFileName?: string; + /** The specific type of chat */ chatType?: ValueOf;