chore(server): remove useless free subscription enum (#6634)
This commit is contained in:
@@ -56,7 +56,10 @@ export class UserSubscriptionType implements Partial<UserSubscription> {
|
|||||||
@Field({ name: 'id' })
|
@Field({ name: 'id' })
|
||||||
stripeSubscriptionId!: string;
|
stripeSubscriptionId!: string;
|
||||||
|
|
||||||
@Field(() => SubscriptionPlan)
|
@Field(() => SubscriptionPlan, {
|
||||||
|
description:
|
||||||
|
"The 'Free' plan just exists to be a placeholder and for the type convenience of frontend.\nThere won't actually be a subscription with plan 'Free'",
|
||||||
|
})
|
||||||
plan!: SubscriptionPlan;
|
plan!: SubscriptionPlan;
|
||||||
|
|
||||||
@Field(() => SubscriptionRecurring)
|
@Field(() => SubscriptionRecurring)
|
||||||
|
|||||||
@@ -443,6 +443,11 @@ type UserSubscription {
|
|||||||
end: DateTime!
|
end: DateTime!
|
||||||
id: String!
|
id: String!
|
||||||
nextBillAt: DateTime
|
nextBillAt: DateTime
|
||||||
|
|
||||||
|
"""
|
||||||
|
The 'Free' plan just exists to be a placeholder and for the type convenience of frontend.
|
||||||
|
There won't actually be a subscription with plan 'Free'
|
||||||
|
"""
|
||||||
plan: SubscriptionPlan!
|
plan: SubscriptionPlan!
|
||||||
recurring: SubscriptionRecurring!
|
recurring: SubscriptionRecurring!
|
||||||
start: DateTime!
|
start: DateTime!
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export const UserPlanButton = () => {
|
|||||||
serverConfigService.serverConfig.features$.map(r => r?.payment)
|
serverConfigService.serverConfig.features$.map(r => r?.payment)
|
||||||
);
|
);
|
||||||
const plan = useLiveData(
|
const plan = useLiveData(
|
||||||
subscriptionService.subscription.primary$.map(subscription =>
|
subscriptionService.subscription.pro$.map(subscription =>
|
||||||
subscription !== null ? subscription?.plan : null
|
subscription !== null ? subscription?.plan : null
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { Button, ErrorMessage, Skeleton, Tooltip } from '@affine/component';
|
import { Button, ErrorMessage, Skeleton, Tooltip } from '@affine/component';
|
||||||
import { SubscriptionPlan } from '@affine/graphql';
|
|
||||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||||
import { useLiveData, useService } from '@toeverything/infra';
|
import { useLiveData, useService } from '@toeverything/infra';
|
||||||
import { cssVar } from '@toeverything/theme';
|
import { cssVar } from '@toeverything/theme';
|
||||||
@@ -45,15 +44,14 @@ export const StorageProgress = ({ onUpgrade }: StorageProgressProgress) => {
|
|||||||
subscription.revalidate();
|
subscription.revalidate();
|
||||||
}, [subscription]);
|
}, [subscription]);
|
||||||
|
|
||||||
const primarySubscription = useLiveData(subscription.primary$);
|
const proSubscription = useLiveData(subscription.pro$);
|
||||||
const isFreeUser =
|
const isFreeUser = !proSubscription;
|
||||||
!primarySubscription || primarySubscription?.plan === SubscriptionPlan.Free;
|
|
||||||
const quotaName = useLiveData(
|
const quotaName = useLiveData(
|
||||||
quota.quota$.map(q => (q !== null ? q?.humanReadable.name : null))
|
quota.quota$.map(q => (q !== null ? q?.humanReadable.name : null))
|
||||||
);
|
);
|
||||||
|
|
||||||
const loading =
|
const loading =
|
||||||
primarySubscription === null || percent === null || quotaName === null;
|
proSubscription === null || percent === null || quotaName === null;
|
||||||
const loadError = useLiveData(quota.error$);
|
const loadError = useLiveData(quota.error$);
|
||||||
|
|
||||||
const buttonType = useMemo(() => {
|
const buttonType = useMemo(() => {
|
||||||
|
|||||||
@@ -96,47 +96,39 @@ const SubscriptionSettings = () => {
|
|||||||
subscriptionService.prices.revalidate();
|
subscriptionService.prices.revalidate();
|
||||||
}, [subscriptionService]);
|
}, [subscriptionService]);
|
||||||
|
|
||||||
const primarySubscription = useLiveData(
|
const proSubscription = useLiveData(subscriptionService.subscription.pro$);
|
||||||
subscriptionService.subscription.primary$
|
|
||||||
);
|
|
||||||
const proPrice = useLiveData(subscriptionService.prices.proPrice$);
|
const proPrice = useLiveData(subscriptionService.prices.proPrice$);
|
||||||
|
|
||||||
const currentPlan =
|
|
||||||
primarySubscription === null
|
|
||||||
? null
|
|
||||||
: primarySubscription?.plan ?? SubscriptionPlan.Free;
|
|
||||||
|
|
||||||
const [openCancelModal, setOpenCancelModal] = useState(false);
|
const [openCancelModal, setOpenCancelModal] = useState(false);
|
||||||
|
|
||||||
const recurring =
|
|
||||||
primarySubscription?.recurring ?? SubscriptionRecurring.Monthly;
|
|
||||||
|
|
||||||
const setOpenSettingModalAtom = useSetAtom(openSettingModalAtom);
|
const setOpenSettingModalAtom = useSetAtom(openSettingModalAtom);
|
||||||
|
|
||||||
|
const currentPlan = proSubscription?.plan ?? SubscriptionPlan.Free;
|
||||||
|
const currentRecurring =
|
||||||
|
proSubscription?.recurring ?? SubscriptionRecurring.Monthly;
|
||||||
|
|
||||||
const gotoPlansSetting = useCallback(() => {
|
const gotoPlansSetting = useCallback(() => {
|
||||||
mixpanel.track('Button', {
|
mixpanel.track('Button', {
|
||||||
resolve: 'ChangePlan',
|
resolve: 'ChangePlan',
|
||||||
currentPlan: currentPlan,
|
currentPlan: proSubscription?.plan,
|
||||||
});
|
});
|
||||||
setOpenSettingModalAtom({
|
setOpenSettingModalAtom({
|
||||||
open: true,
|
open: true,
|
||||||
activeTab: 'plans',
|
activeTab: 'plans',
|
||||||
});
|
});
|
||||||
}, [currentPlan, setOpenSettingModalAtom]);
|
}, [proSubscription, setOpenSettingModalAtom]);
|
||||||
|
|
||||||
const amount = currentPlan
|
const amount = proSubscription
|
||||||
? currentPlan === SubscriptionPlan.Free
|
? proPrice
|
||||||
? '0'
|
? proSubscription.recurring === SubscriptionRecurring.Monthly
|
||||||
: proPrice
|
? String((proPrice.amount ?? 0) / 100)
|
||||||
? recurring === SubscriptionRecurring.Monthly
|
: String((proPrice.yearlyAmount ?? 0) / 100)
|
||||||
? String((proPrice.amount ?? 0) / 100)
|
: '?'
|
||||||
: String((proPrice.yearlyAmount ?? 0) / 100)
|
: '0';
|
||||||
: '?'
|
|
||||||
: '?';
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.subscription}>
|
<div className={styles.subscription}>
|
||||||
{currentPlan !== null ? (
|
{/* loaded */}
|
||||||
|
{proSubscription !== null ? (
|
||||||
<div className={styles.planCard}>
|
<div className={styles.planCard}>
|
||||||
<div className={styles.currentPlan}>
|
<div className={styles.currentPlan}>
|
||||||
<SettingRow
|
<SettingRow
|
||||||
@@ -144,7 +136,7 @@ const SubscriptionSettings = () => {
|
|||||||
name={t['com.affine.payment.billing-setting.current-plan']()}
|
name={t['com.affine.payment.billing-setting.current-plan']()}
|
||||||
desc={
|
desc={
|
||||||
<Trans
|
<Trans
|
||||||
i18nKey={getMessageKey(currentPlan, recurring)}
|
i18nKey={getMessageKey(currentPlan, currentRecurring)}
|
||||||
values={{
|
values={{
|
||||||
planName: currentPlan,
|
planName: currentPlan,
|
||||||
}}
|
}}
|
||||||
@@ -168,7 +160,7 @@ const SubscriptionSettings = () => {
|
|||||||
${amount}
|
${amount}
|
||||||
<span className={styles.billingFrequency}>
|
<span className={styles.billingFrequency}>
|
||||||
/
|
/
|
||||||
{recurring === SubscriptionRecurring.Monthly
|
{currentRecurring === SubscriptionRecurring.Monthly
|
||||||
? t['com.affine.payment.billing-setting.month']()
|
? t['com.affine.payment.billing-setting.month']()
|
||||||
: t['com.affine.payment.billing-setting.year']()}
|
: t['com.affine.payment.billing-setting.year']()}
|
||||||
</span>
|
</span>
|
||||||
@@ -179,8 +171,8 @@ const SubscriptionSettings = () => {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<AIPlanCard />
|
<AIPlanCard />
|
||||||
{primarySubscription !== null ? (
|
{proSubscription !== null ? (
|
||||||
primarySubscription?.status === SubscriptionStatus.Active && (
|
proSubscription?.status === SubscriptionStatus.Active && (
|
||||||
<>
|
<>
|
||||||
<SettingRow
|
<SettingRow
|
||||||
className={styles.paymentMethod}
|
className={styles.paymentMethod}
|
||||||
@@ -191,26 +183,26 @@ const SubscriptionSettings = () => {
|
|||||||
>
|
>
|
||||||
<PaymentMethodUpdater />
|
<PaymentMethodUpdater />
|
||||||
</SettingRow>
|
</SettingRow>
|
||||||
{primarySubscription.nextBillAt && (
|
{proSubscription.nextBillAt && (
|
||||||
<SettingRow
|
<SettingRow
|
||||||
name={t['com.affine.payment.billing-setting.renew-date']()}
|
name={t['com.affine.payment.billing-setting.renew-date']()}
|
||||||
desc={t[
|
desc={t[
|
||||||
'com.affine.payment.billing-setting.renew-date.description'
|
'com.affine.payment.billing-setting.renew-date.description'
|
||||||
]({
|
]({
|
||||||
renewDate: new Date(
|
renewDate: new Date(
|
||||||
primarySubscription.nextBillAt
|
proSubscription.nextBillAt
|
||||||
).toLocaleDateString(),
|
).toLocaleDateString(),
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{primarySubscription.canceledAt ? (
|
{proSubscription.canceledAt ? (
|
||||||
<SettingRow
|
<SettingRow
|
||||||
name={t['com.affine.payment.billing-setting.expiration-date']()}
|
name={t['com.affine.payment.billing-setting.expiration-date']()}
|
||||||
desc={t[
|
desc={t[
|
||||||
'com.affine.payment.billing-setting.expiration-date.description'
|
'com.affine.payment.billing-setting.expiration-date.description'
|
||||||
]({
|
]({
|
||||||
expirationDate: new Date(
|
expirationDate: new Date(
|
||||||
primarySubscription.end
|
proSubscription.end
|
||||||
).toLocaleDateString(),
|
).toLocaleDateString(),
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
@@ -322,9 +314,9 @@ const PlanAction = ({
|
|||||||
type="primary"
|
type="primary"
|
||||||
onClick={gotoPlansSetting}
|
onClick={gotoPlansSetting}
|
||||||
>
|
>
|
||||||
{plan === SubscriptionPlan.Free
|
{plan === SubscriptionPlan.Pro
|
||||||
? t['com.affine.payment.billing-setting.upgrade']()
|
? t['com.affine.payment.billing-setting.change-plan']()
|
||||||
: t['com.affine.payment.billing-setting.change-plan']()}
|
: t['com.affine.payment.billing-setting.upgrade']()}
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -36,9 +36,7 @@ const Settings = () => {
|
|||||||
const scrollWrapper = useRef<HTMLDivElement>(null);
|
const scrollWrapper = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
const subscriptionService = useService(SubscriptionService);
|
const subscriptionService = useService(SubscriptionService);
|
||||||
const primarySubscription = useLiveData(
|
const proSubscription = useLiveData(subscriptionService.subscription.pro$);
|
||||||
subscriptionService.subscription.primary$
|
|
||||||
);
|
|
||||||
const prices = useLiveData(subscriptionService.prices.prices$);
|
const prices = useLiveData(subscriptionService.prices.prices$);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -62,13 +60,13 @@ const Settings = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const [recurring, setRecurring] = useState<SubscriptionRecurring>(
|
const [recurring, setRecurring] = useState<SubscriptionRecurring>(
|
||||||
primarySubscription?.recurring ?? SubscriptionRecurring.Yearly
|
proSubscription?.recurring ?? SubscriptionRecurring.Yearly
|
||||||
);
|
);
|
||||||
|
|
||||||
const currentPlan = primarySubscription?.plan ?? SubscriptionPlan.Free;
|
const currentPlan = proSubscription?.plan ?? SubscriptionPlan.Free;
|
||||||
const isCanceled = !!primarySubscription?.canceledAt;
|
const isCanceled = !!proSubscription?.canceledAt;
|
||||||
const currentRecurring =
|
const currentRecurring =
|
||||||
primarySubscription?.recurring ?? SubscriptionRecurring.Monthly;
|
proSubscription?.recurring ?? SubscriptionRecurring.Monthly;
|
||||||
|
|
||||||
const yearlyDiscount = (
|
const yearlyDiscount = (
|
||||||
planDetail.get(SubscriptionPlan.Pro) as FixedPrice | undefined
|
planDetail.get(SubscriptionPlan.Pro) as FixedPrice | undefined
|
||||||
|
|||||||
@@ -32,15 +32,13 @@ export const PlanCard = (props: PlanCardProps) => {
|
|||||||
const loggedIn =
|
const loggedIn =
|
||||||
useLiveData(useService(AuthService).session.status$) === 'authenticated';
|
useLiveData(useService(AuthService).session.status$) === 'authenticated';
|
||||||
const subscriptionService = useService(SubscriptionService);
|
const subscriptionService = useService(SubscriptionService);
|
||||||
const primarySubscription = useLiveData(
|
const proSubscription = useLiveData(subscriptionService.subscription.pro$);
|
||||||
subscriptionService.subscription.primary$
|
const currentPlan = proSubscription?.plan ?? SubscriptionPlan.Free;
|
||||||
);
|
|
||||||
const currentPlan = primarySubscription?.plan ?? SubscriptionPlan.Free;
|
|
||||||
|
|
||||||
const isCurrent =
|
const isCurrent =
|
||||||
loggedIn &&
|
loggedIn &&
|
||||||
detail.plan === currentPlan &&
|
detail.plan === currentPlan &&
|
||||||
recurring === primarySubscription?.recurring;
|
recurring === proSubscription?.recurring;
|
||||||
const isPro = detail.plan === SubscriptionPlan.Pro;
|
const isPro = detail.plan === SubscriptionPlan.Pro;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -93,7 +91,7 @@ const ActionButton = ({ detail, recurring }: PlanCardProps) => {
|
|||||||
useLiveData(useService(AuthService).session.status$) === 'authenticated';
|
useLiveData(useService(AuthService).session.status$) === 'authenticated';
|
||||||
const subscriptionService = useService(SubscriptionService);
|
const subscriptionService = useService(SubscriptionService);
|
||||||
const primarySubscription = useLiveData(
|
const primarySubscription = useLiveData(
|
||||||
subscriptionService.subscription.primary$
|
subscriptionService.subscription.pro$
|
||||||
);
|
);
|
||||||
const currentPlan = primarySubscription?.plan ?? SubscriptionPlan.Free;
|
const currentPlan = primarySubscription?.plan ?? SubscriptionPlan.Free;
|
||||||
const currentRecurring = primarySubscription?.recurring;
|
const currentRecurring = primarySubscription?.recurring;
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import { useRevokeMemberPermission } from '@affine/core/hooks/affine/use-revoke-
|
|||||||
import { WorkspacePermissionService } from '@affine/core/modules/permissions';
|
import { WorkspacePermissionService } from '@affine/core/modules/permissions';
|
||||||
import { WorkspaceQuotaService } from '@affine/core/modules/quota';
|
import { WorkspaceQuotaService } from '@affine/core/modules/quota';
|
||||||
import { WorkspaceFlavour } from '@affine/env/workspace';
|
import { WorkspaceFlavour } from '@affine/env/workspace';
|
||||||
import { Permission, SubscriptionPlan } from '@affine/graphql';
|
import { Permission } from '@affine/graphql';
|
||||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||||
import { MoreVerticalIcon } from '@blocksuite/icons';
|
import { MoreVerticalIcon } from '@blocksuite/icons';
|
||||||
import {
|
import {
|
||||||
@@ -98,7 +98,7 @@ export const CloudWorkspaceMembersPanel = () => {
|
|||||||
const workspaceQuota = useLiveData(workspaceQuotaService.quota.quota$);
|
const workspaceQuota = useLiveData(workspaceQuotaService.quota.quota$);
|
||||||
const subscriptionService = useService(SubscriptionService);
|
const subscriptionService = useService(SubscriptionService);
|
||||||
const plan = useLiveData(
|
const plan = useLiveData(
|
||||||
subscriptionService.subscription.primary$.map(s => s?.plan)
|
subscriptionService.subscription.pro$.map(s => s?.plan)
|
||||||
);
|
);
|
||||||
const isLimited = workspaceQuota
|
const isLimited = workspaceQuota
|
||||||
? checkMemberCountLimit(memberCount, workspaceQuota.memberLimit)
|
? checkMemberCountLimit(memberCount, workspaceQuota.memberLimit)
|
||||||
@@ -206,7 +206,7 @@ export const CloudWorkspaceMembersPanel = () => {
|
|||||||
<Button onClick={openModal}>{t['Invite Members']()}</Button>
|
<Button onClick={openModal}>{t['Invite Members']()}</Button>
|
||||||
{isLimited ? (
|
{isLimited ? (
|
||||||
<MemberLimitModal
|
<MemberLimitModal
|
||||||
isFreePlan={plan === SubscriptionPlan.Free}
|
isFreePlan={!!plan}
|
||||||
open={open}
|
open={open}
|
||||||
plan={workspaceQuota?.humanReadable.name ?? ''}
|
plan={workspaceQuota?.humanReadable.name ?? ''}
|
||||||
quota={workspaceQuota?.humanReadable.memberLimit ?? ''}
|
quota={workspaceQuota?.humanReadable.memberLimit ?? ''}
|
||||||
|
|||||||
@@ -21,8 +21,7 @@ const UserInfo = () => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
subscription.revalidate();
|
subscription.revalidate();
|
||||||
}, [subscription]);
|
}, [subscription]);
|
||||||
const primary = useLiveData(subscription.primary$);
|
const plan = useLiveData(subscription.pro$)?.plan;
|
||||||
const plan = primary?.plan;
|
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
// TODO: loading UI
|
// TODO: loading UI
|
||||||
|
|||||||
@@ -28,34 +28,11 @@ export class Subscription extends Entity {
|
|||||||
isRevalidating$ = new LiveData(false);
|
isRevalidating$ = new LiveData(false);
|
||||||
error$ = new LiveData<any | null>(null);
|
error$ = new LiveData<any | null>(null);
|
||||||
|
|
||||||
/**
|
|
||||||
* Primary subscription is the subscription that is not AI.
|
|
||||||
*/
|
|
||||||
primary$ = this.subscription$.map(subscriptions =>
|
|
||||||
subscriptions
|
|
||||||
? subscriptions.find(sub => sub.plan !== SubscriptionPlan.AI)
|
|
||||||
: null
|
|
||||||
);
|
|
||||||
isFree$ = this.subscription$.map(subscriptions =>
|
|
||||||
subscriptions
|
|
||||||
? subscriptions.some(sub => sub.plan === SubscriptionPlan.Free)
|
|
||||||
: null
|
|
||||||
);
|
|
||||||
isPro$ = this.subscription$.map(subscriptions =>
|
|
||||||
subscriptions
|
|
||||||
? subscriptions.some(sub => sub.plan === SubscriptionPlan.Pro)
|
|
||||||
: null
|
|
||||||
);
|
|
||||||
pro$ = this.subscription$.map(subscriptions =>
|
pro$ = this.subscription$.map(subscriptions =>
|
||||||
subscriptions
|
subscriptions
|
||||||
? subscriptions.find(sub => sub.plan === SubscriptionPlan.Pro)
|
? subscriptions.find(sub => sub.plan === SubscriptionPlan.Pro)
|
||||||
: null
|
: null
|
||||||
);
|
);
|
||||||
isSelfHosted$ = this.subscription$.map(subscriptions =>
|
|
||||||
subscriptions
|
|
||||||
? subscriptions.some(sub => sub.plan === SubscriptionPlan.SelfHosted)
|
|
||||||
: null
|
|
||||||
);
|
|
||||||
ai$ = this.subscription$.map(subscriptions =>
|
ai$ = this.subscription$.map(subscriptions =>
|
||||||
subscriptions
|
subscriptions
|
||||||
? subscriptions.find(sub => sub.plan === SubscriptionPlan.AI)
|
? subscriptions.find(sub => sub.plan === SubscriptionPlan.AI)
|
||||||
|
|||||||
Reference in New Issue
Block a user