Move the attachment logic into a separate file
authorAlexander Ebert <ebert@woltlab.com>
Sun, 5 May 2024 10:19:16 +0000 (12:19 +0200)
committerAlexander Ebert <ebert@woltlab.com>
Sat, 8 Jun 2024 10:19:39 +0000 (12:19 +0200)
ts/WoltLabSuite/Core/Component/Attachment/Entry.ts
ts/WoltLabSuite/Core/Component/Attachment/List.ts
wcfsetup/install/files/js/WoltLabSuite/Core/Component/Attachment/Entry.js
wcfsetup/install/files/js/WoltLabSuite/Core/Component/Attachment/List.js

index 940dbcd5d8587b0e687c0e75ec0a0ea810493471..e4723e1984132a7a680bdecc48b9ee8a2cd4b564 100644 (file)
-export class AttachmentEntry {
-  #attachmentId: number;
-  readonly #name: string;
+import { formatFilesize } from "WoltLabSuite/Core/FileUtil";
+import type WoltlabCoreFileElement from "../File/woltlab-core-file";
+import { initFragment, toggleDropdown } from "WoltLabSuite/Core/Ui/Dropdown/Simple";
+import DomChangeListener from "WoltLabSuite/Core/Dom/Change/Listener";
+import { dispatchToCkeditor } from "../Ckeditor/Event";
+import { deleteFile } from "WoltLabSuite/Core/Api/Files/DeleteFile";
 
-  constructor(attachmentId: number, name: string) {
-    this.#attachmentId = attachmentId;
-    this.#name = name;
+type FileProcessorData = {
+  attachmentID: number;
+};
+
+function fileInitializationCompleted(element: HTMLElement, file: WoltlabCoreFileElement, editorId: string): void {
+  const data = file.data;
+  if (data === undefined) {
+    // TODO: error handling
+    return;
+  }
+
+  const fileId = file.fileId;
+  if (fileId === undefined) {
+    // TODO: error handling
+    return;
+  }
+
+  const extraButtons: HTMLButtonElement[] = [];
+
+  let insertButton: HTMLButtonElement;
+  if (file.isImage()) {
+    const thumbnail = file.thumbnails.find((thumbnail) => thumbnail.identifier === "tiny");
+    if (thumbnail !== undefined) {
+      file.thumbnail = thumbnail;
+    }
+
+    const url = file.thumbnails.find((thumbnail) => thumbnail.identifier === "")?.link;
+    if (url !== undefined) {
+      insertButton = getInsertThumbnailButton((data as FileProcessorData).attachmentID, url, editorId);
+
+      extraButtons.push(
+        getInsertAttachBbcodeButton((data as FileProcessorData).attachmentID, file.link ? file.link : "", editorId),
+      );
+    } else {
+      insertButton = getInsertAttachBbcodeButton(
+        (data as FileProcessorData).attachmentID,
+        file.link ? file.link : "",
+        editorId,
+      );
+    }
+
+    if (file.link !== undefined && file.filename !== undefined) {
+      const link = document.createElement("a");
+      link.href = file.link!;
+      link.classList.add("jsImageViewer");
+      link.title = file.filename;
+      link.textContent = file.filename;
+
+      const filename = element.querySelector(".attachment__item__filename")!;
+      filename.innerHTML = "";
+      filename.append(link);
+
+      DomChangeListener.trigger();
+    }
+  } else {
+    insertButton = getInsertAttachBbcodeButton(
+      (data as FileProcessorData).attachmentID,
+      file.isImage() && file.link ? file.link : "",
+      editorId,
+    );
+  }
+
+  const dropdownMenu = document.createElement("ul");
+  dropdownMenu.classList.add("dropdownMenu");
+  for (const button of extraButtons) {
+    const listItem = document.createElement("li");
+    listItem.append(button);
+    dropdownMenu.append(listItem);
   }
+
+  if (dropdownMenu.childElementCount !== 0) {
+    const listItem = document.createElement("li");
+    listItem.classList.add("dropdownDivider");
+    dropdownMenu.append(listItem);
+  }
+
+  const listItem = document.createElement("li");
+  listItem.append(getDeleteAttachButton(fileId, (data as FileProcessorData).attachmentID, editorId, element));
+  dropdownMenu.append(listItem);
+
+  const moreOptions = document.createElement("button");
+  moreOptions.classList.add("button", "small", "jsTooltip");
+  moreOptions.type = "button";
+  moreOptions.title = "TODO: more options";
+  moreOptions.innerHTML = '<fa-icon name="ellipsis-vertical"></fa-icon>';
+
+  const buttonList = document.createElement("div");
+  buttonList.classList.add("attachment__item__buttons");
+  insertButton.classList.add("button", "small");
+  buttonList.append(insertButton, moreOptions);
+
+  element.append(buttonList);
+
+  initFragment(moreOptions, dropdownMenu);
+  moreOptions.addEventListener("click", (event) => {
+    event.stopPropagation();
+
+    toggleDropdown(moreOptions.id);
+  });
 }
 
-export default AttachmentEntry;
+function getDeleteAttachButton(
+  fileId: number,
+  attachmentId: number,
+  editorId: string,
+  element: HTMLElement,
+): HTMLButtonElement {
+  const button = document.createElement("button");
+  button.type = "button";
+  button.textContent = "TODO: delete";
+
+  button.addEventListener("click", () => {
+    const editor = document.getElementById(editorId);
+    if (editor === null) {
+      // TODO: error handling
+      return;
+    }
+
+    void deleteFile(fileId).then((result) => {
+      result.unwrap();
+
+      dispatchToCkeditor(editor).removeAttachment({
+        attachmentId,
+      });
+
+      element.remove();
+    });
+  });
+
+  return button;
+}
+
+function getInsertAttachBbcodeButton(attachmentId: number, url: string, editorId: string): HTMLButtonElement {
+  const button = document.createElement("button");
+  button.type = "button";
+  button.textContent = "TODO: insert";
+
+  button.addEventListener("click", () => {
+    const editor = document.getElementById(editorId);
+    if (editor === null) {
+      // TODO: error handling
+      return;
+    }
+
+    dispatchToCkeditor(editor).insertAttachment({
+      attachmentId,
+      url,
+    });
+  });
+
+  return button;
+}
+
+function getInsertThumbnailButton(attachmentId: number, url: string, editorId: string): HTMLButtonElement {
+  const button = document.createElement("button");
+  button.type = "button";
+  button.textContent = "TODO: insert thumbnail";
+
+  button.addEventListener("click", () => {
+    const editor = document.getElementById(editorId);
+    if (editor === null) {
+      // TODO: error handling
+      return;
+    }
+
+    dispatchToCkeditor(editor).insertAttachment({
+      attachmentId,
+      url,
+    });
+  });
+
+  return button;
+}
+
+function fileInitializationFailed(element: HTMLElement, file: WoltlabCoreFileElement, reason: unknown): void {
+  if (reason instanceof Error) {
+    throw reason;
+  }
+
+  if (file.validationError === undefined) {
+    return;
+  }
+
+  // TODO: Add a proper error message, this is for development purposes only.
+  element.append(JSON.stringify(file.validationError));
+  element.classList.add("attachment__item--error");
+}
+
+export function createAttachmentFromFile(file: WoltlabCoreFileElement, editorId: string) {
+  const element = document.createElement("li");
+  element.classList.add("attachment__item");
+
+  const fileWrapper = document.createElement("div");
+  fileWrapper.classList.add("attachment__item__file");
+  fileWrapper.append(file);
+
+  const filename = document.createElement("div");
+  filename.classList.add("attachment__item__filename");
+  filename.textContent = file.filename || file.dataset.filename!;
+
+  const fileSize = document.createElement("div");
+  fileSize.classList.add("attachment__item__fileSize");
+  fileSize.textContent = formatFilesize(file.fileSize || parseInt(file.dataset.fileSize!));
+
+  element.append(fileWrapper, filename, fileSize);
+
+  void file.ready
+    .then(() => {
+      fileInitializationCompleted(element, file, editorId);
+    })
+    .catch((reason) => {
+      fileInitializationFailed(element, file, reason);
+    });
+
+  return element;
+}
index 298282a41ac14a80cf80b653f9b597bfc74e91f5..f5aab72e5edb8187964226736c52cac69101f026 100644 (file)
-import { deleteFile } from "WoltLabSuite/Core/Api/Files/DeleteFile";
-import { dispatchToCkeditor, listenToCkeditor } from "../Ckeditor/Event";
 import WoltlabCoreFileElement from "../File/woltlab-core-file";
-
-import "../File/woltlab-core-file";
 import { CkeditorDropEvent } from "../File/Upload";
-import { formatFilesize } from "WoltLabSuite/Core/FileUtil";
-import DomChangeListener from "WoltLabSuite/Core/Dom/Change/Listener";
-import { initFragment, toggleDropdown } from "WoltLabSuite/Core/Ui/Dropdown/Simple";
-
-type FileProcessorData = {
-  attachmentID: number;
-};
-
-function upload(fileList: HTMLElement, file: WoltlabCoreFileElement, editorId: string): void {
-  const element = document.createElement("li");
-  element.classList.add("attachment__item");
-
-  const fileWrapper = document.createElement("div");
-  fileWrapper.classList.add("attachment__item__file");
-  fileWrapper.append(file);
-
-  const filename = document.createElement("div");
-  filename.classList.add("attachment__item__filename");
-  filename.textContent = file.filename || file.dataset.filename!;
-
-  const fileSize = document.createElement("div");
-  fileSize.classList.add("attachment__item__fileSize");
-  fileSize.textContent = formatFilesize(file.fileSize || parseInt(file.dataset.fileSize!));
-
-  element.append(fileWrapper, filename, fileSize);
-  fileList.append(element);
-
-  void file.ready
-    .then(() => {
-      const data = file.data;
-      if (data === undefined) {
-        // TODO: error handling
-        return;
-      }
-
-      const fileId = file.fileId;
-      if (fileId === undefined) {
-        // TODO: error handling
-        return;
-      }
-
-      const extraButtons: HTMLButtonElement[] = [];
-
-      let insertButton: HTMLButtonElement;
-      if (file.isImage()) {
-        const thumbnail = file.thumbnails.find((thumbnail) => thumbnail.identifier === "tiny");
-        if (thumbnail !== undefined) {
-          file.thumbnail = thumbnail;
-        }
-
-        const url = file.thumbnails.find((thumbnail) => thumbnail.identifier === "")?.link;
-        if (url !== undefined) {
-          insertButton = getInsertThumbnailButton((data as FileProcessorData).attachmentID, url, editorId);
-
-          extraButtons.push(
-            getInsertAttachBbcodeButton((data as FileProcessorData).attachmentID, file.link ? file.link : "", editorId),
-          );
-        } else {
-          insertButton = getInsertAttachBbcodeButton(
-            (data as FileProcessorData).attachmentID,
-            file.link ? file.link : "",
-            editorId,
-          );
-        }
-
-        if (file.link !== undefined && file.filename !== undefined) {
-          const link = document.createElement("a");
-          link.href = file.link!;
-          link.classList.add("jsImageViewer");
-          link.title = file.filename;
-          link.textContent = file.filename;
-
-          filename.innerHTML = "";
-          filename.append(link);
-
-          DomChangeListener.trigger();
-        }
-      } else {
-        insertButton = getInsertAttachBbcodeButton(
-          (data as FileProcessorData).attachmentID,
-          file.isImage() && file.link ? file.link : "",
-          editorId,
-        );
-      }
-
-      const dropdownMenu = document.createElement("ul");
-      dropdownMenu.classList.add("dropdownMenu");
-      for (const button of extraButtons) {
-        const listItem = document.createElement("li");
-        listItem.append(button);
-        dropdownMenu.append(listItem);
-      }
-
-      if (dropdownMenu.childElementCount !== 0) {
-        const listItem = document.createElement("li");
-        listItem.classList.add("dropdownDivider");
-        dropdownMenu.append(listItem);
-      }
-
-      const listItem = document.createElement("li");
-      listItem.append(getDeleteAttachButton(fileId, (data as FileProcessorData).attachmentID, editorId, element));
-      dropdownMenu.append(listItem);
-
-      const moreOptions = document.createElement("button");
-      moreOptions.classList.add("button", "small", "jsTooltip");
-      moreOptions.type = "button";
-      moreOptions.title = "TODO: more options";
-      moreOptions.innerHTML = '<fa-icon name="ellipsis-vertical"></fa-icon>';
-
-      const buttonList = document.createElement("div");
-      buttonList.classList.add("attachment__item__buttons");
-      insertButton.classList.add("button", "small");
-      buttonList.append(insertButton, moreOptions);
-
-      element.append(buttonList);
-
-      initFragment(moreOptions, dropdownMenu);
-      moreOptions.addEventListener("click", (event) => {
-        event.stopPropagation();
-
-        toggleDropdown(moreOptions.id);
-      });
-    })
-    .catch((e) => {
-      if (e instanceof Error) {
-        throw e;
-      }
+import { createAttachmentFromFile } from "./Entry";
+import { listenToCkeditor } from "../Ckeditor/Event";
 
-      if (file.validationError === undefined) {
-        return;
-      }
-
-      // TODO: Add a proper error message, this is for development purposes only.
-      element.append(JSON.stringify(file.validationError));
-      element.classList.add("attachment__item--error");
-    });
-}
-
-function getDeleteAttachButton(
-  fileId: number,
-  attachmentId: number,
-  editorId: string,
-  element: HTMLElement,
-): HTMLButtonElement {
-  const button = document.createElement("button");
-  button.type = "button";
-  button.textContent = "TODO: delete";
-
-  button.addEventListener("click", () => {
-    const editor = document.getElementById(editorId);
-    if (editor === null) {
-      // TODO: error handling
-      return;
-    }
-
-    void deleteFile(fileId).then((result) => {
-      result.unwrap();
-
-      dispatchToCkeditor(editor).removeAttachment({
-        attachmentId,
-      });
-
-      element.remove();
-    });
-  });
-
-  return button;
-}
-
-function getInsertAttachBbcodeButton(attachmentId: number, url: string, editorId: string): HTMLButtonElement {
-  const button = document.createElement("button");
-  button.type = "button";
-  button.textContent = "TODO: insert";
-
-  button.addEventListener("click", () => {
-    const editor = document.getElementById(editorId);
-    if (editor === null) {
-      // TODO: error handling
-      return;
-    }
-
-    dispatchToCkeditor(editor).insertAttachment({
-      attachmentId,
-      url,
-    });
-  });
-
-  return button;
-}
-
-function getInsertThumbnailButton(attachmentId: number, url: string, editorId: string): HTMLButtonElement {
-  const button = document.createElement("button");
-  button.type = "button";
-  button.textContent = "TODO: insert thumbnail";
-
-  button.addEventListener("click", () => {
-    const editor = document.getElementById(editorId);
-    if (editor === null) {
-      // TODO: error handling
-      return;
-    }
-
-    dispatchToCkeditor(editor).insertAttachment({
-      attachmentId,
-      url,
-    });
-  });
+// This import has the side effect of registering the `<woltlab-core-file>`
+// element. Do not remove!
+import "../File/woltlab-core-file";
 
-  return button;
+function fileToAttachment(fileList: HTMLElement, file: WoltlabCoreFileElement, editorId: string): void {
+  fileList.append(createAttachmentFromFile(file, editorId));
 }
 
 export function setup(editorId: string): void {
@@ -245,7 +41,7 @@ export function setup(editorId: string): void {
   }
 
   uploadButton.addEventListener("uploadStart", (event: CustomEvent<WoltlabCoreFileElement>) => {
-    upload(fileList!, event.detail, editorId);
+    fileToAttachment(fileList!, event.detail, editorId);
   });
 
   listenToCkeditor(editor).uploadAttachment((payload) => {
@@ -258,7 +54,7 @@ export function setup(editorId: string): void {
   const existingFiles = container.querySelector<HTMLElement>(".attachment__list__existingFiles");
   if (existingFiles !== null) {
     existingFiles.querySelectorAll("woltlab-core-file").forEach((file) => {
-      upload(fileList!, file, editorId);
+      fileToAttachment(fileList!, file, editorId);
     });
 
     existingFiles.remove();
index 882e3edb835d7683b30059908ca23793509c92f9..e0c09b3f47abcb6434fa81c2b0b731a545140590 100644 (file)
-define(["require", "exports"], function (require, exports) {
+define(["require", "exports", "tslib", "WoltLabSuite/Core/FileUtil", "WoltLabSuite/Core/Ui/Dropdown/Simple", "WoltLabSuite/Core/Dom/Change/Listener", "../Ckeditor/Event", "WoltLabSuite/Core/Api/Files/DeleteFile"], function (require, exports, tslib_1, FileUtil_1, Simple_1, Listener_1, Event_1, DeleteFile_1) {
     "use strict";
     Object.defineProperty(exports, "__esModule", { value: true });
-    exports.AttachmentEntry = void 0;
-    class AttachmentEntry {
-        #attachmentId;
-        #name;
-        constructor(attachmentId, name) {
-            this.#attachmentId = attachmentId;
-            this.#name = name;
+    exports.createAttachmentFromFile = void 0;
+    Listener_1 = tslib_1.__importDefault(Listener_1);
+    function fileInitializationCompleted(element, file, editorId) {
+        const data = file.data;
+        if (data === undefined) {
+            // TODO: error handling
+            return;
         }
+        const fileId = file.fileId;
+        if (fileId === undefined) {
+            // TODO: error handling
+            return;
+        }
+        const extraButtons = [];
+        let insertButton;
+        if (file.isImage()) {
+            const thumbnail = file.thumbnails.find((thumbnail) => thumbnail.identifier === "tiny");
+            if (thumbnail !== undefined) {
+                file.thumbnail = thumbnail;
+            }
+            const url = file.thumbnails.find((thumbnail) => thumbnail.identifier === "")?.link;
+            if (url !== undefined) {
+                insertButton = getInsertThumbnailButton(data.attachmentID, url, editorId);
+                extraButtons.push(getInsertAttachBbcodeButton(data.attachmentID, file.link ? file.link : "", editorId));
+            }
+            else {
+                insertButton = getInsertAttachBbcodeButton(data.attachmentID, file.link ? file.link : "", editorId);
+            }
+            if (file.link !== undefined && file.filename !== undefined) {
+                const link = document.createElement("a");
+                link.href = file.link;
+                link.classList.add("jsImageViewer");
+                link.title = file.filename;
+                link.textContent = file.filename;
+                const filename = element.querySelector(".attachment__item__filename");
+                filename.innerHTML = "";
+                filename.append(link);
+                Listener_1.default.trigger();
+            }
+        }
+        else {
+            insertButton = getInsertAttachBbcodeButton(data.attachmentID, file.isImage() && file.link ? file.link : "", editorId);
+        }
+        const dropdownMenu = document.createElement("ul");
+        dropdownMenu.classList.add("dropdownMenu");
+        for (const button of extraButtons) {
+            const listItem = document.createElement("li");
+            listItem.append(button);
+            dropdownMenu.append(listItem);
+        }
+        if (dropdownMenu.childElementCount !== 0) {
+            const listItem = document.createElement("li");
+            listItem.classList.add("dropdownDivider");
+            dropdownMenu.append(listItem);
+        }
+        const listItem = document.createElement("li");
+        listItem.append(getDeleteAttachButton(fileId, data.attachmentID, editorId, element));
+        dropdownMenu.append(listItem);
+        const moreOptions = document.createElement("button");
+        moreOptions.classList.add("button", "small", "jsTooltip");
+        moreOptions.type = "button";
+        moreOptions.title = "TODO: more options";
+        moreOptions.innerHTML = '<fa-icon name="ellipsis-vertical"></fa-icon>';
+        const buttonList = document.createElement("div");
+        buttonList.classList.add("attachment__item__buttons");
+        insertButton.classList.add("button", "small");
+        buttonList.append(insertButton, moreOptions);
+        element.append(buttonList);
+        (0, Simple_1.initFragment)(moreOptions, dropdownMenu);
+        moreOptions.addEventListener("click", (event) => {
+            event.stopPropagation();
+            (0, Simple_1.toggleDropdown)(moreOptions.id);
+        });
+    }
+    function getDeleteAttachButton(fileId, attachmentId, editorId, element) {
+        const button = document.createElement("button");
+        button.type = "button";
+        button.textContent = "TODO: delete";
+        button.addEventListener("click", () => {
+            const editor = document.getElementById(editorId);
+            if (editor === null) {
+                // TODO: error handling
+                return;
+            }
+            void (0, DeleteFile_1.deleteFile)(fileId).then((result) => {
+                result.unwrap();
+                (0, Event_1.dispatchToCkeditor)(editor).removeAttachment({
+                    attachmentId,
+                });
+                element.remove();
+            });
+        });
+        return button;
+    }
+    function getInsertAttachBbcodeButton(attachmentId, url, editorId) {
+        const button = document.createElement("button");
+        button.type = "button";
+        button.textContent = "TODO: insert";
+        button.addEventListener("click", () => {
+            const editor = document.getElementById(editorId);
+            if (editor === null) {
+                // TODO: error handling
+                return;
+            }
+            (0, Event_1.dispatchToCkeditor)(editor).insertAttachment({
+                attachmentId,
+                url,
+            });
+        });
+        return button;
+    }
+    function getInsertThumbnailButton(attachmentId, url, editorId) {
+        const button = document.createElement("button");
+        button.type = "button";
+        button.textContent = "TODO: insert thumbnail";
+        button.addEventListener("click", () => {
+            const editor = document.getElementById(editorId);
+            if (editor === null) {
+                // TODO: error handling
+                return;
+            }
+            (0, Event_1.dispatchToCkeditor)(editor).insertAttachment({
+                attachmentId,
+                url,
+            });
+        });
+        return button;
+    }
+    function fileInitializationFailed(element, file, reason) {
+        if (reason instanceof Error) {
+            throw reason;
+        }
+        if (file.validationError === undefined) {
+            return;
+        }
+        // TODO: Add a proper error message, this is for development purposes only.
+        element.append(JSON.stringify(file.validationError));
+        element.classList.add("attachment__item--error");
+    }
+    function createAttachmentFromFile(file, editorId) {
+        const element = document.createElement("li");
+        element.classList.add("attachment__item");
+        const fileWrapper = document.createElement("div");
+        fileWrapper.classList.add("attachment__item__file");
+        fileWrapper.append(file);
+        const filename = document.createElement("div");
+        filename.classList.add("attachment__item__filename");
+        filename.textContent = file.filename || file.dataset.filename;
+        const fileSize = document.createElement("div");
+        fileSize.classList.add("attachment__item__fileSize");
+        fileSize.textContent = (0, FileUtil_1.formatFilesize)(file.fileSize || parseInt(file.dataset.fileSize));
+        element.append(fileWrapper, filename, fileSize);
+        void file.ready
+            .then(() => {
+            fileInitializationCompleted(element, file, editorId);
+        })
+            .catch((reason) => {
+            fileInitializationFailed(element, file, reason);
+        });
+        return element;
     }
-    exports.AttachmentEntry = AttachmentEntry;
-    exports.default = AttachmentEntry;
+    exports.createAttachmentFromFile = createAttachmentFromFile;
 });
index ef710df98291ffac868b97c135fd7804b78f2223..41acc3ca3faa05bd78b29e7c36b4358c606c1d24 100644 (file)
@@ -1,159 +1,9 @@
-define(["require", "exports", "tslib", "WoltLabSuite/Core/Api/Files/DeleteFile", "../Ckeditor/Event", "WoltLabSuite/Core/FileUtil", "WoltLabSuite/Core/Dom/Change/Listener", "WoltLabSuite/Core/Ui/Dropdown/Simple", "../File/woltlab-core-file"], function (require, exports, tslib_1, DeleteFile_1, Event_1, FileUtil_1, Listener_1, Simple_1) {
+define(["require", "exports", "./Entry", "../Ckeditor/Event", "../File/woltlab-core-file"], function (require, exports, Entry_1, Event_1) {
     "use strict";
     Object.defineProperty(exports, "__esModule", { value: true });
     exports.setup = void 0;
-    Listener_1 = tslib_1.__importDefault(Listener_1);
-    function upload(fileList, file, editorId) {
-        const element = document.createElement("li");
-        element.classList.add("attachment__item");
-        const fileWrapper = document.createElement("div");
-        fileWrapper.classList.add("attachment__item__file");
-        fileWrapper.append(file);
-        const filename = document.createElement("div");
-        filename.classList.add("attachment__item__filename");
-        filename.textContent = file.filename || file.dataset.filename;
-        const fileSize = document.createElement("div");
-        fileSize.classList.add("attachment__item__fileSize");
-        fileSize.textContent = (0, FileUtil_1.formatFilesize)(file.fileSize || parseInt(file.dataset.fileSize));
-        element.append(fileWrapper, filename, fileSize);
-        fileList.append(element);
-        void file.ready
-            .then(() => {
-            const data = file.data;
-            if (data === undefined) {
-                // TODO: error handling
-                return;
-            }
-            const fileId = file.fileId;
-            if (fileId === undefined) {
-                // TODO: error handling
-                return;
-            }
-            const extraButtons = [];
-            let insertButton;
-            if (file.isImage()) {
-                const thumbnail = file.thumbnails.find((thumbnail) => thumbnail.identifier === "tiny");
-                if (thumbnail !== undefined) {
-                    file.thumbnail = thumbnail;
-                }
-                const url = file.thumbnails.find((thumbnail) => thumbnail.identifier === "")?.link;
-                if (url !== undefined) {
-                    insertButton = getInsertThumbnailButton(data.attachmentID, url, editorId);
-                    extraButtons.push(getInsertAttachBbcodeButton(data.attachmentID, file.link ? file.link : "", editorId));
-                }
-                else {
-                    insertButton = getInsertAttachBbcodeButton(data.attachmentID, file.link ? file.link : "", editorId);
-                }
-                if (file.link !== undefined && file.filename !== undefined) {
-                    const link = document.createElement("a");
-                    link.href = file.link;
-                    link.classList.add("jsImageViewer");
-                    link.title = file.filename;
-                    link.textContent = file.filename;
-                    filename.innerHTML = "";
-                    filename.append(link);
-                    Listener_1.default.trigger();
-                }
-            }
-            else {
-                insertButton = getInsertAttachBbcodeButton(data.attachmentID, file.isImage() && file.link ? file.link : "", editorId);
-            }
-            const dropdownMenu = document.createElement("ul");
-            dropdownMenu.classList.add("dropdownMenu");
-            for (const button of extraButtons) {
-                const listItem = document.createElement("li");
-                listItem.append(button);
-                dropdownMenu.append(listItem);
-            }
-            if (dropdownMenu.childElementCount !== 0) {
-                const listItem = document.createElement("li");
-                listItem.classList.add("dropdownDivider");
-                dropdownMenu.append(listItem);
-            }
-            const listItem = document.createElement("li");
-            listItem.append(getDeleteAttachButton(fileId, data.attachmentID, editorId, element));
-            dropdownMenu.append(listItem);
-            const moreOptions = document.createElement("button");
-            moreOptions.classList.add("button", "small", "jsTooltip");
-            moreOptions.type = "button";
-            moreOptions.title = "TODO: more options";
-            moreOptions.innerHTML = '<fa-icon name="ellipsis-vertical"></fa-icon>';
-            const buttonList = document.createElement("div");
-            buttonList.classList.add("attachment__item__buttons");
-            insertButton.classList.add("button", "small");
-            buttonList.append(insertButton, moreOptions);
-            element.append(buttonList);
-            (0, Simple_1.initFragment)(moreOptions, dropdownMenu);
-            moreOptions.addEventListener("click", (event) => {
-                event.stopPropagation();
-                (0, Simple_1.toggleDropdown)(moreOptions.id);
-            });
-        })
-            .catch((e) => {
-            if (e instanceof Error) {
-                throw e;
-            }
-            if (file.validationError === undefined) {
-                return;
-            }
-            // TODO: Add a proper error message, this is for development purposes only.
-            element.append(JSON.stringify(file.validationError));
-            element.classList.add("attachment__item--error");
-        });
-    }
-    function getDeleteAttachButton(fileId, attachmentId, editorId, element) {
-        const button = document.createElement("button");
-        button.type = "button";
-        button.textContent = "TODO: delete";
-        button.addEventListener("click", () => {
-            const editor = document.getElementById(editorId);
-            if (editor === null) {
-                // TODO: error handling
-                return;
-            }
-            void (0, DeleteFile_1.deleteFile)(fileId).then((result) => {
-                result.unwrap();
-                (0, Event_1.dispatchToCkeditor)(editor).removeAttachment({
-                    attachmentId,
-                });
-                element.remove();
-            });
-        });
-        return button;
-    }
-    function getInsertAttachBbcodeButton(attachmentId, url, editorId) {
-        const button = document.createElement("button");
-        button.type = "button";
-        button.textContent = "TODO: insert";
-        button.addEventListener("click", () => {
-            const editor = document.getElementById(editorId);
-            if (editor === null) {
-                // TODO: error handling
-                return;
-            }
-            (0, Event_1.dispatchToCkeditor)(editor).insertAttachment({
-                attachmentId,
-                url,
-            });
-        });
-        return button;
-    }
-    function getInsertThumbnailButton(attachmentId, url, editorId) {
-        const button = document.createElement("button");
-        button.type = "button";
-        button.textContent = "TODO: insert thumbnail";
-        button.addEventListener("click", () => {
-            const editor = document.getElementById(editorId);
-            if (editor === null) {
-                // TODO: error handling
-                return;
-            }
-            (0, Event_1.dispatchToCkeditor)(editor).insertAttachment({
-                attachmentId,
-                url,
-            });
-        });
-        return button;
+    function fileToAttachment(fileList, file, editorId) {
+        fileList.append((0, Entry_1.createAttachmentFromFile)(file, editorId));
     }
     function setup(editorId) {
         const container = document.getElementById(`attachments_${editorId}`);
@@ -181,7 +31,7 @@ define(["require", "exports", "tslib", "WoltLabSuite/Core/Api/Files/DeleteFile",
             uploadButton.insertAdjacentElement("afterend", fileList);
         }
         uploadButton.addEventListener("uploadStart", (event) => {
-            upload(fileList, event.detail, editorId);
+            fileToAttachment(fileList, event.detail, editorId);
         });
         (0, Event_1.listenToCkeditor)(editor).uploadAttachment((payload) => {
             const event = new CustomEvent("ckeditorDrop", {
@@ -192,7 +42,7 @@ define(["require", "exports", "tslib", "WoltLabSuite/Core/Api/Files/DeleteFile",
         const existingFiles = container.querySelector(".attachment__list__existingFiles");
         if (existingFiles !== null) {
             existingFiles.querySelectorAll("woltlab-core-file").forEach((file) => {
-                upload(fileList, file, editorId);
+                fileToAttachment(fileList, file, editorId);
             });
             existingFiles.remove();
         }