refactor: use mobx for state and separate files for components

This commit is contained in:
Antonella Sgarlatta
2021-09-07 12:04:25 -03:00
parent 1b87df18bc
commit 28dab13fb1
9 changed files with 288 additions and 225 deletions

View File

@@ -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 (
<PreferencesPane>
<Sync application={application} />
<Subscription application={application} />
<SubscriptionWrapper application={application} />
</PreferencesPane>
);
});

View File

@@ -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 = () => (
<>
<Text>You don't have a Standard Notes subscription yet.</Text>
<div className="flex">
<Button
className="min-w-20 mt-3 mr-3"
type="normal"
label="Refresh"
onClick={() => null}
/>
<Button
className="min-w-20 mt-3"
type="primary"
label="Purchase subscription"
onClick={() => null}
/>
</div>
</>
);
const ActiveSubscription = ({
subscription,
availableSubscriptions,
}: ValidSubscriptionProps) => (
<>
<Text>
Your{' '}
<span className="font-bold">
Standard Notes {availableSubscriptions[subscription.planName]}
</span>{' '}
subscription will be{' '}
<span className="font-bold">
renewed on {new Date(subscription.endsAt).toLocaleString()}
</span>
.
</Text>
<div className="flex">
<Button
className="min-w-20 mt-3 mr-3"
type="normal"
label="Refresh"
onClick={() => null}
/>
<Button
className="min-w-20 mt-3 mr-3"
type="normal"
label="Change plan"
onClick={() => null}
/>
<Button
className="min-w-20 mt-3"
type="primary"
label="Cancel subscription"
onClick={() => null}
/>
</div>
</>
);
const CancelledSubscription = ({
subscription,
availableSubscriptions,
}: ValidSubscriptionProps) => (
<>
<Text>
Your{' '}
<span className="font-bold">
Standard Notes {availableSubscriptions[subscription.planName]}
</span>{' '}
subscription has been{' '}
<span className="font-bold">
canceled but will remain valid until{' '}
{new Date(subscription.endsAt).toLocaleString()}
</span>
. You may resubscribe below if you wish.
</Text>
<div className="flex">
<Button
className="min-w-20 mt-3 mr-3"
type="normal"
label="Refresh"
onClick={() => null}
/>
<Button
className="min-w-20 mt-3 mr-3"
type="normal"
label="Change plan"
onClick={() => null}
/>
<Button
className="min-w-20 mt-3"
type="primary"
label="Renew subscription"
onClick={() => null}
/>
</div>
</>
);
const SubscriptionInformation = ({
subscription,
availableSubscriptions,
}: SubscriptionInformationProps) => {
const now = new Date().getTime();
if (subscription && subscription.endsAt > now) {
return subscription.cancelled ? (
<CancelledSubscription
subscription={subscription}
availableSubscriptions={availableSubscriptions}
/>
) : (
<ActiveSubscription
subscription={subscription}
availableSubscriptions={availableSubscriptions}
/>
);
}
return <NoSubscription />;
};
const Subscription = observer(({ application }: Props) => {
const [subscription, setSubscription] =
useState<Subscription | undefined>(undefined);
const [availableSubscriptions, setAvailableSubscriptions] =
useState<AvailableSubscriptions>({});
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 (
<PreferencesGroup>
<PreferencesSegment>
<div className="flex flex-row items-center">
<div className="flex-grow flex flex-col">
<Title>Subscription</Title>
{error ? (
'No subscription information available.'
) : (
<SubscriptionInformation
subscription={subscription}
availableSubscriptions={availableSubscriptions}
/>
)}
</div>
</div>
</PreferencesSegment>
</PreferencesGroup>
);
});
export default Subscription;

View File

@@ -1,2 +1,2 @@
export { default as Sync } from './Sync';
export { default as Subscription } from './Subscription';
export { SubscriptionWrapper } from './subscription/SubscriptionWrapper';

View File

@@ -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 (
<>
<Text>
Your{' '}
<span className="font-bold">
Standard Notes{userSubscriptionName ? " " : ""}{userSubscriptionName}
</span>{' '}
subscription will be{' '}
<span className="font-bold">
renewed on {new Date(userSubscription!.endsAt).toLocaleString()}
</span>
.
</Text>
<div className="flex">
<Button
className="min-w-20 mt-3 mr-3"
type="normal"
label="Refresh"
onClick={() => null}
/>
<Button
className="min-w-20 mt-3 mr-3"
type="normal"
label="Change plan"
onClick={() => null}
/>
<Button
className="min-w-20 mt-3"
type="primary"
label="Cancel subscription"
onClick={() => null}
/>
</div>
</>
);
}
);

View File

@@ -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 (
<>
<Text>
Your{' '}
<span className="font-bold">
Standard NotesStandard Notes{userSubscriptionName ? " " : ""}{userSubscriptionName}
</span>{' '}
subscription has been{' '}
<span className="font-bold">
canceled but will remain valid until{' '}
{new Date(userSubscription!.endsAt).toLocaleString()}
</span>
. You may resubscribe below if you wish.
</Text>
<div className="flex">
<Button
className="min-w-20 mt-3 mr-3"
type="normal"
label="Refresh"
onClick={() => null}
/>
<Button
className="min-w-20 mt-3 mr-3"
type="normal"
label="Change plan"
onClick={() => null}
/>
<Button
className="min-w-20 mt-3"
type="primary"
label="Renew subscription"
onClick={() => null}
/>
</div>
</>
);
}
);

View File

@@ -0,0 +1,23 @@
import { FunctionalComponent } from "preact";
import { Text } from '@/preferences/components';
import { Button } from '@/components/Button';
export const NoSubscription: FunctionalComponent = () => (
<>
<Text>You don't have a Standard Notes subscription yet.</Text>
<div className="flex">
<Button
className="min-w-20 mt-3 mr-3"
type="normal"
label="Refresh"
onClick={() => null}
/>
<Button
className="min-w-20 mt-3"
type="primary"
label="Purchase subscription"
onClick={() => null}
/>
</div>
</>
);

View File

@@ -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 ? (
<CancelledSubscription subscriptionState={subscriptionState} />
) : (
<ActiveSubscription subscriptionState={subscriptionState} />
);
}
return <NoSubscription />;
};
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 (
<PreferencesGroup>
<PreferencesSegment>
<div className="flex flex-row items-center">
<div className="flex-grow flex flex-col">
<Title>Subscription</Title>
{error ? (
'No subscription information available.'
) : (
<SubscriptionInformation subscriptionState={subscriptionState} />
)}
</div>
</div>
</PreferencesSegment>
</PreferencesGroup>
);
});

View File

@@ -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<Props> = ({
application,
}) => {
const subscriptionState = new SubscriptionState();
return (
<Subscription
application={application}
subscriptionState={subscriptionState}
/>
);
};

View File

@@ -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;
}
}