diff --git a/src/libs/SubscriptionUtils.ts b/src/libs/SubscriptionUtils.ts index 2ea8091da1cc..1799b9c38eb3 100644 --- a/src/libs/SubscriptionUtils.ts +++ b/src/libs/SubscriptionUtils.ts @@ -175,12 +175,6 @@ Onyx.connect({ }, }); -let introSelected: OnyxEntry; -Onyx.connect({ - key: ONYXKEYS.NVP_INTRO_SELECTED, - callback: (value) => (introSelected = value), -}); - /** * @returns The date when the grace period ends. */ @@ -244,7 +238,7 @@ function hasInsufficientFundsError() { return billingStatus?.declineReason === 'insufficient_funds' && getAmountOwed() !== 0; } -function shouldShowPreTrialBillingBanner(): boolean { +function shouldShowPreTrialBillingBanner(introSelected: OnyxEntry): boolean { // We don't want to show the Pre Trial banner if the user was a Test Drive Receiver that created their workspace // with the promo code. const wasUserTestDriveReceiver = introSelected?.previousChoices?.some((choice) => choice === CONST.ONBOARDING_CHOICES.TEST_DRIVE_RECEIVER); @@ -488,13 +482,13 @@ function calculateRemainingFreeTrialDays(): number { * @param policies - The policies collection. * @returns The free trial badge text . */ -function getFreeTrialText(policies: OnyxCollection | null): string | undefined { +function getFreeTrialText(policies: OnyxCollection | null, introSelected: OnyxEntry): string | undefined { const ownedPaidPolicies = getOwnedPaidPolicies(policies, currentUserAccountID); if (isEmptyObject(ownedPaidPolicies)) { return undefined; } - if (shouldShowPreTrialBillingBanner()) { + if (shouldShowPreTrialBillingBanner(introSelected)) { return translateLocal('subscription.billingBanner.preTrial.title'); } if (isUserOnFreeTrial()) { diff --git a/src/pages/settings/InitialSettingsPage.tsx b/src/pages/settings/InitialSettingsPage.tsx index a90ae2543e1f..010cc845677a 100755 --- a/src/pages/settings/InitialSettingsPage.tsx +++ b/src/pages/settings/InitialSettingsPage.tsx @@ -92,6 +92,7 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr const [policies] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {canBeMissing: true}); const [stripeCustomerId] = useOnyx(ONYXKEYS.NVP_PRIVATE_STRIPE_CUSTOMER_ID, {canBeMissing: true}); const [session] = useOnyx(ONYXKEYS.SESSION, {canBeMissing: false}); + const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED, {canBeMissing: true}); const {shouldUseNarrowLayout} = useResponsiveLayout(); const network = useNetwork(); @@ -111,7 +112,7 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr const shouldLogout = useRef(false); - const freeTrialText = getFreeTrialText(policies); + const freeTrialText = getFreeTrialText(policies, introSelected); const shouldDisplayLHB = !shouldUseNarrowLayout; diff --git a/src/pages/settings/Subscription/CardSection/CardSection.tsx b/src/pages/settings/Subscription/CardSection/CardSection.tsx index 33b304403d93..4f0b975d41ee 100644 --- a/src/pages/settings/Subscription/CardSection/CardSection.tsx +++ b/src/pages/settings/Subscription/CardSection/CardSection.tsx @@ -52,6 +52,7 @@ function CardSection() { const [session] = useOnyx(ONYXKEYS.SESSION, {canBeMissing: true}); const [fundList] = useOnyx(ONYXKEYS.FUND_LIST, {canBeMissing: true}); const [purchaseList] = useOnyx(ONYXKEYS.PURCHASE_LIST, {canBeMissing: true}); + const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED, {canBeMissing: true}); const hasTeam2025Pricing = useHasTeam2025Pricing(); const subscriptionPlan = useSubscriptionPlan(); const [subscriptionRetryBillingStatusPending] = useOnyx(ONYXKEYS.SUBSCRIPTION_RETRY_BILLING_STATUS_PENDING, {canBeMissing: true}); @@ -129,7 +130,7 @@ function CardSection() { let BillingBanner: React.ReactNode | undefined; if (shouldShowDiscountBanner(hasTeam2025Pricing, subscriptionPlan)) { BillingBanner = ; - } else if (shouldShowPreTrialBillingBanner()) { + } else if (shouldShowPreTrialBillingBanner(introSelected)) { BillingBanner = ; } else if (isUserOnFreeTrial()) { BillingBanner = ; diff --git a/src/pages/settings/Subscription/FreeTrial.tsx b/src/pages/settings/Subscription/FreeTrial.tsx index 6cb54c3b658b..0837b43c03e7 100644 --- a/src/pages/settings/Subscription/FreeTrial.tsx +++ b/src/pages/settings/Subscription/FreeTrial.tsx @@ -26,6 +26,7 @@ function FreeTrial({badgeStyles, pressable = false, addSpacing = false, success const [policies] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {canBeMissing: true}); const [firstDayFreeTrial] = useOnyx(ONYXKEYS.NVP_FIRST_DAY_FREE_TRIAL, {canBeMissing: true}); const [lastDayFreeTrial] = useOnyx(ONYXKEYS.NVP_LAST_DAY_FREE_TRIAL, {canBeMissing: true}); + const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED, {canBeMissing: true}); const privateSubscription = usePrivateSubscription(); const [freeTrialText, setFreeTrialText] = useState(undefined); @@ -35,8 +36,8 @@ function FreeTrial({badgeStyles, pressable = false, addSpacing = false, success if (!privateSubscription && !isOffline) { return; } - setFreeTrialText(getFreeTrialText(policies)); - }, [isOffline, privateSubscription, policies, firstDayFreeTrial, lastDayFreeTrial]); + setFreeTrialText(getFreeTrialText(policies, introSelected)); + }, [isOffline, privateSubscription, policies, firstDayFreeTrial, lastDayFreeTrial, introSelected]); if (!freeTrialText) { return null; diff --git a/tests/unit/SubscriptionUtilsTest.ts b/tests/unit/SubscriptionUtilsTest.ts index 3e586c81bd8a..8718195b2e8e 100644 --- a/tests/unit/SubscriptionUtilsTest.ts +++ b/tests/unit/SubscriptionUtilsTest.ts @@ -1,3 +1,4 @@ +import {act} from '@testing-library/react-native'; import {addDays, addMinutes, format as formatDate, getUnixTime, subDays} from 'date-fns'; import Onyx from 'react-native-onyx'; import type {OnyxEntry} from 'react-native-onyx'; @@ -11,12 +12,14 @@ import { PAYMENT_STATUS, shouldRestrictUserBillableActions, shouldShowDiscountBanner, + shouldShowPreTrialBillingBanner, } from '@libs/SubscriptionUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {BillingGraceEndPeriod, BillingStatus, FundList, StripeCustomerID} from '@src/types/onyx'; +import type {BillingGraceEndPeriod, BillingStatus, FundList, IntroSelected, StripeCustomerID} from '@src/types/onyx'; import createRandomPolicy from '../utils/collections/policies'; import {STRIPE_CUSTOMER_ID} from '../utils/TestHelper'; +import waitForBatchedUpdatesWithAct from '../utils/waitForBatchedUpdatesWithAct'; const billingGraceEndPeriod: BillingGraceEndPeriod = { value: 0, @@ -624,4 +627,36 @@ describe('SubscriptionUtils', () => { expect(getEarlyDiscountInfo()).toBeNull(); }); }); + describe('shouldShowPreTrialBillingBanner', () => { + it('should return true if the user is NOT on a free trial and trial has not ended', async () => { + // Free trial starts in the future → user is not currently on trial + await act(async () => { + await Onyx.multiSet({ + [ONYXKEYS.NVP_FIRST_DAY_FREE_TRIAL]: formatDate(addDays(new Date(), 5), CONST.DATE.FNS_DATE_TIME_FORMAT_STRING), + [ONYXKEYS.NVP_LAST_DAY_FREE_TRIAL]: formatDate(addDays(new Date(), 10), CONST.DATE.FNS_DATE_TIME_FORMAT_STRING), + }); + }); + + await waitForBatchedUpdatesWithAct(); + + const introSelected: OnyxEntry = { + choice: CONST.ONBOARDING_CHOICES.MANAGE_TEAM, + }; + + expect(shouldShowPreTrialBillingBanner(introSelected)).toBeTruthy(); + }); + + it('should return false if the free trial has ended', async () => { + await Onyx.multiSet({ + [ONYXKEYS.NVP_FIRST_DAY_FREE_TRIAL]: formatDate(subDays(new Date(), 20), CONST.DATE.FNS_DATE_TIME_FORMAT_STRING), + [ONYXKEYS.NVP_LAST_DAY_FREE_TRIAL]: formatDate(subDays(new Date(), 5), CONST.DATE.FNS_DATE_TIME_FORMAT_STRING), + }); + + const introSelected: OnyxEntry = { + choice: CONST.ONBOARDING_CHOICES.MANAGE_TEAM, + }; + + expect(shouldShowPreTrialBillingBanner(introSelected)).toBeFalsy(); + }); + }); });