* feat(client): realtime client * chore: alpha release * fix: remove os requirement * fix: check if process is defined * fix: ws connection key * fix: outgoing request throttling logic * chore: 0.6.0.alpha.4 release * chore: update realtime demo * chore: update preloaded scene * feat: auth wip * fix: compilation issue * feat: basic auth impl missing error handling * chore: remove console.log prepare 0.6.0 * fix: remove unsused code
117 lines
3.3 KiB
TypeScript
117 lines
3.3 KiB
TypeScript
import { type Excalidraw } from '@excalidraw/excalidraw';
|
|
import { ExcalidrawElement } from '@excalidraw/excalidraw/types/element/types';
|
|
import {
|
|
AppState,
|
|
ExcalidrawImperativeAPI,
|
|
} from '@excalidraw/excalidraw/types/types';
|
|
import { useCallback, useEffect, useState } from 'react';
|
|
import initialDrawing from './drawingState.json';
|
|
|
|
export type CanvasChangeEvent = {
|
|
elements: readonly ExcalidrawElement[];
|
|
appState: AppState;
|
|
imageData: string;
|
|
};
|
|
|
|
export type DrawingCanvasProps = {
|
|
onCanvasChange: (event: CanvasChangeEvent) => void;
|
|
};
|
|
|
|
async function blobToBase64(blob: Blob): Promise<string> {
|
|
const reader = new FileReader();
|
|
reader.readAsDataURL(blob);
|
|
return new Promise<string>((resolve) => {
|
|
reader.onloadend = () => {
|
|
resolve(reader.result?.toString() || '');
|
|
};
|
|
});
|
|
}
|
|
|
|
export function DrawingCanvas({ onCanvasChange }: DrawingCanvasProps) {
|
|
const [ExcalidrawComponent, setExcalidrawComponent] = useState<
|
|
typeof Excalidraw | null
|
|
>(null);
|
|
const [excalidrawAPI, setExcalidrawAPI] =
|
|
useState<ExcalidrawImperativeAPI | null>(null);
|
|
const [sceneData, setSceneData] = useState<any>(null);
|
|
|
|
useEffect(() => {
|
|
import('@excalidraw/excalidraw').then((comp) =>
|
|
setExcalidrawComponent(comp.Excalidraw)
|
|
);
|
|
const onResize = () => {
|
|
if (excalidrawAPI) {
|
|
excalidrawAPI.refresh();
|
|
}
|
|
};
|
|
window.addEventListener('resize', onResize);
|
|
return () => {
|
|
window.removeEventListener('resize', onResize);
|
|
};
|
|
}, []);
|
|
|
|
const handleCanvasChanges = useCallback(
|
|
async (elements: readonly ExcalidrawElement[], appState: AppState) => {
|
|
if (!excalidrawAPI || !elements || !elements.length) {
|
|
return;
|
|
}
|
|
const { exportToBlob, convertToExcalidrawElements, serializeAsJSON } =
|
|
await import('@excalidraw/excalidraw');
|
|
|
|
const [boundingBoxElement] = convertToExcalidrawElements([
|
|
{
|
|
type: 'rectangle',
|
|
x: 0,
|
|
y: 0,
|
|
width: 512,
|
|
height: 512,
|
|
fillStyle: 'solid',
|
|
backgroundColor: 'cyan',
|
|
},
|
|
]);
|
|
|
|
const newSceneData = serializeAsJSON(
|
|
elements,
|
|
appState,
|
|
excalidrawAPI.getFiles(),
|
|
'local'
|
|
);
|
|
if (newSceneData !== sceneData) {
|
|
setSceneData(newSceneData);
|
|
const blob = await exportToBlob({
|
|
elements: [boundingBoxElement, ...elements],
|
|
appState: {
|
|
...appState,
|
|
frameRendering: {
|
|
...(appState.frameRendering || {}),
|
|
clip: false,
|
|
},
|
|
},
|
|
files: excalidrawAPI.getFiles(),
|
|
mimeType: 'image/webp',
|
|
quality: 0.5,
|
|
exportPadding: 0,
|
|
getDimensions: () => {
|
|
return { width: 512, height: 512 };
|
|
},
|
|
});
|
|
const imageData = await blobToBase64(blob);
|
|
onCanvasChange({ elements, appState, imageData });
|
|
}
|
|
},
|
|
[excalidrawAPI, onCanvasChange, sceneData]
|
|
);
|
|
|
|
return (
|
|
<div style={{ height: '560px', width: '560px' }}>
|
|
{ExcalidrawComponent && (
|
|
<ExcalidrawComponent
|
|
excalidrawAPI={(api) => setExcalidrawAPI(api)}
|
|
initialData={{ elements: initialDrawing as ExcalidrawElement[] }}
|
|
onChange={handleCanvasChanges}
|
|
/>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|