mirror of
				https://github.com/excalidraw/excalidraw.git
				synced 2025-10-26 08:24:20 +01:00 
			
		
		
		
	 73bf50e8a8
			
		
	
	73bf50e8a8
	
	
	
		
			
			* fix: remove t and allow name to be nullable * pass name as required prop * remove Unnamed * pass name to excalidrawPlus as well for better type safe * render once we have excalidrawAPI to avoid defaulting * rename `getAppName` -> `getName` (temporary) * stop preventing editing filenames when `props.name` supplied * keep `name` as optional param for export functions * keep `appState.name` on `props.name` state separate * fix lint * assertive first * fix lint * Add TODO --------- Co-authored-by: dwelle <5153846+dwelle@users.noreply.github.com>
		
			
				
	
	
		
			135 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			135 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| import React from "react";
 | |
| import { Card } from "../../packages/excalidraw/components/Card";
 | |
| import { ToolButton } from "../../packages/excalidraw/components/ToolButton";
 | |
| import { serializeAsJSON } from "../../packages/excalidraw/data/json";
 | |
| import { loadFirebaseStorage, saveFilesToFirebase } from "../data/firebase";
 | |
| import {
 | |
|   FileId,
 | |
|   NonDeletedExcalidrawElement,
 | |
| } from "../../packages/excalidraw/element/types";
 | |
| import {
 | |
|   AppState,
 | |
|   BinaryFileData,
 | |
|   BinaryFiles,
 | |
| } from "../../packages/excalidraw/types";
 | |
| import { nanoid } from "nanoid";
 | |
| import { useI18n } from "../../packages/excalidraw/i18n";
 | |
| import {
 | |
|   encryptData,
 | |
|   generateEncryptionKey,
 | |
| } from "../../packages/excalidraw/data/encryption";
 | |
| import { isInitializedImageElement } from "../../packages/excalidraw/element/typeChecks";
 | |
| import { FILE_UPLOAD_MAX_BYTES } from "../app_constants";
 | |
| import { encodeFilesForUpload } from "../data/FileManager";
 | |
| import { MIME_TYPES } from "../../packages/excalidraw/constants";
 | |
| import { trackEvent } from "../../packages/excalidraw/analytics";
 | |
| import { getFrame } from "../../packages/excalidraw/utils";
 | |
| import { ExcalidrawLogo } from "../../packages/excalidraw/components/ExcalidrawLogo";
 | |
| 
 | |
| export const exportToExcalidrawPlus = async (
 | |
|   elements: readonly NonDeletedExcalidrawElement[],
 | |
|   appState: Partial<AppState>,
 | |
|   files: BinaryFiles,
 | |
|   name: string,
 | |
| ) => {
 | |
|   const firebase = await loadFirebaseStorage();
 | |
| 
 | |
|   const id = `${nanoid(12)}`;
 | |
| 
 | |
|   const encryptionKey = (await generateEncryptionKey())!;
 | |
|   const encryptedData = await encryptData(
 | |
|     encryptionKey,
 | |
|     serializeAsJSON(elements, appState, files, "database"),
 | |
|   );
 | |
| 
 | |
|   const blob = new Blob(
 | |
|     [encryptedData.iv, new Uint8Array(encryptedData.encryptedBuffer)],
 | |
|     {
 | |
|       type: MIME_TYPES.binary,
 | |
|     },
 | |
|   );
 | |
| 
 | |
|   await firebase
 | |
|     .storage()
 | |
|     .ref(`/migrations/scenes/${id}`)
 | |
|     .put(blob, {
 | |
|       customMetadata: {
 | |
|         data: JSON.stringify({ version: 2, name }),
 | |
|         created: Date.now().toString(),
 | |
|       },
 | |
|     });
 | |
| 
 | |
|   const filesMap = new Map<FileId, BinaryFileData>();
 | |
|   for (const element of elements) {
 | |
|     if (isInitializedImageElement(element) && files[element.fileId]) {
 | |
|       filesMap.set(element.fileId, files[element.fileId]);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (filesMap.size) {
 | |
|     const filesToUpload = await encodeFilesForUpload({
 | |
|       files: filesMap,
 | |
|       encryptionKey,
 | |
|       maxBytes: FILE_UPLOAD_MAX_BYTES,
 | |
|     });
 | |
| 
 | |
|     await saveFilesToFirebase({
 | |
|       prefix: `/migrations/files/scenes/${id}`,
 | |
|       files: filesToUpload,
 | |
|     });
 | |
|   }
 | |
| 
 | |
|   window.open(
 | |
|     `${
 | |
|       import.meta.env.VITE_APP_PLUS_APP
 | |
|     }/import?excalidraw=${id},${encryptionKey}`,
 | |
|   );
 | |
| };
 | |
| 
 | |
| export const ExportToExcalidrawPlus: React.FC<{
 | |
|   elements: readonly NonDeletedExcalidrawElement[];
 | |
|   appState: Partial<AppState>;
 | |
|   files: BinaryFiles;
 | |
|   name: string;
 | |
|   onError: (error: Error) => void;
 | |
|   onSuccess: () => void;
 | |
| }> = ({ elements, appState, files, name, onError, onSuccess }) => {
 | |
|   const { t } = useI18n();
 | |
|   return (
 | |
|     <Card color="primary">
 | |
|       <div className="Card-icon">
 | |
|         <ExcalidrawLogo
 | |
|           style={{
 | |
|             [`--color-logo-icon` as any]: "#fff",
 | |
|             width: "2.8rem",
 | |
|             height: "2.8rem",
 | |
|           }}
 | |
|         />
 | |
|       </div>
 | |
|       <h2>Excalidraw+</h2>
 | |
|       <div className="Card-details">
 | |
|         {t("exportDialog.excalidrawplus_description")}
 | |
|       </div>
 | |
|       <ToolButton
 | |
|         className="Card-button"
 | |
|         type="button"
 | |
|         title={t("exportDialog.excalidrawplus_button")}
 | |
|         aria-label={t("exportDialog.excalidrawplus_button")}
 | |
|         showAriaLabel={true}
 | |
|         onClick={async () => {
 | |
|           try {
 | |
|             trackEvent("export", "eplus", `ui (${getFrame()})`);
 | |
|             await exportToExcalidrawPlus(elements, appState, files, name);
 | |
|             onSuccess();
 | |
|           } catch (error: any) {
 | |
|             console.error(error);
 | |
|             if (error.name !== "AbortError") {
 | |
|               onError(new Error(t("exportDialog.excalidrawplus_exportError")));
 | |
|             }
 | |
|           }
 | |
|         }}
 | |
|       />
 | |
|     </Card>
 | |
|   );
 | |
| };
 |