Phase 2 Interactive Likes Design
Phase 2 Interactive Likes Design
Goal
Enable interactive likes (like/unlike) for authenticated users and an in-context login modal for unauthenticated users, scoped to a site (not individual pages), while keeping the existing Like row placement.
Architecture
Likes are stored in a separate Like table keyed by siteId + userId with a unique constraint. A public TRPC query returns { count, likedByViewer } for a site. A protected TRPC mutation toggles the like row and returns the updated count/state. UI optimistically updates and rolls back on errors. Unauthenticated clicks open a modal containing the existing login page content; after session is established, the client completes the pending like action.
Components and data flow
LikeRowbecomes interactive (client component or client wrapper). It receivessiteIdfromsiteMetadataand calls TRPC.- TRPC
like.getStatus({ siteId })is public and returns the total count and whether the current viewer has liked. - TRPC
like.toggle({ siteId })is protected. It creates or deletes the like row and returns the updated count/state. - For unauthenticated users, clicking Like opens a modal that reuses the
/loginpage content (extracted to a shared component). After login, the Like action is retried automatically and the modal closes. - Unique constraint conflicts are treated as success or reconciled by re-reading status.
Error handling
- Mutation errors show a toast and revert optimistic UI changes.
- Unauthenticated users are guided to GitHub login via the modal without leaving the page.
Testing strategy
- Unit tests for LikeRow: render states, unauthenticated click opens modal, authenticated toggle updates UI.
- TRPC router tests for toggle semantics and count correctness.
- E2E tests optional later due to auth env dependencies.