Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
17 changes: 15 additions & 2 deletions src/libs/TryNewDotUtils.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,24 @@
import {differenceInDays} from 'date-fns';
import type {OnyxEntry} from 'react-native-onyx';
import type {TryNewDot} from '@src/types/onyx';
import {isEmptyObject} from '@src/types/utils/EmptyObject';

const NEW_DOT_MIN_DAYS_BEFORE_HIDING_CLASSIC_REDIRECT = 30;

function isLockedToNewApp(tryNewDot: OnyxEntry<TryNewDot>): boolean {
return tryNewDot?.isLockedToNewApp === true;
}

function hasBeenInNewDot30Days(tryNewDot: OnyxEntry<TryNewDot>): boolean {
const classicRedirect = tryNewDot?.classicRedirect;
if (!classicRedirect || classicRedirect.dismissed !== false || !classicRedirect.timestamp) {
return false;
}

const daysSinceNudge = differenceInDays(new Date(), new Date(classicRedirect.timestamp));
return daysSinceNudge >= NEW_DOT_MIN_DAYS_BEFORE_HIDING_CLASSIC_REDIRECT;
}

function shouldBlockOldAppExit(tryNewDot: OnyxEntry<TryNewDot>, isLoadingTryNewDot: boolean, shouldSetNVP: boolean): boolean {
if (isLockedToNewApp(tryNewDot)) {
return true;
Expand All @@ -15,7 +28,7 @@ function shouldBlockOldAppExit(tryNewDot: OnyxEntry<TryNewDot>, isLoadingTryNewD
}

function isOldAppRedirectBlocked(tryNewDot: OnyxEntry<TryNewDot>, shouldRespectMobileLock: boolean): boolean {
return tryNewDot?.classicRedirect?.isLockedToNewDot === true || (shouldRespectMobileLock && isLockedToNewApp(tryNewDot));
return tryNewDot?.classicRedirect?.isLockedToNewDot === true || hasBeenInNewDot30Days(tryNewDot) || (shouldRespectMobileLock && isLockedToNewApp(tryNewDot));
}

function shouldHideOldAppRedirect(tryNewDot: OnyxEntry<TryNewDot>, isLoadingTryNewDot: boolean, shouldRespectMobileLock: boolean): boolean {
Expand All @@ -34,4 +47,4 @@ function shouldUseOldApp(tryNewDot: TryNewDot): boolean | undefined {
return tryNewDot.classicRedirect.dismissed;
}

export {isLockedToNewApp, isOldAppRedirectBlocked, shouldBlockOldAppExit, shouldHideOldAppRedirect, shouldUseOldApp};
export {hasBeenInNewDot30Days, isLockedToNewApp, isOldAppRedirectBlocked, shouldBlockOldAppExit, shouldHideOldAppRedirect, shouldUseOldApp};
68 changes: 67 additions & 1 deletion tests/unit/TryNewDotUtilsTest.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {subDays} from 'date-fns';
import Onyx from 'react-native-onyx';
import {isOldAppRedirectBlocked, shouldBlockOldAppExit, shouldHideOldAppRedirect, shouldUseOldApp} from '@src/libs/TryNewDotUtils';
import {hasBeenInNewDot30Days, isOldAppRedirectBlocked, shouldBlockOldAppExit, shouldHideOldAppRedirect, shouldUseOldApp} from '@src/libs/TryNewDotUtils';
import ONYXKEYS from '@src/ONYXKEYS';
import type {TryNewDot} from '@src/types/onyx';
import waitForBatchedUpdatesWithAct from '../utils/waitForBatchedUpdatesWithAct';
Expand Down Expand Up @@ -68,6 +69,71 @@ describe('TryNewDotUtils', () => {
expect(shouldBlockOldAppExit({isLockedToNewApp: true} as TryNewDot, false, false)).toBe(true);
});

it('blocks the OldDot redirect when the classicRedirect nudge has gone stale', () => {
const tryNewDot = {
classicRedirect: {
dismissed: false,
timestamp: subDays(new Date(), 31).toISOString(),
},
} as unknown as TryNewDot;

expect(isOldAppRedirectBlocked(tryNewDot, false)).toBe(true);
});

it('still shows the OldDot redirect when the classicRedirect nudge is fresh', () => {
const tryNewDot = {
classicRedirect: {
dismissed: false,
timestamp: subDays(new Date(), 5).toISOString(),
},
} as unknown as TryNewDot;

expect(isOldAppRedirectBlocked(tryNewDot, false)).toBe(false);
});

it('reports that a user has been in NewDot 30 days when the nudge is over a month old and not dismissed', () => {
const tryNewDot = {
classicRedirect: {
dismissed: false,
timestamp: subDays(new Date(), 31).toISOString(),
},
} as unknown as TryNewDot;

expect(hasBeenInNewDot30Days(tryNewDot)).toBe(true);
});

it('does not report 30 days in NewDot when the nudge is less than a month old', () => {
const tryNewDot = {
classicRedirect: {
dismissed: false,
timestamp: subDays(new Date(), 10).toISOString(),
},
} as unknown as TryNewDot;

expect(hasBeenInNewDot30Days(tryNewDot)).toBe(false);
});

it('does not report 30 days in NewDot once the user has dismissed the nudge', () => {
const tryNewDot = {
classicRedirect: {
dismissed: true,
timestamp: subDays(new Date(), 60).toISOString(),
},
} as unknown as TryNewDot;

expect(hasBeenInNewDot30Days(tryNewDot)).toBe(false);
});

it('does not report 30 days in NewDot when no timestamp is set', () => {
const tryNewDot = {
classicRedirect: {
dismissed: false,
},
} as unknown as TryNewDot;

expect(hasBeenInNewDot30Days(tryNewDot)).toBe(false);
});

it('preserves isLockedToNewApp when nvp_tryNewDot is merged', async () => {
await Onyx.set(ONYXKEYS.NVP_TRY_NEW_DOT, {
isLockedToNewApp: true,
Expand Down
Loading