ExportToExcalidrawPlus.tsx 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. import React from "react";
  2. import { Card } from "../../src/components/Card";
  3. import { ToolButton } from "../../src/components/ToolButton";
  4. import { serializeAsJSON } from "../../src/data/json";
  5. import { loadFirebaseStorage, saveFilesToFirebase } from "../data/firebase";
  6. import { FileId, NonDeletedExcalidrawElement } from "../../src/element/types";
  7. import { AppState, BinaryFileData, BinaryFiles } from "../../src/types";
  8. import { nanoid } from "nanoid";
  9. import { useI18n } from "../../src/i18n";
  10. import { encryptData, generateEncryptionKey } from "../../src/data/encryption";
  11. import { isInitializedImageElement } from "../../src/element/typeChecks";
  12. import { FILE_UPLOAD_MAX_BYTES } from "../app_constants";
  13. import { encodeFilesForUpload } from "../data/FileManager";
  14. import { MIME_TYPES } from "../../src/constants";
  15. import { trackEvent } from "../../src/analytics";
  16. import { getFrame } from "../../src/utils";
  17. import { ExcalidrawLogo } from "../../src/components/ExcalidrawLogo";
  18. export const exportToExcalidrawPlus = async (
  19. elements: readonly NonDeletedExcalidrawElement[],
  20. appState: Partial<AppState>,
  21. files: BinaryFiles,
  22. ) => {
  23. const firebase = await loadFirebaseStorage();
  24. const id = `${nanoid(12)}`;
  25. const encryptionKey = (await generateEncryptionKey())!;
  26. const encryptedData = await encryptData(
  27. encryptionKey,
  28. serializeAsJSON(elements, appState, files, "database"),
  29. );
  30. const blob = new Blob(
  31. [encryptedData.iv, new Uint8Array(encryptedData.encryptedBuffer)],
  32. {
  33. type: MIME_TYPES.binary,
  34. },
  35. );
  36. await firebase
  37. .storage()
  38. .ref(`/migrations/scenes/${id}`)
  39. .put(blob, {
  40. customMetadata: {
  41. data: JSON.stringify({ version: 2, name: appState.name }),
  42. created: Date.now().toString(),
  43. },
  44. });
  45. const filesMap = new Map<FileId, BinaryFileData>();
  46. for (const element of elements) {
  47. if (isInitializedImageElement(element) && files[element.fileId]) {
  48. filesMap.set(element.fileId, files[element.fileId]);
  49. }
  50. }
  51. if (filesMap.size) {
  52. const filesToUpload = await encodeFilesForUpload({
  53. files: filesMap,
  54. encryptionKey,
  55. maxBytes: FILE_UPLOAD_MAX_BYTES,
  56. });
  57. await saveFilesToFirebase({
  58. prefix: `/migrations/files/scenes/${id}`,
  59. files: filesToUpload,
  60. });
  61. }
  62. window.open(
  63. `${
  64. import.meta.env.VITE_APP_PLUS_APP
  65. }/import?excalidraw=${id},${encryptionKey}`,
  66. );
  67. };
  68. export const ExportToExcalidrawPlus: React.FC<{
  69. elements: readonly NonDeletedExcalidrawElement[];
  70. appState: Partial<AppState>;
  71. files: BinaryFiles;
  72. onError: (error: Error) => void;
  73. }> = ({ elements, appState, files, onError }) => {
  74. const { t } = useI18n();
  75. return (
  76. <Card color="primary">
  77. <div className="Card-icon">
  78. <ExcalidrawLogo
  79. style={{
  80. [`--color-logo-icon` as any]: "#fff",
  81. width: "2.8rem",
  82. height: "2.8rem",
  83. }}
  84. />
  85. </div>
  86. <h2>Excalidraw+</h2>
  87. <div className="Card-details">
  88. {t("exportDialog.excalidrawplus_description")}
  89. </div>
  90. <ToolButton
  91. className="Card-button"
  92. type="button"
  93. title={t("exportDialog.excalidrawplus_button")}
  94. aria-label={t("exportDialog.excalidrawplus_button")}
  95. showAriaLabel={true}
  96. onClick={async () => {
  97. try {
  98. trackEvent("export", "eplus", `ui (${getFrame()})`);
  99. await exportToExcalidrawPlus(elements, appState, files);
  100. } catch (error: any) {
  101. console.error(error);
  102. if (error.name !== "AbortError") {
  103. onError(new Error(t("exportDialog.excalidrawplus_exportError")));
  104. }
  105. }
  106. }}
  107. />
  108. </Card>
  109. );
  110. };