diff --git a/packages/frontend/core/src/components/affine/setting-modal/general-setting/billing/index.tsx b/packages/frontend/core/src/components/affine/setting-modal/general-setting/billing/index.tsx
index 011f0d27b..f7992269e 100644
--- a/packages/frontend/core/src/components/affine/setting-modal/general-setting/billing/index.tsx
+++ b/packages/frontend/core/src/components/affine/setting-modal/general-setting/billing/index.tsx
@@ -7,6 +7,7 @@ import {
} from '@affine/component/setting-components';
import { Button, IconButton } from '@affine/component/ui/button';
import { Loading } from '@affine/component/ui/loading';
+import { getUpgradeQuestionnaireLink } from '@affine/core/hooks/affine/use-subscription-notify';
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
import type { InvoicesQuery } from '@affine/graphql';
import {
@@ -30,7 +31,7 @@ import {
} from '../../../../../atoms';
import { useMutation } from '../../../../../hooks/use-mutation';
import { useQuery } from '../../../../../hooks/use-query';
-import { SubscriptionService } from '../../../../../modules/cloud';
+import { AuthService, SubscriptionService } from '../../../../../modules/cloud';
import { mixpanel, popupWindow } from '../../../../../utils';
import { SWRErrorBoundary } from '../../../../pure/swr-error-bundary';
import { CancelAction, ResumeAction } from '../plans/actions';
@@ -194,6 +195,8 @@ const SubscriptionSettings = () => {
)}
+
+
{proSubscription !== null ? (
proSubscription?.status === SubscriptionStatus.Active && (
<>
@@ -269,6 +272,45 @@ const SubscriptionSettings = () => {
);
};
+const TypeFormLink = () => {
+ const t = useI18n();
+ const subscriptionService = useService(SubscriptionService);
+ const authService = useService(AuthService);
+
+ const pro = useLiveData(subscriptionService.subscription.pro$);
+ const ai = useLiveData(subscriptionService.subscription.ai$);
+ const account = useLiveData(authService.session.account$);
+
+ if (!account) return null;
+ if (!pro && !ai) return null;
+
+ const plan = [];
+ if (pro) plan.push(SubscriptionPlan.Pro);
+ if (ai) plan.push(SubscriptionPlan.AI);
+
+ const link = getUpgradeQuestionnaireLink({
+ name: account.info?.name,
+ id: account.id,
+ email: account.email,
+ recurring: pro?.recurring ?? ai?.recurring ?? SubscriptionRecurring.Yearly,
+ plan,
+ });
+
+ return (
+
+
+
+
+
+ );
+};
+
const BelieverIdentifier = ({ onOpenPlans }: { onOpenPlans?: () => void }) => {
const t = useI18n();
const subscriptionService = useService(SubscriptionService);
diff --git a/packages/frontend/core/src/components/affine/setting-modal/general-setting/plans/actions.tsx b/packages/frontend/core/src/components/affine/setting-modal/general-setting/plans/actions.tsx
index 6d58ed354..1184aab4c 100644
--- a/packages/frontend/core/src/components/affine/setting-modal/general-setting/plans/actions.tsx
+++ b/packages/frontend/core/src/components/affine/setting-modal/general-setting/plans/actions.tsx
@@ -1,11 +1,14 @@
+import { getDowngradeQuestionnaireLink } from '@affine/core/hooks/affine/use-subscription-notify';
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
import { mixpanel } from '@affine/core/utils';
+import { SubscriptionPlan } from '@affine/graphql';
import { useService } from '@toeverything/infra';
import { nanoid } from 'nanoid';
import type { PropsWithChildren } from 'react';
import { useState } from 'react';
-import { SubscriptionService } from '../../../../../modules/cloud';
+import { AuthService, SubscriptionService } from '../../../../../modules/cloud';
+import { useDowngradeNotify } from '../../../subscription-landing/notify';
import { ConfirmLoadingModal, DowngradeModal } from './modals';
/**
@@ -24,9 +27,13 @@ export const CancelAction = ({
const [idempotencyKey, setIdempotencyKey] = useState(nanoid());
const [isMutating, setIsMutating] = useState(false);
const subscription = useService(SubscriptionService).subscription;
+ const authService = useService(AuthService);
+ const downgradeNotify = useDowngradeNotify();
const downgrade = useAsyncCallback(async () => {
try {
+ const account = authService.session.account$.value;
+ const prevRecurring = subscription.pro$.value?.recurring;
setIsMutating(true);
await subscription.cancelSubscription(idempotencyKey);
subscription.revalidate();
@@ -41,10 +48,27 @@ export const CancelAction = ({
type: subscription.pro$.value?.plan,
category: subscription.pro$.value?.recurring,
});
+ if (account && prevRecurring) {
+ downgradeNotify(
+ getDowngradeQuestionnaireLink({
+ email: account.email ?? '',
+ id: account.id,
+ name: account.info?.name ?? '',
+ plan: SubscriptionPlan.Pro,
+ recurring: prevRecurring,
+ })
+ );
+ }
} finally {
setIsMutating(false);
}
- }, [subscription, idempotencyKey, onOpenChange]);
+ }, [
+ authService.session.account$.value,
+ subscription,
+ idempotencyKey,
+ onOpenChange,
+ downgradeNotify,
+ ]);
return (
<>
diff --git a/packages/frontend/core/src/components/affine/setting-modal/general-setting/plans/ai/actions/cancel.tsx b/packages/frontend/core/src/components/affine/setting-modal/general-setting/plans/ai/actions/cancel.tsx
index 9bcbb5e98..87bc77f94 100644
--- a/packages/frontend/core/src/components/affine/setting-modal/general-setting/plans/ai/actions/cancel.tsx
+++ b/packages/frontend/core/src/components/affine/setting-modal/general-setting/plans/ai/actions/cancel.tsx
@@ -1,6 +1,8 @@
import { Button, type ButtonProps, useConfirmModal } from '@affine/component';
+import { useDowngradeNotify } from '@affine/core/components/affine/subscription-landing/notify';
+import { getDowngradeQuestionnaireLink } from '@affine/core/hooks/affine/use-subscription-notify';
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
-import { SubscriptionService } from '@affine/core/modules/cloud';
+import { AuthService, SubscriptionService } from '@affine/core/modules/cloud';
import { mixpanel } from '@affine/core/utils';
import { SubscriptionPlan } from '@affine/graphql';
import { useI18n } from '@affine/i18n';
@@ -14,8 +16,10 @@ export const AICancel = ({ ...btnProps }: AICancelProps) => {
const [isMutating, setMutating] = useState(false);
const [idempotencyKey, setIdempotencyKey] = useState(nanoid());
const subscription = useService(SubscriptionService).subscription;
+ const authService = useService(AuthService);
const { openConfirmModal } = useConfirmModal();
+ const downgradeNotify = useDowngradeNotify();
const cancel = useAsyncCallback(async () => {
mixpanel.track('PlanChangeStarted', {
@@ -51,12 +55,32 @@ export const AICancel = ({ ...btnProps }: AICancelProps) => {
segment: 'settings panel',
control: 'plan cancel action',
});
+ const account = authService.session.account$.value;
+ const prevRecurring = subscription.ai$.value?.recurring;
+ if (account && prevRecurring) {
+ downgradeNotify(
+ getDowngradeQuestionnaireLink({
+ email: account.email,
+ name: account.info?.name,
+ id: account.id,
+ plan: SubscriptionPlan.AI,
+ recurring: prevRecurring,
+ })
+ );
+ }
} finally {
setMutating(false);
}
},
});
- }, [openConfirmModal, t, subscription, idempotencyKey]);
+ }, [
+ subscription,
+ openConfirmModal,
+ t,
+ idempotencyKey,
+ authService.session.account$.value,
+ downgradeNotify,
+ ]);
return (