Files
SoulStream/src/lib/whoHas.svelte.js
T

57 lines
2.1 KiB
JavaScript

// "Who has this?" — the ask, shared by every surface that can raise it: a
// gap card on Library, a release on an artist page, an open album, a Space's
// Holdings tab. The answer is local (the rooms' holdings are cached on this
// device), so asking is cheap enough to do for a whole shelf of gaps at once.
//
// One sheet at a time, portalled to body by WhoHasSheet.svelte, opened from
// wherever the card is and anchored to it. App.svelte mounts the sheet once.
import { askHoldings } from "./community.svelte.js";
export const whoHas = $state({
open: false,
artist: "",
album: "",
mbid: null,
cover: null,
/** Where the card was (viewport px), so the sheet can sit beside it. */
anchor: null,
});
export function openWhoHas({ artist, album, mbid = null, cover = null }, anchorEl = null) {
const r = anchorEl?.getBoundingClientRect?.();
whoHas.artist = artist;
whoHas.album = album;
whoHas.mbid = mbid;
whoHas.cover = cover;
whoHas.anchor = r ? { x: r.left, y: r.top, w: r.width, h: r.height } : null;
whoHas.open = true;
}
export function closeWhoHas() {
whoHas.open = false;
}
/** The map key a page files holders under. */
export const pairKey = (artist, album) => `${(artist ?? "").trim().toLowerCase()}${(album ?? "").trim().toLowerCase()}`;
/** Holders for many albums in one ask: pairKey → holders[]. Empty lists are
* filed too, so a page can tell "asked, nobody" from "not asked yet". */
export async function holdersFor(items) {
const out = new Map();
if (!items.length) return out;
try {
const answers = await askHoldings(items.map((i) => ({ artist: i.artist, album: i.album, mbid: i.mbid ?? null })));
for (const a of answers) out.set(pairKey(a.artist, a.album), a.holders);
} catch {
// no rooms, no node, no answer — the shelf simply carries no marker
}
return out;
}
/** Distinct people, so a holder in two rooms counts once on a card. */
export function holderCount(holders) {
const seen = new Set();
for (const h of holders ?? []) seen.add((h.username ?? h.member).toLowerCase());
return seen.size;
}