From 84ba497857db1afbbbefa556fc44cf28d69027d9 Mon Sep 17 00:00:00 2001 From: Antonella Sgarlatta Date: Mon, 6 Sep 2021 03:57:33 -0300 Subject: [PATCH 1/9] feat: subscription info in preferences --- .../preferences/panes/AccountPreferences.tsx | 3 +- .../panes/account/Subscription.tsx | 187 ++++++++++++++++++ .../preferences/panes/account/index.ts | 1 + app/assets/stylesheets/_sn.scss | 4 + 4 files changed, 194 insertions(+), 1 deletion(-) create mode 100644 app/assets/javascripts/preferences/panes/account/Subscription.tsx diff --git a/app/assets/javascripts/preferences/panes/AccountPreferences.tsx b/app/assets/javascripts/preferences/panes/AccountPreferences.tsx index 6b034d7c6..8dd126cba 100644 --- a/app/assets/javascripts/preferences/panes/AccountPreferences.tsx +++ b/app/assets/javascripts/preferences/panes/AccountPreferences.tsx @@ -1,4 +1,4 @@ -import { Sync } from '@/preferences/panes/account'; +import { Sync, Subscription } from '@/preferences/panes/account'; import { PreferencesPane } from '@/preferences/components'; import { observer } from 'mobx-react-lite'; import { WebApplication } from '@/ui_models/application'; @@ -7,6 +7,7 @@ export const AccountPreferences = observer(({application}: {application: WebAppl return ( + ); }); diff --git a/app/assets/javascripts/preferences/panes/account/Subscription.tsx b/app/assets/javascripts/preferences/panes/account/Subscription.tsx new file mode 100644 index 000000000..0a3c79bd4 --- /dev/null +++ b/app/assets/javascripts/preferences/panes/account/Subscription.tsx @@ -0,0 +1,187 @@ +import { + PreferencesGroup, + PreferencesSegment, + Text, + Title, +} from '@/preferences/components'; +import { Button } from '@/components/Button'; +import { observer } from '@node_modules/mobx-react-lite'; +import { WebApplication } from '@/ui_models/application'; +import { useEffect, useState } from 'preact/hooks'; +import { GetSubscriptionResponse } from '@standardnotes/snjs/dist/@types/services/api/responses'; + +type Props = { + application: WebApplication; +}; + +enum PlanName { + CorePlan = 'CORE_PLAN', + PlusPlan = 'PLUS_PLAN', + ProPlan = 'PRO_PLAN', +} + +type Subscription = { + cancelled: boolean; + planName: PlanName; + endsAt: number; +}; + +type SubscriptionInformationProps = { + subscription?: Subscription; +}; + +type ValidSubscriptionProps = { + subscription: Subscription; +}; + +const mapPlanNameToString = (planName: PlanName) => { + switch (planName) { + case 'CORE_PLAN': + return 'Core'; + case 'PLUS_PLAN': + return 'Plus'; + case 'PRO_PLAN': + return 'Pro'; + default: + return ''; + } +}; + +const NoSubscription = () => ( + <> + You don't have a Standard Notes subscription yet. +
+
+ +); + +const ActiveSubscription = ({ subscription }: ValidSubscriptionProps) => ( + <> + + Your{' '} + + Standard Notes {mapPlanNameToString(subscription.planName)} + {' '} + subscription will be{' '} + + renewed on {new Date(subscription.endsAt).toLocaleString()} + + . + +
+
+ +); + +const CancelledSubscription = ({ subscription }: ValidSubscriptionProps) => ( + <> + + Your{' '} + + Standard Notes {mapPlanNameToString(subscription.planName)} + {' '} + subscription has been{' '} + + canceled but will remain valid until{' '} + {new Date(subscription.endsAt).toLocaleString()} + + . You may resubscribe below if you wish. + +
+
+ +); + +const SubscriptionInformation = ({ + subscription, +}: SubscriptionInformationProps) => { + const now = new Date().getTime(); + if (subscription && subscription.endsAt > now) { + return subscription.cancelled ? ( + + ) : ( + + ); + } + return ; +}; + +const Subscription = observer(({ application }: Props) => { + const [subscription, setSubscription] = + useState(undefined); + + useEffect(() => { + const getSubscription = async () => { + const result = await application.getUserSubscription(); + if (!result.error && result.data) { + const data = (result as GetSubscriptionResponse).data; + const subscription = data!.subscription; + setSubscription(subscription); + } + }; + getSubscription(); + }, [application]); + + return ( + + +
+
+ Subscription + +
+
+
+
+ ); +}); + +export default Subscription; diff --git a/app/assets/javascripts/preferences/panes/account/index.ts b/app/assets/javascripts/preferences/panes/account/index.ts index 2225b5881..d30c31420 100644 --- a/app/assets/javascripts/preferences/panes/account/index.ts +++ b/app/assets/javascripts/preferences/panes/account/index.ts @@ -1 +1,2 @@ export { default as Sync } from './Sync'; +export { default as Subscription } from './Subscription'; diff --git a/app/assets/stylesheets/_sn.scss b/app/assets/stylesheets/_sn.scss index 32baa1cb4..a8a4bed8b 100644 --- a/app/assets/stylesheets/_sn.scss +++ b/app/assets/stylesheets/_sn.scss @@ -149,3 +149,7 @@ .sn-title { @extend .font-bold; } + +.mr-3 { + margin-right: 0.75rem; +} From 1b87df18bc42b0f4491d881465ffdead96de4762 Mon Sep 17 00:00:00 2001 From: Antonella Sgarlatta Date: Tue, 7 Sep 2021 11:19:51 -0300 Subject: [PATCH 2/9] feat: get plan name from subscriptions endpoint --- .../panes/account/Subscription.tsx | 103 ++++++++++++------ 1 file changed, 69 insertions(+), 34 deletions(-) diff --git a/app/assets/javascripts/preferences/panes/account/Subscription.tsx b/app/assets/javascripts/preferences/panes/account/Subscription.tsx index 0a3c79bd4..712964a17 100644 --- a/app/assets/javascripts/preferences/panes/account/Subscription.tsx +++ b/app/assets/javascripts/preferences/panes/account/Subscription.tsx @@ -8,43 +8,35 @@ import { Button } from '@/components/Button'; import { observer } from '@node_modules/mobx-react-lite'; import { WebApplication } from '@/ui_models/application'; import { useEffect, useState } from 'preact/hooks'; -import { GetSubscriptionResponse } from '@standardnotes/snjs/dist/@types/services/api/responses'; +import { + GetSubscriptionResponse, + GetSubscriptionsResponse, +} from '@standardnotes/snjs/dist/@types/services/api/responses'; type Props = { application: WebApplication; }; -enum PlanName { - CorePlan = 'CORE_PLAN', - PlusPlan = 'PLUS_PLAN', - ProPlan = 'PRO_PLAN', -} - type Subscription = { + planName: string; cancelled: boolean; - planName: PlanName; endsAt: number; }; +type AvailableSubscriptions = { + [key: string]: { + name: string; + }; +}; + type SubscriptionInformationProps = { subscription?: Subscription; + availableSubscriptions: AvailableSubscriptions; }; type ValidSubscriptionProps = { subscription: Subscription; -}; - -const mapPlanNameToString = (planName: PlanName) => { - switch (planName) { - case 'CORE_PLAN': - return 'Core'; - case 'PLUS_PLAN': - return 'Plus'; - case 'PRO_PLAN': - return 'Pro'; - default: - return ''; - } + availableSubscriptions: AvailableSubscriptions; }; const NoSubscription = () => ( @@ -67,12 +59,15 @@ const NoSubscription = () => ( ); -const ActiveSubscription = ({ subscription }: ValidSubscriptionProps) => ( +const ActiveSubscription = ({ + subscription, + availableSubscriptions, +}: ValidSubscriptionProps) => ( <> Your{' '} - Standard Notes {mapPlanNameToString(subscription.planName)} + Standard Notes {availableSubscriptions[subscription.planName]} {' '} subscription will be{' '} @@ -103,12 +98,15 @@ const ActiveSubscription = ({ subscription }: ValidSubscriptionProps) => ( ); -const CancelledSubscription = ({ subscription }: ValidSubscriptionProps) => ( +const CancelledSubscription = ({ + subscription, + availableSubscriptions, +}: ValidSubscriptionProps) => ( <> Your{' '} - Standard Notes {mapPlanNameToString(subscription.planName)} + Standard Notes {availableSubscriptions[subscription.planName]} {' '} subscription has been{' '} @@ -142,13 +140,20 @@ const CancelledSubscription = ({ subscription }: ValidSubscriptionProps) => ( const SubscriptionInformation = ({ subscription, + availableSubscriptions, }: SubscriptionInformationProps) => { const now = new Date().getTime(); if (subscription && subscription.endsAt > now) { return subscription.cancelled ? ( - + ) : ( - + ); } return ; @@ -157,16 +162,39 @@ const SubscriptionInformation = ({ const Subscription = observer(({ application }: Props) => { const [subscription, setSubscription] = useState(undefined); + const [availableSubscriptions, setAvailableSubscriptions] = + useState({}); + const [error, setError] = useState(false); useEffect(() => { - const getSubscription = async () => { - const result = await application.getUserSubscription(); - if (!result.error && result.data) { - const data = (result as GetSubscriptionResponse).data; - const subscription = data!.subscription; - setSubscription(subscription); + const getSubscriptions = async () => { + try { + const result = await application.getSubscriptions(); + if (result.data) { + const data = (result as GetSubscriptionsResponse).data; + setAvailableSubscriptions(data!); + } else { + setError(true); + } + } catch (e) { + setError(true); } }; + const getSubscription = async () => { + try { + const result = await application.getUserSubscription(); + if (!result.error && result.data) { + const data = (result as GetSubscriptionResponse).data; + const subscription = data!.subscription; + setSubscription(subscription); + } else { + setError(true); + } + } catch (e) { + setError(true); + } + }; + getSubscriptions(); getSubscription(); }, [application]); @@ -176,7 +204,14 @@ const Subscription = observer(({ application }: Props) => {
Subscription - + {error ? ( + 'No subscription information available.' + ) : ( + + )}
From 28dab13fb19631bcf8946d8fc40306b086fff676 Mon Sep 17 00:00:00 2001 From: Antonella Sgarlatta Date: Tue, 7 Sep 2021 12:04:25 -0300 Subject: [PATCH 3/9] refactor: use mobx for state and separate files for components --- .../preferences/panes/AccountPreferences.tsx | 4 +- .../panes/account/Subscription.tsx | 222 ------------------ .../preferences/panes/account/index.ts | 2 +- .../subscription/ActiveSubscription.tsx | 49 ++++ .../subscription/CancelledSubscription.tsx | 50 ++++ .../account/subscription/NoSubscription.tsx | 23 ++ .../account/subscription/Subscription.tsx | 92 ++++++++ .../subscription/SubscriptionWrapper.tsx | 20 ++ .../subscription/subscription_state.tsx | 51 ++++ 9 files changed, 288 insertions(+), 225 deletions(-) delete mode 100644 app/assets/javascripts/preferences/panes/account/Subscription.tsx create mode 100644 app/assets/javascripts/preferences/panes/account/subscription/ActiveSubscription.tsx create mode 100644 app/assets/javascripts/preferences/panes/account/subscription/CancelledSubscription.tsx create mode 100644 app/assets/javascripts/preferences/panes/account/subscription/NoSubscription.tsx create mode 100644 app/assets/javascripts/preferences/panes/account/subscription/Subscription.tsx create mode 100644 app/assets/javascripts/preferences/panes/account/subscription/SubscriptionWrapper.tsx create mode 100644 app/assets/javascripts/preferences/panes/account/subscription/subscription_state.tsx diff --git a/app/assets/javascripts/preferences/panes/AccountPreferences.tsx b/app/assets/javascripts/preferences/panes/AccountPreferences.tsx index 8dd126cba..4d74e7a89 100644 --- a/app/assets/javascripts/preferences/panes/AccountPreferences.tsx +++ b/app/assets/javascripts/preferences/panes/AccountPreferences.tsx @@ -1,4 +1,4 @@ -import { Sync, Subscription } from '@/preferences/panes/account'; +import { Sync, SubscriptionWrapper } from '@/preferences/panes/account'; import { PreferencesPane } from '@/preferences/components'; import { observer } from 'mobx-react-lite'; import { WebApplication } from '@/ui_models/application'; @@ -7,7 +7,7 @@ export const AccountPreferences = observer(({application}: {application: WebAppl return ( - + ); }); diff --git a/app/assets/javascripts/preferences/panes/account/Subscription.tsx b/app/assets/javascripts/preferences/panes/account/Subscription.tsx deleted file mode 100644 index 712964a17..000000000 --- a/app/assets/javascripts/preferences/panes/account/Subscription.tsx +++ /dev/null @@ -1,222 +0,0 @@ -import { - PreferencesGroup, - PreferencesSegment, - Text, - Title, -} from '@/preferences/components'; -import { Button } from '@/components/Button'; -import { observer } from '@node_modules/mobx-react-lite'; -import { WebApplication } from '@/ui_models/application'; -import { useEffect, useState } from 'preact/hooks'; -import { - GetSubscriptionResponse, - GetSubscriptionsResponse, -} from '@standardnotes/snjs/dist/@types/services/api/responses'; - -type Props = { - application: WebApplication; -}; - -type Subscription = { - planName: string; - cancelled: boolean; - endsAt: number; -}; - -type AvailableSubscriptions = { - [key: string]: { - name: string; - }; -}; - -type SubscriptionInformationProps = { - subscription?: Subscription; - availableSubscriptions: AvailableSubscriptions; -}; - -type ValidSubscriptionProps = { - subscription: Subscription; - availableSubscriptions: AvailableSubscriptions; -}; - -const NoSubscription = () => ( - <> - You don't have a Standard Notes subscription yet. -
-
- -); - -const ActiveSubscription = ({ - subscription, - availableSubscriptions, -}: ValidSubscriptionProps) => ( - <> - - Your{' '} - - Standard Notes {availableSubscriptions[subscription.planName]} - {' '} - subscription will be{' '} - - renewed on {new Date(subscription.endsAt).toLocaleString()} - - . - -
-
- -); - -const CancelledSubscription = ({ - subscription, - availableSubscriptions, -}: ValidSubscriptionProps) => ( - <> - - Your{' '} - - Standard Notes {availableSubscriptions[subscription.planName]} - {' '} - subscription has been{' '} - - canceled but will remain valid until{' '} - {new Date(subscription.endsAt).toLocaleString()} - - . You may resubscribe below if you wish. - -
-
- -); - -const SubscriptionInformation = ({ - subscription, - availableSubscriptions, -}: SubscriptionInformationProps) => { - const now = new Date().getTime(); - if (subscription && subscription.endsAt > now) { - return subscription.cancelled ? ( - - ) : ( - - ); - } - return ; -}; - -const Subscription = observer(({ application }: Props) => { - const [subscription, setSubscription] = - useState(undefined); - const [availableSubscriptions, setAvailableSubscriptions] = - useState({}); - const [error, setError] = useState(false); - - useEffect(() => { - const getSubscriptions = async () => { - try { - const result = await application.getSubscriptions(); - if (result.data) { - const data = (result as GetSubscriptionsResponse).data; - setAvailableSubscriptions(data!); - } else { - setError(true); - } - } catch (e) { - setError(true); - } - }; - const getSubscription = async () => { - try { - const result = await application.getUserSubscription(); - if (!result.error && result.data) { - const data = (result as GetSubscriptionResponse).data; - const subscription = data!.subscription; - setSubscription(subscription); - } else { - setError(true); - } - } catch (e) { - setError(true); - } - }; - getSubscriptions(); - getSubscription(); - }, [application]); - - return ( - - -
-
- Subscription - {error ? ( - 'No subscription information available.' - ) : ( - - )} -
-
-
-
- ); -}); - -export default Subscription; diff --git a/app/assets/javascripts/preferences/panes/account/index.ts b/app/assets/javascripts/preferences/panes/account/index.ts index d30c31420..063947150 100644 --- a/app/assets/javascripts/preferences/panes/account/index.ts +++ b/app/assets/javascripts/preferences/panes/account/index.ts @@ -1,2 +1,2 @@ export { default as Sync } from './Sync'; -export { default as Subscription } from './Subscription'; +export { SubscriptionWrapper } from './subscription/SubscriptionWrapper'; diff --git a/app/assets/javascripts/preferences/panes/account/subscription/ActiveSubscription.tsx b/app/assets/javascripts/preferences/panes/account/subscription/ActiveSubscription.tsx new file mode 100644 index 000000000..eb5d984ad --- /dev/null +++ b/app/assets/javascripts/preferences/panes/account/subscription/ActiveSubscription.tsx @@ -0,0 +1,49 @@ +import { observer } from 'mobx-react-lite'; +import { SubscriptionState } from './subscription_state'; +import { Text } from '@/preferences/components'; +import { Button } from '@/components/Button'; + +type Props = { + subscriptionState: SubscriptionState; +}; + +export const ActiveSubscription = observer( + ({ subscriptionState }: Props) => { + const { userSubscription, userSubscriptionName } = subscriptionState; + return ( + <> + + Your{' '} + + Standard Notes{userSubscriptionName ? " " : ""}{userSubscriptionName} + {' '} + subscription will be{' '} + + renewed on {new Date(userSubscription!.endsAt).toLocaleString()} + + . + +
+
+ + ); + } +); diff --git a/app/assets/javascripts/preferences/panes/account/subscription/CancelledSubscription.tsx b/app/assets/javascripts/preferences/panes/account/subscription/CancelledSubscription.tsx new file mode 100644 index 000000000..84cf6639c --- /dev/null +++ b/app/assets/javascripts/preferences/panes/account/subscription/CancelledSubscription.tsx @@ -0,0 +1,50 @@ +import { observer } from 'mobx-react-lite'; +import { SubscriptionState } from './subscription_state'; +import { Text } from '@/preferences/components'; +import { Button } from '@/components/Button'; + +type Props = { + subscriptionState: SubscriptionState; +}; + +export const CancelledSubscription = observer( + ({ subscriptionState }: Props) => { + const { userSubscription, userSubscriptionName } = subscriptionState; + return ( + <> + + Your{' '} + + Standard NotesStandard Notes{userSubscriptionName ? " " : ""}{userSubscriptionName} + {' '} + subscription has been{' '} + + canceled but will remain valid until{' '} + {new Date(userSubscription!.endsAt).toLocaleString()} + + . You may resubscribe below if you wish. + +
+
+ + ); + } +); diff --git a/app/assets/javascripts/preferences/panes/account/subscription/NoSubscription.tsx b/app/assets/javascripts/preferences/panes/account/subscription/NoSubscription.tsx new file mode 100644 index 000000000..5042ea992 --- /dev/null +++ b/app/assets/javascripts/preferences/panes/account/subscription/NoSubscription.tsx @@ -0,0 +1,23 @@ +import { FunctionalComponent } from "preact"; +import { Text } from '@/preferences/components'; +import { Button } from '@/components/Button'; + +export const NoSubscription: FunctionalComponent = () => ( + <> + You don't have a Standard Notes subscription yet. +
+
+ +); diff --git a/app/assets/javascripts/preferences/panes/account/subscription/Subscription.tsx b/app/assets/javascripts/preferences/panes/account/subscription/Subscription.tsx new file mode 100644 index 000000000..3ced9309e --- /dev/null +++ b/app/assets/javascripts/preferences/panes/account/subscription/Subscription.tsx @@ -0,0 +1,92 @@ +import { + PreferencesGroup, + PreferencesSegment, + Title, +} from '@/preferences/components'; +import { observer } from '@node_modules/mobx-react-lite'; +import { WebApplication } from '@/ui_models/application'; +import { useEffect, useState } from 'preact/hooks'; +import { + GetSubscriptionResponse, + GetSubscriptionsResponse, +} from '@standardnotes/snjs/dist/@types/services/api/responses'; +import { SubscriptionState } from './subscription_state'; +import { CancelledSubscription } from './CancelledSubscription'; +import { ActiveSubscription } from './ActiveSubscription'; +import { NoSubscription } from './NoSubscription'; + +type Props = { + application: WebApplication; + subscriptionState: SubscriptionState; +}; + +type SubscriptionInformationProps = { + subscriptionState: SubscriptionState; +}; + +const SubscriptionInformation = ({ + subscriptionState, +}: SubscriptionInformationProps) => { + const now = new Date().getTime(); + const { userSubscription } = subscriptionState; + + if (userSubscription && userSubscription.endsAt > now) { + return userSubscription.cancelled ? ( + + ) : ( + + ); + } + return ; +}; + +export const Subscription = observer(({ application, subscriptionState }: Props) => { + const [error, setError] = useState(false); + + useEffect(() => { + const getSubscriptions = async () => { + try { + const result = await application.getSubscriptions(); + if (result.data) { + const data = (result as GetSubscriptionsResponse).data; + subscriptionState.setAvailableSubscriptions(data!); + } + } catch (e) { + // Error in this call will only prevent the plan name from showing + } + }; + const getSubscription = async () => { + try { + const result = await application.getUserSubscription(); + if (!result.error && result.data) { + const data = (result as GetSubscriptionResponse).data; + const subscription = data!.subscription; + subscriptionState.setUserSubscription(subscription); + } else { + setError(true); + } + } catch (e) { + setError(true); + } + }; + getSubscriptions(); + getSubscription(); + }, [application, subscriptionState]); + + return ( + + +
+
+ Subscription + {error ? ( + 'No subscription information available.' + ) : ( + + )} +
+
+
+
+ ); +}); diff --git a/app/assets/javascripts/preferences/panes/account/subscription/SubscriptionWrapper.tsx b/app/assets/javascripts/preferences/panes/account/subscription/SubscriptionWrapper.tsx new file mode 100644 index 000000000..9a1c4033f --- /dev/null +++ b/app/assets/javascripts/preferences/panes/account/subscription/SubscriptionWrapper.tsx @@ -0,0 +1,20 @@ +import { WebApplication } from '@/ui_models/application'; +import { FunctionalComponent } from 'preact'; +import { Subscription } from './Subscription'; +import { SubscriptionState } from './subscription_state'; + +type Props = { + application: WebApplication; +}; + +export const SubscriptionWrapper: FunctionalComponent = ({ + application, +}) => { + const subscriptionState = new SubscriptionState(); + return ( + + ); +}; diff --git a/app/assets/javascripts/preferences/panes/account/subscription/subscription_state.tsx b/app/assets/javascripts/preferences/panes/account/subscription/subscription_state.tsx new file mode 100644 index 000000000..aaa2bab72 --- /dev/null +++ b/app/assets/javascripts/preferences/panes/account/subscription/subscription_state.tsx @@ -0,0 +1,51 @@ +import { action, computed, makeObservable, observable } from 'mobx'; + +type Subscription = { + planName: string; + cancelled: boolean; + endsAt: number; +}; + +type AvailableSubscriptions = { + [key: string]: { + name: string; + }; +}; + +export class SubscriptionState { + userSubscription: Subscription | undefined; + availableSubscriptions: AvailableSubscriptions | undefined; + + constructor() { + makeObservable(this, { + userSubscription: observable, + availableSubscriptions: observable, + + userSubscriptionName: computed, + + setUserSubscription: action, + setAvailableSubscriptions: action, + }); + } + + get userSubscriptionName(): string { + if ( + this.availableSubscriptions && + this.userSubscription && + this.availableSubscriptions[this.userSubscription.planName] + ) { + return this.availableSubscriptions[this.userSubscription.planName].name; + } + return ''; + } + + public setUserSubscription(subscription: Subscription): void { + this.userSubscription = subscription; + } + + public setAvailableSubscriptions( + subscriptions: AvailableSubscriptions + ): void { + this.availableSubscriptions = subscriptions; + } +} From 99ef854ae8b36a993583b7e29ffa6ca66a57d3c6 Mon Sep 17 00:00:00 2001 From: Antonella Sgarlatta Date: Tue, 7 Sep 2021 12:06:25 -0300 Subject: [PATCH 4/9] fix: set mobx store as local state --- .../panes/account/subscription/SubscriptionWrapper.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/preferences/panes/account/subscription/SubscriptionWrapper.tsx b/app/assets/javascripts/preferences/panes/account/subscription/SubscriptionWrapper.tsx index 9a1c4033f..9a19112e6 100644 --- a/app/assets/javascripts/preferences/panes/account/subscription/SubscriptionWrapper.tsx +++ b/app/assets/javascripts/preferences/panes/account/subscription/SubscriptionWrapper.tsx @@ -1,5 +1,6 @@ import { WebApplication } from '@/ui_models/application'; import { FunctionalComponent } from 'preact'; +import { useState } from 'preact/hooks'; import { Subscription } from './Subscription'; import { SubscriptionState } from './subscription_state'; @@ -10,7 +11,7 @@ type Props = { export const SubscriptionWrapper: FunctionalComponent = ({ application, }) => { - const subscriptionState = new SubscriptionState(); + const [subscriptionState] = useState(() => new SubscriptionState()); return ( Date: Tue, 7 Sep 2021 13:14:12 -0300 Subject: [PATCH 5/9] fix: fix state and add loading state --- .../subscription/CancelledSubscription.tsx | 2 +- .../account/subscription/Subscription.tsx | 78 +++++++++++-------- .../subscription/subscription_state.tsx | 6 +- 3 files changed, 52 insertions(+), 34 deletions(-) diff --git a/app/assets/javascripts/preferences/panes/account/subscription/CancelledSubscription.tsx b/app/assets/javascripts/preferences/panes/account/subscription/CancelledSubscription.tsx index 84cf6639c..d3742e65c 100644 --- a/app/assets/javascripts/preferences/panes/account/subscription/CancelledSubscription.tsx +++ b/app/assets/javascripts/preferences/panes/account/subscription/CancelledSubscription.tsx @@ -15,7 +15,7 @@ export const CancelledSubscription = observer( Your{' '} - Standard NotesStandard Notes{userSubscriptionName ? " " : ""}{userSubscriptionName} + Standard Notes{userSubscriptionName ? " " : ""}{userSubscriptionName} {' '} subscription has been{' '} diff --git a/app/assets/javascripts/preferences/panes/account/subscription/Subscription.tsx b/app/assets/javascripts/preferences/panes/account/subscription/Subscription.tsx index 3ced9309e..df5b3f9fc 100644 --- a/app/assets/javascripts/preferences/panes/account/subscription/Subscription.tsx +++ b/app/assets/javascripts/preferences/panes/account/subscription/Subscription.tsx @@ -5,7 +5,7 @@ import { } from '@/preferences/components'; import { observer } from '@node_modules/mobx-react-lite'; import { WebApplication } from '@/ui_models/application'; -import { useEffect, useState } from 'preact/hooks'; +import { useCallback, useEffect, useState } from 'preact/hooks'; import { GetSubscriptionResponse, GetSubscriptionsResponse, @@ -14,6 +14,8 @@ import { SubscriptionState } from './subscription_state'; import { CancelledSubscription } from './CancelledSubscription'; import { ActiveSubscription } from './ActiveSubscription'; import { NoSubscription } from './NoSubscription'; +import { Text } from '@/preferences/components'; +import { FunctionalComponent } from 'preact'; type Props = { application: WebApplication; @@ -24,7 +26,7 @@ type SubscriptionInformationProps = { subscriptionState: SubscriptionState; }; -const SubscriptionInformation = ({ +const SubscriptionInformation = observer(({ subscriptionState, }: SubscriptionInformationProps) => { const now = new Date().getTime(); @@ -38,41 +40,53 @@ const SubscriptionInformation = ({ ); } return ; -}; +}); -export const Subscription = observer(({ application, subscriptionState }: Props) => { +export const Subscription: FunctionalComponent = ({ application, subscriptionState }) => { + const [loading, setLoading] = useState(true); const [error, setError] = useState(false); - useEffect(() => { - const getSubscriptions = async () => { - try { - const result = await application.getSubscriptions(); - if (result.data) { - const data = (result as GetSubscriptionsResponse).data; - subscriptionState.setAvailableSubscriptions(data!); - } - } catch (e) { - // Error in this call will only prevent the plan name from showing + const getSubscriptions = useCallback(async () => { + try { + const result = await application.getSubscriptions(); + if (result.data) { + const data = (result as GetSubscriptionsResponse).data; + subscriptionState.setAvailableSubscriptions(data!); } - }; - const getSubscription = async () => { - try { - const result = await application.getUserSubscription(); - if (!result.error && result.data) { - const data = (result as GetSubscriptionResponse).data; - const subscription = data!.subscription; - subscriptionState.setUserSubscription(subscription); - } else { - setError(true); - } - } catch (e) { + } catch (e) { + // Error in this call will only prevent the plan name from showing + } + }, [application, subscriptionState]); + + const getSubscription = useCallback(async () => { + try { + const result = await application.getUserSubscription(); + if (!result.error && result.data) { + const data = (result as GetSubscriptionResponse).data; + const subscription = data!.subscription; + subscriptionState.setUserSubscription(subscription); + } else { setError(true); } - }; - getSubscriptions(); - getSubscription(); + } catch (e) { + setError(true); + } }, [application, subscriptionState]); + const getSubscriptionInfo = useCallback(async () => { + setLoading(true); + try { + await getSubscription(); + await getSubscriptions(); + } finally { + setLoading(false); + } + }, [getSubscription, getSubscriptions]); + + useEffect(() => { + getSubscriptionInfo(); + }, [getSubscriptionInfo]); + return ( @@ -80,7 +94,9 @@ export const Subscription = observer(({ application, subscriptionState }: Props)
Subscription {error ? ( - 'No subscription information available.' + No subscription information available. + ) : loading ? ( + Loading subscription information... ) : ( )} @@ -89,4 +105,4 @@ export const Subscription = observer(({ application, subscriptionState }: Props) ); -}); +}; diff --git a/app/assets/javascripts/preferences/panes/account/subscription/subscription_state.tsx b/app/assets/javascripts/preferences/panes/account/subscription/subscription_state.tsx index aaa2bab72..f5a9a0a74 100644 --- a/app/assets/javascripts/preferences/panes/account/subscription/subscription_state.tsx +++ b/app/assets/javascripts/preferences/panes/account/subscription/subscription_state.tsx @@ -13,8 +13,8 @@ type AvailableSubscriptions = { }; export class SubscriptionState { - userSubscription: Subscription | undefined; - availableSubscriptions: AvailableSubscriptions | undefined; + userSubscription: Subscription | undefined = undefined; + availableSubscriptions: AvailableSubscriptions | undefined = undefined; constructor() { makeObservable(this, { @@ -40,7 +40,9 @@ export class SubscriptionState { } public setUserSubscription(subscription: Subscription): void { + console.log('set subscription in state', subscription); this.userSubscription = subscription; + console.log(this.userSubscription); } public setAvailableSubscriptions( From dee0c74ebb238a4dd44e746242cfb457f49a3131 Mon Sep 17 00:00:00 2001 From: Antonella Sgarlatta Date: Tue, 7 Sep 2021 14:37:37 -0300 Subject: [PATCH 6/9] Update app/assets/javascripts/preferences/panes/account/subscription/subscription_state.tsx Co-authored-by: Mo Bitar --- .../panes/account/subscription/subscription_state.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/app/assets/javascripts/preferences/panes/account/subscription/subscription_state.tsx b/app/assets/javascripts/preferences/panes/account/subscription/subscription_state.tsx index f5a9a0a74..fa09dfd0b 100644 --- a/app/assets/javascripts/preferences/panes/account/subscription/subscription_state.tsx +++ b/app/assets/javascripts/preferences/panes/account/subscription/subscription_state.tsx @@ -40,7 +40,6 @@ export class SubscriptionState { } public setUserSubscription(subscription: Subscription): void { - console.log('set subscription in state', subscription); this.userSubscription = subscription; console.log(this.userSubscription); } From 2696f5b735db370e0efa10d60a8f2993d04b9510 Mon Sep 17 00:00:00 2001 From: Antonella Sgarlatta Date: Tue, 7 Sep 2021 15:11:27 -0300 Subject: [PATCH 7/9] refactor: merge active and cancelled components into one --- .../subscription/ActiveSubscription.tsx | 49 ----------- .../subscription/CancelledSubscription.tsx | 50 ------------ .../account/subscription/Subscription.tsx | 39 +++------ .../subscription/SubscriptionInformation.tsx | 81 +++++++++++++++++++ .../subscription/subscription_state.tsx | 1 - 5 files changed, 94 insertions(+), 126 deletions(-) delete mode 100644 app/assets/javascripts/preferences/panes/account/subscription/ActiveSubscription.tsx delete mode 100644 app/assets/javascripts/preferences/panes/account/subscription/CancelledSubscription.tsx create mode 100644 app/assets/javascripts/preferences/panes/account/subscription/SubscriptionInformation.tsx diff --git a/app/assets/javascripts/preferences/panes/account/subscription/ActiveSubscription.tsx b/app/assets/javascripts/preferences/panes/account/subscription/ActiveSubscription.tsx deleted file mode 100644 index eb5d984ad..000000000 --- a/app/assets/javascripts/preferences/panes/account/subscription/ActiveSubscription.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import { observer } from 'mobx-react-lite'; -import { SubscriptionState } from './subscription_state'; -import { Text } from '@/preferences/components'; -import { Button } from '@/components/Button'; - -type Props = { - subscriptionState: SubscriptionState; -}; - -export const ActiveSubscription = observer( - ({ subscriptionState }: Props) => { - const { userSubscription, userSubscriptionName } = subscriptionState; - return ( - <> - - Your{' '} - - Standard Notes{userSubscriptionName ? " " : ""}{userSubscriptionName} - {' '} - subscription will be{' '} - - renewed on {new Date(userSubscription!.endsAt).toLocaleString()} - - . - -
-
- - ); - } -); diff --git a/app/assets/javascripts/preferences/panes/account/subscription/CancelledSubscription.tsx b/app/assets/javascripts/preferences/panes/account/subscription/CancelledSubscription.tsx deleted file mode 100644 index d3742e65c..000000000 --- a/app/assets/javascripts/preferences/panes/account/subscription/CancelledSubscription.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import { observer } from 'mobx-react-lite'; -import { SubscriptionState } from './subscription_state'; -import { Text } from '@/preferences/components'; -import { Button } from '@/components/Button'; - -type Props = { - subscriptionState: SubscriptionState; -}; - -export const CancelledSubscription = observer( - ({ subscriptionState }: Props) => { - const { userSubscription, userSubscriptionName } = subscriptionState; - return ( - <> - - Your{' '} - - Standard Notes{userSubscriptionName ? " " : ""}{userSubscriptionName} - {' '} - subscription has been{' '} - - canceled but will remain valid until{' '} - {new Date(userSubscription!.endsAt).toLocaleString()} - - . You may resubscribe below if you wish. - -
-
- - ); - } -); diff --git a/app/assets/javascripts/preferences/panes/account/subscription/Subscription.tsx b/app/assets/javascripts/preferences/panes/account/subscription/Subscription.tsx index df5b3f9fc..33dff200b 100644 --- a/app/assets/javascripts/preferences/panes/account/subscription/Subscription.tsx +++ b/app/assets/javascripts/preferences/panes/account/subscription/Subscription.tsx @@ -3,7 +3,6 @@ import { PreferencesSegment, Title, } from '@/preferences/components'; -import { observer } from '@node_modules/mobx-react-lite'; import { WebApplication } from '@/ui_models/application'; import { useCallback, useEffect, useState } from 'preact/hooks'; import { @@ -11,41 +10,25 @@ import { GetSubscriptionsResponse, } from '@standardnotes/snjs/dist/@types/services/api/responses'; import { SubscriptionState } from './subscription_state'; -import { CancelledSubscription } from './CancelledSubscription'; -import { ActiveSubscription } from './ActiveSubscription'; +import { SubscriptionInformation } from './SubscriptionInformation'; import { NoSubscription } from './NoSubscription'; import { Text } from '@/preferences/components'; -import { FunctionalComponent } from 'preact'; +import { observer } from 'mobx-react-lite'; type Props = { application: WebApplication; subscriptionState: SubscriptionState; }; -type SubscriptionInformationProps = { - subscriptionState: SubscriptionState; -}; - -const SubscriptionInformation = observer(({ +export const Subscription = observer(({ + application, subscriptionState, -}: SubscriptionInformationProps) => { - const now = new Date().getTime(); - const { userSubscription } = subscriptionState; - - if (userSubscription && userSubscription.endsAt > now) { - return userSubscription.cancelled ? ( - - ) : ( - - ); - } - return ; -}); - -export const Subscription: FunctionalComponent = ({ application, subscriptionState }) => { +}: Props) => { const [loading, setLoading] = useState(true); const [error, setError] = useState(false); + const { userSubscription } = subscriptionState; + const getSubscriptions = useCallback(async () => { try { const result = await application.getSubscriptions(); @@ -87,6 +70,8 @@ export const Subscription: FunctionalComponent = ({ application, subscrip getSubscriptionInfo(); }, [getSubscriptionInfo]); + const now = new Date().getTime(); + return ( @@ -97,12 +82,14 @@ export const Subscription: FunctionalComponent = ({ application, subscrip No subscription information available. ) : loading ? ( Loading subscription information... - ) : ( + ) : userSubscription && userSubscription.endsAt > now ? ( + ) : ( + )}
); -}; +}); diff --git a/app/assets/javascripts/preferences/panes/account/subscription/SubscriptionInformation.tsx b/app/assets/javascripts/preferences/panes/account/subscription/SubscriptionInformation.tsx new file mode 100644 index 000000000..eb97147a5 --- /dev/null +++ b/app/assets/javascripts/preferences/panes/account/subscription/SubscriptionInformation.tsx @@ -0,0 +1,81 @@ +import { observer } from 'mobx-react-lite'; +import { SubscriptionState } from './subscription_state'; +import { Text } from '@/preferences/components'; +import { Button } from '@/components/Button'; + +type Props = { + subscriptionState: SubscriptionState; +}; + +const StatusText = observer(({ subscriptionState }: Props) => { + const { userSubscription, userSubscriptionName } = subscriptionState; + + return userSubscription!.cancelled ? ( + + Your{' '} + + Standard Notes{userSubscriptionName ? ' ' : ''} + {userSubscriptionName} + {' '} + subscription has been{' '} + + canceled but will remain valid until{' '} + {new Date(userSubscription!.endsAt).toLocaleString()} + + . You may resubscribe below if you wish. + + ) : ( + + Your{' '} + + Standard Notes{userSubscriptionName ? ' ' : ''} + {userSubscriptionName} + {' '} + subscription will be{' '} + + renewed on {new Date(userSubscription!.endsAt).toLocaleString()} + + . + + ); +}); + +const PrimaryButton = observer(({ subscriptionState }: Props) => { + const { userSubscription } = subscriptionState; + + return ( +