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 { openSettingModalAtom, openWorkspaceListModalAtom } from '../atoms';
|
||||||
import type { useNavigateHelper } from '../hooks/use-navigate-helper';
|
import type { useNavigateHelper } from '../hooks/use-navigate-helper';
|
||||||
import { mixpanel, track } from '../mixpanel';
|
import { track } from '../mixpanel';
|
||||||
import { registerAffineCommand } from './registry';
|
import { registerAffineCommand } from './registry';
|
||||||
|
|
||||||
export function registerAffineNavigationCommands({
|
export function registerAffineNavigationCommands({
|
||||||
@@ -93,10 +93,7 @@ export function registerAffineNavigationCommands({
|
|||||||
icon: <ArrowRightBigIcon />,
|
icon: <ArrowRightBigIcon />,
|
||||||
label: t['com.affine.cmdk.affine.navigation.open-account-settings'](),
|
label: t['com.affine.cmdk.affine.navigation.open-account-settings'](),
|
||||||
run() {
|
run() {
|
||||||
mixpanel.track('AccountSettingsViewed', {
|
track.$.cmdk.settings.openSettings({ to: 'account' });
|
||||||
// page:
|
|
||||||
segment: 'cmdk',
|
|
||||||
});
|
|
||||||
store.set(openSettingModalAtom, s => ({
|
store.set(openSettingModalAtom, s => ({
|
||||||
activeTab: 'account',
|
activeTab: 'account',
|
||||||
open: !s.open,
|
open: !s.open,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Button, FlexWrapper, notify } from '@affine/component';
|
import { Button, FlexWrapper, notify } from '@affine/component';
|
||||||
import { openSettingModalAtom } from '@affine/core/atoms';
|
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 { SubscriptionService } from '@affine/core/modules/cloud';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import { AiIcon } from '@blocksuite/icons/rc';
|
import { AiIcon } from '@blocksuite/icons/rc';
|
||||||
@@ -65,11 +65,7 @@ export const AIOnboardingEdgeless = () => {
|
|||||||
const mode = useLiveData(doc.mode$);
|
const mode = useLiveData(doc.mode$);
|
||||||
|
|
||||||
const goToPricingPlans = useCallback(() => {
|
const goToPricingPlans = useCallback(() => {
|
||||||
mixpanel.track('PlansViewed', {
|
track.$.aiOnboarding.dialog.viewPlans();
|
||||||
page: 'whiteboard editor',
|
|
||||||
segment: 'ai onboarding',
|
|
||||||
module: 'whiteboard dialog',
|
|
||||||
});
|
|
||||||
setSettingModal({
|
setSettingModal({
|
||||||
open: true,
|
open: true,
|
||||||
activeTab: 'plans',
|
activeTab: 'plans',
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Button, IconButton, Modal } from '@affine/component';
|
import { Button, IconButton, Modal } from '@affine/component';
|
||||||
import { openSettingModalAtom } from '@affine/core/atoms';
|
import { openSettingModalAtom } from '@affine/core/atoms';
|
||||||
import { useBlurRoot } from '@affine/core/hooks/use-blur-root';
|
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 { AuthService, SubscriptionService } from '@affine/core/modules/cloud';
|
||||||
import { Trans, useI18n } from '@affine/i18n';
|
import { Trans, useI18n } from '@affine/i18n';
|
||||||
import { ArrowLeftSmallIcon } from '@blocksuite/icons/rc';
|
import { ArrowLeftSmallIcon } from '@blocksuite/icons/rc';
|
||||||
@@ -116,11 +116,7 @@ export const AIOnboardingGeneral = () => {
|
|||||||
activeTab: 'plans',
|
activeTab: 'plans',
|
||||||
scrollAnchor: 'aiPricingPlan',
|
scrollAnchor: 'aiPricingPlan',
|
||||||
});
|
});
|
||||||
mixpanel.track('PlansViewed', {
|
track.$.aiOnboarding.dialog.viewPlans();
|
||||||
page: 'whiteboard editor',
|
|
||||||
segment: 'ai onboarding',
|
|
||||||
module: 'general',
|
|
||||||
});
|
|
||||||
closeAndDismiss();
|
closeAndDismiss();
|
||||||
}, [closeAndDismiss, setSettingModal]);
|
}, [closeAndDismiss, setSettingModal]);
|
||||||
const onPrev = useCallback(() => {
|
const onPrev = useCallback(() => {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { notify, Skeleton } from '@affine/component';
|
import { notify, Skeleton } from '@affine/component';
|
||||||
import { Button } from '@affine/component/ui/button';
|
import { Button } from '@affine/component/ui/button';
|
||||||
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
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 { OAuthProviderType } from '@affine/graphql';
|
||||||
import { GithubIcon, GoogleDuotoneIcon } from '@blocksuite/icons/rc';
|
import { GithubIcon, GoogleDuotoneIcon } from '@blocksuite/icons/rc';
|
||||||
import { useLiveData, useService } from '@toeverything/infra';
|
import { useLiveData, useService } from '@toeverything/infra';
|
||||||
@@ -75,7 +75,7 @@ function OAuthProvider({
|
|||||||
notify.error({ title: 'Failed to sign in, please try again.' });
|
notify.error({ title: 'Failed to sign in, please try again.' });
|
||||||
} finally {
|
} finally {
|
||||||
setIsConnecting(false);
|
setIsConnecting(false);
|
||||||
mixpanel.track('OAuth', { provider });
|
track.$.$.auth.oauth({ provider });
|
||||||
}
|
}
|
||||||
}, [authService, provider, redirectUri]);
|
}, [authService, provider, redirectUri]);
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { AuthInput, ModalHeader } from '@affine/component/auth-components';
|
|||||||
import { Button } from '@affine/component/ui/button';
|
import { Button } from '@affine/component/ui/button';
|
||||||
import { authAtom } from '@affine/core/atoms';
|
import { authAtom } from '@affine/core/atoms';
|
||||||
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
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 { Trans, useI18n } from '@affine/i18n';
|
||||||
import { ArrowRightBigIcon } from '@blocksuite/icons/rc';
|
import { ArrowRightBigIcon } from '@blocksuite/icons/rc';
|
||||||
import { useLiveData, useService } from '@toeverything/infra';
|
import { useLiveData, useService } from '@toeverything/infra';
|
||||||
@@ -75,9 +75,7 @@ export const SignIn: FC<AuthPanelProps> = ({
|
|||||||
if (hasPassword) {
|
if (hasPassword) {
|
||||||
setAuthState('signInWithPassword');
|
setAuthState('signInWithPassword');
|
||||||
} else {
|
} else {
|
||||||
mixpanel.track('SignIn', {
|
track.$.$.auth.signIn();
|
||||||
email,
|
|
||||||
});
|
|
||||||
await authService.sendEmailMagicLink(
|
await authService.sendEmailMagicLink(
|
||||||
email,
|
email,
|
||||||
verifyToken,
|
verifyToken,
|
||||||
@@ -93,9 +91,7 @@ export const SignIn: FC<AuthPanelProps> = ({
|
|||||||
challenge,
|
challenge,
|
||||||
searchParams.get('redirect_uri')
|
searchParams.get('redirect_uri')
|
||||||
);
|
);
|
||||||
mixpanel.track('SignUp', {
|
track.$.$.auth.signUp();
|
||||||
email,
|
|
||||||
});
|
|
||||||
setAuthState('afterSignUpSendEmail');
|
setAuthState('afterSignUpSendEmail');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { Tooltip } from '@affine/component/ui/tooltip';
|
import { Tooltip } from '@affine/component/ui/tooltip';
|
||||||
import { mixpanel } from '@affine/core/mixpanel';
|
|
||||||
import { SubscriptionPlan } from '@affine/graphql';
|
import { SubscriptionPlan } from '@affine/graphql';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import { useLiveData, useServices } from '@toeverything/infra';
|
import { useLiveData, useServices } from '@toeverything/infra';
|
||||||
@@ -44,10 +43,6 @@ export const UserPlanButton = () => {
|
|||||||
activeTab: 'plans',
|
activeTab: 'plans',
|
||||||
scrollAnchor: 'cloudPricingPlan',
|
scrollAnchor: 'cloudPricingPlan',
|
||||||
});
|
});
|
||||||
mixpanel.track('PlansViewed', {
|
|
||||||
segment: 'settings panel',
|
|
||||||
module: 'profile and badge',
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
[setSettingModalAtom]
|
[setSettingModalAtom]
|
||||||
);
|
);
|
||||||
@@ -72,6 +67,7 @@ export const UserPlanButton = () => {
|
|||||||
data-is-believer={isBeliever ? 'true' : undefined}
|
data-is-believer={isBeliever ? 'true' : undefined}
|
||||||
className={styles.userPlanButton}
|
className={styles.userPlanButton}
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
|
data-event-props="$.settingsPanel.profileAndBadge.viewPlans"
|
||||||
>
|
>
|
||||||
{planLabel}
|
{planLabel}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import type { ConfirmModalProps } from '@affine/component/ui/modal';
|
|||||||
import { ConfirmModal, Modal } from '@affine/component/ui/modal';
|
import { ConfirmModal, Modal } from '@affine/component/ui/modal';
|
||||||
import { authAtom } from '@affine/core/atoms';
|
import { authAtom } from '@affine/core/atoms';
|
||||||
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
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 { DebugLogger } from '@affine/debug';
|
||||||
import { apis } from '@affine/electron-api';
|
import { apis } from '@affine/electron-api';
|
||||||
import { WorkspaceFlavour } from '@affine/env/workspace';
|
import { WorkspaceFlavour } from '@affine/env/workspace';
|
||||||
@@ -222,9 +222,7 @@ export const CreateWorkspaceModal = ({
|
|||||||
|
|
||||||
const onConfirmName = useAsyncCallback(
|
const onConfirmName = useAsyncCallback(
|
||||||
async (name: string, workspaceFlavour: WorkspaceFlavour) => {
|
async (name: string, workspaceFlavour: WorkspaceFlavour) => {
|
||||||
mixpanel.track('CreateWorkspace', {
|
track.$.$.$.createWorkspace({ flavour: workspaceFlavour });
|
||||||
workspaceFlavour,
|
|
||||||
});
|
|
||||||
if (loading) return;
|
if (loading) return;
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { Button, IconButton } from '@affine/component/ui/button';
|
|||||||
import { Modal, useConfirmModal } from '@affine/component/ui/modal';
|
import { Modal, useConfirmModal } from '@affine/component/ui/modal';
|
||||||
import { openSettingModalAtom } from '@affine/core/atoms';
|
import { openSettingModalAtom } from '@affine/core/atoms';
|
||||||
import { useDocCollectionPageTitle } from '@affine/core/hooks/use-block-suite-workspace-page-title';
|
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 { WorkspacePermissionService } from '@affine/core/modules/permissions';
|
||||||
import { WorkspaceQuotaService } from '@affine/core/modules/quota';
|
import { WorkspaceQuotaService } from '@affine/core/modules/quota';
|
||||||
import { i18nTime, Trans, useI18n } from '@affine/i18n';
|
import { i18nTime, Trans, useI18n } from '@affine/i18n';
|
||||||
@@ -108,18 +108,17 @@ const HistoryEditorPreview = ({
|
|||||||
mode,
|
mode,
|
||||||
title,
|
title,
|
||||||
}: HistoryEditorPreviewProps) => {
|
}: HistoryEditorPreviewProps) => {
|
||||||
const onSwitchToPageMode = useCallback(() => {
|
const { onSwitchToPageMode, onSwitchToEdgelessMode } = useMemo(
|
||||||
mixpanel.track('Button', {
|
() => ({
|
||||||
resolve: 'HistorySwitchToPageMode',
|
onSwitchToPageMode: () => {
|
||||||
});
|
onModeChange('page');
|
||||||
onModeChange('page');
|
},
|
||||||
}, [onModeChange]);
|
onSwitchToEdgelessMode: () => {
|
||||||
const onSwitchToEdgelessMode = useCallback(() => {
|
onModeChange('edgeless');
|
||||||
mixpanel.track('Button', {
|
},
|
||||||
resolve: 'HistorySwitchToEdgelessMode',
|
}),
|
||||||
});
|
[onModeChange]
|
||||||
onModeChange('edgeless');
|
);
|
||||||
}, [onModeChange]);
|
|
||||||
|
|
||||||
const content = useMemo(() => {
|
const content = useMemo(() => {
|
||||||
return (
|
return (
|
||||||
@@ -129,11 +128,15 @@ const HistoryEditorPreview = ({
|
|||||||
<PageSwitchItem
|
<PageSwitchItem
|
||||||
data-testid="switch-page-mode-button"
|
data-testid="switch-page-mode-button"
|
||||||
active={mode === 'page'}
|
active={mode === 'page'}
|
||||||
|
data-event-props="$.docHistory.$.switchPageMode"
|
||||||
|
data-event-args-type="page"
|
||||||
onClick={onSwitchToPageMode}
|
onClick={onSwitchToPageMode}
|
||||||
/>
|
/>
|
||||||
<EdgelessSwitchItem
|
<EdgelessSwitchItem
|
||||||
data-testid="switch-edgeless-mode-button"
|
data-testid="switch-edgeless-mode-button"
|
||||||
active={mode === 'edgeless'}
|
active={mode === 'edgeless'}
|
||||||
|
data-event-props="$.docHistory.$.switchPageMode"
|
||||||
|
data-event-args-type="edgeless"
|
||||||
onClick={onSwitchToEdgelessMode}
|
onClick={onSwitchToEdgelessMode}
|
||||||
/>
|
/>
|
||||||
</StyledEditorModeSwitch>
|
</StyledEditorModeSwitch>
|
||||||
@@ -229,9 +232,7 @@ const PlanPrompt = () => {
|
|||||||
activeTab: 'plans',
|
activeTab: 'plans',
|
||||||
scrollAnchor: 'cloudPricingPlan',
|
scrollAnchor: 'cloudPricingPlan',
|
||||||
});
|
});
|
||||||
mixpanel.track('PlansViewed', {
|
track.$.docHistory.$.viewPlans();
|
||||||
segment: 'doc history',
|
|
||||||
});
|
|
||||||
}, [setSettingModalAtom]);
|
}, [setSettingModalAtom]);
|
||||||
|
|
||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
@@ -562,9 +563,7 @@ export const GlobalPageHistoryModal = () => {
|
|||||||
const workspace = useService(WorkspaceService).workspace;
|
const workspace = useService(WorkspaceService).workspace;
|
||||||
const handleOpenChange = useCallback(
|
const handleOpenChange = useCallback(
|
||||||
(open: boolean) => {
|
(open: boolean) => {
|
||||||
mixpanel.track('Button', {
|
track.$.docHistory.$[open ? 'open' : 'close']();
|
||||||
resolve: open ? 'OpenPageHistoryModal' : 'ClosePageHistoryModal',
|
|
||||||
});
|
|
||||||
setState(prev => ({
|
setState(prev => ({
|
||||||
...prev,
|
...prev,
|
||||||
open,
|
open,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { ConfirmModal } from '@affine/component/ui/modal';
|
import { ConfirmModal } from '@affine/component/ui/modal';
|
||||||
import { openQuotaModalAtom, openSettingModalAtom } from '@affine/core/atoms';
|
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 { UserQuotaService } from '@affine/core/modules/cloud';
|
||||||
import { WorkspacePermissionService } from '@affine/core/modules/permissions';
|
import { WorkspacePermissionService } from '@affine/core/modules/permissions';
|
||||||
import { WorkspaceQuotaService } from '@affine/core/modules/quota';
|
import { WorkspaceQuotaService } from '@affine/core/modules/quota';
|
||||||
@@ -50,11 +50,7 @@ export const CloudQuotaModal = () => {
|
|||||||
scrollAnchor: 'cloudPricingPlan',
|
scrollAnchor: 'cloudPricingPlan',
|
||||||
});
|
});
|
||||||
|
|
||||||
mixpanel.track('PlansViewed', {
|
track.$.paywall.storage.viewPlans();
|
||||||
segment: 'payment wall',
|
|
||||||
category: 'payment wall storage',
|
|
||||||
});
|
|
||||||
|
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
}, [setOpen, setSettingModalAtom]);
|
}, [setOpen, setSettingModalAtom]);
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import { Button, ErrorMessage, Skeleton } from '@affine/component';
|
import { Button, ErrorMessage, Skeleton } from '@affine/component';
|
||||||
import { SettingRow } from '@affine/component/setting-components';
|
import { SettingRow } from '@affine/component/setting-components';
|
||||||
import { openSettingModalAtom } from '@affine/core/atoms';
|
import { openSettingModalAtom } from '@affine/core/atoms';
|
||||||
import { mixpanel } from '@affine/core/mixpanel';
|
import { track } from '@affine/core/mixpanel';
|
||||||
import {
|
import {
|
||||||
ServerConfigService,
|
ServerConfigService,
|
||||||
SubscriptionService,
|
SubscriptionService,
|
||||||
UserCopilotQuotaService,
|
UserCopilotQuotaService,
|
||||||
} from '@affine/core/modules/cloud';
|
} from '@affine/core/modules/cloud';
|
||||||
|
import { SubscriptionPlan } from '@affine/graphql';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import { useLiveData, useService } from '@toeverything/infra';
|
import { useLiveData, useService } from '@toeverything/infra';
|
||||||
import { cssVar } from '@toeverything/theme';
|
import { cssVar } from '@toeverything/theme';
|
||||||
@@ -47,12 +48,7 @@ export const AIUsagePanel = () => {
|
|||||||
open: true,
|
open: true,
|
||||||
activeTab: 'billing',
|
activeTab: 'billing',
|
||||||
});
|
});
|
||||||
mixpanel.track('BillingViewed', {
|
track.$.settingsPanel.accountUsage.viewPlans({ plan: SubscriptionPlan.AI });
|
||||||
segment: 'settings panel',
|
|
||||||
module: 'account usage list',
|
|
||||||
control: 'change plan button',
|
|
||||||
type: 'ai subscription',
|
|
||||||
});
|
|
||||||
}, [setOpenSettingModal]);
|
}, [setOpenSettingModal]);
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
@@ -104,7 +100,7 @@ export const AIUsagePanel = () => {
|
|||||||
>
|
>
|
||||||
{copilotActionLimit === 'unlimited' ? (
|
{copilotActionLimit === 'unlimited' ? (
|
||||||
hasPaymentFeature && aiSubscription?.canceledAt ? (
|
hasPaymentFeature && aiSubscription?.canceledAt ? (
|
||||||
<AIResume module="billing subscription list" />
|
<AIResume />
|
||||||
) : (
|
) : (
|
||||||
<Button onClick={openBilling}>
|
<Button onClick={openBilling}>
|
||||||
{t['com.affine.payment.ai.usage.change-button-label']()}
|
{t['com.affine.payment.ai.usage.change-button-label']()}
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ import {
|
|||||||
import { Avatar } from '@affine/component/ui/avatar';
|
import { Avatar } from '@affine/component/ui/avatar';
|
||||||
import { Button } from '@affine/component/ui/button';
|
import { Button } from '@affine/component/ui/button';
|
||||||
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
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 { useI18n } from '@affine/i18n';
|
||||||
import { ArrowRightSmallIcon, CameraIcon } from '@blocksuite/icons/rc';
|
import { ArrowRightSmallIcon, CameraIcon } from '@blocksuite/icons/rc';
|
||||||
import {
|
import {
|
||||||
@@ -38,9 +39,7 @@ export const UserAvatar = () => {
|
|||||||
const handleUpdateUserAvatar = useAsyncCallback(
|
const handleUpdateUserAvatar = useAsyncCallback(
|
||||||
async (file: File) => {
|
async (file: File) => {
|
||||||
try {
|
try {
|
||||||
mixpanel.track('UploadAvatar', {
|
track.$.settingsPanel.accountSettings.uploadAvatar();
|
||||||
userId: account.id,
|
|
||||||
});
|
|
||||||
await session.uploadAvatar(file);
|
await session.uploadAvatar(file);
|
||||||
notify.success({ title: 'Update user avatar success' });
|
notify.success({ title: 'Update user avatar success' });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -51,18 +50,16 @@ export const UserAvatar = () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[account, session]
|
[session]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleRemoveUserAvatar = useAsyncCallback(
|
const handleRemoveUserAvatar = useAsyncCallback(
|
||||||
async (e: MouseEvent<HTMLButtonElement>) => {
|
async (e: MouseEvent<HTMLButtonElement>) => {
|
||||||
mixpanel.track('RemoveAvatar', {
|
track.$.settingsPanel.accountSettings.removeAvatar();
|
||||||
userId: account.id,
|
|
||||||
});
|
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
await session.removeAvatar();
|
await session.removeAvatar();
|
||||||
},
|
},
|
||||||
[account, session]
|
[session]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -104,9 +101,7 @@ export const AvatarAndName = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
mixpanel.track('UpdateUsername', {
|
track.$.settingsPanel.accountSettings.updateUserName();
|
||||||
userId: account.id,
|
|
||||||
});
|
|
||||||
await session.updateLabel(input);
|
await session.updateLabel(input);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
notify.error({
|
notify.error({
|
||||||
@@ -161,11 +156,8 @@ const StoragePanel = () => {
|
|||||||
|
|
||||||
const setSettingModalAtom = useSetAtom(openSettingModalAtom);
|
const setSettingModalAtom = useSetAtom(openSettingModalAtom);
|
||||||
const onUpgrade = useCallback(() => {
|
const onUpgrade = useCallback(() => {
|
||||||
mixpanel.track('PlansViewed', {
|
track.$.settingsPanel.accountUsage.viewPlans({
|
||||||
segment: 'settings panel',
|
plan: SubscriptionPlan.Pro,
|
||||||
module: 'account usage list',
|
|
||||||
control: 'cloud storage upgrade button',
|
|
||||||
type: 'cloud subscription',
|
|
||||||
});
|
});
|
||||||
setSettingModalAtom({
|
setSettingModalAtom({
|
||||||
open: true,
|
open: true,
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { Button, IconButton } from '@affine/component/ui/button';
|
|||||||
import { Loading } from '@affine/component/ui/loading';
|
import { Loading } from '@affine/component/ui/loading';
|
||||||
import { getUpgradeQuestionnaireLink } from '@affine/core/hooks/affine/use-subscription-notify';
|
import { getUpgradeQuestionnaireLink } from '@affine/core/hooks/affine/use-subscription-notify';
|
||||||
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
||||||
import { mixpanel } from '@affine/core/mixpanel';
|
import { track } from '@affine/core/mixpanel';
|
||||||
import type { InvoicesQuery } from '@affine/graphql';
|
import type { InvoicesQuery } from '@affine/graphql';
|
||||||
import {
|
import {
|
||||||
createCustomerPortalMutation,
|
createCustomerPortalMutation,
|
||||||
@@ -112,22 +112,14 @@ const SubscriptionSettings = () => {
|
|||||||
|
|
||||||
const openPlans = useCallback(
|
const openPlans = useCallback(
|
||||||
(scrollAnchor?: PlansScrollAnchor) => {
|
(scrollAnchor?: PlansScrollAnchor) => {
|
||||||
mixpanel.track('PlansViewed', {
|
track.$.settingsPanel.billing.viewPlans();
|
||||||
type: proSubscription?.plan,
|
|
||||||
category: proSubscription?.recurring,
|
|
||||||
// page:
|
|
||||||
segment: 'settings panel',
|
|
||||||
module: 'billing subscription list',
|
|
||||||
control: 'change plan button',
|
|
||||||
});
|
|
||||||
|
|
||||||
setOpenSettingModalAtom({
|
setOpenSettingModalAtom({
|
||||||
open: true,
|
open: true,
|
||||||
activeTab: 'plans',
|
activeTab: 'plans',
|
||||||
scrollAnchor: scrollAnchor,
|
scrollAnchor: scrollAnchor,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[proSubscription?.plan, proSubscription?.recurring, setOpenSettingModalAtom]
|
[setOpenSettingModalAtom]
|
||||||
);
|
);
|
||||||
const gotoCloudPlansSetting = useCallback(
|
const gotoCloudPlansSetting = useCallback(
|
||||||
() => openPlans('cloudPricingPlan'),
|
() => openPlans('cloudPricingPlan'),
|
||||||
@@ -238,7 +230,6 @@ const SubscriptionSettings = () => {
|
|||||||
</SettingRow>
|
</SettingRow>
|
||||||
) : (
|
) : (
|
||||||
<CancelAction
|
<CancelAction
|
||||||
module="billing subscription list"
|
|
||||||
open={openCancelModal}
|
open={openCancelModal}
|
||||||
onOpenChange={setOpenCancelModal}
|
onOpenChange={setOpenCancelModal}
|
||||||
>
|
>
|
||||||
@@ -396,15 +387,9 @@ const AIPlanCard = ({ onClick }: { onClick: () => void }) => {
|
|||||||
{price?.yearlyAmount ? (
|
{price?.yearlyAmount ? (
|
||||||
subscription ? (
|
subscription ? (
|
||||||
subscription.canceledAt ? (
|
subscription.canceledAt ? (
|
||||||
<AIResume
|
<AIResume className={styles.planAction} />
|
||||||
module="billing subscription list"
|
|
||||||
className={styles.planAction}
|
|
||||||
/>
|
|
||||||
) : (
|
) : (
|
||||||
<AICancel
|
<AICancel className={styles.planAction} />
|
||||||
module="billing subscription list"
|
|
||||||
className={styles.planAction}
|
|
||||||
/>
|
|
||||||
)
|
)
|
||||||
) : (
|
) : (
|
||||||
<AISubscribe className={styles.planAction}>
|
<AISubscribe className={styles.planAction}>
|
||||||
@@ -470,22 +455,16 @@ const ResumeSubscription = () => {
|
|||||||
const subscription = useService(SubscriptionService).subscription;
|
const subscription = useService(SubscriptionService).subscription;
|
||||||
const handleClick = useCallback(() => {
|
const handleClick = useCallback(() => {
|
||||||
setOpen(true);
|
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 (
|
return (
|
||||||
<ResumeAction open={open} onOpenChange={setOpen}>
|
<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']()}
|
{t['com.affine.payment.billing-setting.resume-subscription']()}
|
||||||
</Button>
|
</Button>
|
||||||
</ResumeAction>
|
</ResumeAction>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { getDowngradeQuestionnaireLink } from '@affine/core/hooks/affine/use-subscription-notify';
|
import { getDowngradeQuestionnaireLink } from '@affine/core/hooks/affine/use-subscription-notify';
|
||||||
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
||||||
import { mixpanel } from '@affine/core/mixpanel';
|
import { track } from '@affine/core/mixpanel';
|
||||||
import { SubscriptionPlan } from '@affine/graphql';
|
import { SubscriptionPlan } from '@affine/graphql';
|
||||||
import { useLiveData, useService } from '@toeverything/infra';
|
import { useLiveData, useService } from '@toeverything/infra';
|
||||||
import { nanoid } from 'nanoid';
|
import { nanoid } from 'nanoid';
|
||||||
@@ -20,11 +20,9 @@ export const CancelAction = ({
|
|||||||
children,
|
children,
|
||||||
open,
|
open,
|
||||||
onOpenChange,
|
onOpenChange,
|
||||||
module,
|
|
||||||
}: {
|
}: {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
onOpenChange: (open: boolean) => void;
|
onOpenChange: (open: boolean) => void;
|
||||||
module: string;
|
|
||||||
} & PropsWithChildren) => {
|
} & PropsWithChildren) => {
|
||||||
const [idempotencyKey, setIdempotencyKey] = useState(nanoid());
|
const [idempotencyKey, setIdempotencyKey] = useState(nanoid());
|
||||||
const [isMutating, setIsMutating] = useState(false);
|
const [isMutating, setIsMutating] = useState(false);
|
||||||
@@ -35,14 +33,11 @@ export const CancelAction = ({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!open || !proSubscription) return;
|
if (!open || !proSubscription) return;
|
||||||
mixpanel.track('PlanChangeStarted', {
|
track.$.settingsPanel.plans.cancelSubscription({
|
||||||
segment: 'settings panel',
|
plan: proSubscription.plan,
|
||||||
module,
|
recurring: proSubscription.recurring,
|
||||||
control: 'cancel',
|
|
||||||
type: proSubscription.plan,
|
|
||||||
category: proSubscription.recurring,
|
|
||||||
});
|
});
|
||||||
}, [module, open, proSubscription]);
|
}, [open, proSubscription]);
|
||||||
|
|
||||||
const downgrade = useAsyncCallback(async () => {
|
const downgrade = useAsyncCallback(async () => {
|
||||||
try {
|
try {
|
||||||
@@ -57,10 +52,9 @@ export const CancelAction = ({
|
|||||||
onOpenChange(false);
|
onOpenChange(false);
|
||||||
const proSubscription = subscription.pro$.value;
|
const proSubscription = subscription.pro$.value;
|
||||||
if (proSubscription) {
|
if (proSubscription) {
|
||||||
mixpanel.track('PlanChangeSucceeded', {
|
track.$.settingsPanel.plans.confirmCancelingSubscription({
|
||||||
control: 'cancel',
|
plan: proSubscription.plan,
|
||||||
type: proSubscription.plan,
|
recurring: proSubscription.recurring,
|
||||||
category: proSubscription.recurring,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (account && prevRecurring) {
|
if (account && prevRecurring) {
|
||||||
@@ -127,10 +121,9 @@ export const ResumeAction = ({
|
|||||||
onOpenChange(false);
|
onOpenChange(false);
|
||||||
const proSubscription = subscription.pro$.value;
|
const proSubscription = subscription.pro$.value;
|
||||||
if (proSubscription) {
|
if (proSubscription) {
|
||||||
mixpanel.track('PlanChangeSucceeded', {
|
track.$.settingsPanel.plans.confirmResumingSubscription({
|
||||||
control: 'paying',
|
plan: proSubscription.plan,
|
||||||
type: proSubscription.plan,
|
recurring: proSubscription.recurring,
|
||||||
category: proSubscription.recurring,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { Button, type ButtonProps, useConfirmModal } from '@affine/component';
|
|||||||
import { useDowngradeNotify } from '@affine/core/components/affine/subscription-landing/notify';
|
import { useDowngradeNotify } from '@affine/core/components/affine/subscription-landing/notify';
|
||||||
import { getDowngradeQuestionnaireLink } from '@affine/core/hooks/affine/use-subscription-notify';
|
import { getDowngradeQuestionnaireLink } from '@affine/core/hooks/affine/use-subscription-notify';
|
||||||
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
||||||
import { mixpanel } from '@affine/core/mixpanel';
|
import { track } from '@affine/core/mixpanel';
|
||||||
import { AuthService, SubscriptionService } from '@affine/core/modules/cloud';
|
import { AuthService, SubscriptionService } from '@affine/core/modules/cloud';
|
||||||
import { SubscriptionPlan } from '@affine/graphql';
|
import { SubscriptionPlan } from '@affine/graphql';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
@@ -10,10 +10,7 @@ import { useService } from '@toeverything/infra';
|
|||||||
import { nanoid } from 'nanoid';
|
import { nanoid } from 'nanoid';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
export interface AICancelProps extends ButtonProps {
|
export const AICancel = (btnProps: ButtonProps) => {
|
||||||
module: string;
|
|
||||||
}
|
|
||||||
export const AICancel = ({ module, ...btnProps }: AICancelProps) => {
|
|
||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
const [isMutating, setMutating] = useState(false);
|
const [isMutating, setMutating] = useState(false);
|
||||||
const [idempotencyKey, setIdempotencyKey] = useState(nanoid());
|
const [idempotencyKey, setIdempotencyKey] = useState(nanoid());
|
||||||
@@ -26,12 +23,9 @@ export const AICancel = ({ module, ...btnProps }: AICancelProps) => {
|
|||||||
const cancel = useAsyncCallback(async () => {
|
const cancel = useAsyncCallback(async () => {
|
||||||
const aiSubscription = subscription.ai$.value;
|
const aiSubscription = subscription.ai$.value;
|
||||||
if (aiSubscription) {
|
if (aiSubscription) {
|
||||||
mixpanel.track('PlanChangeStarted', {
|
track.$.settingsPanel.plans.cancelSubscription({
|
||||||
module,
|
plan: SubscriptionPlan.AI,
|
||||||
segment: 'settings panel',
|
recurring: aiSubscription.recurring,
|
||||||
control: 'cancel',
|
|
||||||
type: SubscriptionPlan.AI,
|
|
||||||
category: aiSubscription.recurring,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
openConfirmModal({
|
openConfirmModal({
|
||||||
@@ -57,10 +51,9 @@ export const AICancel = ({ module, ...btnProps }: AICancelProps) => {
|
|||||||
SubscriptionPlan.AI
|
SubscriptionPlan.AI
|
||||||
);
|
);
|
||||||
setIdempotencyKey(nanoid());
|
setIdempotencyKey(nanoid());
|
||||||
mixpanel.track('PlanChangeSucceeded', {
|
track.$.settingsPanel.plans.confirmCancelingSubscription({
|
||||||
segment: 'settings panel',
|
plan: SubscriptionPlan.AI,
|
||||||
control: 'plan cancel action',
|
recurring: aiSubscription?.recurring,
|
||||||
type: subscription.ai$.value?.plan,
|
|
||||||
});
|
});
|
||||||
const account = authService.session.account$.value;
|
const account = authService.session.account$.value;
|
||||||
const prevRecurring = subscription.ai$.value?.recurring;
|
const prevRecurring = subscription.ai$.value?.recurring;
|
||||||
@@ -81,7 +74,6 @@ export const AICancel = ({ module, ...btnProps }: AICancelProps) => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}, [
|
}, [
|
||||||
module,
|
|
||||||
subscription,
|
subscription,
|
||||||
openConfirmModal,
|
openConfirmModal,
|
||||||
t,
|
t,
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import {
|
|||||||
useConfirmModal,
|
useConfirmModal,
|
||||||
} from '@affine/component';
|
} from '@affine/component';
|
||||||
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
||||||
import { mixpanel } from '@affine/core/mixpanel';
|
import { track } from '@affine/core/mixpanel';
|
||||||
import { SubscriptionService } from '@affine/core/modules/cloud';
|
import { SubscriptionService } from '@affine/core/modules/cloud';
|
||||||
import { SubscriptionPlan } from '@affine/graphql';
|
import { SubscriptionPlan } from '@affine/graphql';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
@@ -15,11 +15,7 @@ import { cssVar } from '@toeverything/theme';
|
|||||||
import { nanoid } from 'nanoid';
|
import { nanoid } from 'nanoid';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
export interface AIResumeProps extends ButtonProps {
|
export const AIResume = (btnProps: ButtonProps) => {
|
||||||
module: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const AIResume = ({ module, ...btnProps }: AIResumeProps) => {
|
|
||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
const [idempotencyKey, setIdempotencyKey] = useState(nanoid());
|
const [idempotencyKey, setIdempotencyKey] = useState(nanoid());
|
||||||
const subscription = useService(SubscriptionService).subscription;
|
const subscription = useService(SubscriptionService).subscription;
|
||||||
@@ -31,12 +27,9 @@ export const AIResume = ({ module, ...btnProps }: AIResumeProps) => {
|
|||||||
const resume = useAsyncCallback(async () => {
|
const resume = useAsyncCallback(async () => {
|
||||||
const aiSubscription = subscription.ai$.value;
|
const aiSubscription = subscription.ai$.value;
|
||||||
if (aiSubscription) {
|
if (aiSubscription) {
|
||||||
mixpanel.track('PlanChangeStarted', {
|
track.$.settingsPanel.plans.resumeSubscription({
|
||||||
module,
|
plan: SubscriptionPlan.AI,
|
||||||
segment: 'settings panel',
|
recurring: aiSubscription.recurring,
|
||||||
control: 'paying',
|
|
||||||
type: aiSubscription.plan,
|
|
||||||
category: aiSubscription.recurring,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,10 +51,9 @@ export const AIResume = ({ module, ...btnProps }: AIResumeProps) => {
|
|||||||
SubscriptionPlan.AI
|
SubscriptionPlan.AI
|
||||||
);
|
);
|
||||||
if (aiSubscription) {
|
if (aiSubscription) {
|
||||||
mixpanel.track('PlanChangeSucceeded', {
|
track.$.settingsPanel.plans.confirmResumingSubscription({
|
||||||
category: aiSubscription.recurring,
|
plan: aiSubscription.plan,
|
||||||
control: 'paying',
|
recurring: aiSubscription.recurring,
|
||||||
type: aiSubscription.plan,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
notify({
|
notify({
|
||||||
@@ -75,7 +67,7 @@ export const AIResume = ({ module, ...btnProps }: AIResumeProps) => {
|
|||||||
setIdempotencyKey(nanoid());
|
setIdempotencyKey(nanoid());
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}, [subscription, openConfirmModal, t, module, idempotencyKey]);
|
}, [subscription, openConfirmModal, t, idempotencyKey]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Button, type ButtonProps, Skeleton } from '@affine/component';
|
import { Button, type ButtonProps, Skeleton } from '@affine/component';
|
||||||
import { generateSubscriptionCallbackLink } from '@affine/core/hooks/affine/use-subscription-notify';
|
import { generateSubscriptionCallbackLink } from '@affine/core/hooks/affine/use-subscription-notify';
|
||||||
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
||||||
import { mixpanel } from '@affine/core/mixpanel';
|
import { track } from '@affine/core/mixpanel';
|
||||||
import { AuthService, SubscriptionService } from '@affine/core/modules/cloud';
|
import { AuthService, SubscriptionService } from '@affine/core/modules/cloud';
|
||||||
import { popupWindow } from '@affine/core/utils';
|
import { popupWindow } from '@affine/core/utils';
|
||||||
import { SubscriptionPlan, SubscriptionRecurring } from '@affine/graphql';
|
import { SubscriptionPlan, SubscriptionRecurring } from '@affine/graphql';
|
||||||
@@ -50,9 +50,9 @@ export const AISubscribe = ({
|
|||||||
|
|
||||||
const subscribe = useAsyncCallback(async () => {
|
const subscribe = useAsyncCallback(async () => {
|
||||||
setMutating(true);
|
setMutating(true);
|
||||||
mixpanel.track('PlanUpgradeStarted', {
|
track.$.settingsPanel.plans.checkout({
|
||||||
category: SubscriptionRecurring.Yearly,
|
plan: SubscriptionPlan.AI,
|
||||||
type: SubscriptionPlan.AI,
|
recurring: SubscriptionRecurring.Yearly,
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
const session = await subscriptionService.createCheckoutSession({
|
const session = await subscriptionService.createCheckoutSession({
|
||||||
|
|||||||
@@ -53,15 +53,9 @@ export const AIPlan = () => {
|
|||||||
isLoggedIn ? (
|
isLoggedIn ? (
|
||||||
subscription ? (
|
subscription ? (
|
||||||
subscription.canceledAt ? (
|
subscription.canceledAt ? (
|
||||||
<AIResume
|
<AIResume className={styles.purchaseButton} />
|
||||||
module="pricing plan list"
|
|
||||||
className={styles.purchaseButton}
|
|
||||||
/>
|
|
||||||
) : (
|
) : (
|
||||||
<AICancel
|
<AICancel className={styles.purchaseButton} />
|
||||||
module="pricing plan list"
|
|
||||||
className={styles.purchaseButton}
|
|
||||||
/>
|
|
||||||
)
|
)
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { Button, type ButtonProps } from '@affine/component/ui/button';
|
|||||||
import { Tooltip } from '@affine/component/ui/tooltip';
|
import { Tooltip } from '@affine/component/ui/tooltip';
|
||||||
import { generateSubscriptionCallbackLink } from '@affine/core/hooks/affine/use-subscription-notify';
|
import { generateSubscriptionCallbackLink } from '@affine/core/hooks/affine/use-subscription-notify';
|
||||||
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
||||||
import { mixpanel } from '@affine/core/mixpanel';
|
import { track } from '@affine/core/mixpanel';
|
||||||
import { AuthService, SubscriptionService } from '@affine/core/modules/cloud';
|
import { AuthService, SubscriptionService } from '@affine/core/modules/cloud';
|
||||||
import { popupWindow } from '@affine/core/utils';
|
import { popupWindow } from '@affine/core/utils';
|
||||||
import type { SubscriptionRecurring } from '@affine/graphql';
|
import type { SubscriptionRecurring } from '@affine/graphql';
|
||||||
@@ -190,7 +190,7 @@ const Downgrade = ({ disabled }: { disabled?: boolean }) => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CancelAction module="pricing plan list" open={open} onOpenChange={setOpen}>
|
<CancelAction open={open} onOpenChange={setOpen}>
|
||||||
<Tooltip content={tooltipContent} rootOptions={{ delayDuration: 0 }}>
|
<Tooltip content={tooltipContent} rootOptions={{ delayDuration: 0 }}>
|
||||||
<div className={styles.planAction}>
|
<div className={styles.planAction}>
|
||||||
<Button
|
<Button
|
||||||
@@ -226,14 +226,13 @@ const BookDemo = ({ plan }: { plan: SubscriptionPlan }) => {
|
|||||||
href={url}
|
href={url}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
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']()}
|
{t['com.affine.payment.tell-us-use-case']()}
|
||||||
</Button>
|
</Button>
|
||||||
</a>
|
</a>
|
||||||
@@ -276,12 +275,9 @@ export const Upgrade = ({
|
|||||||
|
|
||||||
const upgrade = useAsyncCallback(async () => {
|
const upgrade = useAsyncCallback(async () => {
|
||||||
setMutating(true);
|
setMutating(true);
|
||||||
mixpanel.track('PlanUpgradeStarted', {
|
track.$.settingsPanel.plans.checkout({
|
||||||
segment: 'settings panel',
|
plan: SubscriptionPlan.Pro,
|
||||||
module: 'pricing plan list',
|
recurring: recurring,
|
||||||
control: 'pricing plan action',
|
|
||||||
type: 'cloud pro subscription',
|
|
||||||
category: recurring,
|
|
||||||
});
|
});
|
||||||
const link = await subscriptionService.createCheckoutSession({
|
const link = await subscriptionService.createCheckoutSession({
|
||||||
recurring,
|
recurring,
|
||||||
@@ -338,12 +334,9 @@ const ChangeRecurring = ({
|
|||||||
const subscription = useService(SubscriptionService).subscription;
|
const subscription = useService(SubscriptionService).subscription;
|
||||||
|
|
||||||
const onStartChange = useCallback(() => {
|
const onStartChange = useCallback(() => {
|
||||||
mixpanel.track('PlanChangeStarted', {
|
track.$.settingsPanel.plans.changeSubscriptionRecurring({
|
||||||
segment: 'settings panel',
|
plan: SubscriptionPlan.Pro,
|
||||||
module: 'pricing plan list',
|
recurring: to,
|
||||||
control: 'paying',
|
|
||||||
type: SubscriptionPlan.Pro,
|
|
||||||
category: to,
|
|
||||||
});
|
});
|
||||||
setOpen(true);
|
setOpen(true);
|
||||||
}, [to]);
|
}, [to]);
|
||||||
@@ -422,12 +415,9 @@ const ResumeButton = () => {
|
|||||||
setOpen(true);
|
setOpen(true);
|
||||||
const pro = subscription.pro$.value;
|
const pro = subscription.pro$.value;
|
||||||
if (pro) {
|
if (pro) {
|
||||||
mixpanel.track('PlanChangeStarted', {
|
track.$.settingsPanel.plans.resumeSubscription({
|
||||||
segment: 'settings panel',
|
plan: SubscriptionPlan.Pro,
|
||||||
module: 'pricing plan list',
|
recurring: pro.recurring,
|
||||||
control: 'paying',
|
|
||||||
type: SubscriptionPlan.Pro,
|
|
||||||
category: pro.recurring,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [subscription.pro$.value]);
|
}, [subscription.pro$.value]);
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { Avatar } from '@affine/component/ui/avatar';
|
|||||||
import { Tooltip } from '@affine/component/ui/tooltip';
|
import { Tooltip } from '@affine/component/ui/tooltip';
|
||||||
import { WorkspaceAvatar } from '@affine/component/workspace-avatar';
|
import { WorkspaceAvatar } from '@affine/component/workspace-avatar';
|
||||||
import { useWorkspaceInfo } from '@affine/core/hooks/use-workspace-info';
|
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 { AuthService } from '@affine/core/modules/cloud';
|
||||||
import { UserFeatureService } from '@affine/core/modules/cloud/services/user-feature';
|
import { UserFeatureService } from '@affine/core/modules/cloud/services/user-feature';
|
||||||
import { UNTITLED_WORKSPACE_NAME } from '@affine/env/constant';
|
import { UNTITLED_WORKSPACE_NAME } from '@affine/env/constant';
|
||||||
@@ -22,7 +22,13 @@ import {
|
|||||||
} from '@toeverything/infra';
|
} from '@toeverything/infra';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { useAtom } from 'jotai/react';
|
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 { authAtom } from '../../../../atoms';
|
||||||
import { UserPlanButton } from '../../auth/user-plan-button';
|
import { UserPlanButton } from '../../auth/user-plan-button';
|
||||||
@@ -115,29 +121,30 @@ export const SettingSidebar = ({
|
|||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
const loginStatus = useLiveData(useService(AuthService).session.status$);
|
const loginStatus = useLiveData(useService(AuthService).session.status$);
|
||||||
const generalList = useGeneralSettingList();
|
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(() => {
|
const onAccountSettingClick = useCallback(() => {
|
||||||
mixpanel.track('AccountSettingsViewed', {
|
track.$.settingsPanel.menu.openSettings({ to: 'account' });
|
||||||
// page:
|
|
||||||
segment: 'settings panel',
|
|
||||||
module: 'settings menu',
|
|
||||||
control: 'menu item',
|
|
||||||
});
|
|
||||||
onTabChange('account', null);
|
onTabChange('account', null);
|
||||||
}, [onTabChange]);
|
}, [onTabChange]);
|
||||||
const onWorkspaceSettingClick = useCallback(
|
const onWorkspaceSettingClick = useCallback(
|
||||||
(subTab: WorkspaceSubTab, workspaceMetadata: WorkspaceMetadata) => {
|
(subTab: WorkspaceSubTab, workspaceMetadata: WorkspaceMetadata) => {
|
||||||
mixpanel.track(`view workspace setting`, {
|
track.$.settingsPanel.menu.openSettings({
|
||||||
// page:
|
to: 'workspace',
|
||||||
segment: 'settings panel',
|
control: subTab,
|
||||||
module: 'settings menu',
|
|
||||||
control: 'menu item',
|
|
||||||
type: subTab,
|
|
||||||
workspaceId: workspaceMetadata.id,
|
|
||||||
});
|
});
|
||||||
onTabChange(`workspace:${subTab}`, workspaceMetadata);
|
onTabChange(`workspace:${subTab}`, workspaceMetadata);
|
||||||
},
|
},
|
||||||
[onTabChange]
|
[onTabChange]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={style.settingSlideBar} data-testid="settings-sidebar">
|
<div className={style.settingSlideBar} data-testid="settings-sidebar">
|
||||||
<div className={style.sidebarTitle}>
|
<div className={style.sidebarTitle}>
|
||||||
@@ -155,24 +162,8 @@ export const SettingSidebar = ({
|
|||||||
})}
|
})}
|
||||||
key={key}
|
key={key}
|
||||||
title={title}
|
title={title}
|
||||||
onClick={() => {
|
data-event-arg={key}
|
||||||
if (key === 'billing') {
|
onClick={gotoTab}
|
||||||
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-testid={testId}
|
data-testid={testId}
|
||||||
>
|
>
|
||||||
{icon({ className: 'icon' })}
|
{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 type { Member } from '@affine/core/hooks/affine/use-members';
|
||||||
import { useMembers } 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 { 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 { WorkspacePermissionService } from '@affine/core/modules/permissions';
|
||||||
import { WorkspaceQuotaService } from '@affine/core/modules/quota';
|
import { WorkspaceQuotaService } from '@affine/core/modules/quota';
|
||||||
import { WorkspaceFlavour } from '@affine/env/workspace';
|
import { WorkspaceFlavour } from '@affine/env/workspace';
|
||||||
@@ -146,11 +146,8 @@ export const CloudWorkspaceMembersPanel = () => {
|
|||||||
activeTab: 'plans',
|
activeTab: 'plans',
|
||||||
scrollAnchor: 'cloudPricingPlan',
|
scrollAnchor: 'cloudPricingPlan',
|
||||||
});
|
});
|
||||||
mixpanel.track('PlansViewed', {
|
track.$.settingsPanel.workspace.viewPlans({
|
||||||
// page:
|
control: 'inviteMember',
|
||||||
segment: 'settings panel',
|
|
||||||
module: 'workspace setting',
|
|
||||||
control: 'invite member',
|
|
||||||
});
|
});
|
||||||
}, [setSettingModalAtom]);
|
}, [setSettingModalAtom]);
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { Button } from '@affine/component/ui/button';
|
|||||||
import { Menu, MenuItem, MenuTrigger } from '@affine/component/ui/menu';
|
import { Menu, MenuItem, MenuTrigger } from '@affine/component/ui/menu';
|
||||||
import { useSharingUrl } from '@affine/core/hooks/affine/use-share-url';
|
import { useSharingUrl } from '@affine/core/hooks/affine/use-share-url';
|
||||||
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
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 { ServerConfigService } from '@affine/core/modules/cloud';
|
||||||
import { ShareService } from '@affine/core/modules/share-doc';
|
import { ShareService } from '@affine/core/modules/share-doc';
|
||||||
import { WorkspaceFlavour } from '@affine/env/workspace';
|
import { WorkspaceFlavour } from '@affine/env/workspace';
|
||||||
@@ -106,11 +106,8 @@ export const AffineSharePage = (props: ShareMenuProps) => {
|
|||||||
await shareService.share.enableShare(
|
await shareService.share.enableShare(
|
||||||
mode === 'edgeless' ? PublicPageMode.Edgeless : PublicPageMode.Page
|
mode === 'edgeless' ? PublicPageMode.Edgeless : PublicPageMode.Page
|
||||||
);
|
);
|
||||||
mixpanel.track('ShareCreated', {
|
track.$.header.share.createShareLink({
|
||||||
segment: 'sharing panel',
|
mode,
|
||||||
module: 'public share',
|
|
||||||
control: 'share panel',
|
|
||||||
type: mode,
|
|
||||||
});
|
});
|
||||||
notify.success({
|
notify.success({
|
||||||
title:
|
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 { CloseIcon, DownloadIcon } from '@blocksuite/icons/rc';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { useCallback, useState } from 'react';
|
import { useCallback, useState } from 'react';
|
||||||
@@ -21,9 +21,7 @@ export function AppDownloadButton({
|
|||||||
|
|
||||||
// TODO(@JimmFly): unify this type of literal value.
|
// TODO(@JimmFly): unify this type of literal value.
|
||||||
const handleClick = useCallback(() => {
|
const handleClick = useCallback(() => {
|
||||||
mixpanel.track('Button', {
|
track.$.navigationPanel.bottomButtons.downloadApp();
|
||||||
resolve: 'GoToDownloadAppPage',
|
|
||||||
});
|
|
||||||
const url = `https://affine.pro/download?channel=stable`;
|
const url = `https://affine.pro/download?channel=stable`;
|
||||||
open(url, '_blank');
|
open(url, '_blank');
|
||||||
}, []);
|
}, []);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { notify } from '@affine/component';
|
|||||||
import { authAtom, openSettingModalAtom } from '@affine/core/atoms';
|
import { authAtom, openSettingModalAtom } from '@affine/core/atoms';
|
||||||
import { AIProvider } from '@affine/core/blocksuite/presets/ai';
|
import { AIProvider } from '@affine/core/blocksuite/presets/ai';
|
||||||
import { toggleGeneralAIOnboarding } from '@affine/core/components/affine/ai-onboarding/apis';
|
import { toggleGeneralAIOnboarding } from '@affine/core/components/affine/ai-onboarding/apis';
|
||||||
import { mixpanel } from '@affine/core/mixpanel';
|
import { track } from '@affine/core/mixpanel';
|
||||||
import {
|
import {
|
||||||
getBaseUrl,
|
getBaseUrl,
|
||||||
type getCopilotHistoriesQuery,
|
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',
|
activeTab: 'billing',
|
||||||
open: true,
|
open: true,
|
||||||
});
|
});
|
||||||
mixpanel.track('PlansViewed', {
|
track.$.paywall.aiAction.viewPlans();
|
||||||
segment: 'payment wall',
|
|
||||||
category: 'payment wall ai action count',
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
AIProvider.slots.requestLogin.on(() => {
|
AIProvider.slots.requestLogin.on(() => {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { AIProvider } from '@affine/core/blocksuite/presets/ai';
|
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 { EditorHost } from '@blocksuite/block-std';
|
||||||
import type { BlockModel } from '@blocksuite/store';
|
import type { BlockModel } from '@blocksuite/store';
|
||||||
import { lowerCase, omit } from 'lodash-es';
|
import { lowerCase, omit } from 'lodash-es';
|
||||||
@@ -13,7 +13,7 @@ type AIActionEventName =
|
|||||||
| 'AI result accepted';
|
| 'AI result accepted';
|
||||||
|
|
||||||
type AIActionEventProperties = {
|
type AIActionEventProperties = {
|
||||||
page: 'doc-editor' | 'whiteboard-editor';
|
page: 'doc' | 'edgeless';
|
||||||
segment:
|
segment:
|
||||||
| 'AI action panel'
|
| 'AI action panel'
|
||||||
| 'right side bar'
|
| 'right side bar'
|
||||||
@@ -73,9 +73,7 @@ const trackAction = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const inferPageMode = (host: EditorHost) => {
|
const inferPageMode = (host: EditorHost) => {
|
||||||
return host.querySelector('affine-page-root')
|
return host.querySelector('affine-page-root') ? 'doc' : 'edgeless';
|
||||||
? 'doc-editor'
|
|
||||||
: 'whiteboard-editor';
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const defaultActionOptions = [
|
const defaultActionOptions = [
|
||||||
@@ -253,14 +251,12 @@ const toTrackedOptions = (
|
|||||||
|
|
||||||
export function setupTracker() {
|
export function setupTracker() {
|
||||||
AIProvider.slots.requestUpgradePlan.on(() => {
|
AIProvider.slots.requestUpgradePlan.on(() => {
|
||||||
mixpanel.track('AI', {
|
track.$.paywall.aiAction.viewPlans();
|
||||||
action: 'requestUpgradePlan',
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
AIProvider.slots.requestLogin.on(() => {
|
AIProvider.slots.requestLogin.on(() => {
|
||||||
mixpanel.track('AI', {
|
track.$.$.auth.signIn({
|
||||||
action: 'requestLogin',
|
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 { WorkspacePropertiesAdapter } from '@affine/core/modules/properties';
|
||||||
import { I18n, i18nTime } from '@affine/i18n';
|
import { I18n, i18nTime } from '@affine/i18n';
|
||||||
import type { EditorHost } from '@blocksuite/block-std';
|
import type { EditorHost } from '@blocksuite/block-std';
|
||||||
@@ -75,12 +75,7 @@ export function createLinkedWidgetConfig(framework: FrameworkProvider) {
|
|||||||
inlineEditor,
|
inlineEditor,
|
||||||
docId: doc.id,
|
docId: doc.id,
|
||||||
});
|
});
|
||||||
mixpanel.track('LinkedDocCreated', {
|
track.doc.editor.atMenu.linkDoc();
|
||||||
control: 'linked doc',
|
|
||||||
module: 'inline @',
|
|
||||||
type: 'doc',
|
|
||||||
other: 'existing doc',
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
})),
|
})),
|
||||||
maxDisplay: MAX_DOCS,
|
maxDisplay: MAX_DOCS,
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
toReactNode,
|
toReactNode,
|
||||||
type useConfirmModal,
|
type useConfirmModal,
|
||||||
} from '@affine/component';
|
} from '@affine/component';
|
||||||
import { mixpanel } from '@affine/core/mixpanel';
|
import { track } from '@affine/core/mixpanel';
|
||||||
import { DocsSearchService } from '@affine/core/modules/docs-search';
|
import { DocsSearchService } from '@affine/core/modules/docs-search';
|
||||||
import { resolveLinkToDoc } from '@affine/core/modules/navigation';
|
import { resolveLinkToDoc } from '@affine/core/modules/navigation';
|
||||||
import type { PeekViewService } from '@affine/core/modules/peek-view';
|
import type { PeekViewService } from '@affine/core/modules/peek-view';
|
||||||
@@ -465,30 +465,11 @@ export function patchQuickSearchService(
|
|||||||
pageId: linkedDoc.id,
|
pageId: linkedDoc.id,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
const isEdgeless =
|
|
||||||
rootComponent instanceof EdgelessRootBlockComponent;
|
|
||||||
if (result.isNewDoc) {
|
if (result.isNewDoc) {
|
||||||
mixpanel.track('DocCreated', {
|
track.doc.editor.slashMenu.createDoc({ control: 'linkDoc' });
|
||||||
control: 'linked doc',
|
track.doc.editor.slashMenu.linkDoc({ control: 'createDoc' });
|
||||||
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.linkDoc({ control: 'linkDoc' });
|
||||||
} else if ('userInput' in result) {
|
} else if ('userInput' in result) {
|
||||||
const embedOptions = service.getEmbedBlockOptions(
|
const embedOptions = service.getEmbedBlockOptions(
|
||||||
result.userInput
|
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 { useExportPage } from '@affine/core/hooks/affine/use-export-page';
|
||||||
import { useTrashModalHelper } from '@affine/core/hooks/affine/use-trash-modal-helper';
|
import { useTrashModalHelper } from '@affine/core/hooks/affine/use-trash-modal-helper';
|
||||||
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
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 { WorkbenchService } from '@affine/core/modules/workbench';
|
||||||
import { ViewService } from '@affine/core/modules/workbench/services/view';
|
import { ViewService } from '@affine/core/modules/workbench/services/view';
|
||||||
import { useDetailPageHeaderResponsive } from '@affine/core/pages/workspace/detail-page/use-header-responsive';
|
import { useDetailPageHeaderResponsive } from '@affine/core/pages/workspace/detail-page/use-header-responsive';
|
||||||
@@ -157,36 +157,23 @@ export const PageHeaderMenuButton = ({
|
|||||||
|
|
||||||
const handleDuplicate = useCallback(() => {
|
const handleDuplicate = useCallback(() => {
|
||||||
duplicate(pageId);
|
duplicate(pageId);
|
||||||
mixpanel.track('DocCreated', {
|
track.$.header.actions.createDoc({
|
||||||
segment: 'editor header',
|
control: 'duplicate',
|
||||||
page: doc.mode$.value === 'page' ? 'doc editor' : 'edgeless editor',
|
|
||||||
module: 'header menu',
|
|
||||||
control: 'copy doc',
|
|
||||||
type: 'doc duplicate',
|
|
||||||
category: 'doc',
|
|
||||||
});
|
});
|
||||||
}, [doc.mode$.value, duplicate, pageId]);
|
}, [duplicate, pageId]);
|
||||||
|
|
||||||
const onImportFile = useAsyncCallback(async () => {
|
const onImportFile = useAsyncCallback(async () => {
|
||||||
const options = await importFile();
|
const options = await importFile();
|
||||||
if (options.isWorkspaceFile) {
|
if (options.isWorkspaceFile) {
|
||||||
mixpanel.track('WorkspaceCreated', {
|
track.$.header.actions.createWorkspace({
|
||||||
segment: 'editor header',
|
control: 'import',
|
||||||
page: doc.mode$.value === 'page' ? 'doc editor' : 'edgeless editor',
|
|
||||||
module: 'header menu',
|
|
||||||
control: 'import button',
|
|
||||||
type: 'imported workspace',
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
mixpanel.track('DocCreated', {
|
track.$.header.actions.createDoc({
|
||||||
segment: 'editor header',
|
control: 'import',
|
||||||
page: doc.mode$.value === 'page' ? 'doc editor' : 'edgeless editor',
|
|
||||||
module: 'header menu',
|
|
||||||
control: 'import button',
|
|
||||||
type: 'imported doc',
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [doc.mode$.value, importFile]);
|
}, [importFile]);
|
||||||
|
|
||||||
const showResponsiveMenu = hideShare;
|
const showResponsiveMenu = hideShare;
|
||||||
const ResponsiveMenuItems = (
|
const ResponsiveMenuItems = (
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Tooltip } from '@affine/component/ui/tooltip';
|
import { Tooltip } from '@affine/component/ui/tooltip';
|
||||||
import { useBlockSuiteDocMeta } from '@affine/core/hooks/use-block-suite-page-meta';
|
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 { useI18n } from '@affine/i18n';
|
||||||
import {
|
import {
|
||||||
type DocMode,
|
type DocMode,
|
||||||
@@ -62,8 +62,8 @@ export const EditorModeSwitch = ({
|
|||||||
}, [currentMode, isPublic, doc, pageId, t, trash]);
|
}, [currentMode, isPublic, doc, pageId, t, trash]);
|
||||||
|
|
||||||
const onSwitchToPageMode = useCallback(() => {
|
const onSwitchToPageMode = useCallback(() => {
|
||||||
mixpanel.track('Button', {
|
track.$.header.actions.switchPageMode({
|
||||||
resolve: 'SwitchToPageMode',
|
mode: 'page',
|
||||||
});
|
});
|
||||||
if (currentMode === 'page' || isPublic) {
|
if (currentMode === 'page' || isPublic) {
|
||||||
return;
|
return;
|
||||||
@@ -73,8 +73,8 @@ export const EditorModeSwitch = ({
|
|||||||
}, [currentMode, isPublic, doc, t]);
|
}, [currentMode, isPublic, doc, t]);
|
||||||
|
|
||||||
const onSwitchToEdgelessMode = useCallback(() => {
|
const onSwitchToEdgelessMode = useCallback(() => {
|
||||||
mixpanel.track('Button', {
|
track.$.header.actions.switchPageMode({
|
||||||
resolve: 'SwitchToEdgelessMode',
|
mode: 'edgeless',
|
||||||
});
|
});
|
||||||
if (currentMode === 'edgeless' || isPublic) {
|
if (currentMode === 'edgeless' || isPublic) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { DropdownButton, Menu } from '@affine/component';
|
import { DropdownButton, Menu } from '@affine/component';
|
||||||
import { BlockCard } from '@affine/component/card/block-card';
|
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 { useI18n } from '@affine/i18n';
|
||||||
import { EdgelessIcon, ImportIcon, PageIcon } from '@blocksuite/icons/rc';
|
import { EdgelessIcon, ImportIcon, PageIcon } from '@blocksuite/icons/rc';
|
||||||
import type { PropsWithChildren } from 'react';
|
import type { PropsWithChildren } from 'react';
|
||||||
@@ -70,26 +70,14 @@ export const NewPageButton = ({
|
|||||||
const handleCreateNewPage = useCallback(() => {
|
const handleCreateNewPage = useCallback(() => {
|
||||||
createNewPage();
|
createNewPage();
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
mixpanel.track('DocCreated', {
|
track.allDocs.header.actions.createDoc();
|
||||||
page: 'doc library',
|
|
||||||
segment: 'all doc',
|
|
||||||
module: 'doc list header',
|
|
||||||
control: 'new doc button',
|
|
||||||
type: 'doc',
|
|
||||||
category: 'page',
|
|
||||||
});
|
|
||||||
}, [createNewPage]);
|
}, [createNewPage]);
|
||||||
|
|
||||||
const handleCreateNewEdgeless = useCallback(() => {
|
const handleCreateNewEdgeless = useCallback(() => {
|
||||||
createNewEdgeless();
|
createNewEdgeless();
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
mixpanel.track('DocCreated', {
|
track.allDocs.header.actions.createDoc({
|
||||||
page: 'doc library',
|
mode: 'edgeless',
|
||||||
segment: 'all doc',
|
|
||||||
module: 'doc list header',
|
|
||||||
control: 'new whiteboard button',
|
|
||||||
type: 'doc',
|
|
||||||
category: 'whiteboard',
|
|
||||||
});
|
});
|
||||||
}, [createNewEdgeless]);
|
}, [createNewEdgeless]);
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
} from '@affine/component';
|
} from '@affine/component';
|
||||||
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
||||||
import { useNavigateHelper } from '@affine/core/hooks/use-navigate-helper';
|
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 type { Tag } from '@affine/core/modules/tag';
|
||||||
import { TagService } from '@affine/core/modules/tag';
|
import { TagService } from '@affine/core/modules/tag';
|
||||||
import type { Collection } from '@affine/env/filter';
|
import type { Collection } from '@affine/env/filter';
|
||||||
@@ -49,21 +49,12 @@ export const PageListHeader = () => {
|
|||||||
const onImportFile = useAsyncCallback(async () => {
|
const onImportFile = useAsyncCallback(async () => {
|
||||||
const options = await importFile();
|
const options = await importFile();
|
||||||
if (options.isWorkspaceFile) {
|
if (options.isWorkspaceFile) {
|
||||||
mixpanel.track('WorkspaceCreated', {
|
track.allDocs.header.actions.createWorkspace({
|
||||||
page: 'doc library',
|
control: 'import',
|
||||||
segment: 'all doc',
|
|
||||||
module: 'doc list header',
|
|
||||||
control: 'import button',
|
|
||||||
type: 'imported workspace',
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
mixpanel.track('DocCreated', {
|
track.allDocs.header.actions.createDoc({
|
||||||
page: 'doc library',
|
control: 'import',
|
||||||
segment: 'all doc',
|
|
||||||
module: 'doc list header',
|
|
||||||
control: 'import button',
|
|
||||||
type: 'imported doc',
|
|
||||||
// category
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [importFile]);
|
}, [importFile]);
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import {
|
|||||||
import { useAppSettingHelper } from '@affine/core/hooks/affine/use-app-setting-helper';
|
import { useAppSettingHelper } from '@affine/core/hooks/affine/use-app-setting-helper';
|
||||||
import { useBlockSuiteMetaHelper } from '@affine/core/hooks/affine/use-block-suite-meta-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 { 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 { FavoriteService } from '@affine/core/modules/favorite';
|
||||||
import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/properties';
|
import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/properties';
|
||||||
import { WorkbenchService } from '@affine/core/modules/workbench';
|
import { WorkbenchService } from '@affine/core/modules/workbench';
|
||||||
@@ -115,13 +115,8 @@ export const PageOperationCell = ({
|
|||||||
|
|
||||||
const onDuplicate = useCallback(() => {
|
const onDuplicate = useCallback(() => {
|
||||||
duplicate(page.id, false);
|
duplicate(page.id, false);
|
||||||
mixpanel.track('DocCreated', {
|
track.allDocs.list.docMenu.createDoc({
|
||||||
segment: 'all doc',
|
control: 'duplicate',
|
||||||
module: 'doc item menu',
|
|
||||||
control: 'copy doc',
|
|
||||||
type: 'doc duplicate',
|
|
||||||
category: 'doc',
|
|
||||||
page: 'doc library',
|
|
||||||
});
|
});
|
||||||
}, [duplicate, page.id]);
|
}, [duplicate, page.id]);
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Loading } from '@affine/component';
|
import { Loading } from '@affine/component';
|
||||||
import { Divider } from '@affine/component/ui/divider';
|
import { Divider } from '@affine/component/ui/divider';
|
||||||
import { MenuItem } from '@affine/component/ui/menu';
|
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 { AuthService } from '@affine/core/modules/cloud';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import { Logo1Icon } from '@blocksuite/icons/rc';
|
import { Logo1Icon } from '@blocksuite/icons/rc';
|
||||||
@@ -25,9 +25,7 @@ export const SignInItem = () => {
|
|||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
|
|
||||||
const onClickSignIn = useCallback(() => {
|
const onClickSignIn = useCallback(() => {
|
||||||
mixpanel.track('Button', {
|
track.$.navigationPanel.workspaceList.signIn();
|
||||||
resolve: 'SignIn',
|
|
||||||
});
|
|
||||||
setOpen(state => ({
|
setOpen(state => ({
|
||||||
...state,
|
...state,
|
||||||
openModal: true,
|
openModal: true,
|
||||||
@@ -92,9 +90,7 @@ const UserWithWorkspaceListInner = ({
|
|||||||
if (!isAuthenticated && !runtimeConfig.allowLocalWorkspace) {
|
if (!isAuthenticated && !runtimeConfig.allowLocalWorkspace) {
|
||||||
return openSignInModal();
|
return openSignInModal();
|
||||||
}
|
}
|
||||||
mixpanel.track('Button', {
|
track.$.navigationPanel.workspaceList.createWorkspace();
|
||||||
resolve: 'NewWorkspace',
|
|
||||||
});
|
|
||||||
setOpenCreateWorkspaceModal('new');
|
setOpenCreateWorkspaceModal('new');
|
||||||
onEventEnd?.();
|
onEventEnd?.();
|
||||||
}, [
|
}, [
|
||||||
@@ -104,9 +100,10 @@ const UserWithWorkspaceListInner = ({
|
|||||||
setOpenCreateWorkspaceModal,
|
setOpenCreateWorkspaceModal,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
track.$.navigationPanel.workspaceList.createWorkspace();
|
||||||
const onAddWorkspace = useCallback(() => {
|
const onAddWorkspace = useCallback(() => {
|
||||||
mixpanel.track('Button', {
|
track.$.navigationPanel.workspaceList.createWorkspace({
|
||||||
resolve: 'AddWorkspace',
|
control: 'import',
|
||||||
});
|
});
|
||||||
setOpenCreateWorkspaceModal('add');
|
setOpenCreateWorkspaceModal('add');
|
||||||
onEventEnd?.();
|
onEventEnd?.();
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
||||||
import { mixpanel } from '@affine/core/mixpanel';
|
import { track } from '@affine/core/mixpanel';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import { ImportIcon } from '@blocksuite/icons/rc';
|
import { ImportIcon } from '@blocksuite/icons/rc';
|
||||||
|
|
||||||
@@ -13,22 +13,11 @@ const ImportPage = ({ docCollection }: { docCollection: DocCollection }) => {
|
|||||||
|
|
||||||
const onImportFile = useAsyncCallback(async () => {
|
const onImportFile = useAsyncCallback(async () => {
|
||||||
const options = await importFile();
|
const options = await importFile();
|
||||||
if (options.isWorkspaceFile) {
|
track.$.navigationPanel.workspaceList[
|
||||||
mixpanel.track('WorkspaceCreated', {
|
options.isWorkspaceFile ? 'createWorkspace' : 'createDoc'
|
||||||
segment: 'navigation panel',
|
]({
|
||||||
module: 'doc list header',
|
control: 'import',
|
||||||
control: 'import button',
|
});
|
||||||
type: 'imported workspace',
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
mixpanel.track('DocCreated', {
|
|
||||||
segment: 'navigation panel',
|
|
||||||
module: 'doc list header',
|
|
||||||
control: 'import button',
|
|
||||||
type: 'imported doc',
|
|
||||||
// category
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, [importFile]);
|
}, [importFile]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { openSettingModalAtom } from '@affine/core/atoms';
|
import { openSettingModalAtom } from '@affine/core/atoms';
|
||||||
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
||||||
import { mixpanel } from '@affine/core/mixpanel';
|
import { track } from '@affine/core/mixpanel';
|
||||||
import {
|
import {
|
||||||
ExplorerCollections,
|
ExplorerCollections,
|
||||||
ExplorerFavorites,
|
ExplorerFavorites,
|
||||||
@@ -91,21 +91,14 @@ export const RootAppSidebar = (): ReactElement => {
|
|||||||
async (e?: MouseEvent) => {
|
async (e?: MouseEvent) => {
|
||||||
const page = pageHelper.createPage('page', false);
|
const page = pageHelper.createPage('page', false);
|
||||||
page.load();
|
page.load();
|
||||||
mixpanel.track('DocCreated', {
|
track.$.navigationPanel.$.createDoc();
|
||||||
segment: 'navigation panel',
|
|
||||||
module: 'bottom button',
|
|
||||||
control: 'new doc button',
|
|
||||||
category: 'page',
|
|
||||||
type: 'doc',
|
|
||||||
});
|
|
||||||
workbench.openDoc(page.id, {
|
workbench.openDoc(page.id, {
|
||||||
at: e?.ctrlKey || e?.metaKey ? 'new-tab' : 'active',
|
at: e?.ctrlKey || e?.metaKey ? 'new-tab' : 'active',
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[pageHelper, workbench]
|
[pageHelper, workbench]
|
||||||
);
|
);
|
||||||
|
|
||||||
// Listen to the "New Page" action from the menu
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (environment.isDesktop) {
|
if (environment.isDesktop) {
|
||||||
return events?.applicationMenu.onNewPageAction(() => onClickNewPage());
|
return events?.applicationMenu.onNewPageAction(() => onClickNewPage());
|
||||||
@@ -120,12 +113,7 @@ export const RootAppSidebar = (): ReactElement => {
|
|||||||
activeTab: 'appearance',
|
activeTab: 'appearance',
|
||||||
open: true,
|
open: true,
|
||||||
});
|
});
|
||||||
mixpanel.track('SettingsViewed', {
|
track.$.navigationPanel.$.openSettings();
|
||||||
// page:
|
|
||||||
segment: 'navigation panel',
|
|
||||||
module: 'general list',
|
|
||||||
control: 'settings button',
|
|
||||||
});
|
|
||||||
}, [setOpenSettingModalAtom]);
|
}, [setOpenSettingModalAtom]);
|
||||||
|
|
||||||
const sidebarOpen = useAtomValue(appSidebarOpenAtom);
|
const sidebarOpen = useAtomValue(appSidebarOpenAtom);
|
||||||
@@ -153,7 +141,7 @@ export const RootAppSidebar = (): ReactElement => {
|
|||||||
<QuickSearchInput
|
<QuickSearchInput
|
||||||
className={quickSearch}
|
className={quickSearch}
|
||||||
data-testid="slider-bar-quick-search-button"
|
data-testid="slider-bar-quick-search-button"
|
||||||
data-event-props="$.navigationPanel.generalFunction.quickSearch"
|
data-event-props="$.navigationPanel.$.quickSearch"
|
||||||
onClick={onOpenQuickSearchModal}
|
onClick={onOpenQuickSearchModal}
|
||||||
/>
|
/>
|
||||||
<AddPageButton onClick={onClickNewPage} />
|
<AddPageButton onClick={onClickNewPage} />
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import {
|
|||||||
openSettingModalAtom,
|
openSettingModalAtom,
|
||||||
openSignOutModalAtom,
|
openSignOutModalAtom,
|
||||||
} from '@affine/core/atoms';
|
} from '@affine/core/atoms';
|
||||||
import { mixpanel } from '@affine/core/mixpanel';
|
import { track } from '@affine/core/mixpanel';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import { AccountIcon, SignOutIcon } from '@blocksuite/icons/rc';
|
import { AccountIcon, SignOutIcon } from '@blocksuite/icons/rc';
|
||||||
import { useLiveData, useService } from '@toeverything/infra';
|
import { useLiveData, useService } from '@toeverything/infra';
|
||||||
@@ -83,12 +83,7 @@ const AccountMenu = () => {
|
|||||||
const setOpenSignOutModalAtom = useSetAtom(openSignOutModalAtom);
|
const setOpenSignOutModalAtom = useSetAtom(openSignOutModalAtom);
|
||||||
|
|
||||||
const onOpenAccountSetting = useCallback(() => {
|
const onOpenAccountSetting = useCallback(() => {
|
||||||
mixpanel.track('AccountSettingsViewed', {
|
track.$.navigationPanel.profileAndBadge.openSettings({ to: 'account' });
|
||||||
// page:
|
|
||||||
segment: 'navigation panel',
|
|
||||||
module: 'profile and badge',
|
|
||||||
control: 'profile and email',
|
|
||||||
});
|
|
||||||
setSettingModalAtom(prev => ({
|
setSettingModalAtom(prev => ({
|
||||||
...prev,
|
...prev,
|
||||||
open: true,
|
open: true,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Menu } from '@affine/component';
|
import { Menu } from '@affine/component';
|
||||||
import { mixpanel } from '@affine/core/mixpanel';
|
import { track } from '@affine/core/mixpanel';
|
||||||
import { useService, WorkspacesService } from '@toeverything/infra';
|
import { useService, WorkspacesService } from '@toeverything/infra';
|
||||||
import { useAtom } from 'jotai';
|
import { useAtom } from 'jotai';
|
||||||
import { useCallback, useEffect } from 'react';
|
import { useCallback, useEffect } from 'react';
|
||||||
@@ -16,9 +16,7 @@ export const WorkspaceSelector = () => {
|
|||||||
setOpenUserWorkspaceList(false);
|
setOpenUserWorkspaceList(false);
|
||||||
}, [setOpenUserWorkspaceList]);
|
}, [setOpenUserWorkspaceList]);
|
||||||
const openUserWorkspaceList = useCallback(() => {
|
const openUserWorkspaceList = useCallback(() => {
|
||||||
mixpanel.track('Button', {
|
track.$.navigationPanel.workspaceList.open();
|
||||||
resolve: 'OpenWorkspaceList',
|
|
||||||
});
|
|
||||||
setOpenUserWorkspaceList(true);
|
setOpenUserWorkspaceList(true);
|
||||||
}, [setOpenUserWorkspaceList]);
|
}, [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 { 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 { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
||||||
import { useNavigateHelper } from '@affine/core/hooks/use-navigate-helper';
|
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 { WorkspaceSubPath } from '@affine/core/shared';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import { useLiveData, useService, WorkspaceService } from '@toeverything/infra';
|
import { useLiveData, useService, WorkspaceService } from '@toeverything/infra';
|
||||||
@@ -26,9 +26,7 @@ export const WorkspaceUpgrade = function WorkspaceUpgrade() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mixpanel.track('Button', {
|
track.workspace.$.$.upgradeWorkspace();
|
||||||
resolve: 'UpgradeWorkspace',
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const newWorkspace = await currentWorkspace.upgrade.upgrade();
|
const newWorkspace = await currentWorkspace.upgrade.upgrade();
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import {
|
|||||||
pushGlobalLoadingEventAtom,
|
pushGlobalLoadingEventAtom,
|
||||||
resolveGlobalLoadingEventAtom,
|
resolveGlobalLoadingEventAtom,
|
||||||
} from '@affine/component/global-loading';
|
} from '@affine/component/global-loading';
|
||||||
import { mixpanel } from '@affine/core/mixpanel';
|
import { track } from '@affine/core/mixpanel';
|
||||||
import { apis } from '@affine/electron-api';
|
import { apis } from '@affine/electron-api';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import type { PageRootService, RootBlockModel } from '@blocksuite/blocks';
|
import type { PageRootService, RootBlockModel } from '@blocksuite/blocks';
|
||||||
@@ -26,10 +26,8 @@ async function exportHandler({ page, type }: ExportHandlerOptions) {
|
|||||||
if (editorRoot) {
|
if (editorRoot) {
|
||||||
pageService = editorRoot.spec.getService<PageRootService>('affine:page');
|
pageService = editorRoot.spec.getService<PageRootService>('affine:page');
|
||||||
}
|
}
|
||||||
mixpanel.track('ShareCreated', {
|
track.$.header.share.export({
|
||||||
type,
|
type,
|
||||||
segment: 'sharing panel',
|
|
||||||
module: 'export share',
|
|
||||||
});
|
});
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'html':
|
case 'html':
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import {
|
|||||||
PreconditionStrategy,
|
PreconditionStrategy,
|
||||||
registerAffineCommand,
|
registerAffineCommand,
|
||||||
} from '@affine/core/commands';
|
} from '@affine/core/commands';
|
||||||
import { mixpanel } from '@affine/core/mixpanel';
|
import { track } from '@affine/core/mixpanel';
|
||||||
import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/properties';
|
import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/properties';
|
||||||
import { WorkspaceFlavour } from '@affine/env/workspace';
|
import { WorkspaceFlavour } from '@affine/env/workspace';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
@@ -161,10 +161,8 @@ export function useRegisterBlocksuiteEditorCommands() {
|
|||||||
label: t['com.affine.header.option.duplicate'](),
|
label: t['com.affine.header.option.duplicate'](),
|
||||||
run() {
|
run() {
|
||||||
duplicate(docId);
|
duplicate(docId);
|
||||||
mixpanel.track('DocCreated', {
|
track.$.cmdk.$.createDoc({
|
||||||
control: 'cmdk',
|
control: 'duplicate',
|
||||||
type: 'doc duplicate',
|
|
||||||
category: 'doc',
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { notify } from '@affine/component';
|
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 { getAffineCloudBaseUrl } from '@affine/core/modules/cloud/services/fetch';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import type { Disposable } from '@blocksuite/global/utils';
|
import type { Disposable } from '@blocksuite/global/utils';
|
||||||
@@ -68,9 +68,8 @@ export const useSharingUrl = ({
|
|||||||
.catch(err => {
|
.catch(err => {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
});
|
});
|
||||||
mixpanel.track('ShareLinkCopied', {
|
track.$.header.share.copyShareLink({
|
||||||
module: urlType === 'share' ? 'public share' : 'private share',
|
type: urlType === 'share' ? 'public' : 'private',
|
||||||
type: 'link',
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
notify.error({
|
notify.error({
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useUpgradeNotify } from '@affine/core/components/affine/subscription-landing/notify';
|
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 { SubscriptionPlan, SubscriptionRecurring } from '@affine/graphql';
|
||||||
import { nanoid } from 'nanoid';
|
import { nanoid } from 'nanoid';
|
||||||
import { useCallback, useEffect } from 'react';
|
import { useCallback, useEffect } from 'react';
|
||||||
@@ -129,11 +129,9 @@ export const useSubscriptionNotifyReader = () => {
|
|||||||
upgradeNotify(link);
|
upgradeNotify(link);
|
||||||
localStorage.removeItem(localStorageKey);
|
localStorage.removeItem(localStorageKey);
|
||||||
|
|
||||||
// mixpanel
|
track.$.$.$.checkout({
|
||||||
mixpanel.track('PlanChangeSucceeded', {
|
plan,
|
||||||
category: recurring,
|
recurring,
|
||||||
type: plan,
|
|
||||||
control: 'new subscription',
|
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Failed to parse subscription callback link', err);
|
console.error('Failed to parse subscription callback link', err);
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { atomWithObservable, atomWithStorage } from 'jotai/utils';
|
|||||||
import { useCallback, useState } from 'react';
|
import { useCallback, useState } from 'react';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
import { mixpanel } from '../mixpanel';
|
import { track } from '../mixpanel';
|
||||||
import { popupWindow } from '../utils';
|
import { popupWindow } from '../utils';
|
||||||
import { useAsyncCallback } from './affine-async-hooks';
|
import { useAsyncCallback } from './affine-async-hooks';
|
||||||
|
|
||||||
@@ -122,9 +122,7 @@ export const useAppUpdater = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const quitAndInstall = useCallback(() => {
|
const quitAndInstall = useCallback(() => {
|
||||||
mixpanel.track('Button', {
|
track.$.navigationPanel.bottomButtons.quitAndInstall();
|
||||||
resolve: 'QuitAndInstall',
|
|
||||||
});
|
|
||||||
if (updateReady) {
|
if (updateReady) {
|
||||||
setAppQuitting(true);
|
setAppQuitting(true);
|
||||||
apis?.updater.quitAndInstall().catch(err => {
|
apis?.updater.quitAndInstall().catch(err => {
|
||||||
@@ -135,9 +133,7 @@ export const useAppUpdater = () => {
|
|||||||
}, [updateReady]);
|
}, [updateReady]);
|
||||||
|
|
||||||
const checkForUpdates = useCallback(async () => {
|
const checkForUpdates = useCallback(async () => {
|
||||||
mixpanel.track('Button', {
|
track.$.settingsPanel.about.checkUpdates();
|
||||||
resolve: 'CheckForUpdates',
|
|
||||||
});
|
|
||||||
if (checkingForUpdates) {
|
if (checkingForUpdates) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -154,9 +150,7 @@ export const useAppUpdater = () => {
|
|||||||
}, [checkingForUpdates, setCheckingForUpdates]);
|
}, [checkingForUpdates, setCheckingForUpdates]);
|
||||||
|
|
||||||
const downloadUpdate = useCallback(() => {
|
const downloadUpdate = useCallback(() => {
|
||||||
mixpanel.track('Button', {
|
track.$.settingsPanel.about.downloadUpdate();
|
||||||
resolve: 'DownloadUpdate',
|
|
||||||
});
|
|
||||||
apis?.updater.downloadUpdate().catch(err => {
|
apis?.updater.downloadUpdate().catch(err => {
|
||||||
console.error('Error downloading update:', err);
|
console.error('Error downloading update:', err);
|
||||||
});
|
});
|
||||||
@@ -164,8 +158,8 @@ export const useAppUpdater = () => {
|
|||||||
|
|
||||||
const toggleAutoDownload = useCallback(
|
const toggleAutoDownload = useCallback(
|
||||||
(enable: boolean) => {
|
(enable: boolean) => {
|
||||||
mixpanel.track('Button', {
|
track.$.settingsPanel.about.changeAppSetting({
|
||||||
resolve: 'ToggleAutoDownload',
|
key: 'autoDownload',
|
||||||
value: enable,
|
value: enable,
|
||||||
});
|
});
|
||||||
setSetting({
|
setSetting({
|
||||||
@@ -177,8 +171,8 @@ export const useAppUpdater = () => {
|
|||||||
|
|
||||||
const toggleAutoCheck = useCallback(
|
const toggleAutoCheck = useCallback(
|
||||||
(enable: boolean) => {
|
(enable: boolean) => {
|
||||||
mixpanel.track('Button', {
|
track.$.settingsPanel.about.changeAppSetting({
|
||||||
resolve: 'ToggleAutoCheck',
|
key: 'autoCheckUpdates',
|
||||||
value: enable,
|
value: enable,
|
||||||
});
|
});
|
||||||
setSetting({
|
setSetting({
|
||||||
@@ -189,17 +183,13 @@ export const useAppUpdater = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const openChangelog = useAsyncCallback(async () => {
|
const openChangelog = useAsyncCallback(async () => {
|
||||||
mixpanel.track('Button', {
|
track.$.navigationPanel.bottomButtons.openChangelog();
|
||||||
resolve: 'OpenChangelog',
|
|
||||||
});
|
|
||||||
popupWindow(runtimeConfig.changelogUrl);
|
popupWindow(runtimeConfig.changelogUrl);
|
||||||
await setChangelogUnread(true);
|
await setChangelogUnread(true);
|
||||||
}, [setChangelogUnread]);
|
}, [setChangelogUnread]);
|
||||||
|
|
||||||
const dismissChangelog = useAsyncCallback(async () => {
|
const dismissChangelog = useAsyncCallback(async () => {
|
||||||
mixpanel.track('Button', {
|
track.$.navigationPanel.bottomButtons.dismissChangelog();
|
||||||
resolve: 'DismissChangelog',
|
|
||||||
});
|
|
||||||
await setChangelogUnread(true);
|
await setChangelogUnread(true);
|
||||||
}, [setChangelogUnread]);
|
}, [setChangelogUnread]);
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export function makeTracker(trackFn: TrackFn): CallableEventsChain {
|
|||||||
get(target, prop) {
|
get(target, prop) {
|
||||||
if (
|
if (
|
||||||
typeof prop !== 'string' ||
|
typeof prop !== 'string' ||
|
||||||
prop === '$$typeof' /* webpack hot load reading this prop */
|
prop === '$$typeof' /* webpack hot-reload reads this prop */
|
||||||
) {
|
) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@@ -55,7 +55,12 @@ export function makeTracker(trackFn: TrackFn): CallableEventsChain {
|
|||||||
* @example
|
* @example
|
||||||
*
|
*
|
||||||
* ```html
|
* ```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) {
|
export function enableAutoTrack(root: HTMLElement, trackFn: TrackFn) {
|
||||||
@@ -114,5 +119,6 @@ declare module 'react' {
|
|||||||
interface HTMLAttributes<T> {
|
interface HTMLAttributes<T> {
|
||||||
'data-event-props'?: EventsUnion;
|
'data-event-props'?: EventsUnion;
|
||||||
'data-event-arg'?: string;
|
'data-event-arg'?: string;
|
||||||
|
'data-event-args-control'?: string;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,43 +1,291 @@
|
|||||||
// let '$' stands for unspecific matrix
|
// let '$' stands for unspecific matrix
|
||||||
/* eslint-disable rxjs/finnish */
|
/* eslint-disable rxjs/finnish */
|
||||||
export interface Events {
|
|
||||||
$: {
|
// SECTION: app events
|
||||||
cmdk: {
|
type GeneralEvents = 'openMigrationDataHelp' | 'export';
|
||||||
settings: ['openSettings', 'changeLanguage'];
|
type CmdkEvents = 'quickSearch';
|
||||||
};
|
type AppEvents =
|
||||||
navigationPanel: {
|
| 'checkUpdates'
|
||||||
generalFunction: [
|
| 'downloadUpdate'
|
||||||
'quickSearch',
|
| 'downloadApp'
|
||||||
'createDoc',
|
| 'quitAndInstall'
|
||||||
'goToAllPage',
|
| 'openChangelog'
|
||||||
'goToJournals',
|
| 'dismissChangelog';
|
||||||
'openSettings',
|
type NavigationEvents =
|
||||||
];
|
| 'openInNewTab'
|
||||||
collection: ['createDoc'];
|
| 'openInSplitView'
|
||||||
bottomButtong: ['downloadApp', 'restartAndInstallUpdate'];
|
| 'switchTab'
|
||||||
others: ['openTrash', 'export'];
|
| '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: {
|
doc: {
|
||||||
editor: {
|
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: {
|
type PaymentEventArgs = {
|
||||||
editor: {
|
plan: string;
|
||||||
formatToolbar: ['drawConnector'];
|
recurring: string;
|
||||||
};
|
};
|
||||||
};
|
|
||||||
// remove when type added
|
export type EventArgs = {
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
createWorkspace: { flavour: string };
|
||||||
allDocs: {};
|
oauth: { provider: string };
|
||||||
// remove when type added
|
viewPlans: PaymentEventArgs;
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
checkout: PaymentEventArgs;
|
||||||
collection: {};
|
cancelSubscription: PaymentEventArgs;
|
||||||
// remove when type added
|
confirmCancelingSubscription: PaymentEventArgs;
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
resumeSubscription: PaymentEventArgs;
|
||||||
tag: {};
|
confirmResumingSubscription: PaymentEventArgs;
|
||||||
// remove when type added
|
changeSubscriptionRecurring: PaymentEventArgs;
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
confirmChangingSubscriptionRecurring: PaymentEventArgs;
|
||||||
trash: {};
|
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 = {
|
export type CallableEventsChain = {
|
||||||
[Page in keyof Events]: {
|
[Page in keyof Events]: {
|
||||||
[Segment in keyof Events[Page]]: {
|
[Segment in keyof Events[Page]]: {
|
||||||
[Module in keyof Events[Page][Segment]]: {
|
[Module in keyof Events[Page][Segment]]: {
|
||||||
// @ts-expect-error ignore `symbol | number` as key
|
// @ts-expect-error ignore `symbol | number` as key
|
||||||
[Control in Events[Page][Segment][Module][number]]: (
|
[Event in Events[Page][Segment][Module][number]]: Event extends keyof EventArgs
|
||||||
arg?: string
|
? (
|
||||||
) => void;
|
// 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]]: {
|
[Segment in keyof Events[Page]]: {
|
||||||
[Module in keyof Events[Page][Segment]]: {
|
[Module in keyof Events[Page][Segment]]: {
|
||||||
// @ts-expect-error ignore `symbol | number` as key
|
// @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
|
// @ts-expect-error ignore `symbol | number` as key
|
||||||
}[Events[Page][Segment][Module][number]];
|
}[Events[Page][Segment][Module][number]];
|
||||||
}[keyof Events[Page][Segment]];
|
}[keyof Events[Page][Segment]];
|
||||||
}[keyof Events[Page]];
|
}[keyof Events[Page]];
|
||||||
}[keyof Events];
|
}[keyof Events];
|
||||||
|
|
||||||
// page > segment > module > [controls]
|
// page > segment > module > [events]
|
||||||
type IsFourLevelsDeep<
|
type IsFourLevelsDeep<
|
||||||
T,
|
T,
|
||||||
Depth extends number[] = [],
|
Depth extends number[] = [],
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import {
|
|||||||
filterPage,
|
filterPage,
|
||||||
useEditCollection,
|
useEditCollection,
|
||||||
} from '@affine/core/components/page-list';
|
} 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 { CollectionService } from '@affine/core/modules/collection';
|
||||||
import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/properties';
|
import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/properties';
|
||||||
import { ShareDocsService } from '@affine/core/modules/share-doc';
|
import { ShareDocsService } from '@affine/core/modules/share-doc';
|
||||||
@@ -83,10 +83,9 @@ export const ExplorerCollectionNode = ({
|
|||||||
...collection,
|
...collection,
|
||||||
name,
|
name,
|
||||||
}));
|
}));
|
||||||
mixpanel.track('CollectionRenamed', {
|
|
||||||
page: 'sidebar',
|
track.$.navigationPanel.organize.renameOrganizeItem({
|
||||||
module: 'collection',
|
type: 'collection',
|
||||||
control: 'rename collection',
|
|
||||||
});
|
});
|
||||||
toast(t['com.affine.toastMessage.rename']());
|
toast(t['com.affine.toastMessage.rename']());
|
||||||
}
|
}
|
||||||
@@ -113,10 +112,10 @@ export const ExplorerCollectionNode = ({
|
|||||||
if (collection && data.treeInstruction?.type === 'make-child') {
|
if (collection && data.treeInstruction?.type === 'make-child') {
|
||||||
if (data.source.data.entity?.type === 'doc') {
|
if (data.source.data.entity?.type === 'doc') {
|
||||||
handleAddDocToCollection(data.source.data.entity.id);
|
handleAddDocToCollection(data.source.data.entity.id);
|
||||||
mixpanel.track('AddDocToCollection', {
|
track.$.navigationPanel.organize.createOrganizeItem({
|
||||||
page: 'sidebar',
|
type: 'link',
|
||||||
module: 'collection',
|
target: 'doc',
|
||||||
control: 'drop doc on collection',
|
control: 'drag',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -144,10 +143,9 @@ export const ExplorerCollectionNode = ({
|
|||||||
(data: DropTargetDropEvent<AffineDNDData>) => {
|
(data: DropTargetDropEvent<AffineDNDData>) => {
|
||||||
if (collection && data.source.data.entity?.type === 'doc') {
|
if (collection && data.source.data.entity?.type === 'doc') {
|
||||||
handleAddDocToCollection(data.source.data.entity.id);
|
handleAddDocToCollection(data.source.data.entity.id);
|
||||||
mixpanel.track('AddDocToCollection', {
|
track.$.navigationPanel.organize.createOrganizeItem({
|
||||||
page: 'sidebar',
|
type: 'collection',
|
||||||
module: 'collection',
|
control: 'drag',
|
||||||
control: 'drop doc on collection',
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
} from '@affine/component';
|
} from '@affine/component';
|
||||||
import { useAppSettingHelper } from '@affine/core/hooks/affine/use-app-setting-helper';
|
import { useAppSettingHelper } from '@affine/core/hooks/affine/use-app-setting-helper';
|
||||||
import { useDeleteCollectionInfo } from '@affine/core/hooks/affine/use-delete-collection-info';
|
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 { CollectionService } from '@affine/core/modules/collection';
|
||||||
import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/properties';
|
import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/properties';
|
||||||
import { WorkbenchService } from '@affine/core/modules/workbench';
|
import { WorkbenchService } from '@affine/core/modules/workbench';
|
||||||
@@ -58,15 +58,9 @@ export const useExplorerCollectionNodeOperations = (
|
|||||||
const createAndAddDocument = useCallback(() => {
|
const createAndAddDocument = useCallback(() => {
|
||||||
const newDoc = docsService.createDoc();
|
const newDoc = docsService.createDoc();
|
||||||
collectionService.addPageToCollection(collectionId, newDoc.id);
|
collectionService.addPageToCollection(collectionId, newDoc.id);
|
||||||
mixpanel.track('DocCreated', {
|
track.$.navigationPanel.collections.createDoc();
|
||||||
page: 'sidebar',
|
track.$.navigationPanel.collections.addDocToCollection({
|
||||||
module: 'collection',
|
control: 'button',
|
||||||
control: 'add doc button',
|
|
||||||
});
|
|
||||||
mixpanel.track('AddDocToCollection', {
|
|
||||||
page: 'sidebar',
|
|
||||||
module: 'collection',
|
|
||||||
control: 'add doc button',
|
|
||||||
});
|
});
|
||||||
workbenchService.workbench.openDoc(newDoc.id);
|
workbenchService.workbench.openDoc(newDoc.id);
|
||||||
onOpenCollapsed();
|
onOpenCollapsed();
|
||||||
@@ -80,12 +74,8 @@ export const useExplorerCollectionNodeOperations = (
|
|||||||
|
|
||||||
const handleToggleFavoriteCollection = useCallback(() => {
|
const handleToggleFavoriteCollection = useCallback(() => {
|
||||||
compatibleFavoriteItemsAdapter.toggle(collectionId, 'collection');
|
compatibleFavoriteItemsAdapter.toggle(collectionId, 'collection');
|
||||||
mixpanel.track('ToggleFavorite', {
|
track.$.navigationPanel.organize.toggleFavorite({
|
||||||
page: 'sidebar',
|
|
||||||
module: 'collection',
|
|
||||||
control: 'toggle favorite collection button',
|
|
||||||
type: 'collection',
|
type: 'collection',
|
||||||
id: collectionId,
|
|
||||||
});
|
});
|
||||||
}, [compatibleFavoriteItemsAdapter, collectionId]);
|
}, [compatibleFavoriteItemsAdapter, collectionId]);
|
||||||
|
|
||||||
@@ -104,28 +94,20 @@ export const useExplorerCollectionNodeOperations = (
|
|||||||
|
|
||||||
const handleOpenInSplitView = useCallback(() => {
|
const handleOpenInSplitView = useCallback(() => {
|
||||||
workbenchService.workbench.openCollection(collectionId, { at: 'beside' });
|
workbenchService.workbench.openCollection(collectionId, { at: 'beside' });
|
||||||
mixpanel.track('OpenInSplitView', {
|
track.$.navigationPanel.organize.openInSplitView({
|
||||||
page: 'sidebar',
|
type: 'collection',
|
||||||
module: 'collection',
|
|
||||||
control: 'open in split view button',
|
|
||||||
});
|
});
|
||||||
}, [collectionId, workbenchService.workbench]);
|
}, [collectionId, workbenchService.workbench]);
|
||||||
|
|
||||||
const handleOpenInNewTab = useCallback(() => {
|
const handleOpenInNewTab = useCallback(() => {
|
||||||
workbenchService.workbench.openCollection(collectionId, { at: 'new-tab' });
|
workbenchService.workbench.openCollection(collectionId, { at: 'new-tab' });
|
||||||
mixpanel.track('OpenInNewTab', {
|
track.$.navigationPanel.organize.openInNewTab({ type: 'collection' });
|
||||||
page: 'sidebar',
|
|
||||||
module: 'collection',
|
|
||||||
control: 'open in new tab button',
|
|
||||||
});
|
|
||||||
}, [collectionId, workbenchService.workbench]);
|
}, [collectionId, workbenchService.workbench]);
|
||||||
|
|
||||||
const handleDeleteCollection = useCallback(() => {
|
const handleDeleteCollection = useCallback(() => {
|
||||||
collectionService.deleteCollection(deleteInfo, collectionId);
|
collectionService.deleteCollection(deleteInfo, collectionId);
|
||||||
mixpanel.track('CollectionDeleted', {
|
track.$.navigationPanel.organize.deleteOrganizeItem({
|
||||||
page: 'sidebar',
|
type: 'collection',
|
||||||
module: 'collection',
|
|
||||||
control: 'delete collection button',
|
|
||||||
});
|
});
|
||||||
}, [collectionId, collectionService, deleteInfo]);
|
}, [collectionId, collectionService, deleteInfo]);
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
} from '@affine/component';
|
} from '@affine/component';
|
||||||
import { InfoModal } from '@affine/core/components/affine/page-properties';
|
import { InfoModal } from '@affine/core/components/affine/page-properties';
|
||||||
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
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 { DocsSearchService } from '@affine/core/modules/docs-search';
|
||||||
import type { AffineDNDData } from '@affine/core/types/dnd';
|
import type { AffineDNDData } from '@affine/core/types/dnd';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
@@ -116,11 +116,7 @@ export const ExplorerDocNode = ({
|
|||||||
const handleRename = useAsyncCallback(
|
const handleRename = useAsyncCallback(
|
||||||
async (newName: string) => {
|
async (newName: string) => {
|
||||||
await docsService.changeDocTitle(docId, newName);
|
await docsService.changeDocTitle(docId, newName);
|
||||||
mixpanel.track('DocRenamed', {
|
track.$.navigationPanel.organize.renameOrganizeItem({ type: 'doc' });
|
||||||
page: 'sidebar',
|
|
||||||
module: 'doc',
|
|
||||||
control: 'doc rename',
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
[docId, docsService]
|
[docId, docsService]
|
||||||
);
|
);
|
||||||
@@ -130,10 +126,8 @@ export const ExplorerDocNode = ({
|
|||||||
if (data.treeInstruction?.type === 'make-child') {
|
if (data.treeInstruction?.type === 'make-child') {
|
||||||
if (data.source.data.entity?.type === 'doc') {
|
if (data.source.data.entity?.type === 'doc') {
|
||||||
await docsService.addLinkedDoc(docId, data.source.data.entity.id);
|
await docsService.addLinkedDoc(docId, data.source.data.entity.id);
|
||||||
mixpanel.track('LinkedDocCreated', {
|
track.$.navigationPanel.docs.linkDoc({
|
||||||
page: 'sidebar',
|
control: 'drag',
|
||||||
module: 'doc',
|
|
||||||
control: 'drop doc on doc',
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
toast(t['com.affine.rootAppSidebar.doc.link-doc-only']());
|
toast(t['com.affine.rootAppSidebar.doc.link-doc-only']());
|
||||||
@@ -164,10 +158,8 @@ export const ExplorerDocNode = ({
|
|||||||
if (data.source.data.entity?.type === 'doc') {
|
if (data.source.data.entity?.type === 'doc') {
|
||||||
// TODO(eyhn): timeout&error handling
|
// TODO(eyhn): timeout&error handling
|
||||||
await docsService.addLinkedDoc(docId, data.source.data.entity.id);
|
await docsService.addLinkedDoc(docId, data.source.data.entity.id);
|
||||||
mixpanel.track('LinkedDocCreated', {
|
track.$.navigationPanel.docs.linkDoc({
|
||||||
page: 'sidebar',
|
control: 'drag',
|
||||||
module: 'doc',
|
|
||||||
control: 'drop doc on doc',
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
toast(t['com.affine.rootAppSidebar.doc.link-doc-only']());
|
toast(t['com.affine.rootAppSidebar.doc.link-doc-only']());
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import {
|
|||||||
} from '@affine/component';
|
} from '@affine/component';
|
||||||
import { useAppSettingHelper } from '@affine/core/hooks/affine/use-app-setting-helper';
|
import { useAppSettingHelper } from '@affine/core/hooks/affine/use-app-setting-helper';
|
||||||
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
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 { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/properties';
|
||||||
import { WorkbenchService } from '@affine/core/modules/workbench';
|
import { WorkbenchService } from '@affine/core/modules/workbench';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
@@ -68,10 +68,8 @@ export const useExplorerDocNodeOperations = (
|
|||||||
},
|
},
|
||||||
onConfirm() {
|
onConfirm() {
|
||||||
docRecord.moveToTrash();
|
docRecord.moveToTrash();
|
||||||
mixpanel.track('DocMovedTrash', {
|
track.$.navigationPanel.docs.deleteDoc({
|
||||||
page: 'sidebar',
|
control: 'button',
|
||||||
module: 'doc',
|
|
||||||
control: 'move to trash button',
|
|
||||||
});
|
});
|
||||||
toast(t['com.affine.toastMessage.movedTrash']());
|
toast(t['com.affine.toastMessage.movedTrash']());
|
||||||
},
|
},
|
||||||
@@ -82,10 +80,8 @@ export const useExplorerDocNodeOperations = (
|
|||||||
workbenchService.workbench.openDoc(docId, {
|
workbenchService.workbench.openDoc(docId, {
|
||||||
at: 'new-tab',
|
at: 'new-tab',
|
||||||
});
|
});
|
||||||
mixpanel.track('OpenInNewTab', {
|
track.$.navigationPanel.organize.openInNewTab({
|
||||||
page: 'sidebar',
|
type: 'doc',
|
||||||
module: 'doc',
|
|
||||||
control: 'open in new tab button',
|
|
||||||
});
|
});
|
||||||
}, [docId, workbenchService]);
|
}, [docId, workbenchService]);
|
||||||
|
|
||||||
@@ -93,10 +89,8 @@ export const useExplorerDocNodeOperations = (
|
|||||||
workbenchService.workbench.openDoc(docId, {
|
workbenchService.workbench.openDoc(docId, {
|
||||||
at: 'beside',
|
at: 'beside',
|
||||||
});
|
});
|
||||||
mixpanel.track('OpenInSplitView', {
|
track.$.navigationPanel.organize.openInSplitView({
|
||||||
page: 'sidebar',
|
type: 'doc',
|
||||||
module: 'doc',
|
|
||||||
control: 'open in split view button',
|
|
||||||
});
|
});
|
||||||
}, [docId, workbenchService]);
|
}, [docId, workbenchService]);
|
||||||
|
|
||||||
@@ -104,28 +98,16 @@ export const useExplorerDocNodeOperations = (
|
|||||||
const newDoc = docsService.createDoc();
|
const newDoc = docsService.createDoc();
|
||||||
// TODO: handle timeout & error
|
// TODO: handle timeout & error
|
||||||
await docsService.addLinkedDoc(docId, newDoc.id);
|
await docsService.addLinkedDoc(docId, newDoc.id);
|
||||||
mixpanel.track('DocCreated', {
|
track.$.navigationPanel.docs.createDoc({ control: 'linkDoc' });
|
||||||
page: 'sidebar',
|
track.$.navigationPanel.docs.linkDoc({ control: 'createDoc' });
|
||||||
module: 'doc',
|
|
||||||
control: 'add linked doc button',
|
|
||||||
});
|
|
||||||
mixpanel.track('LinkedDocCreated', {
|
|
||||||
page: 'sidebar',
|
|
||||||
module: 'doc',
|
|
||||||
control: 'add linked doc button',
|
|
||||||
});
|
|
||||||
workbenchService.workbench.openDoc(newDoc.id);
|
workbenchService.workbench.openDoc(newDoc.id);
|
||||||
options.openNodeCollapsed();
|
options.openNodeCollapsed();
|
||||||
}, [docId, options, docsService, workbenchService.workbench]);
|
}, [docId, options, docsService, workbenchService.workbench]);
|
||||||
|
|
||||||
const handleToggleFavoriteDoc = useCallback(() => {
|
const handleToggleFavoriteDoc = useCallback(() => {
|
||||||
compatibleFavoriteItemsAdapter.toggle(docId, 'doc');
|
compatibleFavoriteItemsAdapter.toggle(docId, 'doc');
|
||||||
mixpanel.track('ToggleFavorite', {
|
track.$.navigationPanel.organize.toggleFavorite({
|
||||||
page: 'sidebar',
|
|
||||||
module: 'doc',
|
|
||||||
control: 'toggle favorite button',
|
|
||||||
type: 'doc',
|
type: 'doc',
|
||||||
id: docId,
|
|
||||||
});
|
});
|
||||||
}, [docId, compatibleFavoriteItemsAdapter]);
|
}, [docId, compatibleFavoriteItemsAdapter]);
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import {
|
|||||||
useSelectDoc,
|
useSelectDoc,
|
||||||
useSelectTag,
|
useSelectTag,
|
||||||
} from '@affine/core/components/page-list/selector';
|
} from '@affine/core/components/page-list/selector';
|
||||||
import { mixpanel } from '@affine/core/mixpanel';
|
import { track } from '@affine/core/mixpanel';
|
||||||
import {
|
import {
|
||||||
type FolderNode,
|
type FolderNode,
|
||||||
OrganizeService,
|
OrganizeService,
|
||||||
@@ -178,10 +178,8 @@ export const ExplorerFolderNodeFolder = ({
|
|||||||
|
|
||||||
const handleDelete = useCallback(() => {
|
const handleDelete = useCallback(() => {
|
||||||
node.delete();
|
node.delete();
|
||||||
mixpanel.track('FolderDeleted', {
|
track.$.navigationPanel.organize.deleteOrganizeItem({
|
||||||
page: 'sidebar',
|
type: 'folder',
|
||||||
module: 'organize',
|
|
||||||
control: `delete folder`,
|
|
||||||
});
|
});
|
||||||
notify.success({
|
notify.success({
|
||||||
title: t['com.affine.rootAppSidebar.organize.delete.notify-title']({
|
title: t['com.affine.rootAppSidebar.organize.delete.notify-title']({
|
||||||
@@ -229,23 +227,14 @@ export const ExplorerFolderNodeFolder = ({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
node.moveHere(data.source.data.entity.id, node.indexAt('before'));
|
node.moveHere(data.source.data.entity.id, node.indexAt('before'));
|
||||||
mixpanel.track('FolderMoved', {
|
track.$.navigationPanel.organize.moveOrganizeItem({ type: 'folder' });
|
||||||
page: 'sidebar',
|
|
||||||
module: 'organize',
|
|
||||||
control: 'drop folder at folder',
|
|
||||||
type: 'folder',
|
|
||||||
id: data.source.data.entity.id,
|
|
||||||
});
|
|
||||||
} else if (
|
} else if (
|
||||||
data.source.data.from?.at === 'explorer:organize:folder-node'
|
data.source.data.from?.at === 'explorer:organize:folder-node'
|
||||||
) {
|
) {
|
||||||
node.moveHere(data.source.data.from.nodeId, node.indexAt('before'));
|
node.moveHere(data.source.data.from.nodeId, node.indexAt('before'));
|
||||||
mixpanel.track('FolderLinkMoved', {
|
track.$.navigationPanel.organize.moveOrganizeItem({
|
||||||
page: 'sidebar',
|
type: 'link',
|
||||||
module: 'organize',
|
target: data.source.data.entity?.type,
|
||||||
control: 'drop folder link at folder',
|
|
||||||
type: data.source.data.entity?.type,
|
|
||||||
id: data.source.data.entity?.id,
|
|
||||||
});
|
});
|
||||||
} else if (
|
} else if (
|
||||||
data.source.data.entity?.type === 'collection' ||
|
data.source.data.entity?.type === 'collection' ||
|
||||||
@@ -257,12 +246,9 @@ export const ExplorerFolderNodeFolder = ({
|
|||||||
data.source.data.entity.id,
|
data.source.data.entity.id,
|
||||||
node.indexAt('before')
|
node.indexAt('before')
|
||||||
);
|
);
|
||||||
mixpanel.track('FolderLinkCreated', {
|
track.$.navigationPanel.organize.createOrganizeItem({
|
||||||
page: 'sidebar',
|
type: 'link',
|
||||||
module: 'organize',
|
target: data.source.data.entity?.type,
|
||||||
control: 'drop entity at folder',
|
|
||||||
type: data.source.data.entity?.type,
|
|
||||||
id: data.source.data.entity?.id,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -312,23 +298,13 @@ export const ExplorerFolderNodeFolder = ({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
node.moveHere(data.source.data.entity.id, node.indexAt('before'));
|
node.moveHere(data.source.data.entity.id, node.indexAt('before'));
|
||||||
mixpanel.track('FolderMoved', {
|
track.$.navigationPanel.organize.moveOrganizeItem({ type: 'folder' });
|
||||||
page: 'sidebar',
|
|
||||||
module: 'organize',
|
|
||||||
control: 'drop folder at folder',
|
|
||||||
type: 'folder',
|
|
||||||
id: data.source.data.entity.id,
|
|
||||||
});
|
|
||||||
} else if (
|
} else if (
|
||||||
data.source.data.from?.at === 'explorer:organize:folder-node'
|
data.source.data.from?.at === 'explorer:organize:folder-node'
|
||||||
) {
|
) {
|
||||||
node.moveHere(data.source.data.from.nodeId, node.indexAt('before'));
|
node.moveHere(data.source.data.from.nodeId, node.indexAt('before'));
|
||||||
mixpanel.track('FolderLinkMoved', {
|
track.$.navigationPanel.organize.moveOrganizeItem({
|
||||||
page: 'sidebar',
|
|
||||||
module: 'organize',
|
|
||||||
control: 'drop folder link at folder',
|
|
||||||
type: data.source.data.entity?.type,
|
type: data.source.data.entity?.type,
|
||||||
id: data.source.data.entity?.id,
|
|
||||||
});
|
});
|
||||||
} else if (
|
} else if (
|
||||||
data.source.data.entity?.type === 'collection' ||
|
data.source.data.entity?.type === 'collection' ||
|
||||||
@@ -340,12 +316,9 @@ export const ExplorerFolderNodeFolder = ({
|
|||||||
data.source.data.entity.id,
|
data.source.data.entity.id,
|
||||||
node.indexAt('before')
|
node.indexAt('before')
|
||||||
);
|
);
|
||||||
mixpanel.track('FolderLinkCreated', {
|
track.$.navigationPanel.organize.createOrganizeItem({
|
||||||
page: 'sidebar',
|
type: 'link',
|
||||||
module: 'organize',
|
target: data.source.data.entity?.type,
|
||||||
control: 'drop entity at folder',
|
|
||||||
type: data.source.data.entity?.type,
|
|
||||||
id: data.source.data.entity?.id,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -374,13 +347,7 @@ export const ExplorerFolderNodeFolder = ({
|
|||||||
data.source.data.entity.id,
|
data.source.data.entity.id,
|
||||||
node.indexAt(at, dropAtNode.id)
|
node.indexAt(at, dropAtNode.id)
|
||||||
);
|
);
|
||||||
mixpanel.track('FolderMoved', {
|
track.$.navigationPanel.organize.moveOrganizeItem({ type: 'folder' });
|
||||||
page: 'sidebar',
|
|
||||||
module: 'organize',
|
|
||||||
control: `drop folder ${at === 'before' ? 'above' : 'below'} node`,
|
|
||||||
type: 'folder',
|
|
||||||
id: data.source.data.entity?.id,
|
|
||||||
});
|
|
||||||
} else if (
|
} else if (
|
||||||
data.source.data.from?.at === 'explorer:organize:folder-node'
|
data.source.data.from?.at === 'explorer:organize:folder-node'
|
||||||
) {
|
) {
|
||||||
@@ -388,12 +355,9 @@ export const ExplorerFolderNodeFolder = ({
|
|||||||
data.source.data.from.nodeId,
|
data.source.data.from.nodeId,
|
||||||
node.indexAt(at, dropAtNode.id)
|
node.indexAt(at, dropAtNode.id)
|
||||||
);
|
);
|
||||||
mixpanel.track('FolderLinkMoved', {
|
track.$.navigationPanel.organize.moveOrganizeItem({
|
||||||
page: 'sidebar',
|
type: 'link',
|
||||||
module: 'organize',
|
target: data.source.data.entity?.type,
|
||||||
control: `drop folder link ${at === 'before' ? 'above' : 'below'} node`,
|
|
||||||
type: data.source.data.entity?.type,
|
|
||||||
id: data.source.data.entity?.id,
|
|
||||||
});
|
});
|
||||||
} else if (
|
} else if (
|
||||||
data.source.data.entity?.type === 'collection' ||
|
data.source.data.entity?.type === 'collection' ||
|
||||||
@@ -405,12 +369,10 @@ export const ExplorerFolderNodeFolder = ({
|
|||||||
data.source.data.entity.id,
|
data.source.data.entity.id,
|
||||||
node.indexAt(at, dropAtNode.id)
|
node.indexAt(at, dropAtNode.id)
|
||||||
);
|
);
|
||||||
mixpanel.track('FolderLinkCreated', {
|
|
||||||
page: 'sidebar',
|
track.$.navigationPanel.organize.createOrganizeItem({
|
||||||
module: 'organize',
|
type: 'link',
|
||||||
control: `drop entity ${at === 'before' ? 'above' : 'below'} node`,
|
target: data.source.data.entity?.type,
|
||||||
type: data.source.data.entity?.type,
|
|
||||||
id: data.source.data.entity?.id,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if (data.treeInstruction?.type === 'reparent') {
|
} else if (data.treeInstruction?.type === 'reparent') {
|
||||||
@@ -560,17 +522,10 @@ export const ExplorerFolderNodeFolder = ({
|
|||||||
const newDoc = docsService.createDoc();
|
const newDoc = docsService.createDoc();
|
||||||
node.createLink('doc', newDoc.id, node.indexAt('before'));
|
node.createLink('doc', newDoc.id, node.indexAt('before'));
|
||||||
workbenchService.workbench.openDoc(newDoc.id);
|
workbenchService.workbench.openDoc(newDoc.id);
|
||||||
mixpanel.track('DocCreated', {
|
track.$.navigationPanel.folders.createDoc();
|
||||||
page: 'sidebar',
|
track.$.navigationPanel.organize.createOrganizeItem({
|
||||||
module: 'organize',
|
type: 'link',
|
||||||
control: `folder new doc button`,
|
target: 'doc',
|
||||||
});
|
|
||||||
mixpanel.track('FolderLinkCreated', {
|
|
||||||
page: 'sidebar',
|
|
||||||
module: 'organize',
|
|
||||||
control: `folder new doc button`,
|
|
||||||
type: 'doc',
|
|
||||||
id: newDoc.id,
|
|
||||||
});
|
});
|
||||||
setCollapsed(false);
|
setCollapsed(false);
|
||||||
}, [docsService, node, workbenchService.workbench]);
|
}, [docsService, node, workbenchService.workbench]);
|
||||||
@@ -580,11 +535,7 @@ export const ExplorerFolderNodeFolder = ({
|
|||||||
t['com.affine.rootAppSidebar.organize.new-folders'](),
|
t['com.affine.rootAppSidebar.organize.new-folders'](),
|
||||||
node.indexAt('before')
|
node.indexAt('before')
|
||||||
);
|
);
|
||||||
mixpanel.track('FolderCreated', {
|
track.$.navigationPanel.organize.createOrganizeItem({ type: 'folder' });
|
||||||
page: 'sidebar',
|
|
||||||
module: 'organize',
|
|
||||||
control: `create sub folder`,
|
|
||||||
});
|
|
||||||
setCollapsed(false);
|
setCollapsed(false);
|
||||||
setNewFolderId(newFolderId);
|
setNewFolderId(newFolderId);
|
||||||
}, [node, t]);
|
}, [node, t]);
|
||||||
@@ -612,13 +563,6 @@ export const ExplorerFolderNodeFolder = ({
|
|||||||
|
|
||||||
newItemIds.forEach(id => {
|
newItemIds.forEach(id => {
|
||||||
node.createLink(type, id, node.indexAt('after'));
|
node.createLink(type, id, node.indexAt('after'));
|
||||||
mixpanel.track('FolderLinkCreated', {
|
|
||||||
page: 'sidebar',
|
|
||||||
module: 'organize',
|
|
||||||
control: `add selector`,
|
|
||||||
type,
|
|
||||||
id,
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
removedItems.forEach(node => node.delete());
|
removedItems.forEach(node => node.delete());
|
||||||
const updated = newItemIds.length + removedItems.length;
|
const updated = newItemIds.length + removedItems.length;
|
||||||
@@ -627,6 +571,10 @@ export const ExplorerFolderNodeFolder = ({
|
|||||||
.catch(err => {
|
.catch(err => {
|
||||||
console.error(`Unexpected error while selecting ${type}`, err);
|
console.error(`Unexpected error while selecting ${type}`, err);
|
||||||
});
|
});
|
||||||
|
track.$.navigationPanel.organize.createOrganizeItem({
|
||||||
|
type: 'link',
|
||||||
|
target: type,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
children,
|
children,
|
||||||
@@ -763,7 +711,7 @@ export const ExplorerFolderNodeFolder = ({
|
|||||||
handleCreateSubfolder,
|
handleCreateSubfolder,
|
||||||
handleDelete,
|
handleDelete,
|
||||||
handleNewDoc,
|
handleNewDoc,
|
||||||
node.id,
|
node,
|
||||||
t,
|
t,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -774,23 +722,6 @@ export const ExplorerFolderNodeFolder = ({
|
|||||||
return folderOperations;
|
return folderOperations;
|
||||||
}, [additionalOperations, 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(
|
const childrenOperations = useCallback(
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||||
(type: string, node: FolderNode) => {
|
(type: string, node: FolderNode) => {
|
||||||
@@ -806,7 +737,9 @@ export const ExplorerFolderNodeFolder = ({
|
|||||||
<RemoveFolderIcon />
|
<RemoveFolderIcon />
|
||||||
</MenuIcon>
|
</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']()}
|
{t['com.affine.rootAppSidebar.organize.delete-from-folder']()}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
@@ -816,7 +749,7 @@ export const ExplorerFolderNodeFolder = ({
|
|||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
},
|
},
|
||||||
[handleDeleteChildren, t]
|
[t]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleCollapsedChange = useCallback((collapsed: boolean) => {
|
const handleCollapsedChange = useCallback((collapsed: boolean) => {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import {
|
|||||||
type DropTargetOptions,
|
type DropTargetOptions,
|
||||||
toast,
|
toast,
|
||||||
} from '@affine/component';
|
} from '@affine/component';
|
||||||
import { mixpanel } from '@affine/core/mixpanel';
|
import { track } from '@affine/core/mixpanel';
|
||||||
import type { Tag } from '@affine/core/modules/tag';
|
import type { Tag } from '@affine/core/modules/tag';
|
||||||
import { TagService } from '@affine/core/modules/tag';
|
import { TagService } from '@affine/core/modules/tag';
|
||||||
import type { AffineDNDData } from '@affine/core/types/dnd';
|
import type { AffineDNDData } from '@affine/core/types/dnd';
|
||||||
@@ -84,10 +84,8 @@ export const ExplorerTagNode = ({
|
|||||||
(newName: string) => {
|
(newName: string) => {
|
||||||
if (tagRecord && tagRecord.value$.value !== newName) {
|
if (tagRecord && tagRecord.value$.value !== newName) {
|
||||||
tagRecord.rename(newName);
|
tagRecord.rename(newName);
|
||||||
mixpanel.track('TagRenamed', {
|
track.$.navigationPanel.organize.renameOrganizeItem({
|
||||||
page: 'sidebar',
|
type: 'tag',
|
||||||
module: 'tag',
|
|
||||||
control: 'tag rename',
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -99,10 +97,8 @@ export const ExplorerTagNode = ({
|
|||||||
if (data.treeInstruction?.type === 'make-child' && tagRecord) {
|
if (data.treeInstruction?.type === 'make-child' && tagRecord) {
|
||||||
if (data.source.data.entity?.type === 'doc') {
|
if (data.source.data.entity?.type === 'doc') {
|
||||||
tagRecord.tag(data.source.data.entity.id);
|
tagRecord.tag(data.source.data.entity.id);
|
||||||
mixpanel.track('DocTagged', {
|
track.$.navigationPanel.tags.tagDoc({
|
||||||
page: 'sidebar',
|
control: 'drag',
|
||||||
module: 'tag',
|
|
||||||
control: 'drop doc on tag',
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
toast(t['com.affine.rootAppSidebar.tag.doc-only']());
|
toast(t['com.affine.rootAppSidebar.tag.doc-only']());
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
toast,
|
toast,
|
||||||
} from '@affine/component';
|
} from '@affine/component';
|
||||||
import { useAppSettingHelper } from '@affine/core/hooks/affine/use-app-setting-helper';
|
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 { FavoriteService } from '@affine/core/modules/favorite';
|
||||||
import { TagService } from '@affine/core/modules/tag';
|
import { TagService } from '@affine/core/modules/tag';
|
||||||
import { WorkbenchService } from '@affine/core/modules/workbench';
|
import { WorkbenchService } from '@affine/core/modules/workbench';
|
||||||
@@ -51,16 +51,7 @@ export const useExplorerTagNodeOperations = (
|
|||||||
if (tagRecord) {
|
if (tagRecord) {
|
||||||
const newDoc = docsService.createDoc();
|
const newDoc = docsService.createDoc();
|
||||||
tagRecord?.tag(newDoc.id);
|
tagRecord?.tag(newDoc.id);
|
||||||
mixpanel.track('DocCreated', {
|
track.$.navigationPanel.tags.createDoc();
|
||||||
page: 'sidebar',
|
|
||||||
module: 'tag',
|
|
||||||
control: 'add doc button',
|
|
||||||
});
|
|
||||||
mixpanel.track('DocTagged', {
|
|
||||||
page: 'sidebar',
|
|
||||||
module: 'tag',
|
|
||||||
control: 'add doc button',
|
|
||||||
});
|
|
||||||
workbenchService.workbench.openDoc(newDoc.id);
|
workbenchService.workbench.openDoc(newDoc.id);
|
||||||
openNodeCollapsed();
|
openNodeCollapsed();
|
||||||
}
|
}
|
||||||
@@ -68,11 +59,7 @@ export const useExplorerTagNodeOperations = (
|
|||||||
|
|
||||||
const handleMoveToTrash = useCallback(() => {
|
const handleMoveToTrash = useCallback(() => {
|
||||||
tagService.tagList.deleteTag(tagId);
|
tagService.tagList.deleteTag(tagId);
|
||||||
mixpanel.track('TagDeleted', {
|
track.$.navigationPanel.organize.deleteOrganizeItem({ type: 'tag' });
|
||||||
page: 'sidebar',
|
|
||||||
module: 'tag',
|
|
||||||
control: 'remove tag button',
|
|
||||||
});
|
|
||||||
toast(t['com.affine.tags.delete-tags.toast']());
|
toast(t['com.affine.tags.delete-tags.toast']());
|
||||||
}, [t, tagId, tagService.tagList]);
|
}, [t, tagId, tagService.tagList]);
|
||||||
|
|
||||||
@@ -80,21 +67,13 @@ export const useExplorerTagNodeOperations = (
|
|||||||
workbenchService.workbench.openTag(tagId, {
|
workbenchService.workbench.openTag(tagId, {
|
||||||
at: 'beside',
|
at: 'beside',
|
||||||
});
|
});
|
||||||
mixpanel.track('OpenInSplitView', {
|
track.$.navigationPanel.organize.openInSplitView({ type: 'tag' });
|
||||||
page: 'sidebar',
|
|
||||||
module: 'tag',
|
|
||||||
control: 'open in split view button',
|
|
||||||
});
|
|
||||||
}, [tagId, workbenchService]);
|
}, [tagId, workbenchService]);
|
||||||
|
|
||||||
const handleToggleFavoriteTag = useCallback(() => {
|
const handleToggleFavoriteTag = useCallback(() => {
|
||||||
favoriteService.favoriteList.toggle('tag', tagId);
|
favoriteService.favoriteList.toggle('tag', tagId);
|
||||||
mixpanel.track('ToggleFavorite', {
|
track.$.navigationPanel.organize.toggleFavorite({
|
||||||
page: 'sidebar',
|
|
||||||
module: 'tag',
|
|
||||||
control: 'toggle favorite tag button',
|
|
||||||
type: 'tag',
|
type: 'tag',
|
||||||
id: tagId,
|
|
||||||
});
|
});
|
||||||
}, [favoriteService, tagId]);
|
}, [favoriteService, tagId]);
|
||||||
|
|
||||||
@@ -102,11 +81,7 @@ export const useExplorerTagNodeOperations = (
|
|||||||
workbenchService.workbench.openTag(tagId, {
|
workbenchService.workbench.openTag(tagId, {
|
||||||
at: 'new-tab',
|
at: 'new-tab',
|
||||||
});
|
});
|
||||||
mixpanel.track('OpenInNewTab', {
|
track.$.navigationPanel.organize.openInNewTab({ type: 'tag' });
|
||||||
page: 'sidebar',
|
|
||||||
module: 'tag',
|
|
||||||
control: 'open in new tab button',
|
|
||||||
});
|
|
||||||
}, [tagId, workbenchService]);
|
}, [tagId, workbenchService]);
|
||||||
|
|
||||||
return useMemo(
|
return useMemo(
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { IconButton } from '@affine/component';
|
import { IconButton } from '@affine/component';
|
||||||
import { useEditCollectionName } from '@affine/core/components/page-list';
|
import { useEditCollectionName } from '@affine/core/components/page-list';
|
||||||
import { createEmptyCollection } from '@affine/core/components/page-list/use-collection-manager';
|
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 { CollectionService } from '@affine/core/modules/collection';
|
||||||
import { ExplorerTreeRoot } from '@affine/core/modules/explorer/views/tree';
|
import { ExplorerTreeRoot } from '@affine/core/modules/explorer/views/tree';
|
||||||
import { WorkbenchService } from '@affine/core/modules/workbench';
|
import { WorkbenchService } from '@affine/core/modules/workbench';
|
||||||
@@ -35,10 +35,8 @@ export const ExplorerCollections = () => {
|
|||||||
.then(name => {
|
.then(name => {
|
||||||
const id = nanoid();
|
const id = nanoid();
|
||||||
collectionService.addCollection(createEmptyCollection(id, { name }));
|
collectionService.addCollection(createEmptyCollection(id, { name }));
|
||||||
mixpanel.track('CollectionCreated', {
|
track.$.navigationPanel.organize.createOrganizeItem({
|
||||||
page: 'sidebar',
|
type: 'collection',
|
||||||
module: 'collections',
|
|
||||||
control: 'new collection button',
|
|
||||||
});
|
});
|
||||||
workbenchService.workbench.openCollection(id);
|
workbenchService.workbench.openCollection(id);
|
||||||
explorerSection.setCollapsed(false);
|
explorerSection.setCollapsed(false);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import {
|
|||||||
IconButton,
|
IconButton,
|
||||||
useDropTarget,
|
useDropTarget,
|
||||||
} from '@affine/component';
|
} from '@affine/component';
|
||||||
import { mixpanel } from '@affine/core/mixpanel';
|
import { track } from '@affine/core/mixpanel';
|
||||||
import {
|
import {
|
||||||
DropEffect,
|
DropEffect,
|
||||||
type ExplorerTreeNodeDropEffect,
|
type ExplorerTreeNodeDropEffect,
|
||||||
@@ -59,12 +59,9 @@ export const ExplorerFavorites = () => {
|
|||||||
data.source.data.entity.id,
|
data.source.data.entity.id,
|
||||||
favoriteService.favoriteList.indexAt('before')
|
favoriteService.favoriteList.indexAt('before')
|
||||||
);
|
);
|
||||||
mixpanel.track('AddFavorite', {
|
track.$.navigationPanel.organize.toggleFavorite({
|
||||||
page: 'sidebar',
|
|
||||||
module: 'favorite',
|
|
||||||
control: 'drop entity to favorite',
|
|
||||||
type: data.source.data.entity.type,
|
type: data.source.data.entity.type,
|
||||||
id: data.source.data.entity.id,
|
on: true,
|
||||||
});
|
});
|
||||||
explorerSection.setCollapsed(false);
|
explorerSection.setCollapsed(false);
|
||||||
}
|
}
|
||||||
@@ -98,18 +95,6 @@ export const ExplorerFavorites = () => {
|
|||||||
newDoc.id,
|
newDoc.id,
|
||||||
favoriteService.favoriteList.indexAt('before')
|
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);
|
workbenchService.workbench.openDoc(newDoc.id);
|
||||||
explorerSection.setCollapsed(false);
|
explorerSection.setCollapsed(false);
|
||||||
}, [
|
}, [
|
||||||
@@ -144,12 +129,8 @@ export const ExplorerFavorites = () => {
|
|||||||
favorite
|
favorite
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
mixpanel.track('ReorderFavorite', {
|
track.$.navigationPanel.organize.orderOrganizeItem({
|
||||||
page: 'sidebar',
|
|
||||||
module: 'favorite',
|
|
||||||
control: 'drop to reorder favorite',
|
|
||||||
type: data.source.data.entity.type,
|
type: data.source.data.entity.type,
|
||||||
id: data.source.data.entity.id,
|
|
||||||
});
|
});
|
||||||
} else if (
|
} else if (
|
||||||
data.source.data.entity?.type &&
|
data.source.data.entity?.type &&
|
||||||
@@ -165,12 +146,9 @@ export const ExplorerFavorites = () => {
|
|||||||
favorite
|
favorite
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
mixpanel.track('AddFavorite', {
|
track.$.navigationPanel.organize.toggleFavorite({
|
||||||
page: 'sidebar',
|
|
||||||
module: 'favorite',
|
|
||||||
control: 'drop entity to favorite',
|
|
||||||
type: data.source.data.entity.type,
|
type: data.source.data.entity.type,
|
||||||
id: data.source.data.entity.id,
|
on: true,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
return; // not supported
|
return; // not supported
|
||||||
@@ -238,6 +216,8 @@ export const ExplorerFavorites = () => {
|
|||||||
<>
|
<>
|
||||||
<IconButton
|
<IconButton
|
||||||
data-testid="explorer-bar-add-favorite-button"
|
data-testid="explorer-bar-add-favorite-button"
|
||||||
|
data-event-props="$.navigationPanel.favorites.createDoc"
|
||||||
|
data-event-args-control="addFavorite"
|
||||||
onClick={handleCreateNewFavoriteDoc}
|
onClick={handleCreateNewFavoriteDoc}
|
||||||
size="16"
|
size="16"
|
||||||
tooltip={t[
|
tooltip={t[
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { IconButton, useConfirmModal } from '@affine/component';
|
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 { ExplorerTreeRoot } from '@affine/core/modules/explorer/views/tree';
|
||||||
import { FavoriteItemsAdapter } from '@affine/core/modules/properties';
|
import { FavoriteItemsAdapter } from '@affine/core/modules/properties';
|
||||||
import { Trans, useI18n } from '@affine/i18n';
|
import { Trans, useI18n } from '@affine/i18n';
|
||||||
@@ -58,18 +58,8 @@ export const ExplorerMigrationFavorites = () => {
|
|||||||
t['com.affine.rootAppSidebar.migration-data.clean-all.cancel'](),
|
t['com.affine.rootAppSidebar.migration-data.clean-all.cancel'](),
|
||||||
onConfirm() {
|
onConfirm() {
|
||||||
favoriteItemsAdapter.clearAll();
|
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]);
|
}, [favoriteItemsAdapter, openConfirmModal, t]);
|
||||||
|
|
||||||
const handleClickHelp = useCallback(() => {
|
const handleClickHelp = useCallback(() => {
|
||||||
@@ -92,11 +82,7 @@ export const ExplorerMigrationFavorites = () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
mixpanel.track('OpenMigrationDataHelp', {
|
track.$.navigationPanel.migrationData.openMigrationDataHelp();
|
||||||
page: 'sidebar',
|
|
||||||
module: 'migration data',
|
|
||||||
control: 'help button',
|
|
||||||
});
|
|
||||||
}, [handleClickClear, openConfirmModal, t]);
|
}, [handleClickClear, openConfirmModal, t]);
|
||||||
|
|
||||||
if (favorites.length === 0) {
|
if (favorites.length === 0) {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import {
|
|||||||
IconButton,
|
IconButton,
|
||||||
toast,
|
toast,
|
||||||
} from '@affine/component';
|
} from '@affine/component';
|
||||||
import { mixpanel } from '@affine/core/mixpanel';
|
import { track } from '@affine/core/mixpanel';
|
||||||
import {
|
import {
|
||||||
type ExplorerTreeNodeDropEffect,
|
type ExplorerTreeNodeDropEffect,
|
||||||
ExplorerTreeRoot,
|
ExplorerTreeRoot,
|
||||||
@@ -46,11 +46,7 @@ export const ExplorerOrganize = () => {
|
|||||||
'New Folder',
|
'New Folder',
|
||||||
rootFolder.indexAt('before')
|
rootFolder.indexAt('before')
|
||||||
);
|
);
|
||||||
mixpanel.track('FolderCreated', {
|
track.$.navigationPanel.organize.createOrganizeItem({ type: 'folder' });
|
||||||
page: 'sidebar',
|
|
||||||
module: 'organize',
|
|
||||||
control: 'new folder',
|
|
||||||
});
|
|
||||||
setNewFolderId(newFolderId);
|
setNewFolderId(newFolderId);
|
||||||
explorerSection.setCollapsed(false);
|
explorerSection.setCollapsed(false);
|
||||||
}, [explorerSection, rootFolder]);
|
}, [explorerSection, rootFolder]);
|
||||||
@@ -71,13 +67,7 @@ export const ExplorerOrganize = () => {
|
|||||||
data.source.data.entity.id,
|
data.source.data.entity.id,
|
||||||
rootFolder.indexAt(at, node.id)
|
rootFolder.indexAt(at, node.id)
|
||||||
);
|
);
|
||||||
mixpanel.track('FolderMoved', {
|
track.$.navigationPanel.organize.moveOrganizeItem({ type: 'folder' });
|
||||||
page: 'sidebar',
|
|
||||||
module: 'organize',
|
|
||||||
control: 'drop at root',
|
|
||||||
type: 'folder',
|
|
||||||
id: node.id,
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
toast(t['com.affine.rootAppSidebar.organize.root-folder-only']());
|
toast(t['com.affine.rootAppSidebar.organize.root-folder-only']());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { IconButton } from '@affine/component';
|
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 { ExplorerTreeRoot } from '@affine/core/modules/explorer/views/tree';
|
||||||
import type { Tag } from '@affine/core/modules/tag';
|
import type { Tag } from '@affine/core/modules/tag';
|
||||||
import { TagService } from '@affine/core/modules/tag';
|
import { TagService } from '@affine/core/modules/tag';
|
||||||
@@ -32,11 +32,7 @@ export const ExplorerTags = () => {
|
|||||||
tagService.randomTagColor()
|
tagService.randomTagColor()
|
||||||
);
|
);
|
||||||
setCreatedTag(newTags);
|
setCreatedTag(newTags);
|
||||||
mixpanel.track('TagCreated', {
|
track.$.navigationPanel.organize.createOrganizeItem({ type: 'tag' });
|
||||||
page: 'sidebar',
|
|
||||||
module: 'tags',
|
|
||||||
control: 'new tag button',
|
|
||||||
});
|
|
||||||
explorerSection.setCollapsed(false);
|
explorerSection.setCollapsed(false);
|
||||||
}, [explorerSection, t, tagService]);
|
}, [explorerSection, t, tagService]);
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { EMPTY, mergeMap, switchMap } from 'rxjs';
|
|||||||
|
|
||||||
import { generateSubscriptionCallbackLink } from '../hooks/affine/use-subscription-notify';
|
import { generateSubscriptionCallbackLink } from '../hooks/affine/use-subscription-notify';
|
||||||
import { RouteLogic, useNavigateHelper } from '../hooks/use-navigate-helper';
|
import { RouteLogic, useNavigateHelper } from '../hooks/use-navigate-helper';
|
||||||
import { mixpanel } from '../mixpanel';
|
import { mixpanel, track } from '../mixpanel';
|
||||||
import { AuthService, SubscriptionService } from '../modules/cloud';
|
import { AuthService, SubscriptionService } from '../modules/cloud';
|
||||||
import { container } from './subscribe.css';
|
import { container } from './subscribe.css';
|
||||||
|
|
||||||
@@ -70,9 +70,9 @@ export const Component = () => {
|
|||||||
: !!subscriptionService.subscription.pro$.value;
|
: !!subscriptionService.subscription.pro$.value;
|
||||||
if (!subscribed) {
|
if (!subscribed) {
|
||||||
setMessage('Creating checkout...');
|
setMessage('Creating checkout...');
|
||||||
mixpanel.track('PlanUpgradeStarted', {
|
track.subscriptionLanding.$.$.checkout({
|
||||||
type: plan,
|
plan: receivedPlan,
|
||||||
category: recurring,
|
recurring: receivedRecurring,
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
const account = authService.session.account$.value;
|
const account = authService.session.account$.value;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
import { Header } from '@affine/core/components/pure/header';
|
import { Header } from '@affine/core/components/pure/header';
|
||||||
import { WorkspaceModeFilterTab } from '@affine/core/components/pure/workspace-mode-filter-tab';
|
import { WorkspaceModeFilterTab } from '@affine/core/components/pure/workspace-mode-filter-tab';
|
||||||
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
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 type { Filter } from '@affine/env/filter';
|
||||||
import { PlusIcon } from '@blocksuite/icons/rc';
|
import { PlusIcon } from '@blocksuite/icons/rc';
|
||||||
import { useService, WorkspaceService } from '@toeverything/infra';
|
import { useService, WorkspaceService } from '@toeverything/infra';
|
||||||
@@ -32,21 +32,12 @@ export const AllPageHeader = ({
|
|||||||
const onImportFile = useAsyncCallback(async () => {
|
const onImportFile = useAsyncCallback(async () => {
|
||||||
const options = await importFile();
|
const options = await importFile();
|
||||||
if (options.isWorkspaceFile) {
|
if (options.isWorkspaceFile) {
|
||||||
mixpanel.track('WorkspaceCreated', {
|
track.allDocs.header.actions.createWorkspace({
|
||||||
page: 'doc library',
|
control: 'import',
|
||||||
segment: 'all page',
|
|
||||||
module: 'doc list header',
|
|
||||||
control: 'import button',
|
|
||||||
type: 'imported workspace',
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
mixpanel.track('DocCreated', {
|
track.allDocs.header.actions.createDoc({
|
||||||
page: 'doc library',
|
control: 'import',
|
||||||
segment: 'all page',
|
|
||||||
module: 'doc list header',
|
|
||||||
control: 'import button',
|
|
||||||
type: 'imported doc',
|
|
||||||
// category
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [importFile]);
|
}, [importFile]);
|
||||||
|
|||||||
Reference in New Issue
Block a user