Skip to content
6 changes: 6 additions & 0 deletions src/libs/ReportUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,7 @@ type OptimisticChatReport = Pick<
| 'description'
| 'writeCapability'
| 'avatarUrl'
| 'avatarFileName'
| 'invoiceReceiver'
| 'isHidden'
> & {
Expand Down Expand Up @@ -5078,6 +5079,7 @@ function buildOptimisticChatReport(
parentReportID = '',
description = '',
avatarUrl = '',
avatarFileName = '',
optimisticReportID = '',
): OptimisticChatReport {
const isWorkspaceChatType = chatType && isWorkspaceChat(chatType);
Expand Down Expand Up @@ -5118,6 +5120,7 @@ function buildOptimisticChatReport(
description,
writeCapability,
avatarUrl,
avatarFileName,
};

if (chatType === CONST.REPORT.CHAT_TYPE.INVOICE) {
Expand All @@ -5135,6 +5138,7 @@ function buildOptimisticGroupChatReport(
participantAccountIDs: number[],
reportName: string,
avatarUri: string,
avatarFilename: string,
optimisticReportID?: string,
notificationPreference?: NotificationPreference,
) {
Expand All @@ -5153,6 +5157,7 @@ function buildOptimisticGroupChatReport(
undefined,
undefined,
avatarUri,
avatarFilename,
optimisticReportID,
);
}
Expand Down Expand Up @@ -5562,6 +5567,7 @@ function buildOptimisticWorkspaceChats(policyID: string, policyName: string, exp
undefined,
undefined,
undefined,
undefined,
expenseReportId,
);
const expenseChatReportID = expenseChatData.reportID;
Expand Down
16 changes: 13 additions & 3 deletions src/libs/actions/Report.ts
Original file line number Diff line number Diff line change
Expand Up @@ -697,7 +697,8 @@ function updateGroupChatAvatar(reportID: string, file?: File | CustomRNImageMani
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`,
value: {
avatarUrl: file?.uri ?? '',
avatarUrl: file ? file?.uri ?? '' : null,
avatarFileName: file ? file?.name ?? '' : null,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
avatarFileName: file ? file?.name ?? '' : null,
avatarFileName: file?.name ?? '',

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we set avatarFileName to an empty string when file is undefined, the avatarFileName will remain an empty string. For example, when removing the avatar, the avatarFileName will stay as an empty string, but it should be set to null to effectively remove the avatarFileName.

To test this, go to the avatar picker’s popover menu and select "Remove Avatar." Then, check the Onyx value of avatarFileName. While the avatarUrl will be removed, the avatarFileName will remain an empty string.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While the avatarUrl will be removed, the avatarFileName will remain an empty string

How is that the url is being removed but the filename is not? given that both fallback to an empty string, or is it because the former is cleared from BE onyx response?

Either way, let's set both to null

pendingFields: {
avatar: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE,
},
Expand All @@ -708,12 +709,14 @@ 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,
avatarUrl: fetchedReport?.avatarUrl ?? null,
avatarFileName: fetchedReport?.avatarFileName ?? null,
pendingFields: {
avatar: null,
},
Expand Down Expand Up @@ -1011,7 +1014,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.ALWAYS);
newChat = ReportUtils.buildOptimisticGroupChatReport(
participantAccountIDs,
reportName ?? '',
avatarUri ?? '',
avatarFile?.name ?? '',
optimisticReportID,
CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS,
);
} else {
newChat = ReportUtils.buildOptimisticChatReport(
[...participantAccountIDs, currentUserAccountID],
Expand Down
64 changes: 32 additions & 32 deletions src/pages/ReportAvatar.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
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 React, {useMemo} from 'react';
import {useOnyx} from 'react-native-onyx';
import AttachmentModal from '@components/AttachmentModal';
import Navigation from '@libs/Navigation/Navigation';
import type {AuthScreensParamList} from '@libs/Navigation/types';
Expand All @@ -10,50 +9,51 @@ import * as UserUtils from '@libs/UserUtils';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type SCREENS from '@src/SCREENS';
import type {Policy, Report} from '@src/types/onyx';

type ReportAvatarOnyxProps = {
report: OnyxEntry<Report>;
isLoadingApp: OnyxEntry<boolean>;
policies: OnyxCollection<Policy>;
};
type ReportAvatarProps = StackScreenProps<AuthScreensParamList, typeof SCREENS.REPORT_AVATAR>;

type ReportAvatarProps = ReportAvatarOnyxProps & StackScreenProps<AuthScreensParamList, typeof SCREENS.REPORT_AVATAR>;
function ReportAvatar({route}: ReportAvatarProps) {
const reportIDFromRoute = route.params?.reportID ?? '-1';
const policyIDFromRoute = route.params?.policyID ?? '-1';
const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportIDFromRoute}`);
const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${policyIDFromRoute}`);
const [isLoadingApp] = useOnyx(ONYXKEYS.IS_LOADING_APP, {initialValue: true});

function ReportAvatar({report = {} as Report, route, policies, isLoadingApp = true}: ReportAvatarProps) {
const policyID = route.params.policyID ?? '-1';
const policy = policies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`];
const policyName = ReportUtils.getPolicyName(report, false, policy);
const avatarURL = ReportUtils.getWorkspaceIcon(report).source;
const attachment = useMemo(() => {
if (ReportUtils.isGroupChat(report) && !ReportUtils.isThread(report)) {
return {
source: report?.avatarUrl ? UserUtils.getFullSizeAvatar(report.avatarUrl, 0) : ReportUtils.getDefaultGroupAvatar(report?.reportID ?? ''),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

report?.avatarUrl should be enough as you are nut supposed to be able to access this page if report does not have a custom avatar

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can still access it using a URL, for example: dev.new.expensify.com:8082/r/12345678/avatar.

headerTitle: ReportUtils.getReportName(report),
originalFileName: report?.avatarFileName ?? '',
isWorkspaceAvatar: false,
};
}

return {
source: UserUtils.getFullSizeAvatar(ReportUtils.getWorkspaceIcon(report).source, 0),
headerTitle: ReportUtils.getPolicyName(report, false, policy),
// 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 ?? '',
isWorkspaceAvatar: true,
};
}, [report, policy]);

return (
<AttachmentModal
headerTitle={policyName}
headerTitle={attachment.headerTitle}
defaultOpen
source={UserUtils.getFullSizeAvatar(avatarURL, 0)}
source={attachment.source}
onModalClose={() => {
Navigation.goBack(ROUTES.REPORT_WITH_ID_DETAILS.getRoute(report?.reportID ?? '-1'));
}}
isWorkspaceAvatar
isWorkspaceAvatar={attachment.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}
originalFileName={attachment.originalFileName}
shouldShowNotFoundPage={!report?.reportID && !isLoadingApp}
isLoading={(!report?.reportID || !policy?.id) && !!isLoadingApp}
/>
);
}

ReportAvatar.displayName = 'ReportAvatar';

export default withOnyx<ReportAvatarProps, ReportAvatarOnyxProps>({
report: {
key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${route.params.reportID ?? '-1'}`,
},
isLoadingApp: {
key: ONYXKEYS.IS_LOADING_APP,
},
policies: {
key: ONYXKEYS.COLLECTION.POLICY,
},
})(ReportAvatar);
export default ReportAvatar;
2 changes: 1 addition & 1 deletion src/pages/ReportDetailsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -506,7 +506,7 @@ function ReportDetailsPage({policies, report, session, personalDetails}: ReportD
isUsingDefaultAvatar={!report.avatarUrl}
size={CONST.AVATAR_SIZE.XLARGE}
avatarStyle={styles.avatarXLarge}
shouldDisableViewPhoto
onViewPhotoPress={() => Navigation.navigate(ROUTES.REPORT_AVATAR.getRoute(report.reportID ?? '-1'))}
onImageRemoved={() => {
// Calling this without a file will remove the avatar
Report.updateGroupChatAvatar(report.reportID ?? '');
Expand Down
2 changes: 2 additions & 0 deletions src/pages/home/ReportScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ function ReportScreen({route, currentReportID = '', navigation}: ReportScreenPro
isOptimisticReport: reportOnyx?.isOptimisticReport,
lastMentionedTime: reportOnyx?.lastMentionedTime,
avatarUrl: reportOnyx?.avatarUrl,
avatarFileName: reportOnyx?.avatarFileName,
permissions,
invoiceReceiver: reportOnyx?.invoiceReceiver,
policyAvatar: reportOnyx?.policyAvatar,
Expand Down Expand Up @@ -248,6 +249,7 @@ function ReportScreen({route, currentReportID = '', navigation}: ReportScreenPro
reportOnyx?.isOptimisticReport,
reportOnyx?.lastMentionedTime,
reportOnyx?.avatarUrl,
reportOnyx?.avatarFileName,
permissions,
reportOnyx?.invoiceReceiver,
reportOnyx?.policyAvatar,
Expand Down
3 changes: 3 additions & 0 deletions src/types/onyx/Report.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ type Report = OnyxCommon.OnyxValueWithOfflineFeedback<
/** The URL of the Group Chat report custom avatar */
avatarUrl?: string;

/** The filename of the avatar */
avatarFileName?: string;

/** The specific type of chat */
chatType?: ValueOf<typeof CONST.REPORT.CHAT_TYPE>;

Expand Down