Skip to content

refactor: shared call-core — one tested reducer behind both call drivers#630

Merged
gbasin merged 1 commit into
masterfrom
refactor/shared-call-core
Jul 22, 2026
Merged

refactor: shared call-core — one tested reducer behind both call drivers#630
gbasin merged 1 commit into
masterfrom
refactor/shared-call-core

Conversation

@gbasin

@gbasin gbasin commented Jul 22, 2026

Copy link
Copy Markdown
Collaborator

Summary

Extracts the pure call bookkeeping web and mobile duplicated (with real drift) into @atrium/surface-client call-core: state types, participant/phase bookkeeping, comparator-parameterized sortLiveCalls, merge-aware upsertUser, enrichment helpers, and the CallEvent reducer as a pure (state, event) → { state, effects } transition. The two useCall hooks remain thin drivers: web keeps the Room + audio-track attachment model; mobile keeps the Room + all 28 CallKit/AudioSession call sites, verified byte-identical and in original order (site-by-site table in the committed design doc). No livekit-client types cross into shared — web and mobile pin different versions.

Deliberate behavior changes

  • Web ring timeout 45s → shared CALL_RING_TTL_MS (60s): the old value raced the server sweeper (premature "No answer")
  • Mobile adopts merge-aware upsertUser: fallback participants now upgrade to resolved users

Hardening found in review: both drivers now advance their state refs immediately after the reducer runs, so back-to-back CallEvents can never read a stale snapshot (the old web code was burst-safe via functional updaters; the ref pattern wasn't until this).

Honest accounting: production LOC rises ~187 (1,667 → 1,854) — preserving both platforms' genuine divergences (sort direction, dismissed-state storage, accepted-branch policy) costs more scaffolding than the deleted duplication saved. Shipped for the drift protection: shared/test/callCore.test.ts (28 tests) pins each platform's semantics across all six call lifecycles, ending the three-copy drift that bred bugs elsewhere in this arc. Design + full analysis: docs/archive/notes/2026-07-21-call-core-design.md.

Validation

  • lint, web+mobile typecheck, build:ci (LiveKit stays a web-only chunk — the packaging constraint held)
  • shared 496 / web 860 / mobile 233 unit tests; full local e2e 120/120 exit 0
  • Two-user browser call smoke against the local LiveKit (fake media devices): start → ring → join → two-party banner with both participant chips → hangup, zero page errors

⚠️ Before this reaches the deploy branch

The CallKit surface (NATIVE_CALL_UI) only runs on a real device. All 28 sites are structurally intact, but run the §1.8 device checklist from the design doc on a real iPhone: system-UI answer/decline/end/mute, AudioSession route restore, VoIP-push → reportIncomingCall, recents grouping.

Extracts the pure call bookkeeping both platforms duplicated (with drift)
into @atrium/surface-client call-core: state types, participant/phase
bookkeeping, comparator-parameterized sortLiveCalls, merge-aware
upsertUser, user enrichment, and the CallEvent reducer as a pure
(state, event) -> { state, effects } transition. The web and mobile
useCall hooks stay as thin drivers owning the LiveKit Room and (mobile)
every CallKit/AudioSession call site — all 28 verified byte-identical
and in original order against the design's site table. No livekit-client
types cross into shared (web and mobile pin different versions).

Deliberate behavior changes:
- web ring timeout 45s -> shared CALL_RING_TTL_MS (60s): the old value
  raced the server sweeper and could show 'No answer' while the server
  still considered the call ringing
- mobile adopts web's merge-aware upsertUser: fallback participants
  upgrade to resolved users instead of being permanently add-only

Also: both drivers advance their state refs immediately after the
reducer runs (before React renders), so back-to-back CallEvents can
never read a stale snapshot and drop a transition; web's third
duplicate (callPresentation.ts) is deleted.

Honest accounting: production LOC rises ~187 (1,667 -> 1,854) — the
scaffolding to preserve both platforms' real divergences costs more
than the removed duplication saved. Shipped for the drift protection:
shared/test/callCore.test.ts (28 tests) pins each platform's semantics
across all six call lifecycles.

Design doc: docs/archive/notes/2026-07-21-call-core-design.md.

Validation: lint/typechecks/build:ci green; shared 496, web 860,
mobile 233 unit tests; e2e 120/120; two-user browser call smoke vs
local LiveKit (start/ring/join/two-party/hangup, zero page errors).
CallKit half needs a real-iPhone pass (28-site checklist in the design
doc) before reaching the deploy branch.
@gbasin
gbasin added this pull request to the merge queue Jul 22, 2026
Merged via the queue into master with commit fbc3bbf Jul 22, 2026
19 checks passed
@gbasin
gbasin deleted the refactor/shared-call-core branch July 22, 2026 08:49
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