Skip to content

refactor(hooks): one optimistic-toggle hook behind follow, block and blog-follow - #376

Merged
PastaPastaPasta merged 3 commits into
stagingfrom
refactor/toggle-relation
Sep 7, 2026
Merged

refactor(hooks): one optimistic-toggle hook behind follow, block and blog-follow#376
PastaPastaPasta merged 3 commits into
stagingfrom
refactor/toggle-relation

Conversation

@PastaPastaPasta

Copy link
Copy Markdown
Owner

Summary

  • hooks/use-toggle-relation.ts: one hook owning the optimistic flip, rollback on failure, shared TTL cache, request-counter guard, login prompt for logged-out viewers, and the self-target guard. useFollow, useBlock and useBlogFollow are now thin wrappers with unchanged public signatures.
  • lib/caches/status-cache.ts: a viewer-scoped StatusCache class replaces the three hand-rolled follow/block/blog-follow map+TTL triples in user-status-cache.ts; the batch enrichers (use-progressive-enrichment, post-enrichment-helpers, blog-discovery) seed it via seed(), and use-media-gate reads it via get().
  • Follow-up commit from review: settle isLoading and adopt the prefetched value when initialValue arrives after the hook's own query started (the normal post-card path via progressive enrichment), reset isOn on logout (matches the previous blog-follow behaviour), narrow selfError instead of casting, and make StatusCache.key private.

Part of the anti-slop cleanup series (after #370#375).

Test plan

  • npm run lint (zero warnings), tsc --noEmit, npm run test, npx knip, npm run build all green locally
  • CI
  • Manual: follow/unfollow from a feed card while enrichment is still loading; block from profile; blog follow count moves with the toggle and rolls back on failure

🤖 Generated with Claude Code

PastaPastaPasta and others added 2 commits September 7, 2026 14:43
…blog-follow

useFollow, useBlock and useBlogFollow were the same 150-line hook three times: read the shared cache, query if missing, flip optimistically, write, roll back and toast on failure, prompt for login when logged out. They diverged only in the service calls, the copy, the self-target guard, and blog-follow's follower counter. hooks/use-toggle-relation.ts is that hook once, parameterised by those things; the three public hooks keep their exact signatures and become 40-60 lines of wiring each.

The guard that useBlogFollow alone had (a cancelled flag so a slow status check cannot land after the subject changed) is now a request counter every relation gets. The follower counter follows the flip through onOptimistic/onRollback instead of being threaded through the toggle's closure.

lib/caches/user-status-cache.ts had the same TTL-Map block three times (get/set/delete/seed, ~35 lines each). lib/caches/status-cache.ts is a StatusCache class instantiated three times; batch enrichers call .seed() on the instance instead of a per-relation seed function, and the viewer:subject key is built in one place instead of by every caller.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…t on logout

A post card mounts useFollow before progressive enrichment resolves, so initialValue goes undefined -> boolean while the hook's own query is in flight. The effect cleanup orphaned that query and the early return never cleared isLoading, leaving the button disabled. Adopt the prefetched value (or a newer cache entry) and settle. Also clear isOn when the viewer logs out, matching the previous blog-follow behaviour, narrow selfError instead of casting, and make StatusCache.key private.
@coderabbitai

coderabbitai Bot commented Sep 7, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Team

Run ID: bb1381a8-ebb4-4838-933d-1a4f0da325d1

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

load() reruns whenever initialValue changes; if that lands while turnOn/turnOff is still pending it cleared isLoading early, reopening the toggle guard to a duplicate click. Track the pending write in a ref and let its finally block settle state.
@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying yappr-v2 with  Cloudflare Pages  Cloudflare Pages

Latest commit: d2d786f
Status:⚡️  Build in progress...

View logs

@PastaPastaPasta
PastaPastaPasta merged commit 6f16e35 into staging Sep 7, 2026
8 of 9 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.

1 participant