feat(user): add updates feed for followed players#83
Conversation
Replace the homepage "Activity feed coming soon" placeholder with a feed of recent revisions authored by followed users on public decks. Attribution is by editor (DeckRevision.userId), so a followed user's collaborator edits on other people's public decks surface too. Scoped to visibility=PUBLIC, kind=DECK, matching the discovery convention. Key changes: - getFollowingUpdates cached under userFollowingTag so follow/unfollow refreshes instantly; deck edits ride minutes-level staleness - summarizeDeltas helper for the +N/-M/K-changes row summary - (user_id, updated_at DESC) index on deck_revision for the feed query - UpdatesFeed server component with zero-follow and no-updates empty states plus a CLS-safe skeleton
|
Warning Review limit reached
Next review available in: 34 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 (5)
📝 WalkthroughWalkthroughAdds a cached feed of recent public deck revisions from followed users, revision delta summaries and per-editor grouping, database indexing, revision highlighting, tests, and home-page rendering with empty and loading states. ChangesUpdates feed
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant Viewer
participant HomeView
participant UpdatesFeed
participant getFollowingUpdates
participant Prisma
participant DeckHistory
Viewer->>HomeView: open home page
HomeView->>UpdatesFeed: render with viewer ID
UpdatesFeed->>getFollowingUpdates: load followed-user updates
getFollowingUpdates->>Prisma: query follows and public revisions
Prisma-->>getFollowingUpdates: revisions and editor profiles
getFollowingUpdates-->>UpdatesFeed: feed items
UpdatesFeed-->>Viewer: render update rows
Viewer->>DeckHistory: open revision history link
DeckHistory-->>Viewer: highlight selected revision
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 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 |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
lib/user/feed.ts (1)
49-63: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winPagination filtering can reduce visible items below FEED_PAGE_SIZE.
The query fetches 10 revisions, then filters out those with missing editors or empty/malformed changes (lines 84-86). If several are filtered, the user sees fewer than 10 items even when more valid revisions exist. This is a reasonable v1 trade-off, but if the filter rate is high (e.g., deleted users, malformed payloads), consider over-fetching (e.g.,
take: FEED_PAGE_SIZE * 3) and slicing toFEED_PAGE_SIZEafter filtering, or pushing thechangesnon-empty check into the database query.🤖 Prompt for 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. In `@lib/user/feed.ts` around lines 49 - 63, Over-fetch revisions in the feed query to compensate for entries removed by later validation: update the `take` value in the `prisma.deckRevision.findMany` call to a larger multiple such as `FEED_PAGE_SIZE * 3`, then filter invalid revisions and slice the resulting list to `FEED_PAGE_SIZE` before returning it.
🤖 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
`@prisma/migrations/20260710212937_deck_revision_user_updated_at_idx/migration.sql`:
- Around line 1-2: Change the index statement in migration
deck_revision_user_updated_at_idx to CREATE INDEX CONCURRENTLY while preserving
the existing index name, table, columns, and sort order. Ensure this migration
is executed outside a transaction, configuring the Prisma migration workflow as
needed.
---
Nitpick comments:
In `@lib/user/feed.ts`:
- Around line 49-63: Over-fetch revisions in the feed query to compensate for
entries removed by later validation: update the `take` value in the
`prisma.deckRevision.findMany` call to a larger multiple such as `FEED_PAGE_SIZE
* 3`, then filter invalid revisions and slice the resulting list to
`FEED_PAGE_SIZE` before returning it.
🪄 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: 76726b3b-2c7e-4608-8f28-9084a573e2cf
📒 Files selected for processing (8)
app/_components/home/home-view.tsxapp/_components/home/updates-feed.tsxlib/deck/__tests__/revision.test.tslib/deck/revision.tslib/user/__tests__/feed.test.tslib/user/feed.tsprisma/migrations/20260710212937_deck_revision_user_updated_at_idx/migration.sqlprisma/schema.prisma
Scope the 5-minute revision merge window per editor so collaborator edits are attributed to their own author instead of being merged into another editor's revision — the feed attributes by DeckRevision.userId, so cross-editor merges mis-attributed or hid collaborator activity. Also: - Match the feed skeleton to the real list (10 rows from FEED_PAGE_SIZE, 64px rows, same bordered divide-y container) to avoid CLS while streaming. - Rewrite the deck_revision index migration to the repo's CONCURRENTLY convention (IF NOT EXISTS inline, manual CONCURRENTLY rollout notes for production). - Drop unused editor.name/image from FeedItem and its query. - Update history page copy to reflect the per-editor merge window.
Clicking an updates feed row now opens the deck's history page deep-linked to that revision, which scrolls into view with a persistent highlight ring. Key changes: - History page accepts ?revision=<id>, awaited inside the existing Suspense boundary and threaded down to DeckHistoryList - Matching RevisionCard scrolls into view (smooth, centered) and shows a ring-2 ring-primary highlight; unknown ids are ignored - Feed rows use a stretched-link overlay to the revision URL with pointer cursor and hover background; inner editor/deck links stay independently clickable - Fix pre-existing hydration mismatch on the history <time> title (server/client timezone) via suppressHydrationWarning
The "Jump back in" strip capped grid tracks at 360px, so rows with few decks occupied only part of the container while the popular decks grid below spanned full width, unbalancing the page. The fix: - Change grid tracks from minmax(260px,360px) to minmax(260px,1fr) so auto-fit columns grow to fill the row - Update the matching skeleton grid in landing-view so the Suspense fallback layout matches streamed content (no CLS)
Feed rows only showed a +N/−N summary; the actual changes required navigating to the deck's history page. Since FeedItem already carries the full RevisionDelta[] (the summary is computed from it), the diff can render inline with no new fetch or API surface. Key changes: - Extract shared RevisionDiff renderer from RevisionCard, with a renderRowStart slot for history's partial-revert checkbox - Add components/ui/collapsible.tsx wrapper over @base-ui/react - Add FeedRowExpand client component: chevron trigger + panel, both positioned relative so the row's overlay link doesn't swallow clicks - Deck history behavior unchanged (revert all/partial verified)
Description
Replaces the homepage "Activity feed coming soon" placeholder with an updates feed: recent changes that players you follow have made to public decks.
Fixes: #28
Attribution is by editor, not deck owner. The feed filters
DeckRevision.userId IN (followed ids)so a followed user's collaborator edits (#27) on other people's public decks surface too — matching the issue text ("changes they've made"). Always constrained tovisibility=PUBLIC, kind=DECK, so private edits and public wishlists stay out, per the discovery convention.Caching:
getFollowingUpdatesis cached underuserFollowingTag(viewerId)withcacheLife("minutes"). Follow/unfollow already bumps that tag, so the one invalidation that must feel instant is free; deck edits ride the minutes-level staleness rather than fanning tag invalidation out to every follower on the hotapplyDeckMutationpath.No FK added to
DeckRevision— editors are resolved with a second batcheduser.findMany, mirroring the existing two-step pattern inlib/user/queries.ts. Revisions with a missing editor or malformedchangespayload are skipped, never rendered broken.graph TD HV[home-view.tsx] -->|Suspense| UF[UpdatesFeed] UF --> GFU[getFollowingUpdates<br/>lib/user/feed.ts] GFU -->|1. follows| F[(follow)] GFU -->|2. revisions by editor,<br/>PUBLIC + DECK only| DR[(deck_revision<br/>new index: user_id, updated_at DESC)] GFU -->|3. batched editor lookup| U[(user)] UF --> SD[summarizeDeltas<br/>lib/deck/revision.ts] FA[follow/unfollow action] -->|updateTag userFollowingTag| GFUWhat changed
lib/user/feed.ts(new) —getFollowingUpdates(viewerId): cached feed query, page of 10, editor-attributedlib/deck/revision.ts—summarizeDeltashelper for the+N −M · K changesrow summaryapp/_components/home/updates-feed.tsx(new) — server component with zero-follow and no-recent-updates empty states + skeletonapp/_components/home/home-view.tsx— placeholder replaced with<Suspense>-wrapped feedprisma/schema.prisma+ migration —(user_id, updated_at DESC)index ondeck_revisionserving the feed'sIN … ORDER BY … LIMITshapeChecklist
pnpm test) and lint is cleanrevalidate/dynamic/unstable_cache— used'use cache'+cacheLife/cacheTag<Link>imported fromapp/_components/link.tsx, notnext/linkScreenshots / notes
Verified E2E in the browser with a fresh account:
/u/[username]and deck →/deck/[id], withTimeAgoand colored+N/−M · K changessummarykind=DECKdecks surfaceUnit coverage:
lib/user/__tests__/feed.test.ts(early return, query shape, editor dedupe, missing-editor skip, malformed-payload skip, cache tag) andsummarizeDeltascases inlib/deck/__tests__/revision.test.ts. Coverage gate (100 lines / 99 branches) passes.Summary by CodeRabbit