Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Participants migration polish #43610

Merged
merged 10 commits into from
Jun 18, 2024
2 changes: 1 addition & 1 deletion src/components/LHNOptionsList/OptionRowLHN.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ function OptionRowLHN({reportID, isFocused = false, onSelectRow = () => {}, opti

const isGroupChat = ReportUtils.isGroupChat(optionItem) || ReportUtils.isDeprecatedGroupDM(optionItem);

const fullTitle = isGroupChat ? ReportUtils.getGroupChatName(undefined, false, optionItem.reportID ?? '') : optionItem.text;
const fullTitle = isGroupChat ? ReportUtils.getGroupChatName(undefined, false, report) : optionItem.text;
const subscriptAvatarBorderColor = isFocused ? focusedBackgroundColor : theme.sidebar;
return (
<OfflineWithFeedback
Expand Down
8 changes: 2 additions & 6 deletions src/components/ReportWelcomeText.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, {useMemo} from 'react';
import {View} from 'react-native';
import type {OnyxEntry} from 'react-native-onyx';
import {useOnyx, withOnyx} from 'react-native-onyx';
import {withOnyx} from 'react-native-onyx';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import Navigation from '@libs/Navigation/Navigation';
Expand Down Expand Up @@ -33,17 +33,13 @@ type ReportWelcomeTextProps = ReportWelcomeTextOnyxProps & {
function ReportWelcomeText({report, policy, personalDetails}: ReportWelcomeTextProps) {
const {translate} = useLocalize();
const styles = useThemeStyles();
const [session] = useOnyx(ONYXKEYS.SESSION);
const isPolicyExpenseChat = ReportUtils.isPolicyExpenseChat(report);
const isChatRoom = ReportUtils.isChatRoom(report);
const isSelfDM = ReportUtils.isSelfDM(report);
const isInvoiceRoom = ReportUtils.isInvoiceRoom(report);
const isOneOnOneChat = ReportUtils.isOneOnOneChat(report);
const isSystemChat = ReportUtils.isSystemChat(report);
const isDefault = !(isChatRoom || isPolicyExpenseChat || isSelfDM || isInvoiceRoom || isSystemChat);
const participantAccountIDs = Object.keys(report?.participants ?? {})
.map(Number)
.filter((accountID) => accountID !== session?.accountID || (!isOneOnOneChat && !isSystemChat));
const participantAccountIDs = ReportUtils.getParticipantsAccountIDsForDisplay(report);
const isMultipleParticipant = participantAccountIDs.length > 1;
const displayNamesWithTooltips = ReportUtils.getDisplayNamesWithTooltips(OptionsListUtils.getPersonalDetailsForAccountIDs(participantAccountIDs, personalDetails), isMultipleParticipant);
const roomWelcomeMessage = ReportUtils.getRoomWelcomeMessage(report);
Expand Down
35 changes: 6 additions & 29 deletions src/libs/OptionsListUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -773,15 +773,10 @@ function createOption(
result.policyID = report.policyID;
result.isSelfDM = ReportUtils.isSelfDM(report);

// For 1:1 chat, we don't want to include currentUser as participants in order to not mark 1:1 chats as having multiple participants
const isOneOnOneChat = ReportUtils.isOneOnOneChat(report);
const visibleParticipantAccountIDs = Object.entries(report.participants ?? {})
.filter(([, participant]) => participant && !participant.hidden)
.map(([accountID]) => Number(accountID))
.filter((accountID) => accountID !== currentUserAccountID || !isOneOnOneChat);
const visibleParticipantAccountIDs = ReportUtils.getParticipantsAccountIDsForDisplay(report, true);

result.tooltipText = ReportUtils.getReportParticipantsTitle(visibleParticipantAccountIDs);
result.isOneOnOneChat = isOneOnOneChat;
result.isOneOnOneChat = ReportUtils.isOneOnOneChat(report);
s77rt marked this conversation as resolved.
Show resolved Hide resolved

hasMultipleParticipants = personalDetailList.length > 1 || result.isChatRoom || result.isPolicyExpenseChat || ReportUtils.isGroupChat(report);
subtitle = ReportUtils.getChatRoomSubtitle(report);
Expand Down Expand Up @@ -838,13 +833,7 @@ function createOption(
*/
function getReportOption(participant: Participant): ReportUtils.OptionData {
const report = ReportUtils.getReport(participant.reportID);

// For 1:1 chat, we don't want to include currentUser as participants in order to not mark 1:1 chats as having multiple participants
const isOneOnOneChat = ReportUtils.isOneOnOneChat(report);
const visibleParticipantAccountIDs = Object.entries(report?.participants ?? {})
.filter(([, reportParticipant]) => reportParticipant && !reportParticipant.hidden)
.map(([accountID]) => Number(accountID))
.filter((accountID) => accountID !== currentUserAccountID || !isOneOnOneChat);
const visibleParticipantAccountIDs = ReportUtils.getParticipantsAccountIDsForDisplay(report, true);

const option = createOption(
visibleParticipantAccountIDs,
Expand Down Expand Up @@ -1553,11 +1542,8 @@ function createOptionList(personalDetails: OnyxEntry<PersonalDetailsList>, repor
return;
}

// For 1:1 chat, we don't want to include currentUser as participants in order to not mark 1:1 chats as having multiple participants
const isOneOnOneChat = ReportUtils.isOneOnOneChat(report);
const accountIDs = Object.keys(report.participants ?? {})
.map(Number)
.filter((accountID) => accountID !== currentUserAccountID || !isOneOnOneChat);
const accountIDs = ReportUtils.getParticipantsAccountIDsForDisplay(report);

const isChatRoom = ReportUtils.isChatRoom(report);
if ((!accountIDs || accountIDs.length === 0) && !isChatRoom) {
Expand Down Expand Up @@ -1590,11 +1576,7 @@ function createOptionList(personalDetails: OnyxEntry<PersonalDetailsList>, repor
}

function createOptionFromReport(report: Report, personalDetails: OnyxEntry<PersonalDetailsList>) {
// For 1:1 chat, we don't want to include currentUser as participants in order to not mark 1:1 chats as having multiple participants
const isOneOnOneChat = ReportUtils.isOneOnOneChat(report);
const accountIDs = Object.keys(report.participants ?? {})
.map(Number)
.filter((accountID) => accountID !== currentUserAccountID || !isOneOnOneChat);
const accountIDs = ReportUtils.getParticipantsAccountIDsForDisplay(report);

return {
item: report,
Expand Down Expand Up @@ -1863,13 +1845,8 @@ function getOptions(
const isPolicyExpenseChat = option.isPolicyExpenseChat;
const isMoneyRequestReport = option.isMoneyRequestReport;
const isSelfDM = option.isSelfDM;
const isOneOnOneChat = option.isOneOnOneChat;
const isChatRoom = option.isChatRoom;

// For 1:1 chat, we don't want to include currentUser as participants in order to not mark 1:1 chats as having multiple participants
const accountIDs = Object.keys(report.participants ?? {})
.map(Number)
.filter((accountID) => accountID !== currentUserAccountID || !isOneOnOneChat);
const accountIDs = ReportUtils.getParticipantsAccountIDsForDisplay(report);

if (isPolicyExpenseChat && report.isOwnPolicyExpenseChat && !includeOwnedWorkspaceChats) {
return;
Expand Down
96 changes: 37 additions & 59 deletions src/libs/ReportUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1617,13 +1617,7 @@ function getReportRecipientAccountIDs(report: OnyxEntry<Report>, currentLoginAcc
}

let finalParticipantAccountIDs: number[] = [];
if (isMoneyRequestReport(report)) {
s77rt marked this conversation as resolved.
Show resolved Hide resolved
// For money requests i.e the IOU (1:1 person) and Expense (1:* person) reports, use the full `participants`
// and add the `ownerAccountId`. Money request reports don't add `ownerAccountId` in `participants` array
const defaultParticipantAccountIDs = Object.keys(finalReport?.participants ?? {}).map(Number);
const setOfParticipantAccountIDs = new Set<number>(report?.ownerAccountID ? [...defaultParticipantAccountIDs, report.ownerAccountID] : defaultParticipantAccountIDs);
finalParticipantAccountIDs = [...setOfParticipantAccountIDs];
} else if (isTaskReport(report)) {
if (isTaskReport(report)) {
// Task reports `managerID` will change when assignee is changed, in that case the old `managerID` is still present in `participants`
// along with the new one. We only need the `managerID` as a participant here.
finalParticipantAccountIDs = report?.managerID ? [report?.managerID] : [];
Expand Down Expand Up @@ -1837,19 +1831,32 @@ function getDisplayNameForParticipant(accountID?: number, shouldUseShortForm = f
return shouldUseShortForm ? shortName : longName;
}

function getParticipantAccountIDs(reportID: string, includeOnlyActiveMembers = false) {
const report = getReport(reportID);
if (!report || !report.participants) {
return [];
}
const accountIDStrings = Object.keys(report.participants).filter((accountID) => {
if (!includeOnlyActiveMembers) {
function getParticipantsAccountIDsForDisplay(report: OnyxEntry<Report>, shouldExcludeHidden = false, shouldExcludeDeleted = false): number[] {
let participantsEntries = Object.entries(report?.participants ?? {});

// For 1:1 chat, we don't want to include the current user as a participant in order to not mark 1:1 chats as having multiple participants
// For system chat, we want to display Expensify as the only participant
const shouldExcludeCurrentUser = isOneOnOneChat(report) || isSystemChat(report);

if (shouldExcludeCurrentUser || shouldExcludeHidden || shouldExcludeDeleted) {
participantsEntries = participantsEntries.filter(([accountID, participant]) => {
if (shouldExcludeCurrentUser && Number(accountID) === currentUserAccountID) {
return false;
}

if (shouldExcludeHidden && participant.hidden) {
return false;
}

if (shouldExcludeDeleted && report?.pendingChatMembers?.findLast((member) => member.accountID === accountID)?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE) {
return false;
}

return true;
}
const pendingMember = report?.pendingChatMembers?.findLast((member) => member.accountID === accountID.toString());
return !pendingMember || pendingMember.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE;
});
return accountIDStrings.map((accountID) => Number(accountID));
});
}

return participantsEntries.map(([accountID]) => Number(accountID));
}

function buildParticipantsFromAccountIDs(accountIDs: number[]): Participants {
Expand All @@ -1864,18 +1871,14 @@ function buildParticipantsFromAccountIDs(accountIDs: number[]): Participants {
/**
* Returns the report name if the report is a group chat
*/
function getGroupChatName(participantAccountIDs?: number[], shouldApplyLimit = false, reportID = ''): string | undefined {
// If we have a reportID always try to get the name from the report.
if (reportID) {
const reportKey = `${ONYXKEYS.COLLECTION.REPORT}${reportID}`;
const reportName = allReports?.[reportKey]?.reportName;
if (reportName) {
return reportName;
}
function getGroupChatName(participantAccountIDs?: number[], shouldApplyLimit = false, report?: OnyxEntry<Report>): string | undefined {
// If we have a report always try to get the name from the report.
if (report?.reportName) {
return report.reportName;
}

// Get participantAccountIDs from participants object
let participants = participantAccountIDs ?? getParticipantAccountIDs(reportID);
let participants = participantAccountIDs ?? Object.keys(report?.participants ?? {}).map(Number);
if (shouldApplyLimit) {
participants = participants.slice(0, 5);
}
Expand All @@ -1892,20 +1895,6 @@ function getGroupChatName(participantAccountIDs?: number[], shouldApplyLimit = f
return Localize.translateLocal('groupChat.defaultReportName', {displayName: getDisplayNameForParticipant(participants[0], false)});
}

function getVisibleChatMemberAccountIDs(reportID: string): number[] {
const report = getReport(reportID);
if (!report || !report.participants) {
return [];
}
const visibleParticipantAccountIDs = Object.entries(report.participants).reduce<number[]>((accountIDs, [accountID, participant]) => {
if (participant && !participant.hidden) {
accountIDs.push(Number(accountID));
}
return accountIDs;
}, []);
return visibleParticipantAccountIDs;
}

function getParticipants(reportID: string) {
const report = getReport(reportID);
if (!report) {
Expand Down Expand Up @@ -2062,7 +2051,7 @@ function getIcons(
source: report.avatarUrl || getDefaultGroupAvatar(report.reportID),
id: -1,
type: CONST.ICON_TYPE_AVATAR,
name: getGroupChatName(undefined, true, report.reportID ?? ''),
name: getGroupChatName(undefined, true, report),
};
return [groupChatIcon];
}
Expand Down Expand Up @@ -3347,7 +3336,7 @@ function getReportName(report: OnyxEntry<Report>, policy: OnyxEntry<Policy> = nu
}

if (isGroupChat(report)) {
return getGroupChatName(undefined, true, report?.reportID) ?? '';
return getGroupChatName(undefined, true, report) ?? '';
}

if (isChatRoom(report) || isTaskReport(report)) {
Expand Down Expand Up @@ -3479,11 +3468,7 @@ function getParentNavigationSubtitle(report: OnyxEntry<Report>): ParentNavigatio
function navigateToDetailsPage(report: OnyxEntry<Report>) {
const isSelfDMReport = isSelfDM(report);
const isOneOnOneChatReport = isOneOnOneChat(report);

// For 1:1 chat, we don't want to include currentUser as participants in order to not mark 1:1 chats as having multiple participants
const participantAccountID = Object.keys(report?.participants ?? {})
.map(Number)
.filter((accountID) => accountID !== currentUserAccountID || !isOneOnOneChatReport);
const participantAccountID = getParticipantsAccountIDsForDisplay(report);

if (isSelfDMReport || isOneOnOneChatReport) {
Navigation.navigate(ROUTES.PROFILE.getRoute(participantAccountID[0]));
Expand All @@ -3500,11 +3485,7 @@ function navigateToDetailsPage(report: OnyxEntry<Report>) {
*/
function goBackToDetailsPage(report: OnyxEntry<Report>) {
const isOneOnOneChatReport = isOneOnOneChat(report);

// For 1:1 chat, we don't want to include currentUser as participants in order to not mark 1:1 chats as having multiple participants
const participantAccountID = Object.keys(report?.participants ?? {})
.map(Number)
.filter((accountID) => accountID !== currentUserAccountID || !isOneOnOneChatReport);
const participantAccountID = getParticipantsAccountIDsForDisplay(report);

if (isOneOnOneChatReport) {
Navigation.navigate(ROUTES.PROFILE.getRoute(participantAccountID[0]));
Expand All @@ -3523,9 +3504,7 @@ function goBackFromPrivateNotes(report: OnyxEntry<Report>, session: OnyxEntry<Se
}
const currentUserPrivateNote = report.privateNotes?.[session.accountID]?.note ?? '';
if (isEmpty(currentUserPrivateNote)) {
const participantAccountIDs = Object.keys(report?.participants ?? {})
.map(Number)
.filter((accountID) => accountID !== currentUserAccountID || !isOneOnOneChat(report));
const participantAccountIDs = getParticipantsAccountIDsForDisplay(report);

if (isOneOnOneChat(report)) {
Navigation.goBack(ROUTES.PROFILE.getRoute(participantAccountIDs[0]));
Expand Down Expand Up @@ -7043,7 +7022,7 @@ export {
getOutstandingChildRequest,
getParentNavigationSubtitle,
getParsedComment,
getParticipantAccountIDs,
getParticipantsAccountIDsForDisplay,
getParticipants,
getPendingChatMembers,
getPersonalDetailsForAccountID,
Expand Down Expand Up @@ -7075,7 +7054,6 @@ export {
getTransactionReportName,
getTransactionsWithReceipts,
getUserDetailTooltipText,
getVisibleChatMemberAccountIDs,
getWhisperDisplayNames,
getWorkspaceAvatar,
getWorkspaceChats,
Expand Down
13 changes: 3 additions & 10 deletions src/libs/SidebarUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -248,15 +248,8 @@ function getOptionData({
isDeletedParentAction: false,
};

// For 1:1 chat, we don't want to include currentUser as participants in order to not mark 1:1 chats as having multiple participants
const isOneOnOneChat = ReportUtils.isOneOnOneChat(report);
const participantAccountIDs = Object.keys(report.participants ?? {})
.map(Number)
.filter((accountID) => accountID !== currentUserAccountID || !isOneOnOneChat);
const visibleParticipantAccountIDs = Object.entries(report.participants ?? {})
.filter(([, participant]) => participant && !participant.hidden)
.map(([accountID]) => Number(accountID))
.filter((accountID) => accountID !== currentUserAccountID || !isOneOnOneChat);
const participantAccountIDs = ReportUtils.getParticipantsAccountIDsForDisplay(report);
const visibleParticipantAccountIDs = ReportUtils.getParticipantsAccountIDsForDisplay(report, true);

const participantPersonalDetailList = Object.values(OptionsListUtils.getPersonalDetailsForAccountIDs(participantAccountIDs, personalDetails)) as PersonalDetails[];
const personalDetail = participantPersonalDetailList[0] ?? {};
Expand Down Expand Up @@ -295,7 +288,7 @@ function getOptionData({
result.chatType = report.chatType;
result.isDeletedParentAction = report.isDeletedParentAction;
result.isSelfDM = ReportUtils.isSelfDM(report);
result.isOneOnOneChat = isOneOnOneChat;
result.isOneOnOneChat = ReportUtils.isOneOnOneChat(report);
s77rt marked this conversation as resolved.
Show resolved Hide resolved
result.tooltipText = ReportUtils.getReportParticipantsTitle(visibleParticipantAccountIDs);

const hasMultipleParticipants = participantPersonalDetailList.length > 1 || result.isChatRoom || result.isPolicyExpenseChat || ReportUtils.isExpenseReport(report);
Expand Down
4 changes: 1 addition & 3 deletions src/libs/actions/Task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -774,9 +774,7 @@ function getShareDestination(reportID: string, reports: OnyxCollection<OnyxTypes

const isOneOnOneChat = ReportUtils.isOneOnOneChat(report);

const participants = Object.keys(report?.participants ?? {})
.map(Number)
.filter((accountID) => accountID !== currentUserAccountID || !isOneOnOneChat);
const participants = ReportUtils.getParticipantsAccountIDsForDisplay(report);

const isMultipleParticipant = participants.length > 1;
const displayNamesWithTooltips = ReportUtils.getDisplayNamesWithTooltips(OptionsListUtils.getPersonalDetailsForAccountIDs(participants, personalDetails), isMultipleParticipant);
Expand Down
13 changes: 5 additions & 8 deletions src/pages/GroupChatNameEditPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,11 @@ function GroupChatNameEditPage({groupChatDraft, report}: GroupChatNameEditPagePr
const {inputCallbackRef} = useAutoFocusInput();

// We will try to get the chatName from the report or draft depending on what flow we are in
const participantAccountIDs = useMemo(() => {
if (reportID) {
return ReportUtils.getParticipantAccountIDs(reportID);
}

return (groupChatDraft?.participants ?? []).map((participant) => participant.accountID);
}, [groupChatDraft, reportID]);
const existingReportName = useMemo(() => ReportUtils.getGroupChatName(participantAccountIDs, false, reportID), [participantAccountIDs, reportID]);
const draftParticipantAccountIDs = useMemo(() => (groupChatDraft?.participants ?? []).map((participant) => participant.accountID), [groupChatDraft?.participants]);
const existingReportName = useMemo(
() => (report ? ReportUtils.getGroupChatName(undefined, false, report) : ReportUtils.getGroupChatName(draftParticipantAccountIDs)),
[draftParticipantAccountIDs, report],
);
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
const currentChatName = reportID ? existingReportName : groupChatDraft?.reportName || existingReportName;

Expand Down
Loading
Loading