UI: сворачивание активного звонка

This commit is contained in:
AidarKC
2026-07-24 15:00:01 +04:00
parent 5df69d73c7
commit 4d9e42205f
5 changed files with 300 additions and 21 deletions
+86
View File
@@ -554,6 +554,8 @@ function getCallStateSnapshot() {
direction: call.direction || 'out',
phase: callPhase,
statusText: call.statusText || '',
startedAtMs: Number(call.startedAtMs || 0),
connectedAtMs: Number(call.connectedAtMs || 0),
muted: Boolean(call.muted),
canAnswer: callPhase === 'incoming',
canDecline: callPhase === 'incoming',
@@ -658,6 +660,7 @@ function buildCallFactsLine(call, extra = {}) {
pcSignalingState: pc?.signalingState || '',
hasLocalStream: Boolean(call?.localStream),
localAudioTracksCount: call?.localStream?.getAudioTracks?.()?.length || 0,
localTrackStates: summarizeMediaSnapshot(buildMediaSnapshot(call)),
timeline: buildTimelineSummary(call),
...extra,
};
@@ -666,6 +669,60 @@ function buildCallFactsLine(call, extra = {}) {
.join(', ');
}
function buildMediaSnapshot(call) {
const localStream = call?.localStream || null;
const localTracks = localStream?.getTracks?.() || [];
const remoteAudio = call?.remoteAudio || null;
const remoteStream = remoteAudio?.srcObject || null;
const remoteTracks = remoteStream?.getTracks?.() || [];
return {
hasLocalStream: Boolean(localStream),
localTrackCount: localTracks.length,
localAudioTracksCount: localStream?.getAudioTracks?.()?.length || 0,
localTrackStates: localTracks.map((track) => ({
kind: String(track?.kind || ''),
enabled: Boolean(track?.enabled),
muted: Boolean(track?.muted),
readyState: String(track?.readyState || ''),
label: String(track?.label || ''),
})),
hasRemoteAudio: Boolean(remoteAudio),
hasRemoteAudioSrcObject: Boolean(remoteStream),
remoteTrackCount: remoteTracks.length,
remoteTrackStates: remoteTracks.map((track) => ({
kind: String(track?.kind || ''),
enabled: Boolean(track?.enabled),
muted: Boolean(track?.muted),
readyState: String(track?.readyState || ''),
label: String(track?.label || ''),
})),
};
}
function summarizeMediaSnapshot(snapshot) {
if (!snapshot) return '';
const localSummary = Array.isArray(snapshot.localTrackStates)
? snapshot.localTrackStates.map((track) => (
`${track.kind}:${track.readyState}:${track.enabled ? 'on' : 'off'}:${track.muted ? 'muted' : 'live'}`
)).join('|')
: '';
const remoteSummary = Array.isArray(snapshot.remoteTrackStates)
? snapshot.remoteTrackStates.map((track) => (
`${track.kind}:${track.readyState}:${track.enabled ? 'on' : 'off'}:${track.muted ? 'muted' : 'live'}`
)).join('|')
: '';
return [
`localStream=${snapshot.hasLocalStream ? 1 : 0}`,
`localTracks=${Number(snapshot.localTrackCount || 0)}`,
`localAudio=${Number(snapshot.localAudioTracksCount || 0)}`,
`localStates=${localSummary || '-'}`,
`remoteAudio=${snapshot.hasRemoteAudio ? 1 : 0}`,
`remoteSrc=${snapshot.hasRemoteAudioSrcObject ? 1 : 0}`,
`remoteTracks=${Number(snapshot.remoteTrackCount || 0)}`,
`remoteStates=${remoteSummary || '-'}`,
].join(';');
}
function getCallDiagnosticsContext(call) {
const pc = call?.pc || null;
const nav = typeof navigator !== 'undefined' ? navigator : null;
@@ -686,6 +743,7 @@ function getCallDiagnosticsContext(call) {
const iceGatheringState = pc?.iceGatheringState || '';
const currentLocalDescType = pc?.localDescription?.type || '';
const currentRemoteDescType = pc?.remoteDescription?.type || '';
const mediaSnapshot = buildMediaSnapshot(call);
return {
remoteSessionIdPresent: Boolean(call?.remoteSessionId),
@@ -707,6 +765,7 @@ function getCallDiagnosticsContext(call) {
localAudioTracksCount: localAudioTracks.length,
localAudioTracksEnabledCount: enabledLocalAudioTracks,
localAudioTrackLabels: localAudioTracks.map((t) => String(t?.label || '')).join('|'),
localTrackStates: summarizeMediaSnapshot(mediaSnapshot),
hasPeerConnection: Boolean(pc),
pcConnectionState: pc?.connectionState || '',
pcIceConnectionState: pc?.iceConnectionState || '',
@@ -808,6 +867,8 @@ async function flushPendingIceCandidates(call) {
async function closeMedia(call) {
const pc = call?.pc || null;
const beforeSnapshot = buildMediaSnapshot(call);
recordCallTimeline(call, 'media_close_begin', summarizeMediaSnapshot(beforeSnapshot));
try {
const senders = pc?.getSenders?.() || [];
senders.forEach((sender) => {
@@ -822,6 +883,16 @@ async function closeMedia(call) {
try { tr?.stop?.(); } catch {}
});
} catch {}
try {
if (pc) {
pc.ontrack = null;
pc.onicecandidate = null;
pc.onconnectionstatechange = null;
pc.oniceconnectionstatechange = null;
pc.onsignalingstatechange = null;
pc.onicegatheringstatechange = null;
}
} catch {}
try {
call.localStream?.getTracks?.()?.forEach((track) => {
try { track.enabled = false; } catch {}
@@ -830,8 +901,15 @@ async function closeMedia(call) {
} catch {}
try {
if (call.remoteAudio) {
const remoteTracks = call.remoteAudio.srcObject?.getTracks?.() || [];
remoteTracks.forEach((track) => {
try { track.enabled = false; } catch {}
try { track.stop(); } catch {}
});
try { call.remoteAudio.pause?.(); } catch {}
try { call.remoteAudio.srcObject = null; } catch {}
try { call.remoteAudio.removeAttribute?.('src'); } catch {}
try { call.remoteAudio.load?.(); } catch {}
call.remoteAudio = null;
}
} catch {}
@@ -842,6 +920,14 @@ async function closeMedia(call) {
call.connectionRouteLabel = '';
call.connectionRouteDetails = '';
call.pendingRemoteIceCandidates = [];
const afterSnapshot = buildMediaSnapshot(call);
recordCallTimeline(call, 'media_close_end', summarizeMediaSnapshot(afterSnapshot));
await emitDebug(
call,
'info',
'media_closed',
`before=${summarizeMediaSnapshot(beforeSnapshot)} | after=${summarizeMediaSnapshot(afterSnapshot)}`,
);
}
function stopReconnectFlow(call) {