Many improvemtns and fixes related to decoupling of svelte and rust on android.
🏗️ Build and Test JellyTau / Run Tests (push) Failing after 18s
🏗️ Build and Test JellyTau / Build Android APK (push) Has been skipped
Traceability Validation / Check Requirement Traces (push) Failing after 2s

This commit is contained in:
2026-02-28 19:50:47 +01:00
parent 07f3bf04ca
commit e8e37649fa
53 changed files with 2309 additions and 792 deletions
@@ -1,8 +1,8 @@
<script lang="ts">
import type { MediaItem, Library } from "$lib/api/types";
import { auth } from "$lib/stores/auth";
import { downloads } from "$lib/stores/downloads";
import { formatDuration } from "$lib/utils/duration";
import CachedImage from "$lib/components/common/CachedImage.svelte";
interface Props {
items: (MediaItem | Library)[];
@@ -13,37 +13,14 @@
let { items, showProgress = false, showDownloadStatus = true, onItemClick }: Props = $props();
// Map of item IDs to their image URLs, loaded asynchronously
let imageUrls = $state<Map<string, string>>(new Map());
function getDownloadInfo(itemId: string) {
return Object.values($downloads.downloads).find((d) => d.itemId === itemId);
}
// Load image URL for a single item
async function loadImageUrl(item: MediaItem | Library): Promise<void> {
try {
const repo = auth.getRepository();
const tag = "primaryImageTag" in item ? item.primaryImageTag : ("imageTag" in item ? item.imageTag : undefined);
const url = await repo.getImageUrl(item.id, "Primary", {
maxWidth: 80,
tag,
});
imageUrls.set(item.id, url);
} catch {
imageUrls.set(item.id, "");
}
function getImageTag(item: MediaItem | Library): string | undefined {
return "primaryImageTag" in item ? item.primaryImageTag : ("imageTag" in item ? item.imageTag : undefined);
}
// Load image URLs whenever items change
$effect(() => {
items.forEach((item) => {
if (!imageUrls.has(item.id)) {
loadImageUrl(item);
}
});
});
function getSubtitle(item: MediaItem | Library): string {
if (!("type" in item)) return "";
@@ -80,7 +57,6 @@
<div class="space-y-1">
{#each items as item, index (item.id)}
{@const imageUrl = imageUrls.get(item.id) ?? ""}
{@const subtitle = getSubtitle(item)}
{@const duration = "runTimeTicks" in item ? formatDuration(item.runTimeTicks) : ""}
{@const progress = getProgress(item)}
@@ -102,20 +78,14 @@
<!-- Thumbnail -->
<div class="w-10 h-10 rounded bg-[var(--color-surface)] flex-shrink-0 overflow-hidden relative">
{#if imageUrl}
<img
src={imageUrl}
alt={item.name}
class="w-full h-full object-cover"
loading="lazy"
/>
{:else}
<div class="w-full h-full flex items-center justify-center text-gray-600">
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
<path d="M12 3v10.55c-.59-.34-1.27-.55-2-.55-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4V7h4V3h-6z" />
</svg>
</div>
{/if}
<CachedImage
itemId={item.id}
imageType="Primary"
tag={getImageTag(item)}
maxWidth={80}
alt={item.name}
class="w-full h-full object-cover"
/>
<!-- Play overlay on hover -->
<div class="absolute inset-0 bg-black/50 opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center">