Die Bibliotheksfilterleiste erzeugte das Signal laengst und warf es weg: `const [, startTransition] = useTransition()`. Waehrend der Suche blieben die alten Treffer unveraendert stehen — ohne jeden Hinweis, dass gearbeitet wird. Beide Suchen teilen sich jetzt eine schmale Klammer (SearchPending): die Filterleiste meldet den Ladezustand, die Trefferliste daneben — eine Server-Komponente — wird gedaempft und auf aria-busy gesetzt. Die alten Treffer bleiben sichtbar; sie verschwinden zu lassen liesse die Liste bei jedem Tastendruck springen. Der Hinweis „sucht …" steht jetzt auch in der Bibliothek, nicht nur unter /suche. Sammelverzoegerung in beiden auf 250 ms angeglichen (vorher 350 und 300). Zwei Vermutungen aus dem Issue haben sich beim Nachmessen NICHT bestaetigt und bleiben deshalb unangetastet: Die Facetten kosten nichts. Gemessen an einer eigens angelegten Bibliothek mit 2000 Eintraegen (Median aus 9 Laeufen): getLibrary 50 ms, getLibraryFacets 17 ms, beide parallel 61 ms. Ein Wegfall spaerte rund 12 ms — eine Zwischenspeicherung braechte dafuer das Risiko veralteter Zaehler. Langsame Bilder blockieren die Trefferanzeige nicht. Mit kuenstlich um 1500 ms verzoegerten /api/image-Antworten stehen die Treffer nach 465 ms im DOM, 891 ms vor den Bildern; die Rueckfallkacheln zeigen derweil die Titel. Ein Umbau des Bild-Endpunkts waere also Aufwand ohne Wirkung. Refs #3
This commit is contained in:
+35
-30
@@ -6,6 +6,7 @@ import { getLibrary, getLibraryFacets, type LibraryFilters as Filters, type Libr
|
||||
import { AppShell } from "@/components/AppShell";
|
||||
import { LibraryCard } from "@/components/library/LibraryCard";
|
||||
import { LibraryFilters } from "@/components/library/LibraryFilters";
|
||||
import { SearchPendingProvider, SearchResults } from "@/components/SearchPending";
|
||||
import type { EntryStatus, TitleType } from "@/generated/prisma/client";
|
||||
|
||||
export const metadata: Metadata = { title: "Bibliothek" };
|
||||
@@ -56,39 +57,43 @@ export default async function LibraryPage({
|
||||
</Link>
|
||||
</header>
|
||||
|
||||
{facets.counts.ALLE > 0 && <LibraryFilters facets={facets} />}
|
||||
<SearchPendingProvider>
|
||||
{facets.counts.ALLE > 0 && <LibraryFilters facets={facets} />}
|
||||
|
||||
<div className="mt-6">
|
||||
{items.length === 0 ? (
|
||||
<div className="fl-card p-8 text-center">
|
||||
<p className="text-[var(--fl-muted)]">
|
||||
{facets.counts.ALLE === 0
|
||||
? "Deine Bibliothek ist noch leer."
|
||||
: hasFilters
|
||||
? "Zu dieser Auswahl passt nichts."
|
||||
: "Hier ist nichts."}
|
||||
</p>
|
||||
{facets.counts.ALLE === 0 && (
|
||||
<Link href="/suche" className="fl-btn fl-btn-primary mt-4">
|
||||
Ersten Titel suchen
|
||||
</Link>
|
||||
<SearchResults>
|
||||
<div className="mt-6">
|
||||
{items.length === 0 ? (
|
||||
<div className="fl-card p-8 text-center">
|
||||
<p className="text-[var(--fl-muted)]">
|
||||
{facets.counts.ALLE === 0
|
||||
? "Deine Bibliothek ist noch leer."
|
||||
: hasFilters
|
||||
? "Zu dieser Auswahl passt nichts."
|
||||
: "Hier ist nichts."}
|
||||
</p>
|
||||
{facets.counts.ALLE === 0 && (
|
||||
<Link href="/suche" className="fl-btn fl-btn-primary mt-4">
|
||||
Ersten Titel suchen
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<p className="mb-4 text-sm text-[var(--fl-faint)]">
|
||||
{items.length} {items.length === 1 ? "Titel" : "Titel"}
|
||||
</p>
|
||||
<ul className="grid grid-cols-2 gap-4 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-6">
|
||||
{items.map((item) => (
|
||||
<li key={item.entryId}>
|
||||
<LibraryCard item={item} />
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<p className="mb-4 text-sm text-[var(--fl-faint)]">
|
||||
{items.length} {items.length === 1 ? "Titel" : "Titel"}
|
||||
</p>
|
||||
<ul className="grid grid-cols-2 gap-4 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-6">
|
||||
{items.map((item) => (
|
||||
<li key={item.entryId}>
|
||||
<LibraryCard item={item} />
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</SearchResults>
|
||||
</SearchPendingProvider>
|
||||
</AppShell>
|
||||
);
|
||||
}
|
||||
|
||||
+24
-19
@@ -7,6 +7,7 @@ import { searchMovies, searchMulti, searchSeries, type TmdbSearchResult } from "
|
||||
import { AppShell } from "@/components/AppShell";
|
||||
import { Poster } from "@/components/Poster";
|
||||
import { SearchField } from "@/components/SearchField";
|
||||
import { SearchPendingProvider, SearchResults } from "@/components/SearchPending";
|
||||
|
||||
export const metadata: Metadata = { title: "Suchen" };
|
||||
|
||||
@@ -54,27 +55,31 @@ export default async function SearchPage({
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<SearchField initialQuery={query} filter={filter} filters={FILTERS} />
|
||||
<SearchPendingProvider>
|
||||
<SearchField initialQuery={query} filter={filter} filters={FILTERS} />
|
||||
|
||||
<p className="mt-3 text-sm text-[var(--fl-faint)]">
|
||||
Nichts dabei?{" "}
|
||||
<Link href="/titel/neu" className="text-[var(--fl-accent)] underline-offset-4 hover:underline">
|
||||
Titel selbst anlegen
|
||||
</Link>
|
||||
</p>
|
||||
<p className="mt-3 text-sm text-[var(--fl-faint)]">
|
||||
Nichts dabei?{" "}
|
||||
<Link href="/titel/neu" className="text-[var(--fl-accent)] underline-offset-4 hover:underline">
|
||||
Titel selbst anlegen
|
||||
</Link>
|
||||
</p>
|
||||
|
||||
<div className="mt-8">
|
||||
{!env.tmdb.isConfigured ? (
|
||||
<Notice tone="warning">
|
||||
Ohne TMDB-Schlüssel kann Flimmer nicht suchen. Trage <code>TMDB_API_KEY</code> in der{" "}
|
||||
<code>.env</code> ein und starte die Instanz neu.
|
||||
</Notice>
|
||||
) : query.length < 2 ? (
|
||||
<Notice>Gib mindestens zwei Zeichen ein, um zu suchen.</Notice>
|
||||
) : (
|
||||
<Results query={query} filter={filter} />
|
||||
)}
|
||||
</div>
|
||||
<SearchResults>
|
||||
<div className="mt-8">
|
||||
{!env.tmdb.isConfigured ? (
|
||||
<Notice tone="warning">
|
||||
Ohne TMDB-Schlüssel kann Flimmer nicht suchen. Trage <code>TMDB_API_KEY</code> in
|
||||
der <code>.env</code> ein und starte die Instanz neu.
|
||||
</Notice>
|
||||
) : query.length < 2 ? (
|
||||
<Notice>Gib mindestens zwei Zeichen ein, um zu suchen.</Notice>
|
||||
) : (
|
||||
<Results query={query} filter={filter} />
|
||||
)}
|
||||
</div>
|
||||
</SearchResults>
|
||||
</SearchPendingProvider>
|
||||
</AppShell>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user