fix(ui): review round 2 — reveal floor, failed next page, count states, row Play

- DataTable: the reveal floor holds only in the view it was made for (Channels
  never clears its follow, so every later sort mounted rows up to it)
- Feed + Plex browse: a failed next page stops the read-ahead and shows a row
  that retries, instead of re-requesting it once a round-trip
- the YouTube Load more click uses nextPageOnClick; a contract pins NEXT_PAGE
  to the read-aheads and the click sites to nextPageOnClick
- ItemCount: a failed count says so; a stale one says updating to a screen reader
- the narrow-picture Play lift applies only where a hover row is there to clear
- PlayerModal: one description ladder for the popover and the panel, both tested
- plexImage contract reads every img src, any attribute order
- Tabs comment names who persists the sub-tab
This commit is contained in:
2026-10-03 02:25:15 +02:00
parent c4c442bc5b
commit b40440b825
17 changed files with 360 additions and 63 deletions
+22 -4
View File
@@ -7,7 +7,7 @@ import {
useQueryClient,
} from "@tanstack/react-query";
import { qk } from "../lib/queryKeys";
import { NEXT_PAGE } from "../lib/nextPage";
import { nextPageOnClick } from "../lib/nextPage";
import {
ArrowLeft,
Columns2,
@@ -557,7 +557,9 @@ export default function Feed({
<div className="text-center pt-4">
<Button
variant="outline"
onClick={() => void ytQuery.fetchNextPage(NEXT_PAGE)}
onClick={() =>
void ytQuery.fetchNextPage(nextPageOnClick(ytQuery.isFetchingNextPage))
}
loading={ytQuery.isFetchingNextPage}
>
{ytQuery.isFetchingNextPage
@@ -710,7 +712,11 @@ export default function Feed({
<div className="flex items-center gap-1.5 gap-y-2 flex-wrap">
{/* The item count is the fixed leading element — a fixed-width pill so a changing number
never shoves the controls beside it (see ItemCount). */}
<ItemCount count={summaryQuery.data?.count} stale={summaryQuery.isPlaceholderData} />
<ItemCount
count={summaryQuery.data?.count}
stale={summaryQuery.isPlaceholderData}
failed={summaryQuery.isError && !summaryQuery.data}
/>
<FilterFoldButton
fold={fold}
label={t("sidebar.filters")}
@@ -891,7 +897,10 @@ export default function Feed({
onToggleSave={onToggleSave}
onResetState={onResetState}
onOpen={openVideo}
hasNextPage={!!hasNextPage}
// A failed next page stops the read-ahead: retry is off, and re-running on every settle would
// re-request it once a round-trip for as long as the end is in view (E31 review 2). The row
// below says so and retries on a click.
hasNextPage={!!hasNextPage && !query.isFetchNextPageError}
isFetching={query.isFetching}
fetchNextPage={fetchNextPage}
/>
@@ -910,6 +919,15 @@ export default function Feed({
{isFetchingNextPage && (
<div className="text-center text-muted py-4">{t("feed.loadingMore")}</div>
)}
{query.isFetchNextPageError && !isFetchingNextPage && (
<StateMessage
tone="error"
compact
onRetry={() => void fetchNextPage(nextPageOnClick(isFetchingNextPage))}
>
{t("common.loadMoreFailed")}
</StateMessage>
)}
</div>
);
}