-
-
Subscription
- {error ? (
-
No subscription information available.
- ) : loading ? (
-
Loading subscription information...
- ) : userSubscription && userSubscription.endsAt > now ? (
-
- ) : (
-
- )}
+ return (
+
+
+
+
+
Subscription
+ {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
index be1485854..2c36d2b06 100644
--- a/app/assets/javascripts/preferences/panes/account/subscription/SubscriptionInformation.tsx
+++ b/app/assets/javascripts/preferences/panes/account/subscription/SubscriptionInformation.tsx
@@ -1,9 +1,8 @@
import { observer } from 'mobx-react-lite';
-import { SubscriptionState } from './subscription_state';
+import { SubscriptionState } from '../../../../ui_models/app_state/subscription_state';
import { Text } from '@/preferences/components';
import { Button } from '@/components/Button';
import { WebApplication } from '@/ui_models/application';
-import { convertTimestampToMilliseconds } from '@standardnotes/snjs';
import { openSubscriptionDashboard } from '@/hooks/manageSubscription';
type Props = {
@@ -12,15 +11,15 @@ type Props = {
};
const StatusText = observer(({ subscriptionState }: Props) => {
- const { userSubscription, userSubscriptionName } = subscriptionState;
- const expirationDate = new Date(
- convertTimestampToMilliseconds(userSubscription!.endsAt)
- );
- const expirationDateString = expirationDate.toLocaleString();
- const expired = expirationDate.getTime() < new Date().getTime();
- const canceled = userSubscription!.cancelled;
+ const {
+ userSubscriptionName,
+ userSubscriptionExpirationDate,
+ isUserSubscriptionExpired,
+ isUserSubscriptionCanceled,
+ } = subscriptionState;
+ const expirationDateString = userSubscriptionExpirationDate?.toLocaleString();
- if (canceled) {
+ if (isUserSubscriptionCanceled) {
return (
Your{' '}
@@ -28,9 +27,8 @@ const StatusText = observer(({ subscriptionState }: Props) => {
Standard Notes{userSubscriptionName ? ' ' : ''}
{userSubscriptionName}
{' '}
- subscription has been canceled
- {' '}
- {expired ? (
+ subscription has been canceled{' '}
+ {isUserSubscriptionExpired ? (
and expired on {expirationDateString}
@@ -44,7 +42,7 @@ const StatusText = observer(({ subscriptionState }: Props) => {
);
}
- if (expired) {
+ if (isUserSubscriptionExpired) {
return (
Your{' '}
@@ -52,11 +50,9 @@ const StatusText = observer(({ subscriptionState }: Props) => {
Standard Notes{userSubscriptionName ? ' ' : ''}
{userSubscriptionName}
{' '}
- subscription {' '}
-
- expired on {expirationDateString}
-
- . You may resubscribe below if you wish.
+ subscription{' '}
+ expired on {expirationDateString}.
+ You may resubscribe below if you wish.
);
}
diff --git a/app/assets/javascripts/preferences/panes/account/subscription/SubscriptionWrapper.tsx b/app/assets/javascripts/preferences/panes/account/subscription/SubscriptionWrapper.tsx
deleted file mode 100644
index 9a19112e6..000000000
--- a/app/assets/javascripts/preferences/panes/account/subscription/SubscriptionWrapper.tsx
+++ /dev/null
@@ -1,21 +0,0 @@
-import { WebApplication } from '@/ui_models/application';
-import { FunctionalComponent } from 'preact';
-import { useState } from 'preact/hooks';
-import { Subscription } from './Subscription';
-import { SubscriptionState } from './subscription_state';
-
-type Props = {
- application: WebApplication;
-};
-
-export const SubscriptionWrapper: FunctionalComponent = ({
- application,
-}) => {
- const [subscriptionState] = useState(() => 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
deleted file mode 100644
index 38f6768a0..000000000
--- a/app/assets/javascripts/preferences/panes/account/subscription/subscription_state.tsx
+++ /dev/null
@@ -1,51 +0,0 @@
-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 = undefined;
- availableSubscriptions: AvailableSubscriptions | undefined = 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;
- }
-}
diff --git a/app/assets/javascripts/ui_models/app_state/app_state.ts b/app/assets/javascripts/ui_models/app_state/app_state.ts
index 8c2665fbf..f406fec30 100644
--- a/app/assets/javascripts/ui_models/app_state/app_state.ts
+++ b/app/assets/javascripts/ui_models/app_state/app_state.ts
@@ -33,6 +33,7 @@ import { PreferencesState } from './preferences_state';
import { PurchaseFlowState } from './purchase_flow_state';
import { QuickSettingsState } from './quick_settings_state';
import { SearchOptionsState } from './search_options_state';
+import { SubscriptionState } from './subscription_state';
import { SyncState } from './sync_state';
import { TagsState } from './tags_state';
@@ -86,6 +87,7 @@ export class AppState {
readonly features: FeaturesState;
readonly tags: TagsState;
readonly notesView: NotesViewState;
+ readonly subscription: SubscriptionState;
isSessionsModalVisible = false;
@@ -126,6 +128,10 @@ export class AppState {
application,
this.appEventObserverRemovers
);
+ this.subscription = new SubscriptionState(
+ application,
+ this.appEventObserverRemovers
+ );
this.purchaseFlow = new PurchaseFlowState(application);
this.notesView = new NotesViewState(
application,
diff --git a/app/assets/javascripts/ui_models/app_state/subscription_state.ts b/app/assets/javascripts/ui_models/app_state/subscription_state.ts
new file mode 100644
index 000000000..ff510c779
--- /dev/null
+++ b/app/assets/javascripts/ui_models/app_state/subscription_state.ts
@@ -0,0 +1,125 @@
+import {
+ ApplicationEvent,
+ convertTimestampToMilliseconds,
+} from '@standardnotes/snjs';
+import { action, computed, makeObservable, observable } from 'mobx';
+import { WebApplication } from '../application';
+
+type Subscription = {
+ planName: string;
+ cancelled: boolean;
+ endsAt: number;
+};
+
+type AvailableSubscriptions = {
+ [key: string]: {
+ name: string;
+ };
+};
+
+export class SubscriptionState {
+ userSubscription: Subscription | undefined = undefined;
+ availableSubscriptions: AvailableSubscriptions | undefined = undefined;
+
+ constructor(
+ private application: WebApplication,
+ appObservers: (() => void)[]
+ ) {
+ makeObservable(this, {
+ userSubscription: observable,
+ availableSubscriptions: observable,
+
+ userSubscriptionName: computed,
+ userSubscriptionExpirationDate: computed,
+ isUserSubscriptionExpired: computed,
+ isUserSubscriptionCanceled: computed,
+
+ setUserSubscription: action,
+ setAvailableSubscriptions: action,
+ });
+
+ appObservers.push(
+ application.addEventObserver(async () => {
+ if (application.hasAccount()) {
+ this.getSubscriptionInfo();
+ }
+ }, ApplicationEvent.Launched),
+ application.addEventObserver(async () => {
+ this.getSubscriptionInfo();
+ }, ApplicationEvent.SignedIn),
+ application.addEventObserver(async () => {
+ this.getSubscriptionInfo();
+ }, ApplicationEvent.UserRolesChanged)
+ );
+ }
+
+ get userSubscriptionName(): string {
+ if (
+ this.availableSubscriptions &&
+ this.userSubscription &&
+ this.availableSubscriptions[this.userSubscription.planName]
+ ) {
+ return this.availableSubscriptions[this.userSubscription.planName].name;
+ }
+ return '';
+ }
+
+ get userSubscriptionExpirationDate(): Date | undefined {
+ if (!this.userSubscription) {
+ return undefined;
+ }
+
+ return new Date(
+ convertTimestampToMilliseconds(this.userSubscription.endsAt)
+ );
+ }
+
+ get isUserSubscriptionExpired(): boolean {
+ if (!this.userSubscriptionExpirationDate) {
+ return false;
+ }
+
+ return this.userSubscriptionExpirationDate.getTime() < new Date().getTime();
+ }
+
+ get isUserSubscriptionCanceled(): boolean {
+ return Boolean(this.userSubscription?.cancelled);
+ }
+
+ public setUserSubscription(subscription: Subscription): void {
+ this.userSubscription = subscription;
+ }
+
+ public setAvailableSubscriptions(
+ subscriptions: AvailableSubscriptions
+ ): void {
+ this.availableSubscriptions = subscriptions;
+ }
+
+ private async getAvailableSubscriptions() {
+ try {
+ const subscriptions = await this.application.getAvailableSubscriptions();
+ if (subscriptions) {
+ this.setAvailableSubscriptions(subscriptions);
+ }
+ } catch (error) {
+ console.error(error);
+ }
+ }
+
+ private async getSubscription() {
+ try {
+ const subscription = await this.application.getUserSubscription();
+ if (subscription) {
+ this.setUserSubscription(subscription);
+ }
+ } catch (error) {
+ console.error(error);
+ }
+ }
+
+ private async getSubscriptionInfo() {
+ await this.getSubscription();
+ await this.getAvailableSubscriptions();
+ }
+}