Skip to content

Commit

Permalink
Merge pull request #41586 from nexarvo/feat/39850
Browse files Browse the repository at this point in the history
Add support for viewing full screen Group Chat custom avatars
  • Loading branch information
marcaaron authored Jul 19, 2024
2 parents edf8ef0 + 1c3e7db commit 78b7ee9
Show file tree
Hide file tree
Showing 10 changed files with 71 additions and 19 deletions.
7 changes: 6 additions & 1 deletion src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,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',
Expand Down
6 changes: 1 addition & 5 deletions src/components/AvatarWithImagePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -157,7 +154,6 @@ function AvatarWithImagePicker({
disabled = false,
onViewPhotoPress,
enablePreview = false,
shouldDisableViewPhoto = false,
editIcon = Expensicons.Pencil,
shouldUseStyleUtilityForAnchorPosition = false,
}: AvatarWithImagePickerProps) {
Expand Down Expand Up @@ -338,7 +334,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'),
Expand Down
7 changes: 6 additions & 1 deletion src/libs/Navigation/linkingConfig/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,12 @@ const config: LinkingOptions<RootStackParamList>['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,
Expand Down
1 change: 1 addition & 0 deletions src/libs/Navigation/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1228,6 +1228,7 @@ type AuthScreensParamList = CentralPaneScreensParamList &
};
[SCREENS.REPORT_AVATAR]: {
reportID: string;
isNewGroupChat: boolean;
};
[SCREENS.NOT_FOUND]: undefined;
[NAVIGATORS.LEFT_MODAL_NAVIGATOR]: NavigatorScreenParams<LeftModalNavigatorParamList>;
Expand Down
7 changes: 7 additions & 0 deletions src/libs/ReportUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,7 @@ type OptimisticChatReport = Pick<
| 'description'
| 'writeCapability'
| 'avatarUrl'
| 'avatarFileName'
| 'invoiceReceiver'
| 'isHidden'
> & {
Expand Down Expand Up @@ -4606,6 +4607,7 @@ function buildOptimisticChatReport(
parentReportID = '',
description = '',
avatarUrl = '',
avatarFileName = '',
optimisticReportID = '',
shouldShowParticipants = true,
): OptimisticChatReport {
Expand Down Expand Up @@ -4647,6 +4649,7 @@ function buildOptimisticChatReport(
description,
writeCapability,
avatarUrl,
avatarFileName,
};

if (chatType === CONST.REPORT.CHAT_TYPE.INVOICE) {
Expand All @@ -4664,6 +4667,7 @@ function buildOptimisticGroupChatReport(
participantAccountIDs: number[],
reportName: string,
avatarUri: string,
avatarFileName: string,
optimisticReportID?: string,
notificationPreference?: NotificationPreference,
) {
Expand All @@ -4682,6 +4686,7 @@ function buildOptimisticGroupChatReport(
undefined,
undefined,
avatarUri,
avatarFileName,
optimisticReportID,
);
}
Expand Down Expand Up @@ -5061,6 +5066,7 @@ function buildOptimisticWorkspaceChats(policyID: string, policyName: string, exp
undefined,
undefined,
undefined,
undefined,
expenseReportId,
);
const expenseChatReportID = expenseChatData.reportID;
Expand Down Expand Up @@ -5174,6 +5180,7 @@ function buildTransactionThread(
'',
'',
'',
'',
false,
);
}
Expand Down
15 changes: 13 additions & 2 deletions src/libs/actions/Report.ts
Original file line number Diff line number Diff line change
Expand Up @@ -680,6 +680,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,
},
Expand All @@ -690,12 +691,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,
},
Expand Down Expand Up @@ -993,7 +997,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],
Expand Down
2 changes: 1 addition & 1 deletion src/pages/NewChatConfirmPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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}
/>
Expand Down
38 changes: 30 additions & 8 deletions src/pages/ReportAvatar.tsx
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -20,24 +20,46 @@ type ReportAvatarOnyxProps = {

type ReportAvatarProps = ReportAvatarOnyxProps & StackScreenProps<AuthScreensParamList, typeof SCREENS.REPORT_AVATAR>;

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 (
<AttachmentModal
headerTitle={policyName}
headerTitle={title}
defaultOpen
source={UserUtils.getFullSizeAvatar(avatarURL, 0)}
onModalClose={() => {
Navigation.goBack(ROUTES.REPORT_WITH_ID_DETAILS.getRoute(report?.reportID ?? '-1'));
}}
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}
/>
);
Expand Down
2 changes: 1 addition & 1 deletion src/pages/ReportDetailsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,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 ?? '');
Expand All @@ -502,6 +501,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))}
/>
);
}
Expand Down
5 changes: 5 additions & 0 deletions src/types/onyx/Report.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<typeof CONST.REPORT.CHAT_TYPE>;

Expand Down

0 comments on commit 78b7ee9

Please sign in to comment.