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

Tweaks to the expense flow #53760

Open
wants to merge 36 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
49269d0
remove the Just track it button
JKobrynski Dec 9, 2024
7a2556d
add the create $amount expense label
JKobrynski Dec 9, 2024
103bf6c
add Workspace and Personal sections to recipients list
JKobrynski Dec 10, 2024
50bbaaa
Merge branch 'main' into JKobrynski/feat/tweaks-to-create-expense-flow
JKobrynski Dec 11, 2024
3464608
Merge remote-tracking branch 'upstream/main' into JKobrynski/feat/twe…
JKobrynski Dec 11, 2024
052b1ff
update translation, add return type
JKobrynski Dec 11, 2024
520ec32
Merge remote-tracking branch 'upstream/main' into JKobrynski/feat/twe…
JKobrynski Dec 12, 2024
676a76d
put default workspace on top of the list
JKobrynski Dec 13, 2024
ff2fcc4
Merge remote-tracking branch 'upstream/main' into JKobrynski/feat/twe…
JKobrynski Dec 13, 2024
3bdf6d2
Merge remote-tracking branch 'upstream/main' into JKobrynski/feat/twe…
JKobrynski Dec 16, 2024
b1e5d19
Merge remote-tracking branch 'upstream/main' into JKobrynski/feat/twe…
JKobrynski Dec 17, 2024
c4fb54a
replace id fallbacks with CONST.DEFAULT_NUMBER_ID
JKobrynski Dec 17, 2024
50ebfa5
rename recentWorkspaceChats to workspaceChats
JKobrynski Dec 17, 2024
9e9e428
separate all workspace chats, not just recents
JKobrynski Dec 17, 2024
9848008
filter selfDMChat option
JKobrynski Dec 17, 2024
efe51d4
replace id fallbacks with CONST.DEFAULT_NUMBER_ID
JKobrynski Dec 17, 2024
398feac
fix ids rule
JKobrynski Dec 17, 2024
927ee63
Merge remote-tracking branch 'upstream/main' into JKobrynski/feat/twe…
JKobrynski Dec 18, 2024
9b2c48d
Merge remote-tracking branch 'upstream/main' into JKobrynski/feat/twe…
JKobrynski Dec 19, 2024
0bf0562
export getReport from ReportUtils
JKobrynski Dec 19, 2024
53adb31
simplify workspace filtering, fix no results found condition
JKobrynski Dec 19, 2024
2764399
Merge remote-tracking branch 'upstream/main' into JKobrynski/feat/twe…
JKobrynski Dec 19, 2024
e1447e9
replace isPolicyExpenseChat with isOwnPolicyExpenseChat
JKobrynski Dec 20, 2024
8aa913d
Merge remote-tracking branch 'upstream/main' into JKobrynski/feat/twe…
JKobrynski Dec 20, 2024
dd65178
Merge remote-tracking branch 'upstream/main' into JKobrynski/feat/twe…
JKobrynski Dec 20, 2024
d56d865
remove ReportUtils.getReport references
JKobrynski Dec 20, 2024
5f6ffce
filter out archived workspaces
JKobrynski Dec 20, 2024
70ca10d
Merge remote-tracking branch 'upstream/main' into JKobrynski/feat/twe…
JKobrynski Dec 20, 2024
f246b59
remove limit of displayed workspaces
JKobrynski Dec 20, 2024
a652bd5
Merge remote-tracking branch 'upstream/main' into JKobrynski/feat/twe…
JKobrynski Dec 27, 2024
6a6207d
fix id fallbacks
JKobrynski Dec 27, 2024
9f49eed
fix id fallback in MoneyRequestParticipantsSelector
JKobrynski Dec 27, 2024
28d4fef
Merge remote-tracking branch 'upstream/main' into JKobrynski/feat/twe…
JKobrynski Dec 30, 2024
eee9a5b
revert ID fallback changes in OptionsListUtils.ts
JKobrynski Dec 30, 2024
ad595ec
remove unnecessary empty line
JKobrynski Dec 30, 2024
3c27b60
fix optional parameters
JKobrynski Dec 31, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/components/MoneyRequestConfirmationList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,7 @@ function MoneyRequestConfirmationList({
text = translate('common.next');
}
} else if (isTypeTrackExpense) {
text = translate('iou.trackExpense');
text = translate('iou.createExpenseWithAmount', {amount: formattedAmount});
} else if (isTypeSplit && iouAmount === 0) {
text = translate('iou.splitExpense');
} else if ((receiptPath && isTypeRequest) || isDistanceRequestWithPendingRoute) {
Expand All @@ -434,7 +434,7 @@ function MoneyRequestConfirmationList({
text = translate('iou.submitAmount', {amount: formattedAmount});
}
} else {
const translationKey = isTypeSplit ? 'iou.splitAmount' : 'iou.submitAmount';
const translationKey = isTypeSplit ? 'iou.splitAmount' : 'iou.createExpenseWithAmount';
text = translate(translationKey, {amount: formattedAmount});
}
return [
Expand Down
1 change: 1 addition & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -864,6 +864,7 @@ const translations = {
createExpense: 'Create expense',
trackExpense: 'Track expense',
chooseRecipient: 'Choose recipient',
createExpenseWithAmount: ({amount}: {amount: string}) => `Create ${amount} expense`,
confirmDetails: 'Confirm details',
pay: 'Pay',
cancelPayment: 'Cancel payment',
Expand Down
1 change: 1 addition & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -860,6 +860,7 @@ const translations = {
trackExpense: 'Seguimiento de gastos',
chooseRecipient: 'Elige destinatario',
confirmDetails: 'Confirma los detalles',
createExpenseWithAmount: ({amount}: {amount: string}) => `Crear un gasto de ${amount}`,
pay: 'Pagar',
cancelPayment: 'Cancelar el pago',
cancelPaymentConfirmation: '¿Estás seguro de que quieres cancelar este pago?',
Expand Down
98 changes: 96 additions & 2 deletions src/libs/OptionsListUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ type GetOptionsConfig = {
action?: IOUAction;
recentAttendees?: Attendee[];
shouldBoldTitleByDefault?: boolean;
shouldSeparateWorkspaceChat?: boolean;
shouldSeparateSelfDMChat?: boolean;
};

type GetUserToInviteConfig = {
Expand Down Expand Up @@ -138,6 +140,8 @@ type Options = {
personalDetails: ReportUtils.OptionData[];
userToInvite: ReportUtils.OptionData | null;
currentUserOption: ReportUtils.OptionData | null | undefined;
workspaceChats?: ReportUtils.OptionData[];
selfDMChat?: ReportUtils.OptionData | undefined;
};

type PreviewConfig = {showChatPreviewLine?: boolean; forcePolicyNamePreview?: boolean; showPersonalDetails?: boolean};
Expand Down Expand Up @@ -165,7 +169,7 @@ type OrderReportOptionsConfig = {
preferRecentExpenseReports?: boolean;
};

type ReportAndPersonalDetailOptions = Pick<Options, 'recentReports' | 'personalDetails'>;
type ReportAndPersonalDetailOptions = Pick<Options, 'recentReports' | 'personalDetails' | 'workspaceChats'>;

/**
* OptionsListUtils is used to build a list options passed to the OptionsList component. Several different UI views can
Expand Down Expand Up @@ -1001,6 +1005,23 @@ function orderReportOptionsWithSearch(
);
}

function orderWorkspaceOptions(options: ReportUtils.OptionData[]): ReportUtils.OptionData[] {
return lodashOrderBy(
options,
[
(option) => {
// Put default workspace on top
if (option.isPolicyExpenseChat && option.policyID === activePolicyID) {
return 0;
}

return 1;
},
],
['asc'],
);
}

function sortComparatorReportOptionByArchivedStatus(option: ReportUtils.OptionData) {
return option.private_isArchived ? 1 : 0;
}
Expand Down Expand Up @@ -1028,10 +1049,12 @@ function orderOptions(options: ReportAndPersonalDetailOptions, searchValue?: str
orderedReportOptions = orderReportOptions(options.recentReports);
}
const orderedPersonalDetailsOptions = orderPersonalDetailsOptions(options.personalDetails);
const orderedWorkspaceChats = orderWorkspaceOptions(options?.workspaceChats ?? []);

return {
recentReports: orderedReportOptions,
personalDetails: orderedPersonalDetailsOptions,
workspaceChats: orderedWorkspaceChats,
};
}

Expand Down Expand Up @@ -1140,6 +1163,8 @@ function getValidOptions(
action,
recentAttendees,
shouldBoldTitleByDefault = true,
shouldSeparateSelfDMChat = false,
shouldSeparateWorkspaceChat = false,
}: GetOptionsConfig = {},
): Options {
const topmostReportId = Navigation.getTopmostReportId() ?? '-1';
Expand Down Expand Up @@ -1215,6 +1240,12 @@ function getValidOptions(
return true;
});

let workspaceChats: ReportUtils.OptionData[] = [];

if (shouldSeparateWorkspaceChat) {
workspaceChats = allReportOptions.filter((option) => option.isOwnPolicyExpenseChat && !option.private_isArchived);
}

const allPersonalDetailsOptions = includeP2P
? options.personalDetails.filter((detail) => !!detail?.login && !!detail.accountID && !detail?.isOptimisticPersonalDetail && (includeDomainEmail || !Str.isDomainEmail(detail.login)))
: [];
Expand Down Expand Up @@ -1325,6 +1356,15 @@ function getValidOptions(
}

const currentUserOption = allPersonalDetailsOptions.find((personalDetailsOption) => personalDetailsOption.login === currentUserLogin);
let selfDMChat: ReportUtils.OptionData | undefined;

if (shouldSeparateWorkspaceChat) {
recentReportOptions = recentReportOptions.filter((option) => !option.isPolicyExpenseChat);
}
if (shouldSeparateSelfDMChat) {
selfDMChat = recentReportOptions.find((option) => option.isSelfDM);
recentReportOptions = recentReportOptions.filter((option) => !option.isSelfDM);
}

return {
personalDetails: personalDetailsOptions,
Expand All @@ -1333,6 +1373,8 @@ function getValidOptions(
// User to invite is generated by the search input of a user.
// As this function isn't concerned with any search input yet, this is null (will be set when using filterOptions).
userToInvite: null,
workspaceChats,
selfDMChat,
};
}

Expand Down Expand Up @@ -1573,6 +1615,7 @@ function formatSectionsFromSearchTerm(
filteredPersonalDetails: ReportUtils.OptionData[],
personalDetails: OnyxEntry<PersonalDetailsList> = {},
shouldGetOptionDetails = false,
filteredWorkspaceChats: ReportUtils.OptionData[] = [],
): SectionForSearchTerm {
// We show the selected participants at the top of the list when there is no search term or maximum number of participants has already been selected
// However, if there is a search term we remove the selected participants from the top of the list unless they are part of the search results
Expand All @@ -1598,8 +1641,9 @@ function formatSectionsFromSearchTerm(
const selectedParticipantsWithoutDetails = selectedOptions.filter((participant) => {
const accountID = participant.accountID ?? null;
const isPartOfSearchTerm = getPersonalDetailSearchTerms(participant).join(' ').toLowerCase().includes(cleanSearchTerm);
const isReportInRecentReports = filteredRecentReports.some((report) => report.accountID === accountID);
const isReportInRecentReports = filteredRecentReports.some((report) => report.accountID === accountID) || filteredWorkspaceChats.some((report) => report.accountID === accountID);
const isReportInPersonalDetails = filteredPersonalDetails.some((personalDetail) => personalDetail.accountID === accountID);

return isPartOfSearchTerm && !isReportInRecentReports && !isReportInPersonalDetails;
});

Expand Down Expand Up @@ -1685,6 +1729,23 @@ function filterReports(reports: ReportUtils.OptionData[], searchTerms: string[])
return filteredReports;
}

function filterWorkspaceChats(reports: ReportUtils.OptionData[], searchTerms: string[]): ReportUtils.OptionData[] {
Copy link
Contributor

@suneox suneox Dec 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can simplify this function by filter by text the same condition at Filter by workspace page, since the WS option doesn't include isThread, login and subtitle always set to workspace.

Screen.Recording.2024-12-18.at.00.39.05.mp4

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated

const filteredReports = searchTerms.reduceRight(
(items, term) =>
filterArrayByMatch(items, term, (item) => {
const values: string[] = [];
if (item.text) {
values.push(item.text);
}
return uniqFast(values);
}),
// We start from all unfiltered reports:
reports,
);

return filteredReports;
}

function filterPersonalDetails(personalDetails: ReportUtils.OptionData[], searchTerms: string[]): ReportUtils.OptionData[] {
return searchTerms.reduceRight(
(items, term) =>
Expand Down Expand Up @@ -1736,6 +1797,34 @@ function filterUserToInvite(options: Omit<Options, 'userToInvite'>, searchValue:
});
}

function filterSelfDMChat(report: ReportUtils.OptionData, searchTerms: string[]): ReportUtils.OptionData | undefined {
const isMatch = searchTerms.every((term) => {
const values: string[] = [];

if (report.text) {
values.push(report.text);
}
if (report.login) {
values.push(report.login);
values.push(report.login.replace(CONST.EMAIL_SEARCH_REGEX, ''));
}
if (report.isThread) {
if (report.alternateText) {
values.push(report.alternateText);
}
} else if (!!report.isChatRoom || !!report.isPolicyExpenseChat) {
if (report.subtitle) {
values.push(report.subtitle);
}
}

// Remove duplicate values and check if the term matches any value
return uniqFast(values).some((value) => value.includes(term));
});

return isMatch ? report : undefined;
}

function filterOptions(options: Options, searchInputValue: string, config?: FilterUserToInviteConfig): Options {
const parsedPhoneNumber = PhoneNumber.parsePhoneNumber(LoginUtils.appendCountryCode(Str.removeSMSDomain(searchInputValue)));
const searchValue = parsedPhoneNumber.possible && parsedPhoneNumber.number?.e164 ? parsedPhoneNumber.number.e164 : searchInputValue.toLowerCase();
Expand All @@ -1753,12 +1842,17 @@ function filterOptions(options: Options, searchInputValue: string, config?: Filt
searchValue,
config,
);
const workspaceChats = filterWorkspaceChats(options.workspaceChats ?? [], searchTerms);

const selfDMChat = options.selfDMChat ? filterSelfDMChat(options.selfDMChat, searchTerms) : undefined;

return {
personalDetails,
recentReports,
userToInvite,
currentUserOption,
workspaceChats,
selfDMChat,
};
}

Expand Down
2 changes: 1 addition & 1 deletion src/libs/ReportActionsUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@ function isInviteOrRemovedAction(
/**
* Returns whether the comment is a thread parent message/the first message in a thread
*/
function isThreadParentMessage(reportAction: OnyxEntry<ReportAction>, reportID: string): boolean {
function isThreadParentMessage(reportAction: OnyxEntry<ReportAction>, reportID: string | undefined): boolean {
const {childType, childVisibleActionCount = 0, childReportID} = reportAction ?? {};
return childType === CONST.REPORT.TYPE.CHAT && (childVisibleActionCount > 0 || String(childReportID) === reportID);
}
Expand Down
4 changes: 2 additions & 2 deletions src/libs/ReportUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6409,7 +6409,7 @@ function isReportNotFound(report: OnyxEntry<Report>): boolean {
/**
* Check if the report is the parent report of the currently viewed report or at least one child report has report action
*/
function shouldHideReport(report: OnyxEntry<Report>, currentReportId: string): boolean {
function shouldHideReport(report: OnyxEntry<Report>, currentReportId: string | undefined): boolean {
const currentReport = getReportOrDraftReport(currentReportId);
const parentReport = getParentReport(!isEmptyObject(currentReport) ? currentReport : undefined);
const reportActions = allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report?.reportID}`] ?? {};
Expand Down Expand Up @@ -6579,7 +6579,7 @@ function hasReportErrorsOtherThanFailedReceipt(report: Report, doesReportHaveVio

type ShouldReportBeInOptionListParams = {
report: OnyxEntry<Report>;
currentReportId: string;
currentReportId: string | undefined;
isInFocusMode: boolean;
betas: OnyxEntry<Beta[]>;
policies: OnyxCollection<Policy>;
Expand Down
Loading
Loading