Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Free trial] Implement and show Free Trial banner and badges in the App after Free Trial starts #44371

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
7a2754b
feat: implement free trial banner and badges
pac-guerreiro Jun 21, 2024
c9c06e8
Merge branch 'main' into pac-guerreiro/feature/43672-implement-and-sh…
pac-guerreiro Jun 24, 2024
09efa11
Merge branch 'main' into pac-guerreiro/feature/43672-implement-and-sh…
pac-guerreiro Jun 24, 2024
fa048d0
refactor: remove duplicate sizing
pac-guerreiro Jun 25, 2024
702594a
Merge branch 'main' into pac-guerreiro/feature/43672-implement-and-sh…
pac-guerreiro Jun 25, 2024
69f6c26
refactor: apply suggestions
pac-guerreiro Jun 25, 2024
da47db4
refactor: apply suggestion from https://github.com/Expensify/App/pull…
pac-guerreiro Jun 25, 2024
b83fac3
Merge branch 'main' into pac-guerreiro/feature/43672-implement-and-sh…
pac-guerreiro Jun 26, 2024
1d40690
refactor: apply suggestions
pac-guerreiro Jun 26, 2024
1bc28d0
Merge branch 'main' into pac-guerreiro/feature/43672-implement-and-sh…
pac-guerreiro Jun 27, 2024
72672e2
Merge branch 'main' into pac-guerreiro/feature/43672-implement-and-sh…
pac-guerreiro Jun 27, 2024
dea6561
fix: subscription badge border color
pac-guerreiro Jun 27, 2024
0d6f524
Merge branch 'main' into pac-guerreiro/feature/43672-implement-and-sh…
pac-guerreiro Jun 28, 2024
97eb2a0
fix: applying badge success style out of free trial period
pac-guerreiro Jun 28, 2024
83f8a27
Merge branch 'main' into pac-guerreiro/feature/43672-implement-and-sh…
pac-guerreiro Jul 1, 2024
2f9698c
feat: change free trial started banner copy
pac-guerreiro Jul 1, 2024
ffa0da3
Merge branch 'main' into pac-guerreiro/feature/43672-implement-and-sh…
pac-guerreiro Jul 2, 2024
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
9 changes: 9 additions & 0 deletions src/components/LHNOptionsList/OptionRowLHN.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import React, {useCallback, useRef, useState} from 'react';
import type {GestureResponderEvent, ViewStyle} from 'react-native';
import {StyleSheet, View} from 'react-native';
import {useOnyx} from 'react-native-onyx';
import Badge from '@components/Badge';
import DisplayNames from '@components/DisplayNames';
import Hoverable from '@components/Hoverable';
import Icon from '@components/Icon';
Expand All @@ -25,6 +26,7 @@ import * as OptionsListUtils from '@libs/OptionsListUtils';
import Performance from '@libs/Performance';
import ReportActionComposeFocusManager from '@libs/ReportActionComposeFocusManager';
import * as ReportUtils from '@libs/ReportUtils';
import * as SubscriptionUtils from '@libs/SubscriptionUtils';
import * as ReportActionContextMenu from '@pages/home/report/ContextMenu/ReportActionContextMenu';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
Expand Down Expand Up @@ -227,6 +229,13 @@ function OptionRowLHN({reportID, isFocused = false, onSelectRow = () => {}, opti
ReportUtils.isSystemChat(report)
}
/>
{ReportUtils.isChatUsedForOnboarding(report) && SubscriptionUtils.isUserOnFreeTrial() && (
<Badge
success
text={translate('subscription.badge.freeTrial', {numOfDays: SubscriptionUtils.calculateRemainingFreeTrialDays()})}
badgeStyles={[styles.mnh0, styles.pl2, styles.pr2, styles.ml1]}
/>
)}
{isStatusVisible && (
<Tooltip
text={statusContent}
Expand Down
7 changes: 7 additions & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3463,6 +3463,9 @@ export default {
},
subscription: {
mobileReducedFunctionalityMessage: 'You can’t make changes to your subscription in the mobile app.',
badge: {
freeTrial: ({numOfDays}) => `Free trial: ${numOfDays} ${numOfDays === 1 ? 'day' : 'days'} left`,
},
billingBanner: {
policyOwnerAmountOwed: {
title: 'Your payment info is outdated',
Expand Down Expand Up @@ -3518,6 +3521,10 @@ export default {
subtitle: 'To get started, ',
subtitleLink: 'complete your setup checklist here',
},
trialStarted: {
title: ({numOfDays}) => `Free trial: ${numOfDays} ${numOfDays === 1 ? 'day' : 'days'} left!`,
subtitle: 'Add a payment card to continue using all of your favorite features.',
},
},
cardSection: {
title: 'Payment',
Expand Down
7 changes: 7 additions & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3963,6 +3963,9 @@ export default {
},
subscription: {
mobileReducedFunctionalityMessage: 'No puedes hacer cambios en tu suscripción en la aplicación móvil.',
badge: {
freeTrial: ({numOfDays}) => `Prueba gratuita: ${numOfDays === 1 ? `queda 1 día` : `quedan ${numOfDays} días`}`,
},
billingBanner: {
policyOwnerAmountOwed: {
title: 'Tu información de pago está desactualizada',
Expand Down Expand Up @@ -4020,6 +4023,10 @@ export default {
subtitle: 'Para empezar, ',
subtitleLink: 'completa la lista de configuración aquí',
},
trialStarted: {
title: ({numOfDays}) => `Prueba gratuita: ¡${numOfDays === 1 ? `queda 1 día` : `quedan ${numOfDays} días`}!`,
subtitle: 'Añade una tarjeta de pago para seguir utilizando tus funciones favoritas.',
},
},
cardSection: {
title: 'Pago',
Expand Down
2 changes: 1 addition & 1 deletion src/libs/PolicyUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ const isPolicyAdmin = (policy: OnyxInputOrEntry<Policy>, currentUserLogin?: stri
(policy?.role ?? (currentUserLogin && policy?.employeeList?.[currentUserLogin]?.role)) === CONST.POLICY.ROLE.ADMIN;

/**
* Checks if the current user is an user of the policy.
* Checks if the current user is of the role "user" on the policy.
*/
const isPolicyUser = (policy: OnyxInputOrEntry<Policy>, currentUserLogin?: string): boolean =>
(policy?.role ?? (currentUserLogin && policy?.employeeList?.[currentUserLogin]?.role)) === CONST.POLICY.ROLE.USER;
Expand Down
8 changes: 8 additions & 0 deletions src/pages/home/HeaderView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, {memo, useMemo} from 'react';
import {View} from 'react-native';
import type {OnyxEntry} from 'react-native-onyx';
import {withOnyx} from 'react-native-onyx';
import Badge from '@components/Badge';
import Button from '@components/Button';
import CaretWrapper from '@components/CaretWrapper';
import ConfirmModal from '@components/ConfirmModal';
Expand All @@ -27,6 +28,7 @@ import * as HeaderUtils from '@libs/HeaderUtils';
import Navigation from '@libs/Navigation/Navigation';
import * as OptionsListUtils from '@libs/OptionsListUtils';
import * as ReportUtils from '@libs/ReportUtils';
import * as SubscriptionUtils from '@libs/SubscriptionUtils';
import * as Link from '@userActions/Link';
import * as Report from '@userActions/Report';
import * as Session from '@userActions/Session';
Expand Down Expand Up @@ -346,6 +348,12 @@ function HeaderView({
)}
</PressableWithoutFeedback>
<View style={[styles.reportOptions, styles.flexRow, styles.alignItemsCenter]}>
{ReportUtils.isChatUsedForOnboarding(report) && SubscriptionUtils.isUserOnFreeTrial() && (
<Badge
success
text={translate('subscription.badge.freeTrial', {numOfDays: SubscriptionUtils.calculateRemainingFreeTrialDays()})}
/>
)}
{isTaskReport && !shouldUseNarrowLayout && ReportUtils.isOpenTaskReport(report, parentReportAction) && <TaskHeaderActionButton report={report} />}
{canJoin && !shouldUseNarrowLayout && joinButton}
{shouldShowThreeDotsButton && (
Expand Down
10 changes: 8 additions & 2 deletions src/pages/settings/InitialSettingsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import useWaitForNavigation from '@hooks/useWaitForNavigation';
import * as CurrencyUtils from '@libs/CurrencyUtils';
import Navigation from '@libs/Navigation/Navigation';
import shouldShowSubscriptionsMenu from '@libs/shouldShowSubscriptionsMenu';
import * as SubscriptionUtils from '@libs/SubscriptionUtils';
import * as UserUtils from '@libs/UserUtils';
import {hasGlobalWorkspaceSettingsRBR} from '@libs/WorkspacesSettingsUtils';
import * as ReportActionContextMenu from '@pages/home/report/ContextMenu/ReportActionContextMenu';
Expand Down Expand Up @@ -90,6 +91,8 @@ type MenuData = {
title?: string;
shouldShowRightIcon?: boolean;
iconRight?: IconAsset;
badgeText?: string;
badgeStyle?: ViewStyle;
};

type Menu = {sectionStyle: StyleProp<ViewStyle>; sectionTranslationKey: TranslationPaths; items: MenuData[]};
Expand Down Expand Up @@ -209,6 +212,8 @@ function InitialSettingsPage({session, userWallet, bankAccountList, fundList, wa
shouldShowRightIcon: true,
iconRight: Expensicons.NewWindow,
link: () => Link.buildOldDotURL(CONST.OLDDOT_URLS.ADMIN_POLICIES_URL),
badgeText: SubscriptionUtils.isUserOnFreeTrial() ? translate('subscription.badge.freeTrial', {numOfDays: SubscriptionUtils.calculateRemainingFreeTrialDays()}) : undefined,
badgeStyle: SubscriptionUtils.isUserOnFreeTrial() ? styles.badgeSuccess : undefined,
});
}

Expand All @@ -217,7 +222,7 @@ function InitialSettingsPage({session, userWallet, bankAccountList, fundList, wa
sectionTranslationKey: 'common.workspaces',
items,
};
}, [policies, styles.workspaceSettingsSectionContainer]);
}, [policies, styles.badgeSuccess, styles.workspaceSettingsSectionContainer, translate]);

/**
* Retuns a list of menu items data for general section
Expand Down Expand Up @@ -316,7 +321,8 @@ function InitialSettingsPage({session, userWallet, bankAccountList, fundList, wa
}
})}
iconStyles={item.iconStyles}
badgeText={getWalletBalance(isPaymentItem)}
badgeText={item.badgeText ?? getWalletBalance(isPaymentItem)}
badgeStyle={item.badgeStyle}
pac-guerreiro marked this conversation as resolved.
Show resolved Hide resolved
fallbackIcon={item.fallbackIcon}
brickRoadIndicator={item.brickRoadIndicator}
floatRightAvatars={item.floatRightAvatars}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from 'react';
import * as Illustrations from '@components/Icon/Illustrations';
import useLocalize from '@hooks/useLocalize';
import * as SubscriptionUtils from '@libs/SubscriptionUtils';
import BillingBanner from './BillingBanner';

function TrialStartedBillingBanner() {
const {translate} = useLocalize();

return (
<BillingBanner
title={translate('subscription.billingBanner.trialStarted.title', {numOfDays: SubscriptionUtils.calculateRemainingFreeTrialDays()})}
subtitle={translate('subscription.billingBanner.trialStarted.subtitle')}
icon={Illustrations.TreasureChest}
/>
);
}

TrialStartedBillingBanner.displayName = 'TrialStartedBillingBanner';

export default TrialStartedBillingBanner;
4 changes: 4 additions & 0 deletions src/pages/settings/Subscription/CardSection/CardSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@ import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import DateUtils from '@libs/DateUtils';
import Navigation from '@libs/Navigation/Navigation';
import * as SubscriptionUtils from '@libs/SubscriptionUtils';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
import PreTrialBillingBanner from './BillingBanner/PreTrialBillingBanner';
import SubscriptionBillingBanner from './BillingBanner/SubscriptionBillingBanner';
import TrialStartedBillingBanner from './BillingBanner/TrialStartedBillingBanner';
import CardSectionActions from './CardSectionActions';
import CardSectionDataEmpty from './CardSectionDataEmpty';
import CardSectionUtils from './utils';
Expand All @@ -42,6 +44,8 @@ function CardSection() {
let BillingBanner: React.ReactNode | undefined;
if (CardSectionUtils.shouldShowPreTrialBillingBanner()) {
BillingBanner = <PreTrialBillingBanner />;
} else if (SubscriptionUtils.isUserOnFreeTrial()) {
BillingBanner = <TrialStartedBillingBanner />;
} else if (billingStatus) {
BillingBanner = (
<SubscriptionBillingBanner
Expand Down
Loading