refactor: workspace manager (#5060)

This commit is contained in:
EYHN
2023-12-15 07:20:50 +00:00
parent af15aa06d4
commit fe2851d3e9
217 changed files with 3605 additions and 4244 deletions
@@ -3,7 +3,6 @@ import { BlockCard } from '@affine/component/card/block-card';
import { WorkspaceCard } from '@affine/component/card/workspace-card';
import { Tooltip } from '@affine/component/ui/tooltip';
import { WorkspaceFlavour } from '@affine/env/workspace';
import { getOrCreateWorkspace } from '@affine/workspace/manager';
import {
EdgelessIcon,
ExportToHtmlIcon,
@@ -20,13 +19,6 @@ export default {
},
} satisfies Meta;
const blockSuiteWorkspace = getOrCreateWorkspace(
'blocksuite-local',
WorkspaceFlavour.LOCAL
);
blockSuiteWorkspace.meta.setName('Hello World');
export const AffineWorkspaceCard = () => {
return (
<WorkspaceCard
@@ -1,10 +1,12 @@
import { BlockSuiteEditor } from '@affine/component/block-suite-editor';
import { WorkspaceFlavour } from '@affine/env/workspace';
// eslint-disable-next-line @typescript-eslint/no-restricted-imports
import { ImagePreviewModal } from '@affine/image-preview-plugin/src/component';
import { getOrCreateWorkspace } from '@affine/workspace/manager';
import { waitForCurrentWorkspaceAtom } from '@affine/workspace/atom';
import type { Page } from '@blocksuite/store';
import type { Meta } from '@storybook/react';
import { initEmptyPage } from '@toeverything/infra/blocksuite';
import { useAtomValue } from 'jotai';
import { useEffect, useState } from 'react';
import { createPortal } from 'react-dom';
export default {
@@ -12,36 +14,46 @@ export default {
component: ImagePreviewModal,
} satisfies Meta;
const workspace = getOrCreateWorkspace('test', WorkspaceFlavour.LOCAL);
const page = workspace.createPage('page0');
initEmptyPage(page);
fetch(new URL('@affine-test/fixtures/large-image.png', import.meta.url))
.then(res => res.arrayBuffer())
.then(async buffer => {
const id = await workspace.blob.set(
new Blob([buffer], { type: 'image/png' })
);
const frameId = page.getBlockByFlavour('affine:note')[0].id;
page.addBlock(
'affine:paragraph',
{
text: new page.Text('Please double click the image to preview it.'),
},
frameId
);
page.addBlock(
'affine:image',
{
sourceId: id,
},
frameId
);
})
.catch(err => {
console.error('Failed to load large-image.png', err);
});
export const Default = () => {
const workspace = useAtomValue(waitForCurrentWorkspaceAtom);
const [page, setPage] = useState<Page | null>(null);
useEffect(() => {
const page = workspace.blockSuiteWorkspace.createPage('page0');
initEmptyPage(page);
fetch(new URL('@affine-test/fixtures/large-image.png', import.meta.url))
.then(res => res.arrayBuffer())
.then(async buffer => {
const id = await workspace.blockSuiteWorkspace.blob.set(
new Blob([buffer], { type: 'image/png' })
);
const frameId = page.getBlockByFlavour('affine:note')[0].id;
page.addBlock(
'affine:paragraph',
{
text: new page.Text('Please double click the image to preview it.'),
},
frameId
);
page.addBlock(
'affine:image',
{
sourceId: id,
},
frameId
);
})
.catch(err => {
console.error('Failed to load large-image.png', err);
});
setPage(page);
}, [workspace]);
if (!page) {
return null;
}
return (
<div
style={{
@@ -7,11 +7,10 @@ import { CMDKQuickSearchModal } from '@affine/core/components/pure/cmdk';
import { HighlightLabel } from '@affine/core/components/pure/cmdk/highlight';
import { WorkspaceFlavour } from '@affine/env/workspace';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { rootWorkspacesMetadataAtom } from '@affine/workspace/atom';
import { getOrCreateWorkspace } from '@affine/workspace/manager';
import { currentWorkspaceAtom } from '@affine/workspace/atom';
import type { Page } from '@blocksuite/store';
import type { Meta, StoryFn } from '@storybook/react';
import { currentWorkspaceIdAtom } from '@toeverything/infra/atom';
import { useWorkspace } from '@toeverything/hooks/use-workspace';
import { useStore } from 'jotai';
import { useEffect, useLayoutEffect, useState } from 'react';
import { withRouter } from 'storybook-addon-react-router-v6';
@@ -80,19 +79,15 @@ function useRegisterCommands() {
}
function usePrepareWorkspace() {
const workspaceId = 'test-workspace';
const store = useStore();
const workspace = useWorkspace({
id: workspaceId,
flavour: WorkspaceFlavour.LOCAL,
});
useLayoutEffect(() => {
const workspaceId = 'test-workspace';
getOrCreateWorkspace(workspaceId, WorkspaceFlavour.LOCAL);
store.set(rootWorkspacesMetadataAtom, [
{
id: workspaceId,
flavour: WorkspaceFlavour.LOCAL,
version: 4,
},
]);
store.set(currentWorkspaceIdAtom, workspaceId);
}, [store]);
store.set(currentWorkspaceAtom, workspace);
}, [store, workspace]);
}
export const CMDKStoryWithCommands: StoryFn = () => {
@@ -1,17 +1,15 @@
import { toast } from '@affine/component';
import { PublicLinkDisableModal } from '@affine/component/disable-public-link';
import { ShareMenu } from '@affine/core/components/affine/share-page-modal/share-menu';
import type {
AffineCloudWorkspace,
LocalWorkspace,
} from '@affine/env/workspace';
import { WorkspaceFlavour } from '@affine/env/workspace';
import { getOrCreateWorkspace } from '@affine/workspace/manager';
import type { Page } from '@blocksuite/store';
import { waitForCurrentWorkspaceAtom } from '@affine/workspace/atom';
import { type Page } from '@blocksuite/store';
import { expect } from '@storybook/jest';
import type { Meta, StoryFn } from '@storybook/react';
import { use } from 'foxact/use';
import { useState } from 'react';
import { initEmptyPage } from '@toeverything/infra/blocksuite';
import { useAtomValue } from 'jotai';
import { nanoid } from 'nanoid';
import { useEffect, useState } from 'react';
export default {
title: 'AFFiNE/ShareMenu',
@@ -21,57 +19,30 @@ export default {
},
} satisfies Meta;
async function initPage(page: Page) {
await page.waitForLoaded();
// Add page block and surface block at root level
const pageBlockId = page.addBlock('affine:page', {
title: new page.Text('Hello, world!'),
});
page.addBlock('affine:surface', {}, pageBlockId);
const frameId = page.addBlock('affine:note', {}, pageBlockId);
page.addBlock(
'affine:paragraph',
{
text: new page.Text('This is a paragraph.'),
},
frameId
);
page.resetHistory();
}
const blockSuiteWorkspace = getOrCreateWorkspace(
'test-workspace',
WorkspaceFlavour.LOCAL
);
const promise = Promise.all([
initPage(blockSuiteWorkspace.createPage({ id: 'page0' })),
initPage(blockSuiteWorkspace.createPage({ id: 'page1' })),
initPage(blockSuiteWorkspace.createPage({ id: 'page2' })),
]);
const localWorkspace: LocalWorkspace = {
id: 'test-workspace',
flavour: WorkspaceFlavour.LOCAL,
blockSuiteWorkspace,
};
const affineWorkspace: AffineCloudWorkspace = {
id: 'test-workspace',
flavour: WorkspaceFlavour.AFFINE_CLOUD,
blockSuiteWorkspace,
};
async function unimplemented() {
toast('work in progress');
}
export const Basic: StoryFn = () => {
use(promise);
const workspace = useAtomValue(waitForCurrentWorkspaceAtom);
const [page, setPage] = useState<Page | null>(null);
useEffect(() => {
const page = workspace.blockSuiteWorkspace.createPage(nanoid());
initEmptyPage(page);
setPage(page);
}, [workspace]);
if (!page) {
return <div></div>;
}
return (
<ShareMenu
currentPage={blockSuiteWorkspace.getPage('page0') as Page}
workspace={localWorkspace}
currentPage={page}
workspaceMetadata={workspace}
onEnableAffineCloud={unimplemented}
/>
);
@@ -95,11 +66,28 @@ Basic.play = async ({ canvasElement }) => {
};
export const AffineBasic: StoryFn = () => {
use(promise);
const workspace = useAtomValue(waitForCurrentWorkspaceAtom);
const [page, setPage] = useState<Page | null>(null);
useEffect(() => {
const page = workspace.blockSuiteWorkspace.createPage(nanoid());
initEmptyPage(page);
setPage(page);
}, [workspace]);
if (!page) {
return <div></div>;
}
return (
<ShareMenu
currentPage={blockSuiteWorkspace.getPage('page0') as Page}
workspace={affineWorkspace}
currentPage={page}
workspaceMetadata={{
...workspace.meta,
flavour: WorkspaceFlavour.AFFINE_CLOUD,
}}
onEnableAffineCloud={unimplemented}
/>
);
@@ -107,7 +95,6 @@ export const AffineBasic: StoryFn = () => {
export const DisableModal: StoryFn = () => {
const [open, setOpen] = useState(false);
use(promise);
return (
<>
<div onClick={() => setOpen(!open)}>Disable Public Link</div>
@@ -1,10 +1,8 @@
import type { WorkspaceListProps } from '@affine/component/workspace-list';
import { WorkspaceList } from '@affine/component/workspace-list';
import { WorkspaceFlavour } from '@affine/env/workspace';
import { getOrCreateWorkspace } from '@affine/workspace/manager';
import { arrayMove } from '@dnd-kit/sortable';
import { workspaceListAtom } from '@affine/workspace/atom';
import type { Meta } from '@storybook/react';
import { useState } from 'react';
import { useAtomValue } from 'jotai';
export default {
title: 'AFFiNE/WorkspaceList',
@@ -15,49 +13,14 @@ export default {
} satisfies Meta<WorkspaceListProps>;
export const Default = () => {
const [items, setItems] = useState(() => {
const items = [
{
id: '1',
flavour: WorkspaceFlavour.LOCAL,
blockSuiteWorkspace: getOrCreateWorkspace('1', WorkspaceFlavour.LOCAL),
},
{
id: '2',
flavour: WorkspaceFlavour.LOCAL,
blockSuiteWorkspace: getOrCreateWorkspace('2', WorkspaceFlavour.LOCAL),
},
{
id: '3',
flavour: WorkspaceFlavour.LOCAL,
blockSuiteWorkspace: getOrCreateWorkspace('3', WorkspaceFlavour.LOCAL),
},
] satisfies WorkspaceListProps['items'];
items.forEach(item => {
item.blockSuiteWorkspace.meta.setName(item.id);
});
return items;
});
const list = useAtomValue(workspaceListAtom);
return (
<WorkspaceList
currentWorkspaceId={null}
items={items}
items={list}
onClick={() => {}}
onSettingClick={() => {}}
onDragEnd={event => {
const { active, over } = event;
if (active.id !== over?.id) {
setItems(items => {
const oldIndex = items.findIndex(item => item.id === active.id);
const newIndex = items.findIndex(item => item.id === over?.id);
return arrayMove(items, oldIndex, newIndex);
});
}
}}
onDragEnd={_ => {}}
/>
);
};