SHA256
UI: стадии и звук звонка
This commit is contained in:
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user