Skip to content

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

Open
jcserv wants to merge 2 commits into
mainfrom
jarrod/28-updates-feed
Open

feat(user): add updates feed for followed players#83
jcserv wants to merge 2 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.
    • Includes contextual empty states when following no players or when there are no recent updates.
    • Feed items summarize editor/deck info, update time, deck format, and plus/minus change counts.
  • Bug Fixes
    • Improved robustness of the feed to exclude incomplete or malformed update data.
    • Ensured revision merging behavior is scoped per editor to prevent unintended cross-user mixing.
  • Documentation
    • Clarified how revisions are grouped on the deck history page (same player within 5 minutes).

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

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 3d386915-723d-448e-b4d2-0319e6c5bc51

📥 Commits

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

📒 Files selected for processing (7)
  • app/(ui)/deck/[id]/history/page.tsx
  • app/_components/home/updates-feed.tsx
  • lib/deck/mutation/__tests__/revision.test.ts
  • lib/deck/mutation/revision.ts
  • lib/user/__tests__/feed.test.ts
  • lib/user/feed.ts
  • prisma/migrations/20260710212937_deck_revision_user_updated_at_idx/migration.sql
💤 Files with no reviewable changes (2)
  • lib/user/feed.ts
  • lib/user/tests/feed.test.ts
✅ Files skipped from review due to trivial changes (1)
  • app/(ui)/deck/[id]/history/page.tsx

📝 Walkthrough

Walkthrough

Adds a cached feed of recent public deck revisions from followed users, revision delta summaries and per-editor grouping, supporting database indexing, 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, app/(ui)/deck/[id]/history/page.tsx
Adds revision delta aggregation, scopes revision merging to the editing user, updates related tests, and clarifies the five-minute grouping behavior.
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, tests query behavior, and adds a user/update index.
Home updates feed rendering
app/_components/home/home-view.tsx, app/_components/home/updates-feed.tsx
Replaces the activity placeholder with a suspense-wrapped updates feed showing loading, following, empty, and populated states.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Viewer
  participant HomeView
  participant UpdatesFeed
  participant getFollowingUpdates
  participant Prisma

  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 or empty state
Loading

Possibly related PRs

  • jcserv/maindeck#81: Introduces the follow relationships and cache tagging used by the updates feed.
🚥 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 clearly describes the main change: adding an updates feed for followed players.
Linked Issues check ✅ Passed The changes implement a main-page updates section for recent public deck activity from followed users.
Out of Scope Changes check ✅ Passed The additional revision, index, copy, and test changes support the feed and collaborator attribution, so they are in scope.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch jarrod/28-updates-feed

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

app/(ui)/deck/[id]/history/page.tsx

Parsing error: The keyword 'import' is reserved

app/_components/home/updates-feed.tsx

Parsing error: The keyword 'import' is reserved

lib/deck/mutation/__tests__/revision.test.ts

Parsing error: The keyword 'import' is reserved

  • 1 others

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