Skip to content

Commit

Permalink
Merge pull request #11631 from Expensify/tgolen-fix-unread
Browse files Browse the repository at this point in the history
Check for unread reports at the end of the filter logic
  • Loading branch information
johnmlee101 authored Oct 6, 2022
2 parents 5df0171 + c8fb8e4 commit 5da70c8
Show file tree
Hide file tree
Showing 3 changed files with 247 additions and 14 deletions.
12 changes: 4 additions & 8 deletions src/libs/OptionsListUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,6 @@ Onyx.connect({
callback: val => currentUserLogin = val && val.email,
});

let priorityMode;
Onyx.connect({
key: ONYXKEYS.NVP_PRIORITY_MODE,
callback: val => priorityMode = val,
});

let loginList;
Onyx.connect({
key: ONYXKEYS.LOGIN_LIST,
Expand Down Expand Up @@ -106,6 +100,9 @@ function addSMSDomainIfPhoneNumber(login) {
*/
function getPersonalDetailsForLogins(logins, personalDetails) {
const personalDetailsForLogins = {};
if (!personalDetails) {
return personalDetailsForLogins;
}
_.each(logins, (login) => {
let personalDetail = personalDetails[login];
if (!personalDetail) {
Expand Down Expand Up @@ -445,7 +442,6 @@ function getOptions(reports, personalDetails, {
sortPersonalDetailsByAlphaAsc = true,
forcePolicyNamePreview = false,
}) {
const isInGSDMode = priorityMode === CONST.PRIORITY_MODE.GSD;
let recentReportOptions = [];
let personalDetailsOptions = [];
const reportMapForLogins = {};
Expand All @@ -454,7 +450,7 @@ function getOptions(reports, personalDetails, {
const filteredReports = _.filter(reports, report => ReportUtils.shouldReportBeInOptionList(
report,
Navigation.getReportIDFromRoute(),
isInGSDMode,
false,
currentUserLogin,
iouReports,
betas,
Expand Down
12 changes: 6 additions & 6 deletions src/libs/ReportUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -916,12 +916,6 @@ function shouldReportBeInOptionList(report, reportIDFromRoute, isInGSDMode, curr
return true;
}

// Include unread reports when in GSD mode
// GSD mode is specifically for focusing the user on the most relevant chats, primarily, the unread ones
if (isInGSDMode) {
return isUnread(report);
}

// Include reports if they have a draft, are pinned, or have an outstanding IOU
// These are always relevant to the user no matter what view mode the user prefers
if (report.hasDraft || report.isPinned || hasOutstandingIOU(report, currentUserLogin, iouReports)) {
Expand All @@ -941,6 +935,12 @@ function shouldReportBeInOptionList(report, reportIDFromRoute, isInGSDMode, curr
return false;
}

// Include unread reports when in GSD mode
// GSD mode is specifically for focusing the user on the most relevant chats, primarily, the unread ones
if (isInGSDMode) {
return isUnread(report);
}

// Include default rooms for free plan policies
if (isDefaultRoom(report) && getPolicyType(report, policies) === CONST.POLICY.TYPE.FREE) {
return true;
Expand Down
237 changes: 237 additions & 0 deletions tests/unit/LHNFilterTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -398,5 +398,242 @@ describe('Sidebar', () => {
expect(sidebarLinks.queryAllByText(/One, Two/)).toHaveLength(0);
});
});

it('always shows pinned and draft chats', () => {
// Given a draft report and a pinned report
const draftReport = {
...LHNTestUtils.getFakeReport(['email1@test.com', 'email2@test.com']),
hasDraft: true,
};
const pinnedReport = {
...LHNTestUtils.getFakeReport(['email3@test.com', 'email4@test.com']),
isPinned: true,
};
const sidebarLinks = LHNTestUtils.getDefaultRenderedSidebarLinks(draftReport.reportID);

return waitForPromisesToResolve()

// When Onyx is updated to contain that data and the sidebar re-renders
.then(() => Onyx.multiSet({
[ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD,
[ONYXKEYS.PERSONAL_DETAILS]: LHNTestUtils.fakePersonalDetails,
[`${ONYXKEYS.COLLECTION.REPORT}${draftReport.reportID}`]: draftReport,
[`${ONYXKEYS.COLLECTION.REPORT}${pinnedReport.reportID}`]: pinnedReport,
}))

// Then both reports are visible
.then(() => {
const displayNames = sidebarLinks.queryAllByA11yLabel('Chat user display names');
expect(displayNames).toHaveLength(2);
expect(lodashGet(displayNames, [0, 'props', 'children'])).toBe('Three, Four');
expect(lodashGet(displayNames, [1, 'props', 'children'])).toBe('One, Two');
});
});

it('archived rooms are displayed only when they have unread messages', () => {
// Given an archived chat report, an archived default policy room, and an archived user created policy room
const archivedReport = {
...LHNTestUtils.getFakeReport(['email1@test.com', 'email2@test.com']),
statusNum: CONST.REPORT.STATUS.CLOSED,
stateNum: CONST.REPORT.STATE_NUM.SUBMITTED,
};
const archivedPolicyRoomReport = {
...LHNTestUtils.getFakeReport(['email1@test.com', 'email2@test.com']),
chatType: CONST.REPORT.CHAT_TYPE.POLICY_ANNOUNCE,
statusNum: CONST.REPORT.STATUS.CLOSED,
stateNum: CONST.REPORT.STATE_NUM.SUBMITTED,
};
const archivedUserCreatedPolicyRoomReport = {
...LHNTestUtils.getFakeReport(['email1@test.com', 'email2@test.com']),
chatType: CONST.REPORT.CHAT_TYPE.POLICY_ROOM,
statusNum: CONST.REPORT.STATUS.CLOSED,
stateNum: CONST.REPORT.STATE_NUM.SUBMITTED,
};
const sidebarLinks = LHNTestUtils.getDefaultRenderedSidebarLinks();

return waitForPromisesToResolve()

// When Onyx is updated to contain that data and the sidebar re-renders
.then(() => Onyx.multiSet({
[ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD,
[ONYXKEYS.PERSONAL_DETAILS]: LHNTestUtils.fakePersonalDetails,
[`${ONYXKEYS.COLLECTION.REPORT}${archivedReport.reportID}`]: archivedReport,
[`${ONYXKEYS.COLLECTION.REPORT}${archivedPolicyRoomReport.reportID}`]: archivedPolicyRoomReport,
[`${ONYXKEYS.COLLECTION.REPORT}${archivedUserCreatedPolicyRoomReport.reportID}`]: archivedUserCreatedPolicyRoomReport,
}))

// Then neither reports are visible
.then(() => {
const displayNames = sidebarLinks.queryAllByA11yLabel('Chat user display names');
expect(displayNames).toHaveLength(0);
})

// When they have unread messages
.then(() => Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${archivedReport.reportID}`, {
lastReadSequenceNumber: LHNTestUtils.TEST_MAX_SEQUENCE_NUMBER - 1,
}))
.then(() => Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${archivedPolicyRoomReport.reportID}`, {
lastReadSequenceNumber: LHNTestUtils.TEST_MAX_SEQUENCE_NUMBER - 1,
}))
.then(() => Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${archivedUserCreatedPolicyRoomReport.reportID}`, {
lastReadSequenceNumber: LHNTestUtils.TEST_MAX_SEQUENCE_NUMBER - 1,
}))

// Then they are all visible
.then(() => {
const displayNames = sidebarLinks.queryAllByA11yLabel('Chat user display names');
expect(displayNames).toHaveLength(3);
});
});

it('policy rooms are displayed only when they have unread messages', () => {
// Given a default policy room and user created policy room
const policyRoomReport = {
...LHNTestUtils.getFakeReport(['email1@test.com', 'email2@test.com']),
chatType: CONST.REPORT.CHAT_TYPE.POLICY_ANNOUNCE,
};
const userCreatedPolicyRoomReport = {
...LHNTestUtils.getFakeReport(['email1@test.com', 'email2@test.com']),
chatType: CONST.REPORT.CHAT_TYPE.POLICY_ROOM,
};
const sidebarLinks = LHNTestUtils.getDefaultRenderedSidebarLinks();

return waitForPromisesToResolve()

// When Onyx is updated to contain that data and the sidebar re-renders
.then(() => Onyx.multiSet({
[ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD,
[ONYXKEYS.PERSONAL_DETAILS]: LHNTestUtils.fakePersonalDetails,
[`${ONYXKEYS.COLLECTION.REPORT}${policyRoomReport.reportID}`]: policyRoomReport,
[`${ONYXKEYS.COLLECTION.REPORT}${userCreatedPolicyRoomReport.reportID}`]: userCreatedPolicyRoomReport,
}))

// Then neither reports are visible
.then(() => {
const displayNames = sidebarLinks.queryAllByA11yLabel('Chat user display names');
expect(displayNames).toHaveLength(0);
})

// When they both have unread messages
.then(() => Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${policyRoomReport.reportID}`, {
lastReadSequenceNumber: LHNTestUtils.TEST_MAX_SEQUENCE_NUMBER - 1,
}))
.then(() => Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${userCreatedPolicyRoomReport.reportID}`, {
lastReadSequenceNumber: LHNTestUtils.TEST_MAX_SEQUENCE_NUMBER - 1,
}))

// Then both rooms are visible
.then(() => {
const displayNames = sidebarLinks.queryAllByA11yLabel('Chat user display names');
expect(displayNames).toHaveLength(2);
});
});
});

describe('all combinations of hasComments, isArchived, isUserCreatedPolicyRoom, hasAddWorkspaceError, isUnread, isPinned, hasDraft', () => {
// Given a report that is the active report and doesn't change
const report1 = LHNTestUtils.getFakeReport(['email3@test.com', 'email4@test.com']);

// Given a free policy that doesn't change
const policy = {
name: 'Policy One',
policyID: '1',
type: CONST.POLICY.TYPE.FREE,
};

// Given the user is in all betas
const betas = [
CONST.BETAS.DEFAULT_ROOMS,
CONST.BETAS.POLICY_ROOMS,
CONST.BETAS.POLICY_EXPENSE_CHAT,
];

// Given there are 7 boolean variables tested in the filtering logic:
// 1. hasComments
// 2. isArchived
// 3. isUserCreatedPolicyRoom
// 4. hasAddWorkspaceError
// 5. isUnread
// 6. isPinned
// 7. hasDraft
// There is one setting not represented here, which is hasOutstandingIOU. In order to test that setting, there must be
// additional reports in Onyx, so it's being left out for now. It's identical to the logic for hasDraft and isPinned though.

// Given these combinations of booleans which result in the report being filtered out (not shown).
const booleansWhichRemovesInactiveReport = [
JSON.stringify([false, false, false, false, false, false, false]),
JSON.stringify([false, false, false, true, false, false, false]),
JSON.stringify([false, false, true, false, false, false, false]),
JSON.stringify([false, false, true, false, true, false, false]),
JSON.stringify([false, false, true, true, false, false, false]),
JSON.stringify([false, false, true, true, true, false, false]),
JSON.stringify([false, true, false, false, false, false, false]),
JSON.stringify([false, true, false, false, true, false, false]),
JSON.stringify([false, true, false, true, false, false, false]),
JSON.stringify([false, true, false, true, true, false, false]),
JSON.stringify([false, true, true, false, false, false, false]),
JSON.stringify([false, true, true, false, true, false, false]),
JSON.stringify([false, true, true, true, false, false, false]),
JSON.stringify([false, true, true, true, true, false, false]),
JSON.stringify([true, false, false, false, false, false, false]),
JSON.stringify([true, false, false, true, false, false, false]),
JSON.stringify([true, false, true, false, false, false, false]),
JSON.stringify([true, false, true, true, false, false, false]),
JSON.stringify([true, true, false, false, false, false, false]),
JSON.stringify([true, true, false, true, false, false, false]),
JSON.stringify([true, true, true, false, false, false, false]),
JSON.stringify([true, true, true, true, false, false, false]),
];

// When every single combination of those booleans is tested

// Taken from https://stackoverflow.com/a/39734979/9114791 to generate all possible boolean combinations
const AMOUNT_OF_VARIABLES = 7;
// eslint-disable-next-line no-bitwise
for (let i = 0; i < (1 << AMOUNT_OF_VARIABLES); i++) {
const boolArr = [];
for (let j = AMOUNT_OF_VARIABLES - 1; j >= 0; j--) {
// eslint-disable-next-line no-bitwise
boolArr.push(Boolean(i & (1 << j)));
}

// To test a failing set of conditions, comment out the for loop above and then use a hardcoded array
// for the specific case that's failing. You can then debug the code to see why the test is not passing.
// const boolArr = [false, false, false, true, false, false, false];

it(`the booleans ${JSON.stringify(boolArr)}`, () => {
const report2 = {
...LHNTestUtils.getAdvancedFakeReport(...boolArr),
policyID: policy.policyID,
};
const sidebarLinks = LHNTestUtils.getDefaultRenderedSidebarLinks(report1.reportID);

return waitForPromisesToResolve()

// When Onyx is updated to contain that data and the sidebar re-renders
.then(() => Onyx.multiSet({
[ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD,
[ONYXKEYS.BETAS]: betas,
[ONYXKEYS.PERSONAL_DETAILS]: LHNTestUtils.fakePersonalDetails,
[`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1,
[`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2,
[`${ONYXKEYS.COLLECTION.POLICY}${policy.policyID}`]: policy,
}))

// Then depending on the outcome, either one or two reports are visible
.then(() => {
if (booleansWhichRemovesInactiveReport.indexOf(JSON.stringify(boolArr)) > -1) {
// Only one report visible
expect(sidebarLinks.queryAllByA11yHint('Navigates to a chat')).toHaveLength(1);
expect(sidebarLinks.queryAllByA11yLabel('Chat user display names')).toHaveLength(1);
const displayNames = sidebarLinks.queryAllByA11yLabel('Chat user display names');
expect(lodashGet(displayNames, [0, 'props', 'children'])).toBe('Three, Four');
} else {
// Both reports visible
expect(sidebarLinks.queryAllByA11yHint('Navigates to a chat')).toHaveLength(2);
}
});
});
}
});
});

0 comments on commit 5da70c8

Please sign in to comment.