@@ -224,7 +224,16 @@ const SubscriptionSettings = () => {
|
|||||||
>
|
>
|
||||||
<SettingRow
|
<SettingRow
|
||||||
style={{ cursor: 'pointer' }}
|
style={{ cursor: 'pointer' }}
|
||||||
onClick={() => setOpenCancelModal(true)}
|
onClick={() => {
|
||||||
|
mixpanel.track('PlanChangeStarted', {
|
||||||
|
segment: 'settings panel',
|
||||||
|
module: 'billing subscription list',
|
||||||
|
control: 'plan cancel action',
|
||||||
|
type: proSubscription.plan,
|
||||||
|
category: proSubscription.recurring,
|
||||||
|
});
|
||||||
|
setOpenCancelModal(true);
|
||||||
|
}}
|
||||||
className="dangerous-setting"
|
className="dangerous-setting"
|
||||||
name={t[
|
name={t[
|
||||||
'com.affine.payment.billing-setting.cancel-subscription'
|
'com.affine.payment.billing-setting.cancel-subscription'
|
||||||
@@ -369,10 +378,21 @@ const PaymentMethodUpdater = () => {
|
|||||||
const ResumeSubscription = () => {
|
const ResumeSubscription = () => {
|
||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
|
const subscription = useService(SubscriptionService).subscription;
|
||||||
|
const handleClick = useCallback(() => {
|
||||||
|
setOpen(true);
|
||||||
|
mixpanel.track('PlanChangeStarted', {
|
||||||
|
segment: 'settings panel',
|
||||||
|
module: 'pricing plan list',
|
||||||
|
control: 'plan resume action',
|
||||||
|
type: subscription.pro$.value?.plan,
|
||||||
|
category: subscription.pro$.value?.recurring,
|
||||||
|
});
|
||||||
|
}, [subscription.pro$.value?.plan, subscription.pro$.value?.recurring]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ResumeAction open={open} onOpenChange={setOpen}>
|
<ResumeAction open={open} onOpenChange={setOpen}>
|
||||||
<Button className={styles.button} onClick={() => setOpen(true)}>
|
<Button className={styles.button} onClick={handleClick}>
|
||||||
{t['com.affine.payment.billing-setting.resume-subscription']()}
|
{t['com.affine.payment.billing-setting.resume-subscription']()}
|
||||||
</Button>
|
</Button>
|
||||||
</ResumeAction>
|
</ResumeAction>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
||||||
|
import { mixpanel } from '@affine/core/utils';
|
||||||
import { useService } from '@toeverything/infra';
|
import { useService } from '@toeverything/infra';
|
||||||
import { nanoid } from 'nanoid';
|
import { nanoid } from 'nanoid';
|
||||||
import type { PropsWithChildren } from 'react';
|
import type { PropsWithChildren } from 'react';
|
||||||
@@ -33,6 +34,13 @@ export const CancelAction = ({
|
|||||||
// refresh idempotency key
|
// refresh idempotency key
|
||||||
setIdempotencyKey(nanoid());
|
setIdempotencyKey(nanoid());
|
||||||
onOpenChange(false);
|
onOpenChange(false);
|
||||||
|
mixpanel.track('ChangePlanSucceeded', {
|
||||||
|
segment: 'settings panel',
|
||||||
|
module: 'pricing plan list',
|
||||||
|
control: 'plan cancel action',
|
||||||
|
type: subscription.pro$.value?.plan,
|
||||||
|
category: subscription.pro$.value?.recurring,
|
||||||
|
});
|
||||||
} finally {
|
} finally {
|
||||||
setIsMutating(false);
|
setIsMutating(false);
|
||||||
}
|
}
|
||||||
@@ -78,6 +86,13 @@ export const ResumeAction = ({
|
|||||||
// refresh idempotency key
|
// refresh idempotency key
|
||||||
setIdempotencyKey(nanoid());
|
setIdempotencyKey(nanoid());
|
||||||
onOpenChange(false);
|
onOpenChange(false);
|
||||||
|
mixpanel.track('ChangePlanSucceeded', {
|
||||||
|
segment: 'settings panel',
|
||||||
|
module: 'pricing plan list',
|
||||||
|
control: 'plan resume action',
|
||||||
|
type: subscription.pro$.value?.plan,
|
||||||
|
category: subscription.pro$.value?.recurring,
|
||||||
|
});
|
||||||
} finally {
|
} finally {
|
||||||
setIsMutating(false);
|
setIsMutating(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { Button, type ButtonProps, useConfirmModal } from '@affine/component';
|
import { Button, type ButtonProps, useConfirmModal } from '@affine/component';
|
||||||
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
||||||
import { SubscriptionService } from '@affine/core/modules/cloud';
|
import { SubscriptionService } from '@affine/core/modules/cloud';
|
||||||
|
import { mixpanel } from '@affine/core/utils';
|
||||||
import { SubscriptionPlan } from '@affine/graphql';
|
import { SubscriptionPlan } from '@affine/graphql';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import { useService } from '@toeverything/infra';
|
import { useService } from '@toeverything/infra';
|
||||||
@@ -17,6 +18,12 @@ export const AICancel = ({ ...btnProps }: AICancelProps) => {
|
|||||||
const { openConfirmModal } = useConfirmModal();
|
const { openConfirmModal } = useConfirmModal();
|
||||||
|
|
||||||
const cancel = useAsyncCallback(async () => {
|
const cancel = useAsyncCallback(async () => {
|
||||||
|
mixpanel.track('PlanChangeStarted', {
|
||||||
|
segment: 'settings panel',
|
||||||
|
control: 'plan cancel action',
|
||||||
|
type: subscription.ai$.value?.plan,
|
||||||
|
category: subscription.ai$.value?.recurring,
|
||||||
|
});
|
||||||
openConfirmModal({
|
openConfirmModal({
|
||||||
title: t['com.affine.payment.ai.action.cancel.confirm.title'](),
|
title: t['com.affine.payment.ai.action.cancel.confirm.title'](),
|
||||||
description:
|
description:
|
||||||
@@ -40,6 +47,10 @@ export const AICancel = ({ ...btnProps }: AICancelProps) => {
|
|||||||
SubscriptionPlan.AI
|
SubscriptionPlan.AI
|
||||||
);
|
);
|
||||||
setIdempotencyKey(nanoid());
|
setIdempotencyKey(nanoid());
|
||||||
|
mixpanel.track('ChangePlanSucceeded', {
|
||||||
|
segment: 'settings panel',
|
||||||
|
control: 'plan cancel action',
|
||||||
|
});
|
||||||
} finally {
|
} finally {
|
||||||
setMutating(false);
|
setMutating(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
} from '@affine/component';
|
} from '@affine/component';
|
||||||
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
||||||
import { SubscriptionService } from '@affine/core/modules/cloud';
|
import { SubscriptionService } from '@affine/core/modules/cloud';
|
||||||
|
import { mixpanel } from '@affine/core/utils';
|
||||||
import { SubscriptionPlan } from '@affine/graphql';
|
import { SubscriptionPlan } from '@affine/graphql';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import { SingleSelectSelectSolidIcon } from '@blocksuite/icons/rc';
|
import { SingleSelectSelectSolidIcon } from '@blocksuite/icons/rc';
|
||||||
@@ -26,6 +27,13 @@ export const AIResume = ({ ...btnProps }: AIResumeProps) => {
|
|||||||
const { openConfirmModal } = useConfirmModal();
|
const { openConfirmModal } = useConfirmModal();
|
||||||
|
|
||||||
const resume = useAsyncCallback(async () => {
|
const resume = useAsyncCallback(async () => {
|
||||||
|
mixpanel.track('PlanChangeStarted', {
|
||||||
|
segment: 'settings panel',
|
||||||
|
control: 'plan resume action',
|
||||||
|
type: subscription.ai$.value?.plan,
|
||||||
|
category: subscription.ai$.value?.recurring,
|
||||||
|
});
|
||||||
|
|
||||||
openConfirmModal({
|
openConfirmModal({
|
||||||
title: t['com.affine.payment.ai.action.resume.confirm.title'](),
|
title: t['com.affine.payment.ai.action.resume.confirm.title'](),
|
||||||
description:
|
description:
|
||||||
@@ -43,6 +51,10 @@ export const AIResume = ({ ...btnProps }: AIResumeProps) => {
|
|||||||
idempotencyKey,
|
idempotencyKey,
|
||||||
SubscriptionPlan.AI
|
SubscriptionPlan.AI
|
||||||
);
|
);
|
||||||
|
mixpanel.track('ChangePlanSucceeded', {
|
||||||
|
segment: 'settings panel',
|
||||||
|
control: 'plan resume action',
|
||||||
|
});
|
||||||
notify({
|
notify({
|
||||||
icon: <SingleSelectSelectSolidIcon />,
|
icon: <SingleSelectSelectSolidIcon />,
|
||||||
iconColor: cssVar('processingColor'),
|
iconColor: cssVar('processingColor'),
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ export const AISubscribe = ({ ...btnProps }: AISubscribeProps) => {
|
|||||||
|
|
||||||
const subscribe = useAsyncCallback(async () => {
|
const subscribe = useAsyncCallback(async () => {
|
||||||
setMutating(true);
|
setMutating(true);
|
||||||
mixpanel.track('plan upgrade started', {
|
mixpanel.track('PlanUpgradeStarted', {
|
||||||
category: SubscriptionRecurring.Yearly,
|
category: SubscriptionRecurring.Yearly,
|
||||||
type: SubscriptionPlan.AI,
|
type: SubscriptionPlan.AI,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -167,11 +167,23 @@ const CurrentPlan = () => {
|
|||||||
const Downgrade = ({ disabled }: { disabled?: boolean }) => {
|
const Downgrade = ({ disabled }: { disabled?: boolean }) => {
|
||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
|
const subscription = useService(SubscriptionService).subscription;
|
||||||
|
|
||||||
const tooltipContent = disabled
|
const tooltipContent = disabled
|
||||||
? t['com.affine.payment.downgraded-tooltip']()
|
? t['com.affine.payment.downgraded-tooltip']()
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
|
const handleClick = useCallback(() => {
|
||||||
|
setOpen(true);
|
||||||
|
mixpanel.track('PlanChangeStarted', {
|
||||||
|
segment: 'settings panel',
|
||||||
|
module: 'pricing plan list',
|
||||||
|
control: 'billing cancel action',
|
||||||
|
type: subscription.pro$.value?.plan,
|
||||||
|
category: subscription.pro$.value?.recurring,
|
||||||
|
});
|
||||||
|
}, [subscription.pro$.value?.plan, subscription.pro$.value?.recurring]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CancelAction open={open} onOpenChange={setOpen}>
|
<CancelAction open={open} onOpenChange={setOpen}>
|
||||||
<Tooltip content={tooltipContent} rootOptions={{ delayDuration: 0 }}>
|
<Tooltip content={tooltipContent} rootOptions={{ delayDuration: 0 }}>
|
||||||
@@ -179,7 +191,7 @@ const Downgrade = ({ disabled }: { disabled?: boolean }) => {
|
|||||||
<Button
|
<Button
|
||||||
className={styles.planAction}
|
className={styles.planAction}
|
||||||
type="primary"
|
type="primary"
|
||||||
onClick={() => setOpen(true)}
|
onClick={handleClick}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
>
|
>
|
||||||
{t['com.affine.payment.downgrade']()}
|
{t['com.affine.payment.downgrade']()}
|
||||||
@@ -257,6 +269,13 @@ const Upgrade = ({ recurring }: { recurring: SubscriptionRecurring }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setMutating(true);
|
setMutating(true);
|
||||||
|
mixpanel.track('PlanUpgradeStarted', {
|
||||||
|
segment: 'settings panel',
|
||||||
|
module: 'pricing plan list',
|
||||||
|
control: 'pricing plan action',
|
||||||
|
type: 'cloud pro subscription',
|
||||||
|
category: recurring,
|
||||||
|
});
|
||||||
const link = await subscriptionService.createCheckoutSession({
|
const link = await subscriptionService.createCheckoutSession({
|
||||||
recurring,
|
recurring,
|
||||||
idempotencyKey,
|
idempotencyKey,
|
||||||
@@ -268,7 +287,6 @@ const Upgrade = ({ recurring }: { recurring: SubscriptionRecurring }) => {
|
|||||||
setIdempotencyKey(nanoid());
|
setIdempotencyKey(nanoid());
|
||||||
popupWindow(link);
|
popupWindow(link);
|
||||||
setOpenedExternalWindow(true);
|
setOpenedExternalWindow(true);
|
||||||
mixpanel.track_forms('Subscription', SubscriptionPlan.Pro);
|
|
||||||
}, [openPaymentDisableModal, subscriptionService, recurring, idempotencyKey]);
|
}, [openPaymentDisableModal, subscriptionService, recurring, idempotencyKey]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -302,6 +320,17 @@ const ChangeRecurring = ({
|
|||||||
const [idempotencyKey, setIdempotencyKey] = useState(nanoid());
|
const [idempotencyKey, setIdempotencyKey] = useState(nanoid());
|
||||||
const subscription = useService(SubscriptionService).subscription;
|
const subscription = useService(SubscriptionService).subscription;
|
||||||
|
|
||||||
|
const onStartChange = useCallback(() => {
|
||||||
|
mixpanel.track('PlanChangeStarted', {
|
||||||
|
segment: 'settings panel',
|
||||||
|
module: 'pricing plan list',
|
||||||
|
control: 'plan resume action',
|
||||||
|
type: 'cloud pro subscription',
|
||||||
|
category: to,
|
||||||
|
});
|
||||||
|
setOpen(true);
|
||||||
|
}, [to]);
|
||||||
|
|
||||||
const change = useAsyncCallback(async () => {
|
const change = useAsyncCallback(async () => {
|
||||||
setIsMutating(true);
|
setIsMutating(true);
|
||||||
await subscription.setSubscriptionRecurring(idempotencyKey, to);
|
await subscription.setSubscriptionRecurring(idempotencyKey, to);
|
||||||
@@ -327,7 +356,7 @@ const ChangeRecurring = ({
|
|||||||
<Button
|
<Button
|
||||||
className={styles.planAction}
|
className={styles.planAction}
|
||||||
type="primary"
|
type="primary"
|
||||||
onClick={() => setOpen(true)}
|
onClick={onStartChange}
|
||||||
disabled={disabled || isMutating}
|
disabled={disabled || isMutating}
|
||||||
loading={isMutating}
|
loading={isMutating}
|
||||||
>
|
>
|
||||||
@@ -371,6 +400,18 @@ const ResumeButton = () => {
|
|||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [hovered, setHovered] = useState(false);
|
const [hovered, setHovered] = useState(false);
|
||||||
|
const subscription = useService(SubscriptionService).subscription;
|
||||||
|
|
||||||
|
const handleClick = useCallback(() => {
|
||||||
|
setOpen(true);
|
||||||
|
mixpanel.track('PlanChangeStarted', {
|
||||||
|
segment: 'settings panel',
|
||||||
|
module: 'pricing plan list',
|
||||||
|
control: 'pricing plan action',
|
||||||
|
type: 'cloud pro subscription',
|
||||||
|
category: subscription.pro$.value?.recurring,
|
||||||
|
});
|
||||||
|
}, [subscription.pro$.value?.recurring]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ResumeAction open={open} onOpenChange={setOpen}>
|
<ResumeAction open={open} onOpenChange={setOpen}>
|
||||||
@@ -378,7 +419,7 @@ const ResumeButton = () => {
|
|||||||
className={styles.planAction}
|
className={styles.planAction}
|
||||||
onMouseEnter={() => setHovered(true)}
|
onMouseEnter={() => setHovered(true)}
|
||||||
onMouseLeave={() => setHovered(false)}
|
onMouseLeave={() => setHovered(false)}
|
||||||
onClick={() => setOpen(true)}
|
onClick={handleClick}
|
||||||
>
|
>
|
||||||
{hovered
|
{hovered
|
||||||
? t['com.affine.payment.resume-renewal']()
|
? t['com.affine.payment.resume-renewal']()
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
import { AuthPageContainer } from '@affine/component/auth-components';
|
import { AuthPageContainer } from '@affine/component/auth-components';
|
||||||
import { Button } from '@affine/component/ui/button';
|
import { Button } from '@affine/component/ui/button';
|
||||||
import { useNavigateHelper } from '@affine/core/hooks/use-navigate-helper';
|
import { useNavigateHelper } from '@affine/core/hooks/use-navigate-helper';
|
||||||
|
import { SubscriptionPlan } from '@affine/graphql';
|
||||||
import { Trans, useI18n } from '@affine/i18n';
|
import { Trans, useI18n } from '@affine/i18n';
|
||||||
import { type ReactNode, useCallback } from 'react';
|
import mixpanel from 'mixpanel-browser';
|
||||||
|
import { type ReactNode, useCallback, useEffect } from 'react';
|
||||||
import { useSearchParams } from 'react-router-dom';
|
import { useSearchParams } from 'react-router-dom';
|
||||||
|
|
||||||
import * as styles from './styles.css';
|
import * as styles from './styles.css';
|
||||||
@@ -56,6 +58,13 @@ const UpgradeSuccessLayout = ({
|
|||||||
|
|
||||||
export const CloudUpgradeSuccess = () => {
|
export const CloudUpgradeSuccess = () => {
|
||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
|
useEffect(() => {
|
||||||
|
mixpanel.track('PlanUpgradeSucceeded', {
|
||||||
|
segment: 'settings panel',
|
||||||
|
control: 'plan upgrade action',
|
||||||
|
plan: SubscriptionPlan.Pro,
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
return (
|
return (
|
||||||
<UpgradeSuccessLayout
|
<UpgradeSuccessLayout
|
||||||
title={t['com.affine.payment.upgrade-success-page.title']()}
|
title={t['com.affine.payment.upgrade-success-page.title']()}
|
||||||
@@ -66,7 +75,13 @@ export const CloudUpgradeSuccess = () => {
|
|||||||
|
|
||||||
export const AIUpgradeSuccess = () => {
|
export const AIUpgradeSuccess = () => {
|
||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
|
useEffect(() => {
|
||||||
|
mixpanel.track('PlanUpgradeSucceeded', {
|
||||||
|
segment: 'settings panel',
|
||||||
|
control: 'plan upgrade action',
|
||||||
|
plan: SubscriptionPlan.Pro,
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
return (
|
return (
|
||||||
<UpgradeSuccessLayout
|
<UpgradeSuccessLayout
|
||||||
title={t['com.affine.payment.ai-upgrade-success-page.title']()}
|
title={t['com.affine.payment.ai-upgrade-success-page.title']()}
|
||||||
|
|||||||
@@ -53,6 +53,10 @@ export const Component = () => {
|
|||||||
: !!subscriptionService.subscription.pro$.value;
|
: !!subscriptionService.subscription.pro$.value;
|
||||||
if (!subscribed) {
|
if (!subscribed) {
|
||||||
setMessage('Creating checkout...');
|
setMessage('Creating checkout...');
|
||||||
|
mixpanel.track('PlanUpgradeStarted', {
|
||||||
|
type: plan,
|
||||||
|
category: recurring,
|
||||||
|
});
|
||||||
try {
|
try {
|
||||||
const checkout = await subscriptionService.createCheckoutSession({
|
const checkout = await subscriptionService.createCheckoutSession({
|
||||||
idempotencyKey,
|
idempotencyKey,
|
||||||
|
|||||||
Reference in New Issue
Block a user