diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index 8047bb3b7b47..8b254de6f106 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -45,6 +45,16 @@ Onyx.connect({ }, }); +const iouReports = {}; +Onyx.connect({ + key: ONYXKEYS.COLLECTION.REPORT_IOUS, + callback: (iouReport, key) => { + if (iouReport && key && iouReport.ownerEmail) { + iouReports[key] = iouReport; + } + }, +}); + /** * Helper method to return a default avatar * @@ -180,6 +190,10 @@ function createOption(personalDetailList, report, draftComments, { && lodashGet(draftComments, `${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${report.reportID}`, ''); const hasOutstandingIOU = lodashGet(report, 'hasOutstandingIOU', false); + const iouReport = hasOutstandingIOU + ? lodashGet(iouReports, `${ONYXKEYS.COLLECTION.REPORT_IOUS}${report.iouReportID}`, {}) + : {}; + const lastActorDetails = report ? _.find(personalDetailList, {login: report.lastActorEmail}) : null; const lastMessageText = report ? (hasMultipleParticipants && lastActorDetails @@ -226,6 +240,8 @@ function createOption(personalDetailList, report, draftComments, { isPinned: lodashGet(report, 'isPinned', false), hasOutstandingIOU, iouReportID: lodashGet(report, 'iouReportID'), + isIOUReportOwner: lodashGet(iouReport, 'ownerEmail', '') === currentUserLogin, + iouReportAmount: lodashGet(iouReport, 'total', 0), isDefaultChatRoom, }; } @@ -280,10 +296,12 @@ function getOptions(reports, personalDetails, draftComments, activeReportID, { hideReadReports = false, sortByAlphaAsc = false, forcePolicyNamePreview = false, + prioritizeIOUDebts = false, }) { let recentReportOptions = []; const pinnedReportOptions = []; const personalDetailsOptions = []; + const iouDebtReportOptions = []; const reportMapForLogins = {}; let sortProperty = sortByLastMessageTimestamp @@ -307,7 +325,11 @@ function getOptions(reports, personalDetails, draftComments, activeReportID, { const reportDraftComment = report && draftComments && lodashGet(draftComments, `${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${report.reportID}`, ''); + const iouReportOwner = lodashGet(report, 'hasOutstandingIOU', false) + ? lodashGet(iouReports, [`${ONYXKEYS.COLLECTION.REPORT_IOUS}${report.iouReportID}`, 'ownerEmail'], '') + : ''; + const reportContainsIOUDebt = iouReportOwner && iouReportOwner !== currentUserLogin; const shouldFilterReportIfEmpty = !showReportsWithNoComments && report.lastMessageTimestamp === 0; const shouldFilterReportIfRead = hideReadReports && report.unreadActionCount === 0; const shouldShowReportIfHasDraft = showReportsWithDrafts && reportDraftComment && reportDraftComment.length > 0; @@ -315,7 +337,8 @@ function getOptions(reports, personalDetails, draftComments, activeReportID, { if (report.reportID !== activeReportID && !report.isPinned && !shouldShowReportIfHasDraft - && shouldFilterReport) { + && shouldFilterReport + && !reportContainsIOUDebt) { return; } @@ -380,6 +403,8 @@ function getOptions(reports, personalDetails, draftComments, activeReportID, { // collect the pinned reports so we can sort them alphabetically once they are collected if (prioritizePinnedReports && reportOption.isPinned) { pinnedReportOptions.push(reportOption); + } else if (prioritizeIOUDebts && reportOption.hasOutstandingIOU && !reportOption.isIOUReportOwner) { + iouDebtReportOptions.push(reportOption); } else { recentReportOptions.push(reportOption); } @@ -391,6 +416,12 @@ function getOptions(reports, personalDetails, draftComments, activeReportID, { } } + // If we are prioritizing IOUs the user owes, add them before the normal recent report options + if (prioritizeIOUDebts) { + const sortedIOUReports = lodashOrderBy(iouDebtReportOptions, ['iouReportAmount'], ['desc']); + recentReportOptions = sortedIOUReports.concat(recentReportOptions); + } + // If we are prioritizing our pinned reports then shift them to the front and sort them by report name if (prioritizePinnedReports) { const sortedPinnedReports = lodashOrderBy(pinnedReportOptions, ['text'], ['asc']); @@ -475,6 +506,7 @@ function getSearchOptions( includePersonalDetails: true, sortByLastMessageTimestamp: false, forcePolicyNamePreview: true, + prioritizeIOUDebts: false, }); } @@ -589,6 +621,7 @@ function getSidebarOptions( ) { let sideBarOptions = { prioritizePinnedReports: true, + prioritizeIOUDebts: true, }; if (priorityMode === CONST.PRIORITY_MODE.GSD) { sideBarOptions = { diff --git a/tests/unit/OptionsListUtilsTest.js b/tests/unit/OptionsListUtilsTest.js index 2a54da5e2015..27ee71d6976a 100644 --- a/tests/unit/OptionsListUtilsTest.js +++ b/tests/unit/OptionsListUtilsTest.js @@ -86,6 +86,19 @@ describe('OptionsListUtils', () => { reportName: 'Silver Surfer', unreadActionCount: 0, }, + + // Note: This report has an IOU + 9: { + lastVisitedTimestamp: 1610666739302, + lastMessageTimestamp: 1611282168, + isPinned: false, + reportID: 9, + participants: ['mistersinister@marauders.com'], + reportName: 'Mister Sinister', + unreadActionCount: 0, + iouReportID: 100, + hasOutstandingIOU: true, + }, }; // And a set of personalDetails some with existing reports and some without @@ -119,6 +132,10 @@ describe('OptionsListUtils', () => { displayName: 'Captain America', login: 'steverogers@expensify.com', }, + 'mistersinister@marauders.com': { + displayName: 'Mr Sinister', + login: 'mistersinister@marauders.com', + }, // These do not exist in reports at all 'natasharomanoff@expensify.com': { @@ -134,11 +151,11 @@ describe('OptionsListUtils', () => { const REPORTS_WITH_CONCIERGE = { ...REPORTS, - 9: { + 10: { lastVisitedTimestamp: 1610666739302, lastMessageTimestamp: 1, isPinned: false, - reportID: 9, + reportID: 10, participants: ['concierge@expensify.com'], reportName: 'Concierge', unreadActionCount: 1, @@ -160,6 +177,10 @@ describe('OptionsListUtils', () => { keys: ONYXKEYS, initialKeyStates: { [ONYXKEYS.SESSION]: {email: 'tonystark@expensify.com'}, + [`${ONYXKEYS.COLLECTION.REPORT_IOUS}100`]: { + ownerEmail: 'mistersinister@marauders.com', + total: '1000', + }, }, registerStorageEventListener: () => {}, }); @@ -404,11 +425,11 @@ describe('OptionsListUtils', () => { ...REPORTS, // Note: This report has no lastMessageTimestamp but is also pinned - 9: { + 10: { lastVisitedTimestamp: 1610666739300, lastMessageTimestamp: 0, isPinned: true, - reportID: 9, + reportID: 10, participants: ['captain_britain@expensify.com'], reportName: 'Captain Britain', }, @@ -436,8 +457,11 @@ describe('OptionsListUtils', () => { // And the most recent pinned report is first in the list of reports expect(results.recentReports[0].login).toBe('captain_britain@expensify.com'); - // And the third report is the report with a lastMessageTimestamp - expect(results.recentReports[2].login).toBe('steverogers@expensify.com'); + // And the third report is the report with an IOU debt + expect(results.recentReports[2].login).toBe('mistersinister@marauders.com'); + + // And the fourth report is the report with the lastMessage timestamp + expect(results.recentReports[3].login).toBe('steverogers@expensify.com'); }); it('getSidebarOptions() with GSD priority mode', () => { @@ -457,5 +481,8 @@ describe('OptionsListUtils', () => { // And Black Panther is alphabetically the first report and has an unread message expect(results.recentReports[0].login).toBe('tchalla@expensify.com'); + + // And Mister Sinister is alphabetically the fifth report and has an IOU debt despite not being pinned + expect(results.recentReports[5].login).toBe('mistersinister@marauders.com'); }); });