fix test failures, new export/backup solutions
This commit is contained in:
@@ -14,101 +14,19 @@ import { reconcileElements } from '../utils/sync';
|
||||
import { exportFromEditor } from '../utils/exportUtils';
|
||||
import * as api from '../api';
|
||||
import { useTheme } from '../context/ThemeContext';
|
||||
import {
|
||||
UIOptions,
|
||||
getColorFromString,
|
||||
getFilesDelta,
|
||||
getInitialsFromName,
|
||||
haveSameElements,
|
||||
} from './editor/shared';
|
||||
import type { ElementVersionInfo } from './editor/shared';
|
||||
|
||||
interface Peer extends UserIdentity {
|
||||
isActive: boolean;
|
||||
}
|
||||
|
||||
interface ElementVersionInfo {
|
||||
version: number;
|
||||
versionNonce: number;
|
||||
}
|
||||
|
||||
const haveSameElements = (a: readonly any[] = [], b: readonly any[] = []) => {
|
||||
if (!a || !b) return false;
|
||||
if (a.length !== b.length) return false;
|
||||
for (let i = 0; i < a.length; i++) {
|
||||
const left = a[i];
|
||||
const right = b[i];
|
||||
if (!left || !right) return false;
|
||||
if (left.id !== right.id) return false;
|
||||
if ((left.version ?? 0) !== (right.version ?? 0)) return false;
|
||||
if ((left.versionNonce ?? 0) !== (right.versionNonce ?? 0)) return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
const buildFileSignature = (file: any): string => {
|
||||
const mimeType = typeof file?.mimeType === "string" ? file.mimeType : "";
|
||||
const id = typeof file?.id === "string" ? file.id : "";
|
||||
const dataURL = typeof file?.dataURL === "string" ? file.dataURL : "";
|
||||
// Avoid keeping the whole dataURL for comparisons; use a cheap signature.
|
||||
const prefix = dataURL.slice(0, 32);
|
||||
const suffix = dataURL.slice(-32);
|
||||
return `${id}|${mimeType}|${dataURL.length}|${prefix}|${suffix}`;
|
||||
};
|
||||
|
||||
const getFilesDelta = (
|
||||
previous: Record<string, any>,
|
||||
next: Record<string, any>
|
||||
): Record<string, any> => {
|
||||
const delta: Record<string, any> = {};
|
||||
const prev = previous || {};
|
||||
const nxt = next || {};
|
||||
|
||||
for (const fileId of Object.keys(nxt)) {
|
||||
const nextFile = nxt[fileId];
|
||||
const nextHasDataUrl = typeof nextFile?.dataURL === "string" && nextFile.dataURL.length > 0;
|
||||
// Only sync files that actually have data; otherwise other tabs can't render yet.
|
||||
if (!nextHasDataUrl) continue;
|
||||
|
||||
const prevFile = prev[fileId];
|
||||
if (!prevFile) {
|
||||
delta[fileId] = nextFile;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (buildFileSignature(prevFile) !== buildFileSignature(nextFile)) {
|
||||
delta[fileId] = nextFile;
|
||||
}
|
||||
}
|
||||
|
||||
return delta;
|
||||
};
|
||||
|
||||
const UIOptions = {
|
||||
canvasActions: {
|
||||
saveToActiveFile: false,
|
||||
loadScene: false,
|
||||
export: { saveFileToDisk: false },
|
||||
toggleTheme: true,
|
||||
},
|
||||
};
|
||||
|
||||
const getInitialsFromName = (name: string): string => {
|
||||
const trimmed = name.trim();
|
||||
if (!trimmed) return 'U';
|
||||
const parts = trimmed.split(/\s+/).filter(Boolean);
|
||||
if (parts.length >= 2) {
|
||||
return `${parts[0][0]}${parts[parts.length - 1][0]}`.toUpperCase();
|
||||
}
|
||||
return trimmed.slice(0, 2).toUpperCase();
|
||||
};
|
||||
|
||||
// Helper function to generate a color from a string (consistent hash)
|
||||
const getColorFromString = (str: string): string => {
|
||||
const COLORS = [
|
||||
"#ef4444", "#f97316", "#f59e0b", "#84cc16", "#22c55e", "#10b981",
|
||||
"#14b8a6", "#06b6d4", "#0ea5e9", "#3b82f6", "#6366f1", "#8b5cf6",
|
||||
"#a855f7", "#d946ef", "#ec4899", "#f43f5e",
|
||||
];
|
||||
let hash = 0;
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
hash = str.charCodeAt(i) + ((hash << 5) - hash);
|
||||
}
|
||||
return COLORS[Math.abs(hash) % COLORS.length];
|
||||
};
|
||||
|
||||
export const Editor: React.FC = () => {
|
||||
const { id } = useParams<{ id: string }>();
|
||||
const navigate = useNavigate();
|
||||
|
||||
Reference in New Issue
Block a user