From 6affb05227a7ec871ccc9769426b408fea76f87b Mon Sep 17 00:00:00 2001 From: allgandalf Date: Fri, 19 Sep 2025 18:40:01 +0530 Subject: [PATCH 1/3] remove usage of onyx.connect for introSelected --- src/libs/SubscriptionUtils.ts | 12 ++----- src/pages/settings/InitialSettingsPage.tsx | 3 +- .../Subscription/CardSection/CardSection.tsx | 3 +- src/pages/settings/Subscription/FreeTrial.tsx | 5 +-- tests/unit/SubscriptionUtilsTest.ts | 32 ++++++++++++++++++- 5 files changed, 41 insertions(+), 14 deletions(-) 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 727fea7864f7..859de0cf3275 100644 --- a/src/pages/settings/Subscription/CardSection/CardSection.tsx +++ b/src/pages/settings/Subscription/CardSection/CardSection.tsx @@ -51,6 +51,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}); @@ -124,7 +125,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..74cb039414b1 100644 --- a/tests/unit/SubscriptionUtilsTest.ts +++ b/tests/unit/SubscriptionUtilsTest.ts @@ -5,16 +5,18 @@ import { calculateRemainingFreeTrialDays, doesUserHavePaymentCardAdded, getEarlyDiscountInfo, + getFreeTrialText, getSubscriptionStatus, hasUserFreeTrialEnded, isUserOnFreeTrial, 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'; @@ -624,4 +626,32 @@ 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 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), + }); + + 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(); + }); + }); }); From 6a8d5bb337811bead107273be7e789292ce77265 Mon Sep 17 00:00:00 2001 From: allgandalf Date: Fri, 19 Sep 2025 18:45:24 +0530 Subject: [PATCH 2/3] fix changed files eslint --- tests/unit/SubscriptionUtilsTest.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/unit/SubscriptionUtilsTest.ts b/tests/unit/SubscriptionUtilsTest.ts index 74cb039414b1..2c84d844b9aa 100644 --- a/tests/unit/SubscriptionUtilsTest.ts +++ b/tests/unit/SubscriptionUtilsTest.ts @@ -5,7 +5,6 @@ import { calculateRemainingFreeTrialDays, doesUserHavePaymentCardAdded, getEarlyDiscountInfo, - getFreeTrialText, getSubscriptionStatus, hasUserFreeTrialEnded, isUserOnFreeTrial, From 669f36be439b41e5e5e59cd921eae49968544754 Mon Sep 17 00:00:00 2001 From: allgandalf Date: Thu, 2 Oct 2025 11:06:18 +0530 Subject: [PATCH 3/3] update test --- tests/unit/SubscriptionUtilsTest.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tests/unit/SubscriptionUtilsTest.ts b/tests/unit/SubscriptionUtilsTest.ts index 2c84d844b9aa..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'; @@ -18,6 +19,7 @@ import ONYXKEYS from '@src/ONYXKEYS'; 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, @@ -628,11 +630,15 @@ describe('SubscriptionUtils', () => { 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 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 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, };