feat(core): onetime subscription ui (#8462)

This commit is contained in:
CatsJuice
2024-10-10 10:12:43 +00:00
parent 69fb5c06f4
commit 29a31110cd
8 changed files with 74 additions and 8 deletions

View File

@@ -94,6 +94,7 @@ const SubscriptionSettings = () => {
const proSubscription = useLiveData(subscriptionService.subscription.pro$);
const proPrice = useLiveData(subscriptionService.prices.proPrice$);
const isBeliever = useLiveData(subscriptionService.subscription.isBeliever$);
const isOnetime = useLiveData(subscriptionService.subscription.isOnetime$);
const [openCancelModal, setOpenCancelModal] = useState(false);
const setOpenSettingModalAtom = useSetAtom(openSettingModalAtom);
@@ -206,7 +207,17 @@ const SubscriptionSettings = () => {
})}
/>
)}
{isBeliever ? null : proSubscription.end &&
{isOnetime && proSubscription.end && (
<SettingRow
name={t['com.affine.payment.billing-setting.due-date']()}
desc={t[
'com.affine.payment.billing-setting.due-date.description'
]({
dueDate: new Date(proSubscription.end).toLocaleDateString(),
})}
/>
)}
{isBeliever || isOnetime ? null : proSubscription.end &&
proSubscription.canceledAt ? (
<SettingRow
name={t['com.affine.payment.billing-setting.expiration-date']()}

View File

@@ -4,7 +4,7 @@ import { SubscriptionRecurring } from '@affine/graphql';
import { Trans, useI18n } from '@affine/i18n';
import { useLiveData, useService } from '@toeverything/infra';
import { Upgrade } from '../plan-card';
import { RedeemCode, Upgrade } from '../plan-card';
import { BelieverCard } from './believer-card';
import { BelieverBenefits } from './benefits';
import * as styles from './style.css';
@@ -17,6 +17,7 @@ export const LifetimePlan = () => {
subscriptionService.prices.readableLifetimePrice$
);
const isBeliever = useLiveData(subscriptionService.subscription.isBeliever$);
const isOnetime = useLiveData(subscriptionService.subscription.isOnetime$);
if (!readableLifetimePrice) return null;
@@ -36,6 +37,8 @@ export const LifetimePlan = () => {
<Button className={styles.purchase} size="default" disabled>
{t['com.affine.payment.lifetime.purchased']()}
</Button>
) : isOnetime ? (
<RedeemCode className={styles.purchase} size="default" />
) : (
<Upgrade
className={styles.purchase}

View File

@@ -4,8 +4,15 @@ import { generateSubscriptionCallbackLink } from '@affine/core/components/hooks/
import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
import { AuthService, SubscriptionService } from '@affine/core/modules/cloud';
import { popupWindow } from '@affine/core/utils';
import type { SubscriptionRecurring } from '@affine/graphql';
import { SubscriptionPlan, SubscriptionStatus } from '@affine/graphql';
import {
type CreateCheckoutSessionInput,
SubscriptionRecurring,
} from '@affine/graphql';
import {
SubscriptionPlan,
SubscriptionStatus,
SubscriptionVariant,
} from '@affine/graphql';
import { Trans, useI18n } from '@affine/i18n';
import { track } from '@affine/track';
import { DoneIcon } from '@blocksuite/icons/rc';
@@ -96,6 +103,7 @@ const ActionButton = ({ detail, recurring }: PlanCardProps) => {
);
const currentPlan = primarySubscription?.plan ?? SubscriptionPlan.Free;
const currentRecurring = primarySubscription?.recurring;
const isOnetime = useLiveData(subscriptionService.subscription.isOnetime$);
// branches:
// if contact => 'Contact Sales'
@@ -104,12 +112,12 @@ const ActionButton = ({ detail, recurring }: PlanCardProps) => {
// else => 'Buy Pro'
// else
// if isBeliever => 'Included in Lifetime'
// if onetime => 'Redeem Code'
// if isCurrent
// if canceled => 'Resume'
// else => 'Current Plan'
// if isCurrent => 'Current Plan'
// else if free => 'Downgrade'
// else if currentRecurring !== recurring => 'Change to {recurring} Billing'
// if free => 'Downgrade'
// if currentRecurring !== recurring => 'Change to {recurring} Billing'
// else => 'Upgrade'
// contact
@@ -137,6 +145,11 @@ const ActionButton = ({ detail, recurring }: PlanCardProps) => {
);
}
// onetime
if (isOnetime) {
return <RedeemCode recurring={recurring} />;
}
const isCanceled = !!primarySubscription?.canceledAt;
const isFree = detail.plan === SubscriptionPlan.Free;
const isCurrent =
@@ -242,9 +255,11 @@ export const Upgrade = ({
className,
recurring,
children,
checkoutInput,
...btnProps
}: ButtonProps & {
recurring: SubscriptionRecurring;
checkoutInput?: Partial<CreateCheckoutSessionInput>;
}) => {
const [isMutating, setMutating] = useState(false);
const [isOpenedExternalWindow, setOpenedExternalWindow] = useState(false);
@@ -289,6 +304,7 @@ export const Upgrade = ({
SubscriptionPlan.Pro,
recurring
),
...checkoutInput,
});
setMutating(false);
setIdempotencyKey(nanoid());
@@ -299,6 +315,7 @@ export const Upgrade = ({
authService.session.account$.value,
subscriptionService,
idempotencyKey,
checkoutInput,
]);
return (
@@ -435,3 +452,24 @@ const ResumeButton = () => {
</ResumeAction>
);
};
const redeemCodeCheckoutInput = { variant: SubscriptionVariant.Onetime };
export const RedeemCode = ({
className,
recurring = SubscriptionRecurring.Yearly,
children,
...btnProps
}: ButtonProps & { recurring?: SubscriptionRecurring }) => {
const t = useI18n();
return (
<Upgrade
recurring={recurring}
className={className}
checkoutInput={redeemCodeCheckoutInput}
{...btnProps}
>
{children ?? t['com.affine.payment.redeem-code']()}
</Upgrade>
);
};