Browse Source

feat: allow `avif`, `jfif`, `webp`, `bmp`, `ico` image types (#6500

* feat: allow `avif`, `jfif`, `webp`, `bmp`, `ico` image types

* dedupe for SSOT

* more SSOT
David Luzar 2 years ago
parent
commit
fee760d38c
5 changed files with 28 additions and 36 deletions
  1. 4 1
      src/components/App.tsx
  2. 16 16
      src/constants.ts
  3. 3 5
      src/data/blob.ts
  4. 1 10
      src/data/filesystem.ts
  5. 4 4
      src/types.ts

+ 4 - 1
src/components/App.tsx

@@ -60,6 +60,7 @@ import {
   ENV,
   ENV,
   EVENT,
   EVENT,
   GRID_SIZE,
   GRID_SIZE,
+  IMAGE_MIME_TYPES,
   IMAGE_RENDER_TIMEOUT,
   IMAGE_RENDER_TIMEOUT,
   isAndroid,
   isAndroid,
   isBrave,
   isBrave,
@@ -5743,7 +5744,9 @@ class App extends React.Component<AppProps, AppState> {
 
 
       const imageFile = await fileOpen({
       const imageFile = await fileOpen({
         description: "Image",
         description: "Image",
-        extensions: ["jpg", "png", "svg", "gif"],
+        extensions: Object.keys(
+          IMAGE_MIME_TYPES,
+        ) as (keyof typeof IMAGE_MIME_TYPES)[],
       });
       });
 
 
       const imageElement = this.createImageElement({
       const imageElement = this.createImageElement({

+ 16 - 16
src/constants.ts

@@ -105,20 +105,30 @@ export const CANVAS_ONLY_ACTIONS = ["selectAll"];
 
 
 export const GRID_SIZE = 20; // TODO make it configurable?
 export const GRID_SIZE = 20; // TODO make it configurable?
 
 
-export const MIME_TYPES = {
-  excalidraw: "application/vnd.excalidraw+json",
-  excalidrawlib: "application/vnd.excalidrawlib+json",
-  json: "application/json",
+export const IMAGE_MIME_TYPES = {
   svg: "image/svg+xml",
   svg: "image/svg+xml",
-  "excalidraw.svg": "image/svg+xml",
   png: "image/png",
   png: "image/png",
-  "excalidraw.png": "image/png",
   jpg: "image/jpeg",
   jpg: "image/jpeg",
   gif: "image/gif",
   gif: "image/gif",
   webp: "image/webp",
   webp: "image/webp",
   bmp: "image/bmp",
   bmp: "image/bmp",
   ico: "image/x-icon",
   ico: "image/x-icon",
+  avif: "image/avif",
+  jfif: "image/jfif",
+} as const;
+
+export const MIME_TYPES = {
+  json: "application/json",
+  // excalidraw data
+  excalidraw: "application/vnd.excalidraw+json",
+  excalidrawlib: "application/vnd.excalidrawlib+json",
+  // image-encoded excalidraw data
+  "excalidraw.svg": "image/svg+xml",
+  "excalidraw.png": "image/png",
+  // binary
   binary: "application/octet-stream",
   binary: "application/octet-stream",
+  // image
+  ...IMAGE_MIME_TYPES,
 } as const;
 } as const;
 
 
 export const EXPORT_DATA_TYPES = {
 export const EXPORT_DATA_TYPES = {
@@ -189,16 +199,6 @@ export const DEFAULT_EXPORT_PADDING = 10; // px
 
 
 export const DEFAULT_MAX_IMAGE_WIDTH_OR_HEIGHT = 1440;
 export const DEFAULT_MAX_IMAGE_WIDTH_OR_HEIGHT = 1440;
 
 
-export const ALLOWED_IMAGE_MIME_TYPES = [
-  MIME_TYPES.png,
-  MIME_TYPES.jpg,
-  MIME_TYPES.svg,
-  MIME_TYPES.gif,
-  MIME_TYPES.webp,
-  MIME_TYPES.bmp,
-  MIME_TYPES.ico,
-] as const;
-
 export const MAX_ALLOWED_FILE_BYTES = 2 * 1024 * 1024;
 export const MAX_ALLOWED_FILE_BYTES = 2 * 1024 * 1024;
 
 
 export const SVG_NS = "http://www.w3.org/2000/svg";
 export const SVG_NS = "http://www.w3.org/2000/svg";

+ 3 - 5
src/data/blob.ts

@@ -1,6 +1,6 @@
 import { nanoid } from "nanoid";
 import { nanoid } from "nanoid";
 import { cleanAppStateForExport } from "../appState";
 import { cleanAppStateForExport } from "../appState";
-import { ALLOWED_IMAGE_MIME_TYPES, MIME_TYPES } from "../constants";
+import { IMAGE_MIME_TYPES, MIME_TYPES } from "../constants";
 import { clearElementsForExport } from "../element";
 import { clearElementsForExport } from "../element";
 import { ExcalidrawElement, FileId } from "../element/types";
 import { ExcalidrawElement, FileId } from "../element/types";
 import { CanvasError } from "../errors";
 import { CanvasError } from "../errors";
@@ -117,11 +117,9 @@ export const isImageFileHandle = (handle: FileSystemHandle | null) => {
 
 
 export const isSupportedImageFile = (
 export const isSupportedImageFile = (
   blob: Blob | null | undefined,
   blob: Blob | null | undefined,
-): blob is Blob & { type: typeof ALLOWED_IMAGE_MIME_TYPES[number] } => {
+): blob is Blob & { type: ValueOf<typeof IMAGE_MIME_TYPES> } => {
   const { type } = blob || {};
   const { type } = blob || {};
-  return (
-    !!type && (ALLOWED_IMAGE_MIME_TYPES as readonly string[]).includes(type)
-  );
+  return !!type && (Object.values(IMAGE_MIME_TYPES) as string[]).includes(type);
 };
 };
 
 
 export const loadSceneOrLibraryFromBlob = async (
 export const loadSceneOrLibraryFromBlob = async (

+ 1 - 10
src/data/filesystem.ts

@@ -8,16 +8,7 @@ import { EVENT, MIME_TYPES } from "../constants";
 import { AbortError } from "../errors";
 import { AbortError } from "../errors";
 import { debounce } from "../utils";
 import { debounce } from "../utils";
 
 
-type FILE_EXTENSION =
-  | "gif"
-  | "jpg"
-  | "png"
-  | "excalidraw.png"
-  | "svg"
-  | "excalidraw.svg"
-  | "json"
-  | "excalidraw"
-  | "excalidrawlib";
+type FILE_EXTENSION = Exclude<keyof typeof MIME_TYPES, "binary">;
 
 
 const INPUT_CHANGE_INTERVAL_MS = 500;
 const INPUT_CHANGE_INTERVAL_MS = 500;
 
 

+ 4 - 4
src/types.ts

@@ -29,9 +29,9 @@ import { isOverScrollBars } from "./scene";
 import { MaybeTransformHandleType } from "./element/transformHandles";
 import { MaybeTransformHandleType } from "./element/transformHandles";
 import Library from "./data/library";
 import Library from "./data/library";
 import type { FileSystemHandle } from "./data/filesystem";
 import type { FileSystemHandle } from "./data/filesystem";
-import type { ALLOWED_IMAGE_MIME_TYPES, MIME_TYPES } from "./constants";
+import type { IMAGE_MIME_TYPES, MIME_TYPES } from "./constants";
 import { ContextMenuItems } from "./components/ContextMenu";
 import { ContextMenuItems } from "./components/ContextMenu";
-import { Merge, ForwardRef } from "./utility-types";
+import { Merge, ForwardRef, ValueOf } from "./utility-types";
 import React from "react";
 import React from "react";
 
 
 export type Point = Readonly<RoughPoint>;
 export type Point = Readonly<RoughPoint>;
@@ -60,7 +60,7 @@ export type DataURL = string & { _brand: "DataURL" };
 
 
 export type BinaryFileData = {
 export type BinaryFileData = {
   mimeType:
   mimeType:
-    | typeof ALLOWED_IMAGE_MIME_TYPES[number]
+    | ValueOf<typeof IMAGE_MIME_TYPES>
     // future user or unknown file type
     // future user or unknown file type
     | typeof MIME_TYPES.binary;
     | typeof MIME_TYPES.binary;
   id: FileId;
   id: FileId;
@@ -419,7 +419,7 @@ export type AppClassProperties = {
     FileId,
     FileId,
     {
     {
       image: HTMLImageElement | Promise<HTMLImageElement>;
       image: HTMLImageElement | Promise<HTMLImageElement>;
-      mimeType: typeof ALLOWED_IMAGE_MIME_TYPES[number];
+      mimeType: ValueOf<typeof IMAGE_MIME_TYPES>;
     }
     }
   >;
   >;
   files: BinaryFiles;
   files: BinaryFiles;