feat(editor): track citation events (#12664)
Closes: [BS-3551](https://linear.app/affine-design/issue/BS-3551/citation埋点) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Enhanced citation tracking across attachments, bookmarks, embedded documents, paragraphs, footnotes, rename modals, and toolbars for actions like editing, deleting, expanding, and hovering on citations. - Introduced a centralized citation service to unify citation detection and telemetry event management. - **Chores** - Updated service exports and telemetry modules to include the new citation service and citation-related event types. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -17,6 +17,7 @@ import {
|
|||||||
AttachmentBlockStyles,
|
AttachmentBlockStyles,
|
||||||
} from '@blocksuite/affine-model';
|
} from '@blocksuite/affine-model';
|
||||||
import {
|
import {
|
||||||
|
CitationProvider,
|
||||||
DocModeProvider,
|
DocModeProvider,
|
||||||
FileSizeLimitProvider,
|
FileSizeLimitProvider,
|
||||||
TelemetryProvider,
|
TelemetryProvider,
|
||||||
@@ -37,6 +38,7 @@ import { type ClassInfo, classMap } from 'lit/directives/class-map.js';
|
|||||||
import { guard } from 'lit/directives/guard.js';
|
import { guard } from 'lit/directives/guard.js';
|
||||||
import { styleMap } from 'lit/directives/style-map.js';
|
import { styleMap } from 'lit/directives/style-map.js';
|
||||||
import { when } from 'lit/directives/when.js';
|
import { when } from 'lit/directives/when.js';
|
||||||
|
import { filter } from 'rxjs/operators';
|
||||||
|
|
||||||
import { AttachmentEmbedProvider } from './embed';
|
import { AttachmentEmbedProvider } from './embed';
|
||||||
import { styles } from './styles';
|
import { styles } from './styles';
|
||||||
@@ -79,8 +81,12 @@ export class AttachmentBlockComponent extends CaptionedBlockComponent<Attachment
|
|||||||
return this.std.get(FileSizeLimitProvider).maxFileSize;
|
return this.std.get(FileSizeLimitProvider).maxFileSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get citationService() {
|
||||||
|
return this.std.get(CitationProvider);
|
||||||
|
}
|
||||||
|
|
||||||
get isCitation() {
|
get isCitation() {
|
||||||
return !!this.model.props.footnoteIdentifier;
|
return this.citationService.isCitationModel(this.model);
|
||||||
}
|
}
|
||||||
|
|
||||||
convertTo = () => {
|
convertTo = () => {
|
||||||
@@ -139,6 +145,34 @@ export class AttachmentBlockComponent extends CaptionedBlockComponent<Attachment
|
|||||||
selectionManager.setGroup('note', [blockSelection]);
|
selectionManager.setGroup('note', [blockSelection]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private readonly _trackCitationDeleteEvent = () => {
|
||||||
|
// Check citation delete event
|
||||||
|
this._disposables.add(
|
||||||
|
this.std.store.slots.blockUpdated
|
||||||
|
.pipe(
|
||||||
|
filter(payload => {
|
||||||
|
if (!payload.isLocal) return false;
|
||||||
|
|
||||||
|
const { flavour, id, type } = payload;
|
||||||
|
if (
|
||||||
|
type !== 'delete' ||
|
||||||
|
flavour !== this.model.flavour ||
|
||||||
|
id !== this.model.id
|
||||||
|
)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const { model } = payload;
|
||||||
|
if (!this.citationService.isCitationModel(model)) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.subscribe(() => {
|
||||||
|
this.citationService.trackEvent('Delete');
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
override connectedCallback() {
|
override connectedCallback() {
|
||||||
super.connectedCallback();
|
super.connectedCallback();
|
||||||
|
|
||||||
@@ -162,6 +196,8 @@ export class AttachmentBlockComponent extends CaptionedBlockComponent<Attachment
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._trackCitationDeleteEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
override firstUpdated() {
|
override firstUpdated() {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { ConfirmIcon } from '@blocksuite/affine-components/icons';
|
import { ConfirmIcon } from '@blocksuite/affine-components/icons';
|
||||||
import { toast } from '@blocksuite/affine-components/toast';
|
import { toast } from '@blocksuite/affine-components/toast';
|
||||||
import type { AttachmentBlockModel } from '@blocksuite/affine-model';
|
import type { AttachmentBlockModel } from '@blocksuite/affine-model';
|
||||||
|
import { CitationProvider } from '@blocksuite/affine-shared/services';
|
||||||
import type { EditorHost } from '@blocksuite/std';
|
import type { EditorHost } from '@blocksuite/std';
|
||||||
import { html } from 'lit';
|
import { html } from 'lit';
|
||||||
import { createRef, ref } from 'lit/directives/ref.js';
|
import { createRef, ref } from 'lit/directives/ref.js';
|
||||||
@@ -33,6 +34,7 @@ export const RenameModal = ({
|
|||||||
|
|
||||||
let fileName = includeExtension ? nameWithoutExtension : originalName;
|
let fileName = includeExtension ? nameWithoutExtension : originalName;
|
||||||
const extension = includeExtension ? originalExtension : '';
|
const extension = includeExtension ? originalExtension : '';
|
||||||
|
const citationService = editorHost.std.get(CitationProvider);
|
||||||
|
|
||||||
const abort = () => abortController.abort();
|
const abort = () => abortController.abort();
|
||||||
const onConfirm = () => {
|
const onConfirm = () => {
|
||||||
@@ -44,6 +46,9 @@ export const RenameModal = ({
|
|||||||
model.store.updateBlock(model, {
|
model.store.updateBlock(model, {
|
||||||
name: newFileName,
|
name: newFileName,
|
||||||
});
|
});
|
||||||
|
if (citationService.isCitationModel(model)) {
|
||||||
|
citationService.trackEvent('Edit');
|
||||||
|
}
|
||||||
abort();
|
abort();
|
||||||
};
|
};
|
||||||
const onInput = (e: InputEvent) => {
|
const onInput = (e: InputEvent) => {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import type {
|
|||||||
} from '@blocksuite/affine-model';
|
} from '@blocksuite/affine-model';
|
||||||
import { ImageProxyService } from '@blocksuite/affine-shared/adapters';
|
import { ImageProxyService } from '@blocksuite/affine-shared/adapters';
|
||||||
import {
|
import {
|
||||||
|
CitationProvider,
|
||||||
DocModeProvider,
|
DocModeProvider,
|
||||||
LinkPreviewServiceIdentifier,
|
LinkPreviewServiceIdentifier,
|
||||||
} from '@blocksuite/affine-shared/services';
|
} from '@blocksuite/affine-shared/services';
|
||||||
@@ -18,6 +19,7 @@ import { html } from 'lit';
|
|||||||
import { property, query } from 'lit/decorators.js';
|
import { property, query } from 'lit/decorators.js';
|
||||||
import { type ClassInfo, classMap } from 'lit/directives/class-map.js';
|
import { type ClassInfo, classMap } from 'lit/directives/class-map.js';
|
||||||
import { type StyleInfo, styleMap } from 'lit/directives/style-map.js';
|
import { type StyleInfo, styleMap } from 'lit/directives/style-map.js';
|
||||||
|
import { filter } from 'rxjs/operators';
|
||||||
|
|
||||||
import { refreshBookmarkUrlData } from './utils.js';
|
import { refreshBookmarkUrlData } from './utils.js';
|
||||||
|
|
||||||
@@ -114,11 +116,12 @@ export class BookmarkBlockComponent extends CaptionedBlockComponent<BookmarkBloc
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
get citationService() {
|
||||||
|
return this.std.get(CitationProvider);
|
||||||
|
}
|
||||||
|
|
||||||
get isCitation() {
|
get isCitation() {
|
||||||
return (
|
return this.citationService.isCitationModel(this.model);
|
||||||
!!this.model.props.footnoteIdentifier &&
|
|
||||||
this.model.props.style === 'citation'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get imageProxyService() {
|
get imageProxyService() {
|
||||||
@@ -166,6 +169,31 @@ export class BookmarkBlockComponent extends CaptionedBlockComponent<BookmarkBloc
|
|||||||
></bookmark-card>`;
|
></bookmark-card>`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private readonly _trackCitationDeleteEvent = () => {
|
||||||
|
// Check citation delete event
|
||||||
|
this._disposables.add(
|
||||||
|
this.std.store.slots.blockUpdated
|
||||||
|
.pipe(
|
||||||
|
filter(payload => {
|
||||||
|
if (!payload.isLocal) return false;
|
||||||
|
const { flavour, id, type } = payload;
|
||||||
|
if (
|
||||||
|
type !== 'delete' ||
|
||||||
|
flavour !== this.model.flavour ||
|
||||||
|
id !== this.model.id
|
||||||
|
)
|
||||||
|
return false;
|
||||||
|
const { model } = payload;
|
||||||
|
if (!this.citationService.isCitationModel(model)) return false;
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.subscribe(() => {
|
||||||
|
this.citationService.trackEvent('Delete');
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
override connectedCallback() {
|
override connectedCallback() {
|
||||||
super.connectedCallback();
|
super.connectedCallback();
|
||||||
|
|
||||||
@@ -203,6 +231,8 @@ export class BookmarkBlockComponent extends CaptionedBlockComponent<BookmarkBloc
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this._trackCitationDeleteEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
override disconnectedCallback(): void {
|
override disconnectedCallback(): void {
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import {
|
|||||||
REFERENCE_NODE,
|
REFERENCE_NODE,
|
||||||
} from '@blocksuite/affine-shared/consts';
|
} from '@blocksuite/affine-shared/consts';
|
||||||
import {
|
import {
|
||||||
|
CitationProvider,
|
||||||
DocDisplayMetaProvider,
|
DocDisplayMetaProvider,
|
||||||
DocModeProvider,
|
DocModeProvider,
|
||||||
OpenDocExtensionIdentifier,
|
OpenDocExtensionIdentifier,
|
||||||
@@ -43,6 +44,7 @@ import { repeat } from 'lit/directives/repeat.js';
|
|||||||
import { styleMap } from 'lit/directives/style-map.js';
|
import { styleMap } from 'lit/directives/style-map.js';
|
||||||
import { when } from 'lit/directives/when.js';
|
import { when } from 'lit/directives/when.js';
|
||||||
import throttle from 'lodash-es/throttle';
|
import throttle from 'lodash-es/throttle';
|
||||||
|
import { filter } from 'rxjs/operators';
|
||||||
import * as Y from 'yjs';
|
import * as Y from 'yjs';
|
||||||
|
|
||||||
import { renderLinkedDocInCard } from '../common/render-linked-doc';
|
import { renderLinkedDocInCard } from '../common/render-linked-doc';
|
||||||
@@ -254,11 +256,12 @@ export class EmbedLinkedDocBlockComponent extends EmbedBlockComponent<EmbedLinke
|
|||||||
return this.store.readonly;
|
return this.store.readonly;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get citationService() {
|
||||||
|
return this.std.get(CitationProvider);
|
||||||
|
}
|
||||||
|
|
||||||
get isCitation() {
|
get isCitation() {
|
||||||
return (
|
return this.citationService.isCitationModel(this.model);
|
||||||
!!this.model.props.footnoteIdentifier &&
|
|
||||||
this.model.props.style === 'citation'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly _handleDoubleClick = (event: MouseEvent) => {
|
private readonly _handleDoubleClick = (event: MouseEvent) => {
|
||||||
@@ -454,6 +457,31 @@ export class EmbedLinkedDocBlockComponent extends EmbedBlockComponent<EmbedLinke
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private readonly _trackCitationDeleteEvent = () => {
|
||||||
|
// Check citation delete event
|
||||||
|
this._disposables.add(
|
||||||
|
this.std.store.slots.blockUpdated
|
||||||
|
.pipe(
|
||||||
|
filter(payload => {
|
||||||
|
if (!payload.isLocal) return false;
|
||||||
|
const { flavour, id, type } = payload;
|
||||||
|
if (
|
||||||
|
type !== 'delete' ||
|
||||||
|
flavour !== this.model.flavour ||
|
||||||
|
id !== this.model.id
|
||||||
|
)
|
||||||
|
return false;
|
||||||
|
const { model } = payload;
|
||||||
|
if (!this.citationService.isCitationModel(model)) return false;
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.subscribe(() => {
|
||||||
|
this.citationService.trackEvent('Delete');
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
override connectedCallback() {
|
override connectedCallback() {
|
||||||
super.connectedCallback();
|
super.connectedCallback();
|
||||||
|
|
||||||
@@ -532,6 +560,8 @@ export class EmbedLinkedDocBlockComponent extends EmbedBlockComponent<EmbedLinke
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this._trackCitationDeleteEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
getInitialState(): {
|
getInitialState(): {
|
||||||
|
|||||||
@@ -7,7 +7,10 @@ import {
|
|||||||
BLOCK_CHILDREN_CONTAINER_PADDING_LEFT,
|
BLOCK_CHILDREN_CONTAINER_PADDING_LEFT,
|
||||||
EDGELESS_TOP_CONTENTEDITABLE_SELECTOR,
|
EDGELESS_TOP_CONTENTEDITABLE_SELECTOR,
|
||||||
} from '@blocksuite/affine-shared/consts';
|
} from '@blocksuite/affine-shared/consts';
|
||||||
import { DocModeProvider } from '@blocksuite/affine-shared/services';
|
import {
|
||||||
|
CitationProvider,
|
||||||
|
DocModeProvider,
|
||||||
|
} from '@blocksuite/affine-shared/services';
|
||||||
import {
|
import {
|
||||||
calculateCollapsedSiblings,
|
calculateCollapsedSiblings,
|
||||||
getNearestHeadingBefore,
|
getNearestHeadingBefore,
|
||||||
@@ -63,6 +66,10 @@ export class ParagraphBlockComponent extends CaptionedBlockComponent<ParagraphBl
|
|||||||
?.getPlaceholder(this.model);
|
?.getPlaceholder(this.model);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get citationService() {
|
||||||
|
return this.std.get(CitationProvider);
|
||||||
|
}
|
||||||
|
|
||||||
get attributeRenderer() {
|
get attributeRenderer() {
|
||||||
return this.inlineManager.getRenderer();
|
return this.inlineManager.getRenderer();
|
||||||
}
|
}
|
||||||
@@ -94,6 +101,12 @@ export class ParagraphBlockComponent extends CaptionedBlockComponent<ParagraphBl
|
|||||||
return this.std.get(DefaultInlineManagerExtension.identifier);
|
return this.std.get(DefaultInlineManagerExtension.identifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get hasCitationSiblings() {
|
||||||
|
return this.collapsedSiblings.some(sibling =>
|
||||||
|
this.citationService.isCitationModel(sibling)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
override get topContenteditableElement() {
|
override get topContenteditableElement() {
|
||||||
if (this.std.get(DocModeProvider).getEditorMode() === 'edgeless') {
|
if (this.std.get(DocModeProvider).getEditorMode() === 'edgeless') {
|
||||||
return this.closest<BlockComponent>(
|
return this.closest<BlockComponent>(
|
||||||
@@ -286,6 +299,13 @@ export class ParagraphBlockComponent extends CaptionedBlockComponent<ParagraphBl
|
|||||||
collapsed: value,
|
collapsed: value,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.hasCitationSiblings) {
|
||||||
|
this.citationService.trackEvent('Expand', {
|
||||||
|
control: 'Source Button',
|
||||||
|
type: value ? 'Hide' : 'Show',
|
||||||
|
});
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
></blocksuite-toggle-button>
|
></blocksuite-toggle-button>
|
||||||
`
|
`
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
} from '@blocksuite/affine-ext-loader';
|
} from '@blocksuite/affine-ext-loader';
|
||||||
import {
|
import {
|
||||||
AutoClearSelectionService,
|
AutoClearSelectionService,
|
||||||
|
CitationService,
|
||||||
DefaultOpenDocExtension,
|
DefaultOpenDocExtension,
|
||||||
DNDAPIExtension,
|
DNDAPIExtension,
|
||||||
DocDisplayMetaService,
|
DocDisplayMetaService,
|
||||||
@@ -76,6 +77,7 @@ export class FoundationViewExtension extends ViewExtensionProvider<FoundationVie
|
|||||||
FileSizeLimitService,
|
FileSizeLimitService,
|
||||||
LinkPreviewCache,
|
LinkPreviewCache,
|
||||||
LinkPreviewService,
|
LinkPreviewService,
|
||||||
|
CitationService,
|
||||||
]);
|
]);
|
||||||
context.register(clipboardConfigs);
|
context.register(clipboardConfigs);
|
||||||
if (this.isEdgeless(context.scope)) {
|
if (this.isEdgeless(context.scope)) {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { HoverController } from '@blocksuite/affine-components/hover';
|
import { HoverController } from '@blocksuite/affine-components/hover';
|
||||||
import { PeekViewProvider } from '@blocksuite/affine-components/peek';
|
import { PeekViewProvider } from '@blocksuite/affine-components/peek';
|
||||||
import type { FootNote } from '@blocksuite/affine-model';
|
import type { FootNote } from '@blocksuite/affine-model';
|
||||||
|
import { CitationProvider } from '@blocksuite/affine-shared/services';
|
||||||
import { unsafeCSSVarV2 } from '@blocksuite/affine-shared/theme';
|
import { unsafeCSSVarV2 } from '@blocksuite/affine-shared/theme';
|
||||||
import type { AffineTextAttributes } from '@blocksuite/affine-shared/types';
|
import type { AffineTextAttributes } from '@blocksuite/affine-shared/types';
|
||||||
import { WithDisposable } from '@blocksuite/global/lit';
|
import { WithDisposable } from '@blocksuite/global/lit';
|
||||||
@@ -117,6 +118,10 @@ export class AffineFootnoteNode extends WithDisposable(ShadowlessElement) {
|
|||||||
return this.std.store.readonly;
|
return this.std.store.readonly;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get citationService() {
|
||||||
|
return this.std.get(CitationProvider);
|
||||||
|
}
|
||||||
|
|
||||||
onFootnoteClick = () => {
|
onFootnoteClick = () => {
|
||||||
if (!this.footnote) {
|
if (!this.footnote) {
|
||||||
return;
|
return;
|
||||||
@@ -215,6 +220,10 @@ export class AffineFootnoteNode extends WithDisposable(ShadowlessElement) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.citationService.trackEvent('Hover', {
|
||||||
|
control: 'Source Footnote',
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
template: this._FootNotePopup(footnote, abortController),
|
template: this._FootNotePopup(footnote, abortController),
|
||||||
container: this.std.host,
|
container: this.std.host,
|
||||||
|
|||||||
@@ -0,0 +1,84 @@
|
|||||||
|
import { type Container, createIdentifier } from '@blocksuite/global/di';
|
||||||
|
import { type BlockStdScope, StdIdentifier } from '@blocksuite/std';
|
||||||
|
import { type BlockModel, Extension } from '@blocksuite/store';
|
||||||
|
|
||||||
|
import { DocModeProvider } from '../doc-mode-service';
|
||||||
|
import type {
|
||||||
|
CitationEvents,
|
||||||
|
CitationEventType,
|
||||||
|
} from '../telemetry-service/citation';
|
||||||
|
import { TelemetryProvider } from '../telemetry-service/telemetry-service';
|
||||||
|
|
||||||
|
const CitationEventTypeMap = {
|
||||||
|
Hover: 'AICitationHoverSource',
|
||||||
|
Expand: 'AICitationExpandSource',
|
||||||
|
Delete: 'AICitationDelete',
|
||||||
|
Edit: 'AICitationEdit',
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
type EventType = keyof typeof CitationEventTypeMap;
|
||||||
|
|
||||||
|
type EventTypeMapping = {
|
||||||
|
[K in EventType]: CitationEventType;
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface CitationViewService {
|
||||||
|
/**
|
||||||
|
* Tracks citation-related events
|
||||||
|
* @param type - The type of citation event to track
|
||||||
|
* @param properties - The properties of the event
|
||||||
|
*/
|
||||||
|
trackEvent<T extends EventType>(
|
||||||
|
type: T,
|
||||||
|
properties?: CitationEvents[EventTypeMapping[T]]
|
||||||
|
): void;
|
||||||
|
/**
|
||||||
|
* Checks if the model is a citation model
|
||||||
|
* @param model - The model to check
|
||||||
|
* @returns True if the model is a citation model, false otherwise
|
||||||
|
*/
|
||||||
|
isCitationModel(model: BlockModel): boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CitationProvider =
|
||||||
|
createIdentifier<CitationViewService>('CitationService');
|
||||||
|
|
||||||
|
export class CitationService extends Extension implements CitationViewService {
|
||||||
|
constructor(private readonly std: BlockStdScope) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
static override setup(di: Container) {
|
||||||
|
di.addImpl(CitationProvider, CitationService, [StdIdentifier]);
|
||||||
|
}
|
||||||
|
|
||||||
|
get docModeService() {
|
||||||
|
return this.std.getOptional(DocModeProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
get telemetryService() {
|
||||||
|
return this.std.getOptional(TelemetryProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
isCitationModel = (model: BlockModel) => {
|
||||||
|
return (
|
||||||
|
'footnoteIdentifier' in model.props &&
|
||||||
|
!!model.props.footnoteIdentifier &&
|
||||||
|
'style' in model.props &&
|
||||||
|
model.props.style === 'citation'
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
trackEvent<T extends EventType>(
|
||||||
|
type: T,
|
||||||
|
properties?: CitationEvents[EventTypeMapping[T]]
|
||||||
|
) {
|
||||||
|
const editorMode = this.docModeService?.getEditorMode() ?? 'page';
|
||||||
|
this.telemetryService?.track(CitationEventTypeMap[type], {
|
||||||
|
page: editorMode === 'page' ? 'doc editor' : 'whiteboard editor',
|
||||||
|
module: 'AI Result',
|
||||||
|
control: 'Source',
|
||||||
|
...properties,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from './citation-service';
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
export * from './auto-clear-selection-service';
|
export * from './auto-clear-selection-service';
|
||||||
export * from './block-meta-service';
|
export * from './block-meta-service';
|
||||||
|
export * from './citation-service';
|
||||||
export * from './doc-display-meta-service';
|
export * from './doc-display-meta-service';
|
||||||
export * from './doc-mode-service';
|
export * from './doc-mode-service';
|
||||||
export * from './drag-handle-config';
|
export * from './drag-handle-config';
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import type { TelemetryEvent } from './types';
|
||||||
|
export type CitationEventType =
|
||||||
|
| 'AICitationHoverSource'
|
||||||
|
| 'AICitationExpandSource'
|
||||||
|
| 'AICitationDelete'
|
||||||
|
| 'AICitationEdit';
|
||||||
|
|
||||||
|
export type CitationEvents = Record<CitationEventType, TelemetryEvent>;
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
export * from './citation.js';
|
||||||
export * from './database.js';
|
export * from './database.js';
|
||||||
export * from './link.js';
|
export * from './link.js';
|
||||||
export * from './telemetry-service.js';
|
export * from './telemetry-service.js';
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { createIdentifier } from '@blocksuite/global/di';
|
import { createIdentifier } from '@blocksuite/global/di';
|
||||||
import type { ExtensionType } from '@blocksuite/store';
|
import type { ExtensionType } from '@blocksuite/store';
|
||||||
|
|
||||||
|
import type { CitationEvents } from './citation.js';
|
||||||
import type { CodeBlockEvents } from './code-block.js';
|
import type { CodeBlockEvents } from './code-block.js';
|
||||||
import type { OutDatabaseAllEvents } from './database.js';
|
import type { OutDatabaseAllEvents } from './database.js';
|
||||||
import type { LinkToolbarEvents } from './link.js';
|
import type { LinkToolbarEvents } from './link.js';
|
||||||
@@ -28,7 +29,8 @@ export type TelemetryEventMap = OutDatabaseAllEvents &
|
|||||||
LinkToolbarEvents &
|
LinkToolbarEvents &
|
||||||
SlashMenuEvents &
|
SlashMenuEvents &
|
||||||
CodeBlockEvents &
|
CodeBlockEvents &
|
||||||
NoteEvents & {
|
NoteEvents &
|
||||||
|
CitationEvents & {
|
||||||
DocCreated: DocCreatedEvent;
|
DocCreated: DocCreatedEvent;
|
||||||
Link: TelemetryEvent;
|
Link: TelemetryEvent;
|
||||||
LinkedDocCreated: LinkedDocCreatedEvent;
|
LinkedDocCreated: LinkedDocCreatedEvent;
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ import { getSelectedModelsCommand } from '@blocksuite/affine/shared/commands';
|
|||||||
import { ImageSelection } from '@blocksuite/affine/shared/selection';
|
import { ImageSelection } from '@blocksuite/affine/shared/selection';
|
||||||
import {
|
import {
|
||||||
ActionPlacement,
|
ActionPlacement,
|
||||||
|
CitationProvider,
|
||||||
GenerateDocUrlProvider,
|
GenerateDocUrlProvider,
|
||||||
isRemovedUserInfo,
|
isRemovedUserInfo,
|
||||||
OpenDocExtensionIdentifier,
|
OpenDocExtensionIdentifier,
|
||||||
@@ -462,6 +463,10 @@ function createExternalLinkableToolbarConfig(
|
|||||||
(_std, _component, props) => {
|
(_std, _component, props) => {
|
||||||
ctx.store.updateBlock(model, props);
|
ctx.store.updateBlock(model, props);
|
||||||
block.requestUpdate();
|
block.requestUpdate();
|
||||||
|
const citationService = ctx.std.get(CitationProvider);
|
||||||
|
if (citationService.isCitationModel(model)) {
|
||||||
|
citationService.trackEvent('Edit');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
abortController
|
abortController
|
||||||
);
|
);
|
||||||
@@ -805,6 +810,10 @@ const embedLinkedDocToolbarConfig = {
|
|||||||
(_std, _component, props) => {
|
(_std, _component, props) => {
|
||||||
ctx.store.updateBlock(model, props);
|
ctx.store.updateBlock(model, props);
|
||||||
block.requestUpdate();
|
block.requestUpdate();
|
||||||
|
const citationService = ctx.std.get(CitationProvider);
|
||||||
|
if (citationService.isCitationModel(model)) {
|
||||||
|
citationService.trackEvent('Edit');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
abortController
|
abortController
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user