Skip to content

Commit 2019d6d

Browse files
Rename isNewUser to shouldHideForYouSection
1 parent 4e803fb commit 2019d6d

4 files changed

Lines changed: 89 additions & 26 deletions

File tree

src/pages/home/ForYouSection/index.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {accountIDSelector} from '@src/selectors/Session';
1919
import todosReportCountsSelector, {EMPTY_TODOS_SINGLE_REPORT_IDS, todosSingleReportIDsSelector} from '@src/selectors/Todos';
2020
import EmptyState from './EmptyState';
2121
import ForYouSkeleton from './ForYouSkeleton';
22+
import shouldHideForYouSection from './shouldHideForYouSection';
2223
import useReviewFlaggedExpenses from './useReviewFlaggedExpenses';
2324

2425
function ForYouSection() {
@@ -33,6 +34,7 @@ function ForYouSection() {
3334
// Gating the skeleton on it prevents the section from flashing skeleton on every foreground/reconnect
3435
// when IS_LOADING_REPORT_DATA is optimistically set to true by ReconnectApp.
3536
const [hasLoadedApp = false] = useOnyx(ONYXKEYS.HAS_LOADED_APP);
37+
const [firstDayFreeTrial] = useOnyx(ONYXKEYS.NVP_FIRST_DAY_FREE_TRIAL);
3638
const [reportCounts = CONST.EMPTY_TODOS_REPORT_COUNTS] = useOnyx(ONYXKEYS.DERIVED.TODOS, {selector: todosReportCountsSelector});
3739
const [singleReportIDs = EMPTY_TODOS_SINGLE_REPORT_IDS] = useOnyx(ONYXKEYS.DERIVED.TODOS, {selector: todosSingleReportIDsSelector});
3840
const {count: flaggedExpensesCount, reviewExpenses} = useReviewFlaggedExpenses();
@@ -162,8 +164,9 @@ function ForYouSection() {
162164
</View>
163165
);
164166

167+
const isInitialLoad = !hasLoadedApp && (isLoadingApp || isLoadingReportData || reportCounts === undefined);
168+
165169
const renderContent = () => {
166-
const isInitialLoad = !hasLoadedApp && (isLoadingApp || isLoadingReportData || reportCounts === undefined);
167170
if (isInitialLoad) {
168171
const reasonAttributes: SkeletonSpanReasonAttributes = {
169172
context: 'ForYouSection.ForYouSkeleton',
@@ -178,6 +181,10 @@ function ForYouSection() {
178181
return hasAnyTodos ? renderTodoItems() : <EmptyState />;
179182
};
180183

184+
if (shouldHideForYouSection({isInitialLoad, hasAnyTodos, firstDayFreeTrial, cutoffDate: CONST.HOME.FOR_YOU_NEW_USER_CUTOFF_DATE})) {
185+
return null;
186+
}
187+
181188
return <WidgetContainer title={translate('homePage.forYou')}>{renderContent()}</WidgetContainer>;
182189
}
183190

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
type ShouldHideForYouSectionParams = {
2+
/** Whether the section is still in its initial load (skeleton) state. */
3+
isInitialLoad: boolean;
4+
5+
/** Whether the user currently has any actionable to-do. */
6+
hasAnyTodos: boolean;
7+
8+
/** The user's free-trial start date (`nvp_private_firstDayFreeTrial`), e.g. "2026-06-24". */
9+
firstDayFreeTrial: string | undefined;
10+
11+
/** The cutoff date that splits new from old users. */
12+
cutoffDate: string;
13+
};
14+
15+
/**
16+
* Decides whether the empty "For You" section should be hidden.
17+
*
18+
* New users (free-trial start on/after the cutoff date) have the empty section hidden until they have an actionable
19+
* to-do, while old users (onboarded before the cutoff, or without a trial start date) always keep the section. We
20+
* never hide during the initial load so the skeleton can render regardless of segment.
21+
*/
22+
function shouldHideForYouSection({isInitialLoad, hasAnyTodos, firstDayFreeTrial, cutoffDate}: ShouldHideForYouSectionParams): boolean {
23+
if (isInitialLoad || hasAnyTodos) {
24+
return false;
25+
}
26+
27+
if (!firstDayFreeTrial) {
28+
return false;
29+
}
30+
31+
const trialStartMs = new Date(firstDayFreeTrial).getTime();
32+
const cutoffMs = new Date(cutoffDate).getTime();
33+
34+
if (Number.isNaN(trialStartMs) || Number.isNaN(cutoffMs)) {
35+
return false;
36+
}
37+
38+
return trialStartMs >= cutoffMs;
39+
}
40+
41+
export default shouldHideForYouSection;

tests/unit/pages/home/ForYouSection/isNewUserTest.ts

Lines changed: 0 additions & 25 deletions
This file was deleted.
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import shouldHideForYouSection from '@pages/home/ForYouSection/shouldHideForYouSection';
2+
3+
const CUTOFF = '2026-06-24';
4+
5+
const baseParams = {
6+
isInitialLoad: false,
7+
hasAnyTodos: false,
8+
firstDayFreeTrial: undefined as string | undefined,
9+
cutoffDate: CUTOFF,
10+
};
11+
12+
describe('shouldHideForYouSection', () => {
13+
it('hides the section for a user who onboarded on the cutoff date', () => {
14+
expect(shouldHideForYouSection({...baseParams, firstDayFreeTrial: '2026-06-24'})).toBe(true);
15+
});
16+
17+
it('hides the section for a user who onboarded after the cutoff date', () => {
18+
expect(shouldHideForYouSection({...baseParams, firstDayFreeTrial: '2026-07-01'})).toBe(true);
19+
});
20+
21+
it('keeps the section for a user who onboarded before the cutoff date', () => {
22+
expect(shouldHideForYouSection({...baseParams, firstDayFreeTrial: '2026-01-01'})).toBe(false);
23+
});
24+
25+
it('keeps the section when the trial start date is missing', () => {
26+
expect(shouldHideForYouSection({...baseParams, firstDayFreeTrial: undefined})).toBe(false);
27+
});
28+
29+
it('keeps the section when the trial start date is unparseable', () => {
30+
expect(shouldHideForYouSection({...baseParams, firstDayFreeTrial: 'not-a-date'})).toBe(false);
31+
});
32+
33+
it('keeps the section during the initial load even for a new user', () => {
34+
expect(shouldHideForYouSection({...baseParams, isInitialLoad: true, firstDayFreeTrial: '2026-07-01'})).toBe(false);
35+
});
36+
37+
it('keeps the section when the user has actionable to-dos even for a new user', () => {
38+
expect(shouldHideForYouSection({...baseParams, hasAnyTodos: true, firstDayFreeTrial: '2026-07-01'})).toBe(false);
39+
});
40+
});

0 commit comments

Comments
 (0)