feat(call): Discord-style polish pass (groups A-C)
A — Call core: - Deafen now implies mute + remembers pre-deafen mic state so un-deafen restores it (Discord-parity). Peers still see the headphones-off + mic-off badges in sync via the existing data-channel broadcast. - Self-join sound fires on the local peer's r.connect() too, not just on remote ParticipantConnected, so the user gets the "I'm in" cue. - New CallState.reconnecting holds the UI steady when LiveKit drops the signaling socket and retries; duration keeps ticking, status label switches to "Verbinde neu…". Full teardown only on terminal Disconnected (after LK gives up). - joinActiveCall falls back to connected after 5s if no peer arrived — avoids hanging in "Verbinde…" when peers left the room mid-rejoin. B — Ringtone: - Oscillator base gain up (incoming 0.22 -> 0.4, outgoing 0.14 -> 0.22) so the default pattern survives laptop speakers + background music. - New ringtoneVolume slider in Settings, default 0.9, live-applies to both the oscillator fallback and the custom-file <audio> element. C — Participant tile: - Split the speaking indicator: video tiles get the emerald border + inset glow; audio tiles rely on the existing avatar pulse. No more double-chrome when someone talks in grid/focus view. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -99,6 +99,7 @@ export function InCallPanel({ conversation }: Props) {
|
||||
const active =
|
||||
(state.kind === 'connected' ||
|
||||
state.kind === 'connecting' ||
|
||||
state.kind === 'reconnecting' ||
|
||||
state.kind === 'outgoing') &&
|
||||
state.conversationId === conversation.id;
|
||||
if (!active) return null;
|
||||
@@ -117,6 +118,9 @@ export function InCallPanel({ conversation }: Props) {
|
||||
remoteSharerIds: new Set(remoteScreenShares.map((s) => s.participantId)),
|
||||
});
|
||||
|
||||
// Duration keeps ticking during reconnecting so the user sees the call is
|
||||
// still alive — but the status label below takes precedence in the header
|
||||
// so the "Verbinde neu…" message is prominent, not buried under the timer.
|
||||
const duration =
|
||||
state.kind === 'connected'
|
||||
? <LiveDuration startedAt={state.startedAt} />
|
||||
@@ -127,9 +131,11 @@ export function InCallPanel({ conversation }: Props) {
|
||||
? t('app:call.outgoing_ringing')
|
||||
: state.kind === 'connecting'
|
||||
? t('app:call.connecting')
|
||||
: remoteParticipants.length === 0
|
||||
? t('app:call.waiting_for_peers', { defaultValue: 'Warte auf andere…' })
|
||||
: t('app:call.connected');
|
||||
: state.kind === 'reconnecting'
|
||||
? t('app:call.reconnecting', { defaultValue: 'Verbinde neu…' })
|
||||
: remoteParticipants.length === 0
|
||||
? t('app:call.waiting_for_peers', { defaultValue: 'Warte auf andere…' })
|
||||
: t('app:call.connected');
|
||||
|
||||
// A screen-share tile becomes the auto-focus target when no one explicitly
|
||||
// picked a tile yet. Focus ids now use Tile.id (kind-prefixed) so we can
|
||||
|
||||
Reference in New Issue
Block a user