1import type { FilePreviewItem, FilePreviewSource } from "../components/FilePreviewModal.types";
2
3export function filePreviewKey(source: FilePreviewItem): string {
4 if ("resource" in source)
5 return JSON.stringify([
6 source.environmentId,
7 Object.entries(source.resource)
8 .filter(([key]) => key !== "mimeType" && key !== "fileName")
9 .sort(([a], [b]) => a.localeCompare(b)),
10 ]);
11 if ("attachment" in source) return `attachment:${source.attachment.id}`;
12 return source.uri;
13}
14
15/** Preserve document order and the tapped image's transition source. */
16export function filePreviewGallery(source: FilePreviewSource, group: readonly FilePreviewItem[]) {
17 if (source.kind !== "image") return [source];
18 const selectedKey = filePreviewKey(source);
19 const seen = new Set<string>();
20 const items: FilePreviewItem[] = [];
21 for (const item of source.gallery ?? group) {
22 if (item.kind !== "image") continue;
23 const key = filePreviewKey(item);
24 if (seen.has(key)) continue;
25 seen.add(key);
26 items.push(key === selectedKey ? source : item);
27 }
28 if (!seen.has(selectedKey)) items.unshift(source);
29 return items;
30}
31
32/** Markdown captions are display text, never filenames for native file type detection. */
33export function filePreviewName(source: FilePreviewItem): string {
34 const uriName = "uri" in source ? source.uri.split(/[?#]/)[0]?.split("/").pop() : undefined;
35 const name =
36 source.name ?? ("attachment" in source ? source.attachment.name : uriName) ?? "Preview";
37 const extension = /\.(png|jpe?g|gif|webp|heic|heif|avif|svg|pdf|docx?|xlsx?|pptx?|txt|rtf)$/i;
38 const filename =
39 source.kind !== "image" || extension.test(name)
40 ? name
41 : uriName && extension.test(uriName)
42 ? uriName
43 : name.replace(/\./g, "_");
44 return Array.from(filename, (character) =>
45 character.charCodeAt(0) < 32 || "/:\\".includes(character) ? "_" : character,
46 ).join("");
47}