fix(managers): address the E4 S3 code-review round 3 findings

- The tag menu's "trigger is gone" test now measures against the SCROLLER, not the
  window. The page scroller starts ~230px down (measured), so a row that slid under
  the fixed toolbar was still "on screen" by window coords and the menu floated over
  the chrome, anchored to something invisible.
- Scroll-repositioning keeps whichever side the menu is anchored to instead of
  resetting it to "below" every frame — that reset made the layout effect flip it
  back, costing two renders and two forced layouts per frame near the bottom edge.
- One placement rule (`coordsFrom`) now serves the initial open, the layout
  correction and every reposition, so those can no longer drift apart.
- Card page: reset on the filter/sort INPUTS only, and separately COMMIT the clamp
  when the page falls out of range. Keying the reset on the row count (round 2) was
  too blunt — a background refetch that merely added a channel threw the reader off
  page 7 for something they didn't do.
- Pager derives `pageCount()` once and `hasPagerContent` reuses it, instead of the
  same expression twice in one file.
- Re-indent the pagerNode JSX left dangling by round 2's ternary edit.
This commit is contained in:
2026-07-20 01:10:13 +02:00
parent c630d29ff6
commit ab5f3c9fbb
4 changed files with 78 additions and 47 deletions
+9 -4
View File
@@ -98,12 +98,13 @@ export default function ChannelDiscovery({
const rows = (query.data ?? []).filter(
(c) => !q || `${c.title ?? ""} ${c.handle ?? ""}`.toLowerCase().includes(q)
);
// A re-sorted or RESIZED list invalidates the page you were on — reset to the first, as the table
// does on its own filter/sort changes. The row count is in the deps too, so a subscribe (which
// drops a channel from this tab) counts, not just an explicit filter change.
// Changing what you're looking at starts over at the first page, as the table does on its own
// filter/sort change. NOT keyed on the row count: subscribing drops a row from this tab, and being
// thrown to page 1 for it would punish the very action you just took. A list that shrank past your
// page is handled by committing the clamp instead (below).
useEffect(() => {
setCardPage(0);
}, [search, sort, rows.length]);
}, [search, sort]);
const columns: Column<DiscoveredChannel>[] = [
// Actions first, mirroring the Subscriptions table (where it sits right after Prio) so the two
@@ -202,6 +203,10 @@ export default function ChannelDiscovery({
const sortedRows = isCards ? sortRows(rows, columns, sort) : rows;
const cardTotalPages = cardSize <= 0 ? 1 : Math.max(1, Math.ceil(sortedRows.length / cardSize));
const cardSafePage = Math.min(cardPage, cardTotalPages - 1);
// Commit the clamp when the list shrinks past your page, so it can't spring back later.
useEffect(() => {
if (cardPage > cardTotalPages - 1) setCardPage(cardTotalPages - 1);
}, [cardPage, cardTotalPages]);
const pagedRows =
!isCards || cardSize <= 0
? sortedRows