Skip to content

feat: optimistic message rendering with reconcile#94

Merged
ImpulseB23 merged 2 commits intomainfrom
feat/optimistic-send
Apr 20, 2026
Merged

feat: optimistic message rendering with reconcile#94
ImpulseB23 merged 2 commits intomainfrom
feat/optimistic-send

Conversation

@ImpulseB23
Copy link
Copy Markdown
Owner

Closes #88. Stacked on #93.

Adds an optimistic-send path so locally-authored messages render the moment the user hits Enter, without waiting for the platform echo round-trip.

Frontend (chatStore)

  • ChatMessage gains optional status (pending|failed), local_id, error_message
  • New API: insertPending, confirmPendingId, failPending, retryPending, plus messageRevision signal for in-place mutation
  • Reconcile incoming echo against pending tail by id (after confirm) or fingerprint (platform + username + text + 30s window)
  • Pending arrival_seq sourced from MAX_SAFE_INTEGER - 1M base so they sort to tail in any future cross-platform sort layer
  • buildOptimisticMessage helper builds the pending entry from {platform, login, text}

Frontend (UI)

  • MessageInput now takes the user login, builds optimistic, inserts, then sends; on success calls confirmPendingId, on failure calls failPending
  • ChatFeed subscribes to messageRevision and clears its prepared-layout cache on bump; pending entries render at 0.55 opacity, failed entries get a red left-border + click-to-retry
  • twitchAuth.sendMessage now returns Promise<{ message_id }>

Backend

  • twitch_send_message returns Result<SendChatOk, SendCommandError> with the Helix message_id so the frontend can swap the local id for the authoritative one before the echo arrives

Tests

  • 11 new chatStore tests cover insertPending, reconcile-by-id, reconcile-by-fingerprint, fingerprint window expiry, cross-platform isolation, failPending, retryPending state machine, scan-tail bound, multi-pending ordering
  • All 73 vitest tests + 187 cargo tests pass; clippy/fmt/tsc/eslint/prettier clean

Copilot AI review requested due to automatic review settings April 20, 2026 21:17
@github-actions github-actions Bot added rust Rust/Tauri changes typescript Frontend/TypeScript changes size/l 500-1000 lines changed labels Apr 20, 2026
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds an optimistic-send path so locally-authored Twitch messages render immediately on Enter, then reconcile in-place with the authoritative platform echo (using Helix message_id when available, otherwise a bounded fingerprint scan).

Changes:

  • Extend chatStore with pending/failed message state, reconciliation, and a mutation signal for in-place updates.
  • Update UI to insert pending messages on submit, dim pending entries, and allow click-to-retry for failed sends.
  • Return Helix message_id from the Rust twitch_send_message command and surface it through the frontend sendMessage API.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
apps/desktop/src/stores/chatStore.ts Adds optimistic message model fields, pending insert/confirm/fail/retry APIs, reconcile logic, and messageRevision.
apps/desktop/src/stores/chatStore.test.ts Adds unit tests covering optimistic insert + reconciliation behaviors and state transitions.
apps/desktop/src/lib/twitchAuth.ts Changes sendMessage to return { message_id } on success.
apps/desktop/src/components/MessageInput.tsx Inserts optimistic pending message before sending; confirms/fails pending based on command result; now requires login prop.
apps/desktop/src/components/ChatFeed.tsx Invalidates prepared-layout cache on message mutations; renders pending/failed styling and supports click-to-retry.
apps/desktop/src/App.tsx Wires authenticated login into MessageInput.
apps/desktop/src-tauri/src/sidecar_commands.rs Updates twitch_send_message to return a success payload containing message_id.
apps/desktop/src-tauri/src/host.rs Documents SendChatResult.message_id as frontend-consumed for optimistic reconciliation.
Comments suppressed due to low confidence (1)

apps/desktop/src/stores/chatStore.ts:222

  • The createChatStore doc comment says there is a “single viewport signal”, but this change introduces an additional messageRevision signal. Update the comment to reflect the new reactive surface (or consolidate to one signal if that’s the intent).
/**
 * Creates a chat store backed by a plain pre-allocated ring buffer. Writes
 * happen synchronously; the single viewport signal is batched into one
 * `requestAnimationFrame` tick so multiple batches arriving within the same
 * frame coalesce into exactly one reactive update.

Comment thread apps/desktop/src/stores/chatStore.ts
Comment thread apps/desktop/src/stores/chatStore.ts
Comment thread apps/desktop/src/components/MessageInput.tsx
Comment thread apps/desktop/src/components/ChatFeed.tsx Outdated
Comment thread apps/desktop/src/stores/chatStore.ts Outdated
Comment thread apps/desktop/src/stores/chatStore.ts
@ImpulseB23 ImpulseB23 force-pushed the feat/optimistic-send branch from b03bb1e to b9a1c40 Compare April 20, 2026 21:47
Base automatically changed from feat/unified-ordering to main April 20, 2026 22:23
@ImpulseB23 ImpulseB23 force-pushed the feat/optimistic-send branch from b9a1c40 to 8bc22a5 Compare April 20, 2026 22:26
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 20, 2026

Codecov Report

❌ Patch coverage is 86.79245% with 14 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
apps/desktop/src/stores/chatStore.ts 86.59% 6 Missing and 7 partials ⚠️
apps/desktop/src-tauri/src/sidecar_commands.rs 88.88% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@ImpulseB23 ImpulseB23 merged commit ea71c75 into main Apr 20, 2026
13 checks passed
@ImpulseB23 ImpulseB23 deleted the feat/optimistic-send branch April 20, 2026 22:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

rust Rust/Tauri changes size/l 500-1000 lines changed typescript Frontend/TypeScript changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: optimistic message rendering for sent messages

2 participants