kaneo (minimalist kanban) fork to experiment adding a tangled integration
github.com/usekaneo/kaneo
1import createImageUpload, {
2 finalizeImageUpload,
3} from "@/fetchers/task/create-image-upload";
4
5const allowedImageMimeTypes = new Set([
6 "image/apng",
7 "image/avif",
8 "image/gif",
9 "image/heic",
10 "image/heif",
11 "image/jpeg",
12 "image/jpg",
13 "image/png",
14 "image/svg+xml",
15 "image/webp",
16]);
17
18type UploadSurface = "description" | "comment";
19
20export function isSupportedImageFile(file: File) {
21 return allowedImageMimeTypes.has(file.type.toLowerCase());
22}
23
24export function isSupportedTaskAsset(file: File) {
25 return file.size > 0;
26}
27
28export function getImageAltText(filename: string) {
29 return filename
30 .replace(/\.[^/.]+$/, "")
31 .replace(/[-_]+/g, " ")
32 .trim();
33}
34
35export async function uploadTaskImage({
36 taskId,
37 surface,
38 file,
39}: {
40 taskId: string;
41 surface: UploadSurface;
42 file: File;
43}) {
44 if (!isSupportedImageFile(file)) {
45 if (!isSupportedTaskAsset(file)) {
46 throw new Error("Only non-empty file uploads are supported.");
47 }
48 }
49
50 const upload = await createImageUpload({
51 taskId,
52 filename: file.name || "image",
53 contentType: file.type,
54 size: file.size,
55 surface,
56 });
57
58 const response = await fetch(upload.uploadUrl, {
59 method: "PUT",
60 headers: upload.headers,
61 body: file,
62 });
63
64 if (!response.ok) {
65 throw new Error("Failed to upload file to storage.");
66 }
67
68 const asset = await finalizeImageUpload({
69 taskId,
70 key: upload.key,
71 filename: file.name || "image",
72 contentType: file.type,
73 size: file.size,
74 surface,
75 });
76
77 return {
78 url: asset.url,
79 alt: getImageAltText(file.name || "image"),
80 filename: file.name || "file",
81 kind: isSupportedImageFile(file) ? "image" : "attachment",
82 mimeType: file.type,
83 size: file.size,
84 };
85}