release / go (push) Successful in 1m53s
release / govulncheck (push) Successful in 49s
release / web (push) Successful in 1m50s
release / android (push) Successful in 6m44s
release / Build signed APK (releases and dev) (push) Successful in 6m30s
release / Attach APK to the Release (tag releases only) (push) Canceled after 0s
release / Build + push container image (push) Canceled after 0s
release / Verify release artifacts (tag releases only) (push) Canceled after 0s
release / integration (push) Canceled after 21m41s
Preference 172: no "Load more" buttons. Eight remained, on Liked (three sections), the three Search overflow pages, Genres and Years. - ListContinuation: the one bottom-of-list block. It loads the next page ahead of the reader, announces "Loading more…" through aria-live, shows an optional end line, and on a failed page shows the error with Try again, dropping the sentinel so a dead endpoint isn't re-hit on every scroll. Used on all ten paged lists, including the four that already autoloaded. - A failed next page no longer replaces the list. TanStack sets isError for it, so the page-level error branches now apply only to a failed first load. Genres and Years did the same with their own loader; a later page's failure now keeps the grid. - Liked: Artists | Albums | Tracks tabs (operator's choice). Stacked, a long Artists list loading as you scroll would bury the other two. It opens on the first tab that has likes. - TabStrip: the in-page tab strip, now shared by Liked, Playback errors and Requests. - test-utils/intersectionObserver: a stand-in so tests can scroll to the end. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
50 lines
1.8 KiB
Svelte
50 lines
1.8 KiB
Svelte
<script lang="ts">
|
|
// The bottom of every paged list (#5416, preference 172): the next page
|
|
// loads on its own as the reader nears the end, with no "Load more"
|
|
// button. What the button used to give for free is kept here:
|
|
//
|
|
// - In flight: a "Loading more…" line, announced through aria-live, so the
|
|
// list doesn't look finished while it isn't.
|
|
// - Finished: an optional quiet end line (`endLabel`).
|
|
// - Failed: the error and a Try again button. The sentinel is removed while
|
|
// a page has failed, so a dead endpoint isn't re-requested on every scroll.
|
|
import InfiniteScrollSentinel from './InfiniteScrollSentinel.svelte';
|
|
|
|
let {
|
|
hasMore,
|
|
loading,
|
|
failed = false,
|
|
onMore,
|
|
endLabel
|
|
}: {
|
|
/** Whether another page exists. */
|
|
hasMore: boolean;
|
|
/** A next page is in flight. */
|
|
loading: boolean;
|
|
/** The last next-page fetch failed. */
|
|
failed?: boolean;
|
|
/** Fetch the next page (TanStack's fetchNextPage, or a page's own loader). */
|
|
onMore: () => void;
|
|
/** Shown once the list is complete, e.g. "End of history". */
|
|
endLabel?: string;
|
|
} = $props();
|
|
</script>
|
|
|
|
{#if failed}
|
|
<div role="alert" class="flex items-center justify-center gap-2 py-2 text-sm text-error-fg">
|
|
Couldn't load more.
|
|
<button
|
|
type="button"
|
|
class="rounded border border-border px-2 py-0.5 text-text-primary hover:bg-surface-hover
|
|
focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-accent"
|
|
onclick={onMore}>Try again</button
|
|
>
|
|
</div>
|
|
{:else if hasMore}
|
|
<InfiniteScrollSentinel enabled={!loading} onIntersect={onMore} />
|
|
{/if}
|
|
|
|
<p class="py-2 text-center text-sm text-text-secondary empty:hidden" aria-live="polite">
|
|
{#if !failed && hasMore && loading}Loading more…{:else if !hasMore && endLabel}{endLabel}{/if}
|
|
</p>
|