init: the first public commit for AFFiNE

This commit is contained in:
DarkSky
2022-07-22 15:49:21 +08:00
commit e3e3741393
1451 changed files with 108124 additions and 0 deletions

View 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;
};

View File

@@ -0,0 +1 @@
export * from './use-shapes';

View 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>);
};

View File

@@ -0,0 +1 @@
export { AffineBoardWitchContext as AffineBoard } from './Board';