feat(core): all docs tracks (#12556)

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **New Features**
  - Added enhanced tracking for user interactions across document lists, navigation, display menus, quick actions, and collection operations.
  - User actions such as opening documents, editing collections, toggling favorites, changing view modes, and navigating collections are now logged for analytics.

- **Chores**
  - Expanded internal event tracking capabilities to support more detailed analytics on user interactions throughout the interface.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
EYHN
2025-05-27 08:17:14 +00:00
parent 8d3b20ecc7
commit 5033142a77
16 changed files with 110 additions and 16 deletions

View File

@@ -2,6 +2,7 @@ import { Divider, MenuItem } from '@affine/component';
import type { GroupByParams } from '@affine/core/modules/collection-rules/types'; import type { GroupByParams } from '@affine/core/modules/collection-rules/types';
import { WorkspacePropertyService } from '@affine/core/modules/workspace-property'; import { WorkspacePropertyService } from '@affine/core/modules/workspace-property';
import { useI18n } from '@affine/i18n'; import { useI18n } from '@affine/i18n';
import track from '@affine/track';
import { DoneIcon } from '@blocksuite/icons/rc'; import { DoneIcon } from '@blocksuite/icons/rc';
import { useLiveData, useService } from '@toeverything/infra'; import { useLiveData, useService } from '@toeverything/infra';
import { cssVarV2 } from '@toeverything/theme/v2'; import { cssVarV2 } from '@toeverything/theme/v2';
@@ -128,6 +129,10 @@ const GroupByListItem = ({
<MenuItem <MenuItem
onClick={e => { onClick={e => {
e.preventDefault(); e.preventDefault();
track.allDocs.header.displayMenu.editDisplayMenu({
control: 'groupBy',
type: property.systemProperty?.type ?? 'custom-property',
});
if (value) { if (value) {
onChange?.(value); onChange?.(value);
} }

View File

@@ -2,6 +2,7 @@ import { MenuItem } from '@affine/component';
import type { OrderByParams } from '@affine/core/modules/collection-rules/types'; import type { OrderByParams } from '@affine/core/modules/collection-rules/types';
import { WorkspacePropertyService } from '@affine/core/modules/workspace-property'; import { WorkspacePropertyService } from '@affine/core/modules/workspace-property';
import { useI18n } from '@affine/i18n'; import { useI18n } from '@affine/i18n';
import track from '@affine/track';
import { SortDownIcon, SortUpIcon } from '@blocksuite/icons/rc'; import { SortDownIcon, SortUpIcon } from '@blocksuite/icons/rc';
import { useLiveData, useService } from '@toeverything/infra'; import { useLiveData, useService } from '@toeverything/infra';
import { cssVarV2 } from '@toeverything/theme/v2'; import { cssVarV2 } from '@toeverything/theme/v2';
@@ -125,6 +126,10 @@ const OrderByListItem = ({
<MenuItem <MenuItem
onClick={e => { onClick={e => {
e.preventDefault(); e.preventDefault();
track.allDocs.header.displayMenu.editDisplayMenu({
control: 'orderBy',
type: property.systemProperty?.type ?? 'custom-property',
});
if (value) { if (value) {
onChange?.(value); onChange?.(value);
} }

View File

@@ -1,6 +1,7 @@
import { Button, Divider } from '@affine/component'; import { Button, Divider } from '@affine/component';
import { WorkspacePropertyService } from '@affine/core/modules/workspace-property'; import { WorkspacePropertyService } from '@affine/core/modules/workspace-property';
import { useI18n } from '@affine/i18n'; import { useI18n } from '@affine/i18n';
import track from '@affine/track';
import { useLiveData, useService } from '@toeverything/infra'; import { useLiveData, useService } from '@toeverything/infra';
import { useCallback, useMemo } from 'react'; import { useCallback, useMemo } from 'react';
@@ -160,7 +161,13 @@ const PropertyRenderer = ({
<Button <Button
key={key} key={key}
data-show={isActive} data-show={isActive}
onClick={() => handlePropertyClick(key)} onClick={() => {
track.allDocs.header.displayMenu.editDisplayMenu({
control: 'displayProperties',
type: systemProperty?.type ?? 'custom-property',
});
handlePropertyClick(key);
}}
className={styles.property} className={styles.property}
data-key={key} data-key={key}
> >

View File

@@ -1,5 +1,6 @@
import { Checkbox, MenuItem, MenuSub } from '@affine/component'; import { Checkbox, MenuItem, MenuSub } from '@affine/component';
import { useI18n } from '@affine/i18n'; import { useI18n } from '@affine/i18n';
import track from '@affine/track';
import { useCallback } from 'react'; import { useCallback } from 'react';
import { type QuickAction, quickActions } from '../quick-actions.constants'; import { type QuickAction, quickActions } from '../quick-actions.constants';
@@ -27,6 +28,10 @@ export const QuickActionsConfig = ({
action={action} action={action}
active={displayPreference[`${action.key}`] ?? false} active={displayPreference[`${action.key}`] ?? false}
onClick={() => { onClick={() => {
track.allDocs.header.displayMenu.editDisplayMenu({
control: 'quickActions',
type: action.key,
});
onDisplayPreferenceChange({ onDisplayPreferenceChange({
...displayPreference, ...displayPreference,
[action.key]: !displayPreference[action.key], [action.key]: !displayPreference[action.key],

View File

@@ -1,4 +1,5 @@
import { RadioGroup, type RadioItem } from '@affine/component'; import { RadioGroup, type RadioItem } from '@affine/component';
import track from '@affine/track';
import { useCallback } from 'react'; import { useCallback } from 'react';
import { import {
@@ -34,6 +35,9 @@ export const ViewToggle = ({
}) => { }) => {
const handleViewChange = useCallback( const handleViewChange = useCallback(
(view: DocListItemView) => { (view: DocListItemView) => {
track.allDocs.header.viewMode.editDisplayMenu({
type: view,
});
onViewChange(view); onViewChange(view);
}, },
[onViewChange] [onViewChange]

View File

@@ -10,6 +10,7 @@ import { DocDisplayMetaService } from '@affine/core/modules/doc-display-meta';
import { WorkbenchLink } from '@affine/core/modules/workbench'; import { WorkbenchLink } from '@affine/core/modules/workbench';
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';
import track from '@affine/track';
import { import {
AutoTidyUpIcon, AutoTidyUpIcon,
PropertyIcon, PropertyIcon,
@@ -139,6 +140,7 @@ export const DocListItem = ({ ...props }: DocListItemProps) => {
return; return;
} else { } else {
// as link // as link
track.allDocs.list.doc.openDoc();
return; return;
} }
} }

View File

@@ -42,6 +42,7 @@ const ToggleFavorite = ({ docId }: DocOperationProps) => {
const toggleFavorite = useCallback(() => { const toggleFavorite = useCallback(() => {
favAdapter.toggle(docId, 'doc'); favAdapter.toggle(docId, 'doc');
track.allDocs.list.docMenu.toggleFavorite();
}, [docId, favAdapter]); }, [docId, favAdapter]);
return ( return (
@@ -66,7 +67,7 @@ const DocInfo = ({ docId }: DocOperationProps) => {
const onOpenInfoModal = useCallback(() => { const onOpenInfoModal = useCallback(() => {
if (docId) { if (docId) {
track.$.docInfoPanel.$.open(); track.allDocs.list.docMenu.openDocInfo();
workspaceDialogService.open('doc-info', { docId }); workspaceDialogService.open('doc-info', { docId });
} }
}, [docId, workspaceDialogService]); }, [docId, workspaceDialogService]);
@@ -85,6 +86,8 @@ const NewTab = ({ docId }: DocOperationProps) => {
const t = useI18n(); const t = useI18n();
const workbench = useService(WorkbenchService).workbench; const workbench = useService(WorkbenchService).workbench;
const onOpenInNewTab = useCallback(() => { const onOpenInNewTab = useCallback(() => {
track.allDocs.list.doc.openDoc();
track.allDocs.list.docMenu.openInNewTab();
workbench.openDoc(docId, { at: 'new-tab' }); workbench.openDoc(docId, { at: 'new-tab' });
}, [docId, workbench]); }, [docId, workbench]);
@@ -103,6 +106,7 @@ const SplitView = ({ docId }: DocOperationProps) => {
const workbench = useService(WorkbenchService).workbench; const workbench = useService(WorkbenchService).workbench;
const onOpenInSplitView = useCallback(() => { const onOpenInSplitView = useCallback(() => {
track.allDocs.list.doc.openDoc();
track.allDocs.list.docMenu.openInSplitView(); track.allDocs.list.docMenu.openInSplitView();
workbench.openDoc(docId, { at: 'tail' }); workbench.openDoc(docId, { at: 'tail' });
}, [docId, workbench]); }, [docId, workbench]);

View File

@@ -45,6 +45,7 @@ export const QuickFavorite = memo(function QuickFavorite({
onClick?.(e); onClick?.(e);
e.stopPropagation(); e.stopPropagation();
e.preventDefault(); e.preventDefault();
track.allDocs.list.docMenu.toggleFavorite();
favAdapter.toggle(doc.id, 'doc'); favAdapter.toggle(doc.id, 'doc');
}, },
[doc.id, favAdapter, onClick] [doc.id, favAdapter, onClick]
@@ -77,6 +78,8 @@ export const QuickTab = memo(function QuickTab({
onClick?.(e); onClick?.(e);
e.stopPropagation(); e.stopPropagation();
e.preventDefault(); e.preventDefault();
track.allDocs.list.doc.openDoc();
track.allDocs.list.docMenu.openInNewTab();
workbench.openDoc(doc.id, { at: 'new-tab' }); workbench.openDoc(doc.id, { at: 'new-tab' });
}, },
[doc.id, onClick, workbench] [doc.id, onClick, workbench]
@@ -109,6 +112,7 @@ export const QuickSplit = memo(function QuickSplit({
onClick?.(e); onClick?.(e);
e.stopPropagation(); e.stopPropagation();
e.preventDefault(); e.preventDefault();
track.allDocs.list.doc.openDoc();
track.allDocs.list.docMenu.openInSplitView(); track.allDocs.list.docMenu.openInSplitView();
workbench.openDoc(doc.id, { at: 'tail' }); workbench.openDoc(doc.id, { at: 'tail' });
}, },

View File

@@ -1,5 +1,6 @@
import { WorkbenchLink } from '@affine/core/modules/workbench'; import { WorkbenchLink } from '@affine/core/modules/workbench';
import { useI18n } from '@affine/i18n'; import { useI18n } from '@affine/i18n';
import track from '@affine/track';
import * as styles from './navigation.css'; import * as styles from './navigation.css';
@@ -37,6 +38,11 @@ export const ExplorerNavigation = ({ active }: { active: NavigationKey }) => {
data-testid={item.testId} data-testid={item.testId}
data-active={active === item.value} data-active={active === item.value}
to={item.to} to={item.to}
onClick={() => {
track.allDocs.header.navigation.navigateAllDocsRouter({
control: item.value,
});
}}
className={styles.item} className={styles.item}
> >
{t[item.label]()} {t[item.label]()}

View File

@@ -4,6 +4,7 @@ import { WorkspaceDialogService } from '@affine/core/modules/dialogs';
import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/favorite'; import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/favorite';
import { WorkbenchService } from '@affine/core/modules/workbench'; import { WorkbenchService } from '@affine/core/modules/workbench';
import { useI18n } from '@affine/i18n'; import { useI18n } from '@affine/i18n';
import track from '@affine/track';
import { import {
DeleteIcon, DeleteIcon,
EditIcon, EditIcon,
@@ -71,6 +72,7 @@ export const CollectionOperations = ({
}, [openRenameModal, openPromptModal, t, service, collection]); }, [openRenameModal, openPromptModal, t, service, collection]);
const showEdit = useCallback(() => { const showEdit = useCallback(() => {
track.collection.collection.$.editCollection();
workspaceDialogService.open('collection-editor', { workspaceDialogService.open('collection-editor', {
collectionId: collection.id, collectionId: collection.id,
}); });

View File

@@ -106,6 +106,7 @@ export const useNavigationPanelCollectionNodeOperations = (
const handleShowEdit = useCallback(() => { const handleShowEdit = useCallback(() => {
onOpenEdit(); onOpenEdit();
track.$.navigationPanel.collections.editCollection();
}, [onOpenEdit]); }, [onOpenEdit]);
return useMemo( return useMemo(

View File

@@ -251,6 +251,9 @@ export const NavigationPanelDocNode = ({
setCollapsed={setCollapsed} setCollapsed={setCollapsed}
canDrop={handleCanDrop} canDrop={handleCanDrop}
to={`/${docId}`} to={`/${docId}`}
onClick={() => {
track.$.navigationPanel.docs.openDoc();
}}
active={active} active={active}
postfix={ postfix={
referencesLoading && referencesLoading &&

View File

@@ -105,6 +105,7 @@ export const useNavigationPanelDocNodeOperations = (
workbenchService.workbench.openDoc(docId, { workbenchService.workbench.openDoc(docId, {
at: 'new-tab', at: 'new-tab',
}); });
track.$.navigationPanel.docs.openDoc();
track.$.navigationPanel.organize.openInNewTab({ track.$.navigationPanel.organize.openInNewTab({
type: 'doc', type: 'doc',
}); });
@@ -114,6 +115,7 @@ export const useNavigationPanelDocNodeOperations = (
workbenchService.workbench.openDoc(docId, { workbenchService.workbench.openDoc(docId, {
at: 'beside', at: 'beside',
}); });
track.$.navigationPanel.docs.openDoc();
track.$.navigationPanel.organize.openInSplitView({ track.$.navigationPanel.organize.openInSplitView({
type: 'doc', type: 'doc',
}); });

View File

@@ -14,6 +14,7 @@ import {
import type { FilterParams } from '@affine/core/modules/collection-rules'; import type { FilterParams } from '@affine/core/modules/collection-rules';
import { WorkspaceDialogService } from '@affine/core/modules/dialogs'; import { WorkspaceDialogService } from '@affine/core/modules/dialogs';
import { useI18n } from '@affine/i18n'; import { useI18n } from '@affine/i18n';
import track from '@affine/track';
import { import {
CloseIcon, CloseIcon,
CollectionsIcon, CollectionsIcon,
@@ -102,10 +103,15 @@ export const PinnedCollections = ({
<div <div
className={styles.item} className={styles.item}
data-active={activeCollectionId === null ? 'true' : undefined} data-active={activeCollectionId === null ? 'true' : undefined}
onClick={() => onClick={() => {
// only fire onActiveAll if the collection is not already active // only fire onActiveAll if the collection is not already active
activeCollectionId !== null ? onActiveAll() : undefined if (activeCollectionId !== null) {
} track.allDocs.header.navigation.navigatePinedCollectionRouter({
control: 'all',
});
onActiveAll();
}
}}
role="button" role="button"
> >
{t['com.affine.all-docs.pinned-collection.all']()} {t['com.affine.all-docs.pinned-collection.all']()}
@@ -115,12 +121,15 @@ export const PinnedCollections = ({
key={record.collectionId} key={record.collectionId}
record={record} record={record}
isActive={activeCollectionId === record.collectionId} isActive={activeCollectionId === record.collectionId}
onClick={() => onClick={() => {
// only fire onActiveCollection if the collection is not already active // only fire onActiveCollection if the collection is not already active
activeCollectionId !== record.collectionId if (activeCollectionId !== record.collectionId) {
? onActiveCollection(record.collectionId) track.allDocs.header.navigation.navigatePinedCollectionRouter({
: undefined control: 'user-custom-collection',
} });
onActiveCollection(record.collectionId);
}
}}
onClickRemove={() => { onClickRemove={() => {
const nextCollectionId = pinnedCollections[index - 1]?.collectionId; const nextCollectionId = pinnedCollections[index - 1]?.collectionId;
if (nextCollectionId) { if (nextCollectionId) {
@@ -144,11 +153,12 @@ export const PinnedCollections = ({
<IconButton <IconButton
size="16" size="16"
className={styles.editIconButton} className={styles.editIconButton}
onClick={() => onClick={() => {
track.allDocs.header.collection.editCollection();
workspaceDialogService.open('collection-editor', { workspaceDialogService.open('collection-editor', {
collectionId: activeCollectionId, collectionId: activeCollectionId,
}) });
} }}
> >
<EditIcon /> <EditIcon />
</IconButton> </IconButton>
@@ -228,6 +238,7 @@ export const AddPinnedCollectionMenuContent = ({
prefixIcon={<CollectionsIcon />} prefixIcon={<CollectionsIcon />}
suffixIcon={<PlusIcon />} suffixIcon={<PlusIcon />}
onClick={() => { onClick={() => {
track.allDocs.header.collection.addPinnedCollection();
onPinCollection(meta.id); onPinCollection(meta.id);
}} }}
> >

View File

@@ -10,6 +10,7 @@ import type { DocRecord } from '@affine/core/modules/doc';
import { WorkbenchLink } from '@affine/core/modules/workbench'; import { WorkbenchLink } from '@affine/core/modules/workbench';
import { WorkspaceService } from '@affine/core/modules/workspace'; import { WorkspaceService } from '@affine/core/modules/workspace';
import { useI18n } from '@affine/i18n'; import { useI18n } from '@affine/i18n';
import track from '@affine/track';
import type { DocMode } from '@blocksuite/affine/model'; import type { DocMode } from '@blocksuite/affine/model';
import { ViewLayersIcon } from '@blocksuite/icons/rc'; import { ViewLayersIcon } from '@blocksuite/icons/rc';
import { useLiveData, useServices } from '@toeverything/infra'; import { useLiveData, useServices } from '@toeverything/infra';
@@ -31,6 +32,7 @@ export const CollectionListHeader = ({
}); });
const handleEdit = useCallback(() => { const handleEdit = useCallback(() => {
track.collection.collection.$.editCollection();
workspaceDialogService.open('collection-editor', { workspaceDialogService.open('collection-editor', {
collectionId: collection.id, collectionId: collection.id,
}); });

View File

@@ -39,6 +39,7 @@ type WorkspaceEvents =
| 'export' | 'export'
| 'openWorkspaceList'; | 'openWorkspaceList';
type DocEvents = type DocEvents =
| 'openDoc'
| 'createDoc' | 'createDoc'
| 'quickStart' | 'quickStart'
| 'renameDoc' | 'renameDoc'
@@ -53,7 +54,10 @@ type DocEvents =
| 'bookmark' | 'bookmark'
| 'editProperty' | 'editProperty'
| 'editPropertyMeta' | 'editPropertyMeta'
| 'addProperty'; | 'addProperty'
| 'editDisplayMenu'
| 'navigateAllDocsRouter'
| 'navigatePinedCollectionRouter';
type EditorEvents = type EditorEvents =
| 'bold' | 'bold'
| 'italic' | 'italic'
@@ -77,7 +81,9 @@ type CollectionEvents =
| 'createCollection' | 'createCollection'
| 'deleteCollection' | 'deleteCollection'
| 'renameCollection' | 'renameCollection'
| 'addDocToCollection'; | 'addDocToCollection'
| 'editCollection'
| 'addPinnedCollection';
type FolderEvents = type FolderEvents =
| 'createFolder' | 'createFolder'
| 'renameFolder' | 'renameFolder'
@@ -340,12 +346,13 @@ interface PageEvents extends PageDivision {
'toggleFavorite', 'toggleFavorite',
'drop', 'drop',
]; ];
docs: ['createDoc', 'deleteDoc', 'linkDoc', 'drop']; docs: ['createDoc', 'deleteDoc', 'linkDoc', 'drop', 'openDoc'];
collections: [ collections: [
'createDoc', 'createDoc',
'addDocToCollection', 'addDocToCollection',
'removeOrganizeItem', 'removeOrganizeItem',
'drop', 'drop',
'editCollection',
]; ];
folders: ['createDoc', 'drop']; folders: ['createDoc', 'drop'];
tags: ['createDoc', 'tagDoc', 'drop']; tags: ['createDoc', 'tagDoc', 'drop'];
@@ -489,15 +496,21 @@ interface PageEvents extends PageDivision {
}; };
allDocs: { allDocs: {
header: { header: {
navigation: ['navigateAllDocsRouter', 'navigatePinedCollectionRouter'];
actions: ['createDoc', 'createWorkspace']; actions: ['createDoc', 'createWorkspace'];
displayMenu: ['editDisplayMenu'];
viewMode: ['editDisplayMenu'];
collection: ['editCollection', 'addPinnedCollection'];
}; };
list: { list: {
doc: ['openDoc'];
docMenu: [ docMenu: [
'createDoc', 'createDoc',
'deleteDoc', 'deleteDoc',
'openInSplitView', 'openInSplitView',
'toggleFavorite', 'toggleFavorite',
'openInNewTab', 'openInNewTab',
'openDocInfo',
]; ];
}; };
}; };
@@ -505,6 +518,9 @@ interface PageEvents extends PageDivision {
docList: { docList: {
docMenu: ['removeOrganizeItem']; docMenu: ['removeOrganizeItem'];
}; };
collection: {
$: ['editCollection'];
};
}; };
tag: {}; tag: {};
trash: {}; trash: {};
@@ -766,6 +782,21 @@ export type EventArgs = {
control: 'Additional docs'; control: 'Additional docs';
result: 'success' | 'failure'; result: 'success' | 'failure';
}; };
editDisplayMenu: {
control:
| 'groupBy'
| 'orderBy'
| 'displayProperties'
| 'listViewOptions'
| 'quickActions';
type: string;
};
navigateAllDocsRouter: {
control: string;
};
navigatePinedCollectionRouter: {
control: 'all' | 'user-custom-collection';
};
}; };
// for type checking // for type checking