chore(core): replace with new track impl (#7735)

This commit is contained in:
liuyi
2024-08-06 17:15:15 +08:00
committed by GitHub
parent 7373e174db
commit d0f1bb24fd
59 changed files with 647 additions and 821 deletions

View File

@@ -6,7 +6,7 @@ import type { createStore } from 'jotai';
import { openSettingModalAtom, openWorkspaceListModalAtom } from '../atoms';
import type { useNavigateHelper } from '../hooks/use-navigate-helper';
import { mixpanel, track } from '../mixpanel';
import { track } from '../mixpanel';
import { registerAffineCommand } from './registry';
export function registerAffineNavigationCommands({
@@ -93,10 +93,7 @@ export function registerAffineNavigationCommands({
icon: <ArrowRightBigIcon />,
label: t['com.affine.cmdk.affine.navigation.open-account-settings'](),
run() {
mixpanel.track('AccountSettingsViewed', {
// page:
segment: 'cmdk',
});
track.$.cmdk.settings.openSettings({ to: 'account' });
store.set(openSettingModalAtom, s => ({
activeTab: 'account',
open: !s.open,

View File

@@ -1,6 +1,6 @@
import { Button, FlexWrapper, notify } from '@affine/component';
import { openSettingModalAtom } from '@affine/core/atoms';
import { mixpanel } from '@affine/core/mixpanel';
import { track } from '@affine/core/mixpanel';
import { SubscriptionService } from '@affine/core/modules/cloud';
import { useI18n } from '@affine/i18n';
import { AiIcon } from '@blocksuite/icons/rc';
@@ -65,11 +65,7 @@ export const AIOnboardingEdgeless = () => {
const mode = useLiveData(doc.mode$);
const goToPricingPlans = useCallback(() => {
mixpanel.track('PlansViewed', {
page: 'whiteboard editor',
segment: 'ai onboarding',
module: 'whiteboard dialog',
});
track.$.aiOnboarding.dialog.viewPlans();
setSettingModal({
open: true,
activeTab: 'plans',

View File

@@ -1,7 +1,7 @@
import { Button, IconButton, Modal } from '@affine/component';
import { openSettingModalAtom } from '@affine/core/atoms';
import { useBlurRoot } from '@affine/core/hooks/use-blur-root';
import { mixpanel } from '@affine/core/mixpanel';
import { track } from '@affine/core/mixpanel';
import { AuthService, SubscriptionService } from '@affine/core/modules/cloud';
import { Trans, useI18n } from '@affine/i18n';
import { ArrowLeftSmallIcon } from '@blocksuite/icons/rc';
@@ -116,11 +116,7 @@ export const AIOnboardingGeneral = () => {
activeTab: 'plans',
scrollAnchor: 'aiPricingPlan',
});
mixpanel.track('PlansViewed', {
page: 'whiteboard editor',
segment: 'ai onboarding',
module: 'general',
});
track.$.aiOnboarding.dialog.viewPlans();
closeAndDismiss();
}, [closeAndDismiss, setSettingModal]);
const onPrev = useCallback(() => {

View File

@@ -1,7 +1,7 @@
import { notify, Skeleton } from '@affine/component';
import { Button } from '@affine/component/ui/button';
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
import { mixpanel } from '@affine/core/mixpanel';
import { track } from '@affine/core/mixpanel';
import { OAuthProviderType } from '@affine/graphql';
import { GithubIcon, GoogleDuotoneIcon } from '@blocksuite/icons/rc';
import { useLiveData, useService } from '@toeverything/infra';
@@ -75,7 +75,7 @@ function OAuthProvider({
notify.error({ title: 'Failed to sign in, please try again.' });
} finally {
setIsConnecting(false);
mixpanel.track('OAuth', { provider });
track.$.$.auth.oauth({ provider });
}
}, [authService, provider, redirectUri]);

View File

@@ -3,7 +3,7 @@ import { AuthInput, ModalHeader } from '@affine/component/auth-components';
import { Button } from '@affine/component/ui/button';
import { authAtom } from '@affine/core/atoms';
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
import { mixpanel } from '@affine/core/mixpanel';
import { track } from '@affine/core/mixpanel';
import { Trans, useI18n } from '@affine/i18n';
import { ArrowRightBigIcon } from '@blocksuite/icons/rc';
import { useLiveData, useService } from '@toeverything/infra';
@@ -75,9 +75,7 @@ export const SignIn: FC<AuthPanelProps> = ({
if (hasPassword) {
setAuthState('signInWithPassword');
} else {
mixpanel.track('SignIn', {
email,
});
track.$.$.auth.signIn();
await authService.sendEmailMagicLink(
email,
verifyToken,
@@ -93,9 +91,7 @@ export const SignIn: FC<AuthPanelProps> = ({
challenge,
searchParams.get('redirect_uri')
);
mixpanel.track('SignUp', {
email,
});
track.$.$.auth.signUp();
setAuthState('afterSignUpSendEmail');
}
}

View File

@@ -1,5 +1,4 @@
import { Tooltip } from '@affine/component/ui/tooltip';
import { mixpanel } from '@affine/core/mixpanel';
import { SubscriptionPlan } from '@affine/graphql';
import { useI18n } from '@affine/i18n';
import { useLiveData, useServices } from '@toeverything/infra';
@@ -44,10 +43,6 @@ export const UserPlanButton = () => {
activeTab: 'plans',
scrollAnchor: 'cloudPricingPlan',
});
mixpanel.track('PlansViewed', {
segment: 'settings panel',
module: 'profile and badge',
});
},
[setSettingModalAtom]
);
@@ -72,6 +67,7 @@ export const UserPlanButton = () => {
data-is-believer={isBeliever ? 'true' : undefined}
className={styles.userPlanButton}
onClick={handleClick}
data-event-props="$.settingsPanel.profileAndBadge.viewPlans"
>
{planLabel}
</div>

View File

@@ -3,7 +3,7 @@ import type { ConfirmModalProps } from '@affine/component/ui/modal';
import { ConfirmModal, Modal } from '@affine/component/ui/modal';
import { authAtom } from '@affine/core/atoms';
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
import { mixpanel } from '@affine/core/mixpanel';
import { track } from '@affine/core/mixpanel';
import { DebugLogger } from '@affine/debug';
import { apis } from '@affine/electron-api';
import { WorkspaceFlavour } from '@affine/env/workspace';
@@ -222,9 +222,7 @@ export const CreateWorkspaceModal = ({
const onConfirmName = useAsyncCallback(
async (name: string, workspaceFlavour: WorkspaceFlavour) => {
mixpanel.track('CreateWorkspace', {
workspaceFlavour,
});
track.$.$.$.createWorkspace({ flavour: workspaceFlavour });
if (loading) return;
setLoading(true);

View File

@@ -4,7 +4,7 @@ import { Button, IconButton } from '@affine/component/ui/button';
import { Modal, useConfirmModal } from '@affine/component/ui/modal';
import { openSettingModalAtom } from '@affine/core/atoms';
import { useDocCollectionPageTitle } from '@affine/core/hooks/use-block-suite-workspace-page-title';
import { mixpanel } from '@affine/core/mixpanel';
import { track } from '@affine/core/mixpanel';
import { WorkspacePermissionService } from '@affine/core/modules/permissions';
import { WorkspaceQuotaService } from '@affine/core/modules/quota';
import { i18nTime, Trans, useI18n } from '@affine/i18n';
@@ -108,18 +108,17 @@ const HistoryEditorPreview = ({
mode,
title,
}: HistoryEditorPreviewProps) => {
const onSwitchToPageMode = useCallback(() => {
mixpanel.track('Button', {
resolve: 'HistorySwitchToPageMode',
});
onModeChange('page');
}, [onModeChange]);
const onSwitchToEdgelessMode = useCallback(() => {
mixpanel.track('Button', {
resolve: 'HistorySwitchToEdgelessMode',
});
onModeChange('edgeless');
}, [onModeChange]);
const { onSwitchToPageMode, onSwitchToEdgelessMode } = useMemo(
() => ({
onSwitchToPageMode: () => {
onModeChange('page');
},
onSwitchToEdgelessMode: () => {
onModeChange('edgeless');
},
}),
[onModeChange]
);
const content = useMemo(() => {
return (
@@ -129,11 +128,15 @@ const HistoryEditorPreview = ({
<PageSwitchItem
data-testid="switch-page-mode-button"
active={mode === 'page'}
data-event-props="$.docHistory.$.switchPageMode"
data-event-args-type="page"
onClick={onSwitchToPageMode}
/>
<EdgelessSwitchItem
data-testid="switch-edgeless-mode-button"
active={mode === 'edgeless'}
data-event-props="$.docHistory.$.switchPageMode"
data-event-args-type="edgeless"
onClick={onSwitchToEdgelessMode}
/>
</StyledEditorModeSwitch>
@@ -229,9 +232,7 @@ const PlanPrompt = () => {
activeTab: 'plans',
scrollAnchor: 'cloudPricingPlan',
});
mixpanel.track('PlansViewed', {
segment: 'doc history',
});
track.$.docHistory.$.viewPlans();
}, [setSettingModalAtom]);
const t = useI18n();
@@ -562,9 +563,7 @@ export const GlobalPageHistoryModal = () => {
const workspace = useService(WorkspaceService).workspace;
const handleOpenChange = useCallback(
(open: boolean) => {
mixpanel.track('Button', {
resolve: open ? 'OpenPageHistoryModal' : 'ClosePageHistoryModal',
});
track.$.docHistory.$[open ? 'open' : 'close']();
setState(prev => ({
...prev,
open,

View File

@@ -1,6 +1,6 @@
import { ConfirmModal } from '@affine/component/ui/modal';
import { openQuotaModalAtom, openSettingModalAtom } from '@affine/core/atoms';
import { mixpanel } from '@affine/core/mixpanel';
import { track } from '@affine/core/mixpanel';
import { UserQuotaService } from '@affine/core/modules/cloud';
import { WorkspacePermissionService } from '@affine/core/modules/permissions';
import { WorkspaceQuotaService } from '@affine/core/modules/quota';
@@ -50,11 +50,7 @@ export const CloudQuotaModal = () => {
scrollAnchor: 'cloudPricingPlan',
});
mixpanel.track('PlansViewed', {
segment: 'payment wall',
category: 'payment wall storage',
});
track.$.paywall.storage.viewPlans();
setOpen(false);
}, [setOpen, setSettingModalAtom]);

View File

@@ -1,12 +1,13 @@
import { Button, ErrorMessage, Skeleton } from '@affine/component';
import { SettingRow } from '@affine/component/setting-components';
import { openSettingModalAtom } from '@affine/core/atoms';
import { mixpanel } from '@affine/core/mixpanel';
import { track } from '@affine/core/mixpanel';
import {
ServerConfigService,
SubscriptionService,
UserCopilotQuotaService,
} from '@affine/core/modules/cloud';
import { SubscriptionPlan } from '@affine/graphql';
import { useI18n } from '@affine/i18n';
import { useLiveData, useService } from '@toeverything/infra';
import { cssVar } from '@toeverything/theme';
@@ -47,12 +48,7 @@ export const AIUsagePanel = () => {
open: true,
activeTab: 'billing',
});
mixpanel.track('BillingViewed', {
segment: 'settings panel',
module: 'account usage list',
control: 'change plan button',
type: 'ai subscription',
});
track.$.settingsPanel.accountUsage.viewPlans({ plan: SubscriptionPlan.AI });
}, [setOpenSettingModal]);
if (loading) {
@@ -104,7 +100,7 @@ export const AIUsagePanel = () => {
>
{copilotActionLimit === 'unlimited' ? (
hasPaymentFeature && aiSubscription?.canceledAt ? (
<AIResume module="billing subscription list" />
<AIResume />
) : (
<Button onClick={openBilling}>
{t['com.affine.payment.ai.usage.change-button-label']()}

View File

@@ -6,7 +6,8 @@ import {
import { Avatar } from '@affine/component/ui/avatar';
import { Button } from '@affine/component/ui/button';
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
import { mixpanel } from '@affine/core/mixpanel';
import { track } from '@affine/core/mixpanel';
import { SubscriptionPlan } from '@affine/graphql';
import { useI18n } from '@affine/i18n';
import { ArrowRightSmallIcon, CameraIcon } from '@blocksuite/icons/rc';
import {
@@ -38,9 +39,7 @@ export const UserAvatar = () => {
const handleUpdateUserAvatar = useAsyncCallback(
async (file: File) => {
try {
mixpanel.track('UploadAvatar', {
userId: account.id,
});
track.$.settingsPanel.accountSettings.uploadAvatar();
await session.uploadAvatar(file);
notify.success({ title: 'Update user avatar success' });
} catch (e) {
@@ -51,18 +50,16 @@ export const UserAvatar = () => {
});
}
},
[account, session]
[session]
);
const handleRemoveUserAvatar = useAsyncCallback(
async (e: MouseEvent<HTMLButtonElement>) => {
mixpanel.track('RemoveAvatar', {
userId: account.id,
});
track.$.settingsPanel.accountSettings.removeAvatar();
e.stopPropagation();
await session.removeAvatar();
},
[account, session]
[session]
);
return (
@@ -104,9 +101,7 @@ export const AvatarAndName = () => {
}
try {
mixpanel.track('UpdateUsername', {
userId: account.id,
});
track.$.settingsPanel.accountSettings.updateUserName();
await session.updateLabel(input);
} catch (e) {
notify.error({
@@ -161,11 +156,8 @@ const StoragePanel = () => {
const setSettingModalAtom = useSetAtom(openSettingModalAtom);
const onUpgrade = useCallback(() => {
mixpanel.track('PlansViewed', {
segment: 'settings panel',
module: 'account usage list',
control: 'cloud storage upgrade button',
type: 'cloud subscription',
track.$.settingsPanel.accountUsage.viewPlans({
plan: SubscriptionPlan.Pro,
});
setSettingModalAtom({
open: true,

View File

@@ -9,7 +9,7 @@ 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 { mixpanel } from '@affine/core/mixpanel';
import { track } from '@affine/core/mixpanel';
import type { InvoicesQuery } from '@affine/graphql';
import {
createCustomerPortalMutation,
@@ -112,22 +112,14 @@ const SubscriptionSettings = () => {
const openPlans = useCallback(
(scrollAnchor?: PlansScrollAnchor) => {
mixpanel.track('PlansViewed', {
type: proSubscription?.plan,
category: proSubscription?.recurring,
// page:
segment: 'settings panel',
module: 'billing subscription list',
control: 'change plan button',
});
track.$.settingsPanel.billing.viewPlans();
setOpenSettingModalAtom({
open: true,
activeTab: 'plans',
scrollAnchor: scrollAnchor,
});
},
[proSubscription?.plan, proSubscription?.recurring, setOpenSettingModalAtom]
[setOpenSettingModalAtom]
);
const gotoCloudPlansSetting = useCallback(
() => openPlans('cloudPricingPlan'),
@@ -238,7 +230,6 @@ const SubscriptionSettings = () => {
</SettingRow>
) : (
<CancelAction
module="billing subscription list"
open={openCancelModal}
onOpenChange={setOpenCancelModal}
>
@@ -396,15 +387,9 @@ const AIPlanCard = ({ onClick }: { onClick: () => void }) => {
{price?.yearlyAmount ? (
subscription ? (
subscription.canceledAt ? (
<AIResume
module="billing subscription list"
className={styles.planAction}
/>
<AIResume className={styles.planAction} />
) : (
<AICancel
module="billing subscription list"
className={styles.planAction}
/>
<AICancel className={styles.planAction} />
)
) : (
<AISubscribe className={styles.planAction}>
@@ -470,22 +455,16 @@ const ResumeSubscription = () => {
const subscription = useService(SubscriptionService).subscription;
const handleClick = useCallback(() => {
setOpen(true);
const type = subscription.pro$.value?.plan;
const category = subscription.pro$.value?.recurring;
if (type && category) {
mixpanel.track('PlanChangeStarted', {
segment: 'settings panel',
module: 'pricing plan list',
control: 'paying',
type,
category,
});
}
}, [subscription.pro$.value?.plan, subscription.pro$.value?.recurring]);
}, []);
return (
<ResumeAction open={open} onOpenChange={setOpen}>
<Button onClick={handleClick}>
<Button
onClick={handleClick}
data-event-props="$.settingsPanel.plans.resumeSubscription"
data-event-args-type={subscription.pro$.value?.plan}
data-event-args-category={subscription.pro$.value?.recurring}
>
{t['com.affine.payment.billing-setting.resume-subscription']()}
</Button>
</ResumeAction>

View File

@@ -1,6 +1,6 @@
import { getDowngradeQuestionnaireLink } from '@affine/core/hooks/affine/use-subscription-notify';
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
import { mixpanel } from '@affine/core/mixpanel';
import { track } from '@affine/core/mixpanel';
import { SubscriptionPlan } from '@affine/graphql';
import { useLiveData, useService } from '@toeverything/infra';
import { nanoid } from 'nanoid';
@@ -20,11 +20,9 @@ export const CancelAction = ({
children,
open,
onOpenChange,
module,
}: {
open: boolean;
onOpenChange: (open: boolean) => void;
module: string;
} & PropsWithChildren) => {
const [idempotencyKey, setIdempotencyKey] = useState(nanoid());
const [isMutating, setIsMutating] = useState(false);
@@ -35,14 +33,11 @@ export const CancelAction = ({
useEffect(() => {
if (!open || !proSubscription) return;
mixpanel.track('PlanChangeStarted', {
segment: 'settings panel',
module,
control: 'cancel',
type: proSubscription.plan,
category: proSubscription.recurring,
track.$.settingsPanel.plans.cancelSubscription({
plan: proSubscription.plan,
recurring: proSubscription.recurring,
});
}, [module, open, proSubscription]);
}, [open, proSubscription]);
const downgrade = useAsyncCallback(async () => {
try {
@@ -57,10 +52,9 @@ export const CancelAction = ({
onOpenChange(false);
const proSubscription = subscription.pro$.value;
if (proSubscription) {
mixpanel.track('PlanChangeSucceeded', {
control: 'cancel',
type: proSubscription.plan,
category: proSubscription.recurring,
track.$.settingsPanel.plans.confirmCancelingSubscription({
plan: proSubscription.plan,
recurring: proSubscription.recurring,
});
}
if (account && prevRecurring) {
@@ -127,10 +121,9 @@ export const ResumeAction = ({
onOpenChange(false);
const proSubscription = subscription.pro$.value;
if (proSubscription) {
mixpanel.track('PlanChangeSucceeded', {
control: 'paying',
type: proSubscription.plan,
category: proSubscription.recurring,
track.$.settingsPanel.plans.confirmResumingSubscription({
plan: proSubscription.plan,
recurring: proSubscription.recurring,
});
}
} finally {

View File

@@ -2,7 +2,7 @@ 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 { mixpanel } from '@affine/core/mixpanel';
import { track } from '@affine/core/mixpanel';
import { AuthService, SubscriptionService } from '@affine/core/modules/cloud';
import { SubscriptionPlan } from '@affine/graphql';
import { useI18n } from '@affine/i18n';
@@ -10,10 +10,7 @@ import { useService } from '@toeverything/infra';
import { nanoid } from 'nanoid';
import { useState } from 'react';
export interface AICancelProps extends ButtonProps {
module: string;
}
export const AICancel = ({ module, ...btnProps }: AICancelProps) => {
export const AICancel = (btnProps: ButtonProps) => {
const t = useI18n();
const [isMutating, setMutating] = useState(false);
const [idempotencyKey, setIdempotencyKey] = useState(nanoid());
@@ -26,12 +23,9 @@ export const AICancel = ({ module, ...btnProps }: AICancelProps) => {
const cancel = useAsyncCallback(async () => {
const aiSubscription = subscription.ai$.value;
if (aiSubscription) {
mixpanel.track('PlanChangeStarted', {
module,
segment: 'settings panel',
control: 'cancel',
type: SubscriptionPlan.AI,
category: aiSubscription.recurring,
track.$.settingsPanel.plans.cancelSubscription({
plan: SubscriptionPlan.AI,
recurring: aiSubscription.recurring,
});
}
openConfirmModal({
@@ -57,10 +51,9 @@ export const AICancel = ({ module, ...btnProps }: AICancelProps) => {
SubscriptionPlan.AI
);
setIdempotencyKey(nanoid());
mixpanel.track('PlanChangeSucceeded', {
segment: 'settings panel',
control: 'plan cancel action',
type: subscription.ai$.value?.plan,
track.$.settingsPanel.plans.confirmCancelingSubscription({
plan: SubscriptionPlan.AI,
recurring: aiSubscription?.recurring,
});
const account = authService.session.account$.value;
const prevRecurring = subscription.ai$.value?.recurring;
@@ -81,7 +74,6 @@ export const AICancel = ({ module, ...btnProps }: AICancelProps) => {
},
});
}, [
module,
subscription,
openConfirmModal,
t,

View File

@@ -5,7 +5,7 @@ import {
useConfirmModal,
} from '@affine/component';
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
import { mixpanel } from '@affine/core/mixpanel';
import { track } from '@affine/core/mixpanel';
import { SubscriptionService } from '@affine/core/modules/cloud';
import { SubscriptionPlan } from '@affine/graphql';
import { useI18n } from '@affine/i18n';
@@ -15,11 +15,7 @@ import { cssVar } from '@toeverything/theme';
import { nanoid } from 'nanoid';
import { useState } from 'react';
export interface AIResumeProps extends ButtonProps {
module: string;
}
export const AIResume = ({ module, ...btnProps }: AIResumeProps) => {
export const AIResume = (btnProps: ButtonProps) => {
const t = useI18n();
const [idempotencyKey, setIdempotencyKey] = useState(nanoid());
const subscription = useService(SubscriptionService).subscription;
@@ -31,12 +27,9 @@ export const AIResume = ({ module, ...btnProps }: AIResumeProps) => {
const resume = useAsyncCallback(async () => {
const aiSubscription = subscription.ai$.value;
if (aiSubscription) {
mixpanel.track('PlanChangeStarted', {
module,
segment: 'settings panel',
control: 'paying',
type: aiSubscription.plan,
category: aiSubscription.recurring,
track.$.settingsPanel.plans.resumeSubscription({
plan: SubscriptionPlan.AI,
recurring: aiSubscription.recurring,
});
}
@@ -58,10 +51,9 @@ export const AIResume = ({ module, ...btnProps }: AIResumeProps) => {
SubscriptionPlan.AI
);
if (aiSubscription) {
mixpanel.track('PlanChangeSucceeded', {
category: aiSubscription.recurring,
control: 'paying',
type: aiSubscription.plan,
track.$.settingsPanel.plans.confirmResumingSubscription({
plan: aiSubscription.plan,
recurring: aiSubscription.recurring,
});
}
notify({
@@ -75,7 +67,7 @@ export const AIResume = ({ module, ...btnProps }: AIResumeProps) => {
setIdempotencyKey(nanoid());
},
});
}, [subscription, openConfirmModal, t, module, idempotencyKey]);
}, [subscription, openConfirmModal, t, idempotencyKey]);
return (
<Button

View File

@@ -1,7 +1,7 @@
import { Button, type ButtonProps, Skeleton } from '@affine/component';
import { generateSubscriptionCallbackLink } from '@affine/core/hooks/affine/use-subscription-notify';
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
import { mixpanel } from '@affine/core/mixpanel';
import { track } from '@affine/core/mixpanel';
import { AuthService, SubscriptionService } from '@affine/core/modules/cloud';
import { popupWindow } from '@affine/core/utils';
import { SubscriptionPlan, SubscriptionRecurring } from '@affine/graphql';
@@ -50,9 +50,9 @@ export const AISubscribe = ({
const subscribe = useAsyncCallback(async () => {
setMutating(true);
mixpanel.track('PlanUpgradeStarted', {
category: SubscriptionRecurring.Yearly,
type: SubscriptionPlan.AI,
track.$.settingsPanel.plans.checkout({
plan: SubscriptionPlan.AI,
recurring: SubscriptionRecurring.Yearly,
});
try {
const session = await subscriptionService.createCheckoutSession({

View File

@@ -53,15 +53,9 @@ export const AIPlan = () => {
isLoggedIn ? (
subscription ? (
subscription.canceledAt ? (
<AIResume
module="pricing plan list"
className={styles.purchaseButton}
/>
<AIResume className={styles.purchaseButton} />
) : (
<AICancel
module="pricing plan list"
className={styles.purchaseButton}
/>
<AICancel className={styles.purchaseButton} />
)
) : (
<>

View File

@@ -2,7 +2,7 @@ import { Button, type ButtonProps } from '@affine/component/ui/button';
import { Tooltip } from '@affine/component/ui/tooltip';
import { generateSubscriptionCallbackLink } from '@affine/core/hooks/affine/use-subscription-notify';
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
import { mixpanel } from '@affine/core/mixpanel';
import { track } from '@affine/core/mixpanel';
import { AuthService, SubscriptionService } from '@affine/core/modules/cloud';
import { popupWindow } from '@affine/core/utils';
import type { SubscriptionRecurring } from '@affine/graphql';
@@ -190,7 +190,7 @@ const Downgrade = ({ disabled }: { disabled?: boolean }) => {
}, []);
return (
<CancelAction module="pricing plan list" open={open} onOpenChange={setOpen}>
<CancelAction open={open} onOpenChange={setOpen}>
<Tooltip content={tooltipContent} rootOptions={{ delayDuration: 0 }}>
<div className={styles.planAction}>
<Button
@@ -226,14 +226,13 @@ const BookDemo = ({ plan }: { plan: SubscriptionPlan }) => {
href={url}
target="_blank"
rel="noreferrer"
onClick={() => {
mixpanel.track('Button', {
resolve: 'BookDemo',
url,
});
}}
>
<Button className={styles.planAction} variant="primary">
<Button
className={styles.planAction}
variant="primary"
data-event-props="$.settingsPanel.billing.bookDemo"
data-event-args-url={url}
>
{t['com.affine.payment.tell-us-use-case']()}
</Button>
</a>
@@ -276,12 +275,9 @@ export const Upgrade = ({
const upgrade = useAsyncCallback(async () => {
setMutating(true);
mixpanel.track('PlanUpgradeStarted', {
segment: 'settings panel',
module: 'pricing plan list',
control: 'pricing plan action',
type: 'cloud pro subscription',
category: recurring,
track.$.settingsPanel.plans.checkout({
plan: SubscriptionPlan.Pro,
recurring: recurring,
});
const link = await subscriptionService.createCheckoutSession({
recurring,
@@ -338,12 +334,9 @@ const ChangeRecurring = ({
const subscription = useService(SubscriptionService).subscription;
const onStartChange = useCallback(() => {
mixpanel.track('PlanChangeStarted', {
segment: 'settings panel',
module: 'pricing plan list',
control: 'paying',
type: SubscriptionPlan.Pro,
category: to,
track.$.settingsPanel.plans.changeSubscriptionRecurring({
plan: SubscriptionPlan.Pro,
recurring: to,
});
setOpen(true);
}, [to]);
@@ -422,12 +415,9 @@ const ResumeButton = () => {
setOpen(true);
const pro = subscription.pro$.value;
if (pro) {
mixpanel.track('PlanChangeStarted', {
segment: 'settings panel',
module: 'pricing plan list',
control: 'paying',
type: SubscriptionPlan.Pro,
category: pro.recurring,
track.$.settingsPanel.plans.resumeSubscription({
plan: SubscriptionPlan.Pro,
recurring: pro.recurring,
});
}
}, [subscription.pro$.value]);

View File

@@ -6,7 +6,7 @@ import { Avatar } from '@affine/component/ui/avatar';
import { Tooltip } from '@affine/component/ui/tooltip';
import { WorkspaceAvatar } from '@affine/component/workspace-avatar';
import { useWorkspaceInfo } from '@affine/core/hooks/use-workspace-info';
import { mixpanel } from '@affine/core/mixpanel';
import { track } from '@affine/core/mixpanel';
import { AuthService } from '@affine/core/modules/cloud';
import { UserFeatureService } from '@affine/core/modules/cloud/services/user-feature';
import { UNTITLED_WORKSPACE_NAME } from '@affine/env/constant';
@@ -22,7 +22,13 @@ import {
} from '@toeverything/infra';
import clsx from 'clsx';
import { useAtom } from 'jotai/react';
import { Suspense, useCallback, useEffect, useMemo } from 'react';
import {
type MouseEvent,
Suspense,
useCallback,
useEffect,
useMemo,
} from 'react';
import { authAtom } from '../../../../atoms';
import { UserPlanButton } from '../../auth/user-plan-button';
@@ -115,29 +121,30 @@ export const SettingSidebar = ({
const t = useI18n();
const loginStatus = useLiveData(useService(AuthService).session.status$);
const generalList = useGeneralSettingList();
const gotoTab = useCallback(
(e: MouseEvent<HTMLDivElement>) => {
const tab = e.currentTarget.dataset.eventArg;
if (!tab) return;
track.$.settingsPanel.menu.openSettings({ to: tab });
onTabChange(tab as ActiveTab, null);
},
[onTabChange]
);
const onAccountSettingClick = useCallback(() => {
mixpanel.track('AccountSettingsViewed', {
// page:
segment: 'settings panel',
module: 'settings menu',
control: 'menu item',
});
track.$.settingsPanel.menu.openSettings({ to: 'account' });
onTabChange('account', null);
}, [onTabChange]);
const onWorkspaceSettingClick = useCallback(
(subTab: WorkspaceSubTab, workspaceMetadata: WorkspaceMetadata) => {
mixpanel.track(`view workspace setting`, {
// page:
segment: 'settings panel',
module: 'settings menu',
control: 'menu item',
type: subTab,
workspaceId: workspaceMetadata.id,
track.$.settingsPanel.menu.openSettings({
to: 'workspace',
control: subTab,
});
onTabChange(`workspace:${subTab}`, workspaceMetadata);
},
[onTabChange]
);
return (
<div className={style.settingSlideBar} data-testid="settings-sidebar">
<div className={style.sidebarTitle}>
@@ -155,24 +162,8 @@ export const SettingSidebar = ({
})}
key={key}
title={title}
onClick={() => {
if (key === 'billing') {
mixpanel.track('BillingViewed', {
// page:
segment: 'settings panel',
module: 'settings menu',
control: 'menu item',
});
} else if (key === 'plans') {
mixpanel.track('PlansViewed', {
// page:
segment: 'settings panel',
module: 'settings menu',
control: 'menu item',
});
}
onTabChange(key, null);
}}
data-event-arg={key}
onClick={gotoTab}
data-testid={testId}
>
{icon({ className: 'icon' })}

View File

@@ -21,7 +21,7 @@ import { useMemberCount } from '@affine/core/hooks/affine/use-member-count';
import type { Member } from '@affine/core/hooks/affine/use-members';
import { useMembers } from '@affine/core/hooks/affine/use-members';
import { useRevokeMemberPermission } from '@affine/core/hooks/affine/use-revoke-member-permission';
import { mixpanel } from '@affine/core/mixpanel';
import { track } from '@affine/core/mixpanel';
import { WorkspacePermissionService } from '@affine/core/modules/permissions';
import { WorkspaceQuotaService } from '@affine/core/modules/quota';
import { WorkspaceFlavour } from '@affine/env/workspace';
@@ -146,11 +146,8 @@ export const CloudWorkspaceMembersPanel = () => {
activeTab: 'plans',
scrollAnchor: 'cloudPricingPlan',
});
mixpanel.track('PlansViewed', {
// page:
segment: 'settings panel',
module: 'workspace setting',
control: 'invite member',
track.$.settingsPanel.workspace.viewPlans({
control: 'inviteMember',
});
}, [setSettingModalAtom]);

View File

@@ -4,7 +4,7 @@ import { Button } from '@affine/component/ui/button';
import { Menu, MenuItem, MenuTrigger } from '@affine/component/ui/menu';
import { useSharingUrl } from '@affine/core/hooks/affine/use-share-url';
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
import { mixpanel } from '@affine/core/mixpanel';
import { track } from '@affine/core/mixpanel';
import { ServerConfigService } from '@affine/core/modules/cloud';
import { ShareService } from '@affine/core/modules/share-doc';
import { WorkspaceFlavour } from '@affine/env/workspace';
@@ -106,11 +106,8 @@ export const AffineSharePage = (props: ShareMenuProps) => {
await shareService.share.enableShare(
mode === 'edgeless' ? PublicPageMode.Edgeless : PublicPageMode.Page
);
mixpanel.track('ShareCreated', {
segment: 'sharing panel',
module: 'public share',
control: 'share panel',
type: mode,
track.$.header.share.createShareLink({
mode,
});
notify.success({
title:

View File

@@ -1,4 +1,4 @@
import { mixpanel } from '@affine/core/mixpanel';
import { track } from '@affine/core/mixpanel';
import { CloseIcon, DownloadIcon } from '@blocksuite/icons/rc';
import clsx from 'clsx';
import { useCallback, useState } from 'react';
@@ -21,9 +21,7 @@ export function AppDownloadButton({
// TODO(@JimmFly): unify this type of literal value.
const handleClick = useCallback(() => {
mixpanel.track('Button', {
resolve: 'GoToDownloadAppPage',
});
track.$.navigationPanel.bottomButtons.downloadApp();
const url = `https://affine.pro/download?channel=stable`;
open(url, '_blank');
}, []);

View File

@@ -2,7 +2,7 @@ import { notify } from '@affine/component';
import { authAtom, openSettingModalAtom } from '@affine/core/atoms';
import { AIProvider } from '@affine/core/blocksuite/presets/ai';
import { toggleGeneralAIOnboarding } from '@affine/core/components/affine/ai-onboarding/apis';
import { mixpanel } from '@affine/core/mixpanel';
import { track } from '@affine/core/mixpanel';
import {
getBaseUrl,
type getCopilotHistoriesQuery,
@@ -470,10 +470,7 @@ Could you make a new website based on these notes and send back just the html fi
activeTab: 'billing',
open: true,
});
mixpanel.track('PlansViewed', {
segment: 'payment wall',
category: 'payment wall ai action count',
});
track.$.paywall.aiAction.viewPlans();
});
AIProvider.slots.requestLogin.on(() => {

View File

@@ -1,5 +1,5 @@
import { AIProvider } from '@affine/core/blocksuite/presets/ai';
import { mixpanel } from '@affine/core/mixpanel';
import { mixpanel, track } from '@affine/core/mixpanel';
import type { EditorHost } from '@blocksuite/block-std';
import type { BlockModel } from '@blocksuite/store';
import { lowerCase, omit } from 'lodash-es';
@@ -13,7 +13,7 @@ type AIActionEventName =
| 'AI result accepted';
type AIActionEventProperties = {
page: 'doc-editor' | 'whiteboard-editor';
page: 'doc' | 'edgeless';
segment:
| 'AI action panel'
| 'right side bar'
@@ -73,9 +73,7 @@ const trackAction = ({
};
const inferPageMode = (host: EditorHost) => {
return host.querySelector('affine-page-root')
? 'doc-editor'
: 'whiteboard-editor';
return host.querySelector('affine-page-root') ? 'doc' : 'edgeless';
};
const defaultActionOptions = [
@@ -253,14 +251,12 @@ const toTrackedOptions = (
export function setupTracker() {
AIProvider.slots.requestUpgradePlan.on(() => {
mixpanel.track('AI', {
action: 'requestUpgradePlan',
});
track.$.paywall.aiAction.viewPlans();
});
AIProvider.slots.requestLogin.on(() => {
mixpanel.track('AI', {
action: 'requestLogin',
track.$.$.auth.signIn({
control: 'aiAction',
});
});

View File

@@ -1,4 +1,4 @@
import { mixpanel } from '@affine/core/mixpanel';
import { track } from '@affine/core/mixpanel';
import { WorkspacePropertiesAdapter } from '@affine/core/modules/properties';
import { I18n, i18nTime } from '@affine/i18n';
import type { EditorHost } from '@blocksuite/block-std';
@@ -75,12 +75,7 @@ export function createLinkedWidgetConfig(framework: FrameworkProvider) {
inlineEditor,
docId: doc.id,
});
mixpanel.track('LinkedDocCreated', {
control: 'linked doc',
module: 'inline @',
type: 'doc',
other: 'existing doc',
});
track.doc.editor.atMenu.linkDoc();
},
})),
maxDisplay: MAX_DOCS,

View File

@@ -7,7 +7,7 @@ import {
toReactNode,
type useConfirmModal,
} from '@affine/component';
import { mixpanel } from '@affine/core/mixpanel';
import { track } from '@affine/core/mixpanel';
import { DocsSearchService } from '@affine/core/modules/docs-search';
import { resolveLinkToDoc } from '@affine/core/modules/navigation';
import type { PeekViewService } from '@affine/core/modules/peek-view';
@@ -465,30 +465,11 @@ export function patchQuickSearchService(
pageId: linkedDoc.id,
},
]);
const isEdgeless =
rootComponent instanceof EdgelessRootBlockComponent;
if (result.isNewDoc) {
mixpanel.track('DocCreated', {
control: 'linked doc',
module: 'slash commands',
type: 'linked doc',
category: 'doc',
page: isEdgeless ? 'whiteboard editor' : 'doc editor',
});
mixpanel.track('LinkedDocCreated', {
control: 'new doc',
module: 'slash commands',
type: 'doc',
page: isEdgeless ? 'whiteboard editor' : 'doc editor',
});
} else {
mixpanel.track('LinkedDocCreated', {
control: 'linked doc',
module: 'slash commands',
type: 'doc',
page: isEdgeless ? 'whiteboard editor' : 'doc editor',
});
track.doc.editor.slashMenu.createDoc({ control: 'linkDoc' });
track.doc.editor.slashMenu.linkDoc({ control: 'createDoc' });
}
track.doc.editor.slashMenu.linkDoc({ control: 'linkDoc' });
} else if ('userInput' in result) {
const embedOptions = service.getEmbedBlockOptions(
result.userInput

View File

@@ -18,7 +18,7 @@ import { useEnableCloud } from '@affine/core/hooks/affine/use-enable-cloud';
import { useExportPage } from '@affine/core/hooks/affine/use-export-page';
import { useTrashModalHelper } from '@affine/core/hooks/affine/use-trash-modal-helper';
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
import { mixpanel } from '@affine/core/mixpanel';
import { track } from '@affine/core/mixpanel';
import { WorkbenchService } from '@affine/core/modules/workbench';
import { ViewService } from '@affine/core/modules/workbench/services/view';
import { useDetailPageHeaderResponsive } from '@affine/core/pages/workspace/detail-page/use-header-responsive';
@@ -157,36 +157,23 @@ export const PageHeaderMenuButton = ({
const handleDuplicate = useCallback(() => {
duplicate(pageId);
mixpanel.track('DocCreated', {
segment: 'editor header',
page: doc.mode$.value === 'page' ? 'doc editor' : 'edgeless editor',
module: 'header menu',
control: 'copy doc',
type: 'doc duplicate',
category: 'doc',
track.$.header.actions.createDoc({
control: 'duplicate',
});
}, [doc.mode$.value, duplicate, pageId]);
}, [duplicate, pageId]);
const onImportFile = useAsyncCallback(async () => {
const options = await importFile();
if (options.isWorkspaceFile) {
mixpanel.track('WorkspaceCreated', {
segment: 'editor header',
page: doc.mode$.value === 'page' ? 'doc editor' : 'edgeless editor',
module: 'header menu',
control: 'import button',
type: 'imported workspace',
track.$.header.actions.createWorkspace({
control: 'import',
});
} else {
mixpanel.track('DocCreated', {
segment: 'editor header',
page: doc.mode$.value === 'page' ? 'doc editor' : 'edgeless editor',
module: 'header menu',
control: 'import button',
type: 'imported doc',
track.$.header.actions.createDoc({
control: 'import',
});
}
}, [doc.mode$.value, importFile]);
}, [importFile]);
const showResponsiveMenu = hideShare;
const ResponsiveMenuItems = (

View File

@@ -1,6 +1,6 @@
import { Tooltip } from '@affine/component/ui/tooltip';
import { useBlockSuiteDocMeta } from '@affine/core/hooks/use-block-suite-page-meta';
import { mixpanel } from '@affine/core/mixpanel';
import { track } from '@affine/core/mixpanel';
import { useI18n } from '@affine/i18n';
import {
type DocMode,
@@ -62,8 +62,8 @@ export const EditorModeSwitch = ({
}, [currentMode, isPublic, doc, pageId, t, trash]);
const onSwitchToPageMode = useCallback(() => {
mixpanel.track('Button', {
resolve: 'SwitchToPageMode',
track.$.header.actions.switchPageMode({
mode: 'page',
});
if (currentMode === 'page' || isPublic) {
return;
@@ -73,8 +73,8 @@ export const EditorModeSwitch = ({
}, [currentMode, isPublic, doc, t]);
const onSwitchToEdgelessMode = useCallback(() => {
mixpanel.track('Button', {
resolve: 'SwitchToEdgelessMode',
track.$.header.actions.switchPageMode({
mode: 'edgeless',
});
if (currentMode === 'edgeless' || isPublic) {
return;

View File

@@ -1,6 +1,6 @@
import { DropdownButton, Menu } from '@affine/component';
import { BlockCard } from '@affine/component/card/block-card';
import { mixpanel } from '@affine/core/mixpanel';
import { track } from '@affine/core/mixpanel';
import { useI18n } from '@affine/i18n';
import { EdgelessIcon, ImportIcon, PageIcon } from '@blocksuite/icons/rc';
import type { PropsWithChildren } from 'react';
@@ -70,26 +70,14 @@ export const NewPageButton = ({
const handleCreateNewPage = useCallback(() => {
createNewPage();
setOpen(false);
mixpanel.track('DocCreated', {
page: 'doc library',
segment: 'all doc',
module: 'doc list header',
control: 'new doc button',
type: 'doc',
category: 'page',
});
track.allDocs.header.actions.createDoc();
}, [createNewPage]);
const handleCreateNewEdgeless = useCallback(() => {
createNewEdgeless();
setOpen(false);
mixpanel.track('DocCreated', {
page: 'doc library',
segment: 'all doc',
module: 'doc list header',
control: 'new whiteboard button',
type: 'doc',
category: 'whiteboard',
track.allDocs.header.actions.createDoc({
mode: 'edgeless',
});
}, [createNewEdgeless]);

View File

@@ -7,7 +7,7 @@ import {
} from '@affine/component';
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
import { useNavigateHelper } from '@affine/core/hooks/use-navigate-helper';
import { mixpanel } from '@affine/core/mixpanel';
import { track } from '@affine/core/mixpanel';
import type { Tag } from '@affine/core/modules/tag';
import { TagService } from '@affine/core/modules/tag';
import type { Collection } from '@affine/env/filter';
@@ -49,21 +49,12 @@ export const PageListHeader = () => {
const onImportFile = useAsyncCallback(async () => {
const options = await importFile();
if (options.isWorkspaceFile) {
mixpanel.track('WorkspaceCreated', {
page: 'doc library',
segment: 'all doc',
module: 'doc list header',
control: 'import button',
type: 'imported workspace',
track.allDocs.header.actions.createWorkspace({
control: 'import',
});
} else {
mixpanel.track('DocCreated', {
page: 'doc library',
segment: 'all doc',
module: 'doc list header',
control: 'import button',
type: 'imported doc',
// category
track.allDocs.header.actions.createDoc({
control: 'import',
});
}
}, [importFile]);

View File

@@ -9,7 +9,7 @@ import {
import { useAppSettingHelper } from '@affine/core/hooks/affine/use-app-setting-helper';
import { useBlockSuiteMetaHelper } from '@affine/core/hooks/affine/use-block-suite-meta-helper';
import { useTrashModalHelper } from '@affine/core/hooks/affine/use-trash-modal-helper';
import { mixpanel } from '@affine/core/mixpanel';
import { track } from '@affine/core/mixpanel';
import { FavoriteService } from '@affine/core/modules/favorite';
import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/properties';
import { WorkbenchService } from '@affine/core/modules/workbench';
@@ -115,13 +115,8 @@ export const PageOperationCell = ({
const onDuplicate = useCallback(() => {
duplicate(page.id, false);
mixpanel.track('DocCreated', {
segment: 'all doc',
module: 'doc item menu',
control: 'copy doc',
type: 'doc duplicate',
category: 'doc',
page: 'doc library',
track.allDocs.list.docMenu.createDoc({
control: 'duplicate',
});
}, [duplicate, page.id]);

View File

@@ -1,7 +1,7 @@
import { Loading } from '@affine/component';
import { Divider } from '@affine/component/ui/divider';
import { MenuItem } from '@affine/component/ui/menu';
import { mixpanel } from '@affine/core/mixpanel';
import { track } from '@affine/core/mixpanel';
import { AuthService } from '@affine/core/modules/cloud';
import { useI18n } from '@affine/i18n';
import { Logo1Icon } from '@blocksuite/icons/rc';
@@ -25,9 +25,7 @@ export const SignInItem = () => {
const t = useI18n();
const onClickSignIn = useCallback(() => {
mixpanel.track('Button', {
resolve: 'SignIn',
});
track.$.navigationPanel.workspaceList.signIn();
setOpen(state => ({
...state,
openModal: true,
@@ -92,9 +90,7 @@ const UserWithWorkspaceListInner = ({
if (!isAuthenticated && !runtimeConfig.allowLocalWorkspace) {
return openSignInModal();
}
mixpanel.track('Button', {
resolve: 'NewWorkspace',
});
track.$.navigationPanel.workspaceList.createWorkspace();
setOpenCreateWorkspaceModal('new');
onEventEnd?.();
}, [
@@ -104,9 +100,10 @@ const UserWithWorkspaceListInner = ({
setOpenCreateWorkspaceModal,
]);
track.$.navigationPanel.workspaceList.createWorkspace();
const onAddWorkspace = useCallback(() => {
mixpanel.track('Button', {
resolve: 'AddWorkspace',
track.$.navigationPanel.workspaceList.createWorkspace({
control: 'import',
});
setOpenCreateWorkspaceModal('add');
onEventEnd?.();

View File

@@ -1,5 +1,5 @@
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
import { mixpanel } from '@affine/core/mixpanel';
import { track } from '@affine/core/mixpanel';
import { useI18n } from '@affine/i18n';
import { ImportIcon } from '@blocksuite/icons/rc';
@@ -13,22 +13,11 @@ const ImportPage = ({ docCollection }: { docCollection: DocCollection }) => {
const onImportFile = useAsyncCallback(async () => {
const options = await importFile();
if (options.isWorkspaceFile) {
mixpanel.track('WorkspaceCreated', {
segment: 'navigation panel',
module: 'doc list header',
control: 'import button',
type: 'imported workspace',
});
} else {
mixpanel.track('DocCreated', {
segment: 'navigation panel',
module: 'doc list header',
control: 'import button',
type: 'imported doc',
// category
});
}
track.$.navigationPanel.workspaceList[
options.isWorkspaceFile ? 'createWorkspace' : 'createDoc'
]({
control: 'import',
});
}, [importFile]);
return (

View File

@@ -1,6 +1,6 @@
import { openSettingModalAtom } from '@affine/core/atoms';
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
import { mixpanel } from '@affine/core/mixpanel';
import { track } from '@affine/core/mixpanel';
import {
ExplorerCollections,
ExplorerFavorites,
@@ -91,21 +91,14 @@ export const RootAppSidebar = (): ReactElement => {
async (e?: MouseEvent) => {
const page = pageHelper.createPage('page', false);
page.load();
mixpanel.track('DocCreated', {
segment: 'navigation panel',
module: 'bottom button',
control: 'new doc button',
category: 'page',
type: 'doc',
});
track.$.navigationPanel.$.createDoc();
workbench.openDoc(page.id, {
at: e?.ctrlKey || e?.metaKey ? 'new-tab' : 'active',
});
},
[pageHelper, workbench]
);
// Listen to the "New Page" action from the menu
useEffect(() => {
if (environment.isDesktop) {
return events?.applicationMenu.onNewPageAction(() => onClickNewPage());
@@ -120,12 +113,7 @@ export const RootAppSidebar = (): ReactElement => {
activeTab: 'appearance',
open: true,
});
mixpanel.track('SettingsViewed', {
// page:
segment: 'navigation panel',
module: 'general list',
control: 'settings button',
});
track.$.navigationPanel.$.openSettings();
}, [setOpenSettingModalAtom]);
const sidebarOpen = useAtomValue(appSidebarOpenAtom);
@@ -153,7 +141,7 @@ export const RootAppSidebar = (): ReactElement => {
<QuickSearchInput
className={quickSearch}
data-testid="slider-bar-quick-search-button"
data-event-props="$.navigationPanel.generalFunction.quickSearch"
data-event-props="$.navigationPanel.$.quickSearch"
onClick={onOpenQuickSearchModal}
/>
<AddPageButton onClick={onClickNewPage} />

View File

@@ -14,7 +14,7 @@ import {
openSettingModalAtom,
openSignOutModalAtom,
} from '@affine/core/atoms';
import { mixpanel } from '@affine/core/mixpanel';
import { track } from '@affine/core/mixpanel';
import { useI18n } from '@affine/i18n';
import { AccountIcon, SignOutIcon } from '@blocksuite/icons/rc';
import { useLiveData, useService } from '@toeverything/infra';
@@ -83,12 +83,7 @@ const AccountMenu = () => {
const setOpenSignOutModalAtom = useSetAtom(openSignOutModalAtom);
const onOpenAccountSetting = useCallback(() => {
mixpanel.track('AccountSettingsViewed', {
// page:
segment: 'navigation panel',
module: 'profile and badge',
control: 'profile and email',
});
track.$.navigationPanel.profileAndBadge.openSettings({ to: 'account' });
setSettingModalAtom(prev => ({
...prev,
open: true,

View File

@@ -1,5 +1,5 @@
import { Menu } from '@affine/component';
import { mixpanel } from '@affine/core/mixpanel';
import { track } from '@affine/core/mixpanel';
import { useService, WorkspacesService } from '@toeverything/infra';
import { useAtom } from 'jotai';
import { useCallback, useEffect } from 'react';
@@ -16,9 +16,7 @@ export const WorkspaceSelector = () => {
setOpenUserWorkspaceList(false);
}, [setOpenUserWorkspaceList]);
const openUserWorkspaceList = useCallback(() => {
mixpanel.track('Button', {
resolve: 'OpenWorkspaceList',
});
track.$.navigationPanel.workspaceList.open();
setOpenUserWorkspaceList(true);
}, [setOpenUserWorkspaceList]);

View File

@@ -2,7 +2,7 @@ import { Button } from '@affine/component/ui/button';
import { AffineShapeIcon } from '@affine/core/components/page-list'; // TODO(@eyhn): import from page-list temporarily, need to defined common svg icon/images management.
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
import { useNavigateHelper } from '@affine/core/hooks/use-navigate-helper';
import { mixpanel } from '@affine/core/mixpanel';
import { track } from '@affine/core/mixpanel';
import { WorkspaceSubPath } from '@affine/core/shared';
import { useI18n } from '@affine/i18n';
import { useLiveData, useService, WorkspaceService } from '@toeverything/infra';
@@ -26,9 +26,7 @@ export const WorkspaceUpgrade = function WorkspaceUpgrade() {
return;
}
mixpanel.track('Button', {
resolve: 'UpgradeWorkspace',
});
track.workspace.$.$.upgradeWorkspace();
try {
const newWorkspace = await currentWorkspace.upgrade.upgrade();

View File

@@ -3,7 +3,7 @@ import {
pushGlobalLoadingEventAtom,
resolveGlobalLoadingEventAtom,
} from '@affine/component/global-loading';
import { mixpanel } from '@affine/core/mixpanel';
import { track } from '@affine/core/mixpanel';
import { apis } from '@affine/electron-api';
import { useI18n } from '@affine/i18n';
import type { PageRootService, RootBlockModel } from '@blocksuite/blocks';
@@ -26,10 +26,8 @@ async function exportHandler({ page, type }: ExportHandlerOptions) {
if (editorRoot) {
pageService = editorRoot.spec.getService<PageRootService>('affine:page');
}
mixpanel.track('ShareCreated', {
track.$.header.share.export({
type,
segment: 'sharing panel',
module: 'export share',
});
switch (type) {
case 'html':

View File

@@ -4,7 +4,7 @@ import {
PreconditionStrategy,
registerAffineCommand,
} from '@affine/core/commands';
import { mixpanel } from '@affine/core/mixpanel';
import { track } from '@affine/core/mixpanel';
import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/properties';
import { WorkspaceFlavour } from '@affine/env/workspace';
import { useI18n } from '@affine/i18n';
@@ -161,10 +161,8 @@ export function useRegisterBlocksuiteEditorCommands() {
label: t['com.affine.header.option.duplicate'](),
run() {
duplicate(docId);
mixpanel.track('DocCreated', {
control: 'cmdk',
type: 'doc duplicate',
category: 'doc',
track.$.cmdk.$.createDoc({
control: 'duplicate',
});
},
})

View File

@@ -1,5 +1,5 @@
import { notify } from '@affine/component';
import { mixpanel } from '@affine/core/mixpanel';
import { track } from '@affine/core/mixpanel';
import { getAffineCloudBaseUrl } from '@affine/core/modules/cloud/services/fetch';
import { useI18n } from '@affine/i18n';
import type { Disposable } from '@blocksuite/global/utils';
@@ -68,9 +68,8 @@ export const useSharingUrl = ({
.catch(err => {
console.error(err);
});
mixpanel.track('ShareLinkCopied', {
module: urlType === 'share' ? 'public share' : 'private share',
type: 'link',
track.$.header.share.copyShareLink({
type: urlType === 'share' ? 'public' : 'private',
});
} else {
notify.error({

View File

@@ -1,5 +1,5 @@
import { useUpgradeNotify } from '@affine/core/components/affine/subscription-landing/notify';
import { mixpanel } from '@affine/core/mixpanel';
import { track } from '@affine/core/mixpanel';
import { SubscriptionPlan, SubscriptionRecurring } from '@affine/graphql';
import { nanoid } from 'nanoid';
import { useCallback, useEffect } from 'react';
@@ -129,11 +129,9 @@ export const useSubscriptionNotifyReader = () => {
upgradeNotify(link);
localStorage.removeItem(localStorageKey);
// mixpanel
mixpanel.track('PlanChangeSucceeded', {
category: recurring,
type: plan,
control: 'new subscription',
track.$.$.$.checkout({
plan,
recurring,
});
} catch (err) {
console.error('Failed to parse subscription callback link', err);

View File

@@ -7,7 +7,7 @@ import { atomWithObservable, atomWithStorage } from 'jotai/utils';
import { useCallback, useState } from 'react';
import { Observable } from 'rxjs';
import { mixpanel } from '../mixpanel';
import { track } from '../mixpanel';
import { popupWindow } from '../utils';
import { useAsyncCallback } from './affine-async-hooks';
@@ -122,9 +122,7 @@ export const useAppUpdater = () => {
);
const quitAndInstall = useCallback(() => {
mixpanel.track('Button', {
resolve: 'QuitAndInstall',
});
track.$.navigationPanel.bottomButtons.quitAndInstall();
if (updateReady) {
setAppQuitting(true);
apis?.updater.quitAndInstall().catch(err => {
@@ -135,9 +133,7 @@ export const useAppUpdater = () => {
}, [updateReady]);
const checkForUpdates = useCallback(async () => {
mixpanel.track('Button', {
resolve: 'CheckForUpdates',
});
track.$.settingsPanel.about.checkUpdates();
if (checkingForUpdates) {
return;
}
@@ -154,9 +150,7 @@ export const useAppUpdater = () => {
}, [checkingForUpdates, setCheckingForUpdates]);
const downloadUpdate = useCallback(() => {
mixpanel.track('Button', {
resolve: 'DownloadUpdate',
});
track.$.settingsPanel.about.downloadUpdate();
apis?.updater.downloadUpdate().catch(err => {
console.error('Error downloading update:', err);
});
@@ -164,8 +158,8 @@ export const useAppUpdater = () => {
const toggleAutoDownload = useCallback(
(enable: boolean) => {
mixpanel.track('Button', {
resolve: 'ToggleAutoDownload',
track.$.settingsPanel.about.changeAppSetting({
key: 'autoDownload',
value: enable,
});
setSetting({
@@ -177,8 +171,8 @@ export const useAppUpdater = () => {
const toggleAutoCheck = useCallback(
(enable: boolean) => {
mixpanel.track('Button', {
resolve: 'ToggleAutoCheck',
track.$.settingsPanel.about.changeAppSetting({
key: 'autoCheckUpdates',
value: enable,
});
setSetting({
@@ -189,17 +183,13 @@ export const useAppUpdater = () => {
);
const openChangelog = useAsyncCallback(async () => {
mixpanel.track('Button', {
resolve: 'OpenChangelog',
});
track.$.navigationPanel.bottomButtons.openChangelog();
popupWindow(runtimeConfig.changelogUrl);
await setChangelogUnread(true);
}, [setChangelogUnread]);
const dismissChangelog = useAsyncCallback(async () => {
mixpanel.track('Button', {
resolve: 'DismissChangelog',
});
track.$.navigationPanel.bottomButtons.dismissChangelog();
await setChangelogUnread(true);
}, [setChangelogUnread]);

View File

@@ -15,7 +15,7 @@ export function makeTracker(trackFn: TrackFn): CallableEventsChain {
get(target, prop) {
if (
typeof prop !== 'string' ||
prop === '$$typeof' /* webpack hot load reading this prop */
prop === '$$typeof' /* webpack hot-reload reads this prop */
) {
return undefined;
}
@@ -55,7 +55,12 @@ export function makeTracker(trackFn: TrackFn): CallableEventsChain {
* @example
*
* ```html
* <button data-event-chain='$.cmdk.settings.quicksearch.changeLanguage' data-event-arg='cn' />
* <button
* data-event-chain='$.cmdk.settings.changeLanguage'
* data-event-arg='cn'
* <!-- or -->
* data-event-args-foo='bar'
* />
* ```
*/
export function enableAutoTrack(root: HTMLElement, trackFn: TrackFn) {
@@ -114,5 +119,6 @@ declare module 'react' {
interface HTMLAttributes<T> {
'data-event-props'?: EventsUnion;
'data-event-arg'?: string;
'data-event-args-control'?: string;
}
}

View File

@@ -1,43 +1,291 @@
// let '$' stands for unspecific matrix
/* eslint-disable rxjs/finnish */
export interface Events {
$: {
cmdk: {
settings: ['openSettings', 'changeLanguage'];
};
navigationPanel: {
generalFunction: [
'quickSearch',
'createDoc',
'goToAllPage',
'goToJournals',
'openSettings',
];
collection: ['createDoc'];
bottomButtong: ['downloadApp', 'restartAndInstallUpdate'];
others: ['openTrash', 'export'];
// SECTION: app events
type GeneralEvents = 'openMigrationDataHelp' | 'export';
type CmdkEvents = 'quickSearch';
type AppEvents =
| 'checkUpdates'
| 'downloadUpdate'
| 'downloadApp'
| 'quitAndInstall'
| 'openChangelog'
| 'dismissChangelog';
type NavigationEvents =
| 'openInNewTab'
| 'openInSplitView'
| 'switchTab'
| 'switchSplitView'
| 'navigate'
| 'open'
| 'close'; // openclose modal/diaglog
// END SECTION
// SECTION: doc events
type WorkspaceEvents =
| 'createWorkspace'
| 'upgradeWorkspace'
| 'enableCloudWorkspace'
| 'import'
| 'export'
| 'openWorkspaceList';
type DocEvents =
| 'createDoc'
| 'renameDoc'
| 'linkDoc'
| 'deleteDoc'
| 'switchPageMode';
type EditorEvents = 'bold' | 'italic' | 'underline' | 'strikeThrough';
// END SECTION
// SECTION: setting events
type SettingEvents =
| 'openSettings'
| 'changeAppSetting'
| 'changeEditorSetting';
// END SECTION
// SECTION: organize events
type CollectionEvents =
| 'createCollection'
| 'deleteCollection'
| 'renameCollection'
| 'addDocToCollection';
type FolderEvents =
| 'createFolder'
| 'renameFolder'
| 'moveFolder'
| 'deleteFolder';
type TagEvents = 'createTag' | 'deleteTag' | 'renameTag' | 'tagDoc';
type FavoriteEvents = 'toggleFavorite';
type OrganizeItemEvents = // doc, link, folder, collection, tag
| 'createOrganizeItem'
| 'renameOrganizeItem'
| 'moveOrganizeItem'
| 'deleteOrganizeItem'
| 'orderOrganizeItem';
type OrganizeEvents =
| OrganizeItemEvents
| CollectionEvents
| FolderEvents
| TagEvents
| FavoriteEvents;
// END SECTION
// SECTION: cloud events
type ShareEvents = 'createShareLink' | 'copyShareLink';
type AuthEvents = 'signIn' | 'signUp' | 'oauth' | 'signOut';
type AccountEvents = 'uploadAvatar' | 'removeAvatar' | 'updateUserName';
type PaymentEvents =
| 'viewPlans'
| 'bookDemo'
| 'checkout'
| 'changeSubscriptionRecurring'
| 'confirmChangingSubscriptionRecurring'
| 'cancelSubscription'
| 'confirmCancelingSubscription'
| 'resumeSubscription'
| 'confirmResumingSubscription';
// END SECTION
type UserEvents =
| GeneralEvents
| AppEvents
| NavigationEvents
| WorkspaceEvents
| DocEvents
| EditorEvents
| SettingEvents
| CmdkEvents
| OrganizeEvents
| ShareEvents
| AuthEvents
| AccountEvents
| PaymentEvents;
interface PageDivision {
[page: string]: {
[segment: string]: {
[module: string]: UserEvents[];
};
};
}
const PageEvents = {
// page: {
// $: {}
// ^ if empty
// segment: {
// module: ['event1', 'event2']
// },
// },
// to: page.$.segment.module.event1()
$: {
$: {
$: ['createWorkspace', 'checkout'],
auth: ['oauth', 'signIn', 'signUp'],
},
settingsPanel: {
menu: ['openSettings'],
workspace: ['viewPlans'],
profileAndBadge: ['viewPlans'],
accountUsage: ['viewPlans'],
accountSettings: ['uploadAvatar', 'removeAvatar', 'updateUserName'],
plans: [
'checkout',
'changeSubscriptionRecurring',
'confirmChangingSubscriptionRecurring',
'cancelSubscription',
'confirmCancelingSubscription',
'resumeSubscription',
'confirmResumingSubscription',
],
billing: ['viewPlans', 'bookDemo'],
about: ['checkUpdates', 'downloadUpdate', 'changeAppSetting'],
},
cmdk: {
$: ['createDoc'],
settings: ['openSettings', 'changeAppSetting'],
},
navigationPanel: {
$: ['quickSearch', 'createDoc', 'navigate', 'openSettings'],
organize: [
'createOrganizeItem',
'renameOrganizeItem',
'moveOrganizeItem',
'deleteOrganizeItem',
'orderOrganizeItem',
'openInNewTab',
'openInSplitView',
'toggleFavorite',
],
docs: ['createDoc', 'deleteDoc', 'linkDoc'],
collections: ['createDoc', 'addDocToCollection'],
folders: ['createDoc'],
tags: ['createDoc', 'tagDoc'],
favorites: ['createDoc'],
migrationData: ['openMigrationDataHelp'],
bottomButtons: [
'downloadApp',
'quitAndInstall',
'openChangelog',
'dismissChangelog',
],
others: ['navigate', 'import'],
workspaceList: [
'open',
'signIn',
'createWorkspace',
'createDoc',
'openSettings',
],
profileAndBadge: ['openSettings'],
},
aiOnboarding: {
dialog: ['viewPlans'],
},
docHistory: {
$: ['open', 'close', 'switchPageMode', 'viewPlans'],
},
paywall: {
storage: ['viewPlans'],
aiAction: ['viewPlans'],
},
header: {
actions: ['createDoc', 'createWorkspace', 'switchPageMode'],
share: ['createShareLink', 'copyShareLink', 'export'],
},
},
doc: {
editor: {
formatToolbar: ['bold'];
slashMenu: ['linkDoc', 'createDoc'],
atMenu: ['linkDoc'],
formatToolbar: ['bold'],
},
},
// remove when type added
// eslint-disable-next-line @typescript-eslint/ban-types
edgeless: {},
workspace: {
$: {
$: ['upgradeWorkspace'],
},
},
allDocs: {
header: {
actions: ['createDoc', 'createWorkspace'],
},
list: {
docMenu: ['createDoc'],
},
},
// remove when type added
// eslint-disable-next-line @typescript-eslint/ban-types
collection: {},
// remove when type added
// eslint-disable-next-line @typescript-eslint/ban-types
tag: {},
// remove when type added
// eslint-disable-next-line @typescript-eslint/ban-types
trash: {},
subscriptionLanding: {
$: {
$: ['checkout'],
},
},
} as const satisfies PageDivision;
type OrganizeItemType = 'doc' | 'folder' | 'collection' | 'tag' | 'favorite';
type OrganizeItemArgs =
| {
type: 'link';
target: OrganizeItemType;
}
| {
type: OrganizeItemType;
};
};
edgeless: {
editor: {
formatToolbar: ['drawConnector'];
};
};
// remove when type added
// eslint-disable-next-line @typescript-eslint/ban-types
allDocs: {};
// remove when type added
// eslint-disable-next-line @typescript-eslint/ban-types
collection: {};
// remove when type added
// eslint-disable-next-line @typescript-eslint/ban-types
tag: {};
// remove when type added
// eslint-disable-next-line @typescript-eslint/ban-types
trash: {};
}
type PaymentEventArgs = {
plan: string;
recurring: string;
};
export type EventArgs = {
createWorkspace: { flavour: string };
oauth: { provider: string };
viewPlans: PaymentEventArgs;
checkout: PaymentEventArgs;
cancelSubscription: PaymentEventArgs;
confirmCancelingSubscription: PaymentEventArgs;
resumeSubscription: PaymentEventArgs;
confirmResumingSubscription: PaymentEventArgs;
changeSubscriptionRecurring: PaymentEventArgs;
confirmChangingSubscriptionRecurring: PaymentEventArgs;
navigate: { to: string };
openSettings: { to: string };
changeAppSetting: { key: string; value: string | boolean | number };
changeEditorSetting: { key: string; value: string | boolean | number };
createOrganizeItem: OrganizeItemArgs;
renameOrganizeItem: OrganizeItemArgs;
moveOrganizeItem: OrganizeItemArgs;
deleteOrganizeItem: OrganizeItemArgs;
orderOrganizeItem: OrganizeItemArgs;
openInNewTab: { type: OrganizeItemType };
openInSplitView: { type: OrganizeItemType };
toggleFavorite: OrganizeItemArgs & { on: boolean };
createDoc: { mode?: 'edgeless' | 'page' };
switchPageMode: { mode: 'edgeless' | 'page' };
createShareLink: { mode: 'edgeless' | 'page' };
copyShareLink: { type: 'public' | 'private' };
export: { type: string };
};
// for type checking
// if it complains, check the definition of [EventArgs] to make sure it's key is a subset of [UserEvents]
export const YOU_MUST_DEFINE_ARGS_WITH_WRONG_EVENT_NAME: keyof EventArgs extends UserEvents
? true
: false = true;
export type Events = typeof PageEvents;

View File

@@ -1,13 +1,23 @@
import type { Events } from './events';
import type { EventArgs, Events } from './events';
type EventPropsOverride = {
page?: keyof Events;
segment?: string;
module?: string;
control?: string;
};
export type CallableEventsChain = {
[Page in keyof Events]: {
[Segment in keyof Events[Page]]: {
[Module in keyof Events[Page][Segment]]: {
// @ts-expect-error ignore `symbol | number` as key
[Control in Events[Page][Segment][Module][number]]: (
arg?: string
) => void;
[Event in Events[Page][Segment][Module][number]]: Event extends keyof EventArgs
? (
// we make all args partial to simply satisfies nullish type checking
args?: Partial<EventArgs[Event]> & EventPropsOverride
) => void
: (args?: EventPropsOverride) => void;
};
};
};
@@ -18,14 +28,14 @@ export type EventsUnion = {
[Segment in keyof Events[Page]]: {
[Module in keyof Events[Page][Segment]]: {
// @ts-expect-error ignore `symbol | number` as key
[Control in Events[Page][Segment][Module][number]]: `${Page}.${Segment}.${Module}.${Control}`;
[Event in Events[Page][Segment][Module][number]]: `${Page}.${Segment}.${Module}.${Event}`;
// @ts-expect-error ignore `symbol | number` as key
}[Events[Page][Segment][Module][number]];
}[keyof Events[Page][Segment]];
}[keyof Events[Page]];
}[keyof Events];
// page > segment > module > [controls]
// page > segment > module > [events]
type IsFourLevelsDeep<
T,
Depth extends number[] = [],

View File

@@ -10,7 +10,7 @@ import {
filterPage,
useEditCollection,
} from '@affine/core/components/page-list';
import { mixpanel } from '@affine/core/mixpanel';
import { track } from '@affine/core/mixpanel';
import { CollectionService } from '@affine/core/modules/collection';
import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/properties';
import { ShareDocsService } from '@affine/core/modules/share-doc';
@@ -83,10 +83,9 @@ export const ExplorerCollectionNode = ({
...collection,
name,
}));
mixpanel.track('CollectionRenamed', {
page: 'sidebar',
module: 'collection',
control: 'rename collection',
track.$.navigationPanel.organize.renameOrganizeItem({
type: 'collection',
});
toast(t['com.affine.toastMessage.rename']());
}
@@ -113,10 +112,10 @@ export const ExplorerCollectionNode = ({
if (collection && data.treeInstruction?.type === 'make-child') {
if (data.source.data.entity?.type === 'doc') {
handleAddDocToCollection(data.source.data.entity.id);
mixpanel.track('AddDocToCollection', {
page: 'sidebar',
module: 'collection',
control: 'drop doc on collection',
track.$.navigationPanel.organize.createOrganizeItem({
type: 'link',
target: 'doc',
control: 'drag',
});
}
} else {
@@ -144,10 +143,9 @@ export const ExplorerCollectionNode = ({
(data: DropTargetDropEvent<AffineDNDData>) => {
if (collection && data.source.data.entity?.type === 'doc') {
handleAddDocToCollection(data.source.data.entity.id);
mixpanel.track('AddDocToCollection', {
page: 'sidebar',
module: 'collection',
control: 'drop doc on collection',
track.$.navigationPanel.organize.createOrganizeItem({
type: 'collection',
control: 'drag',
});
}
},

View File

@@ -7,7 +7,7 @@ import {
} from '@affine/component';
import { useAppSettingHelper } from '@affine/core/hooks/affine/use-app-setting-helper';
import { useDeleteCollectionInfo } from '@affine/core/hooks/affine/use-delete-collection-info';
import { mixpanel } from '@affine/core/mixpanel';
import { track } from '@affine/core/mixpanel';
import { CollectionService } from '@affine/core/modules/collection';
import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/properties';
import { WorkbenchService } from '@affine/core/modules/workbench';
@@ -58,15 +58,9 @@ export const useExplorerCollectionNodeOperations = (
const createAndAddDocument = useCallback(() => {
const newDoc = docsService.createDoc();
collectionService.addPageToCollection(collectionId, newDoc.id);
mixpanel.track('DocCreated', {
page: 'sidebar',
module: 'collection',
control: 'add doc button',
});
mixpanel.track('AddDocToCollection', {
page: 'sidebar',
module: 'collection',
control: 'add doc button',
track.$.navigationPanel.collections.createDoc();
track.$.navigationPanel.collections.addDocToCollection({
control: 'button',
});
workbenchService.workbench.openDoc(newDoc.id);
onOpenCollapsed();
@@ -80,12 +74,8 @@ export const useExplorerCollectionNodeOperations = (
const handleToggleFavoriteCollection = useCallback(() => {
compatibleFavoriteItemsAdapter.toggle(collectionId, 'collection');
mixpanel.track('ToggleFavorite', {
page: 'sidebar',
module: 'collection',
control: 'toggle favorite collection button',
track.$.navigationPanel.organize.toggleFavorite({
type: 'collection',
id: collectionId,
});
}, [compatibleFavoriteItemsAdapter, collectionId]);
@@ -104,28 +94,20 @@ export const useExplorerCollectionNodeOperations = (
const handleOpenInSplitView = useCallback(() => {
workbenchService.workbench.openCollection(collectionId, { at: 'beside' });
mixpanel.track('OpenInSplitView', {
page: 'sidebar',
module: 'collection',
control: 'open in split view button',
track.$.navigationPanel.organize.openInSplitView({
type: 'collection',
});
}, [collectionId, workbenchService.workbench]);
const handleOpenInNewTab = useCallback(() => {
workbenchService.workbench.openCollection(collectionId, { at: 'new-tab' });
mixpanel.track('OpenInNewTab', {
page: 'sidebar',
module: 'collection',
control: 'open in new tab button',
});
track.$.navigationPanel.organize.openInNewTab({ type: 'collection' });
}, [collectionId, workbenchService.workbench]);
const handleDeleteCollection = useCallback(() => {
collectionService.deleteCollection(deleteInfo, collectionId);
mixpanel.track('CollectionDeleted', {
page: 'sidebar',
module: 'collection',
control: 'delete collection button',
track.$.navigationPanel.organize.deleteOrganizeItem({
type: 'collection',
});
}, [collectionId, collectionService, deleteInfo]);

View File

@@ -7,7 +7,7 @@ import {
} from '@affine/component';
import { InfoModal } from '@affine/core/components/affine/page-properties';
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
import { mixpanel } from '@affine/core/mixpanel';
import { track } from '@affine/core/mixpanel';
import { DocsSearchService } from '@affine/core/modules/docs-search';
import type { AffineDNDData } from '@affine/core/types/dnd';
import { useI18n } from '@affine/i18n';
@@ -116,11 +116,7 @@ export const ExplorerDocNode = ({
const handleRename = useAsyncCallback(
async (newName: string) => {
await docsService.changeDocTitle(docId, newName);
mixpanel.track('DocRenamed', {
page: 'sidebar',
module: 'doc',
control: 'doc rename',
});
track.$.navigationPanel.organize.renameOrganizeItem({ type: 'doc' });
},
[docId, docsService]
);
@@ -130,10 +126,8 @@ export const ExplorerDocNode = ({
if (data.treeInstruction?.type === 'make-child') {
if (data.source.data.entity?.type === 'doc') {
await docsService.addLinkedDoc(docId, data.source.data.entity.id);
mixpanel.track('LinkedDocCreated', {
page: 'sidebar',
module: 'doc',
control: 'drop doc on doc',
track.$.navigationPanel.docs.linkDoc({
control: 'drag',
});
} else {
toast(t['com.affine.rootAppSidebar.doc.link-doc-only']());
@@ -164,10 +158,8 @@ export const ExplorerDocNode = ({
if (data.source.data.entity?.type === 'doc') {
// TODO(eyhn): timeout&error handling
await docsService.addLinkedDoc(docId, data.source.data.entity.id);
mixpanel.track('LinkedDocCreated', {
page: 'sidebar',
module: 'doc',
control: 'drop doc on doc',
track.$.navigationPanel.docs.linkDoc({
control: 'drag',
});
} else {
toast(t['com.affine.rootAppSidebar.doc.link-doc-only']());

View File

@@ -8,7 +8,7 @@ import {
} from '@affine/component';
import { useAppSettingHelper } from '@affine/core/hooks/affine/use-app-setting-helper';
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
import { mixpanel } from '@affine/core/mixpanel';
import { track } from '@affine/core/mixpanel';
import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/properties';
import { WorkbenchService } from '@affine/core/modules/workbench';
import { useI18n } from '@affine/i18n';
@@ -68,10 +68,8 @@ export const useExplorerDocNodeOperations = (
},
onConfirm() {
docRecord.moveToTrash();
mixpanel.track('DocMovedTrash', {
page: 'sidebar',
module: 'doc',
control: 'move to trash button',
track.$.navigationPanel.docs.deleteDoc({
control: 'button',
});
toast(t['com.affine.toastMessage.movedTrash']());
},
@@ -82,10 +80,8 @@ export const useExplorerDocNodeOperations = (
workbenchService.workbench.openDoc(docId, {
at: 'new-tab',
});
mixpanel.track('OpenInNewTab', {
page: 'sidebar',
module: 'doc',
control: 'open in new tab button',
track.$.navigationPanel.organize.openInNewTab({
type: 'doc',
});
}, [docId, workbenchService]);
@@ -93,10 +89,8 @@ export const useExplorerDocNodeOperations = (
workbenchService.workbench.openDoc(docId, {
at: 'beside',
});
mixpanel.track('OpenInSplitView', {
page: 'sidebar',
module: 'doc',
control: 'open in split view button',
track.$.navigationPanel.organize.openInSplitView({
type: 'doc',
});
}, [docId, workbenchService]);
@@ -104,28 +98,16 @@ export const useExplorerDocNodeOperations = (
const newDoc = docsService.createDoc();
// TODO: handle timeout & error
await docsService.addLinkedDoc(docId, newDoc.id);
mixpanel.track('DocCreated', {
page: 'sidebar',
module: 'doc',
control: 'add linked doc button',
});
mixpanel.track('LinkedDocCreated', {
page: 'sidebar',
module: 'doc',
control: 'add linked doc button',
});
track.$.navigationPanel.docs.createDoc({ control: 'linkDoc' });
track.$.navigationPanel.docs.linkDoc({ control: 'createDoc' });
workbenchService.workbench.openDoc(newDoc.id);
options.openNodeCollapsed();
}, [docId, options, docsService, workbenchService.workbench]);
const handleToggleFavoriteDoc = useCallback(() => {
compatibleFavoriteItemsAdapter.toggle(docId, 'doc');
mixpanel.track('ToggleFavorite', {
page: 'sidebar',
module: 'doc',
control: 'toggle favorite button',
track.$.navigationPanel.organize.toggleFavorite({
type: 'doc',
id: docId,
});
}, [docId, compatibleFavoriteItemsAdapter]);

View File

@@ -15,7 +15,7 @@ import {
useSelectDoc,
useSelectTag,
} from '@affine/core/components/page-list/selector';
import { mixpanel } from '@affine/core/mixpanel';
import { track } from '@affine/core/mixpanel';
import {
type FolderNode,
OrganizeService,
@@ -178,10 +178,8 @@ export const ExplorerFolderNodeFolder = ({
const handleDelete = useCallback(() => {
node.delete();
mixpanel.track('FolderDeleted', {
page: 'sidebar',
module: 'organize',
control: `delete folder`,
track.$.navigationPanel.organize.deleteOrganizeItem({
type: 'folder',
});
notify.success({
title: t['com.affine.rootAppSidebar.organize.delete.notify-title']({
@@ -229,23 +227,14 @@ export const ExplorerFolderNodeFolder = ({
return;
}
node.moveHere(data.source.data.entity.id, node.indexAt('before'));
mixpanel.track('FolderMoved', {
page: 'sidebar',
module: 'organize',
control: 'drop folder at folder',
type: 'folder',
id: data.source.data.entity.id,
});
track.$.navigationPanel.organize.moveOrganizeItem({ type: 'folder' });
} else if (
data.source.data.from?.at === 'explorer:organize:folder-node'
) {
node.moveHere(data.source.data.from.nodeId, node.indexAt('before'));
mixpanel.track('FolderLinkMoved', {
page: 'sidebar',
module: 'organize',
control: 'drop folder link at folder',
type: data.source.data.entity?.type,
id: data.source.data.entity?.id,
track.$.navigationPanel.organize.moveOrganizeItem({
type: 'link',
target: data.source.data.entity?.type,
});
} else if (
data.source.data.entity?.type === 'collection' ||
@@ -257,12 +246,9 @@ export const ExplorerFolderNodeFolder = ({
data.source.data.entity.id,
node.indexAt('before')
);
mixpanel.track('FolderLinkCreated', {
page: 'sidebar',
module: 'organize',
control: 'drop entity at folder',
type: data.source.data.entity?.type,
id: data.source.data.entity?.id,
track.$.navigationPanel.organize.createOrganizeItem({
type: 'link',
target: data.source.data.entity?.type,
});
}
} else {
@@ -312,23 +298,13 @@ export const ExplorerFolderNodeFolder = ({
return;
}
node.moveHere(data.source.data.entity.id, node.indexAt('before'));
mixpanel.track('FolderMoved', {
page: 'sidebar',
module: 'organize',
control: 'drop folder at folder',
type: 'folder',
id: data.source.data.entity.id,
});
track.$.navigationPanel.organize.moveOrganizeItem({ type: 'folder' });
} else if (
data.source.data.from?.at === 'explorer:organize:folder-node'
) {
node.moveHere(data.source.data.from.nodeId, node.indexAt('before'));
mixpanel.track('FolderLinkMoved', {
page: 'sidebar',
module: 'organize',
control: 'drop folder link at folder',
track.$.navigationPanel.organize.moveOrganizeItem({
type: data.source.data.entity?.type,
id: data.source.data.entity?.id,
});
} else if (
data.source.data.entity?.type === 'collection' ||
@@ -340,12 +316,9 @@ export const ExplorerFolderNodeFolder = ({
data.source.data.entity.id,
node.indexAt('before')
);
mixpanel.track('FolderLinkCreated', {
page: 'sidebar',
module: 'organize',
control: 'drop entity at folder',
type: data.source.data.entity?.type,
id: data.source.data.entity?.id,
track.$.navigationPanel.organize.createOrganizeItem({
type: 'link',
target: data.source.data.entity?.type,
});
}
},
@@ -374,13 +347,7 @@ export const ExplorerFolderNodeFolder = ({
data.source.data.entity.id,
node.indexAt(at, dropAtNode.id)
);
mixpanel.track('FolderMoved', {
page: 'sidebar',
module: 'organize',
control: `drop folder ${at === 'before' ? 'above' : 'below'} node`,
type: 'folder',
id: data.source.data.entity?.id,
});
track.$.navigationPanel.organize.moveOrganizeItem({ type: 'folder' });
} else if (
data.source.data.from?.at === 'explorer:organize:folder-node'
) {
@@ -388,12 +355,9 @@ export const ExplorerFolderNodeFolder = ({
data.source.data.from.nodeId,
node.indexAt(at, dropAtNode.id)
);
mixpanel.track('FolderLinkMoved', {
page: 'sidebar',
module: 'organize',
control: `drop folder link ${at === 'before' ? 'above' : 'below'} node`,
type: data.source.data.entity?.type,
id: data.source.data.entity?.id,
track.$.navigationPanel.organize.moveOrganizeItem({
type: 'link',
target: data.source.data.entity?.type,
});
} else if (
data.source.data.entity?.type === 'collection' ||
@@ -405,12 +369,10 @@ export const ExplorerFolderNodeFolder = ({
data.source.data.entity.id,
node.indexAt(at, dropAtNode.id)
);
mixpanel.track('FolderLinkCreated', {
page: 'sidebar',
module: 'organize',
control: `drop entity ${at === 'before' ? 'above' : 'below'} node`,
type: data.source.data.entity?.type,
id: data.source.data.entity?.id,
track.$.navigationPanel.organize.createOrganizeItem({
type: 'link',
target: data.source.data.entity?.type,
});
}
} else if (data.treeInstruction?.type === 'reparent') {
@@ -560,17 +522,10 @@ export const ExplorerFolderNodeFolder = ({
const newDoc = docsService.createDoc();
node.createLink('doc', newDoc.id, node.indexAt('before'));
workbenchService.workbench.openDoc(newDoc.id);
mixpanel.track('DocCreated', {
page: 'sidebar',
module: 'organize',
control: `folder new doc button`,
});
mixpanel.track('FolderLinkCreated', {
page: 'sidebar',
module: 'organize',
control: `folder new doc button`,
type: 'doc',
id: newDoc.id,
track.$.navigationPanel.folders.createDoc();
track.$.navigationPanel.organize.createOrganizeItem({
type: 'link',
target: 'doc',
});
setCollapsed(false);
}, [docsService, node, workbenchService.workbench]);
@@ -580,11 +535,7 @@ export const ExplorerFolderNodeFolder = ({
t['com.affine.rootAppSidebar.organize.new-folders'](),
node.indexAt('before')
);
mixpanel.track('FolderCreated', {
page: 'sidebar',
module: 'organize',
control: `create sub folder`,
});
track.$.navigationPanel.organize.createOrganizeItem({ type: 'folder' });
setCollapsed(false);
setNewFolderId(newFolderId);
}, [node, t]);
@@ -612,13 +563,6 @@ export const ExplorerFolderNodeFolder = ({
newItemIds.forEach(id => {
node.createLink(type, id, node.indexAt('after'));
mixpanel.track('FolderLinkCreated', {
page: 'sidebar',
module: 'organize',
control: `add selector`,
type,
id,
});
});
removedItems.forEach(node => node.delete());
const updated = newItemIds.length + removedItems.length;
@@ -627,6 +571,10 @@ export const ExplorerFolderNodeFolder = ({
.catch(err => {
console.error(`Unexpected error while selecting ${type}`, err);
});
track.$.navigationPanel.organize.createOrganizeItem({
type: 'link',
target: type,
});
},
[
children,
@@ -763,7 +711,7 @@ export const ExplorerFolderNodeFolder = ({
handleCreateSubfolder,
handleDelete,
handleNewDoc,
node.id,
node,
t,
]);
@@ -774,23 +722,6 @@ export const ExplorerFolderNodeFolder = ({
return folderOperations;
}, [additionalOperations, folderOperations]);
const handleDeleteChildren = useCallback((node: FolderNode) => {
node.delete();
if (node.type$.value === 'folder') {
mixpanel.track('FolderDeleted', {
page: 'sidebar',
module: 'organize',
control: 'remove from folder button',
});
} else {
mixpanel.track('FolderLinkDeleted', {
page: 'sidebar',
module: 'organize',
control: 'remove from folder button',
});
}
}, []);
const childrenOperations = useCallback(
// eslint-disable-next-line @typescript-eslint/ban-types
(type: string, node: FolderNode) => {
@@ -806,7 +737,9 @@ export const ExplorerFolderNodeFolder = ({
<RemoveFolderIcon />
</MenuIcon>
}
onClick={() => handleDeleteChildren(node)}
data-event-props="$.navigationPanel.organize.deleteOrganizeItem"
data-event-args-type={node.type$.value}
onClick={node.delete}
>
{t['com.affine.rootAppSidebar.organize.delete-from-folder']()}
</MenuItem>
@@ -816,7 +749,7 @@ export const ExplorerFolderNodeFolder = ({
}
return [];
},
[handleDeleteChildren, t]
[t]
);
const handleCollapsedChange = useCallback((collapsed: boolean) => {

View File

@@ -3,7 +3,7 @@ import {
type DropTargetOptions,
toast,
} from '@affine/component';
import { mixpanel } from '@affine/core/mixpanel';
import { track } from '@affine/core/mixpanel';
import type { Tag } from '@affine/core/modules/tag';
import { TagService } from '@affine/core/modules/tag';
import type { AffineDNDData } from '@affine/core/types/dnd';
@@ -84,10 +84,8 @@ export const ExplorerTagNode = ({
(newName: string) => {
if (tagRecord && tagRecord.value$.value !== newName) {
tagRecord.rename(newName);
mixpanel.track('TagRenamed', {
page: 'sidebar',
module: 'tag',
control: 'tag rename',
track.$.navigationPanel.organize.renameOrganizeItem({
type: 'tag',
});
}
},
@@ -99,10 +97,8 @@ export const ExplorerTagNode = ({
if (data.treeInstruction?.type === 'make-child' && tagRecord) {
if (data.source.data.entity?.type === 'doc') {
tagRecord.tag(data.source.data.entity.id);
mixpanel.track('DocTagged', {
page: 'sidebar',
module: 'tag',
control: 'drop doc on tag',
track.$.navigationPanel.tags.tagDoc({
control: 'drag',
});
} else {
toast(t['com.affine.rootAppSidebar.tag.doc-only']());

View File

@@ -6,7 +6,7 @@ import {
toast,
} from '@affine/component';
import { useAppSettingHelper } from '@affine/core/hooks/affine/use-app-setting-helper';
import { mixpanel } from '@affine/core/mixpanel';
import { track } from '@affine/core/mixpanel';
import { FavoriteService } from '@affine/core/modules/favorite';
import { TagService } from '@affine/core/modules/tag';
import { WorkbenchService } from '@affine/core/modules/workbench';
@@ -51,16 +51,7 @@ export const useExplorerTagNodeOperations = (
if (tagRecord) {
const newDoc = docsService.createDoc();
tagRecord?.tag(newDoc.id);
mixpanel.track('DocCreated', {
page: 'sidebar',
module: 'tag',
control: 'add doc button',
});
mixpanel.track('DocTagged', {
page: 'sidebar',
module: 'tag',
control: 'add doc button',
});
track.$.navigationPanel.tags.createDoc();
workbenchService.workbench.openDoc(newDoc.id);
openNodeCollapsed();
}
@@ -68,11 +59,7 @@ export const useExplorerTagNodeOperations = (
const handleMoveToTrash = useCallback(() => {
tagService.tagList.deleteTag(tagId);
mixpanel.track('TagDeleted', {
page: 'sidebar',
module: 'tag',
control: 'remove tag button',
});
track.$.navigationPanel.organize.deleteOrganizeItem({ type: 'tag' });
toast(t['com.affine.tags.delete-tags.toast']());
}, [t, tagId, tagService.tagList]);
@@ -80,21 +67,13 @@ export const useExplorerTagNodeOperations = (
workbenchService.workbench.openTag(tagId, {
at: 'beside',
});
mixpanel.track('OpenInSplitView', {
page: 'sidebar',
module: 'tag',
control: 'open in split view button',
});
track.$.navigationPanel.organize.openInSplitView({ type: 'tag' });
}, [tagId, workbenchService]);
const handleToggleFavoriteTag = useCallback(() => {
favoriteService.favoriteList.toggle('tag', tagId);
mixpanel.track('ToggleFavorite', {
page: 'sidebar',
module: 'tag',
control: 'toggle favorite tag button',
track.$.navigationPanel.organize.toggleFavorite({
type: 'tag',
id: tagId,
});
}, [favoriteService, tagId]);
@@ -102,11 +81,7 @@ export const useExplorerTagNodeOperations = (
workbenchService.workbench.openTag(tagId, {
at: 'new-tab',
});
mixpanel.track('OpenInNewTab', {
page: 'sidebar',
module: 'tag',
control: 'open in new tab button',
});
track.$.navigationPanel.organize.openInNewTab({ type: 'tag' });
}, [tagId, workbenchService]);
return useMemo(

View File

@@ -1,7 +1,7 @@
import { IconButton } from '@affine/component';
import { useEditCollectionName } from '@affine/core/components/page-list';
import { createEmptyCollection } from '@affine/core/components/page-list/use-collection-manager';
import { mixpanel } from '@affine/core/mixpanel';
import { track } from '@affine/core/mixpanel';
import { CollectionService } from '@affine/core/modules/collection';
import { ExplorerTreeRoot } from '@affine/core/modules/explorer/views/tree';
import { WorkbenchService } from '@affine/core/modules/workbench';
@@ -35,10 +35,8 @@ export const ExplorerCollections = () => {
.then(name => {
const id = nanoid();
collectionService.addCollection(createEmptyCollection(id, { name }));
mixpanel.track('CollectionCreated', {
page: 'sidebar',
module: 'collections',
control: 'new collection button',
track.$.navigationPanel.organize.createOrganizeItem({
type: 'collection',
});
workbenchService.workbench.openCollection(id);
explorerSection.setCollapsed(false);

View File

@@ -4,7 +4,7 @@ import {
IconButton,
useDropTarget,
} from '@affine/component';
import { mixpanel } from '@affine/core/mixpanel';
import { track } from '@affine/core/mixpanel';
import {
DropEffect,
type ExplorerTreeNodeDropEffect,
@@ -59,12 +59,9 @@ export const ExplorerFavorites = () => {
data.source.data.entity.id,
favoriteService.favoriteList.indexAt('before')
);
mixpanel.track('AddFavorite', {
page: 'sidebar',
module: 'favorite',
control: 'drop entity to favorite',
track.$.navigationPanel.organize.toggleFavorite({
type: data.source.data.entity.type,
id: data.source.data.entity.id,
on: true,
});
explorerSection.setCollapsed(false);
}
@@ -98,18 +95,6 @@ export const ExplorerFavorites = () => {
newDoc.id,
favoriteService.favoriteList.indexAt('before')
);
mixpanel.track('DocCreated', {
page: 'sidebar',
module: 'favorites',
control: 'new favorite doc button',
});
mixpanel.track('AddFavorite', {
page: 'sidebar',
module: 'favorite',
control: 'new favorite doc button',
type: 'doc',
id: newDoc.id,
});
workbenchService.workbench.openDoc(newDoc.id);
explorerSection.setCollapsed(false);
}, [
@@ -144,12 +129,8 @@ export const ExplorerFavorites = () => {
favorite
)
);
mixpanel.track('ReorderFavorite', {
page: 'sidebar',
module: 'favorite',
control: 'drop to reorder favorite',
track.$.navigationPanel.organize.orderOrganizeItem({
type: data.source.data.entity.type,
id: data.source.data.entity.id,
});
} else if (
data.source.data.entity?.type &&
@@ -165,12 +146,9 @@ export const ExplorerFavorites = () => {
favorite
)
);
mixpanel.track('AddFavorite', {
page: 'sidebar',
module: 'favorite',
control: 'drop entity to favorite',
track.$.navigationPanel.organize.toggleFavorite({
type: data.source.data.entity.type,
id: data.source.data.entity.id,
on: true,
});
} else {
return; // not supported
@@ -238,6 +216,8 @@ export const ExplorerFavorites = () => {
<>
<IconButton
data-testid="explorer-bar-add-favorite-button"
data-event-props="$.navigationPanel.favorites.createDoc"
data-event-args-control="addFavorite"
onClick={handleCreateNewFavoriteDoc}
size="16"
tooltip={t[

View File

@@ -1,5 +1,5 @@
import { IconButton, useConfirmModal } from '@affine/component';
import { mixpanel } from '@affine/core/mixpanel';
import { track } from '@affine/core/mixpanel';
import { ExplorerTreeRoot } from '@affine/core/modules/explorer/views/tree';
import { FavoriteItemsAdapter } from '@affine/core/modules/properties';
import { Trans, useI18n } from '@affine/i18n';
@@ -58,18 +58,8 @@ export const ExplorerMigrationFavorites = () => {
t['com.affine.rootAppSidebar.migration-data.clean-all.cancel'](),
onConfirm() {
favoriteItemsAdapter.clearAll();
mixpanel.track('AllMigrationDataCleared', {
page: 'sidebar',
module: 'migration data',
control: 'clear button',
});
},
});
mixpanel.track('ClickClearMigrationDataButton', {
page: 'sidebar',
module: 'migration data',
control: 'clear button',
});
}, [favoriteItemsAdapter, openConfirmModal, t]);
const handleClickHelp = useCallback(() => {
@@ -92,11 +82,7 @@ export const ExplorerMigrationFavorites = () => {
},
},
});
mixpanel.track('OpenMigrationDataHelp', {
page: 'sidebar',
module: 'migration data',
control: 'help button',
});
track.$.navigationPanel.migrationData.openMigrationDataHelp();
}, [handleClickClear, openConfirmModal, t]);
if (favorites.length === 0) {

View File

@@ -4,7 +4,7 @@ import {
IconButton,
toast,
} from '@affine/component';
import { mixpanel } from '@affine/core/mixpanel';
import { track } from '@affine/core/mixpanel';
import {
type ExplorerTreeNodeDropEffect,
ExplorerTreeRoot,
@@ -46,11 +46,7 @@ export const ExplorerOrganize = () => {
'New Folder',
rootFolder.indexAt('before')
);
mixpanel.track('FolderCreated', {
page: 'sidebar',
module: 'organize',
control: 'new folder',
});
track.$.navigationPanel.organize.createOrganizeItem({ type: 'folder' });
setNewFolderId(newFolderId);
explorerSection.setCollapsed(false);
}, [explorerSection, rootFolder]);
@@ -71,13 +67,7 @@ export const ExplorerOrganize = () => {
data.source.data.entity.id,
rootFolder.indexAt(at, node.id)
);
mixpanel.track('FolderMoved', {
page: 'sidebar',
module: 'organize',
control: 'drop at root',
type: 'folder',
id: node.id,
});
track.$.navigationPanel.organize.moveOrganizeItem({ type: 'folder' });
} else {
toast(t['com.affine.rootAppSidebar.organize.root-folder-only']());
}

View File

@@ -1,5 +1,5 @@
import { IconButton } from '@affine/component';
import { mixpanel } from '@affine/core/mixpanel';
import { track } from '@affine/core/mixpanel';
import { ExplorerTreeRoot } from '@affine/core/modules/explorer/views/tree';
import type { Tag } from '@affine/core/modules/tag';
import { TagService } from '@affine/core/modules/tag';
@@ -32,11 +32,7 @@ export const ExplorerTags = () => {
tagService.randomTagColor()
);
setCreatedTag(newTags);
mixpanel.track('TagCreated', {
page: 'sidebar',
module: 'tags',
control: 'new tag button',
});
track.$.navigationPanel.organize.createOrganizeItem({ type: 'tag' });
explorerSection.setCollapsed(false);
}, [explorerSection, t, tagService]);

View File

@@ -8,7 +8,7 @@ import { EMPTY, mergeMap, switchMap } from 'rxjs';
import { generateSubscriptionCallbackLink } from '../hooks/affine/use-subscription-notify';
import { RouteLogic, useNavigateHelper } from '../hooks/use-navigate-helper';
import { mixpanel } from '../mixpanel';
import { mixpanel, track } from '../mixpanel';
import { AuthService, SubscriptionService } from '../modules/cloud';
import { container } from './subscribe.css';
@@ -70,9 +70,9 @@ export const Component = () => {
: !!subscriptionService.subscription.pro$.value;
if (!subscribed) {
setMessage('Creating checkout...');
mixpanel.track('PlanUpgradeStarted', {
type: plan,
category: recurring,
track.subscriptionLanding.$.$.checkout({
plan: receivedPlan,
recurring: receivedRecurring,
});
try {
const account = authService.session.account$.value;

View File

@@ -7,7 +7,7 @@ import {
import { Header } from '@affine/core/components/pure/header';
import { WorkspaceModeFilterTab } from '@affine/core/components/pure/workspace-mode-filter-tab';
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
import { mixpanel } from '@affine/core/mixpanel';
import { track } from '@affine/core/mixpanel';
import type { Filter } from '@affine/env/filter';
import { PlusIcon } from '@blocksuite/icons/rc';
import { useService, WorkspaceService } from '@toeverything/infra';
@@ -32,21 +32,12 @@ export const AllPageHeader = ({
const onImportFile = useAsyncCallback(async () => {
const options = await importFile();
if (options.isWorkspaceFile) {
mixpanel.track('WorkspaceCreated', {
page: 'doc library',
segment: 'all page',
module: 'doc list header',
control: 'import button',
type: 'imported workspace',
track.allDocs.header.actions.createWorkspace({
control: 'import',
});
} else {
mixpanel.track('DocCreated', {
page: 'doc library',
segment: 'all page',
module: 'doc list header',
control: 'import button',
type: 'imported doc',
// category
track.allDocs.header.actions.createDoc({
control: 'import',
});
}
}, [importFile]);