88 lines
2.9 KiB
TypeScript
88 lines
2.9 KiB
TypeScript
// What the library overview mosaic is made of, and in what order.
|
|
//
|
|
// Pure: takes the libraries, returns the tiles to draw. No DOM, no stores — so
|
|
// the ordering and the de-duplication rules below are unit-testable rather than
|
|
// buried in markup.
|
|
//
|
|
// Note what is NOT decided here: which favourites category a library belongs to.
|
|
// That is Jellyfin vocabulary and arrives on the library itself as
|
|
// `favoritesScope` (Rust: `SearchScope::for_collection_type`). This file only
|
|
// decides what to *call* it and where to put it.
|
|
//
|
|
// TRACES: UR-075, UR-067 | DR-174, DR-175 | UT-167
|
|
|
|
import type { Library } from "$lib/api/types";
|
|
import {
|
|
FAVORITE_SCOPE_LABELS,
|
|
asFavoritesScope,
|
|
favoritesRouteUrl,
|
|
type FavoritesScope,
|
|
} from "$lib/utils/favoritesView";
|
|
|
|
/** Artwork shapes, as the source images generally arrive. A measured image
|
|
* overrides these (see MosaicGrid); they are the shape assumed until then. */
|
|
const SQUARE = 1;
|
|
const WIDE = 16 / 9;
|
|
|
|
export type LibraryMosaicEntry = {
|
|
/** Stable identity for the layout and for `{#each}` keying. */
|
|
key: string;
|
|
/** Assumed width / height until the artwork reports its own. */
|
|
ratio: number;
|
|
label: string;
|
|
} & (
|
|
| { kind: "library"; library: Library }
|
|
| { kind: "favorites"; scope: FavoritesScope; href: string }
|
|
);
|
|
|
|
/**
|
|
* A music library's artwork is a square cover; everything else is a wide
|
|
* backdrop. Presentation, not taxonomy: this is the shape of a picture, and it
|
|
* is a starting guess that the decoded image is allowed to overrule.
|
|
*/
|
|
export function assumedLibraryRatio(lib: Library): number {
|
|
return lib.collectionType === "music" ? SQUARE : WIDE;
|
|
}
|
|
|
|
/**
|
|
* The mosaic's tiles, in order: the cross-library favourites entry first, then
|
|
* each library followed by its own favourites tile.
|
|
*
|
|
* A category's favourites tile appears **once**, after the first library of that
|
|
* category — two movie libraries ("Films", "Kids") share one favourites list, so
|
|
* a tile each would be two tiles going to the same place.
|
|
*/
|
|
export function buildLibraryMosaic(libraries: Library[]): LibraryMosaicEntry[] {
|
|
const entries: LibraryMosaicEntry[] = [
|
|
{
|
|
key: "favorites:all",
|
|
kind: "favorites",
|
|
scope: "all",
|
|
href: favoritesRouteUrl("all"),
|
|
ratio: WIDE,
|
|
label: "Favourites",
|
|
},
|
|
];
|
|
|
|
const seenScopes = new Set<FavoritesScope>(["all"]);
|
|
|
|
for (const lib of libraries) {
|
|
const ratio = assumedLibraryRatio(lib);
|
|
entries.push({ key: `library:${lib.id}`, kind: "library", library: lib, ratio, label: lib.name });
|
|
|
|
const scope = asFavoritesScope(lib.favoritesScope);
|
|
if (!scope || seenScopes.has(scope)) continue;
|
|
seenScopes.add(scope);
|
|
entries.push({
|
|
key: `favorites:${scope}`,
|
|
kind: "favorites",
|
|
scope,
|
|
href: favoritesRouteUrl(scope),
|
|
ratio,
|
|
label: `Favourite ${FAVORITE_SCOPE_LABELS[scope]}`,
|
|
});
|
|
}
|
|
|
|
return entries;
|
|
}
|