From 51353d1a4affb0e92a9334aa15e4e23f49db2dd0 Mon Sep 17 00:00:00 2001
From: Samran Ahmed <samranahmed03@gmail.com>
Date: Mon, 13 Oct 2025 16:05:51 +0500
Subject: [PATCH 01/14] Revert "[CP Staging] Revert "feat: allow users to
 change room avatar""

---
 src/components/RoomHeaderAvatars.tsx          | 36 +++++++++++++++++--
 .../UpdatePolicyRoomAvatarParams.ts           |  8 +++++
 src/libs/API/parameters/index.ts              |  1 +
 src/libs/API/types.ts                         |  2 ++
 src/libs/ReportUtils.ts                       | 23 ++++++++++++
 src/libs/actions/Report.ts                    | 30 +++++++++++++++-
 src/pages/ReportAvatar.tsx                    |  9 ++++-
 src/pages/ReportDetailsPage.tsx               | 27 +++++++-------
 .../EnforceActionExportRestrictions.ts        |  5 +++
 tests/unit/ReportUtilsGetIconsTest.ts         | 20 +++++++++++
 10 files changed, 141 insertions(+), 20 deletions(-)
 create mode 100644 src/libs/API/parameters/UpdatePolicyRoomAvatarParams.ts

diff --git a/src/components/RoomHeaderAvatars.tsx b/src/components/RoomHeaderAvatars.tsx
index e2c661526fb0..b5735b9172d1 100644
--- a/src/components/RoomHeaderAvatars.tsx
+++ b/src/components/RoomHeaderAvatars.tsx
@@ -2,24 +2,29 @@ import React, {memo} from 'react';
 import {View} from 'react-native';
 import useStyleUtils from '@hooks/useStyleUtils';
 import useThemeStyles from '@hooks/useThemeStyles';
+import {clearAvatarErrors, updatePolicyRoomAvatar} from '@libs/actions/Report';
 import Navigation from '@libs/Navigation/Navigation';
+import {isUserCreatedPolicyRoom} from '@libs/ReportUtils';
+import {isDefaultAvatar} from '@libs/UserUtils';
 import CONST from '@src/CONST';
 import ROUTES from '@src/ROUTES';
+import type {Report} from '@src/types/onyx';
 import type {Icon} from '@src/types/onyx/OnyxCommon';
 import Avatar from './Avatar';
+import AvatarWithImagePicker from './AvatarWithImagePicker';
 import * as Expensicons from './Icon/Expensicons';
 import PressableWithoutFocus from './Pressable/PressableWithoutFocus';
 import Text from './Text';
 
 type RoomHeaderAvatarsProps = {
     icons: Icon[];
-    reportID: string;
+    report: Report;
 };
 
-function RoomHeaderAvatars({icons, reportID}: RoomHeaderAvatarsProps) {
+function RoomHeaderAvatars({icons, report}: RoomHeaderAvatarsProps) {
     const navigateToAvatarPage = (icon: Icon) => {
         if (icon.type === CONST.ICON_TYPE_WORKSPACE && icon.id) {
-            Navigation.navigate(ROUTES.REPORT_AVATAR.getRoute(reportID, icon.id.toString()));
+            Navigation.navigate(ROUTES.REPORT_AVATAR.getRoute(report?.reportID, icon.id.toString()));
             return;
         }
 
@@ -30,6 +35,7 @@ function RoomHeaderAvatars({icons, reportID}: RoomHeaderAvatarsProps) {
 
     const styles = useThemeStyles();
     const StyleUtils = useStyleUtils();
+    const isPolicyRoom = isUserCreatedPolicyRoom(report);
 
     if (!icons.length) {
         return null;
@@ -42,6 +48,30 @@ function RoomHeaderAvatars({icons, reportID}: RoomHeaderAvatarsProps) {
             return;
         }
 
+        if (isPolicyRoom) {
+            return (
+                <AvatarWithImagePicker
+                    source={report.avatarUrl ?? icon.source}
+                    avatarID={icon.id}
+                    isUsingDefaultAvatar={!report.avatarUrl || isDefaultAvatar(icon.source)}
+                    size={CONST.AVATAR_SIZE.X_LARGE}
+                    avatarStyle={[styles.avatarXLarge, styles.alignSelfCenter]}
+                    onViewPhotoPress={() => Navigation.navigate(ROUTES.REPORT_AVATAR.getRoute(report.reportID))}
+                    onImageRemoved={() => updatePolicyRoomAvatar(report.reportID)}
+                    onImageSelected={(file) => updatePolicyRoomAvatar(report.reportID, file)}
+                    editIcon={Expensicons.Camera}
+                    editIconStyle={styles.smallEditIconAccount}
+                    pendingAction={report.pendingFields?.avatar}
+                    errors={report.errorFields?.avatar ?? null}
+                    errorRowStyles={styles.mt6}
+                    onErrorClose={() => clearAvatarErrors(report.reportID)}
+                    style={[styles.w100, styles.mb3, styles.alignItemsStart, styles.sectionMenuItemTopDescription]}
+                    type={icon.type}
+                    editorMaskImage={Expensicons.ImageCropSquareMask}
+                />
+            );
+        }
+
         return (
             <PressableWithoutFocus
                 style={styles.noOutline}
diff --git a/src/libs/API/parameters/UpdatePolicyRoomAvatarParams.ts b/src/libs/API/parameters/UpdatePolicyRoomAvatarParams.ts
new file mode 100644
index 000000000000..63c98f9819c3
--- /dev/null
+++ b/src/libs/API/parameters/UpdatePolicyRoomAvatarParams.ts
@@ -0,0 +1,8 @@
+import type {CustomRNImageManipulatorResult} from '@libs/cropOrRotateImage/types';
+
+type UpdatePolicyRoomAvatarParams = {
+    reportID: string;
+    file: File | CustomRNImageManipulatorResult | undefined;
+};
+
+export default UpdatePolicyRoomAvatarParams;
diff --git a/src/libs/API/parameters/index.ts b/src/libs/API/parameters/index.ts
index 13a10deaac1f..c165cfb177c1 100644
--- a/src/libs/API/parameters/index.ts
+++ b/src/libs/API/parameters/index.ts
@@ -102,6 +102,7 @@ export type {default as UpdateStatusParams} from './UpdateStatusParams';
 export type {default as UpdateThemeParams} from './UpdateThemeParams';
 export type {default as UpdateUserAvatarParams} from './UpdateUserAvatarParams';
 export type {default as UpdateGroupChatAvatarParams} from './UpdateGroupChatAvatarParams';
+export type {default as UpdatePolicyRoomAvatarParams} from './UpdatePolicyRoomAvatarParams';
 export type {default as ValidateBankAccountWithTransactionsParams} from './ValidateBankAccountWithTransactionsParams';
 export type {default as ValidateLoginParams} from './ValidateLoginParams';
 export type {default as ValidateSecondaryLoginParams} from './ValidateSecondaryLoginParams';
diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts
index 3300766aaa74..6dcd6cfceebe 100644
--- a/src/libs/API/types.ts
+++ b/src/libs/API/types.ts
@@ -69,6 +69,7 @@ const WRITE_COMMANDS = {
     UPDATE_SELECTED_TIMEZONE: 'UpdateSelectedTimezone',
     UPDATE_USER_AVATAR: 'UpdateUserAvatar',
     UPDATE_GROUP_CHAT_AVATAR: 'UpdateGroupChatAvatar',
+    UPDATE_POLICY_ROOM_AVATAR: 'UpdatePolicyRoomAvatar',
     DELETE_USER_AVATAR: 'DeleteUserAvatar',
     REFER_TEACHERS_UNITE_VOLUNTEER: 'ReferTeachersUniteVolunteer',
     ADD_SCHOOL_PRINCIPAL: 'AddSchoolPrincipal',
@@ -627,6 +628,7 @@ type WriteCommandParameters = {
     [WRITE_COMMANDS.INVITE_TO_ROOM]: Parameters.InviteToRoomParams;
     [WRITE_COMMANDS.INVITE_TO_GROUP_CHAT]: Parameters.InviteToGroupChatParams;
     [WRITE_COMMANDS.UPDATE_GROUP_CHAT_AVATAR]: Parameters.UpdateGroupChatAvatarParams;
+    [WRITE_COMMANDS.UPDATE_POLICY_ROOM_AVATAR]: Parameters.UpdatePolicyRoomAvatarParams;
     [WRITE_COMMANDS.PUSHER_PING]: Parameters.PusherPingParams;
     [WRITE_COMMANDS.LEAVE_GROUP_CHAT]: Parameters.LeaveGroupChatParams;
     [WRITE_COMMANDS.REMOVE_FROM_GROUP_CHAT]: Parameters.RemoveFromGroupChatParams;
diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts
index dd9d817c96c0..4d868cc859f1 100644
--- a/src/libs/ReportUtils.ts
+++ b/src/libs/ReportUtils.ts
@@ -3467,6 +3467,26 @@ function getIconsForInvoiceReport(
     return icons;
 }
 
+/**
+ * Helper function to get the icons for a user-created policy room. Only to be used in getIcons().
+ */
+function getIconsForUserCreatedPolicyRoom(report: OnyxInputOrEntry<Report>, policy: OnyxInputOrEntry<Policy>): Icon[] {
+    if (!report) {
+        return [];
+    }
+    if (report?.avatarUrl) {
+        return [
+            {
+                source: report.avatarUrl,
+                type: CONST.ICON_TYPE_WORKSPACE,
+                name: getReportName(report),
+                id: report?.policyID,
+            },
+        ];
+    }
+    return [getWorkspaceIcon(report, policy)];
+}
+
 /**
  * Returns the appropriate icons for the given chat report using the stored personalDetails.
  * The Avatar sources can be URLs or Icon components according to the chat type.
@@ -3503,6 +3523,9 @@ function getIcons(
     if (isDomainRoom(report)) {
         return getIconsForDomainRoom(report);
     }
+    if (isUserCreatedPolicyRoom(report)) {
+        return getIconsForUserCreatedPolicyRoom(report, policy);
+    }
     if (isAdminRoom(report) || isAnnounceRoom(report) || isChatRoom(report) || (isArchivedNonExpenseReport(report, isReportArchived) && !chatIncludesConcierge(report))) {
         return getIconsForPolicyRoom(report, personalDetails, policy, invoiceReceiverPolicy);
     }
diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts
index ddbae5c1d8b1..48d4ac585d2a 100644
--- a/src/libs/actions/Report.ts
+++ b/src/libs/actions/Report.ts
@@ -50,6 +50,7 @@ import type {
     UpdateCommentParams,
     UpdateGroupChatAvatarParams,
     UpdateGroupChatMemberRolesParams,
+    UpdatePolicyRoomAvatarParams,
     UpdatePolicyRoomNameParams,
     UpdateReportNotificationPreferenceParams,
     UpdateReportPrivateNoteParams,
@@ -940,7 +941,15 @@ function updateChatName(reportID: string, reportName: string, type: typeof CONST
     API.write(command, parameters, {optimisticData, successData, failureData});
 }
 
-function updateGroupChatAvatar(reportID: string, file?: File | CustomRNImageManipulatorResult) {
+/**
+ * Helper function to build optimistic, success, and failure Onyx updates
+ * when updating or removing a report's avatar.
+ *
+ * @param reportID - The report ID of the policy room.
+ * @param [file] - (Optional) The selected image file to update the avatar with.
+ * If not provided, the existing avatar will be removed.
+ */
+function buildUpdateReportAvatarOnyxData(reportID: string, file?: File | CustomRNImageManipulatorResult) {
     // If we have no file that means we are removing the avatar.
     const optimisticData: OnyxUpdate[] = [
         {
@@ -983,10 +992,28 @@ function updateGroupChatAvatar(reportID: string, file?: File | CustomRNImageMani
             },
         },
     ];
+
+    return {optimisticData, successData, failureData};
+}
+
+/**
+ * Updates the avatar for a group chat.
+ */
+function updateGroupChatAvatar(reportID: string, file?: File | CustomRNImageManipulatorResult) {
+    const {optimisticData, successData, failureData} = buildUpdateReportAvatarOnyxData(reportID, file);
     const parameters: UpdateGroupChatAvatarParams = {file, reportID};
     API.write(WRITE_COMMANDS.UPDATE_GROUP_CHAT_AVATAR, parameters, {optimisticData, failureData, successData});
 }
 
+/**
+ * Updates the avatar for a policy room.
+ */
+function updatePolicyRoomAvatar(reportID: string, file?: File | CustomRNImageManipulatorResult) {
+    const {optimisticData, successData, failureData} = buildUpdateReportAvatarOnyxData(reportID, file);
+    const parameters: UpdatePolicyRoomAvatarParams = {reportID, file};
+    API.write(WRITE_COMMANDS.UPDATE_POLICY_ROOM_AVATAR, parameters, {optimisticData, failureData, successData});
+}
+
 /**
  * Clear error and pending fields for the report avatar
  */
@@ -6221,6 +6248,7 @@ export {
     unsubscribeFromReportChannel,
     updateDescription,
     updateGroupChatAvatar,
+    updatePolicyRoomAvatar,
     updateGroupChatMemberRoles,
     updateChatName,
     updateLastVisitTime,
diff --git a/src/pages/ReportAvatar.tsx b/src/pages/ReportAvatar.tsx
index f04c0d78e61d..a2637549a1c4 100644
--- a/src/pages/ReportAvatar.tsx
+++ b/src/pages/ReportAvatar.tsx
@@ -4,7 +4,7 @@ import useOnyx from '@hooks/useOnyx';
 import Navigation from '@libs/Navigation/Navigation';
 import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types';
 import type {AuthScreensParamList} from '@libs/Navigation/types';
-import {getDefaultGroupAvatar, getPolicyName, getReportName, getWorkspaceIcon, isGroupChat, isThread} from '@libs/ReportUtils';
+import {getDefaultGroupAvatar, getPolicyName, getReportName, getWorkspaceIcon, isGroupChat, isThread, isUserCreatedPolicyRoom} from '@libs/ReportUtils';
 import {getFullSizeAvatar} from '@libs/UserUtils';
 import ONYXKEYS from '@src/ONYXKEYS';
 import ROUTES from '@src/ROUTES';
@@ -26,6 +26,13 @@ function ReportAvatar({route}: ReportAvatarProps) {
                 isWorkspaceAvatar: false,
             };
         }
+        if (isUserCreatedPolicyRoom(report)) {
+            return {
+                source: report?.avatarUrl ? getFullSizeAvatar(report.avatarUrl, 0) : undefined,
+                headerTitle: getReportName(report),
+                isWorkspaceAvatar: false,
+            };
+        }
 
         return {
             source: getFullSizeAvatar(getWorkspaceIcon(report, policy).source, 0),
diff --git a/src/pages/ReportDetailsPage.tsx b/src/pages/ReportDetailsPage.tsx
index 6da0de0b2663..e5beddf46721 100644
--- a/src/pages/ReportDetailsPage.tsx
+++ b/src/pages/ReportDetailsPage.tsx
@@ -19,6 +19,7 @@ import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeed
 import type {PromotedAction} from '@components/PromotedActionsBar';
 import PromotedActionsBar, {PromotedActions} from '@components/PromotedActionsBar';
 import ReportActionAvatars from '@components/ReportActionAvatars';
+import RoomHeaderAvatars from '@components/RoomHeaderAvatars';
 import ScreenWrapper from '@components/ScreenWrapper';
 import ScrollView from '@components/ScrollView';
 import {useSearchContext} from '@components/Search/SearchContext';
@@ -581,6 +582,16 @@ function ReportDetailsPage({policy, report, route, reportMetadata}: ReportDetail
     ) : null;
 
     const renderedAvatar = useMemo(() => {
+        if (isChatRoom && !isThread) {
+            return (
+                <View style={styles.mb3}>
+                    <RoomHeaderAvatars
+                        icons={icons}
+                        report={report}
+                    />
+                </View>
+            );
+        }
         if (!isGroupChat || isThread) {
             return (
                 <View style={styles.mb3}>
@@ -617,21 +628,7 @@ function ReportDetailsPage({policy, report, route, reportMetadata}: ReportDetail
                 style={[styles.w100, styles.mb3]}
             />
         );
-    }, [
-        isGroupChat,
-        isThread,
-        icons,
-        report.avatarUrl,
-        report.pendingFields?.avatar,
-        report.errorFields?.avatar,
-        report.reportID,
-        styles.avatarXLarge,
-        styles.smallEditIconAccount,
-        styles.mt6,
-        styles.w100,
-        styles.mb3,
-        moneyRequestReport,
-    ]);
+    }, [isGroupChat, isThread, isChatRoom, icons, report, styles.avatarXLarge, styles.smallEditIconAccount, styles.mt6, styles.w100, styles.mb3, moneyRequestReport?.reportID]);
 
     const canJoin = canJoinChat(report, parentReportAction, policy, !!reportNameValuePairs?.private_isArchived);
 
diff --git a/tests/actions/EnforceActionExportRestrictions.ts b/tests/actions/EnforceActionExportRestrictions.ts
index be76cb9f5b02..5c467834715d 100644
--- a/tests/actions/EnforceActionExportRestrictions.ts
+++ b/tests/actions/EnforceActionExportRestrictions.ts
@@ -88,6 +88,11 @@ describe('ReportUtils', () => {
         expect(ReportUtils.getIconsForGroupChat).toBeUndefined();
     });
 
+    it('does not export getIconsForUserCreatedPolicyRoom', () => {
+        // @ts-expect-error the test is asserting that it's undefined, so the TS error is normal
+        expect(ReportUtils.getIconsForUserCreatedPolicyRoom).toBeUndefined();
+    });
+
     it('does not export getAllReportActions', () => {
         // @ts-expect-error the test is asserting that it's undefined, so the TS error is normal
         expect(ReportUtils.getAllReportActions).toBeUndefined();
diff --git a/tests/unit/ReportUtilsGetIconsTest.ts b/tests/unit/ReportUtilsGetIconsTest.ts
index 2b30796a0384..7441f8d04f80 100644
--- a/tests/unit/ReportUtilsGetIconsTest.ts
+++ b/tests/unit/ReportUtilsGetIconsTest.ts
@@ -21,6 +21,7 @@ import {
     isSelfDM,
     isTaskReport,
     isThread,
+    isUserCreatedPolicyRoom,
     isWorkspaceTaskReport,
     isWorkspaceThread,
 } from '@libs/ReportUtils';
@@ -215,6 +216,25 @@ describe('getIcons', () => {
         expect(icons.at(0)?.name).toBe('Workspace-Test-001');
     });
 
+    it('should return the correct icons for a user created policy room', () => {
+        const report: Report = {
+            ...LHNTestUtils.getFakeReport([], 0, true),
+            chatType: CONST.REPORT.CHAT_TYPE.POLICY_ROOM,
+            policyID: '1',
+            avatarUrl: 'https://example.com/avatar.png',
+        };
+        const policy = LHNTestUtils.getFakePolicy('1');
+
+        // Verify report type conditions
+        expect(isUserCreatedPolicyRoom(report)).toBe(true);
+        expect(isChatReport(report)).toBe(true);
+
+        const icons = getIcons(report, FAKE_PERSONAL_DETAILS, null, '', -1, policy);
+        expect(icons).toHaveLength(1);
+        expect(icons.at(0)?.type).toBe(CONST.ICON_TYPE_WORKSPACE);
+        expect(icons.at(0)?.source).toBe('https://example.com/avatar.png');
+    });
+
     it('should return the correct icons for a policy expense chat', () => {
         const report: Report = {
             ...LHNTestUtils.getFakeReport([1], 0, true),

From 7a4a421447fbd01740ca7e2a90de7cf87a18966b Mon Sep 17 00:00:00 2001
From: Samran Ahmed <samranahmed03@gmail.com>
Date: Mon, 13 Oct 2025 16:18:51 +0500
Subject: [PATCH 02/14] fix: prevent anonymous (not logged-in) users from
 editing room avatar

---
 src/components/RoomHeaderAvatars.tsx | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/components/RoomHeaderAvatars.tsx b/src/components/RoomHeaderAvatars.tsx
index b5735b9172d1..d75ed1a08a37 100644
--- a/src/components/RoomHeaderAvatars.tsx
+++ b/src/components/RoomHeaderAvatars.tsx
@@ -3,6 +3,7 @@ import {View} from 'react-native';
 import useStyleUtils from '@hooks/useStyleUtils';
 import useThemeStyles from '@hooks/useThemeStyles';
 import {clearAvatarErrors, updatePolicyRoomAvatar} from '@libs/actions/Report';
+import {isAnonymousUser} from '@libs/actions/Session';
 import Navigation from '@libs/Navigation/Navigation';
 import {isUserCreatedPolicyRoom} from '@libs/ReportUtils';
 import {isDefaultAvatar} from '@libs/UserUtils';
@@ -48,7 +49,7 @@ function RoomHeaderAvatars({icons, report}: RoomHeaderAvatarsProps) {
             return;
         }
 
-        if (isPolicyRoom) {
+        if (isPolicyRoom && !isAnonymousUser()) {
             return (
                 <AvatarWithImagePicker
                     source={report.avatarUrl ?? icon.source}

From 015993bb2116938bcf5f3d016f23a5de9fda014c Mon Sep 17 00:00:00 2001
From: Samran Ahmed <samranahmed03@gmail.com>
Date: Mon, 13 Oct 2025 20:19:41 +0500
Subject: [PATCH 03/14] fix: default WorkspaceAvatar to use first letter
 instead of WorkspaceBuilding

---
 src/components/AvatarButtonWithIcon.tsx  | 5 +++++
 src/components/AvatarWithImagePicker.tsx | 5 +++++
 src/components/RoomHeaderAvatars.tsx     | 3 ++-
 src/libs/ReportUtils.ts                  | 2 +-
 4 files changed, 13 insertions(+), 2 deletions(-)

diff --git a/src/components/AvatarButtonWithIcon.tsx b/src/components/AvatarButtonWithIcon.tsx
index d2800733d6ca..c99161469915 100644
--- a/src/components/AvatarButtonWithIcon.tsx
+++ b/src/components/AvatarButtonWithIcon.tsx
@@ -61,6 +61,9 @@ type AvatarButtonWithIconProps = {
 
     /** Optionally override the default "Edit" icon */
     editIcon?: IconAsset;
+
+    /** The name associated with avatar */
+    name?: string;
 };
 
 /**
@@ -82,6 +85,7 @@ function AvatarButtonWithIcon({
     disabled = false,
     editIcon = Expensicons.Pencil,
     anchorRef,
+    name = '',
 }: AvatarButtonWithIconProps) {
     const theme = useTheme();
     const styles = useThemeStyles();
@@ -110,6 +114,7 @@ function AvatarButtonWithIcon({
                             fallbackIcon={fallbackIcon}
                             size={size}
                             type={type}
+                            name={name}
                         />
                     ) : (
                         <DefaultAvatar />
diff --git a/src/components/AvatarWithImagePicker.tsx b/src/components/AvatarWithImagePicker.tsx
index 4212aa602571..2b6621baa3aa 100644
--- a/src/components/AvatarWithImagePicker.tsx
+++ b/src/components/AvatarWithImagePicker.tsx
@@ -83,6 +83,9 @@ type AvatarWithImagePickerProps = Omit<AvatarButtonWithIconProps, 'text' | 'onPr
 
     /** Hard disables the "View photo" option */
     shouldDisableViewPhoto?: boolean;
+
+    /** The name associated with avatar */
+    name?: string;
 };
 
 const anchorAlignment = {horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.CENTER, vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.TOP};
@@ -114,6 +117,7 @@ function AvatarWithImagePicker({
     enablePreview = false,
     shouldDisableViewPhoto = false,
     editIcon = Expensicons.Pencil,
+    name = '',
 }: AvatarWithImagePickerProps) {
     const styles = useThemeStyles();
     const isFocused = useIsFocused();
@@ -295,6 +299,7 @@ function AvatarWithImagePicker({
                                                 type={type}
                                                 disabledStyle={disabledStyle}
                                                 editIconStyle={editIconStyle}
+                                                name={name}
                                             />
                                         </OfflineWithFeedback>
                                         <PopoverMenu
diff --git a/src/components/RoomHeaderAvatars.tsx b/src/components/RoomHeaderAvatars.tsx
index d75ed1a08a37..d50b7dd4db9d 100644
--- a/src/components/RoomHeaderAvatars.tsx
+++ b/src/components/RoomHeaderAvatars.tsx
@@ -52,7 +52,7 @@ function RoomHeaderAvatars({icons, report}: RoomHeaderAvatarsProps) {
         if (isPolicyRoom && !isAnonymousUser()) {
             return (
                 <AvatarWithImagePicker
-                    source={report.avatarUrl ?? icon.source}
+                    source={icon.source || report.avatarUrl}
                     avatarID={icon.id}
                     isUsingDefaultAvatar={!report.avatarUrl || isDefaultAvatar(icon.source)}
                     size={CONST.AVATAR_SIZE.X_LARGE}
@@ -69,6 +69,7 @@ function RoomHeaderAvatars({icons, report}: RoomHeaderAvatarsProps) {
                     style={[styles.w100, styles.mb3, styles.alignItemsStart, styles.sectionMenuItemTopDescription]}
                     type={icon.type}
                     editorMaskImage={Expensicons.ImageCropSquareMask}
+                    name={icon.name}
                 />
             );
         }
diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts
index 4d868cc859f1..1114ecf5f8b8 100644
--- a/src/libs/ReportUtils.ts
+++ b/src/libs/ReportUtils.ts
@@ -3479,7 +3479,7 @@ function getIconsForUserCreatedPolicyRoom(report: OnyxInputOrEntry<Report>, poli
             {
                 source: report.avatarUrl,
                 type: CONST.ICON_TYPE_WORKSPACE,
-                name: getReportName(report),
+                name: getPolicyName({report, policy}),
                 id: report?.policyID,
             },
         ];

From 90a6bc16663a6ec81df49579f0d30ebe59c952a7 Mon Sep 17 00:00:00 2001
From: Samran Ahmed <samranahmed03@gmail.com>
Date: Mon, 13 Oct 2025 20:26:41 +0500
Subject: [PATCH 04/14] fix: pass policyName to display correct default
 Workspace avatar in WorkspaceOverviewPage

---
 src/pages/workspace/WorkspaceOverviewPage.tsx | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/pages/workspace/WorkspaceOverviewPage.tsx b/src/pages/workspace/WorkspaceOverviewPage.tsx
index 9363bf866006..713b1025d955 100644
--- a/src/pages/workspace/WorkspaceOverviewPage.tsx
+++ b/src/pages/workspace/WorkspaceOverviewPage.tsx
@@ -355,6 +355,7 @@ function WorkspaceOverviewPage({policyDraft, policy: policyProp, route}: Workspa
                             source={policy?.avatarURL ?? ''}
                             avatarID={policy?.id}
                             size={CONST.AVATAR_SIZE.X_LARGE}
+                            name={policyName}
                             avatarStyle={styles.avatarXLarge}
                             enablePreview
                             DefaultAvatar={DefaultAvatar}

From 67ee603e6a3a23d07c9226e031fb64548567fd98 Mon Sep 17 00:00:00 2001
From: Samran Ahmed <samranahmed03@gmail.com>
Date: Thu, 23 Oct 2025 17:41:31 +0500
Subject: [PATCH 05/14] fix: change command name to UpdateRoomAvatar

---
 ...ePolicyRoomAvatarParams.ts => UpdateRoomAvatarParams.ts} | 4 ++--
 src/libs/API/parameters/index.ts                            | 2 +-
 src/libs/API/types.ts                                       | 4 ++--
 src/libs/actions/Report.ts                                  | 6 +++---
 4 files changed, 8 insertions(+), 8 deletions(-)
 rename src/libs/API/parameters/{UpdatePolicyRoomAvatarParams.ts => UpdateRoomAvatarParams.ts} (67%)

diff --git a/src/libs/API/parameters/UpdatePolicyRoomAvatarParams.ts b/src/libs/API/parameters/UpdateRoomAvatarParams.ts
similarity index 67%
rename from src/libs/API/parameters/UpdatePolicyRoomAvatarParams.ts
rename to src/libs/API/parameters/UpdateRoomAvatarParams.ts
index 63c98f9819c3..fadd66b4d84c 100644
--- a/src/libs/API/parameters/UpdatePolicyRoomAvatarParams.ts
+++ b/src/libs/API/parameters/UpdateRoomAvatarParams.ts
@@ -1,8 +1,8 @@
 import type {CustomRNImageManipulatorResult} from '@libs/cropOrRotateImage/types';
 
-type UpdatePolicyRoomAvatarParams = {
+type UpdateRoomAvatarParams = {
     reportID: string;
     file: File | CustomRNImageManipulatorResult | undefined;
 };
 
-export default UpdatePolicyRoomAvatarParams;
+export default UpdateRoomAvatarParams;
diff --git a/src/libs/API/parameters/index.ts b/src/libs/API/parameters/index.ts
index 84f1a07f4318..e1f7fb3a1c4d 100644
--- a/src/libs/API/parameters/index.ts
+++ b/src/libs/API/parameters/index.ts
@@ -104,7 +104,7 @@ export type {default as UpdateStatusParams} from './UpdateStatusParams';
 export type {default as UpdateThemeParams} from './UpdateThemeParams';
 export type {default as UpdateUserAvatarParams} from './UpdateUserAvatarParams';
 export type {default as UpdateGroupChatAvatarParams} from './UpdateGroupChatAvatarParams';
-export type {default as UpdatePolicyRoomAvatarParams} from './UpdatePolicyRoomAvatarParams';
+export type {default as UpdateRoomAvatarParams} from './UpdateRoomAvatarParams';
 export type {default as ValidateBankAccountWithTransactionsParams} from './ValidateBankAccountWithTransactionsParams';
 export type {default as ValidateLoginParams} from './ValidateLoginParams';
 export type {default as ValidateSecondaryLoginParams} from './ValidateSecondaryLoginParams';
diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts
index e0b3ce13af8c..e3ea06cb6cfa 100644
--- a/src/libs/API/types.ts
+++ b/src/libs/API/types.ts
@@ -71,7 +71,7 @@ const WRITE_COMMANDS = {
     UPDATE_SELECTED_TIMEZONE: 'UpdateSelectedTimezone',
     UPDATE_USER_AVATAR: 'UpdateUserAvatar',
     UPDATE_GROUP_CHAT_AVATAR: 'UpdateGroupChatAvatar',
-    UPDATE_POLICY_ROOM_AVATAR: 'UpdatePolicyRoomAvatar',
+    UPDATE_ROOM_AVATAR: 'UpdateRoomAvatar',
     DELETE_USER_AVATAR: 'DeleteUserAvatar',
     REFER_TEACHERS_UNITE_VOLUNTEER: 'ReferTeachersUniteVolunteer',
     ADD_SCHOOL_PRINCIPAL: 'AddSchoolPrincipal',
@@ -633,7 +633,7 @@ type WriteCommandParameters = {
     [WRITE_COMMANDS.INVITE_TO_ROOM]: Parameters.InviteToRoomParams;
     [WRITE_COMMANDS.INVITE_TO_GROUP_CHAT]: Parameters.InviteToGroupChatParams;
     [WRITE_COMMANDS.UPDATE_GROUP_CHAT_AVATAR]: Parameters.UpdateGroupChatAvatarParams;
-    [WRITE_COMMANDS.UPDATE_POLICY_ROOM_AVATAR]: Parameters.UpdatePolicyRoomAvatarParams;
+    [WRITE_COMMANDS.UPDATE_ROOM_AVATAR]: Parameters.UpdateRoomAvatarParams;
     [WRITE_COMMANDS.PUSHER_PING]: Parameters.PusherPingParams;
     [WRITE_COMMANDS.LEAVE_GROUP_CHAT]: Parameters.LeaveGroupChatParams;
     [WRITE_COMMANDS.REMOVE_FROM_GROUP_CHAT]: Parameters.RemoveFromGroupChatParams;
diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts
index fabf5958c942..e1c795de0a72 100644
--- a/src/libs/actions/Report.ts
+++ b/src/libs/actions/Report.ts
@@ -50,11 +50,11 @@ import type {
     UpdateCommentParams,
     UpdateGroupChatAvatarParams,
     UpdateGroupChatMemberRolesParams,
-    UpdatePolicyRoomAvatarParams,
     UpdatePolicyRoomNameParams,
     UpdateReportNotificationPreferenceParams,
     UpdateReportPrivateNoteParams,
     UpdateReportWriteCapabilityParams,
+    UpdateRoomAvatarParams,
     UpdateRoomDescriptionParams,
 } from '@libs/API/parameters';
 import type ExportReportCSVParams from '@libs/API/parameters/ExportReportCSVParams';
@@ -995,8 +995,8 @@ function updateGroupChatAvatar(reportID: string, file?: File | CustomRNImageMani
  */
 function updatePolicyRoomAvatar(reportID: string, file?: File | CustomRNImageManipulatorResult) {
     const {optimisticData, successData, failureData} = buildUpdateReportAvatarOnyxData(reportID, file);
-    const parameters: UpdatePolicyRoomAvatarParams = {reportID, file};
-    API.write(WRITE_COMMANDS.UPDATE_POLICY_ROOM_AVATAR, parameters, {optimisticData, failureData, successData});
+    const parameters: UpdateRoomAvatarParams = {reportID, file};
+    API.write(WRITE_COMMANDS.UPDATE_ROOM_AVATAR, parameters, {optimisticData, failureData, successData});
 }
 
 /**

From e5cf954d4661aa74662e7beee91b4590b7ddaf76 Mon Sep 17 00:00:00 2001
From: Samran Ahmed <samranahmed03@gmail.com>
Date: Tue, 28 Oct 2025 17:48:07 +0500
Subject: [PATCH 06/14] update can edit room avatar permissions

---
 src/components/RoomHeaderAvatars.tsx | 22 ++++++++++++++++------
 src/pages/ReportDetailsPage.tsx      |  1 +
 2 files changed, 17 insertions(+), 6 deletions(-)

diff --git a/src/components/RoomHeaderAvatars.tsx b/src/components/RoomHeaderAvatars.tsx
index d50b7dd4db9d..0b405417a138 100644
--- a/src/components/RoomHeaderAvatars.tsx
+++ b/src/components/RoomHeaderAvatars.tsx
@@ -1,15 +1,18 @@
 import React, {memo} from 'react';
 import {View} from 'react-native';
+import {OnyxEntry} from 'react-native-onyx';
+import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails';
 import useStyleUtils from '@hooks/useStyleUtils';
 import useThemeStyles from '@hooks/useThemeStyles';
-import {clearAvatarErrors, updatePolicyRoomAvatar} from '@libs/actions/Report';
+import {clearAvatarErrors, getCurrentUserAccountID, getCurrentUserEmail, updatePolicyRoomAvatar} from '@libs/actions/Report';
 import {isAnonymousUser} from '@libs/actions/Session';
 import Navigation from '@libs/Navigation/Navigation';
-import {isUserCreatedPolicyRoom} from '@libs/ReportUtils';
+import {isPolicyMember} from '@libs/PolicyUtils';
+import {getParticipantsAccountIDsForDisplay, isAuditor, isReportParticipant, isUserCreatedPolicyRoom} from '@libs/ReportUtils';
 import {isDefaultAvatar} from '@libs/UserUtils';
 import CONST from '@src/CONST';
 import ROUTES from '@src/ROUTES';
-import type {Report} from '@src/types/onyx';
+import type {Policy, Report} from '@src/types/onyx';
 import type {Icon} from '@src/types/onyx/OnyxCommon';
 import Avatar from './Avatar';
 import AvatarWithImagePicker from './AvatarWithImagePicker';
@@ -20,9 +23,10 @@ import Text from './Text';
 type RoomHeaderAvatarsProps = {
     icons: Icon[];
     report: Report;
+    policy: OnyxEntry<Policy>;
 };
 
-function RoomHeaderAvatars({icons, report}: RoomHeaderAvatarsProps) {
+function RoomHeaderAvatars({icons, report, policy}: RoomHeaderAvatarsProps) {
     const navigateToAvatarPage = (icon: Icon) => {
         if (icon.type === CONST.ICON_TYPE_WORKSPACE && icon.id) {
             Navigation.navigate(ROUTES.REPORT_AVATAR.getRoute(report?.reportID, icon.id.toString()));
@@ -36,7 +40,13 @@ function RoomHeaderAvatars({icons, report}: RoomHeaderAvatarsProps) {
 
     const styles = useThemeStyles();
     const StyleUtils = useStyleUtils();
-    const isPolicyRoom = isUserCreatedPolicyRoom(report);
+    const currentUserPersonalDetails = useCurrentUserPersonalDetails();
+
+    const canEditRoomAvatar =
+        isUserCreatedPolicyRoom(report) &&
+        isPolicyMember(policy, currentUserPersonalDetails?.login) &&
+        isReportParticipant(currentUserPersonalDetails?.accountID, report) &&
+        !isAuditor(report);
 
     if (!icons.length) {
         return null;
@@ -49,7 +59,7 @@ function RoomHeaderAvatars({icons, report}: RoomHeaderAvatarsProps) {
             return;
         }
 
-        if (isPolicyRoom && !isAnonymousUser()) {
+        if (canEditRoomAvatar) {
             return (
                 <AvatarWithImagePicker
                     source={icon.source || report.avatarUrl}
diff --git a/src/pages/ReportDetailsPage.tsx b/src/pages/ReportDetailsPage.tsx
index 6ade14d58bf3..33c3bbdddcc1 100644
--- a/src/pages/ReportDetailsPage.tsx
+++ b/src/pages/ReportDetailsPage.tsx
@@ -598,6 +598,7 @@ function ReportDetailsPage({policy, report, route, reportMetadata}: ReportDetail
                     <RoomHeaderAvatars
                         icons={icons}
                         report={report}
+                        policy={policy}
                     />
                 </View>
             );

From be630fd738d8a2163610a630539e2c99c76b22d5 Mon Sep 17 00:00:00 2001
From: Samran Ahmed <samranahmed03@gmail.com>
Date: Tue, 28 Oct 2025 18:04:29 +0500
Subject: [PATCH 07/14] lint

---
 src/components/RoomHeaderAvatars.tsx | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/src/components/RoomHeaderAvatars.tsx b/src/components/RoomHeaderAvatars.tsx
index 0b405417a138..83eeb9b660dc 100644
--- a/src/components/RoomHeaderAvatars.tsx
+++ b/src/components/RoomHeaderAvatars.tsx
@@ -1,14 +1,13 @@
 import React, {memo} from 'react';
 import {View} from 'react-native';
-import {OnyxEntry} from 'react-native-onyx';
+import type {OnyxEntry} from 'react-native-onyx';
 import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails';
 import useStyleUtils from '@hooks/useStyleUtils';
 import useThemeStyles from '@hooks/useThemeStyles';
-import {clearAvatarErrors, getCurrentUserAccountID, getCurrentUserEmail, updatePolicyRoomAvatar} from '@libs/actions/Report';
-import {isAnonymousUser} from '@libs/actions/Session';
+import {clearAvatarErrors, updatePolicyRoomAvatar} from '@libs/actions/Report';
 import Navigation from '@libs/Navigation/Navigation';
 import {isPolicyMember} from '@libs/PolicyUtils';
-import {getParticipantsAccountIDsForDisplay, isAuditor, isReportParticipant, isUserCreatedPolicyRoom} from '@libs/ReportUtils';
+import {isAuditor, isReportParticipant, isUserCreatedPolicyRoom} from '@libs/ReportUtils';
 import {isDefaultAvatar} from '@libs/UserUtils';
 import CONST from '@src/CONST';
 import ROUTES from '@src/ROUTES';

From 4c5c737c322d66f1d54ea3577c87af3b38e3c8da Mon Sep 17 00:00:00 2001
From: Samran Ahmed <samranahmed03@gmail.com>
Date: Thu, 30 Oct 2025 01:03:16 +0500
Subject: [PATCH 08/14] fix merge conflict

---
 src/components/AvatarWithImagePicker.tsx | 139 ++++++++++-------------
 src/libs/ReportUtils.ts                  |   1 +
 2 files changed, 62 insertions(+), 78 deletions(-)

diff --git a/src/components/AvatarWithImagePicker.tsx b/src/components/AvatarWithImagePicker.tsx
index 219c693aaec9..60914e47fdf1 100644
--- a/src/components/AvatarWithImagePicker.tsx
+++ b/src/components/AvatarWithImagePicker.tsx
@@ -70,9 +70,6 @@ type AvatarWithImagePickerProps = Omit<AvatarButtonWithIconProps, 'text' | 'onPr
     /** Allows to open an image without Attachment Picker. */
     enablePreview?: boolean;
 
-    /** Hard disables the "View photo" option */
-    shouldDisableViewPhoto?: boolean;
-
     /** The name associated with avatar */
     name?: string;
 };
@@ -235,81 +232,67 @@ function AvatarWithImagePicker({
                     // We need to skip the validation in AttachmentPicker because it is handled in this component itself
                     shouldValidateImage={false}
                 >
-                    {({show}) => (
-                        <AttachmentPicker
-                            type={CONST.ATTACHMENT_PICKER_TYPE.IMAGE}
-                            // We need to skip the validation in AttachmentPicker because it is handled in this component itself
-                            shouldValidateImage={false}
-                        >
-                            {({openPicker}) => {
-                                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) {
-                                    menuItems.push({
-                                        icon: Expensicons.Eye,
-                                        text: translate('avatarWithImagePicker.viewPhoto'),
-                                        onSelected: () => {
-                                            if (typeof onViewPhotoPress !== 'function') {
-                                                show();
-                                                return;
-                                            }
-                                            onViewPhotoPress();
-                                        },
-                                        shouldCallAfterModalHide: true,
-                                    });
-                                }
-
-                                return (
-                                    <>
-                                        <OfflineWithFeedback
-                                            errors={errors}
-                                            errorRowStyles={errorRowStyles}
-                                            onClose={onErrorClose}
-                                        >
-                                            <AvatarButtonWithIcon
-                                                text={translate('avatarWithImagePicker.editImage')}
-                                                source={source}
-                                                avatarID={avatarID}
-                                                onPress={() => onPressAvatar(openPicker)}
-                                                avatarStyle={avatarStyle}
-                                                pendingAction={pendingAction}
-                                                fallbackIcon={fallbackIcon}
-                                                anchorRef={anchorRef}
-                                                DefaultAvatar={DefaultAvatar}
-                                                editIcon={editIcon}
-                                                size={size}
-                                                type={type}
-                                                disabledStyle={disabledStyle}
-                                                editIconStyle={editIconStyle}
-                                                name={name}
-                                            />
-                                        </OfflineWithFeedback>
-                                        <PopoverMenu
-                                            anchorPosition={popoverPosition}
-                                            isVisible={isMenuVisible}
-                                            onClose={() => setIsMenuVisible(false)}
-                                            onItemSelected={(item, index) => {
-                                                setIsMenuVisible(false);
-                                                // In order for the file picker to open dynamically, the click
-                                                // function must be called from within an event handler that was initiated
-                                                // by the user on Safari.
-                                                if (index === 0 && isSafari()) {
-                                                    openPicker({
-                                                        onPicked: (data) => showAvatarCropModal(data.at(0) ?? {}),
-                                                    });
-                                                }
-                                            }}
-                                            menuItems={menuItems}
-                                            anchorAlignment={anchorAlignment}
-                                            anchorRef={anchorRef}
-                                        />
-                                    </>
-                                );
-                            }}
-                        </AttachmentPicker>
-                    )}
-                </AttachmentModal>
+                    {({openPicker}) => {
+                        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 (onViewPhotoPress && !isUsingDefaultAvatar) {
+                            menuItems.push({
+                                icon: Expensicons.Eye,
+                                text: translate('avatarWithImagePicker.viewPhoto'),
+                                onSelected: onViewPhotoPress,
+                                shouldCallAfterModalHide: true,
+                            });
+                        }
+
+                        return (
+                            <>
+                                <OfflineWithFeedback
+                                    errors={errors}
+                                    errorRowStyles={errorRowStyles}
+                                    onClose={onErrorClose}
+                                >
+                                    <AvatarButtonWithIcon
+                                        text={translate('avatarWithImagePicker.editImage')}
+                                        source={source}
+                                        avatarID={avatarID}
+                                        onPress={() => onPressAvatar(openPicker)}
+                                        avatarStyle={avatarStyle}
+                                        pendingAction={pendingAction}
+                                        fallbackIcon={fallbackIcon}
+                                        anchorRef={anchorRef}
+                                        DefaultAvatar={DefaultAvatar}
+                                        editIcon={editIcon}
+                                        size={size}
+                                        type={type}
+                                        disabledStyle={disabledStyle}
+                                        editIconStyle={editIconStyle}
+                                        name={name}
+                                    />
+                                </OfflineWithFeedback>
+                                <PopoverMenu
+                                    anchorPosition={popoverPosition}
+                                    isVisible={isMenuVisible}
+                                    onClose={() => setIsMenuVisible(false)}
+                                    onItemSelected={(item, index) => {
+                                        setIsMenuVisible(false);
+                                        // In order for the file picker to open dynamically, the click
+                                        // function must be called from within an event handler that was initiated
+                                        // by the user on Safari.
+                                        if (index === 0 && isSafari()) {
+                                            openPicker({
+                                                onPicked: (data) => showAvatarCropModal(data.at(0) ?? {}),
+                                            });
+                                        }
+                                    }}
+                                    menuItems={menuItems}
+                                    anchorAlignment={anchorAlignment}
+                                    anchorRef={anchorRef}
+                                />
+                            </>
+                        );
+                    }}
+                </AttachmentPicker>
             </View>
             {!!errorData.validationError && (
                 <DotIndicatorMessage
diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts
index 03096129ebf3..918f17295e2d 100644
--- a/src/libs/ReportUtils.ts
+++ b/src/libs/ReportUtils.ts
@@ -1004,6 +1004,7 @@ Onyx.connect({
                 return acc;
             }
 
+            // eslint-disable-next-line @typescript-eslint/no-deprecated
             InteractionManager.runAfterInteractions(() => {
                 handlePreexistingReport(report);
             });

From 25e40b1b127c29c8d9df8bf0a9c459b9217b24ed Mon Sep 17 00:00:00 2001
From: Samran Ahmed <samranahmed03@gmail.com>
Date: Thu, 30 Oct 2025 17:55:58 +0500
Subject: [PATCH 09/14] feat: add room avatar updated log message in all locals

---
 src/languages/de.ts      | 2 ++
 src/languages/en.ts      | 2 ++
 src/languages/es.ts      | 2 ++
 src/languages/fr.ts      | 2 ++
 src/languages/it.ts      | 2 ++
 src/languages/ja.ts      | 2 ++
 src/languages/nl.ts      | 2 ++
 src/languages/pl.ts      | 2 ++
 src/languages/pt-BR.ts   | 2 ++
 src/languages/zh-hans.ts | 2 ++
 10 files changed, 20 insertions(+)

diff --git a/src/languages/de.ts b/src/languages/de.ts
index 2bd3f0d26878..0203e2e9b1d4 100644
--- a/src/languages/de.ts
+++ b/src/languages/de.ts
@@ -7145,6 +7145,8 @@ ${amount} für ${merchant} - ${date}`,
     roomChangeLog: {
         updateRoomDescription: 'setze die Raumbeschreibung auf:',
         clearRoomDescription: 'Raumbeschreibung gelöscht',
+        changedRoomAvatar: 'Hat das Raum-Avatar geändert',
+        removedRoomAvatar: 'Hat das Raum-Avatar entfernt',
     },
     delegate: {
         switchAccount: 'Konten wechseln:',
diff --git a/src/languages/en.ts b/src/languages/en.ts
index 445f97f58ef7..e6adc7b7c1ac 100755
--- a/src/languages/en.ts
+++ b/src/languages/en.ts
@@ -7094,6 +7094,8 @@ const translations = {
     roomChangeLog: {
         updateRoomDescription: 'set the room description to:',
         clearRoomDescription: 'cleared the room description',
+        changedRoomAvatar: 'changed the room avatar',
+        removedRoomAvatar: 'removed the room avatar',
     },
     delegate: {
         switchAccount: 'Switch accounts:',
diff --git a/src/languages/es.ts b/src/languages/es.ts
index 05f168b0369f..0a68a98750b4 100644
--- a/src/languages/es.ts
+++ b/src/languages/es.ts
@@ -7589,6 +7589,8 @@ ${amount} para ${merchant} - ${date}`,
     roomChangeLog: {
         updateRoomDescription: 'establece la descripción de la sala a:',
         clearRoomDescription: 'la descripción de la habitación ha sido borrada',
+        changedRoomAvatar: 'Cambió el avatar de la sala',
+        removedRoomAvatar: 'Eliminó el avatar de la sala',
     },
     delegate: {
         switchAccount: 'Cambiar de cuenta:',
diff --git a/src/languages/fr.ts b/src/languages/fr.ts
index 41041203c045..b4b9cf92229b 100644
--- a/src/languages/fr.ts
+++ b/src/languages/fr.ts
@@ -7148,6 +7148,8 @@ ${amount} pour ${merchant} - ${date}`,
     roomChangeLog: {
         updateRoomDescription: 'définir la description de la salle sur :',
         clearRoomDescription: 'effacé la description de la salle',
+        changedRoomAvatar: 'A changé l’avatar de la salle',
+        removedRoomAvatar: 'A supprimé l’avatar de la salle',
     },
     delegate: {
         switchAccount: 'Changer de compte :',
diff --git a/src/languages/it.ts b/src/languages/it.ts
index 947068e6ac30..2b0b16753ae8 100644
--- a/src/languages/it.ts
+++ b/src/languages/it.ts
@@ -7153,6 +7153,8 @@ ${amount} per ${merchant} - ${date}`,
     roomChangeLog: {
         updateRoomDescription: 'imposta la descrizione della stanza su:',
         clearRoomDescription: 'cancellato la descrizione della stanza',
+        changedRoomAvatar: "Ha cambiato l'avatar della stanza",
+        removedRoomAvatar: "Ha rimosso l'avatar della stanza",
     },
     delegate: {
         switchAccount: 'Cambia account:',
diff --git a/src/languages/ja.ts b/src/languages/ja.ts
index b21529a4a43d..bc5dd4aa22db 100644
--- a/src/languages/ja.ts
+++ b/src/languages/ja.ts
@@ -7082,6 +7082,8 @@ ${date} - ${merchant}に${amount}`,
     roomChangeLog: {
         updateRoomDescription: '部屋の説明を次のように設定します：',
         clearRoomDescription: '部屋の説明をクリアしました',
+        changedRoomAvatar: 'ルームのアバターを変更しました',
+        removedRoomAvatar: 'ルームのアバターを削除しました',
     },
     delegate: {
         switchAccount: 'アカウントを切り替える:',
diff --git a/src/languages/nl.ts b/src/languages/nl.ts
index 04dbe1ef57e8..fefb5971ee2b 100644
--- a/src/languages/nl.ts
+++ b/src/languages/nl.ts
@@ -7135,6 +7135,8 @@ ${amount} voor ${merchant} - ${date}`,
     roomChangeLog: {
         updateRoomDescription: 'stel de kamerbeschrijving in op:',
         clearRoomDescription: 'de kamerbeschrijving gewist',
+        changedRoomAvatar: 'De avatar van de kamer is gewijzigd',
+        removedRoomAvatar: 'De avatar van de kamer is verwijderd',
     },
     delegate: {
         switchAccount: 'Accounts wisselen:',
diff --git a/src/languages/pl.ts b/src/languages/pl.ts
index 9699057292ec..d92b7223ae1e 100644
--- a/src/languages/pl.ts
+++ b/src/languages/pl.ts
@@ -7123,6 +7123,8 @@ ${amount} dla ${merchant} - ${date}`,
     roomChangeLog: {
         updateRoomDescription: 'ustaw opis pokoju na:',
         clearRoomDescription: 'wyczyszczono opis pokoju',
+        changedRoomAvatar: 'Zmieniono awatar pokoju',
+        removedRoomAvatar: 'Usunięto awatar pokoju',
     },
     delegate: {
         switchAccount: 'Przełącz konta:',
diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts
index 7050ce68dc56..602e893bd8ef 100644
--- a/src/languages/pt-BR.ts
+++ b/src/languages/pt-BR.ts
@@ -7130,6 +7130,8 @@ ${amount} para ${merchant} - ${date}`,
     roomChangeLog: {
         updateRoomDescription: 'defina a descrição da sala para:',
         clearRoomDescription: 'limpou a descrição da sala',
+        changedRoomAvatar: 'Alterou o avatar da sala',
+        removedRoomAvatar: 'Removeu o avatar da sala',
     },
     delegate: {
         switchAccount: 'Alternar contas:',
diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts
index 2db0379d1000..8af25e6010ef 100644
--- a/src/languages/zh-hans.ts
+++ b/src/languages/zh-hans.ts
@@ -6985,6 +6985,8 @@ ${merchant}的${amount} - ${date}`,
     roomChangeLog: {
         updateRoomDescription: '将房间描述设置为：',
         clearRoomDescription: '清除了房间描述',
+        changedRoomAvatar: '更改了房间头像',
+        removedRoomAvatar: '移除了房间头像',
     },
     delegate: {
         switchAccount: '切换账户：',

From 9167f5ed25b360808b974977e0c014f7ee15f840 Mon Sep 17 00:00:00 2001
From: Samran Ahmed <samranahmed03@gmail.com>
Date: Thu, 30 Oct 2025 18:01:55 +0500
Subject: [PATCH 10/14] add getRoomAvatarUpdatedMessage util to get the message
 accordingly

---
 src/CONST/index.ts                              |  1 +
 .../API/parameters/UpdateRoomAvatarParams.ts    |  1 +
 src/libs/ReportActionsUtils.ts                  | 17 +++++++++++++++++
 src/types/onyx/OriginalMessage.ts               |  3 +++
 4 files changed, 22 insertions(+)

diff --git a/src/CONST/index.ts b/src/CONST/index.ts
index cf0c6368090a..86f634120491 100755
--- a/src/CONST/index.ts
+++ b/src/CONST/index.ts
@@ -1335,6 +1335,7 @@ const CONST = {
                     REMOVE_FROM_ROOM: 'REMOVEFROMROOM',
                     LEAVE_ROOM: 'LEAVEROOM',
                     UPDATE_ROOM_DESCRIPTION: 'UPDATEROOMDESCRIPTION',
+                    UPDATE_ROOM_AVATAR: 'UPDATEROOMAVATAR',
                 },
                 REJECTEDTRANSACTION_THREAD: 'REJECTEDTRANSACTION_THREAD',
                 REJECTED_TRANSACTION_MARKASRESOLVED: 'REJECTEDTRANSACTIONMARKASRESOLVED',
diff --git a/src/libs/API/parameters/UpdateRoomAvatarParams.ts b/src/libs/API/parameters/UpdateRoomAvatarParams.ts
index fadd66b4d84c..889d13e3d80d 100644
--- a/src/libs/API/parameters/UpdateRoomAvatarParams.ts
+++ b/src/libs/API/parameters/UpdateRoomAvatarParams.ts
@@ -3,6 +3,7 @@ import type {CustomRNImageManipulatorResult} from '@libs/cropOrRotateImage/types
 type UpdateRoomAvatarParams = {
     reportID: string;
     file: File | CustomRNImageManipulatorResult | undefined;
+    reportActionID: string;
 };
 
 export default UpdateRoomAvatarParams;
diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts
index e82026942aee..8fc56d03e7d1 100644
--- a/src/libs/ReportActionsUtils.ts
+++ b/src/libs/ReportActionsUtils.ts
@@ -1905,6 +1905,11 @@ function getReportActionMessageFragments(action: ReportAction): Message[] {
         return [{text: message, html: `<muted-text>${message}</muted-text>`, type: 'COMMENT'}];
     }
 
+    if (isActionOfType(action, CONST.REPORT.ACTIONS.TYPE.ROOM_CHANGE_LOG.UPDATE_ROOM_AVATAR)) {
+        const message = getRoomAvatarUpdatedMessage(action);
+        return [{text: message, html: `<muted-text>${message}</muted-text>`, type: 'COMMENT'}];
+    }
+
     if (isActionOfType(action, CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.UPDATE_DESCRIPTION)) {
         const message = getWorkspaceDescriptionUpdatedMessage(action);
         return [{text: message, html: `<muted-text>${message}</muted-text>`, type: 'COMMENT'}];
@@ -2262,6 +2267,17 @@ function getUpdateRoomDescriptionMessage(reportAction: ReportAction): string {
     return translateLocal('roomChangeLog.clearRoomDescription');
 }
 
+function getRoomAvatarUpdatedMessage(reportAction: ReportAction): string {
+    const originalMessage = getOriginalMessage(reportAction) as OriginalMessageChangeLog;
+    if (originalMessage?.avatarUrl) {
+        // eslint-disable-next-line @typescript-eslint/no-deprecated
+        return translateLocal('roomChangeLog.changedRoomAvatar');
+    }
+
+    // eslint-disable-next-line @typescript-eslint/no-deprecated
+    return translateLocal('roomChangeLog.removedRoomAvatar');
+}
+
 function getRetractedMessage(): string {
     // eslint-disable-next-line @typescript-eslint/no-deprecated
     return translateLocal('iou.retracted');
@@ -3406,6 +3422,7 @@ export {
     getExportIntegrationLastMessageText,
     getExportIntegrationMessageHTML,
     getUpdateRoomDescriptionMessage,
+    getRoomAvatarUpdatedMessage,
     didMessageMentionCurrentUser,
     getPolicyChangeLogAddEmployeeMessage,
     getPolicyChangeLogUpdateEmployee,
diff --git a/src/types/onyx/OriginalMessage.ts b/src/types/onyx/OriginalMessage.ts
index 8751b4c420d5..5b0a4ff326e3 100644
--- a/src/types/onyx/OriginalMessage.ts
+++ b/src/types/onyx/OriginalMessage.ts
@@ -367,6 +367,9 @@ type OriginalMessageChangeLog = {
 
     /** Name of the added category */
     categoryName?: string;
+
+    /** Avatar URL of workspace room */
+    avatarUrl?: string;
 };
 
 /** Model of change log */

From 4fc58be0c892de680c70665963816a0034de0ece Mon Sep 17 00:00:00 2001
From: Samran Ahmed <samranahmed03@gmail.com>
Date: Thu, 30 Oct 2025 18:07:49 +0500
Subject: [PATCH 11/14] build optimistic room avatar updated report action

---
 src/libs/ReportUtils.ts    | 38 ++++++++++++++++++++++++++++++++++++++
 src/libs/actions/Report.ts | 32 +++++++++++++++++++++++++++++++-
 2 files changed, 69 insertions(+), 1 deletion(-)

diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts
index 918f17295e2d..3b299c8ecf28 100644
--- a/src/libs/ReportUtils.ts
+++ b/src/libs/ReportUtils.ts
@@ -570,6 +570,11 @@ type OptimisticRoomDescriptionUpdatedReportAction = Pick<
     'actorAccountID' | 'created' | 'message' | 'person' | 'reportActionID' | 'pendingAction' | 'actionName' | 'originalMessage'
 >;
 
+type OptimisticRoomAvatarUpdatedReportAction = Pick<
+    ReportAction<typeof CONST.REPORT.ACTIONS.TYPE.ROOM_CHANGE_LOG.UPDATE_ROOM_AVATAR>,
+    'actorAccountID' | 'created' | 'message' | 'person' | 'reportActionID' | 'pendingAction' | 'actionName' | 'originalMessage'
+>;
+
 type OptimisticChatReport = Pick<
     Report,
     | 'type'
@@ -7709,6 +7714,38 @@ function buildOptimisticRoomDescriptionUpdatedReportAction(description: string):
     };
 }
 
+/**
+ * Returns the necessary reportAction onyx data to indicate that the room avatar has been updated
+ */
+function buildOptimisticRoomAvatarUpdatedReportAction(avatarUrl: string): OptimisticRoomAvatarUpdatedReportAction {
+    const now = DateUtils.getDBTime();
+    return {
+        reportActionID: rand64(),
+        actionName: CONST.REPORT.ACTIONS.TYPE.ROOM_CHANGE_LOG.UPDATE_ROOM_AVATAR,
+        pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD,
+        actorAccountID: currentUserAccountID,
+        message: [
+            {
+                type: CONST.REPORT.MESSAGE.TYPE.COMMENT,
+                text: avatarUrl ? `changed room avatar` : 'removed the room avatar',
+                html: avatarUrl ? `<muted-text>changed room avatar</muted-text>` : '<muted-text>removed the room avatar</muted-text>',
+            },
+        ],
+        person: [
+            {
+                type: CONST.REPORT.MESSAGE.TYPE.TEXT,
+                style: 'strong',
+                text: getCurrentUserDisplayNameOrEmail(),
+            },
+        ],
+        originalMessage: {
+            avatarUrl,
+            lastModified: now,
+        },
+        created: now,
+    };
+}
+
 /**
  * Returns the necessary reportAction onyx data to indicate that the transaction has been put on hold optimistically
  * @param [created] - Action created time
@@ -12247,6 +12284,7 @@ export {
     buildOptimisticChangePolicyReportAction,
     buildOptimisticRenamedRoomReportAction,
     buildOptimisticRoomDescriptionUpdatedReportAction,
+    buildOptimisticRoomAvatarUpdatedReportAction,
     buildOptimisticReportPreview,
     buildOptimisticActionableTrackExpenseWhisper,
     buildOptimisticSubmittedReportAction,
diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts
index 68c3e7a62f44..61c0135d2108 100644
--- a/src/libs/actions/Report.ts
+++ b/src/libs/actions/Report.ts
@@ -112,6 +112,7 @@ import {
     buildOptimisticMovedReportAction,
     buildOptimisticRenamedRoomReportAction,
     buildOptimisticReportPreview,
+    buildOptimisticRoomAvatarUpdatedReportAction,
     buildOptimisticRoomDescriptionUpdatedReportAction,
     buildOptimisticSelfDMReport,
     buildOptimisticUnHoldReportAction,
@@ -993,8 +994,37 @@ function updateGroupChatAvatar(reportID: string, file?: File | CustomRNImageMani
  * Updates the avatar for a policy room.
  */
 function updatePolicyRoomAvatar(reportID: string, file?: File | CustomRNImageManipulatorResult) {
+    const avatarUrl = file?.uri ?? '';
     const {optimisticData, successData, failureData} = buildUpdateReportAvatarOnyxData(reportID, file);
-    const parameters: UpdateRoomAvatarParams = {reportID, file};
+
+    const optimisticAction = buildOptimisticRoomAvatarUpdatedReportAction(avatarUrl);
+    optimisticData.push({
+        onyxMethod: Onyx.METHOD.MERGE,
+        key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`,
+        value: {[optimisticAction.reportActionID]: optimisticAction},
+    });
+
+    // Update the report with last action details
+    optimisticData.push({
+        onyxMethod: Onyx.METHOD.MERGE,
+        key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`,
+        value: {
+            lastActorAccountID: currentUserAccountID,
+            lastVisibleActionCreated: optimisticAction.created,
+            lastMessageText: (optimisticAction?.message as Message[])?.at(0)?.text,
+        },
+    });
+
+    // Add success data to clear pending action
+    successData.push({
+        onyxMethod: Onyx.METHOD.MERGE,
+        key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`,
+        value: {
+            [optimisticAction.reportActionID]: {pendingAction: null},
+        },
+    });
+
+    const parameters: UpdateRoomAvatarParams = {reportID, file, reportActionID: optimisticAction.reportActionID};
     API.write(WRITE_COMMANDS.UPDATE_ROOM_AVATAR, parameters, {optimisticData, failureData, successData});
 }
 

From 82325e811e26f55a3ec975b4cdbd464de6acd83b Mon Sep 17 00:00:00 2001
From: Samran Ahmed <samranahmed03@gmail.com>
Date: Thu, 30 Oct 2025 18:10:48 +0500
Subject: [PATCH 12/14] use getRoomAvatarUpdatedMessage to get alternate text
 and last message text from report

---
 src/libs/OptionsListUtils/index.ts                       | 3 +++
 src/libs/SidebarUtils.ts                                 | 3 +++
 src/pages/home/report/ContextMenu/ContextMenuActions.tsx | 3 +++
 3 files changed, 9 insertions(+)

diff --git a/src/libs/OptionsListUtils/index.ts b/src/libs/OptionsListUtils/index.ts
index c22c12fa3ffb..4a02b6c33622 100644
--- a/src/libs/OptionsListUtils/index.ts
+++ b/src/libs/OptionsListUtils/index.ts
@@ -51,6 +51,7 @@ import {
     getReportActionHtml,
     getReportActionMessageText,
     getRetractedMessage,
+    getRoomAvatarUpdatedMessage,
     getRoomChangeLogMessage,
     getSortedReportActions,
     getTravelUpdateMessage,
@@ -764,6 +765,8 @@ function getLastMessageTextForReport({
         lastMessageTextFromReport = translateLocal('violations.resolvedDuplicates');
     } else if (isActionOfType(lastReportAction, CONST.REPORT.ACTIONS.TYPE.ROOM_CHANGE_LOG.UPDATE_ROOM_DESCRIPTION)) {
         lastMessageTextFromReport = getUpdateRoomDescriptionMessage(lastReportAction);
+    } else if (isActionOfType(lastReportAction, CONST.REPORT.ACTIONS.TYPE.ROOM_CHANGE_LOG.UPDATE_ROOM_AVATAR)) {
+        lastMessageTextFromReport = getRoomAvatarUpdatedMessage(lastReportAction);
     } else if (isActionOfType(lastReportAction, CONST.REPORT.ACTIONS.TYPE.RETRACTED)) {
         lastMessageTextFromReport = getRetractedMessage();
     } else if (isActionOfType(lastReportAction, CONST.REPORT.ACTIONS.TYPE.REOPENED)) {
diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts
index 2ffe5bf060cd..44198e6fecc6 100644
--- a/src/libs/SidebarUtils.ts
+++ b/src/libs/SidebarUtils.ts
@@ -52,6 +52,7 @@ import {
     getReportAction,
     getReportActionMessageText,
     getRetractedMessage,
+    getRoomAvatarUpdatedMessage,
     getTagListNameUpdatedMessage,
     getTravelUpdateMessage,
     getUpdatedApprovalRuleMessage,
@@ -896,6 +897,8 @@ function getOptionData({
             result.alternateText = getMessageOfOldDotReportAction(lastAction);
         } else if (lastAction?.actionName === CONST.REPORT.ACTIONS.TYPE.ROOM_CHANGE_LOG.UPDATE_ROOM_DESCRIPTION) {
             result.alternateText = getUpdateRoomDescriptionMessage(lastAction);
+        } else if (lastAction?.actionName === CONST.REPORT.ACTIONS.TYPE.ROOM_CHANGE_LOG.UPDATE_ROOM_AVATAR) {
+            result.alternateText = getRoomAvatarUpdatedMessage(lastAction);
         } else if (lastAction?.actionName === CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.ADD_EMPLOYEE) {
             result.alternateText = getPolicyChangeLogAddEmployeeMessage(lastAction);
         } else if (lastAction?.actionName === CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.UPDATE_EMPLOYEE) {
diff --git a/src/pages/home/report/ContextMenu/ContextMenuActions.tsx b/src/pages/home/report/ContextMenu/ContextMenuActions.tsx
index 9c82262e6aba..df4ee4f59f6c 100644
--- a/src/pages/home/report/ContextMenu/ContextMenuActions.tsx
+++ b/src/pages/home/report/ContextMenu/ContextMenuActions.tsx
@@ -50,6 +50,7 @@ import {
     getRenamedAction,
     getReopenedMessage,
     getReportActionMessageText,
+    getRoomAvatarUpdatedMessage,
     getTagListNameUpdatedMessage,
     getTravelUpdateMessage,
     getUpdatedApprovalRuleMessage,
@@ -683,6 +684,8 @@ const ContextMenuActions: ContextMenuAction[] = [
                     setClipboardMessage(getExportIntegrationMessageHTML(reportAction));
                 } else if (reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.ROOM_CHANGE_LOG.UPDATE_ROOM_DESCRIPTION) {
                     setClipboardMessage(getUpdateRoomDescriptionMessage(reportAction));
+                } else if (reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.ROOM_CHANGE_LOG.UPDATE_ROOM_AVATAR) {
+                    setClipboardMessage(getRoomAvatarUpdatedMessage(reportAction));
                 } else if (reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.ADD_EMPLOYEE) {
                     setClipboardMessage(getPolicyChangeLogAddEmployeeMessage(reportAction));
                 } else if (reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.UPDATE_EMPLOYEE) {

From 24a56d4c2eeb9b929fa8cbfc2bf1eee06913cfc6 Mon Sep 17 00:00:00 2001
From: Samran Ahmed <samranahmed03@gmail.com>
Date: Thu, 30 Oct 2025 20:22:54 +0500
Subject: [PATCH 13/14] update canEditRoomAvatar to check current user in 
 participants

---
 src/components/RoomHeaderAvatars.tsx | 18 ++++++------------
 src/pages/ReportDetailsPage.tsx      |  1 +
 2 files changed, 7 insertions(+), 12 deletions(-)

diff --git a/src/components/RoomHeaderAvatars.tsx b/src/components/RoomHeaderAvatars.tsx
index 83eeb9b660dc..b8308ddd7720 100644
--- a/src/components/RoomHeaderAvatars.tsx
+++ b/src/components/RoomHeaderAvatars.tsx
@@ -1,13 +1,11 @@
 import React, {memo} from 'react';
 import {View} from 'react-native';
 import type {OnyxEntry} from 'react-native-onyx';
-import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails';
 import useStyleUtils from '@hooks/useStyleUtils';
 import useThemeStyles from '@hooks/useThemeStyles';
-import {clearAvatarErrors, updatePolicyRoomAvatar} from '@libs/actions/Report';
+import {clearAvatarErrors, getCurrentUserAccountID, updatePolicyRoomAvatar} from '@libs/actions/Report';
 import Navigation from '@libs/Navigation/Navigation';
-import {isPolicyMember} from '@libs/PolicyUtils';
-import {isAuditor, isReportParticipant, isUserCreatedPolicyRoom} from '@libs/ReportUtils';
+import {isUserCreatedPolicyRoom} from '@libs/ReportUtils';
 import {isDefaultAvatar} from '@libs/UserUtils';
 import CONST from '@src/CONST';
 import ROUTES from '@src/ROUTES';
@@ -23,9 +21,10 @@ type RoomHeaderAvatarsProps = {
     icons: Icon[];
     report: Report;
     policy: OnyxEntry<Policy>;
+    participants: number[];
 };
 
-function RoomHeaderAvatars({icons, report, policy}: RoomHeaderAvatarsProps) {
+function RoomHeaderAvatars({icons, report, policy, participants}: RoomHeaderAvatarsProps) {
     const navigateToAvatarPage = (icon: Icon) => {
         if (icon.type === CONST.ICON_TYPE_WORKSPACE && icon.id) {
             Navigation.navigate(ROUTES.REPORT_AVATAR.getRoute(report?.reportID, icon.id.toString()));
@@ -39,13 +38,8 @@ function RoomHeaderAvatars({icons, report, policy}: RoomHeaderAvatarsProps) {
 
     const styles = useThemeStyles();
     const StyleUtils = useStyleUtils();
-    const currentUserPersonalDetails = useCurrentUserPersonalDetails();
-
-    const canEditRoomAvatar =
-        isUserCreatedPolicyRoom(report) &&
-        isPolicyMember(policy, currentUserPersonalDetails?.login) &&
-        isReportParticipant(currentUserPersonalDetails?.accountID, report) &&
-        !isAuditor(report);
+    const currentUserAccountID = getCurrentUserAccountID();
+    const canEditRoomAvatar = isUserCreatedPolicyRoom(report) && participants.includes(currentUserAccountID) && !!policy && policy.role !== CONST.POLICY.ROLE.AUDITOR;
 
     if (!icons.length) {
         return null;
diff --git a/src/pages/ReportDetailsPage.tsx b/src/pages/ReportDetailsPage.tsx
index 33c3bbdddcc1..f9e30c166e6d 100644
--- a/src/pages/ReportDetailsPage.tsx
+++ b/src/pages/ReportDetailsPage.tsx
@@ -599,6 +599,7 @@ function ReportDetailsPage({policy, report, route, reportMetadata}: ReportDetail
                         icons={icons}
                         report={report}
                         policy={policy}
+                        participants={participants}
                     />
                 </View>
             );

From 332d571154d8ef22e92df864b3ff1eebdb919ff7 Mon Sep 17 00:00:00 2001
From: Samran Ahmed <samranahmed03@gmail.com>
Date: Thu, 30 Oct 2025 22:33:44 +0500
Subject: [PATCH 14/14] clear pending action from failure data

---
 src/libs/ReportActionsUtils.ts    |  2 +-
 src/libs/ReportUtils.ts           |  8 ++++----
 src/libs/actions/Report.ts        | 16 +++++++++++++---
 src/types/onyx/OriginalMessage.ts |  2 +-
 4 files changed, 19 insertions(+), 9 deletions(-)

diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts
index 8fc56d03e7d1..5b1e940e3446 100644
--- a/src/libs/ReportActionsUtils.ts
+++ b/src/libs/ReportActionsUtils.ts
@@ -2269,7 +2269,7 @@ function getUpdateRoomDescriptionMessage(reportAction: ReportAction): string {
 
 function getRoomAvatarUpdatedMessage(reportAction: ReportAction): string {
     const originalMessage = getOriginalMessage(reportAction) as OriginalMessageChangeLog;
-    if (originalMessage?.avatarUrl) {
+    if (originalMessage?.avatarURL) {
         // eslint-disable-next-line @typescript-eslint/no-deprecated
         return translateLocal('roomChangeLog.changedRoomAvatar');
     }
diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts
index 3b299c8ecf28..d5583e4ede62 100644
--- a/src/libs/ReportUtils.ts
+++ b/src/libs/ReportUtils.ts
@@ -7717,7 +7717,7 @@ function buildOptimisticRoomDescriptionUpdatedReportAction(description: string):
 /**
  * Returns the necessary reportAction onyx data to indicate that the room avatar has been updated
  */
-function buildOptimisticRoomAvatarUpdatedReportAction(avatarUrl: string): OptimisticRoomAvatarUpdatedReportAction {
+function buildOptimisticRoomAvatarUpdatedReportAction(avatarURL: string): OptimisticRoomAvatarUpdatedReportAction {
     const now = DateUtils.getDBTime();
     return {
         reportActionID: rand64(),
@@ -7727,8 +7727,8 @@ function buildOptimisticRoomAvatarUpdatedReportAction(avatarUrl: string): Optimi
         message: [
             {
                 type: CONST.REPORT.MESSAGE.TYPE.COMMENT,
-                text: avatarUrl ? `changed room avatar` : 'removed the room avatar',
-                html: avatarUrl ? `<muted-text>changed room avatar</muted-text>` : '<muted-text>removed the room avatar</muted-text>',
+                text: avatarURL ? `changed the room avatar` : 'removed the room avatar',
+                html: avatarURL ? `<muted-text>changed the room avatar</muted-text>` : '<muted-text>removed the room avatar</muted-text>',
             },
         ],
         person: [
@@ -7739,7 +7739,7 @@ function buildOptimisticRoomAvatarUpdatedReportAction(avatarUrl: string): Optimi
             },
         ],
         originalMessage: {
-            avatarUrl,
+            avatarURL,
             lastModified: now,
         },
         created: now,
diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts
index 61c0135d2108..0414953ff417 100644
--- a/src/libs/actions/Report.ts
+++ b/src/libs/actions/Report.ts
@@ -994,10 +994,10 @@ function updateGroupChatAvatar(reportID: string, file?: File | CustomRNImageMani
  * Updates the avatar for a policy room.
  */
 function updatePolicyRoomAvatar(reportID: string, file?: File | CustomRNImageManipulatorResult) {
-    const avatarUrl = file?.uri ?? '';
+    const avatarURL = file?.uri ?? '';
     const {optimisticData, successData, failureData} = buildUpdateReportAvatarOnyxData(reportID, file);
 
-    const optimisticAction = buildOptimisticRoomAvatarUpdatedReportAction(avatarUrl);
+    const optimisticAction = buildOptimisticRoomAvatarUpdatedReportAction(avatarURL);
     optimisticData.push({
         onyxMethod: Onyx.METHOD.MERGE,
         key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`,
@@ -1011,7 +1011,7 @@ function updatePolicyRoomAvatar(reportID: string, file?: File | CustomRNImageMan
         value: {
             lastActorAccountID: currentUserAccountID,
             lastVisibleActionCreated: optimisticAction.created,
-            lastMessageText: (optimisticAction?.message as Message[])?.at(0)?.text,
+            lastMessageText: (optimisticAction.message as Message[]).at(0)?.text,
         },
     });
 
@@ -1024,6 +1024,16 @@ function updatePolicyRoomAvatar(reportID: string, file?: File | CustomRNImageMan
         },
     });
 
+    failureData.push({
+        onyxMethod: Onyx.METHOD.MERGE,
+        key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`,
+        value: {
+            [optimisticAction.reportActionID]: {
+                pendingAction: null,
+            },
+        },
+    });
+
     const parameters: UpdateRoomAvatarParams = {reportID, file, reportActionID: optimisticAction.reportActionID};
     API.write(WRITE_COMMANDS.UPDATE_ROOM_AVATAR, parameters, {optimisticData, failureData, successData});
 }
diff --git a/src/types/onyx/OriginalMessage.ts b/src/types/onyx/OriginalMessage.ts
index 5b0a4ff326e3..e86af9b86791 100644
--- a/src/types/onyx/OriginalMessage.ts
+++ b/src/types/onyx/OriginalMessage.ts
@@ -369,7 +369,7 @@ type OriginalMessageChangeLog = {
     categoryName?: string;
 
     /** Avatar URL of workspace room */
-    avatarUrl?: string;
+    avatarURL?: string;
 };
 
 /** Model of change log */
