refactor(core): refactor atom to use di (#5831)
To support multiple instances, this PR removes some atoms and implements them using the new DI system. removed atom - `pageSettingsAtom` - `currentPageIdAtom` - `currentModeAtom`
This commit is contained in:
@@ -36,7 +36,7 @@
|
|||||||
"test": "vitest --run",
|
"test": "vitest --run",
|
||||||
"test:ui": "vitest --ui",
|
"test:ui": "vitest --ui",
|
||||||
"test:coverage": "vitest run --coverage",
|
"test:coverage": "vitest run --coverage",
|
||||||
"typecheck": "tsc -b tsconfig.json --diagnostics",
|
"typecheck": "tsc -b tsconfig.json",
|
||||||
"postinstall": "node ./scripts/check-version.mjs && yarn i18n-codegen gen && yarn husky install",
|
"postinstall": "node ./scripts/check-version.mjs && yarn i18n-codegen gen && yarn husky install",
|
||||||
"prepare": "husky"
|
"prepare": "husky"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
export * from './initialization';
|
|
||||||
export {
|
export {
|
||||||
migratePages as forceUpgradePages,
|
migratePages as forceUpgradePages,
|
||||||
migrateGuidCompatibility,
|
migrateGuidCompatibility,
|
||||||
|
|||||||
@@ -1,114 +0,0 @@
|
|||||||
import type {
|
|
||||||
JobMiddleware,
|
|
||||||
Page,
|
|
||||||
PageMeta,
|
|
||||||
PageSnapshot,
|
|
||||||
Workspace,
|
|
||||||
WorkspaceInfoSnapshot,
|
|
||||||
} from '@blocksuite/store';
|
|
||||||
import { Job } from '@blocksuite/store';
|
|
||||||
import type { createStore, WritableAtom } from 'jotai/vanilla';
|
|
||||||
import { Map as YMap } from 'yjs';
|
|
||||||
|
|
||||||
import { getLatestVersions } from '../migration/blocksuite';
|
|
||||||
import { replaceIdMiddleware } from './middleware';
|
|
||||||
|
|
||||||
export function initEmptyPage(page: Page, title?: string) {
|
|
||||||
page.load(() => {
|
|
||||||
const pageBlockId = page.addBlock('affine:page', {
|
|
||||||
title: new page.Text(title ?? ''),
|
|
||||||
});
|
|
||||||
page.addBlock('affine:surface', {}, pageBlockId);
|
|
||||||
const noteBlockId = page.addBlock('affine:note', {}, pageBlockId);
|
|
||||||
page.addBlock('affine:paragraph', {}, noteBlockId);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* FIXME: Use exported json data to instead of building data.
|
|
||||||
*/
|
|
||||||
export async function buildShowcaseWorkspace(
|
|
||||||
workspace: Workspace,
|
|
||||||
{
|
|
||||||
store,
|
|
||||||
atoms,
|
|
||||||
}: {
|
|
||||||
atoms: {
|
|
||||||
pageMode: WritableAtom<
|
|
||||||
undefined,
|
|
||||||
[pageId: string, mode: 'page' | 'edgeless'],
|
|
||||||
void
|
|
||||||
>;
|
|
||||||
};
|
|
||||||
store: ReturnType<typeof createStore>;
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
const { onboarding } = await import('@affine/templates');
|
|
||||||
|
|
||||||
const info = onboarding['info.json'] as WorkspaceInfoSnapshot;
|
|
||||||
|
|
||||||
const migrationMiddleware: JobMiddleware = ({ slots, workspace }) => {
|
|
||||||
slots.afterImport.on(payload => {
|
|
||||||
if (payload.type === 'page') {
|
|
||||||
workspace.schema.upgradePage(
|
|
||||||
info?.pageVersion ?? 0,
|
|
||||||
{},
|
|
||||||
payload.page.spaceDoc
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const job = new Job({
|
|
||||||
workspace,
|
|
||||||
middlewares: [replaceIdMiddleware, migrationMiddleware],
|
|
||||||
});
|
|
||||||
|
|
||||||
job.snapshotToWorkspaceInfo(info);
|
|
||||||
|
|
||||||
// for now all onboarding assets are considered served via CDN
|
|
||||||
// hack assets so that every blob exists
|
|
||||||
// @ts-expect-error - rethinking API
|
|
||||||
job._assetsManager.writeToBlob = async () => {};
|
|
||||||
|
|
||||||
const pageSnapshots: PageSnapshot[] = Object.entries(onboarding)
|
|
||||||
.filter(([key]) => {
|
|
||||||
return key.endsWith('snapshot.json');
|
|
||||||
})
|
|
||||||
.map(([_, value]) => value as unknown as PageSnapshot);
|
|
||||||
|
|
||||||
await Promise.all(
|
|
||||||
pageSnapshots.map(snapshot => {
|
|
||||||
return job.snapshotToPage(snapshot);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
const newVersions = getLatestVersions(workspace.schema);
|
|
||||||
workspace.doc
|
|
||||||
.getMap('meta')
|
|
||||||
.set('blockVersions', new YMap(Object.entries(newVersions)));
|
|
||||||
|
|
||||||
// todo: find better way to do the following
|
|
||||||
// perhaps put them into middleware?
|
|
||||||
{
|
|
||||||
// the "AFFiNE - not just a note-taking app" page should be set to edgeless mode
|
|
||||||
const edgelessPage1 = (workspace.meta.pages as PageMeta[])?.find(
|
|
||||||
p => p.title === 'AFFiNE - not just a note-taking app'
|
|
||||||
)?.id;
|
|
||||||
|
|
||||||
if (edgelessPage1) {
|
|
||||||
store.set(atoms.pageMode, edgelessPage1, 'edgeless');
|
|
||||||
}
|
|
||||||
|
|
||||||
// should jump to "AFFiNE - not just a note-taking app" by default
|
|
||||||
const defaultPage = (workspace.meta.pages as PageMeta[])?.find(p =>
|
|
||||||
p.title.startsWith('AFFiNE - not just a note-taking app')
|
|
||||||
)?.id;
|
|
||||||
|
|
||||||
if (defaultPage) {
|
|
||||||
workspace.setPageMeta(defaultPage, {
|
|
||||||
jumpOnce: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -337,7 +337,7 @@ class ServiceCollectionEditor {
|
|||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
addImpl = <
|
addImpl = <
|
||||||
Arg1 extends ServiceIdentifier<any>,
|
Arg1 extends ServiceIdentifier<any> | (new (...args: any) => any),
|
||||||
Arg2 extends Type<Trait> | ServiceFactory<Trait> | Trait,
|
Arg2 extends Type<Trait> | ServiceFactory<Trait> | Trait,
|
||||||
Trait = ServiceIdentifierType<Arg1>,
|
Trait = ServiceIdentifierType<Arg1>,
|
||||||
Deps extends Arg2 extends Type<Trait>
|
Deps extends Arg2 extends Type<Trait>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ export * from './atom';
|
|||||||
export * from './blocksuite';
|
export * from './blocksuite';
|
||||||
export * from './command';
|
export * from './command';
|
||||||
export * from './di';
|
export * from './di';
|
||||||
|
export * from './initialization';
|
||||||
export * from './livedata';
|
export * from './livedata';
|
||||||
export * from './page';
|
export * from './page';
|
||||||
export * from './storage';
|
export * from './storage';
|
||||||
|
|||||||
117
packages/common/infra/src/initialization/index.ts
Normal file
117
packages/common/infra/src/initialization/index.ts
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
import type { WorkspaceFlavour } from '@affine/env/workspace';
|
||||||
|
import type {
|
||||||
|
JobMiddleware,
|
||||||
|
Page,
|
||||||
|
PageSnapshot,
|
||||||
|
WorkspaceInfoSnapshot,
|
||||||
|
} from '@blocksuite/store';
|
||||||
|
import { Job } from '@blocksuite/store';
|
||||||
|
import { Map as YMap } from 'yjs';
|
||||||
|
|
||||||
|
import { getLatestVersions } from '../blocksuite/migration/blocksuite';
|
||||||
|
import { PageRecordList } from '../page';
|
||||||
|
import type { WorkspaceManager } from '../workspace';
|
||||||
|
import { replaceIdMiddleware } from './middleware';
|
||||||
|
|
||||||
|
export function initEmptyPage(page: Page, title?: string) {
|
||||||
|
page.load(() => {
|
||||||
|
const pageBlockId = page.addBlock('affine:page', {
|
||||||
|
title: new page.Text(title ?? ''),
|
||||||
|
});
|
||||||
|
page.addBlock('affine:surface', {}, pageBlockId);
|
||||||
|
const noteBlockId = page.addBlock('affine:note', {}, pageBlockId);
|
||||||
|
page.addBlock('affine:paragraph', {}, noteBlockId);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FIXME: Use exported json data to instead of building data.
|
||||||
|
*/
|
||||||
|
export async function buildShowcaseWorkspace(
|
||||||
|
workspaceManager: WorkspaceManager,
|
||||||
|
flavour: WorkspaceFlavour,
|
||||||
|
workspaceName: string
|
||||||
|
) {
|
||||||
|
const meta = await workspaceManager.createWorkspace(
|
||||||
|
flavour,
|
||||||
|
async blockSuiteWorkspace => {
|
||||||
|
blockSuiteWorkspace.meta.setName(workspaceName);
|
||||||
|
const { onboarding } = await import('@affine/templates');
|
||||||
|
|
||||||
|
const info = onboarding['info.json'] as WorkspaceInfoSnapshot;
|
||||||
|
|
||||||
|
const migrationMiddleware: JobMiddleware = ({ slots, workspace }) => {
|
||||||
|
slots.afterImport.on(payload => {
|
||||||
|
if (payload.type === 'page') {
|
||||||
|
workspace.schema.upgradePage(
|
||||||
|
info?.pageVersion ?? 0,
|
||||||
|
{},
|
||||||
|
payload.page.spaceDoc
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const job = new Job({
|
||||||
|
workspace: blockSuiteWorkspace,
|
||||||
|
middlewares: [replaceIdMiddleware, migrationMiddleware],
|
||||||
|
});
|
||||||
|
|
||||||
|
job.snapshotToWorkspaceInfo(info);
|
||||||
|
|
||||||
|
// for now all onboarding assets are considered served via CDN
|
||||||
|
// hack assets so that every blob exists
|
||||||
|
// @ts-expect-error - rethinking API
|
||||||
|
job._assetsManager.writeToBlob = async () => {};
|
||||||
|
|
||||||
|
const pageSnapshots: PageSnapshot[] = Object.entries(onboarding)
|
||||||
|
.filter(([key]) => {
|
||||||
|
return key.endsWith('snapshot.json');
|
||||||
|
})
|
||||||
|
.map(([_, value]) => value as unknown as PageSnapshot);
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
pageSnapshots.map(snapshot => {
|
||||||
|
return job.snapshotToPage(snapshot);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const newVersions = getLatestVersions(blockSuiteWorkspace.schema);
|
||||||
|
blockSuiteWorkspace.doc
|
||||||
|
.getMap('meta')
|
||||||
|
.set('blockVersions', new YMap(Object.entries(newVersions)));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const { workspace, release } = workspaceManager.open(meta);
|
||||||
|
|
||||||
|
await workspace.engine.sync.waitForLoadedRootDoc();
|
||||||
|
|
||||||
|
const pageRecordList = workspace.services.get(PageRecordList);
|
||||||
|
|
||||||
|
// todo: find better way to do the following
|
||||||
|
// perhaps put them into middleware?
|
||||||
|
{
|
||||||
|
// the "AFFiNE - not just a note-taking app" page should be set to edgeless mode
|
||||||
|
const edgelessPage1 = pageRecordList.records.value.find(
|
||||||
|
p => p.title.value === 'AFFiNE - not just a note-taking app'
|
||||||
|
);
|
||||||
|
|
||||||
|
if (edgelessPage1) {
|
||||||
|
edgelessPage1.setMode('edgeless');
|
||||||
|
}
|
||||||
|
|
||||||
|
// should jump to "AFFiNE - not just a note-taking app" by default
|
||||||
|
const defaultPage = pageRecordList.records.value.find(p =>
|
||||||
|
p.title.value.startsWith('AFFiNE - not just a note-taking app')
|
||||||
|
);
|
||||||
|
|
||||||
|
if (defaultPage) {
|
||||||
|
defaultPage.setMeta({
|
||||||
|
jumpOnce: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
release();
|
||||||
|
return meta;
|
||||||
|
}
|
||||||
@@ -3,13 +3,29 @@ import { useSyncExternalStore } from 'react';
|
|||||||
|
|
||||||
import type { LiveData } from './index';
|
import type { LiveData } from './index';
|
||||||
|
|
||||||
|
function noopSubscribe() {
|
||||||
|
return () => {};
|
||||||
|
}
|
||||||
|
|
||||||
|
function noopGetSnapshot() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* subscribe LiveData and return the value.
|
* subscribe LiveData and return the value.
|
||||||
*/
|
*/
|
||||||
export function useLiveData<T>(liveData: LiveData<T>): T {
|
export function useLiveData<Input extends LiveData<any> | null | undefined>(
|
||||||
|
liveData: Input
|
||||||
|
): NonNullable<Input> extends LiveData<infer T>
|
||||||
|
? Input extends undefined
|
||||||
|
? T | undefined
|
||||||
|
: Input extends null
|
||||||
|
? T | null
|
||||||
|
: T
|
||||||
|
: never {
|
||||||
return useSyncExternalStore(
|
return useSyncExternalStore(
|
||||||
liveData.reactSubscribe,
|
liveData ? liveData.reactSubscribe : noopSubscribe,
|
||||||
liveData.reactGetSnapshot
|
liveData ? liveData.reactGetSnapshot : noopGetSnapshot
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
import { WorkspaceFlavour } from '@affine/env/workspace';
|
|
||||||
import { describe, expect, test } from 'vitest';
|
|
||||||
|
|
||||||
import { configureInfraServices, configureTestingInfraServices } from '../..';
|
|
||||||
import { ServiceCollection } from '../../di';
|
|
||||||
import { WorkspaceManager } from '../../workspace';
|
|
||||||
import { PageListService } from '..';
|
|
||||||
|
|
||||||
describe('Page System', () => {
|
|
||||||
test('basic', async () => {
|
|
||||||
const services = new ServiceCollection();
|
|
||||||
configureInfraServices(services);
|
|
||||||
configureTestingInfraServices(services);
|
|
||||||
|
|
||||||
const provider = services.provider();
|
|
||||||
const workspaceManager = provider.get(WorkspaceManager);
|
|
||||||
|
|
||||||
const { workspace } = workspaceManager.open(
|
|
||||||
await workspaceManager.createWorkspace(WorkspaceFlavour.LOCAL)
|
|
||||||
);
|
|
||||||
|
|
||||||
const pageListService = workspace.services.get(PageListService);
|
|
||||||
expect(pageListService.pages.value.length).toBe(0);
|
|
||||||
|
|
||||||
workspace.blockSuiteWorkspace.createPage({
|
|
||||||
id: 'page0',
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(pageListService.pages.value.length).toBe(1);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,21 +1,23 @@
|
|||||||
import type { Page as BlockSuitePage, PageMeta } from '@blocksuite/store';
|
import type { Page as BlockSuitePage } from '@blocksuite/store';
|
||||||
|
|
||||||
import { createIdentifier, type ServiceCollection } from '../di';
|
import { createIdentifier, type ServiceCollection } from '../di';
|
||||||
|
import type { PageRecord } from './record';
|
||||||
import { PageScope } from './service-scope';
|
import { PageScope } from './service-scope';
|
||||||
|
|
||||||
export const BlockSuitePageContext = createIdentifier<BlockSuitePage>(
|
export const BlockSuitePageContext = createIdentifier<BlockSuitePage>(
|
||||||
'BlockSuitePageContext'
|
'BlockSuitePageContext'
|
||||||
);
|
);
|
||||||
|
|
||||||
export const PageMetaContext = createIdentifier<PageMeta>('PageMetaContext');
|
export const PageRecordContext =
|
||||||
|
createIdentifier<PageRecord>('PageRecordContext');
|
||||||
|
|
||||||
export function configurePageContext(
|
export function configurePageContext(
|
||||||
services: ServiceCollection,
|
services: ServiceCollection,
|
||||||
blockSuitePage: BlockSuitePage,
|
blockSuitePage: BlockSuitePage,
|
||||||
pageMeta: PageMeta
|
pageRecord: PageRecord
|
||||||
) {
|
) {
|
||||||
services
|
services
|
||||||
.scope(PageScope)
|
.scope(PageScope)
|
||||||
.addImpl(PageMetaContext, pageMeta)
|
.addImpl(PageRecordContext, pageRecord)
|
||||||
.addImpl(BlockSuitePageContext, blockSuitePage);
|
.addImpl(BlockSuitePageContext, blockSuitePage);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,26 @@
|
|||||||
export * from './context';
|
|
||||||
export * from './list';
|
|
||||||
export * from './manager';
|
export * from './manager';
|
||||||
export * from './page';
|
export * from './page';
|
||||||
|
export * from './record';
|
||||||
|
export * from './record-list';
|
||||||
export * from './service-scope';
|
export * from './service-scope';
|
||||||
|
|
||||||
import { type ServiceCollection, ServiceProvider } from '../di';
|
import { type ServiceCollection, ServiceProvider } from '../di';
|
||||||
import { CleanupService } from '../lifecycle';
|
import { CleanupService } from '../lifecycle';
|
||||||
import { Workspace, WorkspaceScope } from '../workspace';
|
import { Workspace, WorkspaceLocalState, WorkspaceScope } from '../workspace';
|
||||||
import { BlockSuitePageContext, PageMetaContext } from './context';
|
import { BlockSuitePageContext, PageRecordContext } from './context';
|
||||||
import { PageListService } from './list';
|
|
||||||
import { PageManager } from './manager';
|
import { PageManager } from './manager';
|
||||||
import { Page } from './page';
|
import { Page } from './page';
|
||||||
|
import { PageRecordList } from './record-list';
|
||||||
import { PageScope } from './service-scope';
|
import { PageScope } from './service-scope';
|
||||||
|
|
||||||
export function configurePageServices(services: ServiceCollection) {
|
export function configurePageServices(services: ServiceCollection) {
|
||||||
services
|
services
|
||||||
.scope(WorkspaceScope)
|
.scope(WorkspaceScope)
|
||||||
.add(PageListService, [Workspace])
|
.add(PageManager, [Workspace, PageRecordList, ServiceProvider])
|
||||||
.add(PageManager, [Workspace, PageListService, ServiceProvider]);
|
.add(PageRecordList, [Workspace, WorkspaceLocalState]);
|
||||||
|
|
||||||
services
|
services
|
||||||
.scope(PageScope)
|
.scope(PageScope)
|
||||||
.add(CleanupService)
|
.add(CleanupService)
|
||||||
.add(Page, [PageMetaContext, BlockSuitePageContext, ServiceProvider]);
|
.add(Page, [PageRecordContext, BlockSuitePageContext, ServiceProvider]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
import type { PageMeta } from '@blocksuite/store';
|
|
||||||
|
|
||||||
import type { ServiceProvider } from '../di';
|
import type { ServiceProvider } from '../di';
|
||||||
import { ObjectPool } from '../utils/object-pool';
|
import { ObjectPool } from '../utils/object-pool';
|
||||||
import type { Workspace } from '../workspace';
|
import type { Workspace } from '../workspace';
|
||||||
|
import type { PageRecordList } from '.';
|
||||||
import { configurePageContext } from './context';
|
import { configurePageContext } from './context';
|
||||||
import type { PageListService } from './list';
|
|
||||||
import { Page } from './page';
|
import { Page } from './page';
|
||||||
import { PageScope } from './service-scope';
|
import { PageScope } from './service-scope';
|
||||||
|
|
||||||
@@ -13,28 +11,21 @@ export class PageManager {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly workspace: Workspace,
|
private readonly workspace: Workspace,
|
||||||
private readonly pageList: PageListService,
|
private readonly pageRecordList: PageRecordList,
|
||||||
private readonly serviceProvider: ServiceProvider
|
private readonly serviceProvider: ServiceProvider
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
openByPageId(pageId: string) {
|
open(pageId: string) {
|
||||||
const pageMeta = this.pageList.getPageMetaById(pageId);
|
const pageRecord = this.pageRecordList.record(pageId).value;
|
||||||
if (!pageMeta) {
|
if (!pageRecord) {
|
||||||
throw new Error('Page not found');
|
throw new Error('Page record not found');
|
||||||
}
|
}
|
||||||
|
const blockSuitePage = this.workspace.blockSuiteWorkspace.getPage(pageId);
|
||||||
return this.open(pageMeta);
|
|
||||||
}
|
|
||||||
|
|
||||||
open(pageMeta: PageMeta) {
|
|
||||||
const blockSuitePage = this.workspace.blockSuiteWorkspace.getPage(
|
|
||||||
pageMeta.id
|
|
||||||
);
|
|
||||||
if (!blockSuitePage) {
|
if (!blockSuitePage) {
|
||||||
throw new Error('Page not found');
|
throw new Error('Page not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
const exists = this.pool.get(pageMeta.id);
|
const exists = this.pool.get(pageId);
|
||||||
if (exists) {
|
if (exists) {
|
||||||
return { page: exists.obj, release: exists.release };
|
return { page: exists.obj, release: exists.release };
|
||||||
}
|
}
|
||||||
@@ -43,7 +34,7 @@ export class PageManager {
|
|||||||
// avoid to modify the original service collection
|
// avoid to modify the original service collection
|
||||||
.clone();
|
.clone();
|
||||||
|
|
||||||
configurePageContext(serviceCollection, blockSuitePage, pageMeta);
|
configurePageContext(serviceCollection, blockSuitePage, pageRecord);
|
||||||
|
|
||||||
const provider = serviceCollection.provider(
|
const provider = serviceCollection.provider(
|
||||||
PageScope,
|
PageScope,
|
||||||
@@ -52,7 +43,7 @@ export class PageManager {
|
|||||||
|
|
||||||
const page = provider.get(Page);
|
const page = provider.get(Page);
|
||||||
|
|
||||||
const { obj, release } = this.pool.put(pageMeta.id, page);
|
const { obj, release } = this.pool.put(pageId, page);
|
||||||
|
|
||||||
return { page: obj, release };
|
return { page: obj, release };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,28 @@
|
|||||||
import type { Page as BlockSuitePage } from '@blocksuite/store';
|
import type { Page as BlockSuitePage } from '@blocksuite/store';
|
||||||
import { type PageMeta } from '@blocksuite/store';
|
|
||||||
import type { ServiceProvider } from '@toeverything/infra/di';
|
import type { ServiceProvider } from '@toeverything/infra/di';
|
||||||
|
|
||||||
export class Page {
|
import type { PageMode, PageRecord } from './record';
|
||||||
get id() {
|
|
||||||
return this.meta.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
export class Page {
|
||||||
constructor(
|
constructor(
|
||||||
public readonly meta: PageMeta,
|
public readonly record: PageRecord,
|
||||||
public readonly blockSuitePage: BlockSuitePage,
|
public readonly blockSuitePage: BlockSuitePage,
|
||||||
public readonly services: ServiceProvider
|
public readonly services: ServiceProvider
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
get id() {
|
||||||
|
return this.record.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly mete = this.record.meta;
|
||||||
|
readonly mode = this.record.mode;
|
||||||
|
readonly title = this.record.title;
|
||||||
|
|
||||||
|
setMode(mode: PageMode) {
|
||||||
|
this.record.setMode(mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleMode() {
|
||||||
|
this.record.toggleMode();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,35 @@
|
|||||||
import type { PageMeta } from '@blocksuite/store';
|
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
import { LiveData } from '../livedata';
|
import { LiveData } from '../livedata';
|
||||||
import { SyncEngineStep, type Workspace } from '../workspace';
|
import {
|
||||||
|
SyncEngineStep,
|
||||||
|
type Workspace,
|
||||||
|
type WorkspaceLocalState,
|
||||||
|
} from '../workspace';
|
||||||
|
import { PageRecord } from './record';
|
||||||
|
|
||||||
export class PageListService {
|
export class PageRecordList {
|
||||||
constructor(private readonly workspace: Workspace) {}
|
constructor(
|
||||||
|
private readonly workspace: Workspace,
|
||||||
|
private readonly localState: WorkspaceLocalState
|
||||||
|
) {}
|
||||||
|
|
||||||
public readonly pages = LiveData.from<PageMeta[]>(
|
public readonly records = LiveData.from<PageRecord[]>(
|
||||||
new Observable(subscriber => {
|
new Observable(subscriber => {
|
||||||
subscriber.next(
|
const emit = () => {
|
||||||
Array.from(this.workspace.blockSuiteWorkspace.meta.pageMetas)
|
subscriber.next(
|
||||||
);
|
this.workspace.blockSuiteWorkspace.meta.pageMetas.map(
|
||||||
|
v => new PageRecord(v.id, this.workspace, this.localState)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
emit();
|
||||||
|
|
||||||
const dispose =
|
const dispose =
|
||||||
this.workspace.blockSuiteWorkspace.meta.pageMetasUpdated.on(() => {
|
this.workspace.blockSuiteWorkspace.meta.pageMetasUpdated.on(
|
||||||
subscriber.next(
|
emit
|
||||||
Array.from(this.workspace.blockSuiteWorkspace.meta.pageMetas)
|
).dispose;
|
||||||
);
|
|
||||||
}).dispose;
|
|
||||||
return () => {
|
return () => {
|
||||||
dispose();
|
dispose();
|
||||||
};
|
};
|
||||||
@@ -44,7 +55,7 @@ export class PageListService {
|
|||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
public getPageMetaById(id: string) {
|
public record(id: string) {
|
||||||
return this.pages.value.find(page => page.id === id);
|
return this.records.map(record => record.find(record => record.id === id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
65
packages/common/infra/src/page/record.ts
Normal file
65
packages/common/infra/src/page/record.ts
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
import type { PageMeta } from '@blocksuite/store';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
|
import { LiveData } from '../livedata';
|
||||||
|
import type { Workspace, WorkspaceLocalState } from '../workspace';
|
||||||
|
|
||||||
|
export type PageMode = 'edgeless' | 'page';
|
||||||
|
|
||||||
|
export class PageRecord {
|
||||||
|
constructor(
|
||||||
|
public readonly id: string,
|
||||||
|
private readonly workspace: Workspace,
|
||||||
|
private readonly localState: WorkspaceLocalState
|
||||||
|
) {}
|
||||||
|
|
||||||
|
meta = LiveData.from<PageMeta>(
|
||||||
|
new Observable(subscriber => {
|
||||||
|
const emit = () => {
|
||||||
|
const meta = this.workspace.blockSuiteWorkspace.meta.pageMetas.find(
|
||||||
|
page => page.id === this.id
|
||||||
|
);
|
||||||
|
if (meta === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
subscriber.next(meta);
|
||||||
|
};
|
||||||
|
|
||||||
|
emit();
|
||||||
|
|
||||||
|
const dispose =
|
||||||
|
this.workspace.blockSuiteWorkspace.meta.pageMetasUpdated.on(
|
||||||
|
emit
|
||||||
|
).dispose;
|
||||||
|
return () => {
|
||||||
|
dispose();
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
id: this.id,
|
||||||
|
title: '',
|
||||||
|
tags: [],
|
||||||
|
createDate: 0,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
setMeta(meta: Partial<PageMeta>): void {
|
||||||
|
this.workspace.blockSuiteWorkspace.setPageMeta(this.id, meta);
|
||||||
|
}
|
||||||
|
|
||||||
|
mode: LiveData<PageMode> = LiveData.from(
|
||||||
|
this.localState.watch<PageMode>(`page:${this.id}:mode`),
|
||||||
|
'page'
|
||||||
|
).map(mode => (mode === 'edgeless' ? 'edgeless' : 'page'));
|
||||||
|
|
||||||
|
setMode(mode: PageMode) {
|
||||||
|
this.localState.set(`page:${this.id}:mode`, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleMode() {
|
||||||
|
this.setMode(this.mode.value === 'edgeless' ? 'page' : 'edgeless');
|
||||||
|
return this.mode.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
title = this.meta.map(meta => meta.title);
|
||||||
|
}
|
||||||
@@ -6,13 +6,14 @@ export * from './list';
|
|||||||
export * from './manager';
|
export * from './manager';
|
||||||
export * from './metadata';
|
export * from './metadata';
|
||||||
export * from './service-scope';
|
export * from './service-scope';
|
||||||
|
export * from './storage';
|
||||||
export * from './testing';
|
export * from './testing';
|
||||||
export * from './upgrade';
|
export * from './upgrade';
|
||||||
export * from './workspace';
|
export * from './workspace';
|
||||||
|
|
||||||
import { type ServiceCollection, ServiceProvider } from '../di';
|
import { type ServiceCollection, ServiceProvider } from '../di';
|
||||||
import { CleanupService } from '../lifecycle';
|
import { CleanupService } from '../lifecycle';
|
||||||
import { GlobalCache, GlobalState } from '../storage';
|
import { GlobalCache, GlobalState, MemoryMemento } from '../storage';
|
||||||
import {
|
import {
|
||||||
BlockSuiteWorkspaceContext,
|
BlockSuiteWorkspaceContext,
|
||||||
RootYDocContext,
|
RootYDocContext,
|
||||||
@@ -33,6 +34,7 @@ import { WorkspaceFactory } from './factory';
|
|||||||
import { WorkspaceListProvider, WorkspaceListService } from './list';
|
import { WorkspaceListProvider, WorkspaceListService } from './list';
|
||||||
import { WorkspaceManager } from './manager';
|
import { WorkspaceManager } from './manager';
|
||||||
import { WorkspaceScope } from './service-scope';
|
import { WorkspaceScope } from './service-scope';
|
||||||
|
import { WorkspaceLocalState } from './storage';
|
||||||
import {
|
import {
|
||||||
TestingLocalWorkspaceFactory,
|
TestingLocalWorkspaceFactory,
|
||||||
TestingLocalWorkspaceListProvider,
|
TestingLocalWorkspaceListProvider,
|
||||||
@@ -83,5 +85,7 @@ export function configureTestingWorkspaceServices(services: ServiceCollection) {
|
|||||||
)
|
)
|
||||||
.override(WorkspaceFactory('local'), TestingLocalWorkspaceFactory, [
|
.override(WorkspaceFactory('local'), TestingLocalWorkspaceFactory, [
|
||||||
GlobalState,
|
GlobalState,
|
||||||
]);
|
])
|
||||||
|
.scope(WorkspaceScope)
|
||||||
|
.override(WorkspaceLocalState, MemoryMemento);
|
||||||
}
|
}
|
||||||
|
|||||||
8
packages/common/infra/src/workspace/storage.ts
Normal file
8
packages/common/infra/src/workspace/storage.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { createIdentifier } from '../di';
|
||||||
|
import type { Memento } from '../storage';
|
||||||
|
|
||||||
|
export interface WorkspaceLocalState extends Memento {}
|
||||||
|
|
||||||
|
export const WorkspaceLocalState = createIdentifier<WorkspaceLocalState>(
|
||||||
|
'WorkspaceLocalState'
|
||||||
|
);
|
||||||
@@ -10,6 +10,8 @@ import { type WorkspaceMetadata } from './metadata';
|
|||||||
import type { WorkspaceUpgradeController } from './upgrade';
|
import type { WorkspaceUpgradeController } from './upgrade';
|
||||||
import { type WorkspaceUpgradeStatus } from './upgrade';
|
import { type WorkspaceUpgradeStatus } from './upgrade';
|
||||||
|
|
||||||
|
export type { Workspace as BlockSuiteWorkspace } from '@blocksuite/store';
|
||||||
|
|
||||||
const logger = new DebugLogger('affine:workspace');
|
const logger = new DebugLogger('affine:workspace');
|
||||||
|
|
||||||
export type WorkspaceStatus = {
|
export type WorkspaceStatus = {
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
/**
|
|
||||||
* @vitest-environment happy-dom
|
|
||||||
*/
|
|
||||||
import 'fake-indexeddb/auto';
|
|
||||||
|
|
||||||
import { createStore } from 'jotai';
|
|
||||||
import { describe, expect, test } from 'vitest';
|
|
||||||
|
|
||||||
import {
|
|
||||||
pageSettingFamily,
|
|
||||||
pageSettingsAtom,
|
|
||||||
recentPageIdsBaseAtom,
|
|
||||||
} from '../index';
|
|
||||||
|
|
||||||
describe('page mode atom', () => {
|
|
||||||
test('basic', () => {
|
|
||||||
const store = createStore();
|
|
||||||
const page0SettingAtom = pageSettingFamily('page0');
|
|
||||||
store.set(page0SettingAtom, {
|
|
||||||
mode: 'page',
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(store.get(pageSettingsAtom)).toEqual({
|
|
||||||
page0: {
|
|
||||||
mode: 'page',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(store.get(recentPageIdsBaseAtom)).toEqual(['page0']);
|
|
||||||
|
|
||||||
const page1SettingAtom = pageSettingFamily('page1');
|
|
||||||
store.set(page1SettingAtom, {
|
|
||||||
mode: 'edgeless',
|
|
||||||
});
|
|
||||||
expect(store.get(recentPageIdsBaseAtom)).toEqual(['page1', 'page0']);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
import type { PrimitiveAtom } from 'jotai';
|
|
||||||
import { atom } from 'jotai';
|
import { atom } from 'jotai';
|
||||||
import { atomFamily, atomWithStorage } from 'jotai/utils';
|
import { atomWithStorage } from 'jotai/utils';
|
||||||
import type { AtomFamily } from 'jotai/vanilla/utils/atomFamily';
|
|
||||||
|
|
||||||
import type { AuthProps } from '../components/affine/auth';
|
import type { AuthProps } from '../components/affine/auth';
|
||||||
import type { CreateWorkspaceMode } from '../components/affine/create-workspace-modal';
|
import type { CreateWorkspaceMode } from '../components/affine/create-workspace-modal';
|
||||||
@@ -45,66 +43,11 @@ export const authAtom = atom<AuthAtom>({
|
|||||||
|
|
||||||
export const openDisableCloudAlertModalAtom = atom(false);
|
export const openDisableCloudAlertModalAtom = atom(false);
|
||||||
|
|
||||||
export type PageMode = 'page' | 'edgeless';
|
|
||||||
type PageLocalSetting = {
|
|
||||||
mode: PageMode;
|
|
||||||
};
|
|
||||||
|
|
||||||
const pageSettingsBaseAtom = atomWithStorage(
|
|
||||||
'pageSettings',
|
|
||||||
{} as Record<string, PageLocalSetting>
|
|
||||||
);
|
|
||||||
|
|
||||||
// readonly atom by design
|
|
||||||
export const pageSettingsAtom = atom(get => get(pageSettingsBaseAtom));
|
|
||||||
|
|
||||||
export const recentPageIdsBaseAtom = atomWithStorage<string[]>(
|
export const recentPageIdsBaseAtom = atomWithStorage<string[]>(
|
||||||
'recentPageSettings',
|
'recentPageSettings',
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
const defaultPageSetting = {
|
|
||||||
mode: 'page',
|
|
||||||
} satisfies PageLocalSetting;
|
|
||||||
|
|
||||||
export const pageSettingFamily: AtomFamily<
|
|
||||||
string,
|
|
||||||
PrimitiveAtom<PageLocalSetting>
|
|
||||||
> = atomFamily((pageId: string) =>
|
|
||||||
atom(
|
|
||||||
get =>
|
|
||||||
get(pageSettingsBaseAtom)[pageId] ?? {
|
|
||||||
...defaultPageSetting,
|
|
||||||
},
|
|
||||||
(get, set, patch) => {
|
|
||||||
// fixme: this does not work when page reload,
|
|
||||||
// since atomWithStorage is async
|
|
||||||
set(recentPageIdsBaseAtom, ids => {
|
|
||||||
// pick 3 recent page ids
|
|
||||||
return [...new Set([pageId, ...ids]).values()].slice(0, 3);
|
|
||||||
});
|
|
||||||
const prevSetting = {
|
|
||||||
...defaultPageSetting,
|
|
||||||
...get(pageSettingsBaseAtom)[pageId],
|
|
||||||
};
|
|
||||||
set(pageSettingsBaseAtom, settings => ({
|
|
||||||
...settings,
|
|
||||||
[pageId]: {
|
|
||||||
...prevSetting,
|
|
||||||
...(typeof patch === 'function' ? patch(prevSetting) : patch),
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
export const setPageModeAtom = atom(
|
|
||||||
void 0,
|
|
||||||
(_, set, pageId: string, mode: PageMode) => {
|
|
||||||
set(pageSettingFamily(pageId), { mode });
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
export type PageModeOption = 'all' | 'page' | 'edgeless';
|
export type PageModeOption = 'all' | 'page' | 'edgeless';
|
||||||
export const allPageModeSelectAtom = atom<PageModeOption>('all');
|
export const allPageModeSelectAtom = atom<PageModeOption>('all');
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
import { atom } from 'jotai/vanilla';
|
|
||||||
|
|
||||||
import { pageSettingFamily } from './index';
|
|
||||||
|
|
||||||
export const currentPageIdAtom = atom<string | null>(null);
|
|
||||||
|
|
||||||
export const currentModeAtom = atom<'page' | 'edgeless'>(get => {
|
|
||||||
const pageId = get(currentPageIdAtom);
|
|
||||||
if (!pageId) {
|
|
||||||
return 'page';
|
|
||||||
}
|
|
||||||
return get(pageSettingFamily(pageId)).mode;
|
|
||||||
});
|
|
||||||
@@ -2,42 +2,36 @@ import { DebugLogger } from '@affine/debug';
|
|||||||
import { DEFAULT_WORKSPACE_NAME } from '@affine/env/constant';
|
import { DEFAULT_WORKSPACE_NAME } from '@affine/env/constant';
|
||||||
import { WorkspaceFlavour } from '@affine/env/workspace';
|
import { WorkspaceFlavour } from '@affine/env/workspace';
|
||||||
import type { WorkspaceManager } from '@toeverything/infra';
|
import type { WorkspaceManager } from '@toeverything/infra';
|
||||||
import { getCurrentStore } from '@toeverything/infra/atom';
|
import { buildShowcaseWorkspace, initEmptyPage } from '@toeverything/infra';
|
||||||
import {
|
|
||||||
buildShowcaseWorkspace,
|
|
||||||
initEmptyPage,
|
|
||||||
} from '@toeverything/infra/blocksuite';
|
|
||||||
|
|
||||||
import { setPageModeAtom } from '../atoms';
|
const logger = new DebugLogger('createFirstAppData');
|
||||||
|
|
||||||
const logger = new DebugLogger('affine:first-app-data');
|
|
||||||
|
|
||||||
export async function createFirstAppData(workspaceManager: WorkspaceManager) {
|
export async function createFirstAppData(workspaceManager: WorkspaceManager) {
|
||||||
if (localStorage.getItem('is-first-open') !== null) {
|
if (localStorage.getItem('is-first-open') !== null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
localStorage.setItem('is-first-open', 'false');
|
localStorage.setItem('is-first-open', 'false');
|
||||||
const workspaceMetadata = await workspaceManager.createWorkspace(
|
if (runtimeConfig.enablePreloading) {
|
||||||
WorkspaceFlavour.LOCAL,
|
const workspaceMetadata = await buildShowcaseWorkspace(
|
||||||
async workspace => {
|
workspaceManager,
|
||||||
workspace.meta.setName(DEFAULT_WORKSPACE_NAME);
|
WorkspaceFlavour.LOCAL,
|
||||||
if (runtimeConfig.enablePreloading) {
|
DEFAULT_WORKSPACE_NAME
|
||||||
await buildShowcaseWorkspace(workspace, {
|
);
|
||||||
store: getCurrentStore(),
|
logger.info('create first workspace', workspaceMetadata);
|
||||||
atoms: {
|
return workspaceMetadata;
|
||||||
pageMode: setPageModeAtom,
|
} else {
|
||||||
},
|
const workspaceMetadata = await workspaceManager.createWorkspace(
|
||||||
});
|
WorkspaceFlavour.LOCAL,
|
||||||
} else {
|
async workspace => {
|
||||||
|
workspace.meta.setName(DEFAULT_WORKSPACE_NAME);
|
||||||
const page = workspace.createPage();
|
const page = workspace.createPage();
|
||||||
workspace.setPageMeta(page.id, {
|
workspace.setPageMeta(page.id, {
|
||||||
jumpOnce: true,
|
jumpOnce: true,
|
||||||
});
|
});
|
||||||
initEmptyPage(page);
|
initEmptyPage(page);
|
||||||
}
|
}
|
||||||
logger.debug('create first workspace');
|
);
|
||||||
}
|
logger.info('create first workspace', workspaceMetadata);
|
||||||
);
|
return workspaceMetadata;
|
||||||
console.info('create first workspace', workspaceMetadata);
|
}
|
||||||
return workspaceMetadata;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
import { WorkspaceListService } from '@toeverything/infra';
|
import { Page, WorkspaceListService } from '@toeverything/infra';
|
||||||
import { useService } from '@toeverything/infra/di';
|
import { useService, useServiceOptional } from '@toeverything/infra/di';
|
||||||
import { useLiveData } from '@toeverything/infra/livedata';
|
import { useLiveData } from '@toeverything/infra/livedata';
|
||||||
import { useAtomValue } from 'jotai/react';
|
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { useLocation, useParams } from 'react-router-dom';
|
import { useLocation, useParams } from 'react-router-dom';
|
||||||
|
|
||||||
import { currentPageIdAtom } from '../../../../atoms/mode';
|
|
||||||
import { CurrentWorkspaceService } from '../../../../modules/workspace/current-workspace';
|
import { CurrentWorkspaceService } from '../../../../modules/workspace/current-workspace';
|
||||||
|
|
||||||
export interface DumpInfoProps {
|
export interface DumpInfoProps {
|
||||||
@@ -18,7 +16,7 @@ export const DumpInfo = (_props: DumpInfoProps) => {
|
|||||||
const currentWorkspace = useLiveData(
|
const currentWorkspace = useLiveData(
|
||||||
useService(CurrentWorkspaceService).currentWorkspace
|
useService(CurrentWorkspaceService).currentWorkspace
|
||||||
);
|
);
|
||||||
const currentPageId = useAtomValue(currentPageIdAtom);
|
const currentPage = useServiceOptional(Page);
|
||||||
const path = location.pathname;
|
const path = location.pathname;
|
||||||
const query = useParams();
|
const query = useParams();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -26,9 +24,9 @@ export const DumpInfo = (_props: DumpInfoProps) => {
|
|||||||
path,
|
path,
|
||||||
query,
|
query,
|
||||||
currentWorkspaceId: currentWorkspace?.id,
|
currentWorkspaceId: currentWorkspace?.id,
|
||||||
currentPageId,
|
currentPageId: currentPage?.id,
|
||||||
workspaceList,
|
workspaceList,
|
||||||
});
|
});
|
||||||
}, [path, query, currentWorkspace, currentPageId, workspaceList]);
|
}, [path, query, currentWorkspace, workspaceList, currentPage?.id]);
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,11 +4,7 @@ import {
|
|||||||
type ConfirmModalProps,
|
type ConfirmModalProps,
|
||||||
Modal,
|
Modal,
|
||||||
} from '@affine/component/ui/modal';
|
} from '@affine/component/ui/modal';
|
||||||
import {
|
import { authAtom, openDisableCloudAlertModalAtom } from '@affine/core/atoms';
|
||||||
authAtom,
|
|
||||||
openDisableCloudAlertModalAtom,
|
|
||||||
setPageModeAtom,
|
|
||||||
} from '@affine/core/atoms';
|
|
||||||
import { useCurrentLoginStatus } from '@affine/core/hooks/affine/use-current-login-status';
|
import { useCurrentLoginStatus } from '@affine/core/hooks/affine/use-current-login-status';
|
||||||
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
||||||
import { DebugLogger } from '@affine/debug';
|
import { DebugLogger } from '@affine/debug';
|
||||||
@@ -17,11 +13,7 @@ import { WorkspaceFlavour } from '@affine/env/workspace';
|
|||||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||||
import { _addLocalWorkspace } from '@affine/workspace-impl';
|
import { _addLocalWorkspace } from '@affine/workspace-impl';
|
||||||
import { WorkspaceManager } from '@toeverything/infra';
|
import { WorkspaceManager } from '@toeverything/infra';
|
||||||
import { getCurrentStore } from '@toeverything/infra/atom';
|
import { buildShowcaseWorkspace, initEmptyPage } from '@toeverything/infra';
|
||||||
import {
|
|
||||||
buildShowcaseWorkspace,
|
|
||||||
initEmptyPage,
|
|
||||||
} from '@toeverything/infra/blocksuite';
|
|
||||||
import { useService } from '@toeverything/infra/di';
|
import { useService } from '@toeverything/infra/di';
|
||||||
import { useSetAtom } from 'jotai';
|
import { useSetAtom } from 'jotai';
|
||||||
import type { KeyboardEvent } from 'react';
|
import type { KeyboardEvent } from 'react';
|
||||||
@@ -234,28 +226,27 @@ export const CreateWorkspaceModal = ({
|
|||||||
async (name: string, workspaceFlavour: WorkspaceFlavour) => {
|
async (name: string, workspaceFlavour: WorkspaceFlavour) => {
|
||||||
// this will be the last step for web for now
|
// this will be the last step for web for now
|
||||||
// fix me later
|
// fix me later
|
||||||
const { id } = await workspaceManager.createWorkspace(
|
if (runtimeConfig.enablePreloading) {
|
||||||
workspaceFlavour,
|
const { id } = await buildShowcaseWorkspace(
|
||||||
async workspace => {
|
workspaceManager,
|
||||||
workspace.meta.setName(name);
|
workspaceFlavour,
|
||||||
if (runtimeConfig.enablePreloading) {
|
name
|
||||||
await buildShowcaseWorkspace(workspace, {
|
);
|
||||||
store: getCurrentStore(),
|
onCreate(id);
|
||||||
atoms: {
|
} else {
|
||||||
pageMode: setPageModeAtom,
|
const { id } = await workspaceManager.createWorkspace(
|
||||||
},
|
workspaceFlavour,
|
||||||
});
|
async workspace => {
|
||||||
} else {
|
workspace.meta.setName(name);
|
||||||
const page = workspace.createPage();
|
const page = workspace.createPage();
|
||||||
workspace.setPageMeta(page.id, {
|
workspace.setPageMeta(page.id, {
|
||||||
jumpOnce: true,
|
jumpOnce: true,
|
||||||
});
|
});
|
||||||
initEmptyPage(page);
|
initEmptyPage(page);
|
||||||
}
|
}
|
||||||
logger.debug('create first workspace');
|
);
|
||||||
}
|
onCreate(id);
|
||||||
);
|
}
|
||||||
onCreate(id);
|
|
||||||
},
|
},
|
||||||
[onCreate, workspaceManager]
|
[onCreate, workspaceManager]
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { Loading, Scrollable } from '@affine/component';
|
|||||||
import { EditorLoading } from '@affine/component/page-detail-skeleton';
|
import { EditorLoading } from '@affine/component/page-detail-skeleton';
|
||||||
import { Button, IconButton } from '@affine/component/ui/button';
|
import { Button, IconButton } from '@affine/component/ui/button';
|
||||||
import { ConfirmModal, Modal } from '@affine/component/ui/modal';
|
import { ConfirmModal, Modal } from '@affine/component/ui/modal';
|
||||||
import { openSettingModalAtom, type PageMode } from '@affine/core/atoms';
|
import { openSettingModalAtom } from '@affine/core/atoms';
|
||||||
import { useIsWorkspaceOwner } from '@affine/core/hooks/affine/use-is-workspace-owner';
|
import { useIsWorkspaceOwner } from '@affine/core/hooks/affine/use-is-workspace-owner';
|
||||||
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
||||||
import { useBlockSuiteWorkspacePageTitle } from '@affine/core/hooks/use-block-suite-workspace-page-title';
|
import { useBlockSuiteWorkspacePageTitle } from '@affine/core/hooks/use-block-suite-workspace-page-title';
|
||||||
@@ -10,15 +10,13 @@ import { useWorkspaceQuota } from '@affine/core/hooks/use-workspace-quota';
|
|||||||
import { Trans } from '@affine/i18n';
|
import { Trans } from '@affine/i18n';
|
||||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||||
import { CloseIcon, ToggleCollapseIcon } from '@blocksuite/icons';
|
import { CloseIcon, ToggleCollapseIcon } from '@blocksuite/icons';
|
||||||
import {
|
import type { Page as BlockSuitePage } from '@blocksuite/store';
|
||||||
type Page,
|
import { type Workspace as BlockSuiteWorkspace } from '@blocksuite/store';
|
||||||
type Workspace as BlockSuiteWorkspace,
|
|
||||||
} from '@blocksuite/store';
|
|
||||||
import * as Collapsible from '@radix-ui/react-collapsible';
|
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 { Workspace } from '@toeverything/infra';
|
import { Page, type PageMode, Workspace } from '@toeverything/infra';
|
||||||
import { useService } from '@toeverything/infra/di';
|
import { useService } from '@toeverything/infra/di';
|
||||||
import { atom, useAtom, useAtomValue, useSetAtom } from 'jotai';
|
import { atom, useAtom, useSetAtom } from 'jotai';
|
||||||
import {
|
import {
|
||||||
Fragment,
|
Fragment,
|
||||||
type PropsWithChildren,
|
type PropsWithChildren,
|
||||||
@@ -30,7 +28,6 @@ import {
|
|||||||
} from 'react';
|
} from 'react';
|
||||||
import { encodeStateAsUpdate } from 'yjs';
|
import { encodeStateAsUpdate } from 'yjs';
|
||||||
|
|
||||||
import { currentModeAtom } from '../../../atoms/mode';
|
|
||||||
import { pageHistoryModalAtom } from '../../../atoms/page-history';
|
import { pageHistoryModalAtom } from '../../../atoms/page-history';
|
||||||
import { timestampToLocalTime } from '../../../utils';
|
import { timestampToLocalTime } from '../../../utils';
|
||||||
import { BlockSuiteEditor } from '../../blocksuite/block-suite-editor';
|
import { BlockSuiteEditor } from '../../blocksuite/block-suite-editor';
|
||||||
@@ -93,7 +90,7 @@ const ModalContainer = ({
|
|||||||
|
|
||||||
interface HistoryEditorPreviewProps {
|
interface HistoryEditorPreviewProps {
|
||||||
ts?: string;
|
ts?: string;
|
||||||
snapshotPage?: Page;
|
snapshotPage?: BlockSuitePage;
|
||||||
mode: PageMode;
|
mode: PageMode;
|
||||||
onModeChange: (mode: PageMode) => void;
|
onModeChange: (mode: PageMode) => void;
|
||||||
title: string;
|
title: string;
|
||||||
@@ -450,8 +447,8 @@ const PageHistoryManager = ({
|
|||||||
[activeVersion, onClose, onRestore, snapshotPage]
|
[activeVersion, onClose, onRestore, snapshotPage]
|
||||||
);
|
);
|
||||||
|
|
||||||
const defaultPreviewPageMode = useAtomValue(currentModeAtom);
|
const page = useService(Page);
|
||||||
const [mode, setMode] = useState<PageMode>(defaultPreviewPageMode);
|
const [mode, setMode] = useState<PageMode>(page.mode.value);
|
||||||
|
|
||||||
const title = useBlockSuiteWorkspacePageTitle(workspace, pageId);
|
const title = useBlockSuiteWorkspacePageTitle(workspace, pageId);
|
||||||
|
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ import { ExportMenuItems } from '@affine/core/components/page-list';
|
|||||||
import { WorkspaceFlavour } from '@affine/env/workspace';
|
import { WorkspaceFlavour } from '@affine/env/workspace';
|
||||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||||
import { LinkIcon } from '@blocksuite/icons';
|
import { LinkIcon } from '@blocksuite/icons';
|
||||||
import { useAtomValue } from 'jotai';
|
import { Page, useLiveData } from '@toeverything/infra';
|
||||||
|
import { useService } from '@toeverything/infra/di';
|
||||||
|
|
||||||
import { currentModeAtom } from '../../../../atoms/mode';
|
|
||||||
import { useExportPage } from '../../../../hooks/affine/use-export-page';
|
import { useExportPage } from '../../../../hooks/affine/use-export-page';
|
||||||
import * as styles from './index.css';
|
import * as styles from './index.css';
|
||||||
import type { ShareMenuProps } from './share-menu';
|
import type { ShareMenuProps } from './share-menu';
|
||||||
@@ -17,6 +17,7 @@ export const ShareExport = ({
|
|||||||
currentPage,
|
currentPage,
|
||||||
}: ShareMenuProps) => {
|
}: ShareMenuProps) => {
|
||||||
const t = useAFFiNEI18N();
|
const t = useAFFiNEI18N();
|
||||||
|
const page = useService(Page);
|
||||||
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 = useAtomValue(currentModeAtom);
|
const currentMode = useLiveData(page.mode);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -8,14 +8,14 @@ import {
|
|||||||
import { PublicLinkDisableModal } from '@affine/component/disable-public-link';
|
import { PublicLinkDisableModal } from '@affine/component/disable-public-link';
|
||||||
import { Button } from '@affine/component/ui/button';
|
import { Button } from '@affine/component/ui/button';
|
||||||
import { Menu, MenuItem, MenuTrigger } from '@affine/component/ui/menu';
|
import { Menu, MenuItem, MenuTrigger } from '@affine/component/ui/menu';
|
||||||
import type { PageMode } from '@affine/core/atoms';
|
|
||||||
import { currentModeAtom } from '@affine/core/atoms/mode';
|
|
||||||
import { useIsSharedPage } from '@affine/core/hooks/affine/use-is-shared-page';
|
import { useIsSharedPage } from '@affine/core/hooks/affine/use-is-shared-page';
|
||||||
import { useServerBaseUrl } from '@affine/core/hooks/affine/use-server-config';
|
import { useServerBaseUrl } from '@affine/core/hooks/affine/use-server-config';
|
||||||
import { WorkspaceFlavour } from '@affine/env/workspace';
|
import { WorkspaceFlavour } from '@affine/env/workspace';
|
||||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||||
import { ArrowRightSmallIcon } from '@blocksuite/icons';
|
import { ArrowRightSmallIcon } from '@blocksuite/icons';
|
||||||
import { useAtomValue } from 'jotai';
|
import { useService } from '@toeverything/infra';
|
||||||
|
import { useLiveData } from '@toeverything/infra';
|
||||||
|
import { Page, type PageMode } from '@toeverything/infra';
|
||||||
import { useMemo, useState } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
|
|
||||||
@@ -56,6 +56,7 @@ export const AffineSharePage = (props: ShareMenuProps) => {
|
|||||||
currentPage,
|
currentPage,
|
||||||
} = props;
|
} = props;
|
||||||
const pageId = currentPage.id;
|
const pageId = currentPage.id;
|
||||||
|
const page = useService(Page);
|
||||||
const [showDisable, setShowDisable] = useState(false);
|
const [showDisable, setShowDisable] = useState(false);
|
||||||
const {
|
const {
|
||||||
isSharedPage,
|
isSharedPage,
|
||||||
@@ -64,14 +65,15 @@ export const AffineSharePage = (props: ShareMenuProps) => {
|
|||||||
currentShareMode,
|
currentShareMode,
|
||||||
disableShare,
|
disableShare,
|
||||||
} = useIsSharedPage(workspaceId, currentPage.id);
|
} = useIsSharedPage(workspaceId, currentPage.id);
|
||||||
const currentPageMode = useAtomValue(currentModeAtom);
|
|
||||||
|
const currentPageMode = useLiveData(page.mode);
|
||||||
|
|
||||||
const defaultMode = useMemo(() => {
|
const defaultMode = useMemo(() => {
|
||||||
if (isSharedPage) {
|
if (isSharedPage) {
|
||||||
// if it's a shared page, use the share mode
|
// if it's a shared page, use the share mode
|
||||||
return currentShareMode;
|
return currentShareMode;
|
||||||
}
|
}
|
||||||
// default to current page mode
|
// default to page mode
|
||||||
return currentPageMode;
|
return currentPageMode;
|
||||||
}, [currentPageMode, currentShareMode, isSharedPage]);
|
}, [currentPageMode, currentShareMode, isSharedPage]);
|
||||||
const [mode, setMode] = useState<PageMode>(defaultMode);
|
const [mode, setMode] = useState<PageMode>(defaultMode);
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import type { PageMode } from '@affine/core/atoms';
|
|
||||||
import type { BlockElement } from '@blocksuite/lit';
|
import type { BlockElement } from '@blocksuite/lit';
|
||||||
import type {
|
import type {
|
||||||
AffineEditorContainer,
|
AffineEditorContainer,
|
||||||
@@ -6,6 +5,7 @@ import type {
|
|||||||
EdgelessEditor,
|
EdgelessEditor,
|
||||||
} from '@blocksuite/presets';
|
} from '@blocksuite/presets';
|
||||||
import { type Page, Slot } from '@blocksuite/store';
|
import { type Page, Slot } from '@blocksuite/store';
|
||||||
|
import type { PageMode } from '@toeverything/infra';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import type React from 'react';
|
import type React from 'react';
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import {
|
|||||||
MenuSeparator,
|
MenuSeparator,
|
||||||
} from '@affine/component/ui/menu';
|
} from '@affine/component/ui/menu';
|
||||||
import { openHistoryTipsModalAtom } from '@affine/core/atoms';
|
import { openHistoryTipsModalAtom } from '@affine/core/atoms';
|
||||||
import { currentModeAtom } from '@affine/core/atoms/mode';
|
|
||||||
import { PageHistoryModal } from '@affine/core/components/affine/page-history-modal';
|
import { PageHistoryModal } from '@affine/core/components/affine/page-history-modal';
|
||||||
import { Export, MoveToTrash } from '@affine/core/components/page-list';
|
import { Export, MoveToTrash } from '@affine/core/components/page-list';
|
||||||
import { useBlockSuiteMetaHelper } from '@affine/core/hooks/affine/use-block-suite-meta-helper';
|
import { useBlockSuiteMetaHelper } from '@affine/core/hooks/affine/use-block-suite-meta-helper';
|
||||||
@@ -26,8 +25,8 @@ import {
|
|||||||
ImportIcon,
|
ImportIcon,
|
||||||
PageIcon,
|
PageIcon,
|
||||||
} from '@blocksuite/icons';
|
} from '@blocksuite/icons';
|
||||||
import { useService, Workspace } from '@toeverything/infra';
|
import { Page, useLiveData, useService, Workspace } from '@toeverything/infra';
|
||||||
import { useAtomValue, useSetAtom } from 'jotai';
|
import { useSetAtom } from 'jotai';
|
||||||
import { useCallback, useState } from 'react';
|
import { useCallback, useState } from 'react';
|
||||||
|
|
||||||
import { HeaderDropDownButton } from '../../../pure/header-drop-down-button';
|
import { HeaderDropDownButton } from '../../../pure/header-drop-down-button';
|
||||||
@@ -55,12 +54,12 @@ export const PageHeaderMenuButton = ({
|
|||||||
const pageMeta = useBlockSuitePageMeta(blockSuiteWorkspace).find(
|
const pageMeta = useBlockSuitePageMeta(blockSuiteWorkspace).find(
|
||||||
meta => meta.id === pageId
|
meta => meta.id === pageId
|
||||||
);
|
);
|
||||||
const currentMode = useAtomValue(currentModeAtom);
|
const page = useService(Page);
|
||||||
|
const currentMode = useLiveData(page.mode);
|
||||||
|
|
||||||
const { favorite, toggleFavorite } = useFavorite(pageId);
|
const { favorite, toggleFavorite } = useFavorite(pageId);
|
||||||
|
|
||||||
const { togglePageMode, duplicate } =
|
const { duplicate } = useBlockSuiteMetaHelper(blockSuiteWorkspace);
|
||||||
useBlockSuiteMetaHelper(blockSuiteWorkspace);
|
|
||||||
const { importFile } = usePageHelper(blockSuiteWorkspace);
|
const { importFile } = usePageHelper(blockSuiteWorkspace);
|
||||||
const { setTrashModal } = useTrashModalHelper(blockSuiteWorkspace);
|
const { setTrashModal } = useTrashModalHelper(blockSuiteWorkspace);
|
||||||
|
|
||||||
@@ -86,13 +85,13 @@ export const PageHeaderMenuButton = ({
|
|||||||
}, [pageId, pageMeta, setTrashModal]);
|
}, [pageId, pageMeta, setTrashModal]);
|
||||||
|
|
||||||
const handleSwitchMode = useCallback(() => {
|
const handleSwitchMode = useCallback(() => {
|
||||||
togglePageMode(pageId);
|
page.toggleMode();
|
||||||
toast(
|
toast(
|
||||||
currentMode === 'page'
|
currentMode === 'page'
|
||||||
? t['com.affine.toastMessage.edgelessMode']()
|
? t['com.affine.toastMessage.edgelessMode']()
|
||||||
: t['com.affine.toastMessage.pageMode']()
|
: t['com.affine.toastMessage.pageMode']()
|
||||||
);
|
);
|
||||||
}, [currentMode, pageId, t, togglePageMode]);
|
}, [currentMode, page, t]);
|
||||||
const menuItemStyle = {
|
const menuItemStyle = {
|
||||||
padding: '4px 12px',
|
padding: '4px 12px',
|
||||||
transition: 'all 0.3s',
|
transition: 'all 0.3s',
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
import { Tooltip } from '@affine/component/ui/tooltip';
|
import { Tooltip } from '@affine/component/ui/tooltip';
|
||||||
import { useBlockSuitePageMeta } from '@affine/core/hooks/use-block-suite-page-meta';
|
import { useBlockSuitePageMeta } from '@affine/core/hooks/use-block-suite-page-meta';
|
||||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||||
import { useAtomValue } from 'jotai';
|
import {
|
||||||
|
Page,
|
||||||
|
type PageMode,
|
||||||
|
useLiveData,
|
||||||
|
useService,
|
||||||
|
} from '@toeverything/infra';
|
||||||
import type { CSSProperties } from 'react';
|
import type { CSSProperties } from 'react';
|
||||||
import { useCallback, useEffect } from 'react';
|
import { useCallback, useEffect } from 'react';
|
||||||
|
|
||||||
import type { PageMode } from '../../../atoms';
|
|
||||||
import { currentModeAtom } from '../../../atoms/mode';
|
|
||||||
import { useBlockSuiteMetaHelper } from '../../../hooks/affine/use-block-suite-meta-helper';
|
|
||||||
import type { BlockSuiteWorkspace } from '../../../shared';
|
import type { BlockSuiteWorkspace } from '../../../shared';
|
||||||
import { toast } from '../../../utils';
|
import { toast } from '../../../utils';
|
||||||
import { StyledEditorModeSwitch, StyledKeyboardItem } from './style';
|
import { StyledEditorModeSwitch, StyledKeyboardItem } from './style';
|
||||||
@@ -44,10 +46,9 @@ export const EditorModeSwitch = ({
|
|||||||
meta => meta.id === pageId
|
meta => meta.id === pageId
|
||||||
);
|
);
|
||||||
const trash = pageMeta?.trash ?? false;
|
const trash = pageMeta?.trash ?? false;
|
||||||
|
const page = useService(Page);
|
||||||
|
|
||||||
const { togglePageMode, switchToEdgelessMode, switchToPageMode } =
|
const currentMode = useLiveData(page.mode);
|
||||||
useBlockSuiteMetaHelper(blockSuiteWorkspace);
|
|
||||||
const currentMode = useAtomValue(currentModeAtom);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (trash || isPublic) {
|
if (trash || isPublic) {
|
||||||
@@ -56,7 +57,7 @@ export const EditorModeSwitch = ({
|
|||||||
const keydown = (e: KeyboardEvent) => {
|
const keydown = (e: KeyboardEvent) => {
|
||||||
if (e.code === 'KeyS' && e.altKey) {
|
if (e.code === 'KeyS' && e.altKey) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
togglePageMode(pageId);
|
page.toggleMode();
|
||||||
toast(
|
toast(
|
||||||
currentMode === 'page'
|
currentMode === 'page'
|
||||||
? t['com.affine.toastMessage.edgelessMode']()
|
? t['com.affine.toastMessage.edgelessMode']()
|
||||||
@@ -67,23 +68,23 @@ export const EditorModeSwitch = ({
|
|||||||
document.addEventListener('keydown', keydown, { capture: true });
|
document.addEventListener('keydown', keydown, { capture: true });
|
||||||
return () =>
|
return () =>
|
||||||
document.removeEventListener('keydown', keydown, { capture: true });
|
document.removeEventListener('keydown', keydown, { capture: true });
|
||||||
}, [currentMode, isPublic, pageId, t, togglePageMode, trash]);
|
}, [currentMode, isPublic, page, pageId, t, trash]);
|
||||||
|
|
||||||
const onSwitchToPageMode = useCallback(() => {
|
const onSwitchToPageMode = useCallback(() => {
|
||||||
if (currentMode === 'page' || isPublic) {
|
if (currentMode === 'page' || isPublic) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
switchToPageMode(pageId);
|
page.setMode('page');
|
||||||
toast(t['com.affine.toastMessage.pageMode']());
|
toast(t['com.affine.toastMessage.pageMode']());
|
||||||
}, [currentMode, isPublic, pageId, switchToPageMode, t]);
|
}, [currentMode, isPublic, page, t]);
|
||||||
|
|
||||||
const onSwitchToEdgelessMode = useCallback(() => {
|
const onSwitchToEdgelessMode = useCallback(() => {
|
||||||
if (currentMode === 'edgeless' || isPublic) {
|
if (currentMode === 'edgeless' || isPublic) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
switchToEdgelessMode(pageId);
|
page.setMode('edgeless');
|
||||||
toast(t['com.affine.toastMessage.edgelessMode']());
|
toast(t['com.affine.toastMessage.edgelessMode']());
|
||||||
}, [currentMode, isPublic, pageId, switchToEdgelessMode, t]);
|
}, [currentMode, isPublic, page, t]);
|
||||||
|
|
||||||
const shouldHide = useCallback(
|
const shouldHide = useCallback(
|
||||||
(mode: PageMode) =>
|
(mode: PageMode) =>
|
||||||
|
|||||||
@@ -3,36 +3,35 @@ import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
|||||||
import { usePageMetaHelper } from '@affine/core/hooks/use-block-suite-page-meta';
|
import { usePageMetaHelper } from '@affine/core/hooks/use-block-suite-page-meta';
|
||||||
import { useBlockSuiteWorkspaceHelper } from '@affine/core/hooks/use-block-suite-workspace-helper';
|
import { useBlockSuiteWorkspaceHelper } from '@affine/core/hooks/use-block-suite-workspace-helper';
|
||||||
import { WorkspaceSubPath } from '@affine/core/shared';
|
import { WorkspaceSubPath } from '@affine/core/shared';
|
||||||
import { initEmptyPage } from '@toeverything/infra/blocksuite';
|
import { useService } from '@toeverything/infra';
|
||||||
import { useAtomValue, useSetAtom } from 'jotai';
|
import { PageRecordList } from '@toeverything/infra';
|
||||||
|
import { initEmptyPage } from '@toeverything/infra';
|
||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useMemo } from 'react';
|
||||||
|
|
||||||
import { pageSettingsAtom, setPageModeAtom } from '../../../atoms';
|
|
||||||
import { useNavigateHelper } from '../../../hooks/use-navigate-helper';
|
import { useNavigateHelper } from '../../../hooks/use-navigate-helper';
|
||||||
import type { BlockSuiteWorkspace } from '../../../shared';
|
import type { BlockSuiteWorkspace } from '../../../shared';
|
||||||
|
|
||||||
export const usePageHelper = (blockSuiteWorkspace: BlockSuiteWorkspace) => {
|
export const usePageHelper = (blockSuiteWorkspace: BlockSuiteWorkspace) => {
|
||||||
const { openPage, jumpToSubPath } = useNavigateHelper();
|
const { openPage, jumpToSubPath } = useNavigateHelper();
|
||||||
const { createPage } = useBlockSuiteWorkspaceHelper(blockSuiteWorkspace);
|
const { createPage } = useBlockSuiteWorkspaceHelper(blockSuiteWorkspace);
|
||||||
const pageSettings = useAtomValue(pageSettingsAtom);
|
|
||||||
const { setPageMeta } = usePageMetaHelper(blockSuiteWorkspace);
|
const { setPageMeta } = usePageMetaHelper(blockSuiteWorkspace);
|
||||||
|
const pageRecordList = useService(PageRecordList);
|
||||||
|
|
||||||
const isPreferredEdgeless = useCallback(
|
const isPreferredEdgeless = useCallback(
|
||||||
(pageId: string) => pageSettings[pageId]?.mode === 'edgeless',
|
(pageId: string) =>
|
||||||
[pageSettings]
|
pageRecordList.record(pageId).value?.mode.value === 'edgeless',
|
||||||
|
[pageRecordList]
|
||||||
);
|
);
|
||||||
|
|
||||||
const setPageMode = useSetAtom(setPageModeAtom);
|
|
||||||
|
|
||||||
const createPageAndOpen = useCallback(
|
const createPageAndOpen = useCallback(
|
||||||
(mode?: 'page' | 'edgeless') => {
|
(mode?: 'page' | 'edgeless') => {
|
||||||
const page = createPage();
|
const page = createPage();
|
||||||
initEmptyPage(page);
|
initEmptyPage(page);
|
||||||
setPageMode(page.id, mode || 'page');
|
pageRecordList.record(page.id).value?.setMode(mode || 'page');
|
||||||
openPage(blockSuiteWorkspace.id, page.id);
|
openPage(blockSuiteWorkspace.id, page.id);
|
||||||
return page;
|
return page;
|
||||||
},
|
},
|
||||||
[blockSuiteWorkspace.id, createPage, openPage, setPageMode]
|
[blockSuiteWorkspace.id, createPage, openPage, pageRecordList]
|
||||||
);
|
);
|
||||||
|
|
||||||
const createEdgelessAndOpen = useCallback(() => {
|
const createEdgelessAndOpen = useCallback(() => {
|
||||||
@@ -89,17 +88,17 @@ export const usePageHelper = (blockSuiteWorkspace: BlockSuiteWorkspace) => {
|
|||||||
|
|
||||||
return useMemo(() => {
|
return useMemo(() => {
|
||||||
return {
|
return {
|
||||||
|
isPreferredEdgeless,
|
||||||
createPage: createPageAndOpen,
|
createPage: createPageAndOpen,
|
||||||
createEdgeless: createEdgelessAndOpen,
|
createEdgeless: createEdgelessAndOpen,
|
||||||
importFile: importFileAndOpen,
|
importFile: importFileAndOpen,
|
||||||
isPreferredEdgeless: isPreferredEdgeless,
|
|
||||||
createLinkedPage: createLinkedPageAndOpen,
|
createLinkedPage: createLinkedPageAndOpen,
|
||||||
};
|
};
|
||||||
}, [
|
}, [
|
||||||
|
isPreferredEdgeless,
|
||||||
createEdgelessAndOpen,
|
createEdgelessAndOpen,
|
||||||
createLinkedPageAndOpen,
|
createLinkedPageAndOpen,
|
||||||
createPageAndOpen,
|
createPageAndOpen,
|
||||||
importFileAndOpen,
|
importFileAndOpen,
|
||||||
isPreferredEdgeless,
|
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { PageMode } from '@affine/core/atoms';
|
import type { PageMode } from '@toeverything/infra';
|
||||||
import { useCurrentLoginStatus } from '@affine/core/hooks/affine/use-current-login-status';
|
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
|
import { useCurrentLoginStatus } from '../../../hooks/affine/use-current-login-status';
|
||||||
import { AuthenticatedItem } from './authenticated-item';
|
import { AuthenticatedItem } from './authenticated-item';
|
||||||
import { PresentButton } from './present';
|
import { PresentButton } from './present';
|
||||||
import * as styles from './styles.css';
|
import * as styles from './styles.css';
|
||||||
|
|||||||
@@ -4,15 +4,20 @@ import { useActiveBlocksuiteEditor } from '@affine/core/hooks/use-block-suite-ed
|
|||||||
import { useBlockSuiteWorkspacePage } from '@affine/core/hooks/use-block-suite-workspace-page';
|
import { useBlockSuiteWorkspacePage } from '@affine/core/hooks/use-block-suite-workspace-page';
|
||||||
import { assertExists, DisposableGroup } from '@blocksuite/global/utils';
|
import { assertExists, DisposableGroup } from '@blocksuite/global/utils';
|
||||||
import type { AffineEditorContainer } from '@blocksuite/presets';
|
import type { AffineEditorContainer } from '@blocksuite/presets';
|
||||||
import type { Page, Workspace } from '@blocksuite/store';
|
import type { Workspace } from '@blocksuite/store';
|
||||||
|
import type { Page as BlockSuitePage } from '@blocksuite/store';
|
||||||
|
import {
|
||||||
|
Page,
|
||||||
|
type PageMode,
|
||||||
|
useLiveData,
|
||||||
|
useService,
|
||||||
|
} from '@toeverything/infra';
|
||||||
import { fontStyleOptions } from '@toeverything/infra/atom';
|
import { fontStyleOptions } from '@toeverything/infra/atom';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { useAtomValue } from 'jotai';
|
|
||||||
import type { CSSProperties } from 'react';
|
import type { CSSProperties } from 'react';
|
||||||
import { memo, Suspense, useCallback, useMemo } from 'react';
|
import { memo, Suspense, useCallback, useMemo } from 'react';
|
||||||
import { useLocation } from 'react-router-dom';
|
import { useLocation } from 'react-router-dom';
|
||||||
|
|
||||||
import { type PageMode, pageSettingFamily } from '../atoms';
|
|
||||||
import { useAppSettingHelper } from '../hooks/affine/use-app-setting-helper';
|
import { useAppSettingHelper } from '../hooks/affine/use-app-setting-helper';
|
||||||
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';
|
||||||
@@ -23,7 +28,7 @@ declare global {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type OnLoadEditor = (
|
export type OnLoadEditor = (
|
||||||
page: Page,
|
page: BlockSuitePage,
|
||||||
editor: AffineEditorContainer
|
editor: AffineEditorContainer
|
||||||
) => () => void;
|
) => () => void;
|
||||||
|
|
||||||
@@ -41,23 +46,19 @@ function useRouterHash() {
|
|||||||
|
|
||||||
const PageDetailEditorMain = memo(function PageDetailEditorMain({
|
const PageDetailEditorMain = memo(function PageDetailEditorMain({
|
||||||
page,
|
page,
|
||||||
pageId,
|
|
||||||
onLoad,
|
onLoad,
|
||||||
isPublic,
|
isPublic,
|
||||||
publishMode,
|
publishMode,
|
||||||
}: PageDetailEditorProps & { page: Page }) {
|
}: PageDetailEditorProps & { page: BlockSuitePage }) {
|
||||||
const pageSettingAtom = pageSettingFamily(pageId);
|
const currentMode = useLiveData(useService(Page).mode);
|
||||||
const pageSetting = useAtomValue(pageSettingAtom);
|
|
||||||
|
|
||||||
const mode = useMemo(() => {
|
const mode = useMemo(() => {
|
||||||
const currentMode = pageSetting.mode;
|
|
||||||
const shareMode = publishMode || currentMode;
|
const shareMode = publishMode || currentMode;
|
||||||
|
|
||||||
if (isPublic) {
|
if (isPublic) {
|
||||||
return shareMode;
|
return shareMode;
|
||||||
}
|
}
|
||||||
return currentMode;
|
return currentMode;
|
||||||
}, [isPublic, publishMode, pageSetting.mode]);
|
}, [isPublic, publishMode, currentMode]);
|
||||||
|
|
||||||
const { appSettings } = useAppSettingHelper();
|
const { appSettings } = useAppSettingHelper();
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { currentPageIdAtom } from '@affine/core/atoms/mode';
|
|
||||||
import { useCollectionManager } from '@affine/core/components/page-list';
|
import { useCollectionManager } from '@affine/core/components/page-list';
|
||||||
import {
|
import {
|
||||||
useBlockSuitePageMeta,
|
useBlockSuitePageMeta,
|
||||||
@@ -16,19 +15,24 @@ import {
|
|||||||
TodayIcon,
|
TodayIcon,
|
||||||
ViewLayersIcon,
|
ViewLayersIcon,
|
||||||
} from '@blocksuite/icons';
|
} from '@blocksuite/icons';
|
||||||
import { type PageMeta } from '@blocksuite/store';
|
import type { PageMeta } from '@blocksuite/store';
|
||||||
import { useService, Workspace } from '@toeverything/infra';
|
import {
|
||||||
import { getCurrentStore } from '@toeverything/infra/atom';
|
Page,
|
||||||
|
PageRecordList,
|
||||||
|
useLiveData,
|
||||||
|
Workspace,
|
||||||
|
} from '@toeverything/infra';
|
||||||
import {
|
import {
|
||||||
type AffineCommand,
|
type AffineCommand,
|
||||||
AffineCommandRegistry,
|
AffineCommandRegistry,
|
||||||
type CommandCategory,
|
type CommandCategory,
|
||||||
PreconditionStrategy,
|
PreconditionStrategy,
|
||||||
} from '@toeverything/infra/command';
|
} from '@toeverything/infra/command';
|
||||||
|
import { useService, useServiceOptional } from '@toeverything/infra/di';
|
||||||
import { atom, useAtomValue } from 'jotai';
|
import { atom, useAtomValue } from 'jotai';
|
||||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import { pageSettingsAtom, recentPageIdsBaseAtom } from '../../../atoms';
|
import { recentPageIdsBaseAtom } from '../../../atoms';
|
||||||
import { useNavigateHelper } from '../../../hooks/use-navigate-helper';
|
import { useNavigateHelper } from '../../../hooks/use-navigate-helper';
|
||||||
import { usePageHelper } from '../../blocksuite/block-suite-page-list/utils';
|
import { usePageHelper } from '../../blocksuite/block-suite-page-list/utils';
|
||||||
import { filterSortAndGroupCommands } from './filter-commands';
|
import { filterSortAndGroupCommands } from './filter-commands';
|
||||||
@@ -96,7 +100,6 @@ const useRecentPages = () => {
|
|||||||
export const pageToCommand = (
|
export const pageToCommand = (
|
||||||
category: CommandCategory,
|
category: CommandCategory,
|
||||||
page: PageMeta,
|
page: PageMeta,
|
||||||
store: ReturnType<typeof getCurrentStore>,
|
|
||||||
navigationHelper: ReturnType<typeof useNavigateHelper>,
|
navigationHelper: ReturnType<typeof useNavigateHelper>,
|
||||||
getPageTitle: ReturnType<typeof useGetBlockSuiteWorkspacePageTitle>,
|
getPageTitle: ReturnType<typeof useGetBlockSuiteWorkspacePageTitle>,
|
||||||
isPageJournal: (pageId: string) => boolean,
|
isPageJournal: (pageId: string) => boolean,
|
||||||
@@ -105,7 +108,8 @@ export const pageToCommand = (
|
|||||||
subTitle?: string,
|
subTitle?: string,
|
||||||
blockId?: string
|
blockId?: string
|
||||||
): CMDKCommand => {
|
): CMDKCommand => {
|
||||||
const pageMode = store.get(pageSettingsAtom)?.[page.id]?.mode;
|
const pageMode = workspace.services.get(PageRecordList).record(page.id).value
|
||||||
|
?.mode.value;
|
||||||
|
|
||||||
const title = getPageTitle(page.id) || t['Untitled']();
|
const title = getPageTitle(page.id) || t['Untitled']();
|
||||||
const commandLabel = {
|
const commandLabel = {
|
||||||
@@ -147,7 +151,6 @@ export const pageToCommand = (
|
|||||||
export const usePageCommands = () => {
|
export const usePageCommands = () => {
|
||||||
const recentPages = useRecentPages();
|
const recentPages = useRecentPages();
|
||||||
const pages = useWorkspacePages();
|
const pages = useWorkspacePages();
|
||||||
const store = getCurrentStore();
|
|
||||||
const workspace = useService(Workspace);
|
const workspace = useService(Workspace);
|
||||||
const pageHelper = usePageHelper(workspace.blockSuiteWorkspace);
|
const pageHelper = usePageHelper(workspace.blockSuiteWorkspace);
|
||||||
const pageMetaHelper = usePageMetaHelper(workspace.blockSuiteWorkspace);
|
const pageMetaHelper = usePageMetaHelper(workspace.blockSuiteWorkspace);
|
||||||
@@ -185,7 +188,6 @@ export const usePageCommands = () => {
|
|||||||
return pageToCommand(
|
return pageToCommand(
|
||||||
'affine:recent',
|
'affine:recent',
|
||||||
page,
|
page,
|
||||||
store,
|
|
||||||
navigationHelper,
|
navigationHelper,
|
||||||
getPageTitle,
|
getPageTitle,
|
||||||
isPageJournal,
|
isPageJournal,
|
||||||
@@ -207,9 +209,7 @@ export const usePageCommands = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
results = pages.map(page => {
|
results = pages.map(page => {
|
||||||
const pageMode = store.get(pageSettingsAtom)?.[page.id]?.mode;
|
const category = 'affine:pages';
|
||||||
const category =
|
|
||||||
pageMode === 'edgeless' ? 'affine:edgeless' : 'affine:pages';
|
|
||||||
|
|
||||||
const subTitle = resultValues.find(
|
const subTitle = resultValues.find(
|
||||||
result => result.space === page.id
|
result => result.space === page.id
|
||||||
@@ -220,7 +220,6 @@ export const usePageCommands = () => {
|
|||||||
const command = pageToCommand(
|
const command = pageToCommand(
|
||||||
category,
|
category,
|
||||||
page,
|
page,
|
||||||
store,
|
|
||||||
navigationHelper,
|
navigationHelper,
|
||||||
getPageTitle,
|
getPageTitle,
|
||||||
isPageJournal,
|
isPageJournal,
|
||||||
@@ -288,7 +287,6 @@ export const usePageCommands = () => {
|
|||||||
searchTime,
|
searchTime,
|
||||||
query,
|
query,
|
||||||
recentPages,
|
recentPages,
|
||||||
store,
|
|
||||||
navigationHelper,
|
navigationHelper,
|
||||||
getPageTitle,
|
getPageTitle,
|
||||||
isPageJournal,
|
isPageJournal,
|
||||||
@@ -367,11 +365,9 @@ export const useCollectionsCommands = () => {
|
|||||||
export const useCMDKCommandGroups = () => {
|
export const useCMDKCommandGroups = () => {
|
||||||
const pageCommands = usePageCommands();
|
const pageCommands = usePageCommands();
|
||||||
const collectionCommands = useCollectionsCommands();
|
const collectionCommands = useCollectionsCommands();
|
||||||
const currentPageId = useAtomValue(currentPageIdAtom);
|
|
||||||
const pageSettings = useAtomValue(pageSettingsAtom);
|
const currentPage = useServiceOptional(Page);
|
||||||
const currentPageMode = currentPageId
|
const currentPageMode = useLiveData(currentPage?.mode);
|
||||||
? pageSettings[currentPageId]?.mode
|
|
||||||
: undefined;
|
|
||||||
const affineCommands = useMemo(() => {
|
const affineCommands = useMemo(() => {
|
||||||
return getAllCommand({
|
return getAllCommand({
|
||||||
pageMode: currentPageMode,
|
pageMode: currentPageMode,
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
import { Tooltip } from '@affine/component/ui/tooltip';
|
import { Tooltip } from '@affine/component/ui/tooltip';
|
||||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||||
import { CloseIcon, NewIcon } from '@blocksuite/icons';
|
import { CloseIcon, NewIcon } from '@blocksuite/icons';
|
||||||
|
import { useLiveData, useServiceOptional } from '@toeverything/infra';
|
||||||
|
import { Page } from '@toeverything/infra';
|
||||||
import { useSetAtom } from 'jotai/react';
|
import { useSetAtom } from 'jotai/react';
|
||||||
import { useAtomValue } from 'jotai/react';
|
|
||||||
import { useCallback, useState } from 'react';
|
import { useCallback, useState } from 'react';
|
||||||
import { useParams } from 'react-router-dom';
|
|
||||||
|
|
||||||
import { openSettingModalAtom } from '../../../atoms';
|
import { openSettingModalAtom } from '../../../atoms';
|
||||||
import { currentModeAtom } from '../../../atoms/mode';
|
|
||||||
import type { SettingProps } from '../../affine/setting-modal';
|
import type { SettingProps } from '../../affine/setting-modal';
|
||||||
import { ContactIcon, HelpIcon, KeyboardIcon } from './icons';
|
import { ContactIcon, HelpIcon, KeyboardIcon } from './icons';
|
||||||
import {
|
import {
|
||||||
@@ -29,7 +28,9 @@ 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 mode = useAtomValue(currentModeAtom);
|
const page = useServiceOptional(Page);
|
||||||
|
const pageId = page?.id;
|
||||||
|
const mode = useLiveData(page?.mode);
|
||||||
const setOpenSettingModalAtom = useSetAtom(openSettingModalAtom);
|
const setOpenSettingModalAtom = useSetAtom(openSettingModalAtom);
|
||||||
const [spread, setShowSpread] = useState(false);
|
const [spread, setShowSpread] = useState(false);
|
||||||
const t = useAFFiNEI18N();
|
const t = useAFFiNEI18N();
|
||||||
@@ -53,8 +54,6 @@ export const HelpIsland = () => {
|
|||||||
[openSettingModal]
|
[openSettingModal]
|
||||||
);
|
);
|
||||||
|
|
||||||
const { pageId } = useParams();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledIsland
|
<StyledIsland
|
||||||
spread={spread}
|
spread={spread}
|
||||||
|
|||||||
@@ -5,11 +5,10 @@ import { EdgelessIcon, PageIcon } from '@blocksuite/icons';
|
|||||||
import type { PageMeta, Workspace } from '@blocksuite/store';
|
import type { PageMeta, Workspace } from '@blocksuite/store';
|
||||||
import { useDraggable } from '@dnd-kit/core';
|
import { useDraggable } from '@dnd-kit/core';
|
||||||
import * as Collapsible from '@radix-ui/react-collapsible';
|
import * as Collapsible from '@radix-ui/react-collapsible';
|
||||||
import { useAtomValue } from 'jotai/index';
|
import { PageRecordList, useLiveData, useService } from '@toeverything/infra';
|
||||||
import React, { useCallback, useMemo } from 'react';
|
import React, { useCallback, useMemo } from 'react';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
|
|
||||||
import { pageSettingFamily } from '../../../../atoms';
|
|
||||||
import { getDragItemId } from '../../../../hooks/affine/use-sidebar-drag';
|
import { getDragItemId } from '../../../../hooks/affine/use-sidebar-drag';
|
||||||
import { useNavigateHelper } from '../../../../hooks/use-navigate-helper';
|
import { useNavigateHelper } from '../../../../hooks/use-navigate-helper';
|
||||||
import { DragMenuItemOverlay } from '../components/drag-menu-item-overlay';
|
import { DragMenuItemOverlay } from '../components/drag-menu-item-overlay';
|
||||||
@@ -37,12 +36,13 @@ export const Page = ({
|
|||||||
|
|
||||||
const pageId = page.id;
|
const pageId = page.id;
|
||||||
const active = params.pageId === pageId;
|
const active = params.pageId === pageId;
|
||||||
const setting = useAtomValue(pageSettingFamily(pageId));
|
const pageRecord = useLiveData(useService(PageRecordList).record(pageId));
|
||||||
|
const pageMode = useLiveData(pageRecord?.mode);
|
||||||
const dragItemId = getDragItemId('collectionPage', pageId);
|
const dragItemId = getDragItemId('collectionPage', pageId);
|
||||||
|
|
||||||
const icon = useMemo(() => {
|
const icon = useMemo(() => {
|
||||||
return setting?.mode === 'edgeless' ? <EdgelessIcon /> : <PageIcon />;
|
return pageMode === 'edgeless' ? <EdgelessIcon /> : <PageIcon />;
|
||||||
}, [setting?.mode]);
|
}, [pageMode]);
|
||||||
|
|
||||||
const { jumpToPage } = useNavigateHelper();
|
const { jumpToPage } = useNavigateHelper();
|
||||||
const clickPage = useCallback(() => {
|
const clickPage = useCallback(() => {
|
||||||
|
|||||||
@@ -4,11 +4,10 @@ import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
|||||||
import { EdgelessIcon, PageIcon } from '@blocksuite/icons';
|
import { EdgelessIcon, PageIcon } from '@blocksuite/icons';
|
||||||
import { type PageMeta, type Workspace } from '@blocksuite/store';
|
import { type PageMeta, type Workspace } from '@blocksuite/store';
|
||||||
import * as Collapsible from '@radix-ui/react-collapsible';
|
import * as Collapsible from '@radix-ui/react-collapsible';
|
||||||
import { useAtomValue } from 'jotai/react';
|
import { PageRecordList, useLiveData, useService } from '@toeverything/infra';
|
||||||
import { useMemo, useState } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
|
|
||||||
import { pageSettingFamily } from '../../../../atoms';
|
|
||||||
import * as styles from '../favorite/styles.css';
|
import * as styles from '../favorite/styles.css';
|
||||||
import { PostfixItem } from './postfix-item';
|
import { PostfixItem } from './postfix-item';
|
||||||
export interface ReferencePageProps {
|
export interface ReferencePageProps {
|
||||||
@@ -28,10 +27,11 @@ export const ReferencePage = ({
|
|||||||
const params = useParams();
|
const params = useParams();
|
||||||
const active = params.pageId === pageId;
|
const active = params.pageId === pageId;
|
||||||
|
|
||||||
const setting = useAtomValue(pageSettingFamily(pageId));
|
const pageRecord = useLiveData(useService(PageRecordList).record(pageId));
|
||||||
|
const pageMode = useLiveData(pageRecord?.mode);
|
||||||
const icon = useMemo(() => {
|
const icon = useMemo(() => {
|
||||||
return setting?.mode === 'edgeless' ? <EdgelessIcon /> : <PageIcon />;
|
return pageMode === 'edgeless' ? <EdgelessIcon /> : <PageIcon />;
|
||||||
}, [setting?.mode]);
|
}, [pageMode]);
|
||||||
|
|
||||||
const references = useBlockSuitePageReferences(workspace, pageId);
|
const references = useBlockSuitePageReferences(workspace, pageId);
|
||||||
const referencesToShow = useMemo(() => {
|
const referencesToShow = useMemo(() => {
|
||||||
|
|||||||
@@ -4,11 +4,12 @@ import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
|||||||
import { EdgelessIcon, PageIcon } from '@blocksuite/icons';
|
import { EdgelessIcon, PageIcon } from '@blocksuite/icons';
|
||||||
import { useDraggable } from '@dnd-kit/core';
|
import { useDraggable } from '@dnd-kit/core';
|
||||||
import * as Collapsible from '@radix-ui/react-collapsible';
|
import * as Collapsible from '@radix-ui/react-collapsible';
|
||||||
import { useAtomValue } from 'jotai/index';
|
import { useService } from '@toeverything/infra';
|
||||||
|
import { useLiveData } from '@toeverything/infra';
|
||||||
|
import { PageRecordList } from '@toeverything/infra';
|
||||||
import { useMemo, useState } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
|
|
||||||
import { pageSettingFamily } from '../../../../atoms';
|
|
||||||
import { getDragItemId } from '../../../../hooks/affine/use-sidebar-drag';
|
import { getDragItemId } from '../../../../hooks/affine/use-sidebar-drag';
|
||||||
import { DragMenuItemOverlay } from '../components/drag-menu-item-overlay';
|
import { DragMenuItemOverlay } from '../components/drag-menu-item-overlay';
|
||||||
import { PostfixItem } from '../components/postfix-item';
|
import { PostfixItem } from '../components/postfix-item';
|
||||||
@@ -28,11 +29,12 @@ export const FavouritePage = ({
|
|||||||
const params = useParams();
|
const params = useParams();
|
||||||
const active = params.pageId === pageId;
|
const active = params.pageId === pageId;
|
||||||
const dragItemId = getDragItemId('favouritePage', pageId);
|
const dragItemId = getDragItemId('favouritePage', pageId);
|
||||||
|
const pageRecord = useLiveData(useService(PageRecordList).record(pageId));
|
||||||
|
const pageMode = useLiveData(pageRecord?.mode);
|
||||||
|
|
||||||
const setting = useAtomValue(pageSettingFamily(pageId));
|
|
||||||
const icon = useMemo(() => {
|
const icon = useMemo(() => {
|
||||||
return setting?.mode === 'edgeless' ? <EdgelessIcon /> : <PageIcon />;
|
return pageMode === 'edgeless' ? <EdgelessIcon /> : <PageIcon />;
|
||||||
}, [setting?.mode]);
|
}, [pageMode]);
|
||||||
|
|
||||||
const references = useBlockSuitePageReferences(workspace, pageId);
|
const references = useBlockSuitePageReferences(workspace, pageId);
|
||||||
const referencesToShow = useMemo(() => {
|
const referencesToShow = useMemo(() => {
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { MenuItem } from '@affine/component/app-sidebar';
|
import { MenuItem } from '@affine/component/app-sidebar';
|
||||||
import { currentPageIdAtom } from '@affine/core/atoms/mode';
|
|
||||||
import {
|
import {
|
||||||
useJournalInfoHelper,
|
useJournalInfoHelper,
|
||||||
useJournalRouteHelper,
|
useJournalRouteHelper,
|
||||||
@@ -7,7 +6,7 @@ import {
|
|||||||
import type { BlockSuiteWorkspace } from '@affine/core/shared';
|
import type { BlockSuiteWorkspace } from '@affine/core/shared';
|
||||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||||
import { TodayIcon, TomorrowIcon, YesterdayIcon } from '@blocksuite/icons';
|
import { TodayIcon, TomorrowIcon, YesterdayIcon } from '@blocksuite/icons';
|
||||||
import { useAtomValue } from 'jotai';
|
import { Page, useServiceOptional } from '@toeverything/infra';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
|
|
||||||
interface AppSidebarJournalButtonProps {
|
interface AppSidebarJournalButtonProps {
|
||||||
@@ -18,11 +17,11 @@ export const AppSidebarJournalButton = ({
|
|||||||
workspace,
|
workspace,
|
||||||
}: AppSidebarJournalButtonProps) => {
|
}: AppSidebarJournalButtonProps) => {
|
||||||
const t = useAFFiNEI18N();
|
const t = useAFFiNEI18N();
|
||||||
const currentPageId = useAtomValue(currentPageIdAtom);
|
const currentPage = useServiceOptional(Page);
|
||||||
const { openToday } = useJournalRouteHelper(workspace);
|
const { openToday } = useJournalRouteHelper(workspace);
|
||||||
const { journalDate, isJournal } = useJournalInfoHelper(
|
const { journalDate, isJournal } = useJournalInfoHelper(
|
||||||
workspace,
|
workspace,
|
||||||
currentPageId
|
currentPage?.id
|
||||||
);
|
);
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
const isJournalActive = isJournal && !!params.pageId;
|
const isJournalActive = isJournal && !!params.pageId;
|
||||||
|
|||||||
@@ -1,17 +1,11 @@
|
|||||||
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
||||||
import {
|
import { usePageMetaHelper } from '@affine/core/hooks/use-block-suite-page-meta';
|
||||||
useBlockSuitePageMeta,
|
|
||||||
usePageMetaHelper,
|
|
||||||
} from '@affine/core/hooks/use-block-suite-page-meta';
|
|
||||||
import { useBlockSuiteWorkspaceHelper } from '@affine/core/hooks/use-block-suite-workspace-helper';
|
import { useBlockSuiteWorkspaceHelper } from '@affine/core/hooks/use-block-suite-workspace-helper';
|
||||||
import { CollectionService } from '@affine/core/modules/collection';
|
import { CollectionService } from '@affine/core/modules/collection';
|
||||||
import { useService } from '@toeverything/infra';
|
import { PageRecordList, useService } from '@toeverything/infra';
|
||||||
import { useAtomValue, useSetAtom } from 'jotai';
|
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { applyUpdate, encodeStateAsUpdate } from 'yjs';
|
import { applyUpdate, encodeStateAsUpdate } from 'yjs';
|
||||||
|
|
||||||
import { setPageModeAtom } from '../../atoms';
|
|
||||||
import { currentModeAtom } from '../../atoms/mode';
|
|
||||||
import type { BlockSuiteWorkspace } from '../../shared';
|
import type { BlockSuiteWorkspace } from '../../shared';
|
||||||
import { useNavigateHelper } from '../use-navigate-helper';
|
import { useNavigateHelper } from '../use-navigate-helper';
|
||||||
import { useReferenceLinkHelper } from './use-reference-link-helper';
|
import { useReferenceLinkHelper } from './use-reference-link-helper';
|
||||||
@@ -22,31 +16,10 @@ export function useBlockSuiteMetaHelper(
|
|||||||
const { setPageMeta, getPageMeta, setPageReadonly, setPageTitle } =
|
const { setPageMeta, getPageMeta, setPageReadonly, setPageTitle } =
|
||||||
usePageMetaHelper(blockSuiteWorkspace);
|
usePageMetaHelper(blockSuiteWorkspace);
|
||||||
const { addReferenceLink } = useReferenceLinkHelper(blockSuiteWorkspace);
|
const { addReferenceLink } = useReferenceLinkHelper(blockSuiteWorkspace);
|
||||||
const metas = useBlockSuitePageMeta(blockSuiteWorkspace);
|
|
||||||
const setPageMode = useSetAtom(setPageModeAtom);
|
|
||||||
const currentMode = useAtomValue(currentModeAtom);
|
|
||||||
const { createPage } = useBlockSuiteWorkspaceHelper(blockSuiteWorkspace);
|
const { createPage } = useBlockSuiteWorkspaceHelper(blockSuiteWorkspace);
|
||||||
const { openPage } = useNavigateHelper();
|
const { openPage } = useNavigateHelper();
|
||||||
const collectionService = useService(CollectionService);
|
const collectionService = useService(CollectionService);
|
||||||
|
const pageRecordList = useService(PageRecordList);
|
||||||
const switchToPageMode = useCallback(
|
|
||||||
(pageId: string) => {
|
|
||||||
setPageMode(pageId, 'page');
|
|
||||||
},
|
|
||||||
[setPageMode]
|
|
||||||
);
|
|
||||||
const switchToEdgelessMode = useCallback(
|
|
||||||
(pageId: string) => {
|
|
||||||
setPageMode(pageId, 'edgeless');
|
|
||||||
},
|
|
||||||
[setPageMode]
|
|
||||||
);
|
|
||||||
const togglePageMode = useCallback(
|
|
||||||
(pageId: string) => {
|
|
||||||
setPageMode(pageId, currentMode === 'edgeless' ? 'page' : 'edgeless');
|
|
||||||
},
|
|
||||||
[currentMode, setPageMode]
|
|
||||||
);
|
|
||||||
|
|
||||||
const addToFavorite = useCallback(
|
const addToFavorite = useCallback(
|
||||||
(pageId: string) => {
|
(pageId: string) => {
|
||||||
@@ -77,28 +50,21 @@ export function useBlockSuiteMetaHelper(
|
|||||||
// TODO-Doma
|
// TODO-Doma
|
||||||
// "Remove" may cause ambiguity here. Consider renaming as "moveToTrash".
|
// "Remove" may cause ambiguity here. Consider renaming as "moveToTrash".
|
||||||
const removeToTrash = useCallback(
|
const removeToTrash = useCallback(
|
||||||
(pageId: string, isRoot = true) => {
|
(pageId: string) => {
|
||||||
const parentMeta = metas.find(m => m.subpageIds?.includes(pageId));
|
|
||||||
const { subpageIds = [] } = getPageMeta(pageId) ?? {};
|
|
||||||
|
|
||||||
subpageIds.forEach(id => {
|
|
||||||
removeToTrash(id, false);
|
|
||||||
});
|
|
||||||
|
|
||||||
setPageMeta(pageId, {
|
setPageMeta(pageId, {
|
||||||
trash: true,
|
trash: true,
|
||||||
trashDate: Date.now(),
|
trashDate: Date.now(),
|
||||||
trashRelate: isRoot ? parentMeta?.id : undefined,
|
trashRelate: undefined,
|
||||||
});
|
});
|
||||||
setPageReadonly(pageId, true);
|
setPageReadonly(pageId, true);
|
||||||
collectionService.deletePagesFromCollections([pageId]);
|
collectionService.deletePagesFromCollections([pageId]);
|
||||||
},
|
},
|
||||||
[collectionService, getPageMeta, metas, setPageMeta, setPageReadonly]
|
[collectionService, setPageMeta, setPageReadonly]
|
||||||
);
|
);
|
||||||
|
|
||||||
const restoreFromTrash = useCallback(
|
const restoreFromTrash = useCallback(
|
||||||
(pageId: string) => {
|
(pageId: string) => {
|
||||||
const { subpageIds = [], trashRelate } = getPageMeta(pageId) ?? {};
|
const { trashRelate } = getPageMeta(pageId) ?? {};
|
||||||
|
|
||||||
if (trashRelate) {
|
if (trashRelate) {
|
||||||
addReferenceLink(trashRelate, pageId);
|
addReferenceLink(trashRelate, pageId);
|
||||||
@@ -110,9 +76,6 @@ export function useBlockSuiteMetaHelper(
|
|||||||
trashRelate: undefined,
|
trashRelate: undefined,
|
||||||
});
|
});
|
||||||
setPageReadonly(pageId, false);
|
setPageReadonly(pageId, false);
|
||||||
subpageIds.forEach(id => {
|
|
||||||
restoreFromTrash(id);
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
[addReferenceLink, getPageMeta, setPageMeta, setPageReadonly]
|
[addReferenceLink, getPageMeta, setPageMeta, setPageReadonly]
|
||||||
);
|
);
|
||||||
@@ -150,6 +113,7 @@ export function useBlockSuiteMetaHelper(
|
|||||||
|
|
||||||
const duplicate = useAsyncCallback(
|
const duplicate = useAsyncCallback(
|
||||||
async (pageId: string, openPageAfterDuplication: boolean = true) => {
|
async (pageId: string, openPageAfterDuplication: boolean = true) => {
|
||||||
|
const currentPageMode = pageRecordList.record(pageId).value?.mode.value;
|
||||||
const currentPageMeta = getPageMeta(pageId);
|
const currentPageMeta = getPageMeta(pageId);
|
||||||
const newPage = createPage();
|
const newPage = createPage();
|
||||||
const currentPage = blockSuiteWorkspace.getPage(pageId);
|
const currentPage = blockSuiteWorkspace.getPage(pageId);
|
||||||
@@ -174,28 +138,24 @@ export function useBlockSuiteMetaHelper(
|
|||||||
const newPageTitle =
|
const newPageTitle =
|
||||||
currentPageMeta.title.replace(lastDigitRegex, '') + `(${newNumber})`;
|
currentPageMeta.title.replace(lastDigitRegex, '') + `(${newNumber})`;
|
||||||
|
|
||||||
setPageMode(newPage.id, currentMode);
|
pageRecordList
|
||||||
|
.record(newPage.id)
|
||||||
|
.value?.setMode(currentPageMode || 'page');
|
||||||
setPageTitle(newPage.id, newPageTitle);
|
setPageTitle(newPage.id, newPageTitle);
|
||||||
|
|
||||||
openPageAfterDuplication && openPage(blockSuiteWorkspace.id, newPage.id);
|
openPageAfterDuplication && openPage(blockSuiteWorkspace.id, newPage.id);
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
blockSuiteWorkspace,
|
blockSuiteWorkspace,
|
||||||
createPage,
|
createPage,
|
||||||
currentMode,
|
|
||||||
getPageMeta,
|
getPageMeta,
|
||||||
openPage,
|
openPage,
|
||||||
|
pageRecordList,
|
||||||
setPageMeta,
|
setPageMeta,
|
||||||
setPageMode,
|
|
||||||
setPageTitle,
|
setPageTitle,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
switchToPageMode,
|
|
||||||
switchToEdgelessMode,
|
|
||||||
togglePageMode,
|
|
||||||
|
|
||||||
publicPage,
|
publicPage,
|
||||||
cancelPublicPage,
|
cancelPublicPage,
|
||||||
|
|
||||||
|
|||||||
@@ -7,11 +7,10 @@ import {
|
|||||||
revokePublicPageMutation,
|
revokePublicPageMutation,
|
||||||
} from '@affine/graphql';
|
} from '@affine/graphql';
|
||||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||||
import type { Workspace } from '@toeverything/infra/workspace';
|
import type { PageMode, Workspace } from '@toeverything/infra';
|
||||||
import { useSetAtom } from 'jotai';
|
import { useSetAtom } from 'jotai';
|
||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useMemo } from 'react';
|
||||||
|
|
||||||
import type { PageMode } from '../../atoms';
|
|
||||||
import { useMutation } from '../use-mutation';
|
import { useMutation } from '../use-mutation';
|
||||||
import { useQuery } from '../use-query';
|
import { useQuery } from '../use-query';
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { WorkspaceFlavour } from '@affine/env/workspace';
|
|||||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||||
import { assertExists } from '@blocksuite/global/utils';
|
import { assertExists } from '@blocksuite/global/utils';
|
||||||
import { EdgelessIcon, HistoryIcon, PageIcon } from '@blocksuite/icons';
|
import { EdgelessIcon, HistoryIcon, PageIcon } from '@blocksuite/icons';
|
||||||
import { Workspace } from '@toeverything/infra';
|
import { Page, useLiveData, Workspace } from '@toeverything/infra';
|
||||||
import {
|
import {
|
||||||
PreconditionStrategy,
|
PreconditionStrategy,
|
||||||
registerAffineCommand,
|
registerAffineCommand,
|
||||||
@@ -18,10 +18,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() {
|
||||||
pageId: string,
|
const page = useService(Page);
|
||||||
mode: 'page' | 'edgeless'
|
const pageId = page.id;
|
||||||
) {
|
const mode = useLiveData(page.mode);
|
||||||
const t = useAFFiNEI18N();
|
const t = useAFFiNEI18N();
|
||||||
const workspace = useService(Workspace);
|
const workspace = useService(Workspace);
|
||||||
const blockSuiteWorkspace = workspace.blockSuiteWorkspace;
|
const blockSuiteWorkspace = workspace.blockSuiteWorkspace;
|
||||||
@@ -42,7 +42,7 @@ export function useRegisterBlocksuiteEditorCommands(
|
|||||||
}));
|
}));
|
||||||
}, [pageId, setPageHistoryModalState]);
|
}, [pageId, setPageHistoryModalState]);
|
||||||
|
|
||||||
const { togglePageMode, toggleFavorite, restoreFromTrash, duplicate } =
|
const { toggleFavorite, restoreFromTrash, duplicate } =
|
||||||
useBlockSuiteMetaHelper(blockSuiteWorkspace);
|
useBlockSuiteMetaHelper(blockSuiteWorkspace);
|
||||||
const exportHandler = useExportPage(currentPage);
|
const exportHandler = useExportPage(currentPage);
|
||||||
const { setTrashModal } = useTrashModalHelper(blockSuiteWorkspace);
|
const { setTrashModal } = useTrashModalHelper(blockSuiteWorkspace);
|
||||||
@@ -116,7 +116,7 @@ export function useRegisterBlocksuiteEditorCommands(
|
|||||||
: t['com.affine.pageMode.page']()
|
: t['com.affine.pageMode.page']()
|
||||||
}`,
|
}`,
|
||||||
run() {
|
run() {
|
||||||
togglePageMode(pageId);
|
page.toggleMode();
|
||||||
toast(
|
toast(
|
||||||
mode === 'page'
|
mode === 'page'
|
||||||
? t['com.affine.toastMessage.edgelessMode']()
|
? t['com.affine.toastMessage.edgelessMode']()
|
||||||
@@ -245,10 +245,10 @@ export function useRegisterBlocksuiteEditorCommands(
|
|||||||
restoreFromTrash,
|
restoreFromTrash,
|
||||||
t,
|
t,
|
||||||
toggleFavorite,
|
toggleFavorite,
|
||||||
togglePageMode,
|
|
||||||
trash,
|
trash,
|
||||||
isCloudWorkspace,
|
isCloudWorkspace,
|
||||||
openHistoryModal,
|
openHistoryModal,
|
||||||
duplicate,
|
duplicate,
|
||||||
|
page,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
import { useBlockSuiteWorkspacePage } from '@affine/core/hooks/use-block-suite-workspace-page';
|
|
||||||
import { Workspace } from '@toeverything/infra';
|
|
||||||
import { useService } from '@toeverything/infra/di';
|
|
||||||
import { useAtomValue } from 'jotai';
|
|
||||||
|
|
||||||
import { currentPageIdAtom } from '../../atoms/mode';
|
|
||||||
|
|
||||||
export const useCurrentPage = () => {
|
|
||||||
const currentPageId = useAtomValue(currentPageIdAtom);
|
|
||||||
const currentWorkspace = useService(Workspace);
|
|
||||||
return useBlockSuiteWorkspacePage(
|
|
||||||
currentWorkspace.blockSuiteWorkspace,
|
|
||||||
currentPageId
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
import type { PageMeta } from '@blocksuite/store';
|
|
||||||
import { noop } from 'lodash-es';
|
import { noop } from 'lodash-es';
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
|
|
||||||
@@ -16,6 +15,6 @@ export function useDocumentTitle(newTitle?: string | null) {
|
|||||||
}, [newTitle]);
|
}, [newTitle]);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function usePageDocumentTitle(pageMeta?: PageMeta) {
|
export function usePageDocumentTitle(pageTitle?: string) {
|
||||||
useDocumentTitle(pageMeta?.title ? `${pageMeta.title} · AFFiNE` : null);
|
useDocumentTitle(pageTitle ? `${pageTitle} · AFFiNE` : null);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { initEmptyPage } from '@toeverything/infra/blocksuite';
|
import { initEmptyPage } from '@toeverything/infra';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useMemo } from 'react';
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import type { GlobalCache } from '@toeverything/infra';
|
import type { GlobalCache, GlobalState, Memento } from '@toeverything/infra';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
export class LocalStorageGlobalCache implements GlobalCache {
|
export class LocalStorageMemento implements Memento {
|
||||||
prefix = 'cache:';
|
constructor(private readonly prefix: string) {}
|
||||||
|
|
||||||
get<T>(key: string): T | null {
|
get<T>(key: string): T | null {
|
||||||
const json = localStorage.getItem(this.prefix + key);
|
const json = localStorage.getItem(this.prefix + key);
|
||||||
@@ -30,3 +30,21 @@ export class LocalStorageGlobalCache implements GlobalCache {
|
|||||||
channel.close();
|
channel.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class LocalStorageGlobalCache
|
||||||
|
extends LocalStorageMemento
|
||||||
|
implements GlobalCache
|
||||||
|
{
|
||||||
|
constructor() {
|
||||||
|
super('global-cache:');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class LocalStorageGlobalState
|
||||||
|
extends LocalStorageMemento
|
||||||
|
implements GlobalState
|
||||||
|
{
|
||||||
|
constructor() {
|
||||||
|
super('global-state:');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
import {
|
import {
|
||||||
GlobalCache,
|
GlobalCache,
|
||||||
|
GlobalState,
|
||||||
type ServiceCollection,
|
type ServiceCollection,
|
||||||
Workspace,
|
Workspace,
|
||||||
WorkspaceScope,
|
WorkspaceScope,
|
||||||
} from '@toeverything/infra';
|
} from '@toeverything/infra';
|
||||||
|
|
||||||
import { CollectionService } from './collection';
|
import { CollectionService } from './collection';
|
||||||
import { LocalStorageGlobalCache } from './infra-web/storage';
|
import {
|
||||||
|
LocalStorageGlobalCache,
|
||||||
|
LocalStorageGlobalState,
|
||||||
|
} from './infra-web/storage';
|
||||||
import { CurrentPageService } from './page';
|
import { CurrentPageService } from './page';
|
||||||
import {
|
import {
|
||||||
CurrentWorkspaceService,
|
CurrentWorkspaceService,
|
||||||
@@ -25,5 +29,7 @@ export function configureBusinessServices(services: ServiceCollection) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function configureWebInfraServices(services: ServiceCollection) {
|
export function configureWebInfraServices(services: ServiceCollection) {
|
||||||
services.addImpl(GlobalCache, LocalStorageGlobalCache);
|
services
|
||||||
|
.addImpl(GlobalCache, LocalStorageGlobalCache)
|
||||||
|
.addImpl(GlobalState, LocalStorageGlobalState);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,8 +16,10 @@ import {
|
|||||||
LocalSyncStorage,
|
LocalSyncStorage,
|
||||||
Page,
|
Page,
|
||||||
PageManager,
|
PageManager,
|
||||||
|
type PageMode,
|
||||||
ReadonlyMappingSyncStorage,
|
ReadonlyMappingSyncStorage,
|
||||||
RemoteBlobStorage,
|
RemoteBlobStorage,
|
||||||
|
useLiveData,
|
||||||
useService,
|
useService,
|
||||||
useServiceOptional,
|
useServiceOptional,
|
||||||
WorkspaceIdContext,
|
WorkspaceIdContext,
|
||||||
@@ -34,7 +36,6 @@ import {
|
|||||||
useRouteError,
|
useRouteError,
|
||||||
} from 'react-router-dom';
|
} from 'react-router-dom';
|
||||||
|
|
||||||
import type { PageMode } from '../../atoms';
|
|
||||||
import { AppContainer } from '../../components/affine/app-container';
|
import { AppContainer } from '../../components/affine/app-container';
|
||||||
import { PageDetailEditor } from '../../components/page-detail-editor';
|
import { PageDetailEditor } from '../../components/page-detail-editor';
|
||||||
import { SharePageNotFoundError } from '../../components/share-page-not-found-error';
|
import { SharePageNotFoundError } from '../../components/share-page-not-found-error';
|
||||||
@@ -159,9 +160,7 @@ export const Component = () => {
|
|||||||
workspace.engine.sync
|
workspace.engine.sync
|
||||||
.waitForSynced()
|
.waitForSynced()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
const { page } = workspace.services
|
const { page } = workspace.services.get(PageManager).open(pageId);
|
||||||
.get(PageManager)
|
|
||||||
.openByPageId(pageId);
|
|
||||||
|
|
||||||
workspace.blockSuiteWorkspace.awarenessStore.setReadonly(
|
workspace.blockSuiteWorkspace.awarenessStore.setReadonly(
|
||||||
page.blockSuitePage,
|
page.blockSuitePage,
|
||||||
@@ -186,9 +185,11 @@ export const Component = () => {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
const page = useServiceOptional(Page);
|
const page = useServiceOptional(Page);
|
||||||
|
const pageTitle = useLiveData(page?.title);
|
||||||
|
|
||||||
usePageDocumentTitle(page?.meta);
|
usePageDocumentTitle(pageTitle);
|
||||||
const loginStatus = useCurrentLoginStatus();
|
const loginStatus = useCurrentLoginStatus();
|
||||||
|
|
||||||
if (!page) {
|
if (!page) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { EditorModeSwitch } from '@affine/core/components/blocksuite/block-suite-mode-switch';
|
import { EditorModeSwitch } from '@affine/core/components/blocksuite/block-suite-mode-switch';
|
||||||
import ShareHeaderRightItem from '@affine/core/components/cloud/share-header-right-item';
|
import ShareHeaderRightItem from '@affine/core/components/cloud/share-header-right-item';
|
||||||
import type { Workspace as BlockSuiteWorkspace } from '@blocksuite/store';
|
import type { Workspace as BlockSuiteWorkspace } from '@blocksuite/store';
|
||||||
|
import type { PageMode } from '@toeverything/infra';
|
||||||
|
|
||||||
import type { PageMode } from '../../atoms';
|
|
||||||
import { BlocksuiteHeaderTitle } from '../../components/blocksuite/block-suite-header/title/index';
|
import { BlocksuiteHeaderTitle } from '../../components/blocksuite/block-suite-header/title/index';
|
||||||
import * as styles from './share-header.css';
|
import * as styles from './share-header.css';
|
||||||
|
|
||||||
|
|||||||
@@ -17,14 +17,14 @@ import type { Page as BlockSuitePage } from '@blocksuite/store';
|
|||||||
import {
|
import {
|
||||||
globalBlockSuiteSchema,
|
globalBlockSuiteSchema,
|
||||||
Page,
|
Page,
|
||||||
PageListService,
|
|
||||||
PageManager,
|
PageManager,
|
||||||
|
PageRecordList,
|
||||||
useLiveData,
|
useLiveData,
|
||||||
useServiceOptional,
|
useServiceOptional,
|
||||||
} from '@toeverything/infra';
|
} from '@toeverything/infra';
|
||||||
import { appSettingAtom, Workspace } from '@toeverything/infra';
|
import { appSettingAtom, Workspace } from '@toeverything/infra';
|
||||||
import { useService } from '@toeverything/infra';
|
import { useService } from '@toeverything/infra';
|
||||||
import { useAtom, useAtomValue, useSetAtom, useStore } from 'jotai';
|
import { useAtom, useAtomValue, useSetAtom } from 'jotai';
|
||||||
import {
|
import {
|
||||||
memo,
|
memo,
|
||||||
type ReactElement,
|
type ReactElement,
|
||||||
@@ -36,8 +36,7 @@ import {
|
|||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import type { Map as YMap } from 'yjs';
|
import type { Map as YMap } from 'yjs';
|
||||||
|
|
||||||
import { pageSettingFamily, setPageModeAtom } from '../../../atoms';
|
import { recentPageIdsBaseAtom } from '../../../atoms';
|
||||||
import { currentModeAtom, currentPageIdAtom } from '../../../atoms/mode';
|
|
||||||
import { AffineErrorBoundary } from '../../../components/affine/affine-error-boundary';
|
import { AffineErrorBoundary } from '../../../components/affine/affine-error-boundary';
|
||||||
import { HubIsland } from '../../../components/affine/hub-island';
|
import { HubIsland } from '../../../components/affine/hub-island';
|
||||||
import { GlobalPageHistoryModal } from '../../../components/affine/page-history-modal';
|
import { GlobalPageHistoryModal } from '../../../components/affine/page-history-modal';
|
||||||
@@ -116,6 +115,7 @@ const DetailPageLayout = ({
|
|||||||
|
|
||||||
const DetailPageImpl = memo(function DetailPageImpl() {
|
const DetailPageImpl = memo(function DetailPageImpl() {
|
||||||
const page = useService(Page);
|
const page = useService(Page);
|
||||||
|
const pageRecordList = useService(PageRecordList);
|
||||||
const currentPageId = page.id;
|
const currentPageId = page.id;
|
||||||
const { openPage, jumpToSubPath } = useNavigateHelper();
|
const { openPage, jumpToSubPath } = useNavigateHelper();
|
||||||
const currentWorkspace = useService(Workspace);
|
const currentWorkspace = useService(Workspace);
|
||||||
@@ -129,17 +129,16 @@ const DetailPageImpl = memo(function DetailPageImpl() {
|
|||||||
|
|
||||||
const collectionService = useService(CollectionService);
|
const collectionService = useService(CollectionService);
|
||||||
const { setTemporaryFilter } = useCollectionManager(collectionService);
|
const { setTemporaryFilter } = useCollectionManager(collectionService);
|
||||||
const mode = useAtomValue(currentModeAtom);
|
const mode = useLiveData(page.mode);
|
||||||
const setPageMode = useSetAtom(setPageModeAtom);
|
useRegisterBlocksuiteEditorCommands();
|
||||||
useRegisterBlocksuiteEditorCommands(currentPageId, mode);
|
const title = useLiveData(page.title);
|
||||||
usePageDocumentTitle(pageMeta);
|
usePageDocumentTitle(title);
|
||||||
const rootStore = useStore();
|
|
||||||
|
|
||||||
const onLoad = useCallback(
|
const onLoad = useCallback(
|
||||||
(page: BlockSuitePage, editor: AffineEditorContainer) => {
|
(bsPage: BlockSuitePage, editor: AffineEditorContainer) => {
|
||||||
try {
|
try {
|
||||||
// todo(joooye34): improve the following migration code
|
// todo(joooye34): improve the following migration code
|
||||||
const surfaceBlock = page.getBlockByFlavour('affine:surface')[0];
|
const surfaceBlock = bsPage.getBlockByFlavour('affine:surface')[0];
|
||||||
// hotfix for old page
|
// hotfix for old page
|
||||||
if (
|
if (
|
||||||
surfaceBlock &&
|
surfaceBlock &&
|
||||||
@@ -152,7 +151,7 @@ const DetailPageImpl = memo(function DetailPageImpl() {
|
|||||||
{
|
{
|
||||||
'affine:surface': 3,
|
'affine:surface': 3,
|
||||||
},
|
},
|
||||||
page.spaceDoc
|
bsPage.spaceDoc
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch {}
|
} catch {}
|
||||||
@@ -177,17 +176,17 @@ const DetailPageImpl = memo(function DetailPageImpl() {
|
|||||||
'affine:page'
|
'affine:page'
|
||||||
) as PageService;
|
) as PageService;
|
||||||
pageService.getPageMode = (pageId: string) =>
|
pageService.getPageMode = (pageId: string) =>
|
||||||
rootStore.get(pageSettingFamily(pageId)).mode;
|
pageRecordList.record(pageId).value?.mode.value ?? 'page';
|
||||||
pageService.getPageUpdatedAt = (pageId: string) => {
|
pageService.getPageUpdatedAt = (pageId: string) => {
|
||||||
const linkedPage = page.workspace.getPage(pageId);
|
const linkedPage = pageRecordList.record(pageId).value;
|
||||||
if (!linkedPage) return new Date();
|
if (!linkedPage) return new Date();
|
||||||
|
|
||||||
const updatedDate = linkedPage.meta.updatedDate;
|
const updatedDate = linkedPage.meta.value.updatedDate;
|
||||||
const createDate = linkedPage.meta.createDate;
|
const createDate = linkedPage.meta.value.createDate;
|
||||||
return updatedDate ? new Date(updatedDate) : new Date(createDate);
|
return updatedDate ? new Date(updatedDate) : new Date(createDate);
|
||||||
};
|
};
|
||||||
|
|
||||||
setPageMode(currentPageId, mode);
|
page.setMode(mode);
|
||||||
// fixme: it seems pageLinkClicked is not triggered sometimes?
|
// fixme: it seems pageLinkClicked is not triggered sometimes?
|
||||||
const dispose = editor.slots.pageLinkClicked.on(({ pageId }) => {
|
const dispose = editor.slots.pageLinkClicked.on(({ pageId }) => {
|
||||||
return openPage(blockSuiteWorkspace.id, pageId);
|
return openPage(blockSuiteWorkspace.id, pageId);
|
||||||
@@ -202,15 +201,14 @@ const DetailPageImpl = memo(function DetailPageImpl() {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
blockSuiteWorkspace.id,
|
page,
|
||||||
currentPageId,
|
|
||||||
currentWorkspace.id,
|
|
||||||
jumpToSubPath,
|
|
||||||
mode,
|
mode,
|
||||||
|
pageRecordList,
|
||||||
openPage,
|
openPage,
|
||||||
setPageMode,
|
blockSuiteWorkspace.id,
|
||||||
|
jumpToSubPath,
|
||||||
|
currentWorkspace.id,
|
||||||
setTemporaryFilter,
|
setTemporaryFilter,
|
||||||
rootStore,
|
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -269,31 +267,31 @@ const DetailPageImpl = memo(function DetailPageImpl() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const DetailPage = ({ pageId }: { pageId: string }): ReactElement => {
|
export const DetailPage = ({ pageId }: { pageId: string }): ReactElement => {
|
||||||
const pageListService = useService(PageListService);
|
const pageRecordList = useService(PageRecordList);
|
||||||
|
|
||||||
const pageListReady = useLiveData(pageListService.isReady);
|
const pageListReady = useLiveData(pageRecordList.isReady);
|
||||||
|
|
||||||
const pageMetas = useLiveData(pageListService.pages);
|
const pageRecords = useLiveData(pageRecordList.records);
|
||||||
|
|
||||||
const pageMeta = useMemo(
|
const pageRecord = useMemo(
|
||||||
() => pageMetas.find(page => page.id === pageId),
|
() => pageRecords.find(page => page.id === pageId),
|
||||||
[pageMetas, pageId]
|
[pageRecords, pageId]
|
||||||
);
|
);
|
||||||
|
|
||||||
const pageManager = useService(PageManager);
|
const pageManager = useService(PageManager);
|
||||||
const currentPageService = useService(CurrentPageService);
|
const currentPageService = useService(CurrentPageService);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!pageMeta) {
|
if (!pageRecord) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const { page, release } = pageManager.open(pageMeta);
|
const { page, release } = pageManager.open(pageRecord.id);
|
||||||
currentPageService.openPage(page);
|
currentPageService.openPage(page);
|
||||||
return () => {
|
return () => {
|
||||||
currentPageService.closePage();
|
currentPageService.closePage();
|
||||||
release();
|
release();
|
||||||
};
|
};
|
||||||
}, [currentPageService, pageManager, pageMeta]);
|
}, [currentPageService, pageManager, pageRecord]);
|
||||||
|
|
||||||
const page = useServiceOptional(Page);
|
const page = useServiceOptional(Page);
|
||||||
|
|
||||||
@@ -304,6 +302,14 @@ export const DetailPage = ({ pageId }: { pageId: string }): ReactElement => {
|
|||||||
currentWorkspace.setPriorityRule(id => id.endsWith(pageId));
|
currentWorkspace.setPriorityRule(id => id.endsWith(pageId));
|
||||||
}, [pageId, currentWorkspace]);
|
}, [pageId, currentWorkspace]);
|
||||||
|
|
||||||
|
const jumpOnce = useLiveData(pageRecord?.meta.map(meta => meta.jumpOnce));
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (jumpOnce) {
|
||||||
|
pageRecord?.setMeta({ jumpOnce: false });
|
||||||
|
}
|
||||||
|
}, [jumpOnce, pageRecord]);
|
||||||
|
|
||||||
// if sync engine has been synced and the page is null, show 404 page.
|
// if sync engine has been synced and the page is null, show 404 page.
|
||||||
if (pageListReady && !page) {
|
if (pageListReady && !page) {
|
||||||
return <PageNotFound />;
|
return <PageNotFound />;
|
||||||
@@ -313,27 +319,26 @@ export const DetailPage = ({ pageId }: { pageId: string }): ReactElement => {
|
|||||||
return <PageDetailSkeleton key="current-page-is-null" />;
|
return <PageDetailSkeleton key="current-page-is-null" />;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (page.meta.jumpOnce) {
|
|
||||||
currentWorkspace.blockSuiteWorkspace.setPageMeta(page.id, {
|
|
||||||
jumpOnce: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return <DetailPageImpl />;
|
return <DetailPageImpl />;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Component = () => {
|
export const Component = () => {
|
||||||
performanceRenderLogger.info('DetailPage');
|
performanceRenderLogger.info('DetailPage');
|
||||||
|
|
||||||
const setCurrentPageId = useSetAtom(currentPageIdAtom);
|
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
|
const setRecentPageIds = useSetAtom(recentPageIdsBaseAtom);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (params.pageId) {
|
if (params.pageId) {
|
||||||
localStorage.setItem('last_page_id', params.pageId);
|
const pageId = params.pageId;
|
||||||
setCurrentPageId(params.pageId);
|
localStorage.setItem('last_page_id', pageId);
|
||||||
|
|
||||||
|
setRecentPageIds(ids => {
|
||||||
|
// pick 3 recent page ids
|
||||||
|
return [...new Set([pageId, ...ids]).values()].slice(0, 3);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}, [params, setCurrentPageId]);
|
}, [params, setRecentPageIds]);
|
||||||
|
|
||||||
const pageId = params.pageId;
|
const pageId = params.pageId;
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ export async function configureTestingEnvironment() {
|
|||||||
|
|
||||||
await workspace.engine.sync.waitForSynced();
|
await workspace.engine.sync.waitForSynced();
|
||||||
|
|
||||||
const { page } = workspace.services.get(PageManager).openByPageId('page0');
|
const { page } = workspace.services.get(PageManager).open('page0');
|
||||||
|
|
||||||
rootServices.get(CurrentWorkspaceService).openWorkspace(workspace);
|
rootServices.get(CurrentWorkspaceService).openWorkspace(workspace);
|
||||||
workspace.services.get(CurrentPageService).openPage(page);
|
workspace.services.get(CurrentPageService).openPage(page);
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
import { WorkspaceFactory, WorkspaceListProvider } from '@toeverything/infra';
|
import type { ServiceCollection } from '@toeverything/infra';
|
||||||
import type { ServiceCollection } from '@toeverything/infra/di';
|
import {
|
||||||
|
GlobalState,
|
||||||
|
Workspace,
|
||||||
|
WorkspaceFactory,
|
||||||
|
WorkspaceListProvider,
|
||||||
|
WorkspaceLocalState,
|
||||||
|
WorkspaceScope,
|
||||||
|
} from '@toeverything/infra';
|
||||||
|
|
||||||
import { CloudWorkspaceFactory, CloudWorkspaceListProvider } from './cloud';
|
import { CloudWorkspaceFactory, CloudWorkspaceListProvider } from './cloud';
|
||||||
import {
|
import {
|
||||||
@@ -7,6 +14,7 @@ import {
|
|||||||
LocalWorkspaceFactory,
|
LocalWorkspaceFactory,
|
||||||
LocalWorkspaceListProvider,
|
LocalWorkspaceListProvider,
|
||||||
} from './local';
|
} from './local';
|
||||||
|
import { WorkspaceLocalStateImpl } from './local-state';
|
||||||
|
|
||||||
export * from './cloud';
|
export * from './cloud';
|
||||||
export * from './local';
|
export * from './local';
|
||||||
@@ -16,7 +24,12 @@ export function configureWorkspaceImplServices(services: ServiceCollection) {
|
|||||||
.addImpl(WorkspaceListProvider('affine-cloud'), CloudWorkspaceListProvider)
|
.addImpl(WorkspaceListProvider('affine-cloud'), CloudWorkspaceListProvider)
|
||||||
.addImpl(WorkspaceFactory('affine-cloud'), CloudWorkspaceFactory)
|
.addImpl(WorkspaceFactory('affine-cloud'), CloudWorkspaceFactory)
|
||||||
.addImpl(WorkspaceListProvider('local'), LocalWorkspaceListProvider)
|
.addImpl(WorkspaceListProvider('local'), LocalWorkspaceListProvider)
|
||||||
.addImpl(WorkspaceFactory('local'), LocalWorkspaceFactory);
|
.addImpl(WorkspaceFactory('local'), LocalWorkspaceFactory)
|
||||||
|
.scope(WorkspaceScope)
|
||||||
|
.addImpl(WorkspaceLocalState, WorkspaceLocalStateImpl, [
|
||||||
|
Workspace,
|
||||||
|
GlobalState,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
31
packages/frontend/workspace-impl/src/local-state.ts
Normal file
31
packages/frontend/workspace-impl/src/local-state.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import type {
|
||||||
|
GlobalState,
|
||||||
|
Workspace,
|
||||||
|
WorkspaceLocalState,
|
||||||
|
} from '@toeverything/infra';
|
||||||
|
|
||||||
|
export class WorkspaceLocalStateImpl implements WorkspaceLocalState {
|
||||||
|
constructor(
|
||||||
|
private readonly workspace: Workspace,
|
||||||
|
private readonly globalState: GlobalState
|
||||||
|
) {}
|
||||||
|
|
||||||
|
get<T>(key: string): T | null {
|
||||||
|
return this.globalState.get<T>(
|
||||||
|
`workspace-state:${this.workspace.id}:${key}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
watch<T>(key: string) {
|
||||||
|
return this.globalState.watch<T>(
|
||||||
|
`workspace-state:${this.workspace.id}:${key}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
set<T>(key: string, value: T | null): void {
|
||||||
|
return this.globalState.set<T>(
|
||||||
|
`workspace-state:${this.workspace.id}:${key}`,
|
||||||
|
value
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -264,6 +264,7 @@ test('assert the recent browse pages are on the recent list', async ({
|
|||||||
{
|
{
|
||||||
const title = getBlockSuiteEditorTitle(page);
|
const title = getBlockSuiteEditorTitle(page);
|
||||||
await title.click();
|
await title.click();
|
||||||
|
await page.waitForTimeout(200);
|
||||||
await title.pressSequentially('sgtokidoki', { delay: 100 });
|
await title.pressSequentially('sgtokidoki', { delay: 100 });
|
||||||
await expect(title).toHaveText('sgtokidoki');
|
await expect(title).toHaveText('sgtokidoki');
|
||||||
}
|
}
|
||||||
@@ -276,6 +277,7 @@ test('assert the recent browse pages are on the recent list', async ({
|
|||||||
{
|
{
|
||||||
const title = getBlockSuiteEditorTitle(page);
|
const title = getBlockSuiteEditorTitle(page);
|
||||||
await title.click();
|
await title.click();
|
||||||
|
await page.waitForTimeout(200);
|
||||||
await title.pressSequentially('theliquidhorse', { delay: 100 });
|
await title.pressSequentially('theliquidhorse', { delay: 100 });
|
||||||
await expect(title).toHaveText('theliquidhorse');
|
await expect(title).toHaveText('theliquidhorse');
|
||||||
}
|
}
|
||||||
@@ -288,6 +290,7 @@ test('assert the recent browse pages are on the recent list', async ({
|
|||||||
{
|
{
|
||||||
const title = getBlockSuiteEditorTitle(page);
|
const title = getBlockSuiteEditorTitle(page);
|
||||||
await title.click();
|
await title.click();
|
||||||
|
await page.waitForTimeout(200);
|
||||||
await title.pressSequentially('battlekot', { delay: 100 });
|
await title.pressSequentially('battlekot', { delay: 100 });
|
||||||
await expect(title).toHaveText('battlekot');
|
await expect(title).toHaveText('battlekot');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { __unstableSchemas, AffineSchemas } from '@blocksuite/blocks/models';
|
|||||||
import { Workspace } from '@blocksuite/store';
|
import { Workspace } from '@blocksuite/store';
|
||||||
import { Schema } from '@blocksuite/store';
|
import { Schema } from '@blocksuite/store';
|
||||||
import type { StoryFn } from '@storybook/react';
|
import type { StoryFn } from '@storybook/react';
|
||||||
import { initEmptyPage } from '@toeverything/infra/blocksuite';
|
import { initEmptyPage } from '@toeverything/infra';
|
||||||
|
|
||||||
const schema = new Schema();
|
const schema = new Schema();
|
||||||
schema.register(AffineSchemas).register(__unstableSchemas);
|
schema.register(AffineSchemas).register(__unstableSchemas);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { CurrentPageService } from '@affine/core/modules/page';
|
|||||||
import type { Page } from '@blocksuite/store';
|
import type { Page } from '@blocksuite/store';
|
||||||
import type { Meta } from '@storybook/react';
|
import type { Meta } from '@storybook/react';
|
||||||
import { PageManager, useService, Workspace } from '@toeverything/infra';
|
import { PageManager, useService, Workspace } from '@toeverything/infra';
|
||||||
import { initEmptyPage } from '@toeverything/infra/blocksuite';
|
import { initEmptyPage } from '@toeverything/infra';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { createPortal } from 'react-dom';
|
import { createPortal } from 'react-dom';
|
||||||
|
|
||||||
@@ -24,7 +24,7 @@ export const Default = () => {
|
|||||||
const bsPage = workspace.blockSuiteWorkspace.createPage('page0');
|
const bsPage = workspace.blockSuiteWorkspace.createPage('page0');
|
||||||
initEmptyPage(bsPage);
|
initEmptyPage(bsPage);
|
||||||
|
|
||||||
const { page, release } = pageManager.open(bsPage.meta);
|
const { page, release } = pageManager.open(bsPage.meta.id);
|
||||||
currentPageService.openPage(page);
|
currentPageService.openPage(page);
|
||||||
|
|
||||||
fetch(new URL('@affine-test/fixtures/large-image.png', import.meta.url))
|
fetch(new URL('@affine-test/fixtures/large-image.png', import.meta.url))
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { __unstableSchemas, AffineSchemas } from '@blocksuite/blocks/models';
|
|||||||
import { Workspace } from '@blocksuite/store';
|
import { Workspace } from '@blocksuite/store';
|
||||||
import { Schema } from '@blocksuite/store';
|
import { Schema } from '@blocksuite/store';
|
||||||
import type { StoryFn } from '@storybook/react';
|
import type { StoryFn } from '@storybook/react';
|
||||||
import { initEmptyPage } from '@toeverything/infra/blocksuite';
|
import { initEmptyPage } from '@toeverything/infra';
|
||||||
|
|
||||||
const schema = new Schema();
|
const schema = new Schema();
|
||||||
schema.register(AffineSchemas).register(__unstableSchemas);
|
schema.register(AffineSchemas).register(__unstableSchemas);
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import { PageIcon, TagsIcon } from '@blocksuite/icons';
|
|||||||
import { Schema, Workspace } from '@blocksuite/store';
|
import { Schema, Workspace } from '@blocksuite/store';
|
||||||
import type { Meta, StoryFn } from '@storybook/react';
|
import type { Meta, StoryFn } from '@storybook/react';
|
||||||
import { userEvent } from '@storybook/testing-library';
|
import { userEvent } from '@storybook/testing-library';
|
||||||
import { initEmptyPage } from '@toeverything/infra/blocksuite';
|
import { initEmptyPage } from '@toeverything/infra';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { withRouter } from 'storybook-addon-react-router-v6';
|
import { withRouter } from 'storybook-addon-react-router-v6';
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { type Page } from '@blocksuite/store';
|
|||||||
import { expect } from '@storybook/jest';
|
import { expect } from '@storybook/jest';
|
||||||
import type { Meta, StoryFn } from '@storybook/react';
|
import type { Meta, StoryFn } from '@storybook/react';
|
||||||
import { Workspace } from '@toeverything/infra';
|
import { Workspace } from '@toeverything/infra';
|
||||||
import { initEmptyPage } from '@toeverything/infra/blocksuite';
|
import { initEmptyPage } from '@toeverything/infra';
|
||||||
import { useService } from '@toeverything/infra/di';
|
import { useService } from '@toeverything/infra/di';
|
||||||
import { nanoid } from 'nanoid';
|
import { nanoid } from 'nanoid';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
|||||||
1
tools/@types/env/__all.d.ts
vendored
1
tools/@types/env/__all.d.ts
vendored
@@ -21,7 +21,6 @@ declare global {
|
|||||||
declare module '@blocksuite/store' {
|
declare module '@blocksuite/store' {
|
||||||
interface PageMeta {
|
interface PageMeta {
|
||||||
favorite?: boolean;
|
favorite?: boolean;
|
||||||
subpageIds: string[];
|
|
||||||
// If a page remove to trash, and it is a subpage, it will remove from its parent `subpageIds`, 'trashRelate' is use for save it parent
|
// If a page remove to trash, and it is a subpage, it will remove from its parent `subpageIds`, 'trashRelate' is use for save it parent
|
||||||
trashRelate?: string;
|
trashRelate?: string;
|
||||||
trash?: boolean;
|
trash?: boolean;
|
||||||
|
|||||||
Reference in New Issue
Block a user