From dea3afe238c28db5f296206b4d7e3b8d23844f78 Mon Sep 17 00:00:00 2001 From: Michal Muzyk Date: Fri, 7 Jun 2024 15:45:37 +0200 Subject: [PATCH 01/10] feat: subscription size backend integration --- src/ROUTES.ts | 5 ++++- src/libs/Navigation/linkingConfig/config.ts | 5 ++++- src/libs/Navigation/types.ts | 4 ++++ .../SubscriptionDetails/index.tsx | 7 +++++++ .../SubscriptionSize/SubscriptionSizePage.tsx | 18 +++++++++++------- .../substeps/Confirmation.tsx | 19 ++++++++++--------- .../SubscriptionSize/substeps/Size.tsx | 5 +++-- src/types/onyx/Account.ts | 3 +++ 8 files changed, 46 insertions(+), 20 deletions(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index fd03e3c19e2d..2c89956c5983 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -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_PRIORITY_MODE: 'settings/preferences/priority-mode', SETTINGS_LANGUAGE: 'settings/preferences/language', diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index 6c570b444d80..fd7f08cf2be7 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -282,7 +282,10 @@ const config: LinkingOptions['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.WORKSPACE.CURRENCY]: { path: ROUTES.WORKSPACE_PROFILE_CURRENCY.route, diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 5597e7ce00da..7649d6a5bf9f 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -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; diff --git a/src/pages/settings/Subscription/SubscriptionDetails/index.tsx b/src/pages/settings/Subscription/SubscriptionDetails/index.tsx index 350d84d00a46..a688b9409eb1 100644 --- a/src/pages/settings/Subscription/SubscriptionDetails/index.tsx +++ b/src/pages/settings/Subscription/SubscriptionDetails/index.tsx @@ -11,9 +11,11 @@ import Section from '@components/Section'; import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; +import Navigation from '@libs/Navigation/Navigation'; import variables from '@styles/variables'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import ROUTES from '@src/ROUTES'; type SubscriptionVariant = ValueOf; @@ -41,6 +43,11 @@ function SubscriptionDetails() { const [selectedOption, setSelectedOption] = useState(privateSubscription?.type ?? CONST.SUBSCRIPTION.TYPE.ANNUAL); const onOptionSelected = (option: SubscriptionVariant) => { + if (privateSubscription?.type === CONST.SUBSCRIPTION.TYPE.ANNUAL && option === CONST.SUBSCRIPTION.TYPE.PAYPERUSE) { + Navigation.navigate(ROUTES.SETTINGS_SUBSCRIPTION_SIZE.getRoute(0)); + return; + } + setSelectedOption(option); }; diff --git a/src/pages/settings/Subscription/SubscriptionSize/SubscriptionSizePage.tsx b/src/pages/settings/Subscription/SubscriptionSize/SubscriptionSizePage.tsx index 4589d04a774d..ca5e7e2b7984 100644 --- a/src/pages/settings/Subscription/SubscriptionSize/SubscriptionSizePage.tsx +++ b/src/pages/settings/Subscription/SubscriptionSize/SubscriptionSizePage.tsx @@ -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'; @@ -6,21 +7,24 @@ 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 ONYXKEYS from '@src/ONYXKEYS'; +import type SCREENS from '@src/SCREENS'; import Confirmation from './substeps/Confirmation'; import Size from './substeps/Size'; const bodyContent: Array> = [Size, Confirmation]; -function SubscriptionSizePage() { +type SubscriptionSizePageProps = StackScreenProps; + +function SubscriptionSizePage({route}: SubscriptionSizePageProps) { 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 CAN_CHANGE_SUBSCRIPTION_SIZE = !!route.params.canChangeSize; + const startFrom = CAN_CHANGE_SUBSCRIPTION_SIZE ? 0 : 1; const onFinished = () => { - if (CAN_DOWNGRADE) { + if (CAN_CHANGE_SUBSCRIPTION_SIZE) { // 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); @@ -30,7 +34,7 @@ function SubscriptionSizePage() { 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) { @@ -53,7 +57,7 @@ function SubscriptionSizePage() { onBackButtonPress={onBackButtonPress} /> diff --git a/src/pages/settings/Subscription/SubscriptionSize/substeps/Confirmation.tsx b/src/pages/settings/Subscription/SubscriptionSize/substeps/Confirmation.tsx index b54f21726b6f..549c5ca89c42 100644 --- a/src/pages/settings/Subscription/SubscriptionSize/substeps/Confirmation.tsx +++ b/src/pages/settings/Subscription/SubscriptionSize/substeps/Confirmation.tsx @@ -1,3 +1,4 @@ +import {format} from 'date-fns'; import React from 'react'; import {View} from 'react-native'; import {useOnyx} from 'react-native-onyx'; @@ -10,27 +11,27 @@ 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 CONST from '@src/CONST'; 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 CAN_CHANGE_SUBSCRIPTION_SIZE = ((account?.canDowngrade ?? false) || (Number(subscriptionSizeFormDraft) ?? 0) >= (privateSubscription?.userCount ?? 0)) && isEditing; + const SUBSCRIPTION_UNTIL = privateSubscription?.endDate ? format(new Date(privateSubscription?.endDate), CONST.DATE.MONTH_DAY_YEAR_ABBR_FORMAT) : ''; return ( - {CAN_DOWNGRADE ? ( + {CAN_CHANGE_SUBSCRIPTION_SIZE ? ( <> {translate('subscription.subscriptionSize.confirmDetails')} {translate('subscription.subscriptionSize.youCantDowngrade')} {translate('subscription.subscriptionSize.youAlreadyCommitted', { - size: subscriptionSizeFormDraft ? subscriptionSizeFormDraft[INPUT_IDS.SUBSCRIPTION_SIZE] : 0, + size: privateSubscription?.userCount ?? 0, date: SUBSCRIPTION_UNTIL, })} @@ -61,7 +62,7 @@ function Confirmation({onNext}: ConfirmationProps) { success large onPress={onNext} - text={translate(CAN_DOWNGRADE ? 'common.save' : 'common.close')} + text={translate(CAN_CHANGE_SUBSCRIPTION_SIZE ? 'common.save' : 'common.close')} /> diff --git a/src/pages/settings/Subscription/SubscriptionSize/substeps/Size.tsx b/src/pages/settings/Subscription/SubscriptionSize/substeps/Size.tsx index 75e0add6dd5e..e9d265bf974b 100644 --- a/src/pages/settings/Subscription/SubscriptionSize/substeps/Size.tsx +++ b/src/pages/settings/Subscription/SubscriptionSize/substeps/Size.tsx @@ -1,5 +1,6 @@ import React from 'react'; import {View} from 'react-native'; +import {useOnyx} from 'react-native-onyx'; import FormProvider from '@components/Form/FormProvider'; import InputWrapper from '@components/Form/InputWrapper'; import Text from '@components/Text'; @@ -17,10 +18,10 @@ type SizeProps = SubStepProps; function Size({onNext}: SizeProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); + const [privateSubscription] = useOnyx(ONYXKEYS.NVP_PRIVATE_SUBSCRIPTION); const defaultValues = { - // TODO this is temporary and default value will be replaced in next phase once data in ONYX is ready - [INPUT_IDS.SUBSCRIPTION_SIZE]: '0', + [INPUT_IDS.SUBSCRIPTION_SIZE]: `${privateSubscription?.userCount ?? 0}`, }; return ( diff --git a/src/types/onyx/Account.ts b/src/types/onyx/Account.ts index 46eac1e94aa4..cb7c9de2c8ab 100644 --- a/src/types/onyx/Account.ts +++ b/src/types/onyx/Account.ts @@ -69,6 +69,9 @@ type Account = { /** Indicates whether the user is a client of an approved accountant */ isApprovedAccountantClient?: boolean; + + /** Indicates whether the user can downgrade current subscription plan */ + canDowngrade?: boolean; }; export default Account; From ef78c2afe790dcbcbe9e4fdaf9d0c400370d8d09 Mon Sep 17 00:00:00 2001 From: Michal Muzyk Date: Tue, 11 Jun 2024 13:13:17 +0200 Subject: [PATCH 02/10] feat: draft --- .../UpdateSubscriptionSizeParams.ts | 5 ++ src/libs/API/parameters/index.ts | 1 + src/libs/API/types.ts | 2 + src/libs/actions/Subscription.ts | 61 +++++++++++++++++-- .../SubscriptionDetails/index.tsx | 18 ++++-- .../SubscriptionSize/SubscriptionSizePage.tsx | 14 ++--- .../substeps/Confirmation.tsx | 25 +++++--- src/types/onyx/PrivateSubscription.ts | 8 ++- 8 files changed, 104 insertions(+), 30 deletions(-) create mode 100644 src/libs/API/parameters/UpdateSubscriptionSizeParams.ts diff --git a/src/libs/API/parameters/UpdateSubscriptionSizeParams.ts b/src/libs/API/parameters/UpdateSubscriptionSizeParams.ts new file mode 100644 index 000000000000..17382a7265e6 --- /dev/null +++ b/src/libs/API/parameters/UpdateSubscriptionSizeParams.ts @@ -0,0 +1,5 @@ +type UpdateSubscriptionSizeParams = { + userCount: number; +}; + +export default UpdateSubscriptionSizeParams; diff --git a/src/libs/API/parameters/index.ts b/src/libs/API/parameters/index.ts index b853f134b315..b365b3232902 100644 --- a/src/libs/API/parameters/index.ts +++ b/src/libs/API/parameters/index.ts @@ -225,3 +225,4 @@ export type {default as SendInvoiceParams} from './SendInvoiceParams'; export type {default as PayInvoiceParams} from './PayInvoiceParams'; export type {default as MarkAsCashParams} from './MarkAsCashParams'; export type {default as SignUpUserParams} from './SignUpUserParams'; +export type {default as UpdateSubscriptionSizeParams} from './UpdateSubscriptionSizeParams'; diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts index 6b34e04e1937..066b37ee50d6 100644 --- a/src/libs/API/types.ts +++ b/src/libs/API/types.ts @@ -222,6 +222,7 @@ const WRITE_COMMANDS = { PAY_INVOICE: 'PayInvoice', MARK_AS_CASH: 'MarkAsCash', SIGN_UP_USER: 'SignUpUser', + UPDATE_SUBSCRIPTION_SIZE: 'UpdateSubscriptionSize', } as const; type WriteCommand = ValueOf; @@ -444,6 +445,7 @@ type WriteCommandParameters = { [WRITE_COMMANDS.PAY_INVOICE]: Parameters.PayInvoiceParams; [WRITE_COMMANDS.MARK_AS_CASH]: Parameters.MarkAsCashParams; [WRITE_COMMANDS.SIGN_UP_USER]: Parameters.SignUpUserParams; + [WRITE_COMMANDS.UPDATE_SUBSCRIPTION_SIZE]: Parameters.UpdateSubscriptionSizeParams; }; const READ_COMMANDS = { diff --git a/src/libs/actions/Subscription.ts b/src/libs/actions/Subscription.ts index c7732575aaa6..96746419ea81 100644 --- a/src/libs/actions/Subscription.ts +++ b/src/libs/actions/Subscription.ts @@ -1,5 +1,10 @@ +import Onyx from 'react-native-onyx'; import * as API from '@libs/API'; -import {READ_COMMANDS} from '@libs/API/types'; +import {READ_COMMANDS, WRITE_COMMANDS} from '@libs/API/types'; +import * as ErrorUtils from '@libs/ErrorUtils'; +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 @@ -8,7 +13,53 @@ function openSubscriptionPage() { API.read(READ_COMMANDS.OPEN_SUBSCRIPTION_PAGE, null); } -export { - // eslint-disable-next-line import/prefer-default-export - openSubscriptionPage, -}; +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, + pendingFields: { + userCount: null, + }, + }, + }, + ], + failureData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.NVP_PRIVATE_SUBSCRIPTION, + value: { + userCount: currentSubscriptionSize, + errorFields: { + userCount: ErrorUtils.getMicroSecondOnyxError('common.genericErrorMessage'), + }, + pendingFields: { + userCount: null, + }, + }, + }, + ], + }; + + API.write(WRITE_COMMANDS.UPDATE_SUBSCRIPTION_SIZE, {userCount: newSubscriptionSize}, onyxData); +} + +export {openSubscriptionPage, updateSubscriptionSize}; diff --git a/src/pages/settings/Subscription/SubscriptionDetails/index.tsx b/src/pages/settings/Subscription/SubscriptionDetails/index.tsx index 274a33ad05fa..e420c8a9ff94 100644 --- a/src/pages/settings/Subscription/SubscriptionDetails/index.tsx +++ b/src/pages/settings/Subscription/SubscriptionDetails/index.tsx @@ -5,6 +5,7 @@ import type {ValueOf} from 'type-fest'; import Icon from '@components/Icon'; import * as Illustrations from '@components/Icon/Illustrations'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; +import OfflineWithFeedback from '@components/OfflineWithFeedback'; import type {OptionsPickerItem} from '@components/OptionsPicker'; import OptionsPicker from '@components/OptionsPicker'; import Section from '@components/Section'; @@ -58,12 +59,17 @@ function SubscriptionDetails() { if (privateSubscription?.type === CONST.SUBSCRIPTION.TYPE.ANNUAL) { subscriptionSizeSection = privateSubscription?.userCount ? ( - + + + ) : ( <> > = [Size, Confirmatio type SubscriptionSizePageProps = StackScreenProps; function SubscriptionSizePage({route}: SubscriptionSizePageProps) { + const [privateSubscription] = useOnyx(ONYXKEYS.NVP_PRIVATE_SUBSCRIPTION); const [subscriptionSizeFormDraft] = useOnyx(ONYXKEYS.FORMS.SUBSCRIPTION_SIZE_FORM_DRAFT); const {translate} = useLocalize(); - const CAN_CHANGE_SUBSCRIPTION_SIZE = !!route.params.canChangeSize; + const CAN_CHANGE_SUBSCRIPTION_SIZE = !!(route.params?.canChangeSize ?? 1); const startFrom = CAN_CHANGE_SUBSCRIPTION_SIZE ? 0 : 1; const onFinished = () => { - if (CAN_CHANGE_SUBSCRIPTION_SIZE) { - // 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(); }; @@ -51,6 +48,7 @@ function SubscriptionSizePage({route}: SubscriptionSizePageProps) { includeSafeAreaPaddingBottom={false} shouldEnablePickerAvoiding={false} shouldEnableMaxHeight + shouldShowOfflineIndicatorInWideScreen > )} -