mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-08-19 02:51:47 +08:00
feat(core): add globalcontext info to mixpanel track (#7681)
This commit is contained in:
@@ -8,19 +8,36 @@ export class GlobalContext extends Entity {
|
|||||||
|
|
||||||
workspaceId = this.define<string>('workspaceId');
|
workspaceId = this.define<string>('workspaceId');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* is in doc page
|
||||||
|
*/
|
||||||
isDoc = this.define<boolean>('isDoc');
|
isDoc = this.define<boolean>('isDoc');
|
||||||
|
isTrashDoc = this.define<boolean>('isTrashDoc');
|
||||||
docId = this.define<string>('docId');
|
docId = this.define<string>('docId');
|
||||||
|
docMode = this.define<DocMode>('docMode');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* is in collection page
|
||||||
|
*/
|
||||||
isCollection = this.define<boolean>('isCollection');
|
isCollection = this.define<boolean>('isCollection');
|
||||||
collectionId = this.define<string>('collectionId');
|
collectionId = this.define<string>('collectionId');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* is in trash page
|
||||||
|
*/
|
||||||
isTrash = this.define<boolean>('isTrash');
|
isTrash = this.define<boolean>('isTrash');
|
||||||
|
|
||||||
docMode = this.define<DocMode>('docMode');
|
/**
|
||||||
|
* is in tag page
|
||||||
|
*/
|
||||||
isTag = this.define<boolean>('isTag');
|
isTag = this.define<boolean>('isTag');
|
||||||
tagId = this.define<string>('tagId');
|
tagId = this.define<string>('tagId');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* is in all docs page
|
||||||
|
*/
|
||||||
|
isAllDocs = this.define<boolean>('isAllDocs');
|
||||||
|
|
||||||
define<T>(key: string) {
|
define<T>(key: string) {
|
||||||
this.memento.set(key, null);
|
this.memento.set(key, null);
|
||||||
const livedata$ = LiveData.from(this.memento.watch<T>(key), null);
|
const livedata$ = LiveData.from(this.memento.watch<T>(key), null);
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
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 { mixpanel } from '@affine/core/mixpanel';
|
||||||
import { TelemetryWorkspaceContextService } from '@affine/core/modules/telemetry/services/telemetry';
|
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import { ImportIcon } from '@blocksuite/icons/rc';
|
import { ImportIcon } from '@blocksuite/icons/rc';
|
||||||
import { useService } from '@toeverything/infra';
|
|
||||||
|
|
||||||
import type { DocCollection } from '../../shared';
|
import type { DocCollection } from '../../shared';
|
||||||
import { MenuItem } from '../app-sidebar';
|
import { MenuItem } from '../app-sidebar';
|
||||||
@@ -12,14 +10,11 @@ import { usePageHelper } from '../blocksuite/block-suite-page-list/utils';
|
|||||||
const ImportPage = ({ docCollection }: { docCollection: DocCollection }) => {
|
const ImportPage = ({ docCollection }: { docCollection: DocCollection }) => {
|
||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
const { importFile } = usePageHelper(docCollection);
|
const { importFile } = usePageHelper(docCollection);
|
||||||
const telemetry = useService(TelemetryWorkspaceContextService);
|
|
||||||
|
|
||||||
const onImportFile = useAsyncCallback(async () => {
|
const onImportFile = useAsyncCallback(async () => {
|
||||||
const options = await importFile();
|
const options = await importFile();
|
||||||
const page = telemetry.getPageContext();
|
|
||||||
if (options.isWorkspaceFile) {
|
if (options.isWorkspaceFile) {
|
||||||
mixpanel.track('WorkspaceCreated', {
|
mixpanel.track('WorkspaceCreated', {
|
||||||
page,
|
|
||||||
segment: 'navigation panel',
|
segment: 'navigation panel',
|
||||||
module: 'doc list header',
|
module: 'doc list header',
|
||||||
control: 'import button',
|
control: 'import button',
|
||||||
@@ -27,7 +22,6 @@ const ImportPage = ({ docCollection }: { docCollection: DocCollection }) => {
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
mixpanel.track('DocCreated', {
|
mixpanel.track('DocCreated', {
|
||||||
page,
|
|
||||||
segment: 'navigation panel',
|
segment: 'navigation panel',
|
||||||
module: 'doc list header',
|
module: 'doc list header',
|
||||||
control: 'import button',
|
control: 'import button',
|
||||||
@@ -35,7 +29,7 @@ const ImportPage = ({ docCollection }: { docCollection: DocCollection }) => {
|
|||||||
// category
|
// category
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [importFile, telemetry]);
|
}, [importFile]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MenuItem icon={<ImportIcon />} onClick={onImportFile}>
|
<MenuItem icon={<ImportIcon />} onClick={onImportFile}>
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import {
|
|||||||
} from '@affine/core/modules/explorer';
|
} from '@affine/core/modules/explorer';
|
||||||
import { ExplorerTags } from '@affine/core/modules/explorer/views/sections/tags';
|
import { ExplorerTags } from '@affine/core/modules/explorer/views/sections/tags';
|
||||||
import { CMDKQuickSearchService } from '@affine/core/modules/quicksearch/services/cmdk';
|
import { CMDKQuickSearchService } from '@affine/core/modules/quicksearch/services/cmdk';
|
||||||
import { TelemetryWorkspaceContextService } from '@affine/core/modules/telemetry/services/telemetry';
|
|
||||||
import { pathGenerator } from '@affine/core/shared';
|
import { pathGenerator } from '@affine/core/shared';
|
||||||
import { apis, events } from '@affine/electron-api';
|
import { apis, events } from '@affine/electron-api';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
@@ -91,8 +90,6 @@ export const RootAppSidebar = (): ReactElement => {
|
|||||||
});
|
});
|
||||||
}, [cmdkQuickSearchService]);
|
}, [cmdkQuickSearchService]);
|
||||||
|
|
||||||
const telemetry = useService(TelemetryWorkspaceContextService);
|
|
||||||
|
|
||||||
const allPageActive = currentPath === '/all';
|
const allPageActive = currentPath === '/all';
|
||||||
|
|
||||||
const pageHelper = usePageHelper(currentWorkspace.docCollection);
|
const pageHelper = usePageHelper(currentWorkspace.docCollection);
|
||||||
@@ -105,14 +102,13 @@ export const RootAppSidebar = (): ReactElement => {
|
|||||||
page.load();
|
page.load();
|
||||||
openPage(currentWorkspaceId, page.id);
|
openPage(currentWorkspaceId, page.id);
|
||||||
mixpanel.track('DocCreated', {
|
mixpanel.track('DocCreated', {
|
||||||
page: telemetry.getPageContext(),
|
|
||||||
segment: 'navigation panel',
|
segment: 'navigation panel',
|
||||||
module: 'bottom button',
|
module: 'bottom button',
|
||||||
control: 'new doc button',
|
control: 'new doc button',
|
||||||
category: 'page',
|
category: 'page',
|
||||||
type: 'doc',
|
type: 'doc',
|
||||||
});
|
});
|
||||||
}, [createPage, currentWorkspaceId, openPage, telemetry]);
|
}, [createPage, currentWorkspaceId, openPage]);
|
||||||
|
|
||||||
// Listen to the "New Page" action from the menu
|
// Listen to the "New Page" action from the menu
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import {
|
|||||||
} from '@affine/core/commands';
|
} from '@affine/core/commands';
|
||||||
import { mixpanel } from '@affine/core/mixpanel';
|
import { mixpanel } from '@affine/core/mixpanel';
|
||||||
import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/properties';
|
import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/properties';
|
||||||
import { TelemetryWorkspaceContextService } from '@affine/core/modules/telemetry/services/telemetry';
|
|
||||||
import { WorkspaceFlavour } from '@affine/env/workspace';
|
import { WorkspaceFlavour } from '@affine/env/workspace';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import { EdgelessIcon, HistoryIcon, PageIcon } from '@blocksuite/icons/rc';
|
import { EdgelessIcon, HistoryIcon, PageIcon } from '@blocksuite/icons/rc';
|
||||||
@@ -64,8 +63,6 @@ export function useRegisterBlocksuiteEditorCommands() {
|
|||||||
[docId, setTrashModal]
|
[docId, setTrashModal]
|
||||||
);
|
);
|
||||||
|
|
||||||
const telemetry = useService(TelemetryWorkspaceContextService);
|
|
||||||
|
|
||||||
const isCloudWorkspace = workspace.flavour === WorkspaceFlavour.AFFINE_CLOUD;
|
const isCloudWorkspace = workspace.flavour === WorkspaceFlavour.AFFINE_CLOUD;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -168,7 +165,6 @@ export function useRegisterBlocksuiteEditorCommands() {
|
|||||||
control: 'cmdk',
|
control: 'cmdk',
|
||||||
type: 'doc duplicate',
|
type: 'doc duplicate',
|
||||||
category: 'doc',
|
category: 'doc',
|
||||||
page: telemetry.getPageContext(),
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@@ -299,7 +295,6 @@ export function useRegisterBlocksuiteEditorCommands() {
|
|||||||
favAdapter,
|
favAdapter,
|
||||||
docId,
|
docId,
|
||||||
doc,
|
doc,
|
||||||
telemetry,
|
|
||||||
openInfoModal,
|
openInfoModal,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,9 @@ function defaultNavigate(to: To, option?: { replace?: boolean }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO(@eyhn): add a name -> path helper in the results
|
// TODO(@eyhn): add a name -> path helper in the results
|
||||||
|
/**
|
||||||
|
* @deprecated use `WorkbenchService` instead
|
||||||
|
*/
|
||||||
export function useNavigateHelper() {
|
export function useNavigateHelper() {
|
||||||
const navigate = useContext(NavigateContext) ?? defaultNavigate;
|
const navigate = useContext(NavigateContext) ?? defaultNavigate;
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,11 @@ import type { GeneralMixpanelEvent, MixpanelEvents } from './events';
|
|||||||
|
|
||||||
const logger = new DebugLogger('mixpanel');
|
const logger = new DebugLogger('mixpanel');
|
||||||
|
|
||||||
|
type Middleware = (
|
||||||
|
name: string,
|
||||||
|
properties?: Record<string, unknown>
|
||||||
|
) => Record<string, unknown>;
|
||||||
|
|
||||||
function createMixpanel() {
|
function createMixpanel() {
|
||||||
let mixpanel;
|
let mixpanel;
|
||||||
if (process.env.MIXPANEL_TOKEN) {
|
if (process.env.MIXPANEL_TOKEN) {
|
||||||
@@ -22,6 +27,8 @@ function createMixpanel() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const middlewares = new Set<Middleware>();
|
||||||
|
|
||||||
const wrapped = {
|
const wrapped = {
|
||||||
reset() {
|
reset() {
|
||||||
mixpanel.reset();
|
mixpanel.reset();
|
||||||
@@ -40,8 +47,21 @@ function createMixpanel() {
|
|||||||
: Record<string, unknown>) &
|
: Record<string, unknown>) &
|
||||||
GeneralMixpanelEvent,
|
GeneralMixpanelEvent,
|
||||||
>(event_name: T, properties?: P) {
|
>(event_name: T, properties?: P) {
|
||||||
logger.debug('track', event_name, properties);
|
const middlewareProperties = Array.from(middlewares).reduce(
|
||||||
mixpanel.track(event_name, properties);
|
(acc, middleware) => {
|
||||||
|
return middleware(event_name, acc);
|
||||||
|
},
|
||||||
|
properties as Record<string, unknown>
|
||||||
|
);
|
||||||
|
logger.debug('track', event_name, middlewareProperties);
|
||||||
|
|
||||||
|
mixpanel.track(event_name as string, middlewareProperties);
|
||||||
|
},
|
||||||
|
middleware(cb: Middleware): () => void {
|
||||||
|
middlewares.add(cb);
|
||||||
|
return () => {
|
||||||
|
middlewares.delete(cb);
|
||||||
|
};
|
||||||
},
|
},
|
||||||
opt_out_tracking() {
|
opt_out_tracking() {
|
||||||
mixpanel.opt_out_tracking();
|
mixpanel.opt_out_tracking();
|
||||||
|
|||||||
@@ -1,14 +1,8 @@
|
|||||||
import { type Framework, WorkspaceScope } from '@toeverything/infra';
|
import { type Framework, GlobalContextService } from '@toeverything/infra';
|
||||||
|
|
||||||
import { AuthService } from '../cloud';
|
import { AuthService } from '../cloud';
|
||||||
import {
|
import { TelemetryService } from './services/telemetry';
|
||||||
TelemetryService,
|
|
||||||
TelemetryWorkspaceContextService,
|
|
||||||
} from './services/telemetry';
|
|
||||||
|
|
||||||
export function configureTelemetryModule(framework: Framework) {
|
export function configureTelemetryModule(framework: Framework) {
|
||||||
framework.service(TelemetryService, [AuthService]);
|
framework.service(TelemetryService, [AuthService, GlobalContextService]);
|
||||||
framework
|
|
||||||
.scope(WorkspaceScope)
|
|
||||||
.service(TelemetryWorkspaceContextService, [WorkspaceScope]);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,7 @@
|
|||||||
import { mixpanel } from '@affine/core/mixpanel';
|
import { mixpanel } from '@affine/core/mixpanel';
|
||||||
import type { QuotaQuery } from '@affine/graphql';
|
import type { QuotaQuery } from '@affine/graphql';
|
||||||
import type { WorkspaceScope } from '@toeverything/infra';
|
import type { GlobalContextService } from '@toeverything/infra';
|
||||||
import {
|
import { ApplicationStarted, OnEvent, Service } from '@toeverything/infra';
|
||||||
ApplicationStarted,
|
|
||||||
DocsService,
|
|
||||||
OnEvent,
|
|
||||||
Service,
|
|
||||||
} from '@toeverything/infra';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AccountChanged,
|
AccountChanged,
|
||||||
@@ -15,8 +10,6 @@ import {
|
|||||||
} from '../../cloud';
|
} from '../../cloud';
|
||||||
import { AccountLoggedOut } from '../../cloud/services/auth';
|
import { AccountLoggedOut } from '../../cloud/services/auth';
|
||||||
import { UserQuotaChanged } from '../../cloud/services/user-quota';
|
import { UserQuotaChanged } from '../../cloud/services/user-quota';
|
||||||
import { resolveRouteLinkMeta } from '../../navigation';
|
|
||||||
import { WorkbenchService } from '../../workbench';
|
|
||||||
|
|
||||||
@OnEvent(ApplicationStarted, e => e.onApplicationStart)
|
@OnEvent(ApplicationStarted, e => e.onApplicationStart)
|
||||||
@OnEvent(AccountChanged, e => e.updateIdentity)
|
@OnEvent(AccountChanged, e => e.updateIdentity)
|
||||||
@@ -25,14 +18,19 @@ import { WorkbenchService } from '../../workbench';
|
|||||||
export class TelemetryService extends Service {
|
export class TelemetryService extends Service {
|
||||||
private prevQuota: NonNullable<QuotaQuery['currentUser']>['quota'] | null =
|
private prevQuota: NonNullable<QuotaQuery['currentUser']>['quota'] | null =
|
||||||
null;
|
null;
|
||||||
|
private readonly disposables: (() => void)[] = [];
|
||||||
|
|
||||||
constructor(private readonly auth: AuthService) {
|
constructor(
|
||||||
|
private readonly auth: AuthService,
|
||||||
|
private readonly globalContextService: GlobalContextService
|
||||||
|
) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
onApplicationStart() {
|
onApplicationStart() {
|
||||||
const account = this.auth.session.account$.value;
|
const account = this.auth.session.account$.value;
|
||||||
this.updateIdentity(account);
|
this.updateIdentity(account);
|
||||||
|
this.registerMiddlewares();
|
||||||
}
|
}
|
||||||
|
|
||||||
updateIdentity(account: AuthAccountInfo | null) {
|
updateIdentity(account: AuthAccountInfo | null) {
|
||||||
@@ -61,39 +59,41 @@ export class TelemetryService extends Service {
|
|||||||
}
|
}
|
||||||
this.prevQuota = quota;
|
this.prevQuota = quota;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// get telemetry related context in Workspace scope
|
registerMiddlewares() {
|
||||||
export class TelemetryWorkspaceContextService extends Service {
|
this.disposables.push(
|
||||||
constructor(private readonly provider: WorkspaceScope) {
|
mixpanel.middleware((_event, parameters) => {
|
||||||
super();
|
const extraContext = this.extractGlobalContext();
|
||||||
|
return {
|
||||||
|
...extraContext,
|
||||||
|
...parameters,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getPageContext() {
|
extractGlobalContext() {
|
||||||
const workbench = this.provider?.getOptional(WorkbenchService)?.workbench;
|
const globalContext = this.globalContextService.globalContext;
|
||||||
const docs = this.provider?.getOptional(DocsService);
|
const page = globalContext.isDoc.get()
|
||||||
|
? globalContext.isTrashDoc.get()
|
||||||
|
? 'trash'
|
||||||
|
: globalContext.docMode.get() === 'page'
|
||||||
|
? 'doc editor'
|
||||||
|
: 'whiteboard editor'
|
||||||
|
: globalContext.isAllDocs.get()
|
||||||
|
? 'doc library'
|
||||||
|
: globalContext.isTrash.get()
|
||||||
|
? 'trash library'
|
||||||
|
: globalContext.isCollection.get()
|
||||||
|
? 'collection detail'
|
||||||
|
: globalContext.isTag.get()
|
||||||
|
? 'tag detail'
|
||||||
|
: 'unknown';
|
||||||
|
return { page, activePage: page };
|
||||||
|
}
|
||||||
|
|
||||||
if (!workbench || !docs) return '';
|
override dispose(): void {
|
||||||
|
this.disposables.forEach(dispose => dispose());
|
||||||
const basename = workbench.basename$.value;
|
super.dispose();
|
||||||
const path = workbench.location$.value;
|
|
||||||
const fullPath = basename + path.pathname + path.search + path.hash;
|
|
||||||
const linkMeta = resolveRouteLinkMeta(fullPath);
|
|
||||||
return (() => {
|
|
||||||
const moduleName =
|
|
||||||
linkMeta?.moduleName === 'doc'
|
|
||||||
? docs.list.getMode(linkMeta.docId)
|
|
||||||
: linkMeta?.moduleName;
|
|
||||||
switch (moduleName) {
|
|
||||||
case 'page':
|
|
||||||
return 'doc editor';
|
|
||||||
case 'edgeless':
|
|
||||||
return 'whiteboard editor';
|
|
||||||
case 'trash':
|
|
||||||
return 'trash';
|
|
||||||
default:
|
|
||||||
return 'doc library';
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,10 +6,18 @@ import {
|
|||||||
import { useBlockSuiteDocMeta } from '@affine/core/hooks/use-block-suite-page-meta';
|
import { useBlockSuiteDocMeta } from '@affine/core/hooks/use-block-suite-page-meta';
|
||||||
import { performanceRenderLogger } from '@affine/core/shared';
|
import { performanceRenderLogger } from '@affine/core/shared';
|
||||||
import type { Filter } from '@affine/env/filter';
|
import type { Filter } from '@affine/env/filter';
|
||||||
import { useService, WorkspaceService } from '@toeverything/infra';
|
import {
|
||||||
import { useState } from 'react';
|
GlobalContextService,
|
||||||
|
useService,
|
||||||
|
WorkspaceService,
|
||||||
|
} from '@toeverything/infra';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
import { ViewBody, ViewHeader } from '../../../modules/workbench';
|
import {
|
||||||
|
useIsActiveView,
|
||||||
|
ViewBody,
|
||||||
|
ViewHeader,
|
||||||
|
} from '../../../modules/workbench';
|
||||||
import { EmptyPageList } from '../page-list-empty';
|
import { EmptyPageList } from '../page-list-empty';
|
||||||
import * as styles from './all-page.css';
|
import * as styles from './all-page.css';
|
||||||
import { FilterContainer } from './all-page-filter';
|
import { FilterContainer } from './all-page-filter';
|
||||||
@@ -17,6 +25,7 @@ import { AllPageHeader } from './all-page-header';
|
|||||||
|
|
||||||
export const AllPage = () => {
|
export const AllPage = () => {
|
||||||
const currentWorkspace = useService(WorkspaceService).workspace;
|
const currentWorkspace = useService(WorkspaceService).workspace;
|
||||||
|
const globalContext = useService(GlobalContextService).globalContext;
|
||||||
const pageMetas = useBlockSuiteDocMeta(currentWorkspace.docCollection);
|
const pageMetas = useBlockSuiteDocMeta(currentWorkspace.docCollection);
|
||||||
const [hideHeaderCreateNew, setHideHeaderCreateNew] = useState(true);
|
const [hideHeaderCreateNew, setHideHeaderCreateNew] = useState(true);
|
||||||
|
|
||||||
@@ -25,6 +34,19 @@ export const AllPage = () => {
|
|||||||
filters: filters,
|
filters: filters,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const isActiveView = useIsActiveView();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isActiveView) {
|
||||||
|
globalContext.isAllDocs.set(true);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
globalContext.isAllDocs.set(false);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}, [globalContext, isActiveView]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ViewHeader>
|
<ViewHeader>
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ const DetailPageImpl = memo(function DetailPageImpl() {
|
|||||||
const activeSidebarTab = useLiveData(view.activeSidebarTab$);
|
const activeSidebarTab = useLiveData(view.activeSidebarTab$);
|
||||||
|
|
||||||
const doc = useService(DocService).doc;
|
const doc = useService(DocService).doc;
|
||||||
|
const isInTrash = useLiveData(doc.meta$.map(meta => meta.trash));
|
||||||
const { openPage, jumpToPageBlock, jumpToTag } = useNavigateHelper();
|
const { openPage, jumpToPageBlock, jumpToTag } = useNavigateHelper();
|
||||||
const [editor, setEditor] = useState<AffineEditorContainer | null>(null);
|
const [editor, setEditor] = useState<AffineEditorContainer | null>(null);
|
||||||
const workspace = useService(WorkspaceService).workspace;
|
const workspace = useService(WorkspaceService).workspace;
|
||||||
@@ -139,7 +140,17 @@ const DetailPageImpl = memo(function DetailPageImpl() {
|
|||||||
}
|
}
|
||||||
}, [doc.id, setDocReadonly]);
|
}, [doc.id, setDocReadonly]);
|
||||||
|
|
||||||
const isInTrash = useLiveData(doc.meta$.map(meta => meta.trash));
|
useEffect(() => {
|
||||||
|
if (isActiveView) {
|
||||||
|
globalContext.isTrashDoc.set(!!isInTrash);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
globalContext.isTrashDoc.set(null);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}, [globalContext, isActiveView, isInTrash]);
|
||||||
|
|
||||||
useRegisterBlocksuiteEditorCommands();
|
useRegisterBlocksuiteEditorCommands();
|
||||||
const title = useLiveData(doc.title$);
|
const title = useLiveData(doc.title$);
|
||||||
usePageDocumentTitle(title);
|
usePageDocumentTitle(title);
|
||||||
|
|||||||
Reference in New Issue
Block a user