mirror of
https://github.com/toeverything/AFFiNE.git
synced 2025-01-07 04:07:37 +03:00
feat(core): adjust subscription related mixpanel (#7536)
This commit is contained in:
parent
549e7befed
commit
a714961b20
@ -104,7 +104,7 @@ export const AIUsagePanel = () => {
|
|||||||
>
|
>
|
||||||
{copilotActionLimit === 'unlimited' ? (
|
{copilotActionLimit === 'unlimited' ? (
|
||||||
hasPaymentFeature && aiSubscription?.canceledAt ? (
|
hasPaymentFeature && aiSubscription?.canceledAt ? (
|
||||||
<AIResume />
|
<AIResume module="billing subscription list" />
|
||||||
) : (
|
) : (
|
||||||
<Button onClick={openBilling}>
|
<Button onClick={openBilling}>
|
||||||
{t['com.affine.payment.ai.usage.change-button-label']()}
|
{t['com.affine.payment.ai.usage.change-button-label']()}
|
||||||
|
@ -32,7 +32,7 @@ import {
|
|||||||
import { useMutation } from '../../../../../hooks/use-mutation';
|
import { useMutation } from '../../../../../hooks/use-mutation';
|
||||||
import { useQuery } from '../../../../../hooks/use-query';
|
import { useQuery } from '../../../../../hooks/use-query';
|
||||||
import { AuthService, SubscriptionService } from '../../../../../modules/cloud';
|
import { AuthService, SubscriptionService } from '../../../../../modules/cloud';
|
||||||
import { mixpanel, popupWindow } from '../../../../../utils';
|
import { mixpanel, mixpanelTrack, popupWindow } from '../../../../../utils';
|
||||||
import { SWRErrorBoundary } from '../../../../pure/swr-error-bundary';
|
import { SWRErrorBoundary } from '../../../../pure/swr-error-bundary';
|
||||||
import { CancelAction, ResumeAction } from '../plans/actions';
|
import { CancelAction, ResumeAction } from '../plans/actions';
|
||||||
import { AICancel, AIResume, AISubscribe } from '../plans/ai/actions';
|
import { AICancel, AIResume, AISubscribe } from '../plans/ai/actions';
|
||||||
@ -237,19 +237,13 @@ const SubscriptionSettings = () => {
|
|||||||
</SettingRow>
|
</SettingRow>
|
||||||
) : (
|
) : (
|
||||||
<CancelAction
|
<CancelAction
|
||||||
|
module="billing subscription list"
|
||||||
open={openCancelModal}
|
open={openCancelModal}
|
||||||
onOpenChange={setOpenCancelModal}
|
onOpenChange={setOpenCancelModal}
|
||||||
>
|
>
|
||||||
<SettingRow
|
<SettingRow
|
||||||
style={{ cursor: 'pointer' }}
|
style={{ cursor: 'pointer' }}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
mixpanel.track('PlanChangeStarted', {
|
|
||||||
segment: 'settings panel',
|
|
||||||
module: 'billing subscription list',
|
|
||||||
control: 'plan cancel action',
|
|
||||||
type: proSubscription.plan,
|
|
||||||
category: proSubscription.recurring,
|
|
||||||
});
|
|
||||||
setOpenCancelModal(true);
|
setOpenCancelModal(true);
|
||||||
}}
|
}}
|
||||||
className="dangerous-setting"
|
className="dangerous-setting"
|
||||||
@ -403,9 +397,15 @@ const AIPlanCard = ({ onClick }: { onClick: () => void }) => {
|
|||||||
{price?.yearlyAmount ? (
|
{price?.yearlyAmount ? (
|
||||||
subscription ? (
|
subscription ? (
|
||||||
subscription.canceledAt ? (
|
subscription.canceledAt ? (
|
||||||
<AIResume className={styles.planAction} />
|
<AIResume
|
||||||
|
module="billing subscription list"
|
||||||
|
className={styles.planAction}
|
||||||
|
/>
|
||||||
) : (
|
) : (
|
||||||
<AICancel className={styles.planAction} />
|
<AICancel
|
||||||
|
module="billing subscription list"
|
||||||
|
className={styles.planAction}
|
||||||
|
/>
|
||||||
)
|
)
|
||||||
) : (
|
) : (
|
||||||
<AISubscribe className={styles.planAction}>
|
<AISubscribe className={styles.planAction}>
|
||||||
@ -476,13 +476,17 @@ const ResumeSubscription = () => {
|
|||||||
const subscription = useService(SubscriptionService).subscription;
|
const subscription = useService(SubscriptionService).subscription;
|
||||||
const handleClick = useCallback(() => {
|
const handleClick = useCallback(() => {
|
||||||
setOpen(true);
|
setOpen(true);
|
||||||
mixpanel.track('PlanChangeStarted', {
|
const type = subscription.pro$.value?.plan;
|
||||||
segment: 'settings panel',
|
const category = subscription.pro$.value?.recurring;
|
||||||
module: 'pricing plan list',
|
if (type && category) {
|
||||||
control: 'plan resume action',
|
mixpanelTrack('PlanChangeStarted', {
|
||||||
type: subscription.pro$.value?.plan,
|
segment: 'settings panel',
|
||||||
category: subscription.pro$.value?.recurring,
|
module: 'pricing plan list',
|
||||||
});
|
control: 'paying',
|
||||||
|
type,
|
||||||
|
category,
|
||||||
|
});
|
||||||
|
}
|
||||||
}, [subscription.pro$.value?.plan, subscription.pro$.value?.recurring]);
|
}, [subscription.pro$.value?.plan, subscription.pro$.value?.recurring]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
import { getDowngradeQuestionnaireLink } from '@affine/core/hooks/affine/use-subscription-notify';
|
import { getDowngradeQuestionnaireLink } from '@affine/core/hooks/affine/use-subscription-notify';
|
||||||
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 type { MixpanelEvents } from '@affine/core/mixpanel';
|
||||||
|
import { mixpanelTrack } from '@affine/core/utils';
|
||||||
import { SubscriptionPlan } from '@affine/graphql';
|
import { SubscriptionPlan } from '@affine/graphql';
|
||||||
import { useService } from '@toeverything/infra';
|
import { useLiveData, useService } from '@toeverything/infra';
|
||||||
import { nanoid } from 'nanoid';
|
import { nanoid } from 'nanoid';
|
||||||
import type { PropsWithChildren } from 'react';
|
import type { PropsWithChildren } from 'react';
|
||||||
import { useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
import { AuthService, SubscriptionService } from '../../../../../modules/cloud';
|
import { AuthService, SubscriptionService } from '../../../../../modules/cloud';
|
||||||
import { useDowngradeNotify } from '../../../subscription-landing/notify';
|
import { useDowngradeNotify } from '../../../subscription-landing/notify';
|
||||||
@ -20,16 +21,30 @@ export const CancelAction = ({
|
|||||||
children,
|
children,
|
||||||
open,
|
open,
|
||||||
onOpenChange,
|
onOpenChange,
|
||||||
|
module,
|
||||||
}: {
|
}: {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
onOpenChange: (open: boolean) => void;
|
onOpenChange: (open: boolean) => void;
|
||||||
|
module: MixpanelEvents['PlanChangeStarted']['module'];
|
||||||
} & PropsWithChildren) => {
|
} & PropsWithChildren) => {
|
||||||
const [idempotencyKey, setIdempotencyKey] = useState(nanoid());
|
const [idempotencyKey, setIdempotencyKey] = useState(nanoid());
|
||||||
const [isMutating, setIsMutating] = useState(false);
|
const [isMutating, setIsMutating] = useState(false);
|
||||||
const subscription = useService(SubscriptionService).subscription;
|
const subscription = useService(SubscriptionService).subscription;
|
||||||
|
const proSubscription = useLiveData(subscription.pro$);
|
||||||
const authService = useService(AuthService);
|
const authService = useService(AuthService);
|
||||||
const downgradeNotify = useDowngradeNotify();
|
const downgradeNotify = useDowngradeNotify();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!open || !proSubscription) return;
|
||||||
|
mixpanelTrack('PlanChangeStarted', {
|
||||||
|
segment: 'settings panel',
|
||||||
|
module,
|
||||||
|
control: 'cancel',
|
||||||
|
type: proSubscription.plan,
|
||||||
|
category: proSubscription.recurring,
|
||||||
|
});
|
||||||
|
}, [module, open, proSubscription]);
|
||||||
|
|
||||||
const downgrade = useAsyncCallback(async () => {
|
const downgrade = useAsyncCallback(async () => {
|
||||||
try {
|
try {
|
||||||
const account = authService.session.account$.value;
|
const account = authService.session.account$.value;
|
||||||
@ -41,13 +56,14 @@ export const CancelAction = ({
|
|||||||
// refresh idempotency key
|
// refresh idempotency key
|
||||||
setIdempotencyKey(nanoid());
|
setIdempotencyKey(nanoid());
|
||||||
onOpenChange(false);
|
onOpenChange(false);
|
||||||
mixpanel.track('ChangePlanSucceeded', {
|
const proSubscription = subscription.pro$.value;
|
||||||
segment: 'settings panel',
|
if (proSubscription) {
|
||||||
module: 'pricing plan list',
|
mixpanelTrack('PlanChangeSucceeded', {
|
||||||
control: 'plan cancel action',
|
control: 'cancel',
|
||||||
type: subscription.pro$.value?.plan,
|
type: proSubscription.plan,
|
||||||
category: subscription.pro$.value?.recurring,
|
category: proSubscription.recurring,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
if (account && prevRecurring) {
|
if (account && prevRecurring) {
|
||||||
downgradeNotify(
|
downgradeNotify(
|
||||||
getDowngradeQuestionnaireLink({
|
getDowngradeQuestionnaireLink({
|
||||||
@ -110,13 +126,14 @@ export const ResumeAction = ({
|
|||||||
// refresh idempotency key
|
// refresh idempotency key
|
||||||
setIdempotencyKey(nanoid());
|
setIdempotencyKey(nanoid());
|
||||||
onOpenChange(false);
|
onOpenChange(false);
|
||||||
mixpanel.track('ChangePlanSucceeded', {
|
const proSubscription = subscription.pro$.value;
|
||||||
segment: 'settings panel',
|
if (proSubscription) {
|
||||||
module: 'pricing plan list',
|
mixpanelTrack('PlanChangeSucceeded', {
|
||||||
control: 'plan resume action',
|
control: 'paying',
|
||||||
type: subscription.pro$.value?.plan,
|
type: proSubscription.plan,
|
||||||
category: subscription.pro$.value?.recurring,
|
category: proSubscription.recurring,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
setIsMutating(false);
|
setIsMutating(false);
|
||||||
}
|
}
|
||||||
|
@ -2,16 +2,19 @@ import { Button, type ButtonProps, useConfirmModal } from '@affine/component';
|
|||||||
import { useDowngradeNotify } from '@affine/core/components/affine/subscription-landing/notify';
|
import { useDowngradeNotify } from '@affine/core/components/affine/subscription-landing/notify';
|
||||||
import { getDowngradeQuestionnaireLink } from '@affine/core/hooks/affine/use-subscription-notify';
|
import { getDowngradeQuestionnaireLink } from '@affine/core/hooks/affine/use-subscription-notify';
|
||||||
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
||||||
|
import type { MixpanelEvents } from '@affine/core/mixpanel';
|
||||||
import { AuthService, SubscriptionService } from '@affine/core/modules/cloud';
|
import { AuthService, SubscriptionService } from '@affine/core/modules/cloud';
|
||||||
import { mixpanel } from '@affine/core/utils';
|
import { mixpanel, mixpanelTrack } 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';
|
||||||
import { nanoid } from 'nanoid';
|
import { nanoid } from 'nanoid';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
export interface AICancelProps extends ButtonProps {}
|
export interface AICancelProps extends ButtonProps {
|
||||||
export const AICancel = ({ ...btnProps }: AICancelProps) => {
|
module: MixpanelEvents['PlanChangeStarted']['module'];
|
||||||
|
}
|
||||||
|
export const AICancel = ({ module, ...btnProps }: AICancelProps) => {
|
||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
const [isMutating, setMutating] = useState(false);
|
const [isMutating, setMutating] = useState(false);
|
||||||
const [idempotencyKey, setIdempotencyKey] = useState(nanoid());
|
const [idempotencyKey, setIdempotencyKey] = useState(nanoid());
|
||||||
@ -22,12 +25,16 @@ export const AICancel = ({ ...btnProps }: AICancelProps) => {
|
|||||||
const downgradeNotify = useDowngradeNotify();
|
const downgradeNotify = useDowngradeNotify();
|
||||||
|
|
||||||
const cancel = useAsyncCallback(async () => {
|
const cancel = useAsyncCallback(async () => {
|
||||||
mixpanel.track('PlanChangeStarted', {
|
const aiSubscription = subscription.ai$.value;
|
||||||
segment: 'settings panel',
|
if (aiSubscription) {
|
||||||
control: 'plan cancel action',
|
mixpanelTrack('PlanChangeStarted', {
|
||||||
type: subscription.ai$.value?.plan,
|
module,
|
||||||
category: subscription.ai$.value?.recurring,
|
segment: 'settings panel',
|
||||||
});
|
control: 'cancel',
|
||||||
|
type: SubscriptionPlan.AI,
|
||||||
|
category: aiSubscription.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:
|
||||||
@ -51,9 +58,10 @@ export const AICancel = ({ ...btnProps }: AICancelProps) => {
|
|||||||
SubscriptionPlan.AI
|
SubscriptionPlan.AI
|
||||||
);
|
);
|
||||||
setIdempotencyKey(nanoid());
|
setIdempotencyKey(nanoid());
|
||||||
mixpanel.track('ChangePlanSucceeded', {
|
mixpanel.track('PlanChangeSucceeded', {
|
||||||
segment: 'settings panel',
|
segment: 'settings panel',
|
||||||
control: 'plan cancel action',
|
control: 'plan cancel action',
|
||||||
|
type: subscription.ai$.value?.plan,
|
||||||
});
|
});
|
||||||
const account = authService.session.account$.value;
|
const account = authService.session.account$.value;
|
||||||
const prevRecurring = subscription.ai$.value?.recurring;
|
const prevRecurring = subscription.ai$.value?.recurring;
|
||||||
@ -74,6 +82,7 @@ export const AICancel = ({ ...btnProps }: AICancelProps) => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}, [
|
}, [
|
||||||
|
module,
|
||||||
subscription,
|
subscription,
|
||||||
openConfirmModal,
|
openConfirmModal,
|
||||||
t,
|
t,
|
||||||
|
@ -5,8 +5,9 @@ import {
|
|||||||
useConfirmModal,
|
useConfirmModal,
|
||||||
} 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 type { MixpanelEvents } from '@affine/core/mixpanel';
|
||||||
import { SubscriptionService } from '@affine/core/modules/cloud';
|
import { SubscriptionService } from '@affine/core/modules/cloud';
|
||||||
import { mixpanel } from '@affine/core/utils';
|
import { mixpanelTrack } 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';
|
||||||
@ -15,9 +16,11 @@ import { cssVar } from '@toeverything/theme';
|
|||||||
import { nanoid } from 'nanoid';
|
import { nanoid } from 'nanoid';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
export interface AIResumeProps extends ButtonProps {}
|
export interface AIResumeProps extends ButtonProps {
|
||||||
|
module: MixpanelEvents['PlanChangeStarted']['module'];
|
||||||
|
}
|
||||||
|
|
||||||
export const AIResume = ({ ...btnProps }: AIResumeProps) => {
|
export const AIResume = ({ module, ...btnProps }: AIResumeProps) => {
|
||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
const [idempotencyKey, setIdempotencyKey] = useState(nanoid());
|
const [idempotencyKey, setIdempotencyKey] = useState(nanoid());
|
||||||
const subscription = useService(SubscriptionService).subscription;
|
const subscription = useService(SubscriptionService).subscription;
|
||||||
@ -27,12 +30,16 @@ export const AIResume = ({ ...btnProps }: AIResumeProps) => {
|
|||||||
const { openConfirmModal } = useConfirmModal();
|
const { openConfirmModal } = useConfirmModal();
|
||||||
|
|
||||||
const resume = useAsyncCallback(async () => {
|
const resume = useAsyncCallback(async () => {
|
||||||
mixpanel.track('PlanChangeStarted', {
|
const aiSubscription = subscription.ai$.value;
|
||||||
segment: 'settings panel',
|
if (aiSubscription) {
|
||||||
control: 'plan resume action',
|
mixpanelTrack('PlanChangeStarted', {
|
||||||
type: subscription.ai$.value?.plan,
|
module,
|
||||||
category: subscription.ai$.value?.recurring,
|
segment: 'settings panel',
|
||||||
});
|
control: 'paying',
|
||||||
|
type: aiSubscription.plan,
|
||||||
|
category: aiSubscription.recurring,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
openConfirmModal({
|
openConfirmModal({
|
||||||
title: t['com.affine.payment.ai.action.resume.confirm.title'](),
|
title: t['com.affine.payment.ai.action.resume.confirm.title'](),
|
||||||
@ -51,10 +58,13 @@ export const AIResume = ({ ...btnProps }: AIResumeProps) => {
|
|||||||
idempotencyKey,
|
idempotencyKey,
|
||||||
SubscriptionPlan.AI
|
SubscriptionPlan.AI
|
||||||
);
|
);
|
||||||
mixpanel.track('ChangePlanSucceeded', {
|
if (aiSubscription) {
|
||||||
segment: 'settings panel',
|
mixpanelTrack('PlanChangeSucceeded', {
|
||||||
control: 'plan resume action',
|
category: aiSubscription.recurring,
|
||||||
});
|
control: 'paying',
|
||||||
|
type: aiSubscription.plan,
|
||||||
|
});
|
||||||
|
}
|
||||||
notify({
|
notify({
|
||||||
icon: <SingleSelectSelectSolidIcon />,
|
icon: <SingleSelectSelectSolidIcon />,
|
||||||
iconColor: cssVar('processingColor'),
|
iconColor: cssVar('processingColor'),
|
||||||
@ -66,7 +76,7 @@ export const AIResume = ({ ...btnProps }: AIResumeProps) => {
|
|||||||
setIdempotencyKey(nanoid());
|
setIdempotencyKey(nanoid());
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}, [openConfirmModal, t, subscription, idempotencyKey]);
|
}, [subscription, openConfirmModal, t, module, idempotencyKey]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Button loading={isMutating} onClick={resume} type="primary" {...btnProps}>
|
<Button loading={isMutating} onClick={resume} type="primary" {...btnProps}>
|
||||||
|
@ -53,9 +53,15 @@ export const AIPlan = () => {
|
|||||||
isLoggedIn ? (
|
isLoggedIn ? (
|
||||||
subscription ? (
|
subscription ? (
|
||||||
subscription.canceledAt ? (
|
subscription.canceledAt ? (
|
||||||
<AIResume className={styles.purchaseButton} />
|
<AIResume
|
||||||
|
module="pricing plan list"
|
||||||
|
className={styles.purchaseButton}
|
||||||
|
/>
|
||||||
) : (
|
) : (
|
||||||
<AICancel className={styles.purchaseButton} />
|
<AICancel
|
||||||
|
module="pricing plan list"
|
||||||
|
className={styles.purchaseButton}
|
||||||
|
/>
|
||||||
)
|
)
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
|
@ -3,7 +3,7 @@ import { Tooltip } from '@affine/component/ui/tooltip';
|
|||||||
import { generateSubscriptionCallbackLink } from '@affine/core/hooks/affine/use-subscription-notify';
|
import { generateSubscriptionCallbackLink } from '@affine/core/hooks/affine/use-subscription-notify';
|
||||||
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
||||||
import { AuthService, SubscriptionService } from '@affine/core/modules/cloud';
|
import { AuthService, SubscriptionService } from '@affine/core/modules/cloud';
|
||||||
import { popupWindow } from '@affine/core/utils';
|
import { mixpanelTrack, popupWindow } from '@affine/core/utils';
|
||||||
import type { SubscriptionRecurring } from '@affine/graphql';
|
import type { SubscriptionRecurring } from '@affine/graphql';
|
||||||
import { SubscriptionPlan, SubscriptionStatus } from '@affine/graphql';
|
import { SubscriptionPlan, SubscriptionStatus } from '@affine/graphql';
|
||||||
import { Trans, useI18n } from '@affine/i18n';
|
import { Trans, useI18n } from '@affine/i18n';
|
||||||
@ -179,7 +179,6 @@ 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']()
|
||||||
@ -187,17 +186,10 @@ const Downgrade = ({ disabled }: { disabled?: boolean }) => {
|
|||||||
|
|
||||||
const handleClick = useCallback(() => {
|
const handleClick = useCallback(() => {
|
||||||
setOpen(true);
|
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 module="pricing plan list" open={open} onOpenChange={setOpen}>
|
||||||
<Tooltip content={tooltipContent} rootOptions={{ delayDuration: 0 }}>
|
<Tooltip content={tooltipContent} rootOptions={{ delayDuration: 0 }}>
|
||||||
<div className={styles.planAction}>
|
<div className={styles.planAction}>
|
||||||
<Button
|
<Button
|
||||||
@ -345,11 +337,11 @@ const ChangeRecurring = ({
|
|||||||
const subscription = useService(SubscriptionService).subscription;
|
const subscription = useService(SubscriptionService).subscription;
|
||||||
|
|
||||||
const onStartChange = useCallback(() => {
|
const onStartChange = useCallback(() => {
|
||||||
mixpanel.track('PlanChangeStarted', {
|
mixpanelTrack('PlanChangeStarted', {
|
||||||
segment: 'settings panel',
|
segment: 'settings panel',
|
||||||
module: 'pricing plan list',
|
module: 'pricing plan list',
|
||||||
control: 'plan resume action',
|
control: 'paying',
|
||||||
type: 'cloud pro subscription',
|
type: SubscriptionPlan.Pro,
|
||||||
category: to,
|
category: to,
|
||||||
});
|
});
|
||||||
setOpen(true);
|
setOpen(true);
|
||||||
@ -428,14 +420,17 @@ const ResumeButton = () => {
|
|||||||
|
|
||||||
const handleClick = useCallback(() => {
|
const handleClick = useCallback(() => {
|
||||||
setOpen(true);
|
setOpen(true);
|
||||||
mixpanel.track('PlanChangeStarted', {
|
const pro = subscription.pro$.value;
|
||||||
segment: 'settings panel',
|
if (pro) {
|
||||||
module: 'pricing plan list',
|
mixpanelTrack('PlanChangeStarted', {
|
||||||
control: 'pricing plan action',
|
segment: 'settings panel',
|
||||||
type: 'cloud pro subscription',
|
module: 'pricing plan list',
|
||||||
category: subscription.pro$.value?.recurring,
|
control: 'paying',
|
||||||
});
|
type: SubscriptionPlan.Pro,
|
||||||
}, [subscription.pro$.value?.recurring]);
|
category: pro.recurring,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [subscription.pro$.value]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ResumeAction open={open} onOpenChange={setOpen}>
|
<ResumeAction open={open} onOpenChange={setOpen}>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { useUpgradeNotify } from '@affine/core/components/affine/subscription-landing/notify';
|
import { useUpgradeNotify } from '@affine/core/components/affine/subscription-landing/notify';
|
||||||
|
import { mixpanelTrack } from '@affine/core/utils';
|
||||||
import { SubscriptionPlan, SubscriptionRecurring } from '@affine/graphql';
|
import { SubscriptionPlan, SubscriptionRecurring } from '@affine/graphql';
|
||||||
import mixpanel from 'mixpanel-browser';
|
|
||||||
import { nanoid } from 'nanoid';
|
import { nanoid } from 'nanoid';
|
||||||
import { useCallback, useEffect } from 'react';
|
import { useCallback, useEffect } from 'react';
|
||||||
import { useSearchParams } from 'react-router-dom';
|
import { useSearchParams } from 'react-router-dom';
|
||||||
@ -130,10 +130,10 @@ export const useSubscriptionNotifyReader = () => {
|
|||||||
localStorage.removeItem(localStorageKey);
|
localStorage.removeItem(localStorageKey);
|
||||||
|
|
||||||
// mixpanel
|
// mixpanel
|
||||||
mixpanel.track('PlanUpgradeSucceeded', {
|
mixpanelTrack('PlanChangeSucceeded', {
|
||||||
segment: 'settings panel',
|
category: recurring,
|
||||||
control: 'plan upgrade action',
|
type: plan,
|
||||||
plan: plan,
|
control: 'new subscription',
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Failed to parse subscription callback link', err);
|
console.error('Failed to parse subscription callback link', err);
|
||||||
|
7
packages/frontend/core/src/mixpanel/index.ts
Normal file
7
packages/frontend/core/src/mixpanel/index.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import type { PlanChangeStartedEvent } from './plan-change-started';
|
||||||
|
import type { PlanChangeSucceededEvent } from './plan-change-succeed';
|
||||||
|
|
||||||
|
export interface MixpanelEvents {
|
||||||
|
PlanChangeStarted: PlanChangeStartedEvent;
|
||||||
|
PlanChangeSucceeded: PlanChangeSucceededEvent;
|
||||||
|
}
|
15
packages/frontend/core/src/mixpanel/plan-change-started.ts
Normal file
15
packages/frontend/core/src/mixpanel/plan-change-started.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import type { SubscriptionPlan, SubscriptionRecurring } from '@affine/graphql';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Before subscription plan changed
|
||||||
|
*/
|
||||||
|
export interface PlanChangeStartedEvent {
|
||||||
|
segment: 'settings panel';
|
||||||
|
module: 'pricing plan list' | 'billing subscription list';
|
||||||
|
control:
|
||||||
|
| 'new subscription' // no subscription before
|
||||||
|
| 'cancel'
|
||||||
|
| 'paying'; // resume: subscribed before
|
||||||
|
type: SubscriptionPlan;
|
||||||
|
category: SubscriptionRecurring;
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
import type { PlanChangeStartedEvent } from './plan-change-started';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subscription plan changed successfully
|
||||||
|
*/
|
||||||
|
export type PlanChangeSucceededEvent = Pick<
|
||||||
|
PlanChangeStartedEvent,
|
||||||
|
'control' | 'type' | 'category'
|
||||||
|
>;
|
@ -83,10 +83,6 @@ export const Component = () => {
|
|||||||
});
|
});
|
||||||
setMessage('Redirecting...');
|
setMessage('Redirecting...');
|
||||||
location.href = checkout;
|
location.href = checkout;
|
||||||
mixpanel.track('PlanChangeSucceeded', {
|
|
||||||
type: plan,
|
|
||||||
category: recurring,
|
|
||||||
});
|
|
||||||
if (plan) {
|
if (plan) {
|
||||||
mixpanel.people.set({
|
mixpanel.people.set({
|
||||||
[SubscriptionPlan.AI === plan ? 'ai plan' : plan]: plan,
|
[SubscriptionPlan.AI === plan ? 'ai plan' : plan]: plan,
|
||||||
|
@ -2,6 +2,8 @@ import { DebugLogger } from '@affine/debug';
|
|||||||
import type { OverridedMixpanel } from 'mixpanel-browser';
|
import type { OverridedMixpanel } from 'mixpanel-browser';
|
||||||
import mixpanelBrowser from 'mixpanel-browser';
|
import mixpanelBrowser from 'mixpanel-browser';
|
||||||
|
|
||||||
|
import type { MixpanelEvents } from '../mixpanel';
|
||||||
|
|
||||||
const logger = new DebugLogger('affine:mixpanel');
|
const logger = new DebugLogger('affine:mixpanel');
|
||||||
|
|
||||||
export const mixpanel = process.env.MIXPANEL_TOKEN
|
export const mixpanel = process.env.MIXPANEL_TOKEN
|
||||||
@ -31,3 +33,10 @@ function createProxyHandler(property?: string | symbol) {
|
|||||||
} as ProxyHandler<OverridedMixpanel>;
|
} as ProxyHandler<OverridedMixpanel>;
|
||||||
return handler;
|
return handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function mixpanelTrack<T extends keyof MixpanelEvents>(
|
||||||
|
event: T,
|
||||||
|
properties?: MixpanelEvents[T]
|
||||||
|
) {
|
||||||
|
return mixpanel.track(event, properties);
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user