diff --git a/packages/frontend/core/src/components/attachment-viewer/pdf-viewer.tsx b/packages/frontend/core/src/components/attachment-viewer/pdf-viewer.tsx index 4de7630160621..ceeffbf75f3c5 100644 --- a/packages/frontend/core/src/components/attachment-viewer/pdf-viewer.tsx +++ b/packages/frontend/core/src/components/attachment-viewer/pdf-viewer.tsx @@ -17,6 +17,7 @@ import { Scroller, ScrollSeekPlaceholder, } from '@affine/core/modules/pdf/views'; +import track from '@affine/track'; import type { AttachmentBlockModel } from '@blocksuite/affine/blocks'; import { CollapseIcon, ExpandIcon } from '@blocksuite/icons/rc'; import { useLiveData, useService } from '@toeverything/infra'; @@ -202,6 +203,12 @@ const PDFViewerInner = ({ pdf, state }: PDFViewerInnerProps) => { function PDFViewerStatus({ pdf }: { pdf: PDF }) { const state = useLiveData(pdf.state$); + useEffect(() => { + if (state.status !== PDFStatus.Error) return; + + track.$.attachment.$.openPDFRendererFail(); + }, [state]); + if (state?.status !== PDFStatus.Opened) { return ; } diff --git a/packages/frontend/core/src/modules/peek-view/entities/peek-view.ts b/packages/frontend/core/src/modules/peek-view/entities/peek-view.ts index 4bbab3a550b5d..c8ac94f6a0c72 100644 --- a/packages/frontend/core/src/modules/peek-view/entities/peek-view.ts +++ b/packages/frontend/core/src/modules/peek-view/entities/peek-view.ts @@ -55,7 +55,7 @@ export type ImagePeekViewInfo = { export type AttachmentPeekViewInfo = { type: 'attachment'; - docRef: DocReferenceInfo; + docRef: DocReferenceInfo & { filetype?: string }; }; export type AIChatBlockPeekViewInfo = { @@ -173,6 +173,7 @@ function resolvePeekInfoFromPeekTarget( docRef: { docId: blockModel.doc.id, blockIds: [blockModel.id], + filetype: blockModel.type, }, }; } else if (isImageBlockModel(blockModel)) { diff --git a/packages/frontend/core/src/modules/peek-view/view/peek-view-controls.tsx b/packages/frontend/core/src/modules/peek-view/view/peek-view-controls.tsx index 1d8e141e9e6c3..c79bc258e38ad 100644 --- a/packages/frontend/core/src/modules/peek-view/view/peek-view-controls.tsx +++ b/packages/frontend/core/src/modules/peek-view/view/peek-view-controls.tsx @@ -1,5 +1,6 @@ import { IconButton } from '@affine/component'; import { useI18n } from '@affine/i18n'; +import track from '@affine/track'; import type { DocMode } from '@blocksuite/affine/blocks'; import { CloseIcon, @@ -16,12 +17,16 @@ import { type ReactElement, type SVGAttributes, useCallback, + useEffect, useMemo, } from 'react'; import { WorkspaceDialogService } from '../../dialogs'; import { WorkbenchService } from '../../workbench'; -import type { DocReferenceInfo } from '../entities/peek-view'; +import type { + AttachmentPeekViewInfo, + DocReferenceInfo, +} from '../entities/peek-view'; import { PeekViewService } from '../services/peek-view'; import * as styles from './peek-view-controls.css'; @@ -154,33 +159,44 @@ export const DocPeekViewControls = ({ ); }; +type AttachmentPeekViewControls = HTMLAttributes & { + mode?: DocMode; + docRef: AttachmentPeekViewInfo['docRef']; +}; + export const AttachmentPeekViewControls = ({ docRef, className, ...rest -}: DocPeekViewControlsProps) => { +}: AttachmentPeekViewControls) => { + const { docId, blockIds: [blockId] = [], filetype: type } = docRef; const peekView = useService(PeekViewService).peekView; const workbench = useService(WorkbenchService).workbench; const t = useI18n(); + const controls = useMemo(() => { - return [ + const controls = [ { icon: , nameKey: 'close', name: t['com.affine.peek-view-controls.close'](), onClick: () => peekView.close(), }, + ]; + if (!type) return controls; + + return [ + ...controls, // TODO(@fundon): needs to be implemented on mobile BUILD_CONFIG.isDesktopEdition && { icon: , name: t['com.affine.peek-view-controls.open-attachment'](), nameKey: 'open', onClick: () => { - const { docId, blockIds: [blockId] = [] } = docRef; - if (docId && blockId) { - workbench.openAttachment(docId, blockId); - } + workbench.openAttachment(docId, blockId); peekView.close(false); + + track.$.attachment.$.openAttachmentInFullscreen({ type }); }, }, { @@ -188,11 +204,10 @@ export const AttachmentPeekViewControls = ({ nameKey: 'new-tab', name: t['com.affine.peek-view-controls.open-attachment-in-new-tab'](), onClick: () => { - const { docId, blockIds: [blockId] = [] } = docRef; - if (docId && blockId) { - workbench.openAttachment(docId, blockId, { at: 'new-tab' }); - } + workbench.openAttachment(docId, blockId, { at: 'new-tab' }); peekView.close(false); + + track.$.attachment.$.openAttachmentInNewTab({ type }); }, }, BUILD_CONFIG.isElectron && { @@ -202,15 +217,21 @@ export const AttachmentPeekViewControls = ({ 'com.affine.peek-view-controls.open-attachment-in-split-view' ](), onClick: () => { - const { docId, blockIds: [blockId] = [] } = docRef; - if (docId && blockId) { - workbench.openAttachment(docId, blockId, { at: 'beside' }); - } + workbench.openAttachment(docId, blockId, { at: 'beside' }); peekView.close(false); + + track.$.attachment.$.openAttachmentInSplitView({ type }); }, }, ].filter((opt): opt is ControlButtonProps => Boolean(opt)); - }, [t, peekView, workbench, docRef]); + }, [t, peekView, workbench, docId, blockId, type]); + + useEffect(() => { + if (type === undefined) return; + + track.$.attachment.$.openAttachmentInPeekView({ type }); + }, [type]); + return (
{controls.map(option => ( diff --git a/packages/frontend/track/src/events.ts b/packages/frontend/track/src/events.ts index 638d540067a12..8d2554622aaab 100644 --- a/packages/frontend/track/src/events.ts +++ b/packages/frontend/track/src/events.ts @@ -116,6 +116,15 @@ type PaymentEvents = | 'confirmResumingSubscription'; // END SECTION +// SECTION: attachment +type AttachmentEvents = + | 'openAttachmentInFullscreen' + | 'openAttachmentInNewTab' + | 'openAttachmentInPeekView' + | 'openAttachmentInSplitView' + | 'openPDFRendererFail'; +// END SECTION + type UserEvents = | GeneralEvents | AppEvents @@ -130,7 +139,8 @@ type UserEvents = | AuthEvents | AccountEvents | PaymentEvents - | DNDEvents; + | DNDEvents + | AttachmentEvents; interface PageDivision { [page: string]: { [segment: string]: { @@ -284,6 +294,15 @@ const PageEvents = { importModal: ['open'], snapshot: ['import', 'export'], }, + attachment: { + $: [ + 'openAttachmentInFullscreen', + 'openAttachmentInNewTab', + 'openAttachmentInPeekView', + 'openAttachmentInSplitView', + 'openPDFRendererFail', + ], + }, }, doc: { editor: { @@ -353,6 +372,10 @@ type PaymentEventArgs = { recurring: string; }; +type AttachmentEventArgs = { + type: string; // file type +}; + type TabActionControlType = | 'click' | 'dnd' @@ -435,6 +458,10 @@ export type EventArgs = { linkDoc: { type: string; journal: boolean }; drop: { type: string }; dragStart: { type: string }; + openAttachmentInFullscreen: AttachmentEventArgs; + openAttachmentInNewTab: AttachmentEventArgs; + openAttachmentInPeekView: AttachmentEventArgs; + openAttachmentInSplitView: AttachmentEventArgs; }; // for type checking