Skip to content

Commit 44f789a

Browse files
authored
Merge pull request #47207 from FitseTLT/fix-member-count-bug
Fix - Incorrect member count in report details after commenting or inviting another member
2 parents b7f7f1b + 57920c3 commit 44f789a

File tree

4 files changed

+42
-26
lines changed

4 files changed

+42
-26
lines changed

src/libs/ReportUtils.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ import isReportMessageAttachment from './isReportMessageAttachment';
6868
import localeCompare from './LocaleCompare';
6969
import * as LocalePhoneNumber from './LocalePhoneNumber';
7070
import * as Localize from './Localize';
71+
import Log from './Log';
7172
import {isEmailPublicDomain} from './LoginUtils';
7273
import ModifiedExpenseMessage from './ModifiedExpenseMessage';
7374
import linkingConfig from './Navigation/linkingConfig';
@@ -2079,6 +2080,37 @@ function getParticipantsAccountIDsForDisplay(report: OnyxEntry<Report>, shouldEx
20792080
return participantsEntries.map(([accountID]) => Number(accountID));
20802081
}
20812082

2083+
function getParticipantsList(report: Report, personalDetails: OnyxEntry<PersonalDetailsList>, isRoomMembersList = false): number[] {
2084+
const isReportGroupChat = isGroupChat(report);
2085+
const isReportIOU = isIOUReport(report);
2086+
const shouldExcludeHiddenParticipants = !isReportGroupChat && !isReportIOU;
2087+
const chatParticipants = getParticipantsAccountIDsForDisplay(report, isRoomMembersList || shouldExcludeHiddenParticipants);
2088+
2089+
return chatParticipants.filter((accountID) => {
2090+
const details = personalDetails?.[accountID];
2091+
2092+
if (!isRoomMembersList) {
2093+
if (!details) {
2094+
Log.hmmm(`[ReportParticipantsPage] no personal details found for Group chat member with accountID: ${accountID}`);
2095+
return false;
2096+
}
2097+
} else {
2098+
// When adding a new member to a room (whose personal detail does not exist in Onyx), an optimistic personal detail
2099+
// is created. However, when the real personal detail is returned from the backend, a duplicate member may appear
2100+
// briefly before the optimistic personal detail is deleted. To address this, we filter out the optimistically created
2101+
// member here.
2102+
const isDuplicateOptimisticDetail =
2103+
details?.isOptimisticPersonalDetail && chatParticipants.some((accID) => accID !== accountID && details.login === personalDetails?.[accID]?.login);
2104+
2105+
if (!details || isDuplicateOptimisticDetail) {
2106+
Log.hmmm(`[RoomMembersPage] no personal details found for room member with accountID: ${accountID}`);
2107+
return false;
2108+
}
2109+
}
2110+
return true;
2111+
});
2112+
}
2113+
20822114
function buildParticipantsFromAccountIDs(accountIDs: number[]): Participants {
20832115
const finalParticipants: Participants = {};
20842116
return accountIDs.reduce((participants, accountID) => {
@@ -7724,6 +7756,7 @@ export {
77247756
getParentNavigationSubtitle,
77257757
getParsedComment,
77267758
getParticipantsAccountIDsForDisplay,
7759+
getParticipantsList,
77277760
getParticipants,
77287761
getPendingChatMembers,
77297762
getPersonalDetailsForAccountID,

src/pages/ReportDetailsPage.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -136,10 +136,10 @@ function ReportDetailsPage({policies, report, session, personalDetails}: ReportD
136136
const isGroupChat = useMemo(() => ReportUtils.isGroupChat(report), [report]);
137137
const isRootGroupChat = useMemo(() => ReportUtils.isRootGroupChat(report), [report]);
138138
const isThread = useMemo(() => ReportUtils.isThread(report), [report]);
139+
const shouldOpenRoomMembersPage = isUserCreatedPolicyRoom || isChatThread || (isPolicyExpenseChat && isPolicyAdmin);
139140
const participants = useMemo(() => {
140-
const shouldExcludeHiddenParticipants = !isGroupChat && !isSystemChat;
141-
return ReportUtils.getParticipantsAccountIDsForDisplay(report, shouldExcludeHiddenParticipants);
142-
}, [report, isGroupChat, isSystemChat]);
141+
return ReportUtils.getParticipantsList(report, personalDetails, shouldOpenRoomMembersPage);
142+
}, [report, personalDetails, shouldOpenRoomMembersPage]);
143143
const connectedIntegration = PolicyUtils.getConnectedIntegration(policy);
144144

145145
// Get the active chat members by filtering out the pending members with delete action
@@ -307,7 +307,7 @@ function ReportDetailsPage({policies, report, session, personalDetails}: ReportD
307307
isAnonymousAction: false,
308308
shouldShowRightIcon: true,
309309
action: () => {
310-
if (isUserCreatedPolicyRoom || isChatThread || (isPolicyExpenseChat && isPolicyAdmin)) {
310+
if (shouldOpenRoomMembersPage) {
311311
Navigation.navigate(ROUTES.ROOM_MEMBERS.getRoute(report?.reportID ?? '-1'));
312312
} else {
313313
Navigation.navigate(ROUTES.REPORT_PARTICIPANTS.getRoute(report?.reportID ?? '-1'));
@@ -443,6 +443,7 @@ function ReportDetailsPage({policies, report, session, personalDetails}: ReportD
443443
isCanceledTaskReport,
444444
shouldShowLeaveButton,
445445
activeChatMembers.length,
446+
shouldOpenRoomMembersPage,
446447
shouldShowCancelPaymentButton,
447448
session,
448449
leaveChat,

src/pages/ReportParticipantsPage.tsx

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import useStyleUtils from '@hooks/useStyleUtils';
2323
import useThemeStyles from '@hooks/useThemeStyles';
2424
import {turnOffMobileSelectionMode} from '@libs/actions/MobileSelectionMode';
2525
import * as Report from '@libs/actions/Report';
26-
import Log from '@libs/Log';
2726
import Navigation from '@libs/Navigation/Navigation';
2827
import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils';
2928
import * as ReportUtils from '@libs/ReportUtils';
@@ -51,7 +50,6 @@ function ReportParticipantsPage({report}: WithReportOrNotFoundProps) {
5150
const currentUserAccountID = Number(session?.accountID);
5251
const isCurrentUserAdmin = ReportUtils.isGroupChatAdmin(report, currentUserAccountID);
5352
const isGroupChat = useMemo(() => ReportUtils.isGroupChat(report), [report]);
54-
const isIOUReport = ReportUtils.isIOUReport(report);
5553
const isFocused = useIsFocused();
5654
const canSelectMultiple = isGroupChat && isCurrentUserAdmin && (isSmallScreenWidth ? selectionMode?.isEnabled : true);
5755

@@ -64,15 +62,10 @@ function ReportParticipantsPage({report}: WithReportOrNotFoundProps) {
6462

6563
const getUsers = useCallback((): MemberOption[] => {
6664
let result: MemberOption[] = [];
67-
const shouldExcludeHiddenParticipants = !isGroupChat && !isIOUReport;
68-
const chatParticipants = ReportUtils.getParticipantsAccountIDsForDisplay(report, shouldExcludeHiddenParticipants);
65+
const chatParticipants = ReportUtils.getParticipantsList(report, personalDetails);
6966
chatParticipants.forEach((accountID) => {
7067
const role = report.participants?.[accountID].role;
7168
const details = personalDetails?.[accountID];
72-
if (!details) {
73-
Log.hmmm(`[ReportParticipantsPage] no personal details found for Group chat member with accountID: ${accountID}`);
74-
return;
75-
}
7669

7770
const pendingChatMember = report?.pendingChatMembers?.findLast((member) => member.accountID === accountID.toString());
7871
const isSelected = selectedMembers.includes(accountID) && canSelectMultiple;
@@ -107,7 +100,7 @@ function ReportParticipantsPage({report}: WithReportOrNotFoundProps) {
107100

108101
result = result.sort((a, b) => (a.text ?? '').toLowerCase().localeCompare((b.text ?? '').toLowerCase()));
109102
return result;
110-
}, [formatPhoneNumber, personalDetails, report, selectedMembers, currentUserAccountID, translate, isGroupChat, isIOUReport, canSelectMultiple]);
103+
}, [formatPhoneNumber, personalDetails, report, selectedMembers, currentUserAccountID, translate, canSelectMultiple]);
111104

112105
const participants = useMemo(() => getUsers(), [getUsers]);
113106

src/pages/RoomMembersPage.tsx

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import useLocalize from '@hooks/useLocalize';
2020
import useThemeStyles from '@hooks/useThemeStyles';
2121
import * as DeviceCapabilities from '@libs/DeviceCapabilities';
2222
import localeCompare from '@libs/LocaleCompare';
23-
import Log from '@libs/Log';
2423
import Navigation from '@libs/Navigation/Navigation';
2524
import type {RoomMembersNavigatorParamList} from '@libs/Navigation/types';
2625
import * as OptionsListUtils from '@libs/OptionsListUtils';
@@ -170,23 +169,13 @@ function RoomMembersPage({report, session, policies}: RoomMembersPageProps) {
170169
const getMemberOptions = (): ListItem[] => {
171170
let result: ListItem[] = [];
172171

173-
const participants = ReportUtils.getParticipantsAccountIDsForDisplay(report, true);
172+
const participants = ReportUtils.getParticipantsList(report, personalDetails, true);
174173

175174
participants.forEach((accountID) => {
176175
const details = personalDetails[accountID];
177-
// When adding a new member to a room (whose personal detail does not exist in Onyx), an optimistic personal detail
178-
// is created. However, when the real personal detail is returned from the backend, a duplicate member may appear
179-
// briefly before the optimistic personal detail is deleted. To address this, we filter out the optimistically created
180-
// member here.
181-
const isDuplicateOptimisticDetail = details?.isOptimisticPersonalDetail && participants.some((accID) => accID !== accountID && details.login === personalDetails[accID]?.login);
182-
183-
if (!details || isDuplicateOptimisticDetail) {
184-
Log.hmmm(`[RoomMembersPage] no personal details found for room member with accountID: ${accountID}`);
185-
return;
186-
}
187176

188177
// If search value is provided, filter out members that don't match the search value
189-
if (searchValue.trim() && !OptionsListUtils.isSearchStringMatchUserDetails(details, searchValue)) {
178+
if (!details || (searchValue.trim() && !OptionsListUtils.isSearchStringMatchUserDetails(details, searchValue))) {
190179
return;
191180
}
192181
const pendingChatMember = report?.pendingChatMembers?.findLast((member) => member.accountID === accountID.toString());

0 commit comments

Comments
 (0)