107 lines
3.5 KiB
JavaScript
107 lines
3.5 KiB
JavaScript
import { post } from "./api.js";
|
||||
|
|
import { h, replace, showAlert } from "./dom.js";
|
|||
|
|
export const imageDrafts = new Map();
|
|||
|
|
const imageTypes = ["image/png", "image/jpeg", "image/gif", "image/webp"];
|
|||
|
|
const maxImageBytes = 5 << 20;
|
|||
|
|
export function imageTray(drop, input, key, onCount) {
|
|||
|
|
const el = h("div", { class: "compose-images" });
|
|||
|
|
const render = () => {
|
|||
|
|
const imgs = imageDrafts.get(key) ?? [];
|
|||
|
|
onCount(imgs.length);
|
|||
|
|
el.hidden = imgs.length === 0;
|
|||
|
|
replace(el, ...imgs.map((img) => {
|
|||
|
|
const remove = h("button", { type: "button", class: "remove", title: "Remove image" }, "×");
|
|||
|
|
remove.onclick = () => {
|
|||
|
|
URL.revokeObjectURL(img.url);
|
|||
|
|
const rest = (imageDrafts.get(key) ?? []).filter((i) => i !== img);
|
|||
|
|
if (rest.length > 0) {
|
|||
|
|
imageDrafts.set(key, rest);
|
|||
|
|
}
|
|||
|
|
else {
|
|||
|
|
imageDrafts.delete(key);
|
|||
|
|
}
|
|||
|
|
render();
|
|||
|
|
};
|
|||
|
|
return h("div", { class: "thumb" }, h("img", { src: img.url, alt: "attached image" }), remove);
|
|||
|
|
}));
|
|||
|
|
};
|
|||
|
|
const add = (files) => {
|
|||
|
|
const added = [];
|
|||
|
|
for (const f of files) {
|
|||
|
|
if (!imageTypes.includes(f.type)) {
|
|||
|
|
void showAlert(`${f.name || "That file"} isn't a PNG, JPEG, GIF or WebP image.`);
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
if (f.size > maxImageBytes) {
|
|||
|
|
void showAlert(`${f.name || "That image"} is over ${maxImageBytes >> 20} MiB.`);
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
added.push({ file: f, url: URL.createObjectURL(f) });
|
|||
|
|
}
|
|||
|
|
if (added.length === 0) {
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
imageDrafts.set(key, [...(imageDrafts.get(key) ?? []), ...added]);
|
|||
|
|
render();
|
|||
|
|
return true;
|
|||
|
|
};
|
|||
|
|
input.addEventListener("paste", (e) => {
|
|||
|
|
const files = [...(e.clipboardData?.files ?? [])].filter((f) => f.type.startsWith("image/"));
|
|||
|
|
if (files.length > 0) {
|
|||
|
|
e.preventDefault();
|
|||
|
|
add(files);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
drop.addEventListener("dragover", (e) => {
|
|||
|
|
if (e.dataTransfer?.types.includes("Files")) {
|
|||
|
|
e.preventDefault();
|
|||
|
|
drop.classList.add("dropping");
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
drop.addEventListener("dragleave", (e) => {
|
|||
|
|
if (!drop.contains(e.relatedTarget)) {
|
|||
|
|
drop.classList.remove("dropping");
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
drop.addEventListener("drop", (e) => {
|
|||
|
|
drop.classList.remove("dropping");
|
|||
|
|
const files = [...(e.dataTransfer?.files ?? [])];
|
|||
|
|
if (files.length > 0) {
|
|||
|
|
e.preventDefault();
|
|||
|
|
add(files);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
render();
|
|||
|
|
return {
|
|||
|
|
el,
|
|||
|
|
take: () => {
|
|||
|
|
const imgs = imageDrafts.get(key) ?? [];
|
|||
|
|
imageDrafts.delete(key);
|
|||
|
|
render();
|
|||
|
|
return imgs;
|
|||
|
|
},
|
|||
|
|
restore: (imgs) => {
|
|||
|
|
if (imgs.length > 0) {
|
|||
|
|
imageDrafts.set(key, [...imgs, ...(imageDrafts.get(key) ?? [])]);
|
|||
|
|
}
|
|||
|
|
render();
|
|||
|
|
},
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
export async function uploadImages(imgs) {
|
|||
|
|
if (imgs.length === 0) {
|
|||
|
|
return undefined;
|
|||
|
|
}
|
|||
|
|
const ids = [];
|
|||
|
|
for (const img of imgs) {
|
|||
|
|
const blob = await post("/api/blobs", { body: img.file });
|
|||
|
|
ids.push(blob.id);
|
|||
|
|
}
|
|||
|
|
return ids;
|
|||
|
|
}
|
|||
|
|
export function releaseImages(imgs) {
|
|||
|
|
for (const img of imgs) {
|
|||
|
|
URL.revokeObjectURL(img.url);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
//# sourceMappingURL=images.js.map
|