Two defects with one cause: "server only" was a private $derived inside MediaCard. The list view (what LibraryGrid renders when the stored view preference is list) had no notion of it at all, so a library browsed as a list offline showed every revealed item as an ordinary tappable row that plays nothing, with no way to queue it. And the rule asked the downloads store whether *this item id* was downloaded — but only a playable leaf (Audio, Movie, Episode) ever has a download row. An album's tracks carry them, the album does not, so a fully downloaded album greyed itself out and offered to queue what was already on the device. The rule moves to the pure $lib/utils/serverOnly and both views call it. The container half is answered by the backend rather than guessed at: get_download_disk_usage().sizes already carries container subtotals beside leaf sizes (DR-085), so deviceContentIds is membership in a Rust-computed map, not a frontend list of which item types are containers. That map was loaded only by the Downloads page, so the shell primes it at startup and re-reads it whenever the offline gate settles. Queueing is shared too, since the list view had no copy to diverge from. TRACES: UR-052, UR-055 | DR-292 | UT-257, UT-258
53 lines
2.1 KiB
TypeScript
53 lines
2.1 KiB
TypeScript
/**
|
|
* Is a card "server only" — revealed by the offline "Show all server media"
|
|
* toggle, but with nothing on the device behind it?
|
|
*
|
|
* Such cards render greyed out, are inert to tap (there is nothing to play),
|
|
* and offer to queue a download for the next reconnect instead.
|
|
*
|
|
* This lives in its own module because two surfaces answer the question — the
|
|
* grid (`MediaCard`) and the list (`LibraryListView`) — and they disagreed:
|
|
* the list had no notion of server-only at all, so switching a library to list
|
|
* view offline turned every non-downloaded item back into a normal, tappable
|
|
* row that plays nothing.
|
|
*
|
|
* `hasDeviceContent` is the fix for the second half of that defect. An item's
|
|
* *own* download row only ever exists for a playable leaf (Audio, Movie,
|
|
* Episode); an album or a season never has one, so a fully downloaded album
|
|
* greyed itself out and offered to queue what was already on the device. The
|
|
* caller passes the backend's answer — `get_download_disk_usage().sizes`
|
|
* carries container subtotals as well as leaf sizes — rather than the frontend
|
|
* deciding which item types are containers, which is taxonomy that belongs in
|
|
* Rust.
|
|
*
|
|
* TRACES: UR-052 | DR-292 | UT-257
|
|
*/
|
|
|
|
export interface ServerOnlyInput {
|
|
/** False for a `Library` tile — a library is never queued or greyed. */
|
|
isMediaItem: boolean;
|
|
/** Server reachability (`$isConnected`). */
|
|
isConnected: boolean;
|
|
/** The offline banner's "Show all server media" toggle. */
|
|
revealServerCatalog: boolean;
|
|
/** This item's own download row is `completed`. */
|
|
isDownloaded: boolean;
|
|
/** This item's own download row is actively transferring. */
|
|
isActivelyDownloading: boolean;
|
|
/** The device holds bytes at or under this item (leaf file or container). */
|
|
hasDeviceContent: boolean;
|
|
}
|
|
|
|
export function isServerOnly({
|
|
isMediaItem,
|
|
isConnected,
|
|
revealServerCatalog,
|
|
isDownloaded,
|
|
isActivelyDownloading,
|
|
hasDeviceContent,
|
|
}: ServerOnlyInput): boolean {
|
|
if (!isMediaItem) return false;
|
|
if (isConnected || !revealServerCatalog) return false;
|
|
return !isDownloaded && !isActivelyDownloading && !hasDeviceContent;
|
|
}
|