mirror of
https://github.com/block/buzz.git
synced 2026-11-08 18:31:56 +00:00
Clicking a reaction put the kind:7 on the relay but it never rendered: the GUI showed nothing and re-clicking hit "duplicate: reaction already exists". Root cause is a missing optimistic cache write, not a fetch window or transient timeout. useToggleReactionMutation fired the reaction and did nothing locally — unlike useDeleteMessageMutation and useEditMessageMutation right below it, which both apply the change to the channel cache on success (their comments note the relay round-trip "can lag perceptibly"). For a reaction the round-trip never arrives at all: kind:7 carries only an `e` tag, no `h`, so the `#h`-scoped live subscription (buildChannelFilter) misses it — reactions only enter the cache via the cold-load `#e` aux backfill. So a freshly-clicked reaction stayed invisible until the next channel switch, and re-clicking just re-hit the relay's duplicate guard. Give the reaction mutation the same treatment as edit/delete: an optimistic onMutate that synthesizes the kind:7 (target `e` tag, emoji content, NIP-30 emoji tag for custom emoji, actor pubkey) and writes it into the channel cache, with onError rollback. Render dedupes reactions by target:actor:emoji, so when the genuine event later loads via backfill it collapses onto the optimistic one — no double count. Removal drops the matching optimistic (or backfilled) reaction. Scoped via an optional `channelId` in the mutate variables: channel surfaces (ChannelScreen, Home inbox) pass it; Pulse omits it and keeps its own reaction cache, so a note is never counted in two caches. createOptimisticReaction is a pure module so the contract is unit-tested without React: optimistic reaction renders reactedByCurrentUser, the real backfilled event collapses onto it without double-counting, and a custom-emoji reaction carries its NIP-30 tag. Co-authored-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
Buzz
Desktop chat shell with:
- Tauri + React + TypeScript + Vite
- Tailwind CSS
- shadcn/ui-ready shared components
- Biome (lint/format/check)
- Feature-driven frontend structure
Scripts
pnpm dev- run the web frontendpnpm tauri dev- run the desktop apppnpm build- typecheck and build frontendpnpm typecheck- TypeScript checkspnpm lint- Biome lintpnpm format- Biome format (write)pnpm check- Biome check
Structure
src/shared- reusable app-wide code (ui,lib,styles)src/features- feature modules (vertical slices)src/app- top-level app composition