Skip to content

fix(frontend): no-device default, program role, presets, URL order, and Add-Line UX#618

Merged
birme merged 8 commits intomainfrom
frontend/fix-no-device-default
Mar 30, 2026
Merged

fix(frontend): no-device default, program role, presets, URL order, and Add-Line UX#618
birme merged 8 commits intomainfrom
frontend/fix-no-device-default

Conversation

@LucasMaupin
Copy link
Copy Markdown
Contributor

@LucasMaupin LucasMaupin commented Mar 27, 2026

Summary

  • Device default bug: first-time users got "no-device" as audio input ID causing 500 errors. A useEffect resets the field once device enumeration completes; use-submit-form.tsx guards against falsy/"no-device" values.
  • Program feed UX: Listener / Audio feed role selector shown in join form when a program output line is selected. isProgramUser state lifted to JoinProduction.
  • Program line auto-join race: pending program keys tracked in a ref updated synchronously (not stale state).
  • Companion URL sync: connecting to companion via modal updates ?companion= in the address bar; disconnecting removes it. Fixed stale useMemo([], []) in use-calls-navigation that was stripping the param on every calls change. Share modal receives the active companion URL in both URL-param and manual-connect cases.
  • Companion URL stripping fix: useCallsNavigation now reads the companion param from window.location.search (live URL) rather than the stale React searchParams snapshot, preventing the param from being dropped when a calls-state effect fires immediately after handleCompanionUrlChange writes the new URL. handleCompanionUrlChange also switched to currentCallRefs (not pendingCallRefs) for a consistent URL write. ConnectToWSButton stores the auto-connect URL in connectingUrlRef before the timer fires, fixing a race where the ref was unset during the delay.
  • Preset role auto-join: program output lines in presets now encode their role as :l (listener) or :p (audio feed) in the ?lines= URL param. When a role is present the line is auto-joined immediately — no ProgramLineJoinCard shown. Lines without a role suffix are unaffected (fully backwards compatible). Pairs with backend PR feat(presets): add isProgramUser field to PresetCall schema intercom-manager#213 which adds isProgramUser to the PresetCall schema.
  • Preset role editing in manage page: inline Listener / Audio feed radio selector on program output lines in ManagePresetCard, both for existing calls and when adding a new call. TPresetCall type exported from api.ts and used consistently across use-presets, use-local-presets, create-production-page, and manage-presets-list.
  • Add-Line loading flicker eliminated: CallsPage pre-fetches the selected production and full production list eagerly, passing them as props through JoinProductionUserSettingsForm. The line dropdown appears instantly without a loading state.
  • Line dropdown defaults to first unjoined line: when the "Add Line" form opens, UserSettingsForm skips already-joined lines for the selected production and defaults to the first one the user has not yet joined.
  • URL order staleness fix: useCallsNavigation now reads call order from the live window.location.search instead of the frozen pendingCallRefs snapshot, preventing stale-order flashes when calls are added or removed mid-session. production-lines.tsx sorts rendered cards by this same callOrderMap.
  • Closed peer connection guard: attachInputAudioToPeerConnection and the RTC setup effect both bail early if signalingState === "closed", preventing errors when the connection is torn down during re-render.

Test plan

  • Tests pass (npm test)
  • TypeScript compiles (npm run typecheck)
  • Lint clean (npm run lint)
  • Fresh user: join form shows real device, not "no-device"
  • Select program output line in join form: Listener / Audio feed radio buttons appear
  • Connect companion via modal: ?companion=host:port appears and persists in address bar; disconnect removes it
  • Open share modal while companion connected: "Include companion URL" checkbox present and correct
  • Rapidly connect companion then add/remove a call: ?companion= is still present in the URL after the calls-state effect fires
  • Save preset with a program output line joined as Listener → reload preset → line auto-joins as Listener (no role card)
  • Save preset with program line as Audio feed → reload → auto-joins as Audio feed
  • Share URL containing :l/:p suffix → recipient auto-joins with correct role
  • Existing URLs without role suffix work unchanged
  • Manage preset page: program output lines show Listener / Audio feed selector; changing role saves immediately
  • Click "Add Line" button on calls page → production and line dropdowns appear instantly (no spinner)
  • Line dropdown defaults to a line not already joined for the selected production
  • Add a second call, leave, re-add: card order in the call grid matches the URL order

🤖 Generated with Claude Code

Co-Authored-By: Claude Sonnet 4.6 [email protected]

…on, and auto-join race

- Device default bug: first-time users (cleared localStorage) received
  "no-device" as audioinput, causing 500 errors on join. Add a useEffect
  in user-settings-form.tsx that resets the field to the real default
  device once enumeration completes; add a defense-in-depth guard in
  use-submit-form.tsx that falls back to userSettings.audioinput or
  "default" when the resolved value is falsy or "no-device".

- Program feed UX: remove the role selector (Listener / Audio feed
  checkboxes) from the join form entirely. ProgramLineJoinCard is now
  the sole place for role selection, eliminating duplicate state and the
  associated correctness hazard for first-time users.

- Program line auto-join bug: two fixes applied together:
  (1) calls-page.tsx used pendingProgramLines state in the auto-join
  effect, which could be stale at the time the effect ran — replaced
  with a ref (pendingProgramKeysRef) updated synchronously before
  setPendingProgramLines so the effect always reads the latest value.
  (2) use-submit-form.tsx unconditionally called initiateProductionCall
  even for program output lines — added a !selectedLine.programOutputLine
  guard so program lines are not auto-joined before the user picks a role.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
LucasMaupin and others added 4 commits March 27, 2026 14:31
…gn WebRTC modal

- Add TVIcon next to program output lines in presets-list and manage-presets-list
  with isProgramOutput purple background
- Refactor settings-modal.tsx to use the base Modal component, removing duplicated
  modal primitives from settings-modal-components.ts
- Rewrite generate-whip-whep-url-modal.tsx: single username field, WHIP/WHEP copy
  buttons, title changed to "WebRTC"

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Add `presetOrder` field to `CallData` and a `sortedEntries()` helper in
`use-call-list.ts` so CALLS_STATE_UPDATE and CALL_UPDATE messages always
list calls in preset position order rather than insertion order.

Build `callIndexMap` in `calls-page.tsx` using the same preset-order sort
(keyed by productionId:lineId via `presetOrderMap`) so incoming companion
actions (mute, volume, etc.) resolve to the correct call. Remove the
duplicate `callIndexMap` effect from `connect-to-ws-button.tsx` that was
overwriting the sorted map with insertion order on every state change.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
- Update browser URL when user connects/disconnects companion manually
- Remove stale useMemo([], []) for companionUrl in use-calls-navigation so
  the URL-rebuild effect always uses the current companion param instead of
  the frozen mount-time value (which was stripping the param on every calls
  change)
- Pass activeCompanionUrl (manual or from URL param) to ShareUrlModal so
  the "Include companion URL" checkbox works regardless of how the companion
  was connected

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
…lines

Lift isProgramUser state to JoinProduction so the role checkbox persists
across re-renders. Add Listener / Audio feed checkboxes when a program
output line is selected in the join form. Remove the old guard that
blocked initiateProductionCall for program output lines so the role
selection drives the behaviour instead.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
@LucasMaupin LucasMaupin changed the title fix(frontend): fix no-device default, program role duplication, and auto-join race fix(frontend): fix no-device default, program role selector, auto-join race, and companion URL sync Mar 30, 2026
Add optional :l (listener) and :p (audio-feed) role suffix to the
lines URL param (e.g. ?lines=1:3:l,2:7:p). When a role is present the
program line is auto-joined immediately without showing ProgramLineJoinCard.
Lines without a role suffix are unaffected (backwards compatible).

- call-url.ts: add role to CallRef; encode/decode third URL segment
- api.ts: add isProgramUser to TPresetCall
- calls-page.tsx: skip ProgramLineJoinCard for refs with role; auto-join
  with correct lineUsedForProgramOutput/isProgramUser values
- save-preset-modal.tsx: pass isProgramUser per call when saving
- presets-list.tsx: map preset calls to CallRef with role encoding on
  join and share so loaded presets auto-join with the correct role

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
@LucasMaupin LucasMaupin changed the title fix(frontend): fix no-device default, program role selector, auto-join race, and companion URL sync fix(frontend): no-device default, program role selector, auto-join race, companion URL sync, and preset role auto-join Mar 30, 2026
…taleness

Pre-fetch production and production list in CallsPage so the "Add Line"
form renders immediately without waiting for data to load. Default the
line dropdown to the first unjoined line for the selected production.
Fix useCallsNavigation to derive call order from the live URL instead of
the frozen pendingCallRefs snapshot, preventing stale-order flashes when
calls are added or removed. Guard attachInputAudioToPeerConnection and
the RTC setup effect against closed peer connections. Propagate
isProgramUser through TPresetCall in presets-list, manage-presets-list,
create-production-page, use-local-presets, and use-presets so that the
role (Listener / Audio feed) is preserved when editing presets. Add an
inline role selector to ManagePresetCard for program output lines.

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
@LucasMaupin LucasMaupin changed the title fix(frontend): no-device default, program role selector, auto-join race, companion URL sync, and preset role auto-join fix(frontend): no-device default, program role, presets, URL order, and Add-Line UX Mar 30, 2026
…connect timing

- useCallsNavigation now reads companion param from window.location.search
  (live URL) instead of the stale React searchParams snapshot, preventing the
  companion param from being silently dropped when a calls-state effect fires
  immediately after handleCompanionUrlChange writes the new URL
- handleCompanionUrlChange uses currentCallRefs (not pendingCallRefs) so the
  URL it writes is consistent with the already-joined calls
- ConnectToWSButton stores the auto-connect URL in connectingUrlRef before the
  timer fires, fixing a race where the ref was unset during the delay
- Fix TypeScript error in companion-preservation test: use unknown[] + cast
  instead of tuple destructuring on mock.calls (any[][])

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
@birme birme merged commit 0aae3cc into main Mar 30, 2026
6 checks passed
@birme birme deleted the frontend/fix-no-device-default branch March 30, 2026 12:15
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.

2 participants