Skip to content

Commit

Permalink
Merge pull request #43484 from MrMuzyk/feat/subscription-size-backend…
Browse files Browse the repository at this point in the history
…-integration

feat: Subscription size backend integration
  • Loading branch information
amyevans authored Jun 14, 2024
2 parents 15a6ef4 + be89e44 commit 8dea433
Show file tree
Hide file tree
Showing 20 changed files with 209 additions and 74 deletions.
5 changes: 4 additions & 1 deletion src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,10 @@ const ROUTES = {
SETTINGS_PRONOUNS: 'settings/profile/pronouns',
SETTINGS_PREFERENCES: 'settings/preferences',
SETTINGS_SUBSCRIPTION: 'settings/subscription',
SETTINGS_SUBSCRIPTION_SIZE: 'settings/subscription/subscription-size',
SETTINGS_SUBSCRIPTION_SIZE: {
route: 'settings/subscription/subscription-size',
getRoute: (canChangeSize: 0 | 1) => `settings/subscription/subscription-size?canChangeSize=${canChangeSize}` as const,
},
SETTINGS_SUBSCRIPTION_ADD_PAYMENT_CARD: 'settings/subscription/add-payment-card',
SETTINGS_SUBSCRIPTION_DISABLE_AUTO_RENEW_SURVEY: 'settings/subscription/disable-auto-renew-survey',
SETTINGS_PRIORITY_MODE: 'settings/preferences/priority-mode',
Expand Down
1 change: 1 addition & 0 deletions src/components/FocusTrap/WIDE_LAYOUT_INACTIVE_SCREENS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const WIDE_LAYOUT_INACTIVE_SCREENS: string[] = [
SCREENS.SETTINGS.WALLET.ROOT,
SCREENS.SETTINGS.ABOUT,
SCREENS.SETTINGS.WORKSPACES,
SCREENS.SETTINGS.SUBSCRIPTION.ROOT,
SCREENS.WORKSPACE.INITIAL,
SCREENS.WORKSPACE.PROFILE,
SCREENS.WORKSPACE.CARD,
Expand Down
3 changes: 2 additions & 1 deletion src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3268,11 +3268,12 @@ export default {
subscriptionSize: 'Subscription size',
activeMembers: ({size}) => `${size} active members/month`,
subscriptionRenews: 'Subscription renews',
youCantDowngrade: 'You can’t downgrade during your annual subscription',
youCantDowngrade: 'You can’t downgrade during your annual subscription.',
youAlreadyCommitted: ({size, date}) =>
`You already committed to an annual subscription size of ${size} active members per month until ${date}. You can switch to a pay-per-use subscription on ${date} by disabling auto-renew.`,
error: {
size: 'Please enter a valid subscription size.',
sameSize: 'Please enter a number different than your current subscription size.',
},
},
paymentCard: {
Expand Down
3 changes: 2 additions & 1 deletion src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3772,11 +3772,12 @@ export default {
subscriptionSize: 'Tamaño de suscripción',
activeMembers: ({size}) => `${size} miembros activos/mes`,
subscriptionRenews: 'Renovación de la suscripción',
youCantDowngrade: 'No puedes bajar de categoría durante tu suscripción anual',
youCantDowngrade: 'No puedes bajar de categoría durante tu suscripción anual.',
youAlreadyCommitted: ({size, date}) =>
`Ya se ha comprometido a un tamaño de suscripción anual de ${size} miembros activos al mes hasta el ${date}. Puede cambiar a una suscripción de pago por uso en ${date} desactivando la auto-renovación.`,
error: {
size: 'Por favor ingrese un tamaño de suscripción valido.',
sameSize: 'Por favor, introduce un número diferente al de tu subscripción actual.',
},
},
paymentCard: {
Expand Down
5 changes: 5 additions & 0 deletions src/libs/API/parameters/UpdateSubscriptionSizeParams.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
type UpdateSubscriptionSizeParams = {
userCount: number;
};

export default UpdateSubscriptionSizeParams;
1 change: 1 addition & 0 deletions src/libs/API/parameters/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -227,3 +227,4 @@ export type {default as PayInvoiceParams} from './PayInvoiceParams';
export type {default as MarkAsCashParams} from './MarkAsCashParams';
export type {default as UpdateSubscriptionTypeParams} from './UpdateSubscriptionTypeParams';
export type {default as SignUpUserParams} from './SignUpUserParams';
export type {default as UpdateSubscriptionSizeParams} from './UpdateSubscriptionSizeParams';
2 changes: 2 additions & 0 deletions src/libs/API/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ const WRITE_COMMANDS = {
MARK_AS_CASH: 'MarkAsCash',
UPDATE_SUBSCRIPTION_TYPE: 'UpdateSubscriptionType',
SIGN_UP_USER: 'SignUpUser',
UPDATE_SUBSCRIPTION_SIZE: 'UpdateSubscriptionSize',
} as const;

type WriteCommand = ValueOf<typeof WRITE_COMMANDS>;
Expand Down Expand Up @@ -448,6 +449,7 @@ type WriteCommandParameters = {
[WRITE_COMMANDS.MARK_AS_CASH]: Parameters.MarkAsCashParams;
[WRITE_COMMANDS.UPDATE_SUBSCRIPTION_TYPE]: Parameters.UpdateSubscriptionTypeParams;
[WRITE_COMMANDS.SIGN_UP_USER]: Parameters.SignUpUserParams;
[WRITE_COMMANDS.UPDATE_SUBSCRIPTION_SIZE]: Parameters.UpdateSubscriptionSizeParams;
};

const READ_COMMANDS = {
Expand Down
5 changes: 4 additions & 1 deletion src/libs/Navigation/linkingConfig/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,10 @@ const config: LinkingOptions<RootStackParamList>['config'] = {
path: ROUTES.SETTINGS_STATUS_CLEAR_AFTER_TIME,
},
[SCREENS.SETTINGS.SUBSCRIPTION.SIZE]: {
path: ROUTES.SETTINGS_SUBSCRIPTION_SIZE,
path: ROUTES.SETTINGS_SUBSCRIPTION_SIZE.route,
parse: {
canChangeSize: Number,
},
},
[SCREENS.SETTINGS.SUBSCRIPTION.DISABLE_AUTO_RENEW_SURVEY]: {
path: ROUTES.SETTINGS_SUBSCRIPTION_DISABLE_AUTO_RENEW_SURVEY,
Expand Down
4 changes: 4 additions & 0 deletions src/libs/Navigation/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,10 @@ type SettingsNavigatorParamList = {
orderWeight: number;
tagName: string;
};
[SCREENS.SETTINGS.SUBSCRIPTION.ROOT]: undefined;
[SCREENS.SETTINGS.SUBSCRIPTION.SIZE]: {
canChangeSize: 0 | 1;
};
[SCREENS.SETTINGS.SUBSCRIPTION.ADD_PAYMENT_CARD]: undefined;
[SCREENS.WORKSPACE.TAXES_SETTINGS]: {
policyID: string;
Expand Down
2 changes: 1 addition & 1 deletion src/libs/ValidationUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -484,7 +484,7 @@ function isExistingTaxName(taxName: string, taxRates: TaxRates): boolean {
*/
function isValidSubscriptionSize(subscriptionSize: string): boolean {
const parsedSubscriptionSize = Number(subscriptionSize);
return !Number.isNaN(parsedSubscriptionSize) && parsedSubscriptionSize > 0 && parsedSubscriptionSize <= CONST.SUBSCRIPTION_SIZE_LIMIT;
return !Number.isNaN(parsedSubscriptionSize) && parsedSubscriptionSize > 0 && parsedSubscriptionSize <= CONST.SUBSCRIPTION_SIZE_LIMIT && Number.isInteger(parsedSubscriptionSize);
}

export {
Expand Down
60 changes: 59 additions & 1 deletion src/libs/actions/Subscription.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {READ_COMMANDS, WRITE_COMMANDS} from '@libs/API/types';
import type {SubscriptionType} from '@src/CONST';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {OnyxData} from '@src/types/onyx/Request';

/**
* Fetches data when the user opens the SubscriptionSettingsPage
Expand Down Expand Up @@ -61,4 +62,61 @@ function updateSubscriptionType(type: SubscriptionType) {
});
}

export {openSubscriptionPage, updateSubscriptionType};
function updateSubscriptionSize(newSubscriptionSize: number, currentSubscriptionSize: number) {
const onyxData: OnyxData = {
optimisticData: [
{
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.NVP_PRIVATE_SUBSCRIPTION,
value: {
userCount: newSubscriptionSize,
errorFields: {
userCount: null,
},
pendingFields: {
userCount: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE,
},
},
},
],
successData: [
{
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.NVP_PRIVATE_SUBSCRIPTION,
value: {
userCount: newSubscriptionSize,
errorFields: {
userCount: null,
},
pendingFields: {
userCount: null,
},
},
},
],
failureData: [
{
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.NVP_PRIVATE_SUBSCRIPTION,
value: {
userCount: currentSubscriptionSize,
pendingFields: {
userCount: null,
},
},
},
],
};

API.write(WRITE_COMMANDS.UPDATE_SUBSCRIPTION_SIZE, {userCount: newSubscriptionSize}, onyxData);
}

function clearUpdateSubscriptionSizeError() {
Onyx.merge(ONYXKEYS.NVP_PRIVATE_SUBSCRIPTION, {
errorFields: {
userCount: null,
},
});
}

export {openSubscriptionPage, updateSubscriptionType, updateSubscriptionSize, clearUpdateSubscriptionSizeError};
31 changes: 22 additions & 9 deletions src/pages/settings/Subscription/SubscriptionDetails/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,25 +42,38 @@ function SubscriptionDetails() {
const [account] = useOnyx(ONYXKEYS.ACCOUNT);

const onOptionSelected = (option: SubscriptionType) => {
if (privateSubscription?.type === CONST.SUBSCRIPTION.TYPE.ANNUAL && option === CONST.SUBSCRIPTION.TYPE.PAYPERUSE && !account?.canDowngrade) {
Navigation.navigate(ROUTES.SETTINGS_SUBSCRIPTION_SIZE.getRoute(0));
return;
}

Subscription.updateSubscriptionType(option);
};

const onSubscriptionSizePress = () => {
Navigation.navigate(ROUTES.SETTINGS_SUBSCRIPTION_SIZE);
Navigation.navigate(ROUTES.SETTINGS_SUBSCRIPTION_SIZE.getRoute(1));
};

// This section is only shown when the subscription is annual
const subscriptionSizeSection: React.JSX.Element | null =
privateSubscription?.type === CONST.SUBSCRIPTION.TYPE.ANNUAL ? (
<>
<MenuItemWithTopDescription
description={translate('subscription.details.subscriptionSize')}
shouldShowRightIcon
onPress={onSubscriptionSizePress}
wrapperStyle={styles.sectionMenuItemTopDescription}
style={styles.mt5}
title={`${privateSubscription?.userCount ?? ''}`}
/>
<OfflineWithFeedback
pendingAction={privateSubscription?.pendingFields?.userCount}
errors={privateSubscription?.errorFields?.userCount}
onClose={() => {
Subscription.clearUpdateSubscriptionSizeError();
}}
>
<MenuItemWithTopDescription
description={translate('subscription.details.subscriptionSize')}
shouldShowRightIcon
onPress={onSubscriptionSizePress}
wrapperStyle={styles.sectionMenuItemTopDescription}
style={styles.mt5}
title={`${privateSubscription?.userCount ?? ''}`}
/>
</OfflineWithFeedback>
{!privateSubscription?.userCount && <Text style={[styles.mt2, styles.textLabelSupporting, styles.textLineHeightNormal]}>{translate('subscription.details.headsUp')}</Text>}
</>
) : null;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import {format} from 'date-fns';
import React, {useState} from 'react';
import type {StyleProp, TextStyle} from 'react-native';
import {View} from 'react-native';
Expand All @@ -10,6 +9,7 @@ import useSubscriptionPlan from '@hooks/useSubscriptionPlan';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import Navigation from '@navigation/Navigation';
import {formatSubscriptionEndDate} from '@pages/settings/Subscription/utils';
import ToggleSettingOptionRow from '@pages/workspace/workflows/ToggleSettingsOptionRow';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
Expand All @@ -28,7 +28,7 @@ function SubscriptionSettings() {
const [autoRenew, setAutoRenew] = useState(true);
const [autoIncrease, setAutoIncrease] = useState(false);

const autoRenewalDate = privateSubscription?.endDate ? format(new Date(`${privateSubscription?.endDate}T00:00:00`), CONST.DATE.MONTH_DAY_YEAR_ABBR_FORMAT) : '';
const autoRenewalDate = formatSubscriptionEndDate(privateSubscription?.endDate);

// TODO all actions will be implemented in next phase
const handleAutoRenewToggle = () => {
Expand Down
28 changes: 15 additions & 13 deletions src/pages/settings/Subscription/SubscriptionSize/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type {StackScreenProps} from '@react-navigation/stack';
import React from 'react';
import {useOnyx} from 'react-native-onyx';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
Expand All @@ -6,31 +7,31 @@ import useLocalize from '@hooks/useLocalize';
import useSubStep from '@hooks/useSubStep';
import type {SubStepProps} from '@hooks/useSubStep/types';
import Navigation from '@libs/Navigation/Navigation';
import type {SettingsNavigatorParamList} from '@navigation/types';
import * as Subscription from '@userActions/Subscription';
import ONYXKEYS from '@src/ONYXKEYS';
import type SCREENS from '@src/SCREENS';
import INPUT_IDS from '@src/types/form/SubscriptionSizeForm';
import Confirmation from './substeps/Confirmation';
import Size from './substeps/Size';

const bodyContent: Array<React.ComponentType<SubStepProps>> = [Size, Confirmation];

function SubscriptionSizePage() {
type SubscriptionSizePageProps = StackScreenProps<SettingsNavigatorParamList, typeof SCREENS.SETTINGS.SUBSCRIPTION.SIZE>;

function SubscriptionSizePage({route}: SubscriptionSizePageProps) {
const [privateSubscription] = useOnyx(ONYXKEYS.NVP_PRIVATE_SUBSCRIPTION);
const [subscriptionSizeFormDraft] = useOnyx(ONYXKEYS.FORMS.SUBSCRIPTION_SIZE_FORM_DRAFT);
const {translate} = useLocalize();
// TODO startFrom variable will get it's value based on ONYX data, it will be implemented in next phase (account?.canDowngrade field)
const CAN_DOWNGRADE = true;
const startFrom = CAN_DOWNGRADE ? 0 : 1;
const canChangeSubscriptionSize = !!(route.params?.canChangeSize ?? 1);
const startFrom = canChangeSubscriptionSize ? 0 : 1;

const onFinished = () => {
if (CAN_DOWNGRADE) {
// TODO this is temporary solution for the time being, API call will be implemented in next phase
// eslint-disable-next-line no-console
console.log(subscriptionSizeFormDraft);
return;
}

Subscription.updateSubscriptionSize(subscriptionSizeFormDraft ? Number(subscriptionSizeFormDraft[INPUT_IDS.SUBSCRIPTION_SIZE]) : 0, privateSubscription?.userCount ?? 0);
Navigation.goBack();
};

const {componentToRender: SubStep, isEditing, screenIndex, nextScreen, prevScreen, moveTo} = useSubStep({bodyContent, startFrom, onFinished});
const {componentToRender: SubStep, screenIndex, nextScreen, prevScreen, moveTo} = useSubStep({bodyContent, startFrom, onFinished});

const onBackButtonPress = () => {
if (screenIndex !== 0 && startFrom === 0) {
Expand All @@ -47,13 +48,14 @@ function SubscriptionSizePage() {
includeSafeAreaPaddingBottom={false}
shouldEnablePickerAvoiding={false}
shouldEnableMaxHeight
shouldShowOfflineIndicatorInWideScreen
>
<HeaderWithBackButton
title={translate('subscription.subscriptionSize.title')}
onBackButtonPress={onBackButtonPress}
/>
<SubStep
isEditing={isEditing}
isEditing={canChangeSubscriptionSize}
onNext={nextScreen}
onMove={moveTo}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,31 +6,36 @@ import FixedFooter from '@components/FixedFooter';
import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription';
import Text from '@components/Text';
import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
import type {SubStepProps} from '@hooks/useSubStep/types';
import useThemeStyles from '@hooks/useThemeStyles';
import {getNewSubscriptionRenewalDate} from '@pages/settings/Subscription/SubscriptionSize/utils';
import Navigation from '@navigation/Navigation';
import {formatSubscriptionEndDate, getNewSubscriptionRenewalDate} from '@pages/settings/Subscription/utils';
import * as FormActions from '@userActions/FormActions';
import ONYXKEYS from '@src/ONYXKEYS';
import INPUT_IDS from '@src/types/form/SubscriptionSizeForm';

type ConfirmationProps = SubStepProps;

function Confirmation({onNext}: ConfirmationProps) {
function Confirmation({onNext, isEditing}: ConfirmationProps) {
const {translate} = useLocalize();
const styles = useThemeStyles();
const {isOffline} = useNetwork();
const [account] = useOnyx(ONYXKEYS.ACCOUNT);
const [privateSubscription] = useOnyx(ONYXKEYS.NVP_PRIVATE_SUBSCRIPTION);
const [subscriptionSizeFormDraft] = useOnyx(ONYXKEYS.FORMS.SUBSCRIPTION_SIZE_FORM_DRAFT);
const subscriptionRenewalDate = getNewSubscriptionRenewalDate();

// TODO this is temporary and will be replaced in next phase once data in ONYX is ready
// we will have to check if the amount of active members is less than the current amount of active members and if account?.canDowngrade is true - if so then we can't downgrade
const CAN_DOWNGRADE = true;
// TODO this is temporary and will be replaced in next phase once data in ONYX is ready
const SUBSCRIPTION_UNTIL = subscriptionRenewalDate;
const isTryingToIncreaseSubscriptionSize = (subscriptionSizeFormDraft ? Number(subscriptionSizeFormDraft[INPUT_IDS.SUBSCRIPTION_SIZE]) : 0) > (privateSubscription?.userCount ?? 0);
const canChangeSubscriptionSize = (account?.canDowngrade ?? false) || (isTryingToIncreaseSubscriptionSize && isEditing);
const formattedSubscriptionEndDate = formatSubscriptionEndDate(privateSubscription?.endDate);

const onClosePress = () => {
FormActions.clearDraftValues(ONYXKEYS.FORMS.SUBSCRIPTION_SIZE_FORM);
Navigation.goBack();
};

return (
<View style={[styles.flexGrow1]}>
{CAN_DOWNGRADE ? (
{canChangeSubscriptionSize ? (
<>
<Text style={[styles.ph5, styles.pb3]}>{translate('subscription.subscriptionSize.confirmDetails')}</Text>
<MenuItemWithTopDescription
Expand All @@ -49,20 +54,28 @@ function Confirmation({onNext}: ConfirmationProps) {
<Text style={[styles.ph5, styles.pb5, styles.textNormalThemeText]}>{translate('subscription.subscriptionSize.youCantDowngrade')}</Text>
<Text style={[styles.ph5, styles.textNormalThemeText]}>
{translate('subscription.subscriptionSize.youAlreadyCommitted', {
size: subscriptionSizeFormDraft ? subscriptionSizeFormDraft[INPUT_IDS.SUBSCRIPTION_SIZE] : 0,
date: SUBSCRIPTION_UNTIL,
size: privateSubscription?.userCount ?? 0,
date: formattedSubscriptionEndDate,
})}
</Text>
</>
)}
<FixedFooter style={[styles.mtAuto]}>
<Button
isDisabled={isOffline}
success
large
onPress={onNext}
text={translate(CAN_DOWNGRADE ? 'common.save' : 'common.close')}
/>
{canChangeSubscriptionSize ? (
<Button
success
large
onPress={onNext}
text={translate('common.save')}
/>
) : (
<Button
success
large
onPress={onClosePress}
text={translate('common.close')}
/>
)}
</FixedFooter>
</View>
);
Expand Down
Loading

0 comments on commit 8dea433

Please sign in to comment.