mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-08-08 12:45:55 +08:00
refactor: workspace manager (#5060)
This commit is contained in:
@@ -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={_ => {}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user