Skip to content

feat(user): add updates feed for followed players#83

Merged
jcserv merged 5 commits into
mainfrom
jarrod/28-updates-feed
Jul 11, 2026
Merged

feat(user): add updates feed for followed players#83
jcserv merged 5 commits into
mainfrom
jarrod/28-updates-feed

Conversation

@jcserv

@jcserv jcserv commented Jul 10, 2026

Copy link
Copy Markdown
Owner

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 to visibility=PUBLIC, kind=DECK, so private edits and public wishlists stay out, per the discovery convention.

Caching: getFollowingUpdates is cached under userFollowingTag(viewerId) with cacheLife("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 hot applyDeckMutation path.

No FK added to DeckRevision — editors are resolved with a second batched user.findMany, mirroring the existing two-step pattern in lib/user/queries.ts. Revisions with a missing editor or malformed changes payload 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| GFU
Loading

What changed

  • lib/user/feed.ts (new) — getFollowingUpdates(viewerId): cached feed query, page of 10, editor-attributed
  • lib/deck/revision.tssummarizeDeltas helper for the +N −M · K changes row summary
  • app/_components/home/updates-feed.tsx (new) — server component with zero-follow and no-recent-updates empty states + skeleton
  • app/_components/home/home-view.tsx — placeholder replaced with <Suspense>-wrapped feed
  • prisma/schema.prisma + migration — (user_id, updated_at DESC) index on deck_revision serving the feed's IN … ORDER BY … LIMIT shape

Checklist

  • Tests pass (pnpm test) and lint is clean
  • New behavior is covered by tests
  • No revalidate / dynamic / unstable_cache — used 'use cache' + cacheLife/cacheTag
  • <Link> imported from app/_components/link.tsx, not next/link
  • Suspense fallbacks reserve layout space (explicit heights)

Screenshots / notes

Verified E2E in the browser with a fresh account:

  • Zero-follow state shows "Follow players to see their deck updates here" linking to explore
  • Following a user populates the feed immediately (tag invalidation); unfollowing empties it immediately; re-following restores it
  • Rows link editor → /u/[username] and deck → /deck/[id], with TimeAgo and colored +N / −M · K changes summary
  • Revisions on a PRIVATE deck never appear; only PUBLIC kind=DECK decks surface
  • Skeleton rows are fixed 56px, matching the real row height — no CLS while streaming

Unit coverage: lib/user/__tests__/feed.test.ts (early return, query shape, editor dedupe, missing-editor skip, malformed-payload skip, cache tag) and summarizeDeltas cases in lib/deck/__tests__/revision.test.ts. Coverage gate (100 lines / 99 branches) passes.

Summary by CodeRabbit

  • New Features
    • Added an Updates feed to the home screen showing recent deck changes from followed players, with empty states for no followed players and no recent updates.
    • Feed items show editor/deck details, relative update time, deck format, and plus/minus change counts.
  • Bug Fixes
    • Improved feed resilience against incomplete or malformed update data.
    • Ensured revision merging behavior is scoped per editor to avoid cross-user mixing.
    • Deck history can now highlight a specific revision and auto-scroll to it.
  • Documentation
    • Updated deck history grouping wording to reflect “same player within 5 minutes.”
  • Performance
    • Added database indexes to speed up recent updates queries.

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
@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

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

Next review available in: 34 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: cdc2777b-f8e1-4b8f-8569-bb72c0371acb

📥 Commits

Reviewing files that changed from the base of the PR and between b6d90b1 and 62a72be.

📒 Files selected for processing (5)
  • app/_components/deck/deck-history-list.tsx
  • app/_components/deck/revision-diff.tsx
  • app/_components/home/feed-row-expand.tsx
  • app/_components/home/updates-feed.tsx
  • components/ui/collapsible.tsx
📝 Walkthrough

Walkthrough

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

Changes

Updates feed

Layer / File(s) Summary
Revision grouping and summarization
lib/deck/revision.ts, lib/deck/mutation/revision.ts, lib/deck/**/__tests__/revision.test.ts
Adds revision delta aggregation, scopes revision merging to the editing user, and updates related tests.
Following updates retrieval
lib/user/feed.ts, lib/user/__tests__/feed.test.ts, prisma/schema.prisma, prisma/migrations/.../migration.sql
Retrieves cached public deck revisions from followed users, resolves editors, parses changes, filters invalid items, validates query behavior, and adds a user/update index.
Revision history navigation
app/(ui)/deck/[id]/history/page.tsx, app/_components/deck/deck-history-list.tsx
Passes a revision query parameter into history, highlights the matching revision, scrolls it into view, and clarifies grouping behavior.
Home updates feed rendering
app/_components/home/home-view.tsx, app/_components/home/updates-feed.tsx, app/_components/home/deck-strip.tsx, app/_components/home/landing-view.tsx
Replaces the activity placeholder with a suspense-wrapped updates feed and adjusts home grid sizing for flexible columns.

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
Loading

Possibly related PRs

  • jcserv/maindeck#81: Introduces the follow relationships and cache tagging used by the updates feed.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning Several unrelated changes are included, including revision-merge scoping, deck strip/grid tweaks, and landing skeleton updates beyond the feed feature. Move the deck-history, revision-scope, and layout/skeleton tweaks into separate PRs unless they are required for the updates feed.
✅ Passed checks (4 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: adding an updates feed for followed players.
Linked Issues check ✅ Passed The PR implements the requested main-page updates section showing recent changes from followed users to public decks.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch jarrod/28-updates-feed

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.

@jcserv jcserv self-assigned this Jul 10, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
lib/user/feed.ts (1)

49-63: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Pagination 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 to FEED_PAGE_SIZE after filtering, or pushing the changes non-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

📥 Commits

Reviewing files that changed from the base of the PR and between 8179360 and 37560d0.

📒 Files selected for processing (8)
  • app/_components/home/home-view.tsx
  • app/_components/home/updates-feed.tsx
  • lib/deck/__tests__/revision.test.ts
  • lib/deck/revision.ts
  • lib/user/__tests__/feed.test.ts
  • lib/user/feed.ts
  • prisma/migrations/20260710212937_deck_revision_user_updated_at_idx/migration.sql
  • prisma/schema.prisma

Comment thread prisma/migrations/20260710212937_deck_revision_user_updated_at_idx/migration.sql Outdated
jcserv added 4 commits July 10, 2026 21:14
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)
@jcserv jcserv merged commit 8b01e0c into main Jul 11, 2026
5 checks passed
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.

[Feature]: Updates feed

1 participant