UI: стадии и звук звонка

This commit is contained in:
AidarKC
2026-07-24 19:12:18 +04:00
parent 69faf55b2b
commit f31cdb413b
4 changed files with 109 additions and 7 deletions
+59
View File
@@ -37,6 +37,25 @@ function nowMs() {
return Date.now();
}
function isMobileBrowser() {
const uaDataMobile = navigator?.userAgentData?.mobile;
if (typeof uaDataMobile === 'boolean') return uaDataMobile;
const ua = String(navigator?.userAgent || '');
return /Android|iPhone|iPad|iPod|Mobile/i.test(ua);
}
function canRouteAudioOutputInBrowser() {
const mediaProto = globalThis?.HTMLMediaElement?.prototype;
return Boolean(
window.isSecureContext
&& isMobileBrowser()
&& mediaProto
&& typeof mediaProto.setSinkId === 'function'
&& navigator?.mediaDevices
&& typeof navigator.mediaDevices.selectAudioOutput === 'function'
);
}
function sanitizeTimelineText(value) {
return String(value || '')
.replace(/\s+/g, ' ')
@@ -548,6 +567,12 @@ function getCallStateSnapshot() {
if (!call) return null;
if (call.localUiDismissed) return null;
const callPhase = String(call.phase || '').trim();
const canToggleSpeaker = Boolean(
callPhase !== 'incoming'
&& callPhase !== 'ended'
&& canRouteAudioOutputInBrowser()
&& call.remoteAudio
);
return {
callId: call.callId,
peerLogin: call.peerLogin || '',
@@ -557,6 +582,8 @@ function getCallStateSnapshot() {
startedAtMs: Number(call.startedAtMs || 0),
connectedAtMs: Number(call.connectedAtMs || 0),
muted: Boolean(call.muted),
speakerEnabled: Boolean(call.speakerEnabled),
canToggleSpeaker,
canAnswer: callPhase === 'incoming',
canDecline: callPhase === 'incoming',
canHangup: callPhase !== 'ended' && callPhase !== 'incoming',
@@ -1400,8 +1427,10 @@ async function ensurePeerConnection(call) {
recordCallTimeline(call, 'remote_track_received', `streams=${evt?.streams?.length || 0}`);
const audio = new Audio();
audio.autoplay = true;
audio.playsInline = true;
audio.srcObject = evt.streams[0];
call.remoteAudio = audio;
notifyCallState();
};
call.pc = pc;
@@ -1602,6 +1631,36 @@ export async function toggleMicMuted() {
await setMicMuted(!call.muted);
}
export async function toggleSpeakerMode() {
const call = getActiveCall();
if (!call || !call.remoteAudio || !canRouteAudioOutputInBrowser()) return false;
const audio = call.remoteAudio;
if (Boolean(call.speakerEnabled)) {
try {
await audio.setSinkId('');
call.speakerEnabled = false;
notifyCallState();
return true;
} catch {
return false;
}
}
try {
const options = call.speakerSinkId ? { deviceId: call.speakerSinkId } : undefined;
const selected = await navigator.mediaDevices.selectAudioOutput(options);
const deviceId = String(selected?.deviceId || '').trim();
if (!deviceId) return false;
await audio.setSinkId(deviceId);
call.speakerSinkId = deviceId;
call.speakerEnabled = true;
notifyCallState();
return true;
} catch {
return false;
}
}
export async function startDebugConnectionAsResponder({ runId, callId, peerLogin, peerSessionId }) {
const cleanCallId = String(callId || '').trim();
const cleanPeerLogin = String(peerLogin || '').trim();