Skip to content

fix(mint): match Add Mints search skeleton to real result rows#228

Open
Kelbie wants to merge 2 commits into
spike/flashlist-v2-threadfrom
fix/add-mints-skeleton-parity
Open

fix(mint): match Add Mints search skeleton to real result rows#228
Kelbie wants to merge 2 commits into
spike/flashlist-v2-threadfrom
fix/add-mints-skeleton-parity

Conversation

@Kelbie

@Kelbie Kelbie commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Why

The Add Mints search (features/mint/screens/MintAddScreen.tsx) showed a loading skeleton that looked nothing like the real result row, and the height difference made content jump when search data arrived. The skeleton was a hand-rolled LoadingMintsList that had drifted from the real MintItemContactRow row: a rounded-2xl card the real row doesn't have, avatar 42 vs 44, padding 16 vs 20h/12v, and missing the domain subtitle, the inline stats accent, and the checkbox.

This is the drift-from-duplicated-chrome anti-pattern. The fix follows the established "share the chrome" convention (PostCardGutterHeader): render skeleton and real states from the same component so heights can't diverge.

What

  • MintAddScreen — delete LoadingMintsList; feed the result List skeleton placeholders during the initial search and render them through the same MintItemContactRow path in loading mode. One List, so the header spacer / padding / container chrome are shared for free. getItemType keeps skeleton and real rows in separate recycle pools.
  • ContactRow — close the one real height gap in its loading state: reserve the inline stats accent (gated to mint / explicit-stats rows, so contact/ble/geohash skeletons are untouched) and render an inert 24px box for the checkbox slot instead of flashing a live control.
  • RowStatsAccent — add a co-located RowStatsAccentSkeleton so the accent's geometry lives in one file and can't drift between loading and loaded.
  • Tests — new __tests__/contactRowLoadingReservation.test.tsx: accent reserved for loading mint rows, not for loading nostr rows (gate proof), and the inert-checkbox/live-checkbox swap.

Height parity

Every height-contributing element of the real row is now reserved at the same dimensions by the same component (avatar 44, title bar, subtitle bar, accent pill row, 24px checkbox). The only previously-lost contributor was the accent; it's now reserved.

Notes

  • Base branch: stacks on spike/flashlist-v2-thread (PR Replace @legendapp/list with FlashList v2 app-wide #227 / FlashList), which is where the screen uses the shared <List> seam. Merge after Replace @legendapp/list with FlashList v2 app-wide #227.
  • Blast radius: the ContactRow change is gated — accent reservation fires only for mint/explicit-stats rows, the inert checkbox only for selectable + loading. No-op for plain contact skeletons; SearchResultRows is unaffected.
  • Device verification pending: confirm zero .shift. events on the load→data swap and tune the skeleton pill height to the measured real-accent height via npx tsx codereview/log-doctor/index.ts timeline --event 'visual\.layout|\.shift\.' --latest.

Gates

tsc clean · eslint 0 errors on changed files · npx jest contactRow 4/4 pass · knip flags no new dead exports.

Kelbie added 2 commits June 22, 2026 10:57
The Add Mints loading skeleton was a hand-rolled card layout that had
drifted from the real result row (different card chrome, avatar size,
padding, and missing the subtitle, stats accent, and checkbox), so
content jumped when search data arrived.

Render the skeleton through the SAME List + ContactRow path as real rows
(in loading mode) so the container chrome is shared and can't drift, and
close the one real height gap in ContactRow's loading state.

- MintAddScreen: delete the hand-rolled LoadingMintsList; feed the result
  List skeleton placeholders during the initial search and render them via
  MintItem in loading mode (one List, shared header/padding).
- ContactRow: reserve the inline stats-accent height while loading (gated
  to mint / explicit-stats rows so contact/ble/geohash skeletons are
  unaffected) and render an inert 24px box for the checkbox slot.
- RowStatsAccent: add co-located RowStatsAccentSkeleton so accent geometry
  lives in one place and can't drift between loading and loaded states.
- add ContactRow loading-reservation tests (accent + checkbox + gate).
The loading accent used a fixed 14px bar, which under-shot the real pill's
text line box (Text size 12 ≈ 16px) by a couple of pixels, so skeleton rows
sat slightly shorter than real rows and content still nudged on the swap.

Mirror the real pill structure instead of guessing a height: a 12px icon
box + a `Text loading` at the same size/bold, which self-sizes to the exact
real line box. Accent line height is now identical loading vs loaded.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant