From ec2a665ab732485226b03d85a37ecd5f278ed0dd Mon Sep 17 00:00:00 2001 From: Linh Date: Sun, 11 Jan 2026 15:33:04 +0700 Subject: [PATCH 1/9] chore: remove currentUserAccountID from Onyx --- src/components/ArchivedReportFooter.tsx | 7 +- src/components/PromotedActionsBar.tsx | 14 ++-- src/components/RoomHeaderAvatars.tsx | 10 +-- .../Share/ShareTabParticipantsSelector.tsx | 4 +- src/libs/actions/Report.ts | 84 ++++++++++--------- src/pages/ProfilePage.tsx | 7 +- src/pages/ReportDetailsPage.tsx | 10 ++- src/pages/RoomDescriptionPage.tsx | 7 +- src/pages/Share/ShareTab.tsx | 5 +- src/pages/home/HeaderView.tsx | 4 +- src/pages/home/ReportScreen.tsx | 4 +- .../BaseReportActionContextMenu.tsx | 4 +- .../report/ContextMenu/ContextMenuActions.tsx | 21 ++--- .../home/report/PureReportActionItem.tsx | 7 +- .../ComposerWithSuggestions.tsx | 9 +- src/pages/home/report/ReportActionItem.tsx | 3 +- src/pages/home/report/ReportFooter.tsx | 7 +- .../home/report/UserTypingEventListener.tsx | 6 +- .../Report/NotificationPreferencePage.tsx | 6 +- 19 files changed, 126 insertions(+), 93 deletions(-) diff --git a/src/components/ArchivedReportFooter.tsx b/src/components/ArchivedReportFooter.tsx index 260171e580b8..ff472f330841 100644 --- a/src/components/ArchivedReportFooter.tsx +++ b/src/components/ArchivedReportFooter.tsx @@ -4,7 +4,6 @@ import React from 'react'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useThemeStyles from '@hooks/useThemeStyles'; -import {getCurrentUserAccountID} from '@libs/actions/Report'; import {getDisplayNameOrDefault} from '@libs/PersonalDetailsUtils'; import {getOriginalMessage, isClosedAction} from '@libs/ReportActionsUtils'; import {getPolicyName} from '@libs/ReportUtils'; @@ -17,9 +16,11 @@ import Banner from './Banner'; type ArchivedReportFooterProps = { /** The archived report */ report: Report; + /** Current user's account id */ + currentUserAccountID: number; }; -function ArchivedReportFooter({report}: ArchivedReportFooterProps) { +function ArchivedReportFooter({report, currentUserAccountID}: ArchivedReportFooterProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); @@ -57,7 +58,7 @@ function ArchivedReportFooter({report}: ArchivedReportFooterProps) { displayName: `${displayName}`, oldDisplayName: `${oldDisplayName}`, policyName: `${policyName}`, - shouldUseYou: actorPersonalDetails?.accountID === getCurrentUserAccountID(), + shouldUseYou: actorPersonalDetails?.accountID === currentUserAccountID, }) : translate(`reportArchiveReasons.${archiveReason}`); diff --git a/src/components/PromotedActionsBar.tsx b/src/components/PromotedActionsBar.tsx index 5baf40ffb556..f077ffabc586 100644 --- a/src/components/PromotedActionsBar.tsx +++ b/src/components/PromotedActionsBar.tsx @@ -19,12 +19,14 @@ type PromotedAction = { key: string; } & ThreeDotsMenuItem; -type BasePromotedActions = typeof CONST.PROMOTED_ACTIONS.PIN | typeof CONST.PROMOTED_ACTIONS.JOIN; +type BasePromotedActions = typeof CONST.PROMOTED_ACTIONS.PIN; type PromotedActionsType = Record PromotedAction> & { [CONST.PROMOTED_ACTIONS.SHARE]: (report: OnyxReport, backTo?: string) => PromotedAction; } & { - [CONST.PROMOTED_ACTIONS.MESSAGE]: (params: {reportID?: string; accountID?: number; login?: string}) => PromotedAction; + [CONST.PROMOTED_ACTIONS.MESSAGE]: (params: {reportID?: string; accountID?: number; login?: string; currentUserAccountID: number}) => PromotedAction; +} & { + [CONST.PROMOTED_ACTIONS.JOIN]: (report: OnyxReport, currentUserAccountID: number) => PromotedAction; }; type PromotedActionsBarProps = { @@ -44,16 +46,16 @@ const PromotedActions = { key: CONST.PROMOTED_ACTIONS.SHARE, ...getShareMenuItem(report, backTo), }), - join: (report) => ({ + join: (report, currentUserAccountID) => ({ key: CONST.PROMOTED_ACTIONS.JOIN, icon: 'ChatBubbles', translationKey: 'common.join', onSelected: callFunctionIfActionIsAllowed(() => { Navigation.dismissModal(); - joinRoom(report); + joinRoom(report, currentUserAccountID); }), }), - message: ({reportID, accountID, login}) => ({ + message: ({reportID, accountID, login, currentUserAccountID}) => ({ key: CONST.PROMOTED_ACTIONS.MESSAGE, icon: 'CommentBubbles', translationKey: 'common.message', @@ -69,7 +71,7 @@ const PromotedActions = { return; } if (accountID) { - navigateToAndOpenReportWithAccountIDs([accountID]); + navigateToAndOpenReportWithAccountIDs([accountID], currentUserAccountID); } }, }), diff --git a/src/components/RoomHeaderAvatars.tsx b/src/components/RoomHeaderAvatars.tsx index a56a4f7f5a60..dde60ec66b70 100644 --- a/src/components/RoomHeaderAvatars.tsx +++ b/src/components/RoomHeaderAvatars.tsx @@ -4,7 +4,7 @@ import type {OnyxEntry} from 'react-native-onyx'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; -import {clearAvatarErrors, getCurrentUserAccountID, updatePolicyRoomAvatar} from '@libs/actions/Report'; +import {clearAvatarErrors, updatePolicyRoomAvatar} from '@libs/actions/Report'; import Navigation from '@libs/Navigation/Navigation'; import {isUserCreatedPolicyRoom} from '@libs/ReportUtils'; import {isDefaultAvatar} from '@libs/UserAvatarUtils'; @@ -22,9 +22,10 @@ type RoomHeaderAvatarsProps = { report: Report; policy: OnyxEntry; participants: number[]; + currentUserAccountID: number; }; -function RoomHeaderAvatars({icons, report, policy, participants}: RoomHeaderAvatarsProps) { +function RoomHeaderAvatars({icons, report, policy, participants, currentUserAccountID}: 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,7 +40,6 @@ function RoomHeaderAvatars({icons, report, policy, participants}: RoomHeaderAvat const expensifyIcons = useMemoizedLazyExpensifyIcons(['Camera', 'FallbackAvatar', 'ImageCropSquareMask']); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); - const currentUserAccountID = getCurrentUserAccountID(); const canEditRoomAvatar = isUserCreatedPolicyRoom(report) && participants.includes(currentUserAccountID) && !!policy && policy.role !== CONST.POLICY.ROLE.AUDITOR; if (!icons.length) { @@ -62,8 +62,8 @@ function RoomHeaderAvatars({icons, report, policy, participants}: RoomHeaderAvat 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)} + onImageRemoved={() => updatePolicyRoomAvatar(report.reportID, currentUserAccountID)} + onImageSelected={(file) => updatePolicyRoomAvatar(report.reportID, currentUserAccountID, file)} editIcon={expensifyIcons.Camera} editIconStyle={styles.smallEditIconAccount} pendingAction={report.pendingFields?.avatar} diff --git a/src/components/Share/ShareTabParticipantsSelector.tsx b/src/components/Share/ShareTabParticipantsSelector.tsx index ddea87c8d2e4..c1727f0f269a 100644 --- a/src/components/Share/ShareTabParticipantsSelector.tsx +++ b/src/components/Share/ShareTabParticipantsSelector.tsx @@ -1,5 +1,6 @@ import type {Ref} from 'react'; import React from 'react'; +import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import {clearMoneyRequest} from '@libs/actions/IOU'; import {saveUnknownUserDetails} from '@libs/actions/Share'; import Navigation from '@libs/Navigation/Navigation'; @@ -18,6 +19,7 @@ type InputFocusRef = { }; function ShareTabParticipantsSelectorComponent({detailsPageRouteObject, ref}: ShareTabParticipantsSelectorProps) { + const {accountID: currentUserAccountID} = useCurrentUserPersonalDetails(); return ( { diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 94d5c271c091..27934546b589 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -407,7 +407,7 @@ function getNormalizedStatus(typingStatus: UserIsTypingEvent | UserIsLeavingRoom } /** Initialize our pusher subscriptions to listen for someone typing in a report. */ -function subscribeToReportTypingEvents(reportID: string) { +function subscribeToReportTypingEvents(reportID: string, currentUserAccountID: number) { if (!reportID) { return; } @@ -428,7 +428,7 @@ function subscribeToReportTypingEvents(reportID: string) { } // Don't show the typing indicator if the user is typing on another platform - if (Number(accountIDOrLogin) === deprecatedCurrentUserAccountID) { + if (Number(accountIDOrLogin) === currentUserAccountID) { return; } @@ -457,7 +457,7 @@ function subscribeToReportTypingEvents(reportID: string) { } /** Initialize our pusher subscriptions to listen for someone leaving a room. */ -function subscribeToReportLeavingEvents(reportID: string | undefined) { +function subscribeToReportLeavingEvents(reportID: string | undefined, currentUserAccountID: number) { if (!reportID) { return; } @@ -477,7 +477,7 @@ function subscribeToReportLeavingEvents(reportID: string | undefined) { return; } - if (Number(accountIDOrLogin) !== deprecatedCurrentUserAccountID) { + if (Number(accountIDOrLogin) !== currentUserAccountID) { return; } @@ -888,7 +888,7 @@ function updateGroupChatAvatar(reportID: string, file?: File | CustomRNImageMani /** * Updates the avatar for a policy room. */ -function updatePolicyRoomAvatar(reportID: string, file?: File | CustomRNImageManipulatorResult) { +function updatePolicyRoomAvatar(reportID: string, currentUserAccountID: number, file?: File | CustomRNImageManipulatorResult) { const avatarURL = file?.uri ?? ''; const {optimisticData, successData, failureData} = buildUpdateReportAvatarOnyxData(reportID, file); @@ -904,7 +904,7 @@ function updatePolicyRoomAvatar(reportID: string, file?: File | CustomRNImageMan onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, value: { - lastActorAccountID: deprecatedCurrentUserAccountID, + lastActorAccountID: currentUserAccountID, lastVisibleActionCreated: optimisticAction.created, lastMessageText: (optimisticAction.message as Message[]).at(0)?.text, }, @@ -1407,9 +1407,9 @@ function prepareOnyxDataForCleanUpOptimisticParticipants( * This will return an optimistic report object for a given user we want to create a chat with without saving it, when the only thing we know about recipient is his accountID. * * @param accountID accountID of the user that the optimistic chat report is created with. */ -function getOptimisticChatReport(accountID: number): OptimisticChatReport { +function getOptimisticChatReport(accountID: number, currentUserAccountID: number): OptimisticChatReport { return buildOptimisticChatReport({ - participantList: [accountID, deprecatedCurrentUserAccountID], + participantList: [accountID, currentUserAccountID], notificationPreference: CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS, }); } @@ -1536,12 +1536,12 @@ function navigateToAndOpenReport( * * @param participantAccountIDs of user logins to start a chat report with. */ -function navigateToAndOpenReportWithAccountIDs(participantAccountIDs: number[]) { +function navigateToAndOpenReportWithAccountIDs(participantAccountIDs: number[], currentUserAccountID: number) { let newChat: OptimisticChatReport | undefined; - const chat = getChatByParticipants([...participantAccountIDs, deprecatedCurrentUserAccountID]); + const chat = getChatByParticipants([...participantAccountIDs, currentUserAccountID]); if (!chat) { newChat = buildOptimisticChatReport({ - participantList: [...participantAccountIDs, deprecatedCurrentUserAccountID], + participantList: [...participantAccountIDs, currentUserAccountID], }); // We want to pass newChat here because if anything is passed in that param (even an existing chat), we will try to create a chat on the server openReport(newChat?.reportID, '', [], newChat, '0', false, participantAccountIDs); @@ -1823,7 +1823,7 @@ function markAllMessagesAsRead(archivedReportsIdSet: ArchivedReportsIDSet) { /** * Sets the last read time on a report */ -function markCommentAsUnread(reportID: string | undefined, reportAction: ReportAction) { +function markCommentAsUnread(reportID: string | undefined, reportAction: ReportAction, currentUserAccountID: number) { if (!reportID) { Log.warn('7339cd6c-3263-4f89-98e5-730f0be15784 Invalid report passed to MarkCommentAsUnread. Not calling the API because it wil fail.'); return; @@ -1835,7 +1835,7 @@ function markCommentAsUnread(reportID: string | undefined, reportAction: ReportA const latestReportActionFromOtherUsers = Object.values(reportActions ?? {}).reduce((latest: ReportAction | null, current: ReportAction) => { if ( !ReportActionsUtils.isDeletedAction(current) && - current.actorAccountID !== deprecatedCurrentUserAccountID && + current.actorAccountID !== currentUserAccountID && (!latest || current.created > latest.created) && // Whisper action doesn't affect lastVisibleActionCreated, so skip whisper action except actionable mention whisper (!ReportActionsUtils.isWhisperAction(current) || current.actionName === CONST.REPORT.ACTIONS.TYPE.ACTIONABLE_MENTION_WHISPER) @@ -1920,19 +1920,19 @@ function saveReportDraftComment(reportID: string, comment: string | null, callba } /** Broadcasts whether or not a user is typing on a report over the report's private pusher channel. */ -function broadcastUserIsTyping(reportID: string) { +function broadcastUserIsTyping(reportID: string, currentUserAccountID: number) { const privateReportChannelName = getReportChannelName(reportID); const typingStatus: UserIsTypingEvent = { - [deprecatedCurrentUserAccountID]: true, + [currentUserAccountID]: true, }; Pusher.sendEvent(privateReportChannelName, Pusher.TYPE.USER_IS_TYPING, typingStatus); } /** Broadcasts to the report's private pusher channel whether a user is leaving a report */ -function broadcastUserIsLeavingRoom(reportID: string) { +function broadcastUserIsLeavingRoom(reportID: string, currentUserAccountID: number) { const privateReportChannelName = getReportChannelName(reportID); const leavingStatus: UserIsLeavingRoomEvent = { - [deprecatedCurrentUserAccountID]: true, + [currentUserAccountID]: true, }; Pusher.sendEvent(privateReportChannelName, Pusher.TYPE.USER_IS_LEAVING_ROOM, leavingStatus); } @@ -2355,6 +2355,7 @@ function updateNotificationPreference( reportID: string, previousValue: NotificationPreference | undefined, newValue: NotificationPreference, + currentUserAccountID: number, parentReportID?: string, parentReportActionID?: string, ) { @@ -2369,7 +2370,7 @@ function updateNotificationPreference( key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, value: { participants: { - [deprecatedCurrentUserAccountID]: { + [currentUserAccountID]: { notificationPreference: newValue, }, }, @@ -2383,7 +2384,7 @@ function updateNotificationPreference( key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, value: { participants: { - [deprecatedCurrentUserAccountID]: { + [currentUserAccountID]: { notificationPreference: previousValue, }, }, @@ -2445,6 +2446,7 @@ function updateRoomVisibility(reportID: string, previousValue: RoomVisibility | */ function toggleSubscribeToChildReport( childReportID: string | undefined, + currentUserAccountID: number, parentReportAction: Partial = {}, parentReportID?: string, prevNotificationPreference?: NotificationPreference, @@ -2453,12 +2455,12 @@ function toggleSubscribeToChildReport( openReport(childReportID); const parentReportActionID = parentReportAction?.reportActionID; if (!prevNotificationPreference || isHiddenForCurrentUser(prevNotificationPreference)) { - updateNotificationPreference(childReportID, prevNotificationPreference, CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS, parentReportID, parentReportActionID); + updateNotificationPreference(childReportID, prevNotificationPreference, CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS, currentUserAccountID, parentReportID, parentReportActionID); } else { - updateNotificationPreference(childReportID, prevNotificationPreference, CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN, parentReportID, parentReportActionID); + updateNotificationPreference(childReportID, prevNotificationPreference, CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN, currentUserAccountID, parentReportID, parentReportActionID); } } else { - const participantAccountIDs = [...new Set([deprecatedCurrentUserAccountID, Number(parentReportAction?.actorAccountID)])]; + const participantAccountIDs = [...new Set([currentUserAccountID, Number(parentReportAction?.actorAccountID)])]; const parentReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${parentReportID}`]; const newChat = buildOptimisticChatReport({ participantList: participantAccountIDs, @@ -2474,7 +2476,7 @@ function toggleSubscribeToChildReport( const participantLogins = PersonalDetailsUtils.getLoginsByAccountIDs(participantAccountIDs); openReport(newChat.reportID, '', participantLogins, newChat, parentReportAction.reportActionID); const notificationPreference = isHiddenForCurrentUser(prevNotificationPreference) ? CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS : CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN; - updateNotificationPreference(newChat.reportID, prevNotificationPreference, notificationPreference, parentReportID, parentReportAction?.reportActionID); + updateNotificationPreference(newChat.reportID, prevNotificationPreference, notificationPreference, currentUserAccountID, parentReportID, parentReportAction?.reportActionID); } } @@ -2776,7 +2778,7 @@ function deleteReportField(reportID: string, reportField: PolicyReportField) { API.write(WRITE_COMMANDS.DELETE_REPORT_FIELD, parameters, {optimisticData, failureData, successData}); } -function updateDescription(reportID: string, currentDescription: string, newMarkdownValue: string) { +function updateDescription(reportID: string, currentDescription: string, newMarkdownValue: string, currentUserAccountID: number) { // No change needed if (Parser.htmlToMarkdown(currentDescription) === newMarkdownValue) { return; @@ -2793,7 +2795,7 @@ function updateDescription(reportID: string, currentDescription: string, newMark value: { description: parsedDescription, pendingFields: {description: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}, - lastActorAccountID: deprecatedCurrentUserAccountID, + lastActorAccountID: currentUserAccountID, lastVisibleActionCreated: optimisticDescriptionUpdatedReportAction.created, lastMessageText: (optimisticDescriptionUpdatedReportAction?.message as Message[])?.at(0)?.text, }, @@ -3536,7 +3538,7 @@ function clearIOUError(reportID: string | undefined) { * Adds a reaction to the report action. * Uses the NEW FORMAT for "emojiReactions" */ -function addEmojiReaction(reportID: string, reportActionID: string, emoji: Emoji, skinTone: number) { +function addEmojiReaction(reportID: string, reportActionID: string, emoji: Emoji, skinTone: number, currentUserAccountID: number) { const createdAt = timezoneFormat(toZonedTime(new Date(), 'UTC'), CONST.DATE.FNS_DB_FORMAT_STRING); const optimisticData: Array> = [ { @@ -3547,7 +3549,7 @@ function addEmojiReaction(reportID: string, reportActionID: string, emoji: Emoji createdAt, pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, users: { - [deprecatedCurrentUserAccountID]: { + [currentUserAccountID]: { skinTones: { [skinTone]: createdAt, }, @@ -3631,6 +3633,7 @@ function toggleEmojiReaction( reactionObject: Emoji, existingReactions: OnyxEntry, paramSkinTone: number, + currentUserAccountID: number, ignoreSkinToneOnCompare = false, ) { const originalReportID = getOriginalReportID(reportID, reportAction); @@ -3653,12 +3656,12 @@ function toggleEmojiReaction( // Only use skin tone if emoji supports it const skinTone = emoji.types === undefined ? CONST.EMOJI_DEFAULT_SKIN_TONE : paramSkinTone; - if (existingReactionObject && EmojiUtils.hasAccountIDEmojiReacted(deprecatedCurrentUserAccountID, existingReactionObject.users, ignoreSkinToneOnCompare ? undefined : skinTone)) { + if (existingReactionObject && EmojiUtils.hasAccountIDEmojiReacted(currentUserAccountID, existingReactionObject.users, ignoreSkinToneOnCompare ? undefined : skinTone)) { removeEmojiReaction(originalReportID, reportAction.reportActionID, emoji); return; } - addEmojiReaction(originalReportID, reportAction.reportActionID, emoji, skinTone); + addEmojiReaction(originalReportID, reportAction.reportActionID, emoji, skinTone, currentUserAccountID); } function doneCheckingPublicRoom() { @@ -3692,7 +3695,7 @@ function getMostRecentReportID(currentReport: OnyxEntry) { return lastAccessedReportID ?? conciergeReportID; } -function joinRoom(report: OnyxEntry) { +function joinRoom(report: OnyxEntry, currentUserAccountID: number) { if (!report) { return; } @@ -3700,6 +3703,7 @@ function joinRoom(report: OnyxEntry) { report.reportID, getReportNotificationPreference(report), getDefaultNotificationPreferenceForReport(report), + currentUserAccountID, report.parentReportID, report.parentReportActionID, ); @@ -3762,7 +3766,7 @@ function leaveGroupChat(reportID: string, shouldClearQuickAction: boolean) { } /** Leave a report by setting the state to submitted and closed */ -function leaveRoom(reportID: string, isWorkspaceMemberLeavingWorkspaceRoom = false) { +function leaveRoom(reportID: string, currentUserAccountID: number, isWorkspaceMemberLeavingWorkspaceRoom = false) { const report = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; if (!report) { @@ -3773,7 +3777,7 @@ function leaveRoom(reportID: string, isWorkspaceMemberLeavingWorkspaceRoom = fal // Pusher's leavingStatus should be sent earlier. // Place the broadcast before calling the LeaveRoom API to prevent a race condition // between Onyx report being null and Pusher's leavingStatus becoming true. - broadcastUserIsLeavingRoom(reportID); + broadcastUserIsLeavingRoom(reportID, currentUserAccountID); // If a workspace member is leaving a workspace room, they don't actually lose the room from Onyx. // Instead, their notification preference just gets set to "hidden". @@ -3786,7 +3790,7 @@ function leaveRoom(reportID: string, isWorkspaceMemberLeavingWorkspaceRoom = fal isWorkspaceMemberLeavingWorkspaceRoom || isChatThread ? { participants: { - [deprecatedCurrentUserAccountID]: { + [currentUserAccountID]: { notificationPreference: CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN, }, }, @@ -3796,7 +3800,7 @@ function leaveRoom(reportID: string, isWorkspaceMemberLeavingWorkspaceRoom = fal stateNum: CONST.REPORT.STATE_NUM.APPROVED, statusNum: CONST.REPORT.STATUS_NUM.CLOSED, participants: { - [deprecatedCurrentUserAccountID]: { + [currentUserAccountID]: { notificationPreference: CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN, }, }, @@ -3811,7 +3815,7 @@ function leaveRoom(reportID: string, isWorkspaceMemberLeavingWorkspaceRoom = fal key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, value: { participants: { - [deprecatedCurrentUserAccountID]: { + [currentUserAccountID]: { notificationPreference: CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN, }, }, @@ -3851,7 +3855,7 @@ function leaveRoom(reportID: string, isWorkspaceMemberLeavingWorkspaceRoom = fal key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.parentReportID}`, value: { [report.parentReportActionID]: { - childReportNotificationPreference: report?.participants?.[deprecatedCurrentUserAccountID]?.notificationPreference ?? getDefaultNotificationPreferenceForReport(report), + childReportNotificationPreference: report?.participants?.[currentUserAccountID]?.notificationPreference ?? getDefaultNotificationPreferenceForReport(report), }, }, }); @@ -5737,7 +5741,7 @@ function navigateToTrainingModal(isChangePolicyTrainingModalDismissed: boolean, function buildOptimisticChangePolicyData( report: Report, policy: Policy, - accountID: number, + currentUserAccountID: number, email: string, hasViolationsParam: boolean, isASAPSubmitBetaEnabled: boolean, @@ -5886,7 +5890,7 @@ function buildOptimisticChangePolicyData( if (newStatusNum === CONST.REPORT.STATUS_NUM.OPEN) { shouldSetOutstandingChildRequest = isCurrentUserSubmitter(report); } else if (isProcessingReport(report)) { - shouldSetOutstandingChildRequest = report.managerID === deprecatedCurrentUserAccountID; + shouldSetOutstandingChildRequest = report.managerID === currentUserAccountID; } } @@ -6231,7 +6235,7 @@ function changeReportPolicy( function changeReportPolicyAndInviteSubmitter( report: Report, policy: Policy, - accountID: number, + currentUserAccountID: number, email: string, hasViolationsParam: boolean, isChangePolicyTrainingModalDismissed: boolean, @@ -6274,7 +6278,7 @@ function changeReportPolicyAndInviteSubmitter( } = buildOptimisticChangePolicyData( report, policy, - accountID, + currentUserAccountID, email, hasViolationsParam, isASAPSubmitBetaEnabled, diff --git a/src/pages/ProfilePage.tsx b/src/pages/ProfilePage.tsx index 3680549deb34..b0812560f4b0 100755 --- a/src/pages/ProfilePage.tsx +++ b/src/pages/ProfilePage.tsx @@ -16,6 +16,7 @@ import PromotedActionsBar, {PromotedActions} from '@components/PromotedActionsBa import ScreenWrapper from '@components/ScreenWrapper'; import ScrollView from '@components/ScrollView'; import Text from '@components/Text'; +import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; @@ -85,7 +86,7 @@ function ProfilePage({route}: ProfilePageProps) { } return `${ONYXKEYS.COLLECTION.REPORT}${reportID}` as const; }, [accountID, isCurrentUser, reports, session?.accountID]); - + const {accountID: currentUserAccountID} = useCurrentUserPersonalDetails(); const [report] = useOnyx(reportKey, {canBeMissing: true}); const styles = useThemeStyles(); @@ -168,10 +169,10 @@ function ProfilePage({route}: ProfilePageProps) { // If it's a self DM, we only want to show the Message button if the self DM report exists because we don't want to optimistically create a report for self DM if ((!isCurrentUser || report) && !isAnonymousUserSession()) { - result.push(PromotedActions.message({reportID: report?.reportID, accountID, login: loginParams})); + result.push(PromotedActions.message({reportID: report?.reportID, accountID, login: loginParams, currentUserAccountID})); } return result; - }, [accountID, isCurrentUser, loginParams, report]); + }, [accountID, isCurrentUser, loginParams, report, currentUserAccountID]); return ( diff --git a/src/pages/ReportDetailsPage.tsx b/src/pages/ReportDetailsPage.tsx index 2cd9b591b64b..961d87452893 100644 --- a/src/pages/ReportDetailsPage.tsx +++ b/src/pages/ReportDetailsPage.tsx @@ -332,9 +332,9 @@ function ReportDetailsPage({policy, report, route, reportMetadata}: ReportDetail return; } const isWorkspaceMemberLeavingWorkspaceRoom = isWorkspaceMemberLeavingWorkspaceRoomUtil(report, isPolicyEmployee, isPolicyAdmin); - leaveRoom(report.reportID, isWorkspaceMemberLeavingWorkspaceRoom); + leaveRoom(report.reportID, currentUserPersonalDetails.accountID, isWorkspaceMemberLeavingWorkspaceRoom); }); - }, [isRootGroupChat, isPolicyEmployee, isPolicyAdmin, quickAction?.chatReportID, report]); + }, [isRootGroupChat, isPolicyEmployee, isPolicyAdmin, quickAction?.chatReportID, report, currentUserPersonalDetails.accountID]); const shouldShowLeaveButton = canLeaveChat(report, policy, !!reportNameValuePairs?.private_isArchived); const shouldShowGoToWorkspace = shouldShowPolicy(policy, false, currentUserPersonalDetails?.email) && !policy?.isJoinRequestPending; @@ -616,6 +616,7 @@ function ReportDetailsPage({policy, report, route, reportMetadata}: ReportDetail report={report} policy={policy} participants={participants} + currentUserAccountID={currentUserPersonalDetails.accountID} /> ); @@ -671,6 +672,7 @@ function ReportDetailsPage({policy, report, route, reportMetadata}: ReportDetail participants, moneyRequestReport?.reportID, expensifyIcons.Camera, + currentUserPersonalDetails?.accountID, ]); const canJoin = canJoinChat(report, parentReportAction, policy, parentReport, !!reportNameValuePairs?.private_isArchived); @@ -679,7 +681,7 @@ function ReportDetailsPage({policy, report, route, reportMetadata}: ReportDetail const result: PromotedAction[] = []; if (canJoin) { - result.push(PromotedActions.join(report)); + result.push(PromotedActions.join(report, currentUserPersonalDetails.accountID)); } if (report) { @@ -689,7 +691,7 @@ function ReportDetailsPage({policy, report, route, reportMetadata}: ReportDetail result.push(PromotedActions.share(report, backTo)); return result; - }, [canJoin, report, backTo]); + }, [canJoin, report, backTo, currentUserPersonalDetails.accountID]); const nameSectionExpenseIOU = ( diff --git a/src/pages/RoomDescriptionPage.tsx b/src/pages/RoomDescriptionPage.tsx index 021142d24f9e..94a03028d3f1 100644 --- a/src/pages/RoomDescriptionPage.tsx +++ b/src/pages/RoomDescriptionPage.tsx @@ -12,6 +12,7 @@ import ScrollView from '@components/ScrollView'; import Text from '@components/Text'; import TextInput from '@components/TextInput'; import type {BaseTextInputRef} from '@components/TextInput/BaseTextInput/types'; +import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; import useReportIsArchived from '@hooks/useReportIsArchived'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -48,7 +49,7 @@ function RoomDescriptionPage({report, policy}: RoomDescriptionPageProps) { const focusTimeoutRef = useRef | null>(null); const {translate} = useLocalize(); const reportIsArchived = useReportIsArchived(report?.reportID); - + const {accountID: currentUserAccountID} = useCurrentUserPersonalDetails(); const handleReportDescriptionChange = useCallback((value: string) => { setDescription(value); }, []); @@ -61,9 +62,9 @@ function RoomDescriptionPage({report, policy}: RoomDescriptionPageProps) { const previousValue = report?.description ?? ''; const newValue = description.trim(); - updateDescription(report.reportID, previousValue, newValue); + updateDescription(report.reportID, previousValue, newValue, currentUserAccountID); goBack(); - }, [report.reportID, report.description, description, goBack]); + }, [report.reportID, report.description, description, goBack, currentUserAccountID]); const validate = useCallback( (values: FormOnyxValues): Errors => { diff --git a/src/pages/Share/ShareTab.tsx b/src/pages/Share/ShareTab.tsx index 9237a15e7e74..1eda39749bf9 100644 --- a/src/pages/Share/ShareTab.tsx +++ b/src/pages/Share/ShareTab.tsx @@ -6,6 +6,7 @@ import SelectionList from '@components/SelectionList'; import InviteMemberListItem from '@components/SelectionList/ListItem/InviteMemberListItem'; import type {ListItem, SelectionListHandle} from '@components/SelectionList/types'; import Text from '@components/Text'; +import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useDebouncedState from '@hooks/useDebouncedState'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; @@ -50,7 +51,7 @@ function ShareTab({ref}: ShareTabProps) { const [loginList] = useOnyx(ONYXKEYS.LOGIN_LIST, {canBeMissing: true}); const [draftComments] = useOnyx(ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT, {canBeMissing: true}); const [nvpDismissedProductTraining] = useOnyx(ONYXKEYS.NVP_DISMISSED_PRODUCT_TRAINING, {canBeMissing: true}); - + const {accountID: currentUserAccountID} = useCurrentUserPersonalDetails(); useImperativeHandle(ref, () => ({ focus: selectionListRef.current?.focusTextInput, })); @@ -121,7 +122,7 @@ function ShareTab({ref}: ShareTabProps) { const accountID = item?.accountID; if (accountID && !reportID) { saveUnknownUserDetails(item); - const optimisticReport = getOptimisticChatReport(accountID); + const optimisticReport = getOptimisticChatReport(accountID, currentUserAccountID); reportID = optimisticReport.reportID; saveReportDraft(reportID, optimisticReport).then(() => { diff --git a/src/pages/home/HeaderView.tsx b/src/pages/home/HeaderView.tsx index 915e816319dc..1e6bc150df34 100644 --- a/src/pages/home/HeaderView.tsx +++ b/src/pages/home/HeaderView.tsx @@ -21,6 +21,7 @@ import SidePanelButton from '@components/SidePanel/SidePanelButton'; import TaskHeaderActionButton from '@components/TaskHeaderActionButton'; import Text from '@components/Text'; import Tooltip from '@components/Tooltip'; +import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useHasTeam2025Pricing from '@hooks/useHasTeam2025Pricing'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLoadingBarVisibility from '@hooks/useLoadingBarVisibility'; @@ -142,6 +143,7 @@ function HeaderView({report, parentReportAction, onNavigationMenuButtonClicked, const isParentOneTransactionThread = isOneTransactionThread(parentReport, grandParentReport, grandParentReportActions?.[`${parentReport?.parentReportActionID}`]); const parentNavigationReport = isParentOneTransactionThread ? parentReport : reportHeaderData; const isReportHeaderDataArchived = useReportIsArchived(reportHeaderData?.reportID); + const {accountID: currentUserAccountID} = useCurrentUserPersonalDetails(); // Use sorted display names for the title for group chats on native small screen widths // eslint-disable-next-line @typescript-eslint/no-deprecated const title = getReportName(reportHeaderData, policy, parentReportAction, personalDetails, invoiceReceiverPolicy, undefined, undefined, isReportHeaderDataArchived); @@ -182,7 +184,7 @@ function HeaderView({report, parentReportAction, onNavigationMenuButtonClicked, !isChatThread && introSelected?.companySize !== CONST.ONBOARDING_COMPANY_SIZE.MICRO; - const join = callFunctionIfActionIsAllowed(() => joinRoom(report)); + const join = callFunctionIfActionIsAllowed(() => joinRoom(report, currentUserAccountID)); const canJoin = canJoinChat(report, parentReportAction, policy, parentReport, isReportArchived); diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx index b7fbe8bd2516..56cb4b0dc4f6 100644 --- a/src/pages/home/ReportScreen.tsx +++ b/src/pages/home/ReportScreen.tsx @@ -810,7 +810,7 @@ function ReportScreen({route, navigation, isInSidePanel = false}: ReportScreenPr if (!didSubscribeToReportLeavingEvents.current && didCreateReportSuccessfully) { // eslint-disable-next-line @typescript-eslint/no-deprecated interactionTask = InteractionManager.runAfterInteractions(() => { - subscribeToReportLeavingEvents(reportIDFromRoute); + subscribeToReportLeavingEvents(reportIDFromRoute, currentUserAccountID); didSubscribeToReportLeavingEvents.current = true; }); } @@ -820,7 +820,7 @@ function ReportScreen({route, navigation, isInSidePanel = false}: ReportScreenPr } interactionTask.cancel(); }; - }, [report?.reportID, didSubscribeToReportLeavingEvents, reportIDFromRoute, report?.pendingFields]); + }, [report?.reportID, didSubscribeToReportLeavingEvents, reportIDFromRoute, report?.pendingFields, currentUserAccountID]); const actionListValue = useMemo((): ActionListContextType => ({flatListRef, scrollPosition, setScrollPosition}), [flatListRef, scrollPosition, setScrollPosition]); diff --git a/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx b/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx index dbfa0b65753e..b0c80ac45b24 100755 --- a/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx +++ b/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx @@ -10,6 +10,7 @@ import type {ContextMenuItemHandle} from '@components/ContextMenuItem'; import ContextMenuItem from '@components/ContextMenuItem'; import FocusTrapForModal from '@components/FocusTrap/FocusTrapForModal'; import useArrowKeyFocusManager from '@hooks/useArrowKeyFocusManager'; +import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useEnvironment from '@hooks/useEnvironment'; import useGetExpensifyCardFromReportAction from '@hooks/useGetExpensifyCardFromReportAction'; import useKeyboardShortcut from '@hooks/useKeyboardShortcut'; @@ -199,7 +200,7 @@ function BaseReportActionContextMenu({ const [childChatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${childReport?.chatReportID}`, {canBeMissing: true}); const parentReportAction = getReportAction(childReport?.parentReportID, childReport?.parentReportActionID); const {reportActions: paginatedReportActions} = usePaginatedReportActions(childReport?.reportID); - + const {accountID: currentUserAccountID} = useCurrentUserPersonalDetails(); const transactionThreadReportID = useMemo( () => getOneTransactionThreadReportID(childReport, childChatReport, paginatedReportActions ?? [], isOffline), [paginatedReportActions, isOffline, childReport, childChatReport], @@ -406,6 +407,7 @@ function BaseReportActionContextMenu({ policyTags, translate, harvestReport, + currentUserAccountID, }; if ('renderContent' in contextAction) { diff --git a/src/pages/home/report/ContextMenu/ContextMenuActions.tsx b/src/pages/home/report/ContextMenu/ContextMenuActions.tsx index 61a4f160dccc..ee95300f0d61 100644 --- a/src/pages/home/report/ContextMenu/ContextMenuActions.tsx +++ b/src/pages/home/report/ContextMenu/ContextMenuActions.tsx @@ -210,6 +210,7 @@ type ContextMenuActionPayload = { reportAction: ReportAction; transaction?: OnyxEntry; reportID: string | undefined; + currentUserAccountID: number; report: OnyxEntry; draftMessage: string; selection: string; @@ -296,7 +297,7 @@ const ContextMenuActions: ContextMenuAction[] = [ const isDynamicWorkflowRoutedAction = isActionOfType(reportAction, CONST.REPORT.ACTIONS.TYPE.DYNAMIC_EXTERNAL_WORKFLOW_ROUTED); return type === CONST.CONTEXT_MENU_TYPES.REPORT_ACTION && !!reportAction && 'message' in reportAction && !isMessageDeleted(reportAction) && !isDynamicWorkflowRoutedAction; }, - renderContent: (closePopover, {reportID, reportAction, close: closeManually, openContextMenu, setIsEmojiPickerActive}) => { + renderContent: (closePopover, {reportID, reportAction, currentUserAccountID, close: closeManually, openContextMenu, setIsEmojiPickerActive}) => { const isMini = !closePopover; const closeContextMenu = (onHideCallback?: () => void) => { @@ -311,7 +312,7 @@ const ContextMenuActions: ContextMenuAction[] = [ }; const toggleEmojiAndCloseMenu = (emoji: Emoji, existingReactions: OnyxEntry, preferredSkinTone: number) => { - toggleEmojiReaction(reportID, reportAction, emoji, existingReactions, preferredSkinTone); + toggleEmojiReaction(reportID, reportAction, emoji, existingReactions, preferredSkinTone, currentUserAccountID); closeContextMenu(); setIsEmojiPickerActive?.(false); }; @@ -381,8 +382,8 @@ const ContextMenuActions: ContextMenuAction[] = [ const isDynamicWorkflowRoutedAction = isActionOfType(reportAction, CONST.REPORT.ACTIONS.TYPE.DYNAMIC_EXTERNAL_WORKFLOW_ROUTED); return (type === CONST.CONTEXT_MENU_TYPES.REPORT_ACTION && !isDynamicWorkflowRoutedAction) || (type === CONST.CONTEXT_MENU_TYPES.REPORT && !isUnreadChat); }, - onPress: (closePopover, {reportAction, reportID}) => { - markCommentAsUnread(reportID, reportAction); + onPress: (closePopover, {reportAction, reportID, currentUserAccountID}) => { + markCommentAsUnread(reportID, reportAction, currentUserAccountID); if (closePopover) { hideContextMenu(true, ReportActionComposeFocusManager.focus); } @@ -516,19 +517,19 @@ const ContextMenuActions: ContextMenuAction[] = [ (shouldDisplayThreadReplies || (!isDeletedAction && !isArchivedRoom)) ); }, - onPress: (closePopover, {reportAction, reportID}) => { + onPress: (closePopover, {reportAction, reportID, currentUserAccountID}) => { const childReportNotificationPreference = getChildReportNotificationPreferenceReportUtils(reportAction); const originalReportID = getOriginalReportID(reportID, reportAction); if (closePopover) { hideContextMenu(false, () => { ReportActionComposeFocusManager.focus(); - toggleSubscribeToChildReport(reportAction?.childReportID, reportAction, originalReportID, childReportNotificationPreference); + toggleSubscribeToChildReport(reportAction?.childReportID, currentUserAccountID, reportAction, originalReportID, childReportNotificationPreference); }); return; } ReportActionComposeFocusManager.focus(); - toggleSubscribeToChildReport(reportAction?.childReportID, reportAction, originalReportID, childReportNotificationPreference); + toggleSubscribeToChildReport(reportAction?.childReportID, currentUserAccountID, reportAction, originalReportID, childReportNotificationPreference); }, getDescription: () => {}, sentryLabel: CONST.SENTRY_LABEL.CONTEXT_MENU.JOIN_THREAD, @@ -556,19 +557,19 @@ const ContextMenuActions: ContextMenuAction[] = [ (shouldDisplayThreadReplies || (!isDeletedAction && !isArchivedRoom)) ); }, - onPress: (closePopover, {reportAction, reportID}) => { + onPress: (closePopover, {reportAction, reportID, currentUserAccountID}) => { const childReportNotificationPreference = getChildReportNotificationPreferenceReportUtils(reportAction); const originalReportID = getOriginalReportID(reportID, reportAction); if (closePopover) { hideContextMenu(false, () => { ReportActionComposeFocusManager.focus(); - toggleSubscribeToChildReport(reportAction?.childReportID, reportAction, originalReportID, childReportNotificationPreference); + toggleSubscribeToChildReport(reportAction?.childReportID, currentUserAccountID, reportAction, originalReportID, childReportNotificationPreference); }); return; } ReportActionComposeFocusManager.focus(); - toggleSubscribeToChildReport(reportAction?.childReportID, reportAction, originalReportID, childReportNotificationPreference); + toggleSubscribeToChildReport(reportAction?.childReportID, currentUserAccountID, reportAction, originalReportID, childReportNotificationPreference); }, getDescription: () => {}, sentryLabel: CONST.SENTRY_LABEL.CONTEXT_MENU.LEAVE_THREAD, diff --git a/src/pages/home/report/PureReportActionItem.tsx b/src/pages/home/report/PureReportActionItem.tsx index 80d5659408bc..99e3c1f3825a 100644 --- a/src/pages/home/report/PureReportActionItem.tsx +++ b/src/pages/home/report/PureReportActionItem.tsx @@ -346,6 +346,7 @@ type PureReportActionItemProps = { reactionObject: Emoji, existingReactions: OnyxEntry, paramSkinTone: number, + currentUserAccountID: number, ignoreSkinToneOnCompare: boolean | undefined, ) => void; @@ -416,7 +417,7 @@ type PureReportActionItemProps = { isTryNewDotNVPDismissed?: boolean; /** Current user's account id */ - currentUserAccountID?: number; + currentUserAccountID: number; /** The bank account list */ bankAccountList?: OnyxTypes.BankAccountList | undefined; @@ -768,9 +769,9 @@ function PureReportActionItem({ const toggleReaction = useCallback( (emoji: Emoji, preferredSkinTone: number, ignoreSkinToneOnCompare?: boolean) => { - toggleEmojiReaction(reportID, action, emoji, emojiReactions, preferredSkinTone, ignoreSkinToneOnCompare); + toggleEmojiReaction(reportID, action, emoji, emojiReactions, preferredSkinTone, currentUserAccountID, ignoreSkinToneOnCompare); }, - [reportID, action, emojiReactions, toggleEmojiReaction], + [reportID, action, emojiReactions, toggleEmojiReaction, currentUserAccountID], ); const contextValue = useMemo( diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx index 70d6b0587d73..e168871500fe 100644 --- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx +++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx @@ -11,6 +11,7 @@ import type {Emoji} from '@assets/emojis/types'; import type {MeasureParentContainerAndCursorCallback} from '@components/AutoCompleteSuggestions/types'; import Composer from '@components/Composer'; import type {CustomSelectionChangeEvent, TextSelection} from '@components/Composer/types'; +import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useKeyboardState from '@hooks/useKeyboardState'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; @@ -168,8 +169,8 @@ const isIOSNative = getPlatform() === CONST.PLATFORM.IOS; * Broadcast that the user is typing. Debounced to limit how often we publish client events. */ const debouncedBroadcastUserIsTyping = lodashDebounce( - (reportID: string) => { - broadcastUserIsTyping(reportID); + (reportID: string, currentUserAccountID: number) => { + broadcastUserIsTyping(reportID, currentUserAccountID); }, 1000, { @@ -250,6 +251,7 @@ function ComposerWithSuggestions({ } return draftComment; }); + const {accountID: currentUserAccountID} = useCurrentUserPersonalDetails(); const commentRef = useRef(value); @@ -444,7 +446,7 @@ function ComposerWithSuggestions({ saveReportDraftComment(reportID, newCommentConverted); } if (newCommentConverted) { - debouncedBroadcastUserIsTyping(reportID); + debouncedBroadcastUserIsTyping(reportID, currentUserAccountID); } }, [ @@ -458,6 +460,7 @@ function ComposerWithSuggestions({ debouncedSaveReportComment, selection?.end, selection?.start, + currentUserAccountID, ], ); diff --git a/src/pages/home/report/ReportActionItem.tsx b/src/pages/home/report/ReportActionItem.tsx index ab6833febcc0..343bde15a4e0 100644 --- a/src/pages/home/report/ReportActionItem.tsx +++ b/src/pages/home/report/ReportActionItem.tsx @@ -2,6 +2,7 @@ import {accountIDSelector} from '@selectors/Session'; import React from 'react'; import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import {useBlockedFromConcierge} from '@components/OnyxListItemProvider'; +import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useOnyx from '@hooks/useOnyx'; import useOriginalReportID from '@hooks/useOriginalReportID'; import usePolicyForMovingExpenses from '@hooks/usePolicyForMovingExpenses'; @@ -94,7 +95,7 @@ function ReportActionItem({ const originalReportID = useOriginalReportID(reportID, action); const originalReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${originalReportID}`]; const isOriginalReportArchived = useReportIsArchived(originalReportID); - const [currentUserAccountID] = useOnyx(ONYXKEYS.SESSION, {canBeMissing: false, selector: accountIDSelector}); + const {accountID: currentUserAccountID} = useCurrentUserPersonalDetails(); const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED, {canBeMissing: true}); const [reportMetadata] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportID}`, {canBeMissing: true}); const iouReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${getIOUReportIDFromReportActionPreview(action)}`]; diff --git a/src/pages/home/report/ReportFooter.tsx b/src/pages/home/report/ReportFooter.tsx index d9020b0d4699..82f9cfa2e0a7 100644 --- a/src/pages/home/report/ReportFooter.tsx +++ b/src/pages/home/report/ReportFooter.tsx @@ -225,7 +225,12 @@ function ReportFooter({ isSmallSizeLayout={isSmallSizeLayout || isInSidePanel} /> )} - {isArchivedRoom && } + {isArchivedRoom && ( + + )} {!isArchivedRoom && !!isBlockedFromChat && } {!isAnonymousUser && !canWriteInReport && isSystemChat && } {isAdminsOnlyPostingRoom && !isUserPolicyAdmin && !isArchivedRoom && !isAnonymousUser && !isBlockedFromChat && ( diff --git a/src/pages/home/report/UserTypingEventListener.tsx b/src/pages/home/report/UserTypingEventListener.tsx index f6ec7f7f7456..bc375526ad5c 100644 --- a/src/pages/home/report/UserTypingEventListener.tsx +++ b/src/pages/home/report/UserTypingEventListener.tsx @@ -1,6 +1,7 @@ import {useIsFocused, useRoute} from '@react-navigation/native'; import {useEffect, useRef} from 'react'; import {InteractionManager} from 'react-native'; +import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useOnyx from '@hooks/useOnyx'; import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigation/types'; @@ -16,6 +17,7 @@ type UserTypingEventListenerProps = { }; function UserTypingEventListener({report}: UserTypingEventListenerProps) { const [lastVisitedPath = ''] = useOnyx(ONYXKEYS.LAST_VISITED_PATH, {canBeMissing: true}); + const {accountID: currentUserAccountID} = useCurrentUserPersonalDetails(); const didSubscribeToReportTypingEvents = useRef(false); const reportID = report.reportID; const isFocused = useIsFocused(); @@ -55,7 +57,7 @@ function UserTypingEventListener({report}: UserTypingEventListenerProps) { if (!didSubscribeToReportTypingEvents.current && didCreateReportSuccessfully) { // eslint-disable-next-line @typescript-eslint/no-deprecated interactionTask = InteractionManager.runAfterInteractions(() => { - subscribeToReportTypingEvents(reportID); + subscribeToReportTypingEvents(reportID, currentUserAccountID); didSubscribeToReportTypingEvents.current = true; }); } @@ -76,7 +78,7 @@ function UserTypingEventListener({report}: UserTypingEventListenerProps) { } interactionTask.cancel(); }; - }, [isFocused, report.pendingFields, didSubscribeToReportTypingEvents, lastVisitedPath, reportID, route?.params?.reportID]); + }, [isFocused, report.pendingFields, didSubscribeToReportTypingEvents, lastVisitedPath, reportID, currentUserAccountID, route?.params?.reportID]); return null; } diff --git a/src/pages/settings/Report/NotificationPreferencePage.tsx b/src/pages/settings/Report/NotificationPreferencePage.tsx index 7bb0a6a06c8e..4a54a35482a7 100644 --- a/src/pages/settings/Report/NotificationPreferencePage.tsx +++ b/src/pages/settings/Report/NotificationPreferencePage.tsx @@ -6,6 +6,7 @@ import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import SelectionList from '@components/SelectionList'; import RadioListItem from '@components/SelectionList/ListItem/RadioListItem'; +import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; import useReportIsArchived from '@hooks/useReportIsArchived'; import type {PlatformStackRouteProp, PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; @@ -23,6 +24,7 @@ function NotificationPreferencePage({report}: NotificationPreferencePageProps) { const route = useRoute>(); const {translate} = useLocalize(); const isReportArchived = useReportIsArchived(report?.reportID); + const {accountID: currentUserAccountID} = useCurrentUserPersonalDetails(); const isMoneyRequest = isMoneyRequestReport(report); const currentNotificationPreference = getReportNotificationPreference(report); const shouldDisableNotificationPreferences = @@ -42,10 +44,10 @@ function NotificationPreferencePage({report}: NotificationPreferencePageProps) { const updateNotificationPreferenceForReportAction = useCallback( (value: ValueOf) => { - updateNotificationPreference(report.reportID, currentNotificationPreference, value, undefined, undefined); + updateNotificationPreference(report.reportID, currentNotificationPreference, value, currentUserAccountID, undefined, undefined); goBack(); }, - [report.reportID, currentNotificationPreference, goBack], + [report.reportID, currentNotificationPreference, currentUserAccountID, goBack], ); return ( From afa3be5b5ebfc8887e0dfdd78750b8aad12a7c0b Mon Sep 17 00:00:00 2001 From: Linh Date: Sun, 11 Jan 2026 15:57:30 +0700 Subject: [PATCH 2/9] chore: fix ttest fail --- src/libs/actions/Report.ts | 4 ++-- tests/actions/ReportTest.ts | 20 +++++++++++--------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 27934546b589..d03e923d2a1a 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -5826,7 +5826,7 @@ function buildOptimisticChangePolicyData( report: {...report, policyID: policy.id}, predictedNextStatus: newStatusNum, policy, - currentUserAccountIDParam: accountID, + currentUserAccountIDParam: currentUserAccountID, currentUserEmailParam: email, hasViolations: hasViolationsParam, isASAPSubmitBetaEnabled, @@ -5835,7 +5835,7 @@ function buildOptimisticChangePolicyData( report: {...report, policyID: policy.id}, predictedNextStatus: newStatusNum, policy, - currentUserAccountIDParam: accountID, + currentUserAccountIDParam: currentUserAccountID, currentUserEmailParam: email, hasViolations: hasViolationsParam, isASAPSubmitBetaEnabled, diff --git a/tests/actions/ReportTest.ts b/tests/actions/ReportTest.ts index 7ab60a5496f9..4749f3acdf6b 100644 --- a/tests/actions/ReportTest.ts +++ b/tests/actions/ReportTest.ts @@ -726,7 +726,7 @@ describe('actions/Report', () => { if (reportAction) { // Add a reaction to the comment - Report.toggleEmojiReaction(REPORT_ID, reportAction, EMOJI, reportActionsReactions[0], CONST.EMOJI_DEFAULT_SKIN_TONE); + Report.toggleEmojiReaction(REPORT_ID, reportAction, EMOJI, reportActionsReactions[0], CONST.EMOJI_DEFAULT_SKIN_TONE, TEST_USER_ACCOUNT_ID); } return waitForBatchedUpdates(); }) @@ -746,7 +746,7 @@ describe('actions/Report', () => { if (reportAction) { // Now we remove the reaction - Report.toggleEmojiReaction(REPORT_ID, reportAction, EMOJI, reportActionReaction, CONST.EMOJI_DEFAULT_SKIN_TONE); + Report.toggleEmojiReaction(REPORT_ID, reportAction, EMOJI, reportActionReaction, CONST.EMOJI_DEFAULT_SKIN_TONE, TEST_USER_ACCOUNT_ID); } return waitForBatchedUpdates(); }) @@ -761,7 +761,7 @@ describe('actions/Report', () => { if (reportAction) { // Add the same reaction to the same report action with a different skin tone - Report.toggleEmojiReaction(REPORT_ID, reportAction, EMOJI, reportActionsReactions[0], CONST.EMOJI_DEFAULT_SKIN_TONE); + Report.toggleEmojiReaction(REPORT_ID, reportAction, EMOJI, reportActionsReactions[0], CONST.EMOJI_DEFAULT_SKIN_TONE, TEST_USER_ACCOUNT_ID); } return waitForBatchedUpdates() .then(() => { @@ -769,7 +769,7 @@ describe('actions/Report', () => { const reportActionReaction = reportActionsReactions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportActionID}`]; if (reportAction) { - Report.toggleEmojiReaction(REPORT_ID, reportAction, EMOJI, reportActionReaction, EMOJI_SKIN_TONE); + Report.toggleEmojiReaction(REPORT_ID, reportAction, EMOJI, reportActionReaction, EMOJI_SKIN_TONE, TEST_USER_ACCOUNT_ID); } return waitForBatchedUpdates(); }) @@ -794,7 +794,7 @@ describe('actions/Report', () => { if (reportAction) { // Now we remove the reaction, and expect that both variations are removed - Report.toggleEmojiReaction(REPORT_ID, reportAction, EMOJI, reportActionReaction, CONST.EMOJI_DEFAULT_SKIN_TONE); + Report.toggleEmojiReaction(REPORT_ID, reportAction, EMOJI, reportActionReaction, CONST.EMOJI_DEFAULT_SKIN_TONE, TEST_USER_ACCOUNT_ID); } return waitForBatchedUpdates(); }) @@ -854,7 +854,7 @@ describe('actions/Report', () => { if (resultAction) { // Add a reaction to the comment - Report.toggleEmojiReaction(REPORT_ID, resultAction, EMOJI, {}, CONST.EMOJI_DEFAULT_SKIN_TONE); + Report.toggleEmojiReaction(REPORT_ID, resultAction, EMOJI, {}, CONST.EMOJI_DEFAULT_SKIN_TONE, TEST_USER_ACCOUNT_ID); } return waitForBatchedUpdates(); }) @@ -866,7 +866,7 @@ describe('actions/Report', () => { // should get removed instead of added again. const reportActionReaction = reportActionsReactions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${resultAction?.reportActionID}`]; if (resultAction) { - Report.toggleEmojiReaction(REPORT_ID, resultAction, EMOJI, reportActionReaction, 2); + Report.toggleEmojiReaction(REPORT_ID, resultAction, EMOJI, reportActionReaction, 2, TEST_USER_ACCOUNT_ID); } return waitForBatchedUpdates(); }) @@ -1486,7 +1486,7 @@ describe('actions/Report', () => { await waitForBatchedUpdates(); - Report.toggleEmojiReaction(REPORT_ID, newReportAction, {name: 'smile', code: 'πŸ˜„'}, {}, CONST.EMOJI_DEFAULT_SKIN_TONE); + Report.toggleEmojiReaction(REPORT_ID, newReportAction, {name: 'smile', code: 'πŸ˜„'}, {}, CONST.EMOJI_DEFAULT_SKIN_TONE, TEST_USER_ACCOUNT_ID); Report.toggleEmojiReaction( REPORT_ID, newReportAction, @@ -1507,6 +1507,7 @@ describe('actions/Report', () => { }, }, CONST.EMOJI_DEFAULT_SKIN_TONE, + TEST_USER_ACCOUNT_ID, ); await waitForBatchedUpdates(); @@ -1587,7 +1588,7 @@ describe('actions/Report', () => { // wait for Onyx.connect execute the callback and start processing the queue await Promise.resolve(); - Report.toggleEmojiReaction(REPORT_ID, reportAction, {name: 'smile', code: 'πŸ˜„'}, {}, CONST.EMOJI_DEFAULT_SKIN_TONE); + Report.toggleEmojiReaction(REPORT_ID, reportAction, {name: 'smile', code: 'πŸ˜„'}, {}, CONST.EMOJI_DEFAULT_SKIN_TONE, TEST_USER_ACCOUNT_ID); Report.toggleEmojiReaction( REPORT_ID, reportAction, @@ -1608,6 +1609,7 @@ describe('actions/Report', () => { }, }, CONST.EMOJI_DEFAULT_SKIN_TONE, + TEST_USER_ACCOUNT_ID, ); await waitForBatchedUpdates(); From d8b687bd3e2c350dc72ffac7d9bc3dcd559874cf Mon Sep 17 00:00:00 2001 From: Linh Date: Sun, 11 Jan 2026 17:01:25 +0700 Subject: [PATCH 3/9] chore: write ui test for HeaderView --- tests/actions/ReportTest.ts | 4 +-- tests/ui/components/HeaderViewTest.tsx | 48 +++++++++++++++++++++++++- 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/tests/actions/ReportTest.ts b/tests/actions/ReportTest.ts index 4749f3acdf6b..2aea9b3ef3bd 100644 --- a/tests/actions/ReportTest.ts +++ b/tests/actions/ReportTest.ts @@ -396,7 +396,7 @@ describe('actions/Report', () => { expect(ReportUtils.isUnreadWithMention(report)).toBe(false); // When the user manually marks a message as "unread" - Report.markCommentAsUnread(REPORT_ID, reportActions['1']); + Report.markCommentAsUnread(REPORT_ID, reportActions['1'], USER_1_ACCOUNT_ID); return waitForBatchedUpdates(); }) .then(() => { @@ -520,7 +520,7 @@ describe('actions/Report', () => { expect(ReportUtils.isUnread(report, undefined, undefined)).toBe(false); // When the user manually marks a message as "unread" - Report.markCommentAsUnread(REPORT_ID, reportActions[400]); + Report.markCommentAsUnread(REPORT_ID, reportActions[400], USER_1_ACCOUNT_ID); return waitForBatchedUpdates(); }) .then(() => { diff --git a/tests/ui/components/HeaderViewTest.tsx b/tests/ui/components/HeaderViewTest.tsx index c60fa729a0a4..2dfbbd536571 100644 --- a/tests/ui/components/HeaderViewTest.tsx +++ b/tests/ui/components/HeaderViewTest.tsx @@ -1,9 +1,13 @@ -import {act, render, screen} from '@testing-library/react-native'; +import {act, fireEvent, render, screen} from '@testing-library/react-native'; import React from 'react'; import Onyx from 'react-native-onyx'; +import ComposeProviders from '@components/ComposeProviders'; +import {LocaleContextProvider} from '@components/LocaleContextProvider'; import OnyxListItemProvider from '@components/OnyxListItemProvider'; +import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import type Navigation from '@libs/Navigation/Navigation'; import HeaderView from '@pages/home/HeaderView'; +import * as Report from '@userActions/Report'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import {createRandomReport} from '../../utils/collections/reports'; @@ -18,8 +22,20 @@ jest.mock('@react-navigation/native', () => { }); jest.mock('@hooks/useCurrentUserPersonalDetails'); +jest.mock('@userActions/Report'); + +const mockUseCurrentUserPersonalDetails = useCurrentUserPersonalDetails as jest.MockedFunction; +const mockJoinRoom = jest.spyOn(Report, 'joinRoom'); +const currentUserAccountID = 1; describe('HeaderView', () => { + beforeEach(() => { + // Set up default mock return value + mockUseCurrentUserPersonalDetails.mockReturnValue({ + accountID: currentUserAccountID, + }); + }); + afterEach(() => { jest.clearAllMocks(); }); @@ -76,4 +92,34 @@ describe('HeaderView', () => { // Then the header title should be updated using the new display name expect(screen.getByTestId('DisplayNames')).toHaveTextContent(displayName); }); + + it('should display join button', async () => { + // Given an policy room header + const report = { + ...createRandomReport(1, CONST.REPORT.CHAT_TYPE.POLICY_ROOM), + reportName: 'Test Room', + notificationPreference: CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN, + }; + + render( + + {}} + parentReportAction={null} + reportID={report.reportID} + shouldUseNarrowLayout + /> + , + ); + + await waitForBatchedUpdatesWithAct(); + // When the header display the Join button + const joinButton = screen.getByText('Join'); + expect(joinButton).toBeTruthy(); + + // Then the joinRoom action should be called when the user presses the Join button + fireEvent.press(joinButton); + expect(mockJoinRoom).toHaveBeenCalledWith(report, currentUserAccountID); + }); }); From ee2eb8d385a896534c4c622029c2d3ff02e7fdc2 Mon Sep 17 00:00:00 2001 From: Linh Date: Tue, 13 Jan 2026 15:21:32 +0700 Subject: [PATCH 4/9] chore: fix typecheck fail --- src/pages/ProfilePage.tsx | 11 +++++------ src/pages/home/report/ReportActionItem.tsx | 3 +-- tests/actions/ReportTest.ts | 5 +++-- tests/ui/PureReportActionItemTest.tsx | 1 + tests/ui/UnreadIndicatorsTest.tsx | 8 ++++---- tests/ui/components/HeaderViewTest.tsx | 12 ++++++++---- 6 files changed, 22 insertions(+), 18 deletions(-) diff --git a/src/pages/ProfilePage.tsx b/src/pages/ProfilePage.tsx index b0812560f4b0..dac2ba12e39b 100755 --- a/src/pages/ProfilePage.tsx +++ b/src/pages/ProfilePage.tsx @@ -71,22 +71,21 @@ function ProfilePage({route}: ProfilePageProps) { const [reports] = useOnyx(ONYXKEYS.COLLECTION.REPORT, {selector: reportsSelector, canBeMissing: true}); const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {canBeMissing: true}); const [personalDetailsMetadata] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_METADATA, {canBeMissing: true}); - const [session] = useOnyx(ONYXKEYS.SESSION, {canBeMissing: false}); + const {accountID: currentUserAccountID} = useCurrentUserPersonalDetails(); const [account] = useOnyx(ONYXKEYS.ACCOUNT, {canBeMissing: true}); const [isDebugModeEnabled = false] = useOnyx(ONYXKEYS.IS_DEBUG_MODE_ENABLED, {canBeMissing: true}); const guideCalendarLink = account?.guideDetails?.calendarLink ?? ''; const expensifyIcons = useMemoizedLazyExpensifyIcons(['Bug', 'Pencil', 'Phone']); const accountID = Number(route.params?.accountID ?? CONST.DEFAULT_NUMBER_ID); - const isCurrentUser = session?.accountID === accountID; + const isCurrentUser = currentUserAccountID === accountID; const reportKey = useMemo(() => { - const reportID = isCurrentUser ? findSelfDMReportID() : getChatByParticipants(session?.accountID ? [accountID, session.accountID] : [], reports)?.reportID; + const reportID = isCurrentUser ? findSelfDMReportID() : getChatByParticipants(currentUserAccountID ? [accountID, currentUserAccountID] : [], reports)?.reportID; if (isAnonymousUserSession() || !reportID) { return `${ONYXKEYS.COLLECTION.REPORT}0` as const; } return `${ONYXKEYS.COLLECTION.REPORT}${reportID}` as const; - }, [accountID, isCurrentUser, reports, session?.accountID]); - const {accountID: currentUserAccountID} = useCurrentUserPersonalDetails(); + }, [accountID, isCurrentUser, reports, currentUserAccountID]); const [report] = useOnyx(reportKey, {canBeMissing: true}); const styles = useThemeStyles(); @@ -272,7 +271,7 @@ function ProfilePage({route}: ProfilePageProps) { title={`${translate('privateNotes.title')}`} titleStyle={styles.flex1} icon={expensifyIcons.Pencil} - onPress={() => navigateToPrivateNotes(report, session?.accountID ?? CONST.DEFAULT_NUMBER_ID, navigateBackTo)} + onPress={() => navigateToPrivateNotes(report, currentUserAccountID, navigateBackTo)} wrapperStyle={styles.breakAll} shouldShowRightIcon brickRoadIndicator={hasErrorInPrivateNotes(report) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined} diff --git a/src/pages/home/report/ReportActionItem.tsx b/src/pages/home/report/ReportActionItem.tsx index 343bde15a4e0..6302e306eeab 100644 --- a/src/pages/home/report/ReportActionItem.tsx +++ b/src/pages/home/report/ReportActionItem.tsx @@ -1,4 +1,3 @@ -import {accountIDSelector} from '@selectors/Session'; import React from 'react'; import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import {useBlockedFromConcierge} from '@components/OnyxListItemProvider'; @@ -36,7 +35,7 @@ import type {Errors} from '@src/types/onyx/OnyxCommon'; import type {PureReportActionItemProps} from './PureReportActionItem'; import PureReportActionItem from './PureReportActionItem'; -type ReportActionItemProps = Omit & { +type ReportActionItemProps = Omit & { /** All the data of the report collection */ allReports: OnyxCollection; diff --git a/tests/actions/ReportTest.ts b/tests/actions/ReportTest.ts index d161b3da71f9..bc757e8fe982 100644 --- a/tests/actions/ReportTest.ts +++ b/tests/actions/ReportTest.ts @@ -2184,9 +2184,10 @@ describe('actions/Report', () => { }); describe('updateDescription', () => { + const currentUserAccountID = 1; it('should not call UpdateRoomDescription API if the description is not changed', async () => { global.fetch = TestHelper.getGlobalFetchMock(); - Report.updateDescription('1', '

test

', '# test'); + Report.updateDescription('1', '

test

', '# test', currentUserAccountID); await waitForBatchedUpdates(); @@ -2203,7 +2204,7 @@ describe('actions/Report', () => { await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`, report); mockFetch?.fail?.(); - Report.updateDescription('1', '

test

', '# test1'); + Report.updateDescription('1', '

test

', '# test1', currentUserAccountID); await waitForBatchedUpdates(); let updateReport: OnyxEntry; diff --git a/tests/ui/PureReportActionItemTest.tsx b/tests/ui/PureReportActionItemTest.tsx index cda560122c28..3df0b9e55e68 100644 --- a/tests/ui/PureReportActionItemTest.tsx +++ b/tests/ui/PureReportActionItemTest.tsx @@ -99,6 +99,7 @@ describe('PureReportActionItem', () => { taskReport={undefined} linkedReport={undefined} iouReportOfLinkedReport={undefined} + currentUserAccountID={ACTOR_ACCOUNT_ID} />
diff --git a/tests/ui/UnreadIndicatorsTest.tsx b/tests/ui/UnreadIndicatorsTest.tsx index 6407045cd712..5d15e7c592cc 100644 --- a/tests/ui/UnreadIndicatorsTest.tsx +++ b/tests/ui/UnreadIndicatorsTest.tsx @@ -429,7 +429,7 @@ describe('Unread Indicators', () => { .then(() => { // It's difficult to trigger marking a report comment as unread since we would have to mock the long press event and then // another press on the context menu item so we will do it via the action directly and then test if the UI has updated properly - markCommentAsUnread(REPORT_ID, createdReportAction); + markCommentAsUnread(REPORT_ID, createdReportAction, USER_A_ACCOUNT_ID); return waitForBatchedUpdates(); }) .then(() => { @@ -528,7 +528,7 @@ describe('Unread Indicators', () => { expect(unreadIndicator).toHaveLength(0); // Mark a previous comment as unread and verify the unread action indicator returns - markCommentAsUnread(REPORT_ID, createdReportAction); + markCommentAsUnread(REPORT_ID, createdReportAction, USER_A_ACCOUNT_ID); return waitForBatchedUpdates(); }) .then(() => { @@ -612,7 +612,7 @@ describe('Unread Indicators', () => { const firstNewReportAction = reportActions ? lastItem(reportActions) : undefined; if (firstNewReportAction) { - markCommentAsUnread(REPORT_ID, firstNewReportAction); + markCommentAsUnread(REPORT_ID, firstNewReportAction, USER_A_ACCOUNT_ID); await waitForBatchedUpdates(); @@ -755,7 +755,7 @@ describe('Unread Indicators', () => { lastVisibleActionCreated: reportAction11CreatedDate, }); - markCommentAsUnread(REPORT_ID, {reportActionID: -1} as unknown as ReportAction); // Marking the chat as unread from LHN passing a dummy reportActionID + markCommentAsUnread(REPORT_ID, {reportActionID: -1} as unknown as ReportAction, USER_A_ACCOUNT_ID); // Marking the chat as unread from LHN passing a dummy reportActionID await waitForBatchedUpdates(); const hintText = TestHelper.translateLocal('accessibilityHints.chatUserDisplayNames'); diff --git a/tests/ui/components/HeaderViewTest.tsx b/tests/ui/components/HeaderViewTest.tsx index b1d3ee28ef7c..c7e2b70f9f1c 100644 --- a/tests/ui/components/HeaderViewTest.tsx +++ b/tests/ui/components/HeaderViewTest.tsx @@ -9,7 +9,9 @@ import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails' import type Navigation from '@libs/Navigation/Navigation'; import {buildOptimisticCreatedReportForUnapprovedAction} from '@libs/ReportUtils'; import HeaderView from '@pages/home/HeaderView'; -import * as Report from '@userActions/Report'; +import {joinRoom} from '@userActions/Report'; +// eslint-disable-next-line no-restricted-syntax +import type * as ReportType from '@userActions/Report'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {ReportAction} from '@src/types/onyx'; @@ -25,10 +27,12 @@ jest.mock('@react-navigation/native', () => { }); jest.mock('@hooks/useCurrentUserPersonalDetails'); -jest.mock('@userActions/Report'); +jest.mock('@userActions/Report', () => ({ + ...jest.requireActual('@userActions/Report'), + joinRoom: jest.fn(), +})); const mockUseCurrentUserPersonalDetails = useCurrentUserPersonalDetails as jest.MockedFunction; -const mockJoinRoom = jest.spyOn(Report, 'joinRoom'); const currentUserAccountID = 1; describe('HeaderView', () => { @@ -125,7 +129,7 @@ describe('HeaderView', () => { // Then the joinRoom action should be called when the user presses the Join button fireEvent.press(joinButton); - expect(mockJoinRoom).toHaveBeenCalledWith(report, currentUserAccountID); + expect(joinRoom).toHaveBeenCalledWith(report, currentUserAccountID); }); it('should display correct title for report with CREATED_REPORT_FOR_UNAPPROVED_TRANSACTIONS parent action', async () => { From 01e18da1368d48fc56addddfce1f8d0675d1010b Mon Sep 17 00:00:00 2001 From: Linh Date: Tue, 13 Jan 2026 15:33:08 +0700 Subject: [PATCH 5/9] chore: fix typecheck fail --- tests/ui/PureReportActionItemTest.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/ui/PureReportActionItemTest.tsx b/tests/ui/PureReportActionItemTest.tsx index 3df0b9e55e68..782f1c451642 100644 --- a/tests/ui/PureReportActionItemTest.tsx +++ b/tests/ui/PureReportActionItemTest.tsx @@ -278,6 +278,7 @@ describe('PureReportActionItem', () => { linkedReport={undefined} iouReportOfLinkedReport={undefined} reportMetadata={reportMetadata} + currentUserAccountID={ACTOR_ACCOUNT_ID} /> @@ -334,6 +335,7 @@ describe('PureReportActionItem', () => { taskReport={undefined} linkedReport={undefined} iouReportOfLinkedReport={undefined} + currentUserAccountID={ACTOR_ACCOUNT_ID} /> From e9c91fed8c9f4af4a6c91f5e23f4b0e0aa713e1d Mon Sep 17 00:00:00 2001 From: Linh Date: Tue, 13 Jan 2026 16:45:49 +0700 Subject: [PATCH 6/9] chore: write test for markCommentAsUnread --- tests/actions/ReportTest.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/actions/ReportTest.ts b/tests/actions/ReportTest.ts index bc757e8fe982..740e124c3713 100644 --- a/tests/actions/ReportTest.ts +++ b/tests/actions/ReportTest.ts @@ -2214,6 +2214,8 @@ describe('actions/Report', () => { callback: (val) => (updateReport = val), }); expect(updateReport?.description).toBe('

test

'); + expect(updateReport?.lastActorAccountID).toBe(currentUserAccountID); + mockFetch.mockReset(); }); }); From 7b3c6178f2a273a17eb134cc6bd87121a117c9a2 Mon Sep 17 00:00:00 2001 From: Linh Date: Tue, 13 Jan 2026 16:45:56 +0700 Subject: [PATCH 7/9] chore: write test for markCommentAsUnread --- tests/ui/UnreadIndicatorsTest.tsx | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/ui/UnreadIndicatorsTest.tsx b/tests/ui/UnreadIndicatorsTest.tsx index 5d15e7c592cc..c9b27b702b0b 100644 --- a/tests/ui/UnreadIndicatorsTest.tsx +++ b/tests/ui/UnreadIndicatorsTest.tsx @@ -763,4 +763,23 @@ describe('Unread Indicators', () => { expect(displayNameTexts).toHaveLength(1); expect((displayNameTexts.at(0)?.props?.style as TextStyle)?.fontWeight).toBe(FontUtils.fontWeight.bold); }); + + it('Mark the last comment as unread should set lastReadTime to the last action’s creation time', async () => { + await signInAndGetAppWithUnreadChat(); + await navigateToSidebarOption(0); + + const report = await OnyxUtils.get(`${ONYXKEYS.COLLECTION.REPORT}${REPORT_ID}`); + + // When USER_A add a comment + addComment(report, REPORT_ID, [], 'Current User Comment', CONST.DEFAULT_TIME_ZONE); + await waitForBatchedUpdates(); + + // Then USER_A mark the report as unread + markCommentAsUnread(REPORT_ID, {reportActionID: -1} as unknown as ReportAction, USER_A_ACCOUNT_ID); + await waitForBatchedUpdates(); + + // Then the lastReadTime of report should same as last action from USER_B + const updatedReport = await OnyxUtils.get(`${ONYXKEYS.COLLECTION.REPORT}${REPORT_ID}`); + expect(updatedReport?.lastReadTime).toBe(DateUtils.subtractMillisecondsFromDateTime(reportAction9CreatedDate, 1)); + }); }); From 03e3fb7c2a03cc8064cc52f1d68a442af8f29969 Mon Sep 17 00:00:00 2001 From: Linh Date: Tue, 13 Jan 2026 21:15:31 +0700 Subject: [PATCH 8/9] chore: remove currentUserAccountID from Onyx --- src/libs/actions/Report.ts | 8 ++++---- src/pages/ReportDetailsPage.tsx | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index b6987f90159d..b2348e1b00f5 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -3613,7 +3613,7 @@ function addEmojiReaction(reportID: string, reportActionID: string, emoji: Emoji * Removes a reaction to the report action. * Uses the NEW FORMAT for "emojiReactions" */ -function removeEmojiReaction(reportID: string, reportActionID: string, emoji: Emoji) { +function removeEmojiReaction(reportID: string, reportActionID: string, emoji: Emoji, currentUserAccountID: number) { const optimisticData: Array> = [ { onyxMethod: Onyx.METHOD.MERGE, @@ -3621,7 +3621,7 @@ function removeEmojiReaction(reportID: string, reportActionID: string, emoji: Em value: { [emoji.name]: { users: { - [deprecatedCurrentUserAccountID]: null, + [currentUserAccountID]: null, }, }, }, @@ -3723,7 +3723,7 @@ function joinRoom(report: OnyxEntry, currentUserAccountID: number) { ); } -function leaveGroupChat(reportID: string, shouldClearQuickAction: boolean) { +function leaveGroupChat(reportID: string, shouldClearQuickAction: boolean, currentUserAccountID: number) { const report = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; if (!report) { Log.warn('Attempting to leave Group Chat that does not existing locally'); @@ -3741,7 +3741,7 @@ function leaveGroupChat(reportID: string, shouldClearQuickAction: boolean) { stateNum: CONST.REPORT.STATE_NUM.APPROVED, statusNum: CONST.REPORT.STATUS_NUM.CLOSED, participants: { - [deprecatedCurrentUserAccountID]: { + [currentUserAccountID]: { notificationPreference: CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN, }, }, diff --git a/src/pages/ReportDetailsPage.tsx b/src/pages/ReportDetailsPage.tsx index a282c2756852..7eb383470ade 100644 --- a/src/pages/ReportDetailsPage.tsx +++ b/src/pages/ReportDetailsPage.tsx @@ -328,7 +328,7 @@ function ReportDetailsPage({policy, report, route, reportMetadata}: ReportDetail Navigation.dismissModal(); Navigation.isNavigationReady().then(() => { if (isRootGroupChat) { - leaveGroupChat(report.reportID, quickAction?.chatReportID?.toString() === report.reportID); + leaveGroupChat(report.reportID, quickAction?.chatReportID?.toString() === report.reportID, currentUserPersonalDetails.accountID); return; } const isWorkspaceMemberLeavingWorkspaceRoom = isWorkspaceMemberLeavingWorkspaceRoomUtil(report, isPolicyEmployee, isPolicyAdmin); From d8d76bb2eacfab3d4c7c107a61fa820accba6ab9 Mon Sep 17 00:00:00 2001 From: Linh Date: Tue, 13 Jan 2026 21:28:00 +0700 Subject: [PATCH 9/9] chore: remove currentUserAccountID from Onyx --- src/libs/actions/Report.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index b2348e1b00f5..4aabb6636cde 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -3671,7 +3671,7 @@ function toggleEmojiReaction( const skinTone = emoji.types === undefined ? CONST.EMOJI_DEFAULT_SKIN_TONE : paramSkinTone; if (existingReactionObject && EmojiUtils.hasAccountIDEmojiReacted(currentUserAccountID, existingReactionObject.users, ignoreSkinToneOnCompare ? undefined : skinTone)) { - removeEmojiReaction(originalReportID, reportAction.reportActionID, emoji); + removeEmojiReaction(originalReportID, reportAction.reportActionID, emoji, currentUserAccountID); return; }