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()}
-
- .
-
-
- null}
- />
- null}
- />
- null}
- />
-
- >
-);
-
-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.
-
-
- null}
- />
- null}
- />
- null}
- />
-
- >
-);
-
-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()}
+
+ .
+
+
+ null}
+ />
+ null}
+ />
+ null}
+ />
+
+ >
+ );
+ }
+);
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.
+
+
+ null}
+ />
+ null}
+ />
+ null}
+ />
+
+ >
+ );
+ }
+);
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.
+
+ null}
+ />
+ null}
+ />
+
+ >
+);
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;
+ }
+}