Skip to content

[WIP] feat(look): virtualized month timeline grid with blur-hash images - #540

Merged
shinaBR2 merged 2 commits into
mainfrom
swo-613-look-timeline
Jul 25, 2026
Merged

[WIP] feat(look): virtualized month timeline grid with blur-hash images#540
shinaBR2 merged 2 commits into
mainfrom
swo-613-look-timeline

Conversation

@shinaBR2

@shinaBR2 shinaBR2 commented Jul 25, 2026

Copy link
Copy Markdown
Owner

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-merged useLoadPhotos shape via a container that takes queryRs + 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.ts decodes 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 a styled('img') with a transient loaded prop.
  • 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.
  • Storybook stories for BlurImage, PhotoCard, and the container (240-photo populated / loading / empty).

Design decisions

  • Uniform square grid, not justified/masonry — constant row heights virtualize cleanly and are inherently shift-free. width/height still drive the blur-hash placeholder aspect and are available for the lightbox (SWO-614).
  • No packages/ui barrel exists — mirrors the repo's per-folder index.tsx + subpath-export convention (ui/look/...); the ticket's "export from src/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 in apps/backend)

Test plan

  • pnpm --filter ui typecheck passes
  • pnpm --filter ui test — 596 pass (incl. new utils.test.ts: grouping, undated-last, chunking, breakpoint columns, link props — exact assertions)
  • pnpm --filter ui build (tsup) — emits dist/look/**
  • pnpm --filter ui build-storybook passes
  • Biome clean on src/look
  • Live wiring into apps/look is SWO-616; lightbox is SWO-614

Refs SWO-613.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added a responsive photo timeline that groups photos by month and supports loading and empty states.
    • Added photo cards with navigation, hover styling, and square layouts.
    • Added blur-hash image placeholders with smooth image fade-in.
    • Added responsive photo skeletons while content loads.
  • Documentation
    • Added Storybook examples for photo timelines, photo cards, and blurred image loading.
  • Tests
    • Added coverage for timeline grouping, responsive layouts, and photo navigation.

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>
@shinaBR2 shinaBR2 self-assigned this Jul 25, 2026
@changeset-bot

changeset-bot Bot commented Jul 25, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 7d776eb

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying watch with  Cloudflare Pages  Cloudflare Pages

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

View logs

@coderabbitai

coderabbitai Bot commented Jul 25, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@shinaBR2, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 50 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: dd8bdb02-7673-4a85-8bf5-79dcbfc6cc29

📥 Commits

Reviewing files that changed from the base of the PR and between 5ae624a and 7d776eb.

📒 Files selected for processing (4)
  • packages/ui/src/look/home-page/container/index.tsx
  • packages/ui/src/look/home-page/utils.test.ts
  • packages/ui/src/look/home-page/utils.ts
  • packages/ui/src/look/photos/blur-image/blurhash.ts
📝 Walkthrough

Walkthrough

Adds 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.

Changes

Photo UI

Layer / File(s) Summary
Photo presentation primitives
packages/ui/package.json, packages/ui/src/look/photos/...
Adds generic photo link contracts, blurhash decoding, fade-in image loading, PhotoCard, PhotoSkeleton, and their Storybook stories.
Timeline grouping and row model
packages/ui/src/look/home-page/columns.ts, packages/ui/src/look/home-page/utils.ts, packages/ui/src/look/home-page/utils.test.ts
Adds breakpoint column configuration and tested helpers for month grouping, timeline row construction, responsive column resolution, and photo route parameters.
Virtualized timeline rendering
packages/ui/src/look/home-page/container/..., packages/ui/src/look/home-page/empty-state/index.tsx
Adds responsive virtualized rendering with monthly headers, linked photo cards, loading skeletons, empty-state output, and Storybook scenarios.

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
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: a virtualized month timeline grid with blur-hash images.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch swo-613-look-timeline

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Jul 25, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

Impacted file tree graph

@@            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     
Files with missing lines Coverage Δ
packages/ui/src/look/home-page/columns.ts 100.00% <100.00%> (ø)
packages/ui/src/look/home-page/utils.ts 100.00% <100.00%> (ø)

... and 1 file with indirect coverage changes


Continue to review full report in Codecov by Harness.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 8502139...7d776eb. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 8502139 and 5ae624a.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (15)
  • packages/ui/package.json
  • packages/ui/src/look/home-page/columns.ts
  • packages/ui/src/look/home-page/container/index.stories.tsx
  • packages/ui/src/look/home-page/container/index.tsx
  • packages/ui/src/look/home-page/empty-state/index.tsx
  • packages/ui/src/look/home-page/utils.test.ts
  • packages/ui/src/look/home-page/utils.ts
  • packages/ui/src/look/photos/blur-image/blurhash.ts
  • packages/ui/src/look/photos/blur-image/index.stories.tsx
  • packages/ui/src/look/photos/blur-image/index.tsx
  • packages/ui/src/look/photos/blur-image/styled.tsx
  • packages/ui/src/look/photos/photo-card/index.stories.tsx
  • packages/ui/src/look/photos/photo-card/index.tsx
  • packages/ui/src/look/photos/photo-card/skeleton.tsx
  • packages/ui/src/look/photos/types.ts

Comment thread packages/ui/src/look/home-page/container/index.tsx
Comment thread packages/ui/src/look/home-page/utils.test.ts
Comment thread packages/ui/src/look/photos/blur-image/blurhash.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>
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