refactor(core): separate editor & doc mode (#7873)
doc.mode -> primaryMode
(*new) editor.mode
New Service:
editor service
Change Mode:
```
const editor = useService(EditorService).editor;
editor.setMode('page')
```
Change primary mode
```
const editor = useService(EditorService).editor;
editor.doc.setPrimaryMode('page')
```
This commit is contained in:
@@ -1,3 +1,5 @@
|
|||||||
|
import type { RootBlockModel } from '@blocksuite/blocks';
|
||||||
|
|
||||||
import { Entity } from '../../../framework';
|
import { Entity } from '../../../framework';
|
||||||
import type { DocScope } from '../scopes/doc';
|
import type { DocScope } from '../scopes/doc';
|
||||||
import type { DocsStore } from '../stores/docs';
|
import type { DocsStore } from '../stores/docs';
|
||||||
@@ -19,24 +21,22 @@ export class Doc extends Entity {
|
|||||||
public readonly record = this.scope.props.record;
|
public readonly record = this.scope.props.record;
|
||||||
|
|
||||||
readonly meta$ = this.record.meta$;
|
readonly meta$ = this.record.meta$;
|
||||||
readonly mode$ = this.record.mode$;
|
readonly primaryMode$ = this.record.primaryMode$;
|
||||||
readonly title$ = this.record.title$;
|
readonly title$ = this.record.title$;
|
||||||
readonly trash$ = this.record.trash$;
|
readonly trash$ = this.record.trash$;
|
||||||
|
|
||||||
setMode(mode: DocMode) {
|
setPrimaryMode(mode: DocMode) {
|
||||||
return this.record.setMode(mode);
|
return this.record.setPrimaryMode(mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
getMode() {
|
getPrimaryMode() {
|
||||||
return this.record.getMode();
|
return this.record.getPrimaryMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleMode() {
|
togglePrimaryMode() {
|
||||||
return this.record.toggleMode();
|
this.setPrimaryMode(
|
||||||
}
|
this.getPrimaryMode() === 'edgeless' ? 'page' : 'edgeless'
|
||||||
|
);
|
||||||
observeMode() {
|
|
||||||
return this.record.observeMode();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
moveToTrash() {
|
moveToTrash() {
|
||||||
@@ -54,4 +54,16 @@ export class Doc extends Entity {
|
|||||||
setPriorityLoad(priority: number) {
|
setPriorityLoad(priority: number) {
|
||||||
return this.store.setPriorityLoad(this.id, priority);
|
return this.store.setPriorityLoad(this.id, priority);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
changeDocTitle(newTitle: string) {
|
||||||
|
const pageBlock = this.blockSuiteDoc.getBlocksByFlavour('affine:page').at(0)
|
||||||
|
?.model as RootBlockModel | undefined;
|
||||||
|
if (pageBlock) {
|
||||||
|
this.blockSuiteDoc.transact(() => {
|
||||||
|
pageBlock.title.delete(0, pageBlock.title.length);
|
||||||
|
pageBlock.title.insert(newTitle, 0);
|
||||||
|
});
|
||||||
|
this.record.setMeta({ title: newTitle });
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,21 +55,24 @@ export class DocRecordList extends Entity {
|
|||||||
return this.docs$.map(record => record.find(record => record.id === id));
|
return this.docs$.map(record => record.find(record => record.id === id));
|
||||||
}
|
}
|
||||||
|
|
||||||
public setMode(id: string, mode: DocMode) {
|
public setPrimaryMode(id: string, mode: DocMode) {
|
||||||
return this.store.setDocModeSetting(id, mode);
|
return this.store.setDocPrimaryModeSetting(id, mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getMode(id: string) {
|
public getPrimaryMode(id: string) {
|
||||||
return this.store.getDocModeSetting(id);
|
return this.store.getDocPrimaryModeSetting(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public toggleMode(id: string) {
|
public togglePrimaryMode(id: string) {
|
||||||
const mode = this.getMode(id) === 'edgeless' ? 'page' : 'edgeless';
|
const mode = this.getPrimaryMode(id) === 'edgeless' ? 'page' : 'edgeless';
|
||||||
this.setMode(id, mode);
|
this.setPrimaryMode(id, mode);
|
||||||
return this.getMode(id);
|
return this.getPrimaryMode(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public observeMode(id: string) {
|
public primaryMode$(id: string) {
|
||||||
return this.store.watchDocModeSetting(id);
|
return LiveData.from(
|
||||||
|
this.store.watchDocPrimaryModeSetting(id),
|
||||||
|
this.getPrimaryMode(id)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,27 +26,17 @@ export class DocRecord extends Entity<{ id: string }> {
|
|||||||
this.docsStore.setDocMeta(this.id, meta);
|
this.docsStore.setDocMeta(this.id, meta);
|
||||||
}
|
}
|
||||||
|
|
||||||
mode$: LiveData<DocMode> = LiveData.from(
|
primaryMode$: LiveData<DocMode> = LiveData.from(
|
||||||
this.docsStore.watchDocModeSetting(this.id),
|
this.docsStore.watchDocPrimaryModeSetting(this.id),
|
||||||
'page'
|
'page'
|
||||||
).map(mode => (mode === 'edgeless' ? 'edgeless' : 'page'));
|
).map(mode => (mode === 'edgeless' ? 'edgeless' : 'page'));
|
||||||
|
|
||||||
setMode(mode: DocMode) {
|
setPrimaryMode(mode: DocMode) {
|
||||||
return this.docsStore.setDocModeSetting(this.id, mode);
|
return this.docsStore.setDocPrimaryModeSetting(this.id, mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
getMode() {
|
getPrimaryMode() {
|
||||||
return this.docsStore.getDocModeSetting(this.id);
|
return this.docsStore.getDocPrimaryModeSetting(this.id);
|
||||||
}
|
|
||||||
|
|
||||||
toggleMode() {
|
|
||||||
const mode = this.getMode() === 'edgeless' ? 'page' : 'edgeless';
|
|
||||||
this.setMode(mode);
|
|
||||||
return this.getMode();
|
|
||||||
}
|
|
||||||
|
|
||||||
observeMode() {
|
|
||||||
return this.docsStore.watchDocModeSetting(this.id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
moveToTrash() {
|
moveToTrash() {
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { Unreachable } from '@affine/env/constant';
|
import { Unreachable } from '@affine/env/constant';
|
||||||
import type { RootBlockModel } from '@blocksuite/blocks';
|
|
||||||
|
|
||||||
import { Service } from '../../../framework';
|
import { Service } from '../../../framework';
|
||||||
import { initEmptyPage } from '../../../initialization';
|
import { initEmptyPage } from '../../../initialization';
|
||||||
@@ -54,7 +53,7 @@ export class DocsService extends Service {
|
|||||||
|
|
||||||
createDoc(
|
createDoc(
|
||||||
options: {
|
options: {
|
||||||
mode?: DocMode;
|
primaryMode?: DocMode;
|
||||||
title?: string;
|
title?: string;
|
||||||
} = {}
|
} = {}
|
||||||
) {
|
) {
|
||||||
@@ -65,8 +64,8 @@ export class DocsService extends Service {
|
|||||||
if (!docRecord) {
|
if (!docRecord) {
|
||||||
throw new Unreachable();
|
throw new Unreachable();
|
||||||
}
|
}
|
||||||
if (options.mode) {
|
if (options.primaryMode) {
|
||||||
docRecord.setMode(options.mode);
|
docRecord.setPrimaryMode(options.primaryMode);
|
||||||
}
|
}
|
||||||
return docRecord;
|
return docRecord;
|
||||||
}
|
}
|
||||||
@@ -100,15 +99,7 @@ export class DocsService extends Service {
|
|||||||
const { doc, release } = this.open(docId);
|
const { doc, release } = this.open(docId);
|
||||||
doc.setPriorityLoad(10);
|
doc.setPriorityLoad(10);
|
||||||
await doc.waitForSyncReady();
|
await doc.waitForSyncReady();
|
||||||
const pageBlock = doc.blockSuiteDoc.getBlocksByFlavour('affine:page').at(0)
|
doc.changeDocTitle(newTitle);
|
||||||
?.model as RootBlockModel | undefined;
|
|
||||||
if (pageBlock) {
|
|
||||||
doc.blockSuiteDoc.transact(() => {
|
|
||||||
pageBlock.title.delete(0, pageBlock.title.length);
|
|
||||||
pageBlock.title.insert(newTitle, 0);
|
|
||||||
});
|
|
||||||
doc.record.setMeta({ title: newTitle });
|
|
||||||
}
|
|
||||||
release();
|
release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,15 +101,15 @@ export class DocsStore extends Store {
|
|||||||
this.workspaceService.workspace.docCollection.setDocMeta(id, meta);
|
this.workspaceService.workspace.docCollection.setDocMeta(id, meta);
|
||||||
}
|
}
|
||||||
|
|
||||||
setDocModeSetting(id: string, mode: DocMode) {
|
setDocPrimaryModeSetting(id: string, mode: DocMode) {
|
||||||
return this.localState.set(`page:${id}:mode`, mode);
|
return this.localState.set(`page:${id}:mode`, mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
getDocModeSetting(id: string) {
|
getDocPrimaryModeSetting(id: string) {
|
||||||
return this.localState.get<DocMode>(`page:${id}:mode`);
|
return this.localState.get<DocMode>(`page:${id}:mode`);
|
||||||
}
|
}
|
||||||
|
|
||||||
watchDocModeSetting(id: string) {
|
watchDocPrimaryModeSetting(id: string) {
|
||||||
return this.localState.watch<DocMode>(`page:${id}:mode`);
|
return this.localState.watch<DocMode>(`page:${id}:mode`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ type SimpleRadioItem = string;
|
|||||||
export interface RadioProps extends RadioGroupItemProps {
|
export interface RadioProps extends RadioGroupItemProps {
|
||||||
items: RadioItem[] | SimpleRadioItem[];
|
items: RadioItem[] | SimpleRadioItem[];
|
||||||
value: any;
|
value: any;
|
||||||
onChange: (value: any) => void;
|
onChange?: (value: any) => void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Total width of the radio group, items will be evenly distributed
|
* Total width of the radio group, items will be evenly distributed
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export async function buildShowcaseWorkspace(
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (defaultDoc) {
|
if (defaultDoc) {
|
||||||
defaultDoc.setMode('edgeless');
|
defaultDoc.setPrimaryMode('edgeless');
|
||||||
}
|
}
|
||||||
|
|
||||||
dispose();
|
dispose();
|
||||||
|
|||||||
@@ -2,14 +2,10 @@ import { Button, FlexWrapper, notify } from '@affine/component';
|
|||||||
import { openSettingModalAtom } from '@affine/core/atoms';
|
import { openSettingModalAtom } from '@affine/core/atoms';
|
||||||
import { track } from '@affine/core/mixpanel';
|
import { track } from '@affine/core/mixpanel';
|
||||||
import { SubscriptionService } from '@affine/core/modules/cloud';
|
import { SubscriptionService } from '@affine/core/modules/cloud';
|
||||||
|
import { EditorService } from '@affine/core/modules/editor';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import { AiIcon } from '@blocksuite/icons/rc';
|
import { AiIcon } from '@blocksuite/icons/rc';
|
||||||
import {
|
import { useLiveData, useServices } from '@toeverything/infra';
|
||||||
DocService,
|
|
||||||
useLiveData,
|
|
||||||
useServices,
|
|
||||||
WorkspaceService,
|
|
||||||
} from '@toeverything/infra';
|
|
||||||
import { cssVar } from '@toeverything/theme';
|
import { cssVar } from '@toeverything/theme';
|
||||||
import { useAtomValue, useSetAtom } from 'jotai';
|
import { useAtomValue, useSetAtom } from 'jotai';
|
||||||
import Lottie from 'lottie-react';
|
import Lottie from 'lottie-react';
|
||||||
@@ -46,10 +42,9 @@ const EdgelessOnboardingAnimation = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const AIOnboardingEdgeless = () => {
|
export const AIOnboardingEdgeless = () => {
|
||||||
const { docService, subscriptionService } = useServices({
|
const { subscriptionService, editorService } = useServices({
|
||||||
WorkspaceService,
|
|
||||||
DocService,
|
|
||||||
SubscriptionService,
|
SubscriptionService,
|
||||||
|
EditorService,
|
||||||
});
|
});
|
||||||
|
|
||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
@@ -61,8 +56,7 @@ export const AIOnboardingEdgeless = () => {
|
|||||||
|
|
||||||
const setSettingModal = useSetAtom(openSettingModalAtom);
|
const setSettingModal = useSetAtom(openSettingModalAtom);
|
||||||
|
|
||||||
const doc = docService.doc;
|
const mode = useLiveData(editorService.editor.mode$);
|
||||||
const mode = useLiveData(doc.mode$);
|
|
||||||
|
|
||||||
const goToPricingPlans = useCallback(() => {
|
const goToPricingPlans = useCallback(() => {
|
||||||
track.$.aiOnboarding.dialog.viewPlans();
|
track.$.aiOnboarding.dialog.viewPlans();
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { Modal, useConfirmModal } from '@affine/component/ui/modal';
|
|||||||
import { openSettingModalAtom } from '@affine/core/atoms';
|
import { openSettingModalAtom } from '@affine/core/atoms';
|
||||||
import { useDocCollectionPageTitle } from '@affine/core/hooks/use-block-suite-workspace-page-title';
|
import { useDocCollectionPageTitle } from '@affine/core/hooks/use-block-suite-workspace-page-title';
|
||||||
import { track } from '@affine/core/mixpanel';
|
import { track } from '@affine/core/mixpanel';
|
||||||
|
import { EditorService } from '@affine/core/modules/editor';
|
||||||
import { WorkspacePermissionService } from '@affine/core/modules/permissions';
|
import { WorkspacePermissionService } from '@affine/core/modules/permissions';
|
||||||
import { WorkspaceQuotaService } from '@affine/core/modules/quota';
|
import { WorkspaceQuotaService } from '@affine/core/modules/quota';
|
||||||
import { i18nTime, Trans, useI18n } from '@affine/i18n';
|
import { i18nTime, Trans, useI18n } from '@affine/i18n';
|
||||||
@@ -14,7 +15,6 @@ import * as Collapsible from '@radix-ui/react-collapsible';
|
|||||||
import type { DialogContentProps } from '@radix-ui/react-dialog';
|
import type { DialogContentProps } from '@radix-ui/react-dialog';
|
||||||
import {
|
import {
|
||||||
type DocMode,
|
type DocMode,
|
||||||
DocService,
|
|
||||||
useLiveData,
|
useLiveData,
|
||||||
useService,
|
useService,
|
||||||
WorkspaceService,
|
WorkspaceService,
|
||||||
@@ -433,8 +433,8 @@ const PageHistoryManager = ({
|
|||||||
[activeVersion, onClose, onRestore, snapshotPage]
|
[activeVersion, onClose, onRestore, snapshotPage]
|
||||||
);
|
);
|
||||||
|
|
||||||
const doc = useService(DocService).doc;
|
const editor = useService(EditorService).editor;
|
||||||
const [mode, setMode] = useState<DocMode>(doc.mode$.value);
|
const [mode, setMode] = useState<DocMode>(editor.mode$.value);
|
||||||
|
|
||||||
const title = useDocCollectionPageTitle(docCollection, pageId);
|
const title = useDocCollectionPageTitle(docCollection, pageId);
|
||||||
|
|
||||||
|
|||||||
@@ -5,12 +5,7 @@ import {
|
|||||||
Scrollable,
|
Scrollable,
|
||||||
} from '@affine/component';
|
} from '@affine/component';
|
||||||
import { DocsSearchService } from '@affine/core/modules/docs-search';
|
import { DocsSearchService } from '@affine/core/modules/docs-search';
|
||||||
import {
|
import { LiveData, useLiveData, useServices } from '@toeverything/infra';
|
||||||
LiveData,
|
|
||||||
useLiveData,
|
|
||||||
useServices,
|
|
||||||
WorkspaceService,
|
|
||||||
} from '@toeverything/infra';
|
|
||||||
import { Suspense, useCallback, useContext, useMemo, useRef } from 'react';
|
import { Suspense, useCallback, useContext, useMemo, useRef } from 'react';
|
||||||
|
|
||||||
import { BlocksuiteHeaderTitle } from '../../../blocksuite/block-suite-header/title';
|
import { BlocksuiteHeaderTitle } from '../../../blocksuite/block-suite-header/title';
|
||||||
@@ -35,9 +30,8 @@ export const InfoModal = ({
|
|||||||
onOpenChange: (open: boolean) => void;
|
onOpenChange: (open: boolean) => void;
|
||||||
docId: string;
|
docId: string;
|
||||||
}) => {
|
}) => {
|
||||||
const { docsSearchService, workspaceService } = useServices({
|
const { docsSearchService } = useServices({
|
||||||
DocsSearchService,
|
DocsSearchService,
|
||||||
WorkspaceService,
|
|
||||||
});
|
});
|
||||||
const titleInputHandleRef = useRef<InlineEditHandle>(null);
|
const titleInputHandleRef = useRef<InlineEditHandle>(null);
|
||||||
const manager = usePagePropertiesManager(docId);
|
const manager = usePagePropertiesManager(docId);
|
||||||
@@ -72,10 +66,9 @@ export const InfoModal = ({
|
|||||||
>
|
>
|
||||||
<div className={styles.titleContainer} data-testid="info-modal-title">
|
<div className={styles.titleContainer} data-testid="info-modal-title">
|
||||||
<BlocksuiteHeaderTitle
|
<BlocksuiteHeaderTitle
|
||||||
|
docId={docId}
|
||||||
className={styles.titleStyle}
|
className={styles.titleStyle}
|
||||||
inputHandleRef={titleInputHandleRef}
|
inputHandleRef={titleInputHandleRef}
|
||||||
pageId={docId}
|
|
||||||
docCollection={workspaceService.workspace.docCollection}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<managerContext.Provider value={manager}>
|
<managerContext.Provider value={manager}>
|
||||||
|
|||||||
@@ -84,10 +84,17 @@ export function AffinePageReference({
|
|||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
|
|
||||||
const docsService = useService(DocsService);
|
const docsService = useService(DocsService);
|
||||||
const mode$ = LiveData.from(docsService.list.observeMode(pageId), undefined);
|
const docPrimaryMode = useLiveData(
|
||||||
const docMode = useLiveData(mode$) ?? null;
|
LiveData.computed(get => {
|
||||||
|
const primaryMode$ = get(docsService.list.doc$(pageId))?.primaryMode$;
|
||||||
|
if (!primaryMode$) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return get(primaryMode$);
|
||||||
|
})
|
||||||
|
);
|
||||||
const el = pageReferenceRenderer({
|
const el = pageReferenceRenderer({
|
||||||
docMode,
|
docMode: docPrimaryMode,
|
||||||
pageId,
|
pageId,
|
||||||
pageMetaHelper,
|
pageMetaHelper,
|
||||||
journalHelper,
|
journalHelper,
|
||||||
|
|||||||
@@ -3,10 +3,11 @@ import { Divider } from '@affine/component/ui/divider';
|
|||||||
import { ExportMenuItems } from '@affine/core/components/page-list';
|
import { ExportMenuItems } from '@affine/core/components/page-list';
|
||||||
import { useExportPage } from '@affine/core/hooks/affine/use-export-page';
|
import { useExportPage } from '@affine/core/hooks/affine/use-export-page';
|
||||||
import { useSharingUrl } from '@affine/core/hooks/affine/use-share-url';
|
import { useSharingUrl } from '@affine/core/hooks/affine/use-share-url';
|
||||||
|
import { EditorService } from '@affine/core/modules/editor';
|
||||||
import { WorkspaceFlavour } from '@affine/env/workspace';
|
import { WorkspaceFlavour } from '@affine/env/workspace';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import { CopyIcon } from '@blocksuite/icons/rc';
|
import { CopyIcon } from '@blocksuite/icons/rc';
|
||||||
import { DocService, useLiveData, useService } from '@toeverything/infra';
|
import { useLiveData, useService } from '@toeverything/infra';
|
||||||
|
|
||||||
import * as styles from './index.css';
|
import * as styles from './index.css';
|
||||||
import type { ShareMenuProps } from './share-menu';
|
import type { ShareMenuProps } from './share-menu';
|
||||||
@@ -16,7 +17,7 @@ export const ShareExport = ({
|
|||||||
currentPage,
|
currentPage,
|
||||||
}: ShareMenuProps) => {
|
}: ShareMenuProps) => {
|
||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
const doc = useService(DocService).doc;
|
const editor = useService(EditorService).editor;
|
||||||
const workspaceId = workspace.id;
|
const workspaceId = workspace.id;
|
||||||
const pageId = currentPage.id;
|
const pageId = currentPage.id;
|
||||||
const { sharingUrl, onClickCopyLink } = useSharingUrl({
|
const { sharingUrl, onClickCopyLink } = useSharingUrl({
|
||||||
@@ -25,7 +26,7 @@ export const ShareExport = ({
|
|||||||
urlType: 'workspace',
|
urlType: 'workspace',
|
||||||
});
|
});
|
||||||
const exportHandler = useExportPage(currentPage);
|
const exportHandler = useExportPage(currentPage);
|
||||||
const currentMode = useLiveData(doc.mode$);
|
const currentMode = useLiveData(editor.mode$);
|
||||||
const isMac = environment.isBrowser && environment.isMacOs;
|
const isMac = environment.isBrowser && environment.isMacOs;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ export const AffineSharePage = (props: ShareMenuProps) => {
|
|||||||
isSharedPage === null || sharedMode === null || baseUrl === null;
|
isSharedPage === null || sharedMode === null || baseUrl === null;
|
||||||
const [showDisable, setShowDisable] = useState(false);
|
const [showDisable, setShowDisable] = useState(false);
|
||||||
|
|
||||||
const currentDocMode = useLiveData(doc.mode$);
|
const currentDocMode = useLiveData(doc.primaryMode$);
|
||||||
|
|
||||||
const mode = useMemo(() => {
|
const mode = useMemo(() => {
|
||||||
if (isSharedPage && sharedMode) {
|
if (isSharedPage && sharedMode) {
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ export function createLinkedWidgetConfig(framework: FrameworkProvider) {
|
|||||||
const MAX_DOCS = 6;
|
const MAX_DOCS = 6;
|
||||||
const docsService = framework.get(DocsService);
|
const docsService = framework.get(DocsService);
|
||||||
const isEdgeless = (d: DocMeta) =>
|
const isEdgeless = (d: DocMeta) =>
|
||||||
docsService.list.getMode(d.id) === 'edgeless';
|
docsService.list.getPrimaryMode(d.id) === 'edgeless';
|
||||||
return Promise.resolve([
|
return Promise.resolve([
|
||||||
{
|
{
|
||||||
name: 'Link to Doc',
|
name: 'Link to Doc',
|
||||||
|
|||||||
@@ -279,28 +279,28 @@ export function patchDocModeService(
|
|||||||
pageService.docModeService = {
|
pageService.docModeService = {
|
||||||
setMode: (mode: DocMode, id?: string) => {
|
setMode: (mode: DocMode, id?: string) => {
|
||||||
if (id) {
|
if (id) {
|
||||||
docsService.list.setMode(id, mode);
|
docsService.list.setPrimaryMode(id, mode);
|
||||||
} else {
|
} else {
|
||||||
docService.doc.setMode(mode);
|
docService.doc.setPrimaryMode(mode);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getMode: (id?: string) => {
|
getMode: (id?: string) => {
|
||||||
const mode = id
|
const mode = id
|
||||||
? docsService.list.getMode(id)
|
? docsService.list.getPrimaryMode(id)
|
||||||
: docService.doc.getMode();
|
: docService.doc.getPrimaryMode();
|
||||||
return mode || DEFAULT_MODE;
|
return mode || DEFAULT_MODE;
|
||||||
},
|
},
|
||||||
toggleMode: (id?: string) => {
|
toggleMode: (id?: string) => {
|
||||||
const mode = id
|
const mode = id
|
||||||
? docsService.list.toggleMode(id)
|
? docsService.list.togglePrimaryMode(id)
|
||||||
: docService.doc.toggleMode();
|
: docService.doc.togglePrimaryMode();
|
||||||
return mode || DEFAULT_MODE;
|
return mode || DEFAULT_MODE;
|
||||||
},
|
},
|
||||||
onModeChange: (handler: (mode: DocMode) => void, id?: string) => {
|
onModeChange: (handler: (mode: DocMode) => void, id?: string) => {
|
||||||
// eslint-disable-next-line rxjs/finnish
|
// eslint-disable-next-line rxjs/finnish
|
||||||
const mode$ = id
|
const mode$ = id
|
||||||
? docsService.list.observeMode(id)
|
? docsService.list.primaryMode$(id)
|
||||||
: docService.doc.observeMode();
|
: docService.doc.primaryMode$;
|
||||||
const sub = mode$.subscribe(m => handler(m || DEFAULT_MODE));
|
const sub = mode$.subscribe(m => handler(m || DEFAULT_MODE));
|
||||||
return {
|
return {
|
||||||
dispose: sub.unsubscribe,
|
dispose: sub.unsubscribe,
|
||||||
@@ -412,7 +412,7 @@ export function patchQuickSearchService(
|
|||||||
? 'edgeless'
|
? 'edgeless'
|
||||||
: 'page';
|
: 'page';
|
||||||
const newDoc = docsService.createDoc({
|
const newDoc = docsService.createDoc({
|
||||||
mode,
|
primaryMode: mode,
|
||||||
title: result.payload.title,
|
title: result.payload.title,
|
||||||
});
|
});
|
||||||
resolve({
|
resolve({
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import { useExportPage } from '@affine/core/hooks/affine/use-export-page';
|
|||||||
import { useTrashModalHelper } from '@affine/core/hooks/affine/use-trash-modal-helper';
|
import { useTrashModalHelper } from '@affine/core/hooks/affine/use-trash-modal-helper';
|
||||||
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
||||||
import { track } from '@affine/core/mixpanel';
|
import { track } from '@affine/core/mixpanel';
|
||||||
|
import { EditorService } from '@affine/core/modules/editor';
|
||||||
import { WorkbenchService } from '@affine/core/modules/workbench';
|
import { WorkbenchService } from '@affine/core/modules/workbench';
|
||||||
import { ViewService } from '@affine/core/modules/workbench/services/view';
|
import { ViewService } from '@affine/core/modules/workbench/services/view';
|
||||||
import { useDetailPageHeaderResponsive } from '@affine/core/pages/workspace/detail-page/use-header-responsive';
|
import { useDetailPageHeaderResponsive } from '@affine/core/pages/workspace/detail-page/use-header-responsive';
|
||||||
@@ -41,12 +42,7 @@ import {
|
|||||||
TocIcon,
|
TocIcon,
|
||||||
} from '@blocksuite/icons/rc';
|
} from '@blocksuite/icons/rc';
|
||||||
import type { Doc } from '@blocksuite/store';
|
import type { Doc } from '@blocksuite/store';
|
||||||
import {
|
import { useLiveData, useService, WorkspaceService } from '@toeverything/infra';
|
||||||
DocService,
|
|
||||||
useLiveData,
|
|
||||||
useService,
|
|
||||||
WorkspaceService,
|
|
||||||
} from '@toeverything/infra';
|
|
||||||
import { useSetAtom } from 'jotai';
|
import { useSetAtom } from 'jotai';
|
||||||
import { useCallback, useState } from 'react';
|
import { useCallback, useState } from 'react';
|
||||||
|
|
||||||
@@ -75,9 +71,11 @@ export const PageHeaderMenuButton = ({
|
|||||||
const workspace = useService(WorkspaceService).workspace;
|
const workspace = useService(WorkspaceService).workspace;
|
||||||
const docCollection = workspace.docCollection;
|
const docCollection = workspace.docCollection;
|
||||||
|
|
||||||
const doc = useService(DocService).doc;
|
const editorService = useService(EditorService);
|
||||||
const isInTrash = useLiveData(doc.meta$.map(m => m.trash));
|
const isInTrash = useLiveData(
|
||||||
const currentMode = useLiveData(doc.mode$);
|
editorService.editor.doc.meta$.map(meta => meta.trash)
|
||||||
|
);
|
||||||
|
const currentMode = useLiveData(editorService.editor.mode$);
|
||||||
|
|
||||||
const workbench = useService(WorkbenchService).workbench;
|
const workbench = useService(WorkbenchService).workbench;
|
||||||
|
|
||||||
@@ -139,9 +137,9 @@ export const PageHeaderMenuButton = ({
|
|||||||
setTrashModal({
|
setTrashModal({
|
||||||
open: true,
|
open: true,
|
||||||
pageIds: [pageId],
|
pageIds: [pageId],
|
||||||
pageTitles: [doc.meta$.value.title ?? ''],
|
pageTitles: [editorService.editor.doc.meta$.value.title ?? ''],
|
||||||
});
|
});
|
||||||
}, [doc.meta$.value.title, pageId, setTrashModal]);
|
}, [editorService, pageId, setTrashModal]);
|
||||||
|
|
||||||
const handleRename = useCallback(() => {
|
const handleRename = useCallback(() => {
|
||||||
rename?.();
|
rename?.();
|
||||||
@@ -149,7 +147,7 @@ export const PageHeaderMenuButton = ({
|
|||||||
}, [rename]);
|
}, [rename]);
|
||||||
|
|
||||||
const handleSwitchMode = useCallback(() => {
|
const handleSwitchMode = useCallback(() => {
|
||||||
doc.toggleMode();
|
editorService.editor.toggleMode();
|
||||||
track.$.header.docOptions.switchPageMode({
|
track.$.header.docOptions.switchPageMode({
|
||||||
mode: currentMode === 'page' ? 'edgeless' : 'page',
|
mode: currentMode === 'page' ? 'edgeless' : 'page',
|
||||||
});
|
});
|
||||||
@@ -158,7 +156,7 @@ export const PageHeaderMenuButton = ({
|
|||||||
? t['com.affine.toastMessage.edgelessMode']()
|
? t['com.affine.toastMessage.edgelessMode']()
|
||||||
: t['com.affine.toastMessage.pageMode']()
|
: t['com.affine.toastMessage.pageMode']()
|
||||||
);
|
);
|
||||||
}, [currentMode, doc, t]);
|
}, [currentMode, editorService, t]);
|
||||||
const menuItemStyle = {
|
const menuItemStyle = {
|
||||||
padding: '4px 12px',
|
padding: '4px 12px',
|
||||||
transition: 'all 0.3s',
|
transition: 'all 0.3s',
|
||||||
@@ -170,7 +168,7 @@ export const PageHeaderMenuButton = ({
|
|||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const exportHandler = useExportPage(doc.blockSuiteDoc);
|
const exportHandler = useExportPage(editorService.editor.doc.blockSuiteDoc);
|
||||||
|
|
||||||
const handleDuplicate = useCallback(() => {
|
const handleDuplicate = useCallback(() => {
|
||||||
duplicate(pageId);
|
duplicate(pageId);
|
||||||
|
|||||||
@@ -1,53 +1,52 @@
|
|||||||
import type { InlineEditProps } from '@affine/component';
|
import type { InlineEditProps } from '@affine/component';
|
||||||
import { InlineEdit } from '@affine/component';
|
import { InlineEdit } from '@affine/component';
|
||||||
|
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
||||||
|
import { track } from '@affine/core/mixpanel';
|
||||||
import {
|
import {
|
||||||
useBlockSuiteDocMeta,
|
DocsService,
|
||||||
useDocMetaHelper,
|
useLiveData,
|
||||||
} from '@affine/core/hooks/use-block-suite-page-meta';
|
useService,
|
||||||
import type { DocCollection } from '@affine/core/shared';
|
WorkspaceService,
|
||||||
|
} from '@toeverything/infra';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import type { HTMLAttributes } from 'react';
|
import type { HTMLAttributes } from 'react';
|
||||||
import { useCallback } from 'react';
|
|
||||||
|
|
||||||
import * as styles from './style.css';
|
import * as styles from './style.css';
|
||||||
|
|
||||||
export interface BlockSuiteHeaderTitleProps {
|
export interface BlockSuiteHeaderTitleProps {
|
||||||
docCollection: DocCollection;
|
docId: string;
|
||||||
pageId: string;
|
|
||||||
/** if set, title cannot be edited */
|
/** if set, title cannot be edited */
|
||||||
isPublic?: boolean;
|
|
||||||
inputHandleRef?: InlineEditProps['handleRef'];
|
inputHandleRef?: InlineEditProps['handleRef'];
|
||||||
className?: string;
|
className?: string;
|
||||||
onEditSave?: () => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const inputAttrs = {
|
const inputAttrs = {
|
||||||
'data-testid': 'title-content',
|
'data-testid': 'title-content',
|
||||||
} as HTMLAttributes<HTMLInputElement>;
|
} as HTMLAttributes<HTMLInputElement>;
|
||||||
export const BlocksuiteHeaderTitle = (props: BlockSuiteHeaderTitleProps) => {
|
export const BlocksuiteHeaderTitle = (props: BlockSuiteHeaderTitleProps) => {
|
||||||
const { docCollection, pageId, isPublic, inputHandleRef, onEditSave } = props;
|
const { inputHandleRef, docId } = props;
|
||||||
const currentPage = docCollection.getDoc(pageId);
|
const workspaceService = useService(WorkspaceService);
|
||||||
const pageMeta = useBlockSuiteDocMeta(docCollection).find(
|
const isSharedMode = workspaceService.workspace.openOptions.isSharedMode;
|
||||||
meta => meta.id === currentPage?.id
|
const docsService = useService(DocsService);
|
||||||
);
|
|
||||||
const title = pageMeta?.title;
|
|
||||||
const { setDocTitle } = useDocMetaHelper(docCollection);
|
|
||||||
|
|
||||||
const onChange = useCallback(
|
const docRecord = useLiveData(docsService.list.doc$(docId));
|
||||||
(v: string) => {
|
const docTitle = useLiveData(docRecord?.title$);
|
||||||
onEditSave?.();
|
|
||||||
setDocTitle(currentPage?.id || '', v);
|
const onChange = useAsyncCallback(
|
||||||
|
async (v: string) => {
|
||||||
|
await docsService.changeDocTitle(docId, v);
|
||||||
|
track.$.header.actions.renameDoc();
|
||||||
},
|
},
|
||||||
[currentPage?.id, onEditSave, setDocTitle]
|
[docId, docsService]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<InlineEdit
|
<InlineEdit
|
||||||
className={clsx(styles.title, props.className)}
|
className={clsx(styles.title, props.className)}
|
||||||
autoSelect
|
autoSelect
|
||||||
value={title}
|
value={docTitle}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
editable={!isPublic}
|
editable={!isSharedMode}
|
||||||
exitible={true}
|
exitible={true}
|
||||||
placeholder="Untitled"
|
placeholder="Untitled"
|
||||||
data-testid="title-edit-button"
|
data-testid="title-edit-button"
|
||||||
|
|||||||
@@ -1,14 +1,10 @@
|
|||||||
import { RadioGroup, type RadioItem, toast, Tooltip } from '@affine/component';
|
import { RadioGroup, type RadioItem, toast, Tooltip } from '@affine/component';
|
||||||
import { registerAffineCommand } from '@affine/core/commands';
|
import { registerAffineCommand } from '@affine/core/commands';
|
||||||
import { track } from '@affine/core/mixpanel';
|
import { track } from '@affine/core/mixpanel';
|
||||||
|
import { EditorService } from '@affine/core/modules/editor';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import { EdgelessIcon, PageIcon } from '@blocksuite/icons/rc';
|
import { EdgelessIcon, PageIcon } from '@blocksuite/icons/rc';
|
||||||
import {
|
import { type DocMode, useLiveData, useService } from '@toeverything/infra';
|
||||||
type DocMode,
|
|
||||||
DocsService,
|
|
||||||
useLiveData,
|
|
||||||
useService,
|
|
||||||
} from '@toeverything/infra';
|
|
||||||
import { useCallback, useEffect, useMemo } from 'react';
|
import { useCallback, useEffect, useMemo } from 'react';
|
||||||
|
|
||||||
import { switchItem } from './style.css';
|
import { switchItem } from './style.css';
|
||||||
@@ -33,30 +29,28 @@ const PageRadioItem: RadioItem = {
|
|||||||
className: switchItem,
|
className: switchItem,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const EditorModeSwitch = ({
|
export const EditorModeSwitch = () => {
|
||||||
pageId,
|
|
||||||
isPublic,
|
|
||||||
publicMode,
|
|
||||||
}: EditorModeSwitchProps) => {
|
|
||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
const docsService = useService(DocsService);
|
const editor = useService(EditorService).editor;
|
||||||
const doc = useLiveData(docsService.list.doc$(pageId));
|
const trash = useLiveData(editor.doc.trash$);
|
||||||
const trash = useLiveData(doc?.trash$);
|
const isSharedMode = editor.isSharedMode;
|
||||||
const currentMode = useLiveData(doc?.mode$);
|
const currentMode = useLiveData(editor.mode$);
|
||||||
|
|
||||||
const togglePage = useCallback(() => {
|
const togglePage = useCallback(() => {
|
||||||
if (currentMode === 'page' || isPublic || trash) return;
|
if (currentMode === 'page' || isSharedMode || trash) return;
|
||||||
doc?.setMode('page');
|
editor.setMode('page');
|
||||||
|
editor.doc.setPrimaryMode('page');
|
||||||
toast(t['com.affine.toastMessage.pageMode']());
|
toast(t['com.affine.toastMessage.pageMode']());
|
||||||
track.$.header.actions.switchPageMode({ mode: 'page' });
|
track.$.header.actions.switchPageMode({ mode: 'page' });
|
||||||
}, [currentMode, doc, isPublic, t, trash]);
|
}, [currentMode, editor, isSharedMode, t, trash]);
|
||||||
|
|
||||||
const toggleEdgeless = useCallback(() => {
|
const toggleEdgeless = useCallback(() => {
|
||||||
if (currentMode === 'edgeless' || isPublic || trash) return;
|
if (currentMode === 'edgeless' || isSharedMode || trash) return;
|
||||||
doc?.setMode('edgeless');
|
editor.setMode('edgeless');
|
||||||
|
editor.doc.setPrimaryMode('edgeless');
|
||||||
toast(t['com.affine.toastMessage.edgelessMode']());
|
toast(t['com.affine.toastMessage.edgelessMode']());
|
||||||
track.$.header.actions.switchPageMode({ mode: 'edgeless' });
|
track.$.header.actions.switchPageMode({ mode: 'edgeless' });
|
||||||
}, [currentMode, doc, isPublic, t, trash]);
|
}, [currentMode, editor, isSharedMode, t, trash]);
|
||||||
|
|
||||||
const onModeChange = useCallback(
|
const onModeChange = useCallback(
|
||||||
(mode: DocMode) => {
|
(mode: DocMode) => {
|
||||||
@@ -66,13 +60,12 @@ export const EditorModeSwitch = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const shouldHide = useCallback(
|
const shouldHide = useCallback(
|
||||||
(mode: DocMode) =>
|
(mode: DocMode) => (trash || isSharedMode) && currentMode !== mode,
|
||||||
(trash && currentMode !== mode) || (isPublic && publicMode !== mode),
|
[currentMode, isSharedMode, trash]
|
||||||
[currentMode, isPublic, publicMode, trash]
|
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (trash || isPublic || currentMode === undefined) return;
|
if (trash || isSharedMode || currentMode === undefined) return;
|
||||||
return registerAffineCommand({
|
return registerAffineCommand({
|
||||||
id: 'affine:doc-mode-switch',
|
id: 'affine:doc-mode-switch',
|
||||||
category: 'editor:page',
|
category: 'editor:page',
|
||||||
@@ -87,14 +80,14 @@ export const EditorModeSwitch = ({
|
|||||||
},
|
},
|
||||||
run: () => onModeChange(currentMode === 'edgeless' ? 'page' : 'edgeless'),
|
run: () => onModeChange(currentMode === 'edgeless' ? 'page' : 'edgeless'),
|
||||||
});
|
});
|
||||||
}, [currentMode, isPublic, onModeChange, t, trash]);
|
}, [currentMode, isSharedMode, onModeChange, t, trash]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
content={t['Switch']()}
|
content={t['Switch']()}
|
||||||
shortcut={['$alt', 'S']}
|
shortcut={['$alt', 'S']}
|
||||||
side="bottom"
|
side="bottom"
|
||||||
options={{ hidden: isPublic || trash }}
|
options={{ hidden: trash || isSharedMode }}
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<PureEditorModeSwitch
|
<PureEditorModeSwitch
|
||||||
@@ -110,7 +103,7 @@ export const EditorModeSwitch = ({
|
|||||||
|
|
||||||
export interface PureEditorModeSwitchProps {
|
export interface PureEditorModeSwitchProps {
|
||||||
mode?: DocMode;
|
mode?: DocMode;
|
||||||
setMode: (mode: DocMode) => void;
|
setMode?: (mode: DocMode) => void;
|
||||||
hidePage?: boolean;
|
hidePage?: boolean;
|
||||||
hideEdgeless?: boolean;
|
hideEdgeless?: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,19 +9,14 @@ import type { DocCollection } from '../../../shared';
|
|||||||
export const usePageHelper = (docCollection: DocCollection) => {
|
export const usePageHelper = (docCollection: DocCollection) => {
|
||||||
const workbench = useService(WorkbenchService).workbench;
|
const workbench = useService(WorkbenchService).workbench;
|
||||||
const { createDoc } = useDocCollectionHelper(docCollection);
|
const { createDoc } = useDocCollectionHelper(docCollection);
|
||||||
const docRecordList = useService(DocsService).list;
|
const docsService = useService(DocsService);
|
||||||
|
const docRecordList = docsService.list;
|
||||||
const isPreferredEdgeless = useCallback(
|
|
||||||
(pageId: string) =>
|
|
||||||
docRecordList.doc$(pageId).value?.mode$.value === 'edgeless',
|
|
||||||
[docRecordList]
|
|
||||||
);
|
|
||||||
|
|
||||||
const createPageAndOpen = useCallback(
|
const createPageAndOpen = useCallback(
|
||||||
(mode?: 'page' | 'edgeless', open?: boolean | 'new-tab') => {
|
(mode?: 'page' | 'edgeless', open?: boolean | 'new-tab') => {
|
||||||
const page = createDoc();
|
const page = createDoc();
|
||||||
initEmptyPage(page);
|
initEmptyPage(page);
|
||||||
docRecordList.doc$(page.id).value?.setMode(mode || 'page');
|
docRecordList.doc$(page.id).value?.setPrimaryMode(mode || 'page');
|
||||||
if (open !== false)
|
if (open !== false)
|
||||||
workbench.openDoc(page.id, {
|
workbench.openDoc(page.id, {
|
||||||
at: open === 'new-tab' ? 'new-tab' : 'active',
|
at: open === 'new-tab' ? 'new-tab' : 'active',
|
||||||
@@ -82,16 +77,10 @@ export const usePageHelper = (docCollection: DocCollection) => {
|
|||||||
|
|
||||||
return useMemo(() => {
|
return useMemo(() => {
|
||||||
return {
|
return {
|
||||||
isPreferredEdgeless,
|
|
||||||
createPage: (open?: boolean | 'new-tab') =>
|
createPage: (open?: boolean | 'new-tab') =>
|
||||||
createPageAndOpen('page', open),
|
createPageAndOpen('page', open),
|
||||||
createEdgeless: createEdgelessAndOpen,
|
createEdgeless: createEdgelessAndOpen,
|
||||||
importFile: importFileAndOpen,
|
importFile: importFileAndOpen,
|
||||||
};
|
};
|
||||||
}, [
|
}, [createEdgelessAndOpen, createPageAndOpen, importFileAndOpen]);
|
||||||
isPreferredEdgeless,
|
|
||||||
createEdgelessAndOpen,
|
|
||||||
createPageAndOpen,
|
|
||||||
importFileAndOpen,
|
|
||||||
]);
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import type { AffineEditorContainer } from '@blocksuite/presets';
|
|||||||
import type { Doc as BlockSuiteDoc, DocCollection } from '@blocksuite/store';
|
import type { Doc as BlockSuiteDoc, DocCollection } from '@blocksuite/store';
|
||||||
import {
|
import {
|
||||||
type DocMode,
|
type DocMode,
|
||||||
DocService,
|
|
||||||
fontStyleOptions,
|
fontStyleOptions,
|
||||||
useLiveData,
|
useLiveData,
|
||||||
useService,
|
useService,
|
||||||
@@ -17,6 +16,7 @@ import { memo, Suspense, useCallback, useMemo } from 'react';
|
|||||||
import { useLocation } from 'react-router-dom';
|
import { useLocation } from 'react-router-dom';
|
||||||
|
|
||||||
import { useAppSettingHelper } from '../hooks/affine/use-app-setting-helper';
|
import { useAppSettingHelper } from '../hooks/affine/use-app-setting-helper';
|
||||||
|
import { EditorService } from '../modules/editor';
|
||||||
import { BlockSuiteEditor as Editor } from './blocksuite/block-suite-editor';
|
import { BlockSuiteEditor as Editor } from './blocksuite/block-suite-editor';
|
||||||
import * as styles from './page-detail-editor.css';
|
import * as styles from './page-detail-editor.css';
|
||||||
|
|
||||||
@@ -45,19 +45,10 @@ function useRouterHash() {
|
|||||||
const PageDetailEditorMain = memo(function PageDetailEditorMain({
|
const PageDetailEditorMain = memo(function PageDetailEditorMain({
|
||||||
page,
|
page,
|
||||||
onLoad,
|
onLoad,
|
||||||
isPublic,
|
|
||||||
publishMode,
|
|
||||||
}: PageDetailEditorProps & { page: BlockSuiteDoc }) {
|
}: PageDetailEditorProps & { page: BlockSuiteDoc }) {
|
||||||
const currentMode = useLiveData(useService(DocService).doc.mode$);
|
const editor = useService(EditorService).editor;
|
||||||
const mode = useMemo(() => {
|
const mode = useLiveData(editor.mode$);
|
||||||
const shareMode = publishMode || currentMode;
|
const isSharedMode = editor.isSharedMode;
|
||||||
|
|
||||||
if (isPublic) {
|
|
||||||
return shareMode;
|
|
||||||
}
|
|
||||||
return currentMode;
|
|
||||||
}, [isPublic, publishMode, currentMode]);
|
|
||||||
|
|
||||||
const { appSettings } = useAppSettingHelper();
|
const { appSettings } = useAppSettingHelper();
|
||||||
|
|
||||||
const value = useMemo(() => {
|
const value = useMemo(() => {
|
||||||
@@ -97,8 +88,8 @@ const PageDetailEditorMain = memo(function PageDetailEditorMain({
|
|||||||
return (
|
return (
|
||||||
<Editor
|
<Editor
|
||||||
className={clsx(styles.editor, {
|
className={clsx(styles.editor, {
|
||||||
'full-screen': !isPublic && appSettings.fullWidthLayout,
|
'full-screen': !isSharedMode && appSettings.fullWidthLayout,
|
||||||
'is-public': isPublic,
|
'is-public': isSharedMode,
|
||||||
})}
|
})}
|
||||||
style={
|
style={
|
||||||
{
|
{
|
||||||
@@ -107,7 +98,7 @@ const PageDetailEditorMain = memo(function PageDetailEditorMain({
|
|||||||
}
|
}
|
||||||
mode={mode}
|
mode={mode}
|
||||||
page={page}
|
page={page}
|
||||||
shared={isPublic}
|
shared={isSharedMode}
|
||||||
defaultSelectedBlockId={blockId}
|
defaultSelectedBlockId={blockId}
|
||||||
onLoadEditor={onLoadEditor}
|
onLoadEditor={onLoadEditor}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import type { DocMeta } from '@blocksuite/store';
|
|||||||
import { useService, WorkspaceService } from '@toeverything/infra';
|
import { useService, WorkspaceService } from '@toeverything/infra';
|
||||||
import { useCallback, useMemo, useRef, useState } from 'react';
|
import { useCallback, useMemo, useRef, useState } from 'react';
|
||||||
|
|
||||||
import { usePageHelper } from '../../blocksuite/block-suite-page-list/utils';
|
|
||||||
import { ListFloatingToolbar } from '../components/list-floating-toolbar';
|
import { ListFloatingToolbar } from '../components/list-floating-toolbar';
|
||||||
import { usePageItemGroupDefinitions } from '../group-definitions';
|
import { usePageItemGroupDefinitions } from '../group-definitions';
|
||||||
import { usePageHeaderColsDef } from '../header-col-def';
|
import { usePageHeaderColsDef } from '../header-col-def';
|
||||||
@@ -69,7 +68,6 @@ export const VirtualizedPageList = ({
|
|||||||
const currentWorkspace = useService(WorkspaceService).workspace;
|
const currentWorkspace = useService(WorkspaceService).workspace;
|
||||||
const pageMetas = useBlockSuiteDocMeta(currentWorkspace.docCollection);
|
const pageMetas = useBlockSuiteDocMeta(currentWorkspace.docCollection);
|
||||||
const pageOperations = usePageOperationsRenderer();
|
const pageOperations = usePageOperationsRenderer();
|
||||||
const { isPreferredEdgeless } = usePageHelper(currentWorkspace.docCollection);
|
|
||||||
const pageHeaderColsDef = usePageHeaderColsDef();
|
const pageHeaderColsDef = usePageHeaderColsDef();
|
||||||
|
|
||||||
const filteredPageMetas = useFilteredPageMetas(pageMetas, {
|
const filteredPageMetas = useFilteredPageMetas(pageMetas, {
|
||||||
@@ -162,7 +160,6 @@ export const VirtualizedPageList = ({
|
|||||||
onSelectedIdsChange={setSelectedPageIds}
|
onSelectedIdsChange={setSelectedPageIds}
|
||||||
items={pageMetasToRender}
|
items={pageMetasToRender}
|
||||||
rowAsLink
|
rowAsLink
|
||||||
isPreferredEdgeless={isPreferredEdgeless}
|
|
||||||
docCollection={currentWorkspace.docCollection}
|
docCollection={currentWorkspace.docCollection}
|
||||||
operationsRenderer={pageOperationRenderer}
|
operationsRenderer={pageOperationRenderer}
|
||||||
itemRenderer={pageItemRenderer}
|
itemRenderer={pageItemRenderer}
|
||||||
|
|||||||
@@ -201,7 +201,6 @@ export const ItemGroup = <T extends ListItem>({
|
|||||||
const requiredPropNames = [
|
const requiredPropNames = [
|
||||||
'docCollection',
|
'docCollection',
|
||||||
'rowAsLink',
|
'rowAsLink',
|
||||||
'isPreferredEdgeless',
|
|
||||||
'operationsRenderer',
|
'operationsRenderer',
|
||||||
'selectedIds',
|
'selectedIds',
|
||||||
'onSelectedIdsChange',
|
'onSelectedIdsChange',
|
||||||
@@ -294,13 +293,12 @@ const PageTitle = ({ id }: { id: string }) => {
|
|||||||
const UnifiedPageIcon = ({
|
const UnifiedPageIcon = ({
|
||||||
id,
|
id,
|
||||||
docCollection,
|
docCollection,
|
||||||
isPreferredEdgeless,
|
|
||||||
}: {
|
}: {
|
||||||
id: string;
|
id: string;
|
||||||
docCollection: DocCollection;
|
docCollection: DocCollection;
|
||||||
isPreferredEdgeless?: (id: string) => boolean;
|
|
||||||
}) => {
|
}) => {
|
||||||
const isEdgeless = isPreferredEdgeless ? isPreferredEdgeless(id) : false;
|
const list = useService(DocsService).list;
|
||||||
|
const isEdgeless = useLiveData(list.primaryMode$(id)) === 'edgeless';
|
||||||
const { isJournal } = useJournalInfoHelper(docCollection, id);
|
const { isJournal } = useJournalInfoHelper(docCollection, id);
|
||||||
if (isJournal) {
|
if (isJournal) {
|
||||||
return <TodayIcon />;
|
return <TodayIcon />;
|
||||||
@@ -340,13 +338,7 @@ function pageMetaToListItemProp(
|
|||||||
updatedDate: item.updatedDate ? new Date(item.updatedDate) : undefined,
|
updatedDate: item.updatedDate ? new Date(item.updatedDate) : undefined,
|
||||||
to: props.rowAsLink && !props.selectable ? `/${item.id}` : undefined,
|
to: props.rowAsLink && !props.selectable ? `/${item.id}` : undefined,
|
||||||
onClick: toggleSelection,
|
onClick: toggleSelection,
|
||||||
icon: (
|
icon: <UnifiedPageIcon id={item.id} docCollection={props.docCollection} />,
|
||||||
<UnifiedPageIcon
|
|
||||||
id={item.id}
|
|
||||||
docCollection={props.docCollection}
|
|
||||||
isPreferredEdgeless={props.isPreferredEdgeless}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
tags:
|
tags:
|
||||||
item.tags
|
item.tags
|
||||||
?.map(id => tagIdToTagOption(id, props.docCollection))
|
?.map(id => tagIdToTagOption(id, props.docCollection))
|
||||||
|
|||||||
@@ -95,7 +95,6 @@ export interface ListProps<T> {
|
|||||||
className?: string;
|
className?: string;
|
||||||
hideHeader?: boolean; // whether or not to hide the header. default is false (showing header)
|
hideHeader?: boolean; // whether or not to hide the header. default is false (showing header)
|
||||||
groupBy?: ItemGroupDefinition<T>[];
|
groupBy?: ItemGroupDefinition<T>[];
|
||||||
isPreferredEdgeless?: (pageId: string) => boolean; // determines the icon used for each row
|
|
||||||
rowAsLink?: boolean;
|
rowAsLink?: boolean;
|
||||||
selectable?: 'toggle' | boolean; // show selection checkbox. toggle means showing a toggle selection in header on click; boolean == true means showing a selection checkbox for each item
|
selectable?: 'toggle' | boolean; // show selection checkbox. toggle means showing a toggle selection in header on click; boolean == true means showing a selection checkbox for each item
|
||||||
selectedIds?: string[]; // selected page ids
|
selectedIds?: string[]; // selected page ids
|
||||||
|
|||||||
@@ -192,7 +192,6 @@ export const EditCollection = ({
|
|||||||
export type AllPageListConfig = {
|
export type AllPageListConfig = {
|
||||||
allPages: DocMeta[];
|
allPages: DocMeta[];
|
||||||
docCollection: DocCollection;
|
docCollection: DocCollection;
|
||||||
isEdgeless: (id: string) => boolean;
|
|
||||||
/**
|
/**
|
||||||
* Return `undefined` if the page is not public
|
* Return `undefined` if the page is not public
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import {
|
|||||||
ToggleCollapseIcon,
|
ToggleCollapseIcon,
|
||||||
} from '@blocksuite/icons/rc';
|
} from '@blocksuite/icons/rc';
|
||||||
import type { DocMeta } from '@blocksuite/store';
|
import type { DocMeta } from '@blocksuite/store';
|
||||||
import { useLiveData, useService } from '@toeverything/infra';
|
import { DocsService, useLiveData, useService } from '@toeverything/infra';
|
||||||
import { cssVar } from '@toeverything/theme';
|
import { cssVar } from '@toeverything/theme';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import type { ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
@@ -42,6 +42,7 @@ export const RulesMode = ({
|
|||||||
const [showPreview, setShowPreview] = useState(true);
|
const [showPreview, setShowPreview] = useState(true);
|
||||||
const allowListPages: DocMeta[] = [];
|
const allowListPages: DocMeta[] = [];
|
||||||
const rulesPages: DocMeta[] = [];
|
const rulesPages: DocMeta[] = [];
|
||||||
|
const docsService = useService(DocsService);
|
||||||
const favAdapter = useService(CompatibleFavoriteItemsAdapter);
|
const favAdapter = useService(CompatibleFavoriteItemsAdapter);
|
||||||
const favorites = useLiveData(favAdapter.favorites$);
|
const favorites = useLiveData(favAdapter.favorites$);
|
||||||
allPageListConfig.allPages.forEach(meta => {
|
allPageListConfig.allPages.forEach(meta => {
|
||||||
@@ -158,7 +159,8 @@ export const RulesMode = ({
|
|||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{allPageListConfig.isEdgeless(id) ? (
|
{docsService.list.getPrimaryMode(id) ===
|
||||||
|
'edgeless' ? (
|
||||||
<EdgelessIcon style={{ width: 16, height: 16 }} />
|
<EdgelessIcon style={{ width: 16, height: 16 }} />
|
||||||
) : (
|
) : (
|
||||||
<PageIcon style={{ width: 16, height: 16 }} />
|
<PageIcon style={{ width: 16, height: 16 }} />
|
||||||
@@ -211,7 +213,6 @@ export const RulesMode = ({
|
|||||||
className={styles.resultPages}
|
className={styles.resultPages}
|
||||||
items={rulesPages}
|
items={rulesPages}
|
||||||
docCollection={allPageListConfig.docCollection}
|
docCollection={allPageListConfig.docCollection}
|
||||||
isPreferredEdgeless={allPageListConfig.isEdgeless}
|
|
||||||
operationsRenderer={operationsRenderer}
|
operationsRenderer={operationsRenderer}
|
||||||
></List>
|
></List>
|
||||||
) : (
|
) : (
|
||||||
@@ -230,7 +231,6 @@ export const RulesMode = ({
|
|||||||
className={styles.resultPages}
|
className={styles.resultPages}
|
||||||
items={allowListPages}
|
items={allowListPages}
|
||||||
docCollection={allPageListConfig.docCollection}
|
docCollection={allPageListConfig.docCollection}
|
||||||
isPreferredEdgeless={allPageListConfig.isEdgeless}
|
|
||||||
operationsRenderer={operationsRenderer}
|
operationsRenderer={operationsRenderer}
|
||||||
></List>
|
></List>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import { Trans, useI18n } from '@affine/i18n';
|
|||||||
import { FilterIcon } from '@blocksuite/icons/rc';
|
import { FilterIcon } from '@blocksuite/icons/rc';
|
||||||
import type { DocMeta } from '@blocksuite/store';
|
import type { DocMeta } from '@blocksuite/store';
|
||||||
import {
|
import {
|
||||||
DocsService,
|
|
||||||
useLiveData,
|
useLiveData,
|
||||||
useServices,
|
useServices,
|
||||||
WorkspaceService,
|
WorkspaceService,
|
||||||
@@ -57,17 +56,12 @@ export const SelectPage = ({
|
|||||||
const clearSelected = useCallback(() => {
|
const clearSelected = useCallback(() => {
|
||||||
onChange([]);
|
onChange([]);
|
||||||
}, [onChange]);
|
}, [onChange]);
|
||||||
const {
|
const { workspaceService, compatibleFavoriteItemsAdapter, shareDocsService } =
|
||||||
workspaceService,
|
useServices({
|
||||||
compatibleFavoriteItemsAdapter,
|
ShareDocsService,
|
||||||
shareDocsService,
|
WorkspaceService,
|
||||||
docsService,
|
CompatibleFavoriteItemsAdapter,
|
||||||
} = useServices({
|
});
|
||||||
DocsService,
|
|
||||||
ShareDocsService,
|
|
||||||
WorkspaceService,
|
|
||||||
CompatibleFavoriteItemsAdapter,
|
|
||||||
});
|
|
||||||
const shareDocs = useLiveData(shareDocsService.shareDocs?.list$);
|
const shareDocs = useLiveData(shareDocsService.shareDocs?.list$);
|
||||||
const workspace = workspaceService.workspace;
|
const workspace = workspaceService.workspace;
|
||||||
const docCollection = workspace.docCollection;
|
const docCollection = workspace.docCollection;
|
||||||
@@ -97,13 +91,6 @@ export const SelectPage = ({
|
|||||||
[favourites]
|
[favourites]
|
||||||
);
|
);
|
||||||
|
|
||||||
const isEdgeless = useCallback(
|
|
||||||
(id: string) => {
|
|
||||||
return docsService.list.doc$(id).value?.mode$.value === 'edgeless';
|
|
||||||
},
|
|
||||||
[docsService.list]
|
|
||||||
);
|
|
||||||
|
|
||||||
const onToggleFavoritePage = useCallback(
|
const onToggleFavoritePage = useCallback(
|
||||||
(page: DocMeta) => {
|
(page: DocMeta) => {
|
||||||
const status = isFavorite(page);
|
const status = isFavorite(page);
|
||||||
@@ -207,7 +194,6 @@ export const SelectPage = ({
|
|||||||
selectable
|
selectable
|
||||||
onSelectedIdsChange={onChange}
|
onSelectedIdsChange={onChange}
|
||||||
selectedIds={value}
|
selectedIds={value}
|
||||||
isPreferredEdgeless={isEdgeless}
|
|
||||||
operationsRenderer={operationsRenderer}
|
operationsRenderer={operationsRenderer}
|
||||||
itemRenderer={pageItemRenderer}
|
itemRenderer={pageItemRenderer}
|
||||||
headerRenderer={pageHeaderRenderer}
|
headerRenderer={pageHeaderRenderer}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import type { DocMeta } from '@blocksuite/store';
|
|||||||
import { useService, WorkspaceService } from '@toeverything/infra';
|
import { useService, WorkspaceService } from '@toeverything/infra';
|
||||||
import { useCallback, useMemo, useRef, useState } from 'react';
|
import { useCallback, useMemo, useRef, useState } from 'react';
|
||||||
|
|
||||||
import { usePageHelper } from '../blocksuite/block-suite-page-list/utils';
|
|
||||||
import { ListFloatingToolbar } from './components/list-floating-toolbar';
|
import { ListFloatingToolbar } from './components/list-floating-toolbar';
|
||||||
import { usePageHeaderColsDef } from './header-col-def';
|
import { usePageHeaderColsDef } from './header-col-def';
|
||||||
import { TrashOperationCell } from './operation-cell';
|
import { TrashOperationCell } from './operation-cell';
|
||||||
@@ -26,8 +25,6 @@ export const VirtualizedTrashList = () => {
|
|||||||
trash: true,
|
trash: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { isPreferredEdgeless } = usePageHelper(docCollection);
|
|
||||||
|
|
||||||
const listRef = useRef<ItemListHandle>(null);
|
const listRef = useRef<ItemListHandle>(null);
|
||||||
const [showFloatingToolbar, setShowFloatingToolbar] = useState(false);
|
const [showFloatingToolbar, setShowFloatingToolbar] = useState(false);
|
||||||
const [selectedPageIds, setSelectedPageIds] = useState<string[]>([]);
|
const [selectedPageIds, setSelectedPageIds] = useState<string[]>([]);
|
||||||
@@ -121,7 +118,6 @@ export const VirtualizedTrashList = () => {
|
|||||||
selectable="toggle"
|
selectable="toggle"
|
||||||
items={filteredPageMetas}
|
items={filteredPageMetas}
|
||||||
rowAsLink
|
rowAsLink
|
||||||
isPreferredEdgeless={isPreferredEdgeless}
|
|
||||||
onSelectionActiveChange={setShowFloatingToolbar}
|
onSelectionActiveChange={setShowFloatingToolbar}
|
||||||
docCollection={currentWorkspace.docCollection}
|
docCollection={currentWorkspace.docCollection}
|
||||||
operationsRenderer={pageOperationsRenderer}
|
operationsRenderer={pageOperationsRenderer}
|
||||||
|
|||||||
@@ -3,10 +3,9 @@ import { popupWindow } from '@affine/core/utils';
|
|||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import { CloseIcon, NewIcon } from '@blocksuite/icons/rc';
|
import { CloseIcon, NewIcon } from '@blocksuite/icons/rc';
|
||||||
import {
|
import {
|
||||||
DocsService,
|
|
||||||
GlobalContextService,
|
GlobalContextService,
|
||||||
useLiveData,
|
useLiveData,
|
||||||
useService,
|
useServices,
|
||||||
} from '@toeverything/infra';
|
} from '@toeverything/infra';
|
||||||
import { useSetAtom } from 'jotai/react';
|
import { useSetAtom } from 'jotai/react';
|
||||||
import { useCallback, useState } from 'react';
|
import { useCallback, useState } from 'react';
|
||||||
@@ -33,12 +32,11 @@ type IslandItemNames = 'whatNew' | 'contact' | 'shortcuts';
|
|||||||
const showList = environment.isDesktop ? DESKTOP_SHOW_LIST : DEFAULT_SHOW_LIST;
|
const showList = environment.isDesktop ? DESKTOP_SHOW_LIST : DEFAULT_SHOW_LIST;
|
||||||
|
|
||||||
export const HelpIsland = () => {
|
export const HelpIsland = () => {
|
||||||
const docId = useLiveData(
|
const { globalContextService } = useServices({
|
||||||
useService(GlobalContextService).globalContext.docId.$
|
GlobalContextService,
|
||||||
);
|
});
|
||||||
const docRecordList = useService(DocsService).list;
|
const docId = useLiveData(globalContextService.globalContext.docId.$);
|
||||||
const doc = useLiveData(docId ? docRecordList.doc$(docId) : undefined);
|
const docMode = useLiveData(globalContextService.globalContext.docMode.$);
|
||||||
const mode = useLiveData(doc?.mode$);
|
|
||||||
const setOpenSettingModalAtom = useSetAtom(openSettingModalAtom);
|
const setOpenSettingModalAtom = useSetAtom(openSettingModalAtom);
|
||||||
const [spread, setShowSpread] = useState(false);
|
const [spread, setShowSpread] = useState(false);
|
||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
@@ -69,7 +67,7 @@ export const HelpIsland = () => {
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
setShowSpread(!spread);
|
setShowSpread(!spread);
|
||||||
}}
|
}}
|
||||||
inEdgelessPage={!!docId && mode === 'edgeless'}
|
inEdgelessPage={!!docId && docMode === 'edgeless'}
|
||||||
>
|
>
|
||||||
<StyledAnimateWrapper
|
<StyledAnimateWrapper
|
||||||
style={{ height: spread ? `${showList.length * 40 + 4}px` : 0 }}
|
style={{ height: spread ? `${showList.length * 40 + 4}px` : 0 }}
|
||||||
|
|||||||
@@ -10,8 +10,6 @@ import type { DocMeta } from '@blocksuite/store';
|
|||||||
import { useLiveData, useService, WorkspaceService } from '@toeverything/infra';
|
import { useLiveData, useService, WorkspaceService } from '@toeverything/infra';
|
||||||
import { useCallback, useEffect, useMemo } from 'react';
|
import { useCallback, useEffect, useMemo } from 'react';
|
||||||
|
|
||||||
import { usePageHelper } from '../../components/blocksuite/block-suite-page-list/utils';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated very poor performance
|
* @deprecated very poor performance
|
||||||
*/
|
*/
|
||||||
@@ -27,7 +25,6 @@ export const useAllPageListConfig = () => {
|
|||||||
|
|
||||||
const workspace = currentWorkspace.docCollection;
|
const workspace = currentWorkspace.docCollection;
|
||||||
const pageMetas = useBlockSuiteDocMeta(workspace);
|
const pageMetas = useBlockSuiteDocMeta(workspace);
|
||||||
const { isPreferredEdgeless } = usePageHelper(workspace);
|
|
||||||
const pageMap = useMemo(
|
const pageMap = useMemo(
|
||||||
() => Object.fromEntries(pageMetas.map(page => [page.id, page])),
|
() => Object.fromEntries(pageMetas.map(page => [page.id, page])),
|
||||||
[pageMetas]
|
[pageMetas]
|
||||||
@@ -58,7 +55,6 @@ export const useAllPageListConfig = () => {
|
|||||||
return useMemo<AllPageListConfig>(() => {
|
return useMemo<AllPageListConfig>(() => {
|
||||||
return {
|
return {
|
||||||
allPages: pageMetas,
|
allPages: pageMetas,
|
||||||
isEdgeless: isPreferredEdgeless,
|
|
||||||
getPublicMode(id) {
|
getPublicMode(id) {
|
||||||
const mode = shareDocs?.find(shareDoc => shareDoc.id === id)?.mode;
|
const mode = shareDocs?.find(shareDoc => shareDoc.id === id)?.mode;
|
||||||
if (mode === PublicPageMode.Edgeless) {
|
if (mode === PublicPageMode.Edgeless) {
|
||||||
@@ -83,7 +79,6 @@ export const useAllPageListConfig = () => {
|
|||||||
};
|
};
|
||||||
}, [
|
}, [
|
||||||
pageMetas,
|
pageMetas,
|
||||||
isPreferredEdgeless,
|
|
||||||
currentWorkspace.docCollection,
|
currentWorkspace.docCollection,
|
||||||
shareDocs,
|
shareDocs,
|
||||||
pageMap,
|
pageMap,
|
||||||
|
|||||||
@@ -75,7 +75,8 @@ export function useBlockSuiteMetaHelper(docCollection: DocCollection) {
|
|||||||
|
|
||||||
const duplicate = useAsyncCallback(
|
const duplicate = useAsyncCallback(
|
||||||
async (pageId: string, openPageAfterDuplication: boolean = true) => {
|
async (pageId: string, openPageAfterDuplication: boolean = true) => {
|
||||||
const currentPageMode = pageRecordList.doc$(pageId).value?.mode$.value;
|
const currentPagePrimaryMode =
|
||||||
|
pageRecordList.doc$(pageId).value?.primaryMode$.value;
|
||||||
const currentPageMeta = getDocMeta(pageId);
|
const currentPageMeta = getDocMeta(pageId);
|
||||||
const newPage = createDoc();
|
const newPage = createDoc();
|
||||||
const currentPage = docCollection.getDoc(pageId);
|
const currentPage = docCollection.getDoc(pageId);
|
||||||
@@ -99,7 +100,9 @@ export function useBlockSuiteMetaHelper(docCollection: DocCollection) {
|
|||||||
const newPageTitle =
|
const newPageTitle =
|
||||||
currentPageMeta.title.replace(lastDigitRegex, '') + `(${newNumber})`;
|
currentPageMeta.title.replace(lastDigitRegex, '') + `(${newNumber})`;
|
||||||
|
|
||||||
pageRecordList.doc$(newPage.id).value?.setMode(currentPageMode || 'page');
|
pageRecordList
|
||||||
|
.doc$(newPage.id)
|
||||||
|
.value?.setPrimaryMode(currentPagePrimaryMode || 'page');
|
||||||
setDocTitle(newPage.id, newPageTitle);
|
setDocTitle(newPage.id, newPageTitle);
|
||||||
openPageAfterDuplication && openPage(docCollection.id, newPage.id);
|
openPageAfterDuplication && openPage(docCollection.id, newPage.id);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
registerAffineCommand,
|
registerAffineCommand,
|
||||||
} from '@affine/core/commands';
|
} from '@affine/core/commands';
|
||||||
import { track } from '@affine/core/mixpanel';
|
import { track } from '@affine/core/mixpanel';
|
||||||
|
import type { Editor } from '@affine/core/modules/editor';
|
||||||
import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/properties';
|
import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/properties';
|
||||||
import { WorkspaceFlavour } from '@affine/env/workspace';
|
import { WorkspaceFlavour } from '@affine/env/workspace';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
@@ -23,10 +24,10 @@ import { useBlockSuiteMetaHelper } from './use-block-suite-meta-helper';
|
|||||||
import { useExportPage } from './use-export-page';
|
import { useExportPage } from './use-export-page';
|
||||||
import { useTrashModalHelper } from './use-trash-modal-helper';
|
import { useTrashModalHelper } from './use-trash-modal-helper';
|
||||||
|
|
||||||
export function useRegisterBlocksuiteEditorCommands() {
|
export function useRegisterBlocksuiteEditorCommands(editor: Editor) {
|
||||||
const doc = useService(DocService).doc;
|
const doc = useService(DocService).doc;
|
||||||
const docId = doc.id;
|
const docId = doc.id;
|
||||||
const mode = useLiveData(doc.mode$);
|
const mode = useLiveData(editor.mode$);
|
||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
const workspace = useService(WorkspaceService).workspace;
|
const workspace = useService(WorkspaceService).workspace;
|
||||||
const docCollection = workspace.docCollection;
|
const docCollection = workspace.docCollection;
|
||||||
@@ -149,7 +150,7 @@ export function useRegisterBlocksuiteEditorCommands() {
|
|||||||
mode: mode === 'page' ? 'edgeless' : 'page',
|
mode: mode === 'page' ? 'edgeless' : 'page',
|
||||||
});
|
});
|
||||||
|
|
||||||
doc.toggleMode();
|
editor.toggleMode();
|
||||||
toast(
|
toast(
|
||||||
mode === 'page'
|
mode === 'page'
|
||||||
? t['com.affine.toastMessage.edgelessMode']()
|
? t['com.affine.toastMessage.edgelessMode']()
|
||||||
@@ -311,6 +312,7 @@ export function useRegisterBlocksuiteEditorCommands() {
|
|||||||
unsubs.forEach(unsub => unsub());
|
unsubs.forEach(unsub => unsub());
|
||||||
};
|
};
|
||||||
}, [
|
}, [
|
||||||
|
editor,
|
||||||
favorite,
|
favorite,
|
||||||
mode,
|
mode,
|
||||||
onClickDelete,
|
onClickDelete,
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ const WorkspaceLayoutProviders = ({ children }: PropsWithChildren) => {
|
|||||||
timeout(10000 /* 10s */),
|
timeout(10000 /* 10s */),
|
||||||
mergeMap(({ mode, doc }) => {
|
mergeMap(({ mode, doc }) => {
|
||||||
if (doc) {
|
if (doc) {
|
||||||
docsList.setMode(doc.id, mode as DocMode);
|
docsList.setPrimaryMode(doc.id, mode as DocMode);
|
||||||
workbench.openDoc(doc.id);
|
workbench.openDoc(doc.id);
|
||||||
}
|
}
|
||||||
return EMPTY;
|
return EMPTY;
|
||||||
|
|||||||
31
packages/frontend/core/src/modules/editor/entities/editor.ts
Normal file
31
packages/frontend/core/src/modules/editor/entities/editor.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import type { DocMode } from '@blocksuite/blocks';
|
||||||
|
import type { DocService, WorkspaceService } from '@toeverything/infra';
|
||||||
|
import { Entity, LiveData } from '@toeverything/infra';
|
||||||
|
|
||||||
|
import { EditorScope } from '../scopes/editor';
|
||||||
|
|
||||||
|
export class Editor extends Entity<{ defaultMode: DocMode }> {
|
||||||
|
readonly scope = this.framework.createScope(EditorScope, {
|
||||||
|
editor: this as Editor,
|
||||||
|
});
|
||||||
|
|
||||||
|
readonly mode$ = new LiveData(this.props.defaultMode);
|
||||||
|
readonly doc = this.docService.doc;
|
||||||
|
readonly isSharedMode =
|
||||||
|
this.workspaceService.workspace.openOptions.isSharedMode;
|
||||||
|
|
||||||
|
toggleMode() {
|
||||||
|
this.mode$.next(this.mode$.value === 'edgeless' ? 'page' : 'edgeless');
|
||||||
|
}
|
||||||
|
|
||||||
|
setMode(mode: DocMode) {
|
||||||
|
this.mode$.next(mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private readonly docService: DocService,
|
||||||
|
private readonly workspaceService: WorkspaceService
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
}
|
||||||
27
packages/frontend/core/src/modules/editor/index.ts
Normal file
27
packages/frontend/core/src/modules/editor/index.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import {
|
||||||
|
DocScope,
|
||||||
|
DocService,
|
||||||
|
type Framework,
|
||||||
|
WorkspaceScope,
|
||||||
|
WorkspaceService,
|
||||||
|
} from '@toeverything/infra';
|
||||||
|
|
||||||
|
import { Editor } from './entities/editor';
|
||||||
|
import { EditorScope } from './scopes/editor';
|
||||||
|
import { EditorService } from './services/editor';
|
||||||
|
import { EditorsService } from './services/editors';
|
||||||
|
|
||||||
|
export { Editor } from './entities/editor';
|
||||||
|
export { EditorScope } from './scopes/editor';
|
||||||
|
export { EditorService } from './services/editor';
|
||||||
|
export { EditorsService } from './services/editors';
|
||||||
|
|
||||||
|
export function configureEditorModule(framework: Framework) {
|
||||||
|
framework
|
||||||
|
.scope(WorkspaceScope)
|
||||||
|
.scope(DocScope)
|
||||||
|
.service(EditorsService)
|
||||||
|
.entity(Editor, [DocService, WorkspaceService])
|
||||||
|
.scope(EditorScope)
|
||||||
|
.service(EditorService, [EditorScope]);
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import { Scope } from '@toeverything/infra';
|
||||||
|
|
||||||
|
import type { Editor } from '../entities/editor';
|
||||||
|
|
||||||
|
export class EditorScope extends Scope<{
|
||||||
|
editor: Editor;
|
||||||
|
}> {}
|
||||||
11
packages/frontend/core/src/modules/editor/services/editor.ts
Normal file
11
packages/frontend/core/src/modules/editor/services/editor.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { Service } from '@toeverything/infra';
|
||||||
|
|
||||||
|
import type { EditorScope } from '../scopes/editor';
|
||||||
|
|
||||||
|
export class EditorService extends Service {
|
||||||
|
readonly editor = this.scope.props.editor;
|
||||||
|
|
||||||
|
constructor(readonly scope: EditorScope) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import type { DocMode } from '@blocksuite/blocks';
|
||||||
|
import { Service } from '@toeverything/infra';
|
||||||
|
|
||||||
|
import { Editor } from '../entities/editor';
|
||||||
|
|
||||||
|
export class EditorsService extends Service {
|
||||||
|
createEditor(defaultMode: DocMode) {
|
||||||
|
return this.framework.createEntity(Editor, { defaultMode });
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -56,25 +56,25 @@ export const ExplorerDocNode = ({
|
|||||||
const [collapsed, setCollapsed] = useState(true);
|
const [collapsed, setCollapsed] = useState(true);
|
||||||
|
|
||||||
const docRecord = useLiveData(docsService.list.doc$(docId));
|
const docRecord = useLiveData(docsService.list.doc$(docId));
|
||||||
const docMode = useLiveData(docRecord?.mode$);
|
const docPrimaryMode = useLiveData(docRecord?.primaryMode$);
|
||||||
const docTitle = useLiveData(docRecord?.title$);
|
const docTitle = useLiveData(docRecord?.title$);
|
||||||
const isInTrash = useLiveData(docRecord?.trash$);
|
const isInTrash = useLiveData(docRecord?.trash$);
|
||||||
|
|
||||||
const Icon = useCallback(
|
const Icon = useCallback(
|
||||||
({ className }: { className?: string }) => {
|
({ className }: { className?: string }) => {
|
||||||
return isLinked ? (
|
return isLinked ? (
|
||||||
docMode === 'edgeless' ? (
|
docPrimaryMode === 'edgeless' ? (
|
||||||
<LinkedEdgelessIcon className={className} />
|
<LinkedEdgelessIcon className={className} />
|
||||||
) : (
|
) : (
|
||||||
<LinkedPageIcon className={className} />
|
<LinkedPageIcon className={className} />
|
||||||
)
|
)
|
||||||
) : docMode === 'edgeless' ? (
|
) : docPrimaryMode === 'edgeless' ? (
|
||||||
<EdgelessIcon className={className} />
|
<EdgelessIcon className={className} />
|
||||||
) : (
|
) : (
|
||||||
<PageIcon className={className} />
|
<PageIcon className={className} />
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
[docMode, isLinked]
|
[docPrimaryMode, isLinked]
|
||||||
);
|
);
|
||||||
|
|
||||||
const children = useLiveData(
|
const children = useLiveData(
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { configureCloudModule } from './cloud';
|
|||||||
import { configureCollectionModule } from './collection';
|
import { configureCollectionModule } from './collection';
|
||||||
import { configureDocLinksModule } from './doc-link';
|
import { configureDocLinksModule } from './doc-link';
|
||||||
import { configureDocsSearchModule } from './docs-search';
|
import { configureDocsSearchModule } from './docs-search';
|
||||||
|
import { configureEditorModule } from './editor';
|
||||||
import { configureExplorerModule } from './explorer';
|
import { configureExplorerModule } from './explorer';
|
||||||
import { configureFavoriteModule } from './favorite';
|
import { configureFavoriteModule } from './favorite';
|
||||||
import { configureFindInPageModule } from './find-in-page';
|
import { configureFindInPageModule } from './find-in-page';
|
||||||
@@ -39,4 +40,5 @@ export function configureCommonModules(framework: Framework) {
|
|||||||
configureFavoriteModule(framework);
|
configureFavoriteModule(framework);
|
||||||
configureExplorerModule(framework);
|
configureExplorerModule(framework);
|
||||||
configureThemeEditorModule(framework);
|
configureThemeEditorModule(framework);
|
||||||
|
configureEditorModule(framework);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import { useCallback, useEffect, useState } from 'react';
|
|||||||
|
|
||||||
import { WorkbenchService } from '../../../workbench';
|
import { WorkbenchService } from '../../../workbench';
|
||||||
import { PeekViewService } from '../../services/peek-view';
|
import { PeekViewService } from '../../services/peek-view';
|
||||||
import { useDoc } from '../utils';
|
import { useEditor } from '../utils';
|
||||||
import * as styles from './doc-peek-view.css';
|
import * as styles from './doc-peek-view.css';
|
||||||
|
|
||||||
const logger = new DebugLogger('doc-peek-view');
|
const logger = new DebugLogger('doc-peek-view');
|
||||||
@@ -69,33 +69,36 @@ export function DocPeekPreview({
|
|||||||
mode?: DocMode;
|
mode?: DocMode;
|
||||||
xywh?: `[${number},${number},${number},${number}]`;
|
xywh?: `[${number},${number},${number},${number}]`;
|
||||||
}) {
|
}) {
|
||||||
const { doc, workspace, loading } = useDoc(docId);
|
const { doc, workspace, loading } = useEditor(docId, mode);
|
||||||
const { jumpToTag } = useNavigateHelper();
|
const { jumpToTag } = useNavigateHelper();
|
||||||
const workbench = useService(WorkbenchService).workbench;
|
const workbench = useService(WorkbenchService).workbench;
|
||||||
const peekView = useService(PeekViewService).peekView;
|
const peekView = useService(PeekViewService).peekView;
|
||||||
const [editor, setEditor] = useState<AffineEditorContainer | null>(null);
|
const [editorElement, setEditorElement] =
|
||||||
|
useState<AffineEditorContainer | null>(null);
|
||||||
|
|
||||||
const onRef = (editor: AffineEditorContainer) => {
|
const onRef = (editor: AffineEditorContainer) => {
|
||||||
setEditor(editor);
|
setEditorElement(editor);
|
||||||
};
|
};
|
||||||
|
|
||||||
const docs = useService(DocsService);
|
const docs = useService(DocsService);
|
||||||
const [resolvedMode, setResolvedMode] = useState<DocMode | undefined>(mode);
|
const [resolvedMode, setResolvedMode] = useState<DocMode | undefined>(mode);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
editor?.updateComplete
|
editorElement?.updateComplete
|
||||||
.then(() => {
|
.then(() => {
|
||||||
if (resolvedMode === 'edgeless') {
|
if (resolvedMode === 'edgeless') {
|
||||||
fitViewport(editor, xywh);
|
fitViewport(editorElement, xywh);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(console.error);
|
.catch(console.error);
|
||||||
return;
|
return;
|
||||||
}, [editor, resolvedMode, xywh]);
|
}, [editorElement, resolvedMode, xywh]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!mode || !resolvedMode) {
|
if (!mode || !resolvedMode) {
|
||||||
setResolvedMode(docs.list.doc$(docId).value?.mode$.value || 'page');
|
setResolvedMode(
|
||||||
|
docs.list.doc$(docId).value?.primaryMode$.value || 'page'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}, [docId, docs.list, resolvedMode, mode]);
|
}, [docId, docs.list, resolvedMode, mode]);
|
||||||
|
|
||||||
@@ -115,14 +118,15 @@ export function DocPeekPreview({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const disposableGroup = new DisposableGroup();
|
const disposableGroup = new DisposableGroup();
|
||||||
if (editor) {
|
if (editorElement) {
|
||||||
editor.updateComplete
|
editorElement.updateComplete
|
||||||
.then(() => {
|
.then(() => {
|
||||||
if (!editor.host) {
|
if (!editorElement.host) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const rootService = editor.host.std.spec.getService('affine:page');
|
const rootService =
|
||||||
|
editorElement.host.std.spec.getService('affine:page');
|
||||||
// doc change event inside peek view should be handled by peek view
|
// doc change event inside peek view should be handled by peek view
|
||||||
disposableGroup.add(
|
disposableGroup.add(
|
||||||
rootService.slots.docLinkClicked.on(({ docId, blockId }) => {
|
rootService.slots.docLinkClicked.on(({ docId, blockId }) => {
|
||||||
@@ -142,7 +146,7 @@ export function DocPeekPreview({
|
|||||||
return () => {
|
return () => {
|
||||||
disposableGroup.dispose();
|
disposableGroup.dispose();
|
||||||
};
|
};
|
||||||
}, [editor, jumpToTag, peekView, workspace.id]);
|
}, [editorElement, jumpToTag, peekView, workspace.id]);
|
||||||
|
|
||||||
const openOutlinePanel = useCallback(() => {
|
const openOutlinePanel = useCallback(() => {
|
||||||
workbench.openDoc(docId);
|
workbench.openDoc(docId);
|
||||||
@@ -176,7 +180,7 @@ export function DocPeekPreview({
|
|||||||
/>
|
/>
|
||||||
</FrameworkScope>
|
</FrameworkScope>
|
||||||
<EditorOutlineViewer
|
<EditorOutlineViewer
|
||||||
editor={editor}
|
editor={editorElement}
|
||||||
show={resolvedMode === 'page'}
|
show={resolvedMode === 'page'}
|
||||||
openOutlinePanel={openOutlinePanel}
|
openOutlinePanel={openOutlinePanel}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ import { ErrorBoundary } from 'react-error-boundary';
|
|||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
|
|
||||||
import { PeekViewService } from '../../services/peek-view';
|
import { PeekViewService } from '../../services/peek-view';
|
||||||
import { useDoc } from '../utils';
|
import { useEditor } from '../utils';
|
||||||
import { useZoomControls } from './hooks/use-zoom';
|
import { useZoomControls } from './hooks/use-zoom';
|
||||||
import * as styles from './index.css';
|
import * as styles from './index.css';
|
||||||
|
|
||||||
@@ -108,7 +108,7 @@ const ImagePreviewModalImpl = ({
|
|||||||
onBlockIdChange: (blockId: string) => void;
|
onBlockIdChange: (blockId: string) => void;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
}): ReactElement | null => {
|
}): ReactElement | null => {
|
||||||
const { doc, workspace } = useDoc(docId);
|
const { doc, workspace } = useEditor(docId);
|
||||||
const blocksuiteDoc = doc?.blockSuiteDoc;
|
const blocksuiteDoc = doc?.blockSuiteDoc;
|
||||||
const docCollection = workspace.docCollection;
|
const docCollection = workspace.docCollection;
|
||||||
const blockModel = useMemo(() => {
|
const blockModel = useMemo(() => {
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import {
|
|||||||
import { WorkbenchService } from '../../workbench';
|
import { WorkbenchService } from '../../workbench';
|
||||||
import { PeekViewService } from '../services/peek-view';
|
import { PeekViewService } from '../services/peek-view';
|
||||||
import * as styles from './peek-view-controls.css';
|
import * as styles from './peek-view-controls.css';
|
||||||
import { useDoc } from './utils';
|
import { useEditor } from './utils';
|
||||||
|
|
||||||
type ControlButtonProps = {
|
type ControlButtonProps = {
|
||||||
nameKey: string;
|
nameKey: string;
|
||||||
@@ -100,7 +100,7 @@ export const DocPeekViewControls = ({
|
|||||||
const workbench = useService(WorkbenchService).workbench;
|
const workbench = useService(WorkbenchService).workbench;
|
||||||
const { jumpToPageBlock } = useNavigateHelper();
|
const { jumpToPageBlock } = useNavigateHelper();
|
||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
const { doc, workspace } = useDoc(docId);
|
const { doc, workspace } = useEditor(docId);
|
||||||
const controls = useMemo(() => {
|
const controls = useMemo(() => {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
@@ -115,12 +115,15 @@ export const DocPeekViewControls = ({
|
|||||||
nameKey: 'open',
|
nameKey: 'open',
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
// TODO(@Peng): for frame blocks, we should mimic "view in edgeless" button behavior
|
// TODO(@Peng): for frame blocks, we should mimic "view in edgeless" button behavior
|
||||||
|
if (mode) {
|
||||||
|
// TODO(@eyhn): change this to use mode link
|
||||||
|
doc?.setPrimaryMode(mode);
|
||||||
|
}
|
||||||
|
|
||||||
blockId
|
blockId
|
||||||
? jumpToPageBlock(workspace.id, docId, blockId)
|
? jumpToPageBlock(workspace.id, docId, blockId)
|
||||||
: workbench.openDoc(docId);
|
: workbench.openDoc(docId);
|
||||||
if (mode) {
|
|
||||||
doc?.setMode(mode);
|
|
||||||
}
|
|
||||||
peekView.close('none');
|
peekView.close('none');
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,20 +1,24 @@
|
|||||||
import type { Doc } from '@toeverything/infra';
|
import type { Doc, DocMode } from '@toeverything/infra';
|
||||||
import {
|
import {
|
||||||
DocsService,
|
DocsService,
|
||||||
useLiveData,
|
useLiveData,
|
||||||
useService,
|
useService,
|
||||||
WorkspaceService,
|
WorkspaceService,
|
||||||
} from '@toeverything/infra';
|
} from '@toeverything/infra';
|
||||||
import { useEffect, useLayoutEffect, useState } from 'react';
|
import { useEffect, useLayoutEffect, useRef, useState } from 'react';
|
||||||
|
|
||||||
export const useDoc = (pageId: string) => {
|
import { type Editor, EditorsService } from '../../editor';
|
||||||
|
|
||||||
|
export const useEditor = (pageId: string, preferMode?: DocMode) => {
|
||||||
const currentWorkspace = useService(WorkspaceService).workspace;
|
const currentWorkspace = useService(WorkspaceService).workspace;
|
||||||
const docsService = useService(DocsService);
|
const docsService = useService(DocsService);
|
||||||
const docRecordList = docsService.list;
|
const docRecordList = docsService.list;
|
||||||
const docListReady = useLiveData(docRecordList.isReady$);
|
const docListReady = useLiveData(docRecordList.isReady$);
|
||||||
const docRecord = docRecordList.doc$(pageId).value;
|
const docRecord = docRecordList.doc$(pageId).value;
|
||||||
|
const preferModeRef = useRef(preferMode);
|
||||||
|
|
||||||
const [doc, setDoc] = useState<Doc | null>(null);
|
const [doc, setDoc] = useState<Doc | null>(null);
|
||||||
|
const [editor, setEditor] = useState<Editor | null>(null);
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
if (!docRecord) {
|
if (!docRecord) {
|
||||||
@@ -27,6 +31,19 @@ export const useDoc = (pageId: string) => {
|
|||||||
};
|
};
|
||||||
}, [docRecord, docsService, pageId]);
|
}, [docRecord, docsService, pageId]);
|
||||||
|
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
if (!doc) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const editor = doc.scope
|
||||||
|
.get(EditorsService)
|
||||||
|
.createEditor(preferModeRef.current || doc.primaryMode$.value);
|
||||||
|
setEditor(editor);
|
||||||
|
return () => {
|
||||||
|
editor.dispose();
|
||||||
|
};
|
||||||
|
}, [doc]);
|
||||||
|
|
||||||
// set sync engine priority target
|
// set sync engine priority target
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
currentWorkspace.engine.doc.setPriority(pageId, 10);
|
currentWorkspace.engine.doc.setPriority(pageId, 10);
|
||||||
@@ -35,5 +52,5 @@ export const useDoc = (pageId: string) => {
|
|||||||
};
|
};
|
||||||
}, [currentWorkspace, pageId]);
|
}, [currentWorkspace, pageId]);
|
||||||
|
|
||||||
return { doc, workspace: currentWorkspace, loading: !docListReady };
|
return { doc, editor, workspace: currentWorkspace, loading: !docListReady };
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -63,13 +63,13 @@ export class CMDKQuickSearchService extends Service {
|
|||||||
} else if (result.source === 'creation') {
|
} else if (result.source === 'creation') {
|
||||||
if (result.id === 'creation:create-page') {
|
if (result.id === 'creation:create-page') {
|
||||||
const newDoc = this.docsService.createDoc({
|
const newDoc = this.docsService.createDoc({
|
||||||
mode: 'page',
|
primaryMode: 'page',
|
||||||
title: result.payload.title,
|
title: result.payload.title,
|
||||||
});
|
});
|
||||||
this.workbenchService.workbench.openDoc(newDoc.id);
|
this.workbenchService.workbench.openDoc(newDoc.id);
|
||||||
} else if (result.id === 'creation:create-edgeless') {
|
} else if (result.id === 'creation:create-edgeless') {
|
||||||
const newDoc = this.docsService.createDoc({
|
const newDoc = this.docsService.createDoc({
|
||||||
mode: 'edgeless',
|
primaryMode: 'edgeless',
|
||||||
title: result.payload.title,
|
title: result.payload.title,
|
||||||
});
|
});
|
||||||
this.workbenchService.workbench.openDoc(newDoc.id);
|
this.workbenchService.workbench.openDoc(newDoc.id);
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export class DocDisplayMetaService extends Service {
|
|||||||
);
|
);
|
||||||
const icon = journalDateString
|
const icon = journalDateString
|
||||||
? TodayIcon
|
? TodayIcon
|
||||||
: docRecord.mode$.value === 'edgeless'
|
: docRecord.primaryMode$.value === 'edgeless'
|
||||||
? EdgelessIcon
|
? EdgelessIcon
|
||||||
: PageIcon;
|
: PageIcon;
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { Scrollable } from '@affine/component';
|
|||||||
import { useActiveBlocksuiteEditor } from '@affine/core/hooks/use-block-suite-editor';
|
import { useActiveBlocksuiteEditor } from '@affine/core/hooks/use-block-suite-editor';
|
||||||
import { usePageDocumentTitle } from '@affine/core/hooks/use-global-state';
|
import { usePageDocumentTitle } from '@affine/core/hooks/use-global-state';
|
||||||
import { AuthService } from '@affine/core/modules/cloud';
|
import { AuthService } from '@affine/core/modules/cloud';
|
||||||
|
import { type Editor, EditorsService } from '@affine/core/modules/editor';
|
||||||
import { WorkspaceFlavour } from '@affine/env/workspace';
|
import { WorkspaceFlavour } from '@affine/env/workspace';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import { noop } from '@blocksuite/global/utils';
|
import { noop } from '@blocksuite/global/utils';
|
||||||
@@ -121,6 +122,7 @@ export const Component = () => {
|
|||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
const [workspace, setWorkspace] = useState<Workspace | null>(null);
|
const [workspace, setWorkspace] = useState<Workspace | null>(null);
|
||||||
const [page, setPage] = useState<Doc | null>(null);
|
const [page, setPage] = useState<Doc | null>(null);
|
||||||
|
const [editor, setEditor] = useState<Editor | null>(null);
|
||||||
const [_, setActiveBlocksuiteEditor] = useActiveBlocksuiteEditor();
|
const [_, setActiveBlocksuiteEditor] = useActiveBlocksuiteEditor();
|
||||||
|
|
||||||
const defaultCloudProvider = workspacesService.framework.get(
|
const defaultCloudProvider = workspacesService.framework.get(
|
||||||
@@ -177,6 +179,10 @@ export const Component = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
setPage(doc);
|
setPage(doc);
|
||||||
|
|
||||||
|
const editor = doc.scope.get(EditorsService).createEditor(publishMode);
|
||||||
|
|
||||||
|
setEditor(editor);
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
@@ -188,6 +194,7 @@ export const Component = () => {
|
|||||||
workspaceArrayBuffer,
|
workspaceArrayBuffer,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
workspacesService,
|
workspacesService,
|
||||||
|
publishMode,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const pageTitle = useLiveData(page?.title$);
|
const pageTitle = useLiveData(page?.title$);
|
||||||
@@ -204,58 +211,60 @@ export const Component = () => {
|
|||||||
[setActiveBlocksuiteEditor]
|
[setActiveBlocksuiteEditor]
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!workspace || !page) {
|
if (!workspace || !page || !editor) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FrameworkScope scope={workspace.scope}>
|
<FrameworkScope scope={workspace.scope}>
|
||||||
<FrameworkScope scope={page.scope}>
|
<FrameworkScope scope={page.scope}>
|
||||||
<AppContainer>
|
<FrameworkScope scope={editor.scope}>
|
||||||
<MainContainer>
|
<AppContainer>
|
||||||
<div className={styles.root}>
|
<MainContainer>
|
||||||
<div className={styles.mainContainer}>
|
<div className={styles.root}>
|
||||||
<ShareHeader
|
<div className={styles.mainContainer}>
|
||||||
pageId={page.id}
|
<ShareHeader
|
||||||
publishMode={publishMode}
|
pageId={page.id}
|
||||||
docCollection={page.blockSuiteDoc.collection}
|
publishMode={publishMode}
|
||||||
/>
|
docCollection={page.blockSuiteDoc.collection}
|
||||||
<Scrollable.Root>
|
/>
|
||||||
<Scrollable.Viewport
|
<Scrollable.Root>
|
||||||
className={clsx(
|
<Scrollable.Viewport
|
||||||
'affine-page-viewport',
|
className={clsx(
|
||||||
styles.editorContainer
|
'affine-page-viewport',
|
||||||
)}
|
styles.editorContainer
|
||||||
>
|
)}
|
||||||
<PageDetailEditor
|
>
|
||||||
isPublic
|
<PageDetailEditor
|
||||||
publishMode={publishMode}
|
isPublic
|
||||||
docCollection={page.blockSuiteDoc.collection}
|
publishMode={publishMode}
|
||||||
pageId={page.id}
|
docCollection={page.blockSuiteDoc.collection}
|
||||||
onLoad={onEditorLoad}
|
pageId={page.id}
|
||||||
/>
|
onLoad={onEditorLoad}
|
||||||
{publishMode === 'page' ? <ShareFooter /> : null}
|
/>
|
||||||
</Scrollable.Viewport>
|
{publishMode === 'page' ? <ShareFooter /> : null}
|
||||||
<Scrollable.Scrollbar />
|
</Scrollable.Viewport>
|
||||||
</Scrollable.Root>
|
<Scrollable.Scrollbar />
|
||||||
{loginStatus !== 'authenticated' ? (
|
</Scrollable.Root>
|
||||||
<a
|
{loginStatus !== 'authenticated' ? (
|
||||||
href="https://affine.pro"
|
<a
|
||||||
target="_blank"
|
href="https://affine.pro"
|
||||||
className={styles.link}
|
target="_blank"
|
||||||
rel="noreferrer"
|
className={styles.link}
|
||||||
>
|
rel="noreferrer"
|
||||||
<span className={styles.linkText}>
|
>
|
||||||
{t['com.affine.share-page.footer.built-with']()}
|
<span className={styles.linkText}>
|
||||||
</span>
|
{t['com.affine.share-page.footer.built-with']()}
|
||||||
<Logo1Icon fontSize={20} />
|
</span>
|
||||||
</a>
|
<Logo1Icon fontSize={20} />
|
||||||
) : null}
|
</a>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</MainContainer>
|
||||||
</MainContainer>
|
<PeekViewManagerModal />
|
||||||
<PeekViewManagerModal />
|
</AppContainer>
|
||||||
</AppContainer>
|
</FrameworkScope>
|
||||||
</FrameworkScope>
|
</FrameworkScope>
|
||||||
</FrameworkScope>
|
</FrameworkScope>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -17,12 +17,8 @@ export function ShareHeader({
|
|||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className={styles.header}>
|
<div className={styles.header}>
|
||||||
<EditorModeSwitch isPublic pageId={pageId} publicMode={publishMode} />
|
<EditorModeSwitch />
|
||||||
<BlocksuiteHeaderTitle
|
<BlocksuiteHeaderTitle docId={pageId} />
|
||||||
docCollection={docCollection}
|
|
||||||
pageId={pageId}
|
|
||||||
isPublic={true}
|
|
||||||
/>
|
|
||||||
<div className={styles.spacer} />
|
<div className={styles.spacer} />
|
||||||
<ShareHeaderRightItem
|
<ShareHeaderRightItem
|
||||||
workspaceId={docCollection.id}
|
workspaceId={docCollection.id}
|
||||||
|
|||||||
@@ -15,15 +15,10 @@ import { EditorModeSwitch } from '@affine/core/components/blocksuite/block-suite
|
|||||||
import { useRegisterCopyLinkCommands } from '@affine/core/hooks/affine/use-register-copy-link-commands';
|
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 { useDocCollectionPageTitle } from '@affine/core/hooks/use-block-suite-workspace-page-title';
|
||||||
import { useJournalInfoHelper } from '@affine/core/hooks/use-journal';
|
import { useJournalInfoHelper } from '@affine/core/hooks/use-journal';
|
||||||
import { track } from '@affine/core/mixpanel';
|
import { EditorService } from '@affine/core/modules/editor';
|
||||||
import { ViewIcon, ViewTitle } from '@affine/core/modules/workbench';
|
import { ViewIcon, ViewTitle } from '@affine/core/modules/workbench';
|
||||||
import type { Doc } from '@blocksuite/store';
|
import type { Doc } from '@blocksuite/store';
|
||||||
import {
|
import { useLiveData, useService, type Workspace } from '@toeverything/infra';
|
||||||
DocService,
|
|
||||||
useLiveData,
|
|
||||||
useService,
|
|
||||||
type Workspace,
|
|
||||||
} from '@toeverything/infra';
|
|
||||||
import { useAtom, useAtomValue } from 'jotai';
|
import { useAtom, useAtomValue } from 'jotai';
|
||||||
import { forwardRef, useCallback, useEffect, useRef, useState } from 'react';
|
import { forwardRef, useCallback, useEffect, useRef, useState } from 'react';
|
||||||
|
|
||||||
@@ -81,7 +76,7 @@ export function JournalPageHeader({ page, workspace }: PageHeaderProps) {
|
|||||||
<Header className={styles.header} ref={containerRef}>
|
<Header className={styles.header} ref={containerRef}>
|
||||||
<ViewTitle title={title} />
|
<ViewTitle title={title} />
|
||||||
<ViewIcon icon="journal" />
|
<ViewIcon icon="journal" />
|
||||||
<EditorModeSwitch pageId={page?.id} />
|
<EditorModeSwitch />
|
||||||
<div className={styles.journalWeekPicker}>
|
<div className={styles.journalWeekPicker}>
|
||||||
<JournalWeekDatePicker
|
<JournalWeekDatePicker
|
||||||
docCollection={workspace.docCollection}
|
docCollection={workspace.docCollection}
|
||||||
@@ -125,22 +120,17 @@ export function NormalPageHeader({ page, workspace }: PageHeaderProps) {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const title = useDocCollectionPageTitle(workspace.docCollection, page?.id);
|
const title = useDocCollectionPageTitle(workspace.docCollection, page?.id);
|
||||||
const doc = useService(DocService).doc;
|
const editor = useService(EditorService).editor;
|
||||||
const currentMode = useLiveData(doc.mode$);
|
const currentMode = useLiveData(editor.mode$);
|
||||||
const onEditSave = useCallback(() => {
|
|
||||||
track.$.header.actions.renameDoc();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Header className={styles.header} ref={containerRef}>
|
<Header className={styles.header} ref={containerRef}>
|
||||||
<ViewTitle title={title} />
|
<ViewTitle title={title} />
|
||||||
<ViewIcon icon={currentMode ?? 'page'} />
|
<ViewIcon icon={currentMode ?? 'page'} />
|
||||||
<EditorModeSwitch pageId={page?.id} />
|
<EditorModeSwitch />
|
||||||
<BlocksuiteHeaderTitle
|
<BlocksuiteHeaderTitle
|
||||||
|
docId={page.id}
|
||||||
inputHandleRef={titleInputHandleRef}
|
inputHandleRef={titleInputHandleRef}
|
||||||
pageId={page?.id}
|
|
||||||
docCollection={workspace.docCollection}
|
|
||||||
onEditSave={onEditSave}
|
|
||||||
/>
|
/>
|
||||||
<div className={styles.iconButtonContainer}>
|
<div className={styles.iconButtonContainer}>
|
||||||
{hideCollect ? null : (
|
{hideCollect ? null : (
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import { PageAIOnboarding } from '@affine/core/components/affine/ai-onboarding';
|
|||||||
import { EditorOutlineViewer } from '@affine/core/components/blocksuite/outline-viewer';
|
import { EditorOutlineViewer } from '@affine/core/components/blocksuite/outline-viewer';
|
||||||
import { useAppSettingHelper } from '@affine/core/hooks/affine/use-app-setting-helper';
|
import { useAppSettingHelper } from '@affine/core/hooks/affine/use-app-setting-helper';
|
||||||
import { useDocMetaHelper } from '@affine/core/hooks/use-block-suite-page-meta';
|
import { useDocMetaHelper } from '@affine/core/hooks/use-block-suite-page-meta';
|
||||||
|
import type { Editor } from '@affine/core/modules/editor';
|
||||||
|
import { EditorService, EditorsService } from '@affine/core/modules/editor';
|
||||||
import { RecentDocsService } from '@affine/core/modules/quicksearch';
|
import { RecentDocsService } from '@affine/core/modules/quicksearch';
|
||||||
import { ViewService } from '@affine/core/modules/workbench/services/view';
|
import { ViewService } from '@affine/core/modules/workbench/services/view';
|
||||||
import type { PageRootService } from '@blocksuite/blocks';
|
import type { PageRootService } from '@blocksuite/blocks';
|
||||||
@@ -30,6 +32,7 @@ import {
|
|||||||
GlobalContextService,
|
GlobalContextService,
|
||||||
useLiveData,
|
useLiveData,
|
||||||
useService,
|
useService,
|
||||||
|
useServices,
|
||||||
WorkspaceService,
|
WorkspaceService,
|
||||||
} from '@toeverything/infra';
|
} from '@toeverything/infra';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
@@ -71,18 +74,37 @@ import { EditorJournalPanel } from './tabs/journal';
|
|||||||
import { EditorOutlinePanel } from './tabs/outline';
|
import { EditorOutlinePanel } from './tabs/outline';
|
||||||
|
|
||||||
const DetailPageImpl = memo(function DetailPageImpl() {
|
const DetailPageImpl = memo(function DetailPageImpl() {
|
||||||
const workbench = useService(WorkbenchService).workbench;
|
const {
|
||||||
const view = useService(ViewService).view;
|
workbenchService,
|
||||||
|
viewService,
|
||||||
|
editorService,
|
||||||
|
docService,
|
||||||
|
workspaceService,
|
||||||
|
globalContextService,
|
||||||
|
} = useServices({
|
||||||
|
WorkbenchService,
|
||||||
|
ViewService,
|
||||||
|
EditorService,
|
||||||
|
DocService,
|
||||||
|
WorkspaceService,
|
||||||
|
GlobalContextService,
|
||||||
|
});
|
||||||
|
const workbench = workbenchService.workbench;
|
||||||
|
const editor = editorService.editor;
|
||||||
|
const view = viewService.view;
|
||||||
|
const workspace = workspaceService.workspace;
|
||||||
|
const docCollection = workspace.docCollection;
|
||||||
|
const globalContext = globalContextService.globalContext;
|
||||||
|
const doc = docService.doc;
|
||||||
|
|
||||||
|
const mode = useLiveData(editor.mode$);
|
||||||
const activeSidebarTab = useLiveData(view.activeSidebarTab$);
|
const activeSidebarTab = useLiveData(view.activeSidebarTab$);
|
||||||
|
|
||||||
const doc = useService(DocService).doc;
|
|
||||||
const isInTrash = useLiveData(doc.meta$.map(meta => meta.trash));
|
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 [editorContainer, setEditorContainer] =
|
||||||
const workspace = useService(WorkspaceService).workspace;
|
useState<AffineEditorContainer | null>(null);
|
||||||
const globalContext = useService(GlobalContextService).globalContext;
|
|
||||||
const docCollection = workspace.docCollection;
|
|
||||||
const mode = useLiveData(doc.mode$);
|
|
||||||
const isSideBarOpen = useLiveData(workbench.sidebarOpen$);
|
const isSideBarOpen = useLiveData(workbench.sidebarOpen$);
|
||||||
const { appSettings } = useAppSettingHelper();
|
const { appSettings } = useAppSettingHelper();
|
||||||
const chatPanelRef = useRef<ChatPanel | null>(null);
|
const chatPanelRef = useRef<ChatPanel | null>(null);
|
||||||
@@ -94,9 +116,9 @@ const DetailPageImpl = memo(function DetailPageImpl() {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isActiveView) {
|
if (isActiveView) {
|
||||||
setActiveBlockSuiteEditor(editor);
|
setActiveBlockSuiteEditor(editorContainer);
|
||||||
}
|
}
|
||||||
}, [editor, isActiveView, setActiveBlockSuiteEditor]);
|
}, [editorContainer, isActiveView, setActiveBlockSuiteEditor]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const disposable = AIProvider.slots.requestOpenWithChat.on(params => {
|
const disposable = AIProvider.slots.requestOpenWithChat.on(params => {
|
||||||
@@ -152,7 +174,7 @@ const DetailPageImpl = memo(function DetailPageImpl() {
|
|||||||
return;
|
return;
|
||||||
}, [globalContext, isActiveView, isInTrash]);
|
}, [globalContext, isActiveView, isInTrash]);
|
||||||
|
|
||||||
useRegisterBlocksuiteEditorCommands();
|
useRegisterBlocksuiteEditorCommands(editor);
|
||||||
const title = useLiveData(doc.title$);
|
const title = useLiveData(doc.title$);
|
||||||
usePageDocumentTitle(title);
|
usePageDocumentTitle(title);
|
||||||
|
|
||||||
@@ -218,7 +240,7 @@ const DetailPageImpl = memo(function DetailPageImpl() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
setEditor(editor);
|
setEditorContainer(editor);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
disposable.dispose();
|
disposable.dispose();
|
||||||
@@ -236,7 +258,7 @@ const DetailPageImpl = memo(function DetailPageImpl() {
|
|||||||
}, [workbench, view]);
|
}, [workbench, view]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<FrameworkScope scope={editor.scope}>
|
||||||
<ViewHeader>
|
<ViewHeader>
|
||||||
<DetailPageHeader page={doc.blockSuiteDoc} workspace={workspace} />
|
<DetailPageHeader page={doc.blockSuiteDoc} workspace={workspace} />
|
||||||
</ViewHeader>
|
</ViewHeader>
|
||||||
@@ -271,7 +293,7 @@ const DetailPageImpl = memo(function DetailPageImpl() {
|
|||||||
/>
|
/>
|
||||||
</Scrollable.Root>
|
</Scrollable.Root>
|
||||||
<EditorOutlineViewer
|
<EditorOutlineViewer
|
||||||
editor={editor}
|
editor={editorContainer}
|
||||||
show={mode === 'page' && !isSideBarOpen}
|
show={mode === 'page' && !isSideBarOpen}
|
||||||
openOutlinePanel={openOutlinePanel}
|
openOutlinePanel={openOutlinePanel}
|
||||||
/>
|
/>
|
||||||
@@ -281,7 +303,7 @@ const DetailPageImpl = memo(function DetailPageImpl() {
|
|||||||
</ViewBody>
|
</ViewBody>
|
||||||
|
|
||||||
<ViewSidebarTab tabId="chat" icon={<AiIcon />} unmountOnInactive={false}>
|
<ViewSidebarTab tabId="chat" icon={<AiIcon />} unmountOnInactive={false}>
|
||||||
<EditorChatPanel editor={editor} ref={chatPanelRef} />
|
<EditorChatPanel editor={editorContainer} ref={chatPanelRef} />
|
||||||
</ViewSidebarTab>
|
</ViewSidebarTab>
|
||||||
|
|
||||||
<ViewSidebarTab tabId="journal" icon={<TodayIcon />}>
|
<ViewSidebarTab tabId="journal" icon={<TodayIcon />}>
|
||||||
@@ -289,16 +311,16 @@ const DetailPageImpl = memo(function DetailPageImpl() {
|
|||||||
</ViewSidebarTab>
|
</ViewSidebarTab>
|
||||||
|
|
||||||
<ViewSidebarTab tabId="outline" icon={<TocIcon />}>
|
<ViewSidebarTab tabId="outline" icon={<TocIcon />}>
|
||||||
<EditorOutlinePanel editor={editor} />
|
<EditorOutlinePanel editor={editorContainer} />
|
||||||
</ViewSidebarTab>
|
</ViewSidebarTab>
|
||||||
|
|
||||||
<ViewSidebarTab tabId="frame" icon={<FrameIcon />}>
|
<ViewSidebarTab tabId="frame" icon={<FrameIcon />}>
|
||||||
<EditorFramePanel editor={editor} />
|
<EditorFramePanel editor={editorContainer} />
|
||||||
</ViewSidebarTab>
|
</ViewSidebarTab>
|
||||||
|
|
||||||
<GlobalPageHistoryModal />
|
<GlobalPageHistoryModal />
|
||||||
<PageAIOnboarding />
|
<PageAIOnboarding />
|
||||||
</>
|
</FrameworkScope>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -310,6 +332,7 @@ export const DetailPage = ({ pageId }: { pageId: string }): ReactElement => {
|
|||||||
const docRecord = useLiveData(docRecordList.doc$(pageId));
|
const docRecord = useLiveData(docRecordList.doc$(pageId));
|
||||||
|
|
||||||
const [doc, setDoc] = useState<Doc | null>(null);
|
const [doc, setDoc] = useState<Doc | null>(null);
|
||||||
|
const [editor, setEditor] = useState<Editor | null>(null);
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
if (!docRecord) {
|
if (!docRecord) {
|
||||||
@@ -322,6 +345,19 @@ export const DetailPage = ({ pageId }: { pageId: string }): ReactElement => {
|
|||||||
};
|
};
|
||||||
}, [docRecord, docsService, pageId]);
|
}, [docRecord, docsService, pageId]);
|
||||||
|
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
if (!doc) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const editor = doc.scope
|
||||||
|
.get(EditorsService)
|
||||||
|
.createEditor(doc.getPrimaryMode() || 'page');
|
||||||
|
setEditor(editor);
|
||||||
|
return () => {
|
||||||
|
editor.dispose();
|
||||||
|
};
|
||||||
|
}, [doc]);
|
||||||
|
|
||||||
// set sync engine priority target
|
// set sync engine priority target
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
currentWorkspace.engine.doc.setPriority(pageId, 10);
|
currentWorkspace.engine.doc.setPriority(pageId, 10);
|
||||||
@@ -346,13 +382,15 @@ export const DetailPage = ({ pageId }: { pageId: string }): ReactElement => {
|
|||||||
return <PageNotFound noPermission />;
|
return <PageNotFound noPermission />;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!doc) {
|
if (!doc || !editor) {
|
||||||
return <PageDetailSkeleton key="current-page-is-null" />;
|
return <PageDetailSkeleton key="current-page-is-null" />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FrameworkScope scope={doc.scope}>
|
<FrameworkScope scope={doc.scope}>
|
||||||
<DetailPageImpl />
|
<FrameworkScope scope={editor.scope}>
|
||||||
|
<DetailPageImpl />
|
||||||
|
</FrameworkScope>
|
||||||
</FrameworkScope>
|
</FrameworkScope>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ interface PageItemProps
|
|||||||
right?: ReactNode;
|
right?: ReactNode;
|
||||||
}
|
}
|
||||||
const PageItem = ({ docRecord, right, className, ...attrs }: PageItemProps) => {
|
const PageItem = ({ docRecord, right, className, ...attrs }: PageItemProps) => {
|
||||||
const mode = useLiveData(docRecord.mode$);
|
const mode = useLiveData(docRecord.primaryMode$);
|
||||||
const workspace = useService(WorkspaceService).workspace;
|
const workspace = useService(WorkspaceService).workspace;
|
||||||
const title = useDocCollectionPageTitle(
|
const title = useDocCollectionPageTitle(
|
||||||
workspace.docCollection,
|
workspace.docCollection,
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
|
import { EditorService } from '@affine/core/modules/editor';
|
||||||
import { WorkbenchService } from '@affine/core/modules/workbench';
|
import { WorkbenchService } from '@affine/core/modules/workbench';
|
||||||
import { useViewPosition } from '@affine/core/modules/workbench/view/use-view-position';
|
import { useViewPosition } from '@affine/core/modules/workbench/view/use-view-position';
|
||||||
import { DocService, useLiveData, useService } from '@toeverything/infra';
|
import { useLiveData, useService } from '@toeverything/infra';
|
||||||
|
|
||||||
export const useDetailPageHeaderResponsive = (availableWidth: number) => {
|
export const useDetailPageHeaderResponsive = (availableWidth: number) => {
|
||||||
const mode = useLiveData(useService(DocService).doc.mode$);
|
const mode = useLiveData(useService(EditorService).editor.mode$);
|
||||||
|
|
||||||
const workbench = useService(WorkbenchService).workbench;
|
const workbench = useService(WorkbenchService).workbench;
|
||||||
const viewPosition = useViewPosition();
|
const viewPosition = useViewPosition();
|
||||||
|
|||||||
Reference in New Issue
Block a user