// The media bin as a choice, for fields that need one picture or several: a window listing what is in it. import { composer as c } from "../../composer/composer.js"; import { bin, learn, names } from "../../shell/bin.js"; let asked = false; export async function look(api) { const list = (await api.media()).media || []; learn(list); return list; } /** The names behind some ids; looks the bin up once if any are not known yet, then says so with `again`. */ export function namesOf(ids, api, again) { if (!asked && ids.some((id) => !names.has(id))) { asked = true; look(api).then(again, () => { asked = false; }); } return ids.map((id) => names.get(id) || (bin.version ? "(deleted)" : "…")); // a listing has been seen and this id was not in it } /** -> the chosen ids in the order picked, or null when the window was closed. `multiple` false gives at most one. */ export async function pick(api, { title, kinds, multiple, current = [] }) { asked = false; const found = (await look(api)).filter((m) => kinds.includes(m.kind)); if (!found.length) { c.toast.warn({ text: `Nothing to pick: the media bin has no ${kinds.join(" or ")} yet.` }); return null; } const items = found.map((m) => ({ value: m.id, label: `${m.name} (${m.kind})` })); return new Promise((resolve) => { let chosen = multiple ? current.filter((id) => names.has(id)) : []; const body = multiple ? c.checklist.default({ label: title, items, values: chosen, onChange: (v) => { chosen = v; } }) : c.checklist.default({ label: title, items, values: [], onChange: (v) => { chosen = v.filter((id) => !chosen.includes(id)).slice(-1); win.close("done"); } }); let done = false; const finish = (value) => { if (!done) { done = true; resolve(value); } }; const win = c.modal.generic({ title, size: "sm", body, closeOnOutside: !multiple, onClose: (why) => finish(why === "done" ? chosen : null) }); if (multiple) win.setFooter({ actions: [c.button.sm({ label: "Done", tone: "primary", onClick: () => win.close("done") })] }); }); }