diff --git a/packages/frontend/core/src/components/affine/share-page-modal/index.tsx b/packages/frontend/core/src/components/affine/share-page-modal/index.tsx index 491370ec9a..5e0bca85fb 100644 --- a/packages/frontend/core/src/components/affine/share-page-modal/index.tsx +++ b/packages/frontend/core/src/components/affine/share-page-modal/index.tsx @@ -1,6 +1,8 @@ import { useEnableCloud } from '@affine/core/hooks/affine/use-enable-cloud'; +import { track } from '@affine/core/mixpanel'; import type { Doc } from '@blocksuite/store'; -import type { Workspace } from '@toeverything/infra'; +import { type Workspace } from '@toeverything/infra'; +import { useCallback } from 'react'; import { ShareMenu } from './share-menu'; @@ -11,6 +13,11 @@ type SharePageModalProps = { export const SharePageButton = ({ workspace, page }: SharePageModalProps) => { const confirmEnableCloud = useEnableCloud(); + const handleOpenShareModal = useCallback((open: boolean) => { + if (open) { + track.$.sharePanel.$.open(); + } + }, []); return ( { openPageId: page.id, }) } + onOpenShareModal={handleOpenShareModal} /> ); }; diff --git a/packages/frontend/core/src/components/affine/share-page-modal/share-menu/share-menu.tsx b/packages/frontend/core/src/components/affine/share-page-modal/share-menu/share-menu.tsx index 9576ebf10c..42fb69bdd7 100644 --- a/packages/frontend/core/src/components/affine/share-page-modal/share-menu/share-menu.tsx +++ b/packages/frontend/core/src/components/affine/share-page-modal/share-menu/share-menu.tsx @@ -21,6 +21,7 @@ export interface ShareMenuProps extends PropsWithChildren { workspaceMetadata: WorkspaceMetadata; currentPage: Doc; onEnableAffineCloud: () => void; + onOpenShareModal?: (open: boolean) => void; } export const ShareMenuContent = (props: ShareMenuProps) => { @@ -73,6 +74,7 @@ const LocalShareMenu = (props: ShareMenuProps) => { }} rootOptions={{ modal: false, + onOpenChange: props.onOpenShareModal, }} >
@@ -92,6 +94,7 @@ const CloudShareMenu = (props: ShareMenuProps) => { }} rootOptions={{ modal: false, + onOpenChange: props.onOpenShareModal, }} >
diff --git a/packages/frontend/core/src/components/affine/share-page-modal/share-menu/share-page.tsx b/packages/frontend/core/src/components/affine/share-page-modal/share-menu/share-page.tsx index 1be710270b..60c053f216 100644 --- a/packages/frontend/core/src/components/affine/share-page-modal/share-menu/share-page.tsx +++ b/packages/frontend/core/src/components/affine/share-page-modal/share-menu/share-page.tsx @@ -106,7 +106,7 @@ export const AffineSharePage = (props: ShareMenuProps) => { await shareService.share.enableShare( mode === 'edgeless' ? PublicPageMode.Edgeless : PublicPageMode.Page ); - track.$.header.share.createShareLink({ + track.$.sharePanel.$.createShareLink({ mode, }); notify.success({ diff --git a/packages/frontend/core/src/components/blocksuite/block-suite-header/favorite/index.tsx b/packages/frontend/core/src/components/blocksuite/block-suite-header/favorite/index.tsx index 748406fc17..78b098dbf4 100644 --- a/packages/frontend/core/src/components/blocksuite/block-suite-header/favorite/index.tsx +++ b/packages/frontend/core/src/components/blocksuite/block-suite-header/favorite/index.tsx @@ -1,4 +1,5 @@ import { FavoriteTag } from '@affine/core/components/page-list'; +import { track } from '@affine/core/mixpanel'; import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/properties'; import { toast } from '@affine/core/utils'; import { useI18n } from '@affine/i18n'; @@ -30,11 +31,16 @@ export const useFavorite = (pageId: string) => { export const FavoriteButton = ({ pageId }: FavoriteButtonProps) => { const { favorite, toggleFavorite } = useFavorite(pageId); + const handleFavorite = useCallback(() => { + track.$.header.actions.toggleFavorite(); + toggleFavorite(); + }, [toggleFavorite]); + return ( ); }; diff --git a/packages/frontend/core/src/components/blocksuite/block-suite-header/info/index.tsx b/packages/frontend/core/src/components/blocksuite/block-suite-header/info/index.tsx index 557c1958a2..8ecb2d3e79 100644 --- a/packages/frontend/core/src/components/blocksuite/block-suite-header/info/index.tsx +++ b/packages/frontend/core/src/components/blocksuite/block-suite-header/info/index.tsx @@ -1,15 +1,20 @@ import { IconButton } from '@affine/component'; import { openInfoModalAtom } from '@affine/core/atoms'; +import { track } from '@affine/core/mixpanel'; import { useI18n } from '@affine/i18n'; import { InformationIcon } from '@blocksuite/icons/rc'; import { useSetAtom } from 'jotai'; +import { useCallback } from 'react'; export const InfoButton = () => { const setOpenInfoModal = useSetAtom(openInfoModalAtom); const t = useI18n(); - const onOpenInfoModal = () => { + + const onOpenInfoModal = useCallback(() => { + track.$.header.actions.openDocInfo(); setOpenInfoModal(true); - }; + }, [setOpenInfoModal]); + return ( { + track.$.header.history.open(); if (workspace.flavour === WorkspaceFlavour.AFFINE_CLOUD) { return setHistoryModalOpen(true); } @@ -116,9 +117,10 @@ export const PageHeaderMenuButton = ({ }, [setOpenHistoryTipsModal, workspace.flavour]); const setOpenInfoModal = useSetAtom(openInfoModalAtom); - const openInfoModal = () => { + const openInfoModal = useCallback(() => { + track.$.header.pageInfo.open(); setOpenInfoModal(true); - }; + }, [setOpenInfoModal]); const handleOpenInNewTab = useCallback(() => { workbench.openDoc(pageId, { @@ -133,6 +135,7 @@ export const PageHeaderMenuButton = ({ }, [pageId, workbench]); const handleOpenTrashModal = useCallback(() => { + track.$.header.docOptions.deleteDoc(); setTrashModal({ open: true, pageIds: [pageId], @@ -140,8 +143,14 @@ export const PageHeaderMenuButton = ({ }); }, [doc.meta$.value.title, pageId, setTrashModal]); + const handleRename = useCallback(() => { + rename?.(); + track.$.header.docOptions.renameDoc(); + }, [rename]); + const handleSwitchMode = useCallback(() => { doc.toggleMode(); + track.$.header.docOptions.switchPageMode(); toast( currentMode === 'page' ? t['com.affine.toastMessage.edgelessMode']() @@ -153,17 +162,24 @@ export const PageHeaderMenuButton = ({ transition: 'all 0.3s', }; + const handleMenuOpenChange = useCallback((open: boolean) => { + if (open) { + track.$.header.docOptions.open(); + } + }, []); + const exportHandler = useExportPage(doc.blockSuiteDoc); const handleDuplicate = useCallback(() => { duplicate(pageId); - track.$.header.actions.createDoc({ + track.$.header.docOptions.createDoc({ control: 'duplicate', }); }, [duplicate, pageId]); const onImportFile = useAsyncCallback(async () => { const options = await importFile(); + track.$.header.docOptions.import(); if (options.isWorkspaceFile) { track.$.header.actions.createWorkspace({ control: 'import', @@ -175,6 +191,17 @@ export const PageHeaderMenuButton = ({ } }, [importFile]); + const handleShareMenuOpenChange = useCallback((open: boolean) => { + if (open) { + track.$.sharePanel.$.open(); + } + }, []); + + const handleToggleFavorite = useCallback(() => { + track.$.header.docOptions.toggleFavorite(); + toggleFavorite(); + }, [toggleFavorite]); + const showResponsiveMenu = hideShare; const ResponsiveMenuItems = ( <> @@ -204,6 +231,9 @@ export const PageHeaderMenuButton = ({ ), }} + subOptions={{ + onOpenChange: handleShareMenuOpenChange, + }} > {t['com.affine.share-menu.shareButton']()} @@ -223,7 +253,7 @@ export const PageHeaderMenuButton = ({ } data-testid="editor-option-menu-rename" - onSelect={rename} + onSelect={handleRename} style={menuItemStyle} > {t['Rename']()} @@ -246,7 +276,7 @@ export const PageHeaderMenuButton = ({ @@ -389,6 +419,9 @@ export const PageHeaderMenuButton = ({ contentOptions={{ align: 'center', }} + rootOptions={{ + onOpenChange: handleMenuOpenChange, + }} > diff --git a/packages/frontend/core/src/components/blocksuite/block-suite-header/title/index.tsx b/packages/frontend/core/src/components/blocksuite/block-suite-header/title/index.tsx index 0de3202d4c..b4cf76358c 100644 --- a/packages/frontend/core/src/components/blocksuite/block-suite-header/title/index.tsx +++ b/packages/frontend/core/src/components/blocksuite/block-suite-header/title/index.tsx @@ -18,13 +18,14 @@ export interface BlockSuiteHeaderTitleProps { isPublic?: boolean; inputHandleRef?: InlineEditProps['handleRef']; className?: string; + onEditSave?: () => void; } const inputAttrs = { 'data-testid': 'title-content', } as HTMLAttributes; export const BlocksuiteHeaderTitle = (props: BlockSuiteHeaderTitleProps) => { - const { docCollection, pageId, isPublic, inputHandleRef } = props; + const { docCollection, pageId, isPublic, inputHandleRef, onEditSave } = props; const currentPage = docCollection.getDoc(pageId); const pageMeta = useBlockSuiteDocMeta(docCollection).find( meta => meta.id === currentPage?.id @@ -34,9 +35,10 @@ export const BlocksuiteHeaderTitle = (props: BlockSuiteHeaderTitleProps) => { const onChange = useCallback( (v: string) => { + onEditSave?.(); setDocTitle(currentPage?.id || '', v); }, - [currentPage?.id, setDocTitle] + [currentPage?.id, onEditSave, setDocTitle] ); return ( diff --git a/packages/frontend/core/src/components/page-list/operation-menu-items/export.tsx b/packages/frontend/core/src/components/page-list/operation-menu-items/export.tsx index 50db842c97..de41e8dd2f 100644 --- a/packages/frontend/core/src/components/page-list/operation-menu-items/export.tsx +++ b/packages/frontend/core/src/components/page-list/operation-menu-items/export.tsx @@ -1,4 +1,5 @@ import { MenuIcon, MenuItem, MenuSub } from '@affine/component'; +import { track } from '@affine/core/mixpanel'; import { useI18n } from '@affine/i18n'; import { ExportIcon, @@ -8,7 +9,7 @@ import { ExportToPngIcon, } from '@blocksuite/icons/rc'; import type { ReactNode } from 'react'; -import { useMemo } from 'react'; +import { useCallback, useMemo } from 'react'; import { transitionStyle } from './index.css'; @@ -115,6 +116,12 @@ export const Export = ({ exportHandler, className, pageMode }: ExportProps) => { pageMode={pageMode} /> ); + const handleExportMenuOpenChange = useCallback((open: boolean) => { + if (open) { + track.$.header.docOptions.export(); + } + }, []); + return ( { ), ['data-testid' as string]: 'export-menu', }} + subOptions={{ + onOpenChange: handleExportMenuOpenChange, + }} > {t.Export()} diff --git a/packages/frontend/core/src/hooks/affine/use-export-page.ts b/packages/frontend/core/src/hooks/affine/use-export-page.ts index 1f11173c13..8e66017658 100644 --- a/packages/frontend/core/src/hooks/affine/use-export-page.ts +++ b/packages/frontend/core/src/hooks/affine/use-export-page.ts @@ -26,7 +26,7 @@ async function exportHandler({ page, type }: ExportHandlerOptions) { if (editorRoot) { pageService = editorRoot.spec.getService('affine:page'); } - track.$.header.share.export({ + track.$.sharePanel.$.export({ type, }); switch (type) { diff --git a/packages/frontend/core/src/hooks/affine/use-share-url.ts b/packages/frontend/core/src/hooks/affine/use-share-url.ts index affec8cca4..ff9de6cfbc 100644 --- a/packages/frontend/core/src/hooks/affine/use-share-url.ts +++ b/packages/frontend/core/src/hooks/affine/use-share-url.ts @@ -68,7 +68,7 @@ export const useSharingUrl = ({ .catch(err => { console.error(err); }); - track.$.header.share.copyShareLink({ + track.$.sharePanel.$.copyShareLink({ type: urlType === 'share' ? 'public' : 'private', }); } else { diff --git a/packages/frontend/core/src/mixpanel/events.ts b/packages/frontend/core/src/mixpanel/events.ts index 5ef2bdeef9..80591a21e5 100644 --- a/packages/frontend/core/src/mixpanel/events.ts +++ b/packages/frontend/core/src/mixpanel/events.ts @@ -35,7 +35,13 @@ type DocEvents = | 'renameDoc' | 'linkDoc' | 'deleteDoc' - | 'switchPageMode'; + | 'switchPageMode' + | 'openDocOptionsMenu' + | 'openDocInfo' + | 'renameDoc' + | 'deleteDoc' + | 'viewHistoryVersions' + | 'viewInfo'; type EditorEvents = 'bold' | 'italic' | 'underline' | 'strikeThrough'; // END SECTION @@ -75,7 +81,11 @@ type OrganizeEvents = // END SECTION // SECTION: cloud events -type ShareEvents = 'createShareLink' | 'copyShareLink'; +type ShareEvents = + | 'createShareLink' + | 'copyShareLink' + | 'openShareMenu' + | 'share'; type AuthEvents = 'signIn' | 'signUp' | 'oauth' | 'signOut'; type AccountEvents = 'uploadAvatar' | 'removeAvatar' | 'updateUserName'; type PaymentEvents = @@ -127,6 +137,12 @@ const PageEvents = { $: ['createWorkspace', 'checkout'], auth: ['oauth', 'signIn', 'signUp'], }, + sharePanel: { + $: ['createShareLink', 'copyShareLink', 'export', 'open'], + }, + docInfoPanel: { + $: ['open'], + }, settingsPanel: { menu: ['openSettings'], workspace: ['viewPlans'], @@ -194,8 +210,26 @@ const PageEvents = { aiAction: ['viewPlans'], }, header: { - actions: ['createDoc', 'createWorkspace', 'switchPageMode'], - share: ['createShareLink', 'copyShareLink', 'export'], + actions: [ + 'createDoc', + 'createWorkspace', + 'switchPageMode', + 'toggleFavorite', + 'openDocInfo', + 'renameDoc', + ], + docOptions: [ + 'open', + 'deleteDoc', + 'renameDoc', + 'switchPageMode', + 'createDoc', + 'import', + 'toggleFavorite', + 'export', + ], + history: ['open'], + pageInfo: ['open'], }, }, doc: { diff --git a/packages/frontend/core/src/pages/workspace/detail-page/detail-page-header.tsx b/packages/frontend/core/src/pages/workspace/detail-page/detail-page-header.tsx index 7b9c0ccb69..65b3136287 100644 --- a/packages/frontend/core/src/pages/workspace/detail-page/detail-page-header.tsx +++ b/packages/frontend/core/src/pages/workspace/detail-page/detail-page-header.tsx @@ -15,6 +15,7 @@ import { EditorModeSwitch } from '@affine/core/components/blocksuite/block-suite import { useRegisterCopyLinkCommands } from '@affine/core/hooks/affine/use-register-copy-link-commands'; import { useDocCollectionPageTitle } from '@affine/core/hooks/use-block-suite-workspace-page-title'; import { useJournalInfoHelper } from '@affine/core/hooks/use-journal'; +import { track } from '@affine/core/mixpanel'; import { ViewIcon, ViewTitle } from '@affine/core/modules/workbench'; import type { Doc } from '@blocksuite/store'; import { @@ -129,6 +130,9 @@ export function NormalPageHeader({ page, workspace }: PageHeaderProps) { const title = useDocCollectionPageTitle(workspace.docCollection, page?.id); const doc = useService(DocService).doc; const currentMode = useLiveData(doc.mode$); + const onEditSave = useCallback(() => { + track.$.header.actions.renameDoc(); + }, []); return (
@@ -142,6 +146,7 @@ export function NormalPageHeader({ page, workspace }: PageHeaderProps) { inputHandleRef={titleInputHandleRef} pageId={page?.id} docCollection={workspace.docCollection} + onEditSave={onEditSave} />
{hideCollect ? null : (