[WIP] feat(look): virtualized month timeline grid with blur-hash images - #540
Conversation
The month-grouped photo wall for the Look app — @tanstack/react-virtual windows the rows so it stays smooth through thousands of images, each tile backed by a lazy image that fades in over its blur-hash placeholder with no layout shift. All presentational; consumes useLoadPhotos. Refs SWO-613. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Deploying watch with
|
| Latest commit: |
5ae624a
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://df2c30df.watch-4ta.pages.dev |
| Branch Preview URL: | https://swo-613-look-timeline.watch-4ta.pages.dev |
|
Warning Review limit reached
Next review available in: 50 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (4)
📝 WalkthroughWalkthroughAdds blurhash-backed image loading, reusable photo cards, and a responsive virtualized photo timeline grouped by month, with loading and empty states, route-link support, unit tests, and Storybook stories. ChangesPhoto UI
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant Browser
participant PhotoTimelineContainer
participant Virtualizer
participant PhotoCard
participant BlurImage
Browser->>PhotoTimelineContainer: provide photo query results and breakpoint flags
PhotoTimelineContainer->>Virtualizer: create visible monthly timeline rows
Virtualizer-->>PhotoTimelineContainer: return positioned virtual rows
PhotoTimelineContainer->>PhotoCard: render visible photos with generated links
PhotoCard->>BlurImage: pass thumbnail URL and blur hash
BlurImage-->>Browser: display placeholder and fade in loaded image
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Report✅ All modified and coverable lines are covered by tests. @@ Coverage Diff @@
## main #540 +/- ##
==========================================
+ Coverage 92.46% 92.55% +0.09%
==========================================
Files 320 322 +2
Lines 5628 5683 +55
Branches 1204 1212 +8
==========================================
+ Hits 5204 5260 +56
+ Misses 424 423 -1
... and 1 file with indirect coverage changes Continue to review full report in Codecov by Harness.
🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/ui/src/look/home-page/container/index.tsx`:
- Line 9: Update the virtualizer configuration in the home-page container to
provide getItemKey using rows[index].key, and use each virtualRow.key when
rendering mapped elements. Preserve the existing row rendering while ensuring
virtualizer and rendered element identities remain stable as photos or columns
change.
In `@packages/ui/src/look/home-page/utils.test.ts`:
- Around line 27-45: Update the groupPhotosByMonth test fixture to place the
January photo before the March photos while retaining the expected March-first
assertions, then modify groupPhotosByMonth to sort dated month groups in
descending chronological order before returning them.
In `@packages/ui/src/look/photos/blur-image/blurhash.ts`:
- Line 16: Update the canvas creation logic in the blurhash helper to check
whether document is available before calling document.createElement('canvas').
Return undefined immediately in SSR or other non-DOM environments, while
preserving the existing canvas processing path when document exists.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 026d5ac9-8f13-4563-8564-886ad7c2ecfc
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (15)
packages/ui/package.jsonpackages/ui/src/look/home-page/columns.tspackages/ui/src/look/home-page/container/index.stories.tsxpackages/ui/src/look/home-page/container/index.tsxpackages/ui/src/look/home-page/empty-state/index.tsxpackages/ui/src/look/home-page/utils.test.tspackages/ui/src/look/home-page/utils.tspackages/ui/src/look/photos/blur-image/blurhash.tspackages/ui/src/look/photos/blur-image/index.stories.tsxpackages/ui/src/look/photos/blur-image/index.tsxpackages/ui/src/look/photos/blur-image/styled.tsxpackages/ui/src/look/photos/photo-card/index.stories.tsxpackages/ui/src/look/photos/photo-card/index.tsxpackages/ui/src/look/photos/photo-card/skeleton.tsxpackages/ui/src/look/photos/types.ts
- Stable virtualizer item keys (getItemKey from rows[index].key) so a header's cached height isn't misapplied to a photo row on re-chunk. - Order month groups newest-first by key, independent of input order. - Guard document in blurHashToDataUri for non-DOM/SSR per its contract. Refs SWO-613. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Summary
Adds the Look timeline UI to
packages/ui(SWO-613, part of SWO-607) — the fast, month-grouped photo wall that stays smooth through thousands of images. All presentational; consumes the already-mergeduseLoadPhotosshape via a container that takesqueryRs+LinkComponent(the watch container pattern).look/photos/blur-image/—BlurImage: a lazy<img loading="lazy">that fades in over a blur-hash placeholder once decoded, filling its positioned parent so there's no layout shift on load.blurhash.tsdecodes the hash to a 32×32 canvas data-URI (falls back to a solid tint on empty/invalid hash or no canvas). The fade is astyled('img')with a transientloadedprop.look/photos/photo-card/—PhotoCard(square,object-fit: cover, optional link wrapper) +PhotoSkeleton.look/home-page/container/—PhotoTimelineContainer: virtualized month timeline via@tanstack/react-virtual(dynamic-measured rows — one header row per month, photos chunked into rows of N). Only on-screen rows are in the DOM. Loading → skeleton grid; empty →LookEmptyState.look/home-page/utils.ts— pure, unit-tested logic:groupPhotosByMonth(UTC/string-based month keys — deterministic, no timezone/DST bugs; undated photos sort last),buildTimelineRows(flatten to the virtualizer row model),resolveColumns(breakpoint → column count),genPhotoLinkProps.BlurImage,PhotoCard, and the container (240-photo populated / loading / empty).Design decisions
width/heightstill drive the blur-hash placeholder aspect and are available for the lightbox (SWO-614).packages/uibarrel exists — mirrors the repo's per-folderindex.tsx+ subpath-export convention (ui/look/...); the ticket's "export fromsrc/index.tsx" was stale.Dependencies (exact-pinned, cooldown-clear per
supply-chain-security)@tanstack/react-virtual@3.14.6(published 2026-07-12, >7 days aged)blurhash@2.0.5(matches the version already inapps/backend)Test plan
pnpm --filter ui typecheckpassespnpm --filter ui test— 596 pass (incl. newutils.test.ts: grouping, undated-last, chunking, breakpoint columns, link props — exact assertions)pnpm --filter ui build(tsup) — emitsdist/look/**pnpm --filter ui build-storybookpassessrc/lookapps/lookis SWO-616; lightbox is SWO-614Refs SWO-613.
🤖 Generated with Claude Code
Summary by CodeRabbit