Skip to content

Commit a80e083

Browse files
authored
Revert "[CP staging] Revert "Introduce free trial countdown pop-up""
1 parent 3ec2459 commit a80e083

22 files changed

Lines changed: 663 additions & 0 deletions

File tree

assets/images/product-illustrations/arm_with_card_pos.svg

Lines changed: 1 addition & 0 deletions
Loading

src/CONST/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6448,6 +6448,11 @@ const CONST = {
64486448
},
64496449
ONBOARDING_JOINABLE_WORKSPACES_LIMIT: 5,
64506450
ACTIONABLE_TRACK_EXPENSE_WHISPER_MESSAGE: 'What would you like to do with this expense?',
6451+
TRIAL_REMINDER_VARIANT: {
6452+
BASIC: 'basic',
6453+
NEAR_END: 'nearEnd',
6454+
COUNTDOWN: 'countdown',
6455+
},
64516456
ONBOARDING_ACCOUNTING_MAPPING,
64526457

64536458
REPORT_FIELD_TITLE_FIELD_ID: 'text_title',

src/GlobalModals.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ const LazyPopoverReportActionContextMenu = React.lazy(() => import('./pages/inbo
1212
const LazyUpdateAppModal = React.lazy(() => import('./components/UpdateAppModal'));
1313
const LazyScreenShareRequestModal = React.lazy(() => import('./components/ScreenShareRequestModal'));
1414
const LazyProactiveAppReviewModalManager = React.lazy(() => import('./components/ProactiveAppReviewModalManager'));
15+
const LazyTrialPaymentReminderModalManager = React.lazy(() => import('./components/TrialPaymentReminderModalManager'));
1516

1617
// Maximum time (ms) the context menu mount can stay deferred before requestIdleCallback forces it to run,
1718
// guaranteeing mount even if the main thread never becomes idle.
@@ -68,6 +69,9 @@ function GlobalModals() {
6869
{/* Proactive app review modal shown when user has completed a trigger action */}
6970
<LazyProactiveAppReviewModalManager />
7071
</LazyModalSlot>
72+
<LazyModalSlot>
73+
<LazyTrialPaymentReminderModalManager />
74+
</LazyModalSlot>
7175
<LazyModalSlot>
7276
<LazyScreenShareRequestModal />
7377
</LazyModalSlot>

src/ONYXKEYS.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,9 @@ const ONYXKEYS = {
302302
/** ID associated with the payment card added by the user. */
303303
NVP_BILLING_FUND_ID: 'nvp_expensify_billingFundID',
304304

305+
/** ISO timestamp of the last time the trial payment reminder was dismissed */
306+
NVP_DISMISSED_TRIAL_PAYMENT_REMINDER: 'nvp_dismissedTrialPaymentReminder',
307+
305308
/** The user's freebie credits balance (in cents). */
306309
NVP_PRIVATE_FREEBIE_CREDITS: 'nvp_private_freebieCredits',
307310

@@ -1659,6 +1662,7 @@ type OnyxValuesMapping = {
16591662
[ONYXKEYS.NVP_FIRST_DAY_FREE_TRIAL]: string;
16601663
[ONYXKEYS.NVP_LAST_DAY_FREE_TRIAL]: string;
16611664
[ONYXKEYS.NVP_BILLING_FUND_ID]: number;
1665+
[ONYXKEYS.NVP_DISMISSED_TRIAL_PAYMENT_REMINDER]: string;
16621666
[ONYXKEYS.NVP_PRIVATE_FREEBIE_CREDITS]: number;
16631667
[ONYXKEYS.NVP_PRIVATE_AMOUNT_OWED]: number;
16641668
[ONYXKEYS.NVP_PRIVATE_OWNER_BILLING_GRACE_PERIOD_END]: number;

src/components/Icon/chunks/illustrations.chunk.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ import RunOutOfTime from '@assets/images/multifactorAuthentication/running-out-o
5656
import PendingTravel from '@assets/images/pending-travel.svg';
5757
// Product Illustrations
5858
import Abracadabra from '@assets/images/product-illustrations/abracadabra.svg';
59+
import ArmWithCardPos from '@assets/images/product-illustrations/arm_with_card_pos.svg';
5960
import BigVault from '@assets/images/product-illustrations/big-vault.svg';
6061
import BrokenCompanyCardBankConnection from '@assets/images/product-illustrations/broken-humpty-dumpty.svg';
6162
import BrokenMagnifyingGlass from '@assets/images/product-illustrations/broken-magnifying-glass.svg';
@@ -270,6 +271,7 @@ const Illustrations = {
270271

271272
// Product Illustrations
272273
Abracadabra,
274+
ArmWithCardPos,
273275
BigVault,
274276
BrokenCompanyCardBankConnection,
275277
BrokenMagnifyingGlass,
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import {useMemoizedLazyIllustrations} from '@hooks/useLazyAsset';
2+
import useLocalize from '@hooks/useLocalize';
3+
import useResponsiveLayout from '@hooks/useResponsiveLayout';
4+
import useThemeStyles from '@hooks/useThemeStyles';
5+
import type {CountdownTime, TrialReminderVariant} from '@hooks/useTrialPaymentReminder';
6+
7+
import colors from '@styles/theme/colors';
8+
9+
import CONST from '@src/CONST';
10+
11+
import React from 'react';
12+
import {View} from 'react-native';
13+
14+
import Button from './Button';
15+
import ImageSVG from './ImageSVG';
16+
import Modal from './Modal';
17+
import Text from './Text';
18+
19+
type TrialPaymentReminderModalProps = {
20+
/** Whether the modal is visible */
21+
isVisible: boolean;
22+
23+
/** The variant of the modal to display */
24+
variant: TrialReminderVariant;
25+
26+
/** Number of days remaining for 'nearEnd' variant */
27+
daysRemaining?: number;
28+
29+
/** Countdown time for 'countdown' variant */
30+
countdownTime?: CountdownTime;
31+
32+
/** Called when user presses Close */
33+
onClose: () => void;
34+
35+
/** Called when user presses Add payment card */
36+
onAddPaymentCard: () => void;
37+
};
38+
39+
function padZero(num: number): string {
40+
return num.toString().padStart(2, '0');
41+
}
42+
43+
function TrialPaymentReminderModal({isVisible, variant, daysRemaining, countdownTime, onClose, onAddPaymentCard}: TrialPaymentReminderModalProps) {
44+
const {shouldUseNarrowLayout} = useResponsiveLayout();
45+
const styles = useThemeStyles();
46+
const illustrations = useMemoizedLazyIllustrations(['ArmWithCardPos']);
47+
const {translate} = useLocalize();
48+
49+
return (
50+
<Modal
51+
onClose={onClose}
52+
onBackdropPress={() => {}}
53+
isVisible={isVisible}
54+
type={shouldUseNarrowLayout ? CONST.MODAL.MODAL_TYPE.BOTTOM_DOCKED : CONST.MODAL.MODAL_TYPE.CONFIRM}
55+
innerContainerStyle={styles.pv0}
56+
>
57+
<View style={[styles.alignItemsCenter, styles.wAuto, {backgroundColor: colors.blue800, height: CONST.CONFIRM_CONTENT_SVG_SIZE.HEIGHT}, styles.pb7]}>
58+
<ImageSVG
59+
src={illustrations.ArmWithCardPos}
60+
contentFit="contain"
61+
/>
62+
</View>
63+
<View style={[styles.m5]}>
64+
{variant === CONST.TRIAL_REMINDER_VARIANT.NEAR_END && daysRemaining !== undefined && (
65+
<Text style={[styles.textSuccess, styles.textStrong, styles.mb2]}>{translate('trialPaymentReminder.trialEndsInDays', {count: daysRemaining})}</Text>
66+
)}
67+
{variant === CONST.TRIAL_REMINDER_VARIANT.COUNTDOWN && !!countdownTime && (
68+
<Text style={[styles.textSuccess, styles.textStrong, styles.mb2]}>
69+
{translate('trialPaymentReminder.trialEndsCountdown', {
70+
hours: padZero(countdownTime.hours),
71+
minutes: padZero(countdownTime.minutes),
72+
seconds: padZero(countdownTime.seconds),
73+
})}
74+
</Text>
75+
)}
76+
77+
<Text style={[styles.textHeadlineH1, styles.mb3]}>{translate('trialPaymentReminder.title')}</Text>
78+
<Text style={[styles.textSupporting]}>{translate('trialPaymentReminder.subtitle')}</Text>
79+
80+
<Button
81+
success
82+
style={[styles.mt5]}
83+
onPress={onAddPaymentCard}
84+
pressOnEnter
85+
text={translate('trialPaymentReminder.addPaymentCardButton')}
86+
large
87+
/>
88+
<Button
89+
style={[styles.mt3]}
90+
onPress={onClose}
91+
text={translate('trialPaymentReminder.closeButton')}
92+
large
93+
/>
94+
</View>
95+
</Modal>
96+
);
97+
}
98+
99+
export default TrialPaymentReminderModal;
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import useOnyx from '@hooks/useOnyx';
2+
import useTrialPaymentReminder from '@hooks/useTrialPaymentReminder';
3+
4+
import Navigation from '@libs/Navigation/Navigation';
5+
6+
import ONYXKEYS from '@src/ONYXKEYS';
7+
import ROUTES from '@src/ROUTES';
8+
9+
import React, {useCallback, useState} from 'react';
10+
11+
import TrialPaymentReminderModal from './TrialPaymentReminderModal';
12+
13+
function TrialPaymentReminderModalManager() {
14+
const {isEligibleToShow, currentVariation, countdownTime, dismiss} = useTrialPaymentReminder();
15+
const [modal] = useOnyx(ONYXKEYS.MODAL);
16+
const [isModalOpen, setIsModalOpen] = useState(false);
17+
18+
const isOtherModalActive = !!modal?.isVisible || !!modal?.willAlertModalBecomeVisible;
19+
20+
if (isEligibleToShow && !isOtherModalActive && !isModalOpen) {
21+
setIsModalOpen(true);
22+
}
23+
if (!isEligibleToShow && isModalOpen) {
24+
setIsModalOpen(false);
25+
}
26+
27+
const handleClose = useCallback(() => {
28+
setIsModalOpen(false);
29+
dismiss();
30+
}, [dismiss]);
31+
32+
const handleAddPaymentCard = useCallback(() => {
33+
setIsModalOpen(false);
34+
dismiss();
35+
Navigation.navigate(ROUTES.SETTINGS_SUBSCRIPTION_ADD_PAYMENT_CARD);
36+
}, [dismiss]);
37+
38+
if (!currentVariation) {
39+
return null;
40+
}
41+
42+
return (
43+
<TrialPaymentReminderModal
44+
isVisible={isModalOpen}
45+
variant={currentVariation.variant}
46+
daysRemaining={currentVariation.daysRemaining}
47+
countdownTime={countdownTime}
48+
onClose={handleClose}
49+
onAddPaymentCard={handleAddPaymentCard}
50+
/>
51+
);
52+
}
53+
54+
export default TrialPaymentReminderModalManager;

0 commit comments

Comments
 (0)