mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-14 21:27:20 +00:00
init: the first public commit for AFFiNE
This commit is contained in:
136
libs/components/affine-board/src/Board.tsx
Normal file
136
libs/components/affine-board/src/Board.tsx
Normal file
@@ -0,0 +1,136 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Tldraw } from '@toeverything/components/board-draw';
|
||||
import { tools } from '@toeverything/components/board-tools';
|
||||
import { getSession } from '@toeverything/components/board-sessions';
|
||||
import * as commands from '@toeverything/components/board-commands';
|
||||
import { TldrawApp, deepCopy } from '@toeverything/components/board-state';
|
||||
import { TDShapeType } from '@toeverything/components/board-types';
|
||||
import { services } from '@toeverything/datasource/db-service';
|
||||
import { useShapes } from './hooks';
|
||||
import { RecastBlockProvider } from '@toeverything/components/editor-core';
|
||||
import { createEditor } from '@toeverything/components/affine-editor';
|
||||
import { AsyncBlock, BlockEditor } from '@toeverything/framework/virgo';
|
||||
|
||||
interface AffineBoardProps {
|
||||
workspace: string;
|
||||
rootBlockId: string;
|
||||
}
|
||||
|
||||
const AffineBoard = ({ workspace, rootBlockId }: AffineBoardProps) => {
|
||||
const [app, set_app] = useState<TldrawApp>();
|
||||
|
||||
const [document] = useState(() => {
|
||||
return {
|
||||
...deepCopy(TldrawApp.default_document),
|
||||
id: workspace,
|
||||
pages: {
|
||||
[rootBlockId]: {
|
||||
id: rootBlockId,
|
||||
name: `Page ${rootBlockId}`,
|
||||
childIndex: 1,
|
||||
shapes: {},
|
||||
bindings: {},
|
||||
},
|
||||
},
|
||||
pageStates: {
|
||||
[rootBlockId]: {
|
||||
id: rootBlockId,
|
||||
camera: {
|
||||
point: [0, 0],
|
||||
zoom: 1,
|
||||
},
|
||||
selectedIds: [],
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const shapes = useShapes(workspace, rootBlockId);
|
||||
useEffect(() => {
|
||||
if (app) {
|
||||
app.replacePageContent(shapes || {}, {}, {});
|
||||
}
|
||||
}, [app, shapes]);
|
||||
|
||||
return (
|
||||
<Tldraw
|
||||
document={document}
|
||||
commands={commands}
|
||||
tools={tools}
|
||||
getSession={getSession}
|
||||
callbacks={{
|
||||
onMount(app) {
|
||||
set_app(app);
|
||||
},
|
||||
onChangePage(app, shapes, bindings, assets) {
|
||||
Promise.all(
|
||||
Object.entries(shapes).map(async ([id, shape]) => {
|
||||
if (shape === undefined) {
|
||||
return services.api.editorBlock.delete({
|
||||
workspace: workspace,
|
||||
id,
|
||||
});
|
||||
} else {
|
||||
let block = (
|
||||
await services.api.editorBlock.get({
|
||||
workspace: workspace,
|
||||
ids: [shape.affineId],
|
||||
})
|
||||
)?.[0];
|
||||
if (!block) {
|
||||
block =
|
||||
await services.api.editorBlock.create({
|
||||
workspace: workspace,
|
||||
parentId:
|
||||
app.appState.currentPageId,
|
||||
type:
|
||||
shape.type ===
|
||||
TDShapeType.Editor
|
||||
? 'group'
|
||||
: 'shape',
|
||||
});
|
||||
}
|
||||
shape.affineId = block.id;
|
||||
return services.api.editorBlock.update({
|
||||
workspace: shape.workspace,
|
||||
id: block.id,
|
||||
properties: {
|
||||
shapeProps: {
|
||||
value: JSON.stringify(shape),
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
})
|
||||
);
|
||||
},
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const AffineBoardWitchContext = ({
|
||||
workspace,
|
||||
rootBlockId,
|
||||
}: AffineBoardProps) => {
|
||||
const [editor, set_editor] = useState<BlockEditor>();
|
||||
useEffect(() => {
|
||||
const inner_editor = createEditor(workspace, true);
|
||||
set_editor(inner_editor);
|
||||
return () => {
|
||||
inner_editor.dispose();
|
||||
};
|
||||
}, [workspace]);
|
||||
|
||||
const [page, set_page] = useState<AsyncBlock>();
|
||||
useEffect(() => {
|
||||
editor?.getBlockById(rootBlockId).then(block => {
|
||||
set_page(block);
|
||||
});
|
||||
}, [editor, rootBlockId]);
|
||||
return page ? (
|
||||
<RecastBlockProvider block={page}>
|
||||
<AffineBoard workspace={workspace} rootBlockId={rootBlockId} />
|
||||
</RecastBlockProvider>
|
||||
) : null;
|
||||
};
|
||||
1
libs/components/affine-board/src/hooks/index.ts
Normal file
1
libs/components/affine-board/src/hooks/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './use-shapes';
|
||||
73
libs/components/affine-board/src/hooks/use-shapes.ts
Normal file
73
libs/components/affine-board/src/hooks/use-shapes.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { services } from '@toeverything/datasource/db-service';
|
||||
import type { ReturnEditorBlock } from '@toeverything/datasource/db-service';
|
||||
import type { TDShape } from '@toeverything/components/board-types';
|
||||
import { Editor } from '@toeverything/components/board-shapes';
|
||||
|
||||
export const useShapes = (workspace: string, rootBlockId: string) => {
|
||||
const [blocks, setBlocks] = useState<ReturnEditorBlock[]>();
|
||||
useEffect(() => {
|
||||
services.api.editorBlock
|
||||
.get({ workspace, ids: [rootBlockId] })
|
||||
.then(async blockData => {
|
||||
const shapes = await Promise.all(
|
||||
(blockData?.[0]?.children || []).map(async childId => {
|
||||
const childBlock = (
|
||||
await services.api.editorBlock.get({
|
||||
workspace,
|
||||
ids: [childId],
|
||||
})
|
||||
)?.[0];
|
||||
return childBlock;
|
||||
})
|
||||
);
|
||||
setBlocks(shapes);
|
||||
});
|
||||
let unobserve: () => void;
|
||||
services.api.editorBlock
|
||||
.observe({ workspace, id: rootBlockId }, async blockData => {
|
||||
const shapes = await Promise.all(
|
||||
(blockData?.children || []).map(async childId => {
|
||||
const childBlock = (
|
||||
await services.api.editorBlock.get({
|
||||
workspace,
|
||||
ids: [childId],
|
||||
})
|
||||
)?.[0];
|
||||
return childBlock;
|
||||
})
|
||||
);
|
||||
setBlocks(shapes);
|
||||
})
|
||||
.then(cb => {
|
||||
unobserve = cb;
|
||||
});
|
||||
|
||||
return () => {
|
||||
unobserve?.();
|
||||
};
|
||||
}, [workspace, rootBlockId]);
|
||||
|
||||
let groupCount = 0;
|
||||
|
||||
return blocks?.reduce((acc, block) => {
|
||||
const shapeProps = block.properties.shapeProps?.value
|
||||
? JSON.parse(block.properties.shapeProps.value)
|
||||
: {};
|
||||
if (block.type === 'shape') {
|
||||
acc[block.id] = { ...shapeProps, id: block.id };
|
||||
} else {
|
||||
acc[block.id] = Editor.getShape({
|
||||
point: [groupCount * 740, 200],
|
||||
id: block.id,
|
||||
...shapeProps,
|
||||
affineId: shapeProps.affineId ?? block.id,
|
||||
workspace: block.workspace,
|
||||
rootBlockId: block.id,
|
||||
});
|
||||
groupCount = groupCount + 1;
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, {} as Record<string, TDShape>);
|
||||
};
|
||||
1
libs/components/affine-board/src/index.ts
Normal file
1
libs/components/affine-board/src/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { AffineBoardWitchContext as AffineBoard } from './Board';
|
||||
Reference in New Issue
Block a user