Skip to content

Commit

Permalink
feat(core): onetime subscription ui (#8462)
Browse files Browse the repository at this point in the history
  • Loading branch information
CatsJuice committed Oct 10, 2024
1 parent 69fb5c0 commit 29a3111
Show file tree
Hide file tree
Showing 8 changed files with 74 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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']()}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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;

Expand All @@ -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}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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'
Expand All @@ -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
Expand Down Expand Up @@ -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 =
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -289,6 +304,7 @@ export const Upgrade = ({
SubscriptionPlan.Pro,
recurring
),
...checkoutInput,
});
setMutating(false);
setIdempotencyKey(nanoid());
Expand All @@ -299,6 +315,7 @@ export const Upgrade = ({
authService.session.account$.value,
subscriptionService,
idempotencyKey,
checkoutInput,
]);

return (
Expand Down Expand Up @@ -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>
);
};
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { type SubscriptionQuery, SubscriptionRecurring } from '@affine/graphql';
import {
type SubscriptionQuery,
SubscriptionRecurring,
SubscriptionVariant,
} from '@affine/graphql';
import { SubscriptionPlan } from '@affine/graphql';
import {
backoffRetry,
Expand Down Expand Up @@ -41,6 +45,9 @@ export class Subscription extends Entity {
isBeliever$ = this.pro$.map(
sub => sub?.recurring === SubscriptionRecurring.Lifetime
);
isOnetime$ = this.pro$.map(
sub => sub?.variant === SubscriptionVariant.Onetime
);

constructor(
private readonly authService: AuthService,
Expand Down
1 change: 1 addition & 0 deletions packages/frontend/graphql/src/graphql/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1037,6 +1037,7 @@ query subscription {
end
nextBillAt
canceledAt
variant
}
}
}`,
Expand Down
1 change: 1 addition & 0 deletions packages/frontend/graphql/src/graphql/subscription.gql
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ query subscription {
end
nextBillAt
canceledAt
variant
}
}
}
2 changes: 2 additions & 0 deletions packages/frontend/graphql/src/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1141,6 +1141,7 @@ export interface UserSubscription {
trialEnd: Maybe<Scalars['DateTime']['output']>;
trialStart: Maybe<Scalars['DateTime']['output']>;
updatedAt: Scalars['DateTime']['output'];
variant: Maybe<SubscriptionVariant>;
}

export interface UserType {
Expand Down Expand Up @@ -2210,6 +2211,7 @@ export type SubscriptionQuery = {
end: string | null;
nextBillAt: string | null;
canceledAt: string | null;
variant: SubscriptionVariant | null;
}>;
} | null;
};
Expand Down
3 changes: 3 additions & 0 deletions packages/frontend/i18n/src/resources/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -742,6 +742,8 @@
"com.affine.payment.billing-setting.payment-method.description": "Provided by Stripe.",
"com.affine.payment.billing-setting.renew-date": "Renew date",
"com.affine.payment.billing-setting.renew-date.description": "Next billing date: {{renewDate}}",
"com.affine.payment.billing-setting.due-date": "Due date",
"com.affine.payment.billing-setting.due-date.description": "Your subscription will end on {{dueDate}}",
"com.affine.payment.billing-setting.resume-subscription": "Resume",
"com.affine.payment.billing-setting.subtitle": "Manage your billing information and invoices.",
"com.affine.payment.billing-setting.title": "Billing",
Expand Down Expand Up @@ -870,6 +872,7 @@
"com.affine.payment.updated-notify-msg": "You have changed your plan to {{plan}} billing.",
"com.affine.payment.updated-notify-title": "Subscription updated",
"com.affine.payment.upgrade": "Upgrade",
"com.affine.payment.redeem-code": "Redeem code",
"com.affine.payment.upgrade-success-notify.content": "We'd like to hear more about your use case, so that we can make AFFiNE better.",
"com.affine.payment.upgrade-success-notify.later": "Later",
"com.affine.payment.upgrade-success-notify.ok-client": "Sure, open in browser",
Expand Down

0 comments on commit 29a3111

Please sign in to comment.