Daniel Rochetti 145159a12f
feat(client): realtime client (#29)
* 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
2023-11-27 09:43:37 -08:00

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