refactor(feed): derive the row's drop-out thresholds from its column widths

They were two hand-written sets of numbers describing the same boxes —
change a w-32 and the thresholds are silently wrong, the symptom being a
column appearing just before it fits. Now computed from one table (which
reproduces the old values exactly).

Width also stops reaching the four views that ignore it: it changes every
frame of a resize, so passing it everywhere pushed all of them through
VideoCard's memo for nothing.
This commit is contained in:
2026-07-17 02:13:45 +02:00
parent be05c80d82
commit ba23c0c996
2 changed files with 32 additions and 10 deletions
+27 -9
View File
@@ -322,15 +322,33 @@ function Thumb({
);
}
// What the one-line row needs before each column earns its place, cumulative from the left:
// actions (192) + a title worth reading (~120) + gaps + the row's padding, then each column plus
// its gap. Driven by the ROW's measured width, never the viewport — see VirtualFeed's itemWidth.
const ROW_FITS = {
channel: 490,
meta: 606,
views: 682,
published: 886,
};
// The one-line row's column widths, in ONE place — the Tailwind classes below are generated from
// them, so the thresholds can't drift out of step with the boxes they describe (they did when both
// were written by hand). Each is measured against the worst case in the library AND in both
// languages; see the row's own comment for where each number comes from.
const ROW_COL = {
actions: 192, // w-48 — six buttons at 28 + five 4px gaps = 188
title: 120, // — not a column: the floor below which a title isn't worth showing
channel: 128, // w-32
meta: 104, // — RowMetaCells' own grid: 4rem + 1rem + 1rem + two 4px gaps
views: 64, // w-16
published: 192, // w-48
} as const;
const ROW_GAP = 12; // gap-3
const ROW_PAD = 26; // px-3 both sides, plus a rounding allowance
// What the row must measure before each column earns its place — cumulative from the left, so each
// one only appears once everything ahead of it AND a readable title already fit.
const ROW_FITS = (() => {
let w = ROW_PAD + ROW_COL.actions + ROW_GAP + ROW_COL.title;
const at = (px: number) => (w += ROW_GAP + px);
return {
channel: at(ROW_COL.channel),
meta: at(ROW_COL.meta),
views: at(ROW_COL.views),
published: at(ROW_COL.published),
};
})();
function VideoCard({
video,
+5 -1
View File
@@ -180,7 +180,11 @@ export default function VirtualFeed({
key={v.id}
video={v}
view={view}
width={itemWidth}
// Only the one-line row reads its own width (it stands columns down as it
// narrows). Everything else takes a stable 0, so a resize — which changes
// itemWidth every frame — doesn't blow past VideoCard's memo for views that
// wouldn't do anything with it.
width={FEED_VIEW_SPEC[view].thumb ? 0 : itemWidth}
onState={onState}
onToggleSave={onToggleSave}
onOpenChannel={onOpenChannel}