mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-25 02:13:00 +08:00
chore(core): replace with new track impl (#7735)
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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(() => {
|
||||
|
||||
@@ -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]);
|
||||
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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]);
|
||||
|
||||
|
||||
@@ -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']()}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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} />
|
||||
)
|
||||
) : (
|
||||
<>
|
||||
|
||||
@@ -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]);
|
||||
|
||||
@@ -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' })}
|
||||
|
||||
@@ -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]);
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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');
|
||||
}, []);
|
||||
|
||||
@@ -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(() => {
|
||||
|
||||
@@ -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',
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 = (
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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]);
|
||||
|
||||
|
||||
@@ -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]);
|
||||
|
||||
@@ -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]);
|
||||
|
||||
|
||||
@@ -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?.();
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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} />
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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]);
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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':
|
||||
|
||||
@@ -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',
|
||||
});
|
||||
},
|
||||
})
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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]);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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[] = [],
|
||||
|
||||
@@ -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',
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
@@ -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]);
|
||||
|
||||
|
||||
@@ -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']());
|
||||
|
||||
@@ -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]);
|
||||
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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']());
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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[
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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']());
|
||||
}
|
||||
|
||||
@@ -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]);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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]);
|
||||
|
||||
Reference in New Issue
Block a user