diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index e062a4857e19..69ed92689b6c 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -45,7 +45,7 @@ import type { ReimbursementDeQueuedMessage, } from '@src/types/onyx/OriginalMessage'; import type {Status} from '@src/types/onyx/PersonalDetails'; -import type {NotificationPreference} from '@src/types/onyx/Report'; +import type {NotificationPreference, PendingChatMember} from '@src/types/onyx/Report'; import type {Message, ReportActionBase, ReportActions} from '@src/types/onyx/ReportAction'; import type {Receipt, TransactionChanges, WaypointCollection} from '@src/types/onyx/Transaction'; import type {EmptyObject} from '@src/types/utils/EmptyObject'; @@ -2697,6 +2697,14 @@ function getChatRoomSubtitle(report: OnyxEntry): string | undefined { return getPolicyName(report); } +/** + * Get pending members for reports + */ +function getPendingChatMembers(accountIDs: number[], previousPendingChatMembers: PendingChatMember[], pendingAction: PendingAction): PendingChatMember[] { + const pendingChatMembers = accountIDs.map((accountID) => ({accountID: accountID.toString(), pendingAction})); + return [...previousPendingChatMembers, ...pendingChatMembers]; +} + /** * Gets the parent navigation subtitle for the report */ @@ -3829,16 +3837,19 @@ function buildOptimisticWorkspaceChats(policyID: string, policyName: string): Op const announceReportActionData = { [announceCreatedAction.reportActionID]: announceCreatedAction, }; - - const adminsChatData = buildOptimisticChatReport( - [currentUserAccountID ?? -1], - CONST.REPORT.WORKSPACE_CHAT_ROOMS.ADMINS, - CONST.REPORT.CHAT_TYPE.POLICY_ADMINS, - policyID, - CONST.POLICY.OWNER_ACCOUNT_ID_FAKE, - false, - policyName, - ); + const pendingChatMembers = getPendingChatMembers(currentUserAccountID ? [currentUserAccountID] : [], [], CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD); + const adminsChatData = { + ...buildOptimisticChatReport( + [currentUserAccountID ?? -1], + CONST.REPORT.WORKSPACE_CHAT_ROOMS.ADMINS, + CONST.REPORT.CHAT_TYPE.POLICY_ADMINS, + policyID, + CONST.POLICY.OWNER_ACCOUNT_ID_FAKE, + false, + policyName, + ), + pendingChatMembers, + }; const adminsChatReportID = adminsChatData.reportID; const adminsCreatedAction = buildOptimisticCreatedReportAction(CONST.POLICY.OWNER_EMAIL_FAKE); const adminsReportActionData = { @@ -5388,6 +5399,7 @@ export { getAvailableReportFields, reportFieldsEnabled, getAllAncestorReportActionIDs, + getPendingChatMembers, canEditRoomVisibility, canEditPolicyDescription, getPolicyDescriptionText, diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index f6a1ec3ec340..f7203edd9b6b 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -66,6 +66,7 @@ import {isEmptyObject} from '@src/types/utils/EmptyObject'; type AnnounceRoomMembersOnyxData = { onyxOptimisticData: OnyxUpdate[]; + onyxSuccessData: OnyxUpdate[]; onyxFailureData: OnyxUpdate[]; }; @@ -373,6 +374,7 @@ function buildAnnounceRoomMembersOnyxData(policyID: string, accountIDs: number[] const announceRoomMembers: AnnounceRoomMembersOnyxData = { onyxOptimisticData: [], onyxFailureData: [], + onyxSuccessData: [], }; if (!announceReport) { @@ -382,6 +384,7 @@ function buildAnnounceRoomMembersOnyxData(policyID: string, accountIDs: number[] if (announceReport?.participantAccountIDs) { // Everyone in special policy rooms is visible const participantAccountIDs = [...announceReport.participantAccountIDs, ...accountIDs]; + const pendingChatMembers = ReportUtils.getPendingChatMembers(accountIDs, announceReport?.pendingChatMembers ?? [], CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD); announceRoomMembers.onyxOptimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, @@ -389,6 +392,7 @@ function buildAnnounceRoomMembersOnyxData(policyID: string, accountIDs: number[] value: { participantAccountIDs, visibleChatMemberAccountIDs: participantAccountIDs, + pendingChatMembers, }, }); } @@ -399,6 +403,14 @@ function buildAnnounceRoomMembersOnyxData(policyID: string, accountIDs: number[] value: { participantAccountIDs: announceReport?.participantAccountIDs, visibleChatMemberAccountIDs: announceReport?.visibleChatMemberAccountIDs, + pendingChatMembers: announceReport?.pendingChatMembers ?? null, + }, + }); + announceRoomMembers.onyxSuccessData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${announceReport?.reportID}`, + value: { + pendingChatMembers: announceReport?.pendingChatMembers ?? null, }, }); return announceRoomMembers; @@ -572,6 +584,7 @@ function removeOptimisticAnnounceRoomMembers(policyID: string, accountIDs: numbe const announceRoomMembers: AnnounceRoomMembersOnyxData = { onyxOptimisticData: [], onyxFailureData: [], + onyxSuccessData: [], }; if (!announceReport) { @@ -580,12 +593,15 @@ function removeOptimisticAnnounceRoomMembers(policyID: string, accountIDs: numbe if (announceReport?.participantAccountIDs) { const remainUsers = announceReport.participantAccountIDs.filter((e) => !accountIDs.includes(e)); + const pendingChatMembers = ReportUtils.getPendingChatMembers(accountIDs, announceReport?.pendingChatMembers ?? [], CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE); + announceRoomMembers.onyxOptimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${announceReport.reportID}`, value: { participantAccountIDs: [...remainUsers], visibleChatMemberAccountIDs: [...remainUsers], + pendingChatMembers, }, }); @@ -595,6 +611,14 @@ function removeOptimisticAnnounceRoomMembers(policyID: string, accountIDs: numbe value: { participantAccountIDs: announceReport.participantAccountIDs, visibleChatMemberAccountIDs: announceReport.visibleChatMemberAccountIDs, + pendingChatMembers: announceReport?.pendingChatMembers ?? null, + }, + }); + announceRoomMembers.onyxSuccessData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${announceReport.reportID}`, + value: { + pendingChatMembers: announceReport?.pendingChatMembers ?? null, }, }); } @@ -638,6 +662,26 @@ function removeMembers(accountIDs: number[], policyID: string) { ...announceRoomMembers.onyxOptimisticData, ]; + const successData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: membersListKey, + value: successMembersState, + }, + ...announceRoomMembers.onyxSuccessData, + ]; + + const failureData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: membersListKey, + value: failureMembersState, + }, + ...announceRoomMembers.onyxFailureData, + ]; + + const pendingChatMembers = ReportUtils.getPendingChatMembers(accountIDs, [], CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE); + workspaceChats.forEach((report) => { optimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, @@ -647,6 +691,21 @@ function removeMembers(accountIDs: number[], policyID: string) { stateNum: CONST.REPORT.STATE_NUM.APPROVED, oldPolicyName: policy.name, hasDraft: false, + pendingChatMembers, + }, + }); + successData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${report?.reportID}`, + value: { + pendingChatMembers: null, + }, + }); + failureData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${report?.reportID}`, + value: { + pendingChatMembers: null, }, }); }); @@ -683,23 +742,7 @@ function removeMembers(accountIDs: number[], policyID: string) { } } - const successData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: membersListKey, - value: successMembersState, - }, - ]; - const filteredWorkspaceChats = workspaceChats.filter((report): report is Report => report !== null); - const failureData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: membersListKey, - value: failureMembersState, - }, - ...announceRoomMembers.onyxFailureData, - ]; filteredWorkspaceChats.forEach(({reportID, stateNum, statusNum, hasDraft, oldPolicyName = null}) => { failureData.push({ @@ -847,6 +890,12 @@ function createPolicyExpenseChats(policyID: string, invitedEmailsToAccountIDs: I }, isOptimisticReport: true, hasOutstandingChildRequest, + pendingChatMembers: [ + { + accountID: accountID.toString(), + pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, + }, + ], }, }); workspaceMembersChats.onyxOptimisticData.push({ @@ -866,6 +915,7 @@ function createPolicyExpenseChats(policyID: string, invitedEmailsToAccountIDs: I createChat: null, }, isOptimisticReport: false, + pendingChatMembers: null, }, }); workspaceMembersChats.onyxSuccessData.push({ @@ -943,6 +993,7 @@ function addMembersToWorkspace(invitedEmailsToAccountIDs: InvitedEmailsToAccount }, ...newPersonalDetailsOnyxData.finallyData, ...membersChats.onyxSuccessData, + ...announceRoomMembers.onyxSuccessData, ]; const failureData: OnyxUpdate[] = [ @@ -1577,7 +1628,6 @@ function createWorkspace(policyOwnerEmail = '', makeMeAdmin = false, policyName expenseReportActionData, expenseCreatedReportActionID, } = ReportUtils.buildOptimisticWorkspaceChats(policyID, workspaceName); - const optimisticData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.SET, @@ -1701,6 +1751,7 @@ function createWorkspace(policyOwnerEmail = '', makeMeAdmin = false, policyName addWorkspaceRoom: null, }, pendingAction: null, + pendingChatMembers: [], }, }, { diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 8399070ed21b..2c797ccba9c2 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -2416,6 +2416,7 @@ function inviteToRoom(reportID: string, inviteeEmailsToAccountIDs: Record PhoneNumber.addSMSDomainIfPhoneNumber(memberLogin)); const newPersonalDetailsOnyxData = PersonalDetailsUtils.getNewPersonalDetailsOnyxData(logins, inviteeAccountIDs); + const pendingChatMembers = ReportUtils.getPendingChatMembers(inviteeAccountIDs, report?.pendingChatMembers ?? [], CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD); const optimisticData: OnyxUpdate[] = [ { @@ -2424,13 +2425,22 @@ function inviteToRoom(reportID: string, inviteeEmailsToAccountIDs: Record !targetAccountIDs.includes(id)); const visibleChatMemberAccountIDsAfterRemoval = report?.visibleChatMemberAccountIDs?.filter((id: number) => !targetAccountIDs.includes(id)); + const pendingChatMembers = ReportUtils.getPendingChatMembers(targetAccountIDs, report?.pendingChatMembers ?? [], CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE); const optimisticData: OnyxUpdate[] = [ { @@ -2467,6 +2479,7 @@ function removeFromRoom(reportID: string, targetAccountIDs: number[]) { value: { participantAccountIDs: participantAccountIDsAfterRemoval, visibleChatMemberAccountIDs: visibleChatMemberAccountIDsAfterRemoval, + pendingChatMembers, }, }, ]; @@ -2478,6 +2491,7 @@ function removeFromRoom(reportID: string, targetAccountIDs: number[]) { value: { participantAccountIDs: report?.participantAccountIDs, visibleChatMemberAccountIDs: report?.visibleChatMemberAccountIDs, + pendingChatMembers: report?.pendingChatMembers ?? null, }, }, ]; @@ -2491,6 +2505,7 @@ function removeFromRoom(reportID: string, targetAccountIDs: number[]) { value: { participantAccountIDs: participantAccountIDsAfterRemoval, visibleChatMemberAccountIDs: visibleChatMemberAccountIDsAfterRemoval, + pendingChatMembers: report?.pendingChatMembers ?? null, }, }, ]; diff --git a/src/pages/ReportParticipantsPage.tsx b/src/pages/ReportParticipantsPage.tsx index 24d696ca2fb0..a2b2f094ac26 100755 --- a/src/pages/ReportParticipantsPage.tsx +++ b/src/pages/ReportParticipantsPage.tsx @@ -50,8 +50,10 @@ const getAllParticipants = ( !!userPersonalDetail?.login && !CONST.RESTRICTED_ACCOUNT_IDS.includes(accountID) ? LocalePhoneNumber.formatPhoneNumber(userPersonalDetail.login) : translate('common.hidden'); const displayName = PersonalDetailsUtils.getDisplayNameOrDefault(userPersonalDetail); + const pendingChatMember = report?.pendingChatMembers?.find((member) => member.accountID === accountID.toString()); return { alternateText: userLogin, + pendingAction: pendingChatMember?.pendingAction, displayName, accountID: userPersonalDetail?.accountID ?? accountID, icons: [ diff --git a/src/pages/RoomMembersPage.tsx b/src/pages/RoomMembersPage.tsx index 557211dc0235..47ba5174e4b0 100644 --- a/src/pages/RoomMembersPage.tsx +++ b/src/pages/RoomMembersPage.tsx @@ -183,6 +183,7 @@ function RoomMembersPage({report, session, policies}: RoomMembersPageProps) { return; } } + const pendingChatMember = report?.pendingChatMembers?.find((member) => member.accountID === accountID.toString()); result.push({ keyForList: String(accountID), @@ -199,6 +200,7 @@ function RoomMembersPage({report, session, policies}: RoomMembersPageProps) { id: Number(accountID), }, ], + pendingAction: pendingChatMember?.pendingAction, }); }); diff --git a/src/types/onyx/Report.ts b/src/types/onyx/Report.ts index 22618bb357d0..1f3b49ff77b0 100644 --- a/src/types/onyx/Report.ts +++ b/src/types/onyx/Report.ts @@ -17,6 +17,12 @@ type Note = OnyxCommon.OnyxValueWithOfflineFeedback<{ errors?: OnyxCommon.Errors; }>; +/** The pending member of report */ +type PendingChatMember = { + accountID: string; + pendingAction: OnyxCommon.PendingAction; +}; + type Participant = { hidden: boolean; role?: 'admin' | 'member'; @@ -170,6 +176,9 @@ type Report = OnyxCommon.OnyxValueWithOfflineFeedback< isLoadingPrivateNotes?: boolean; selected?: boolean; + /** Pending members of the report */ + pendingChatMembers?: PendingChatMember[]; + /** If the report contains reportFields, save the field id and its value */ reportFields?: Record; }, @@ -180,4 +189,4 @@ type ReportCollectionDataSet = CollectionDataSet