SHA256
Звонки: WebPush incoming/stop, actions и TTL; обновлена логика
This commit is contained in:
@@ -1064,6 +1064,84 @@ function ensureIncomingNotification(peerLogin) {
|
||||
} catch {}
|
||||
}
|
||||
|
||||
function isIncomingCallPushFresh(payload) {
|
||||
const expiresAtMs = Number(payload?.expiresAtMs || 0);
|
||||
if (Number.isFinite(expiresAtMs) && expiresAtMs > 0 && Date.now() > expiresAtMs) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
async function handleIncomingInvitePayload(payload, { source = 'ws' } = {}) {
|
||||
const callId = String(payload?.callId || '').trim();
|
||||
const fromLogin = String(payload?.fromLogin || '').trim();
|
||||
const fromSessionId = String(payload?.fromSessionId || '').trim();
|
||||
if (!callId || !fromLogin || !fromSessionId) return null;
|
||||
|
||||
if (activeCallId && activeCallId !== callId) {
|
||||
try {
|
||||
await authService.callSignalToSession({
|
||||
toLogin: fromLogin,
|
||||
targetSessionId: fromSessionId,
|
||||
callId,
|
||||
type: TYPES.DECLINE_BUSY,
|
||||
data: 'busy',
|
||||
});
|
||||
} catch {}
|
||||
return null;
|
||||
}
|
||||
|
||||
let call = getCall(callId);
|
||||
if (!call) {
|
||||
call = {
|
||||
callId,
|
||||
peerLogin: fromLogin,
|
||||
direction: 'in',
|
||||
phase: 'incoming',
|
||||
statusText: `Вам звонит ${fromLogin}`,
|
||||
remoteSessionId: fromSessionId,
|
||||
timers: {},
|
||||
startedAtMs: nowMs(),
|
||||
connectedAtMs: 0,
|
||||
pc: null,
|
||||
localStream: null,
|
||||
audioSenders: [],
|
||||
muted: false,
|
||||
connectionRouteLabel: '',
|
||||
reconnectInProgress: false,
|
||||
reconnectAttempts: 0,
|
||||
debugMode: false,
|
||||
debugRunId: '',
|
||||
debugRole: '',
|
||||
pendingRemoteIceCandidates: [],
|
||||
initialOfferInProgress: false,
|
||||
initialOfferSent: false,
|
||||
};
|
||||
calls.set(callId, call);
|
||||
} else if (!call.remoteSessionId && fromSessionId) {
|
||||
call.remoteSessionId = fromSessionId;
|
||||
}
|
||||
|
||||
activeCallId = callId;
|
||||
setStatus(call, `Вам звонит ${fromLogin}`, 'incoming');
|
||||
ensureIncomingNotification(fromLogin);
|
||||
|
||||
try {
|
||||
await sendSignal(call, TYPES.RINGING, `ringing:${source}`);
|
||||
} catch {}
|
||||
|
||||
if (!call.timers.incoming20s) {
|
||||
call.timers.incoming20s = setTimeout(async () => {
|
||||
if (!calls.has(callId)) return;
|
||||
try {
|
||||
await sendSignal(call, TYPES.TIMEOUT, 'timeout_20s');
|
||||
} catch {}
|
||||
await finalizeCall(call, { localReasonCode: 'no_answer', debugReason: 'incoming_timeout_20s' });
|
||||
}, 20000);
|
||||
}
|
||||
return call;
|
||||
}
|
||||
|
||||
export function setCallDebugReporter(fn) {
|
||||
debugReporter = typeof fn === 'function' ? fn : null;
|
||||
}
|
||||
@@ -1272,69 +1350,7 @@ export async function startOutgoingCall(peerLogin) {
|
||||
}
|
||||
|
||||
export async function handleIncomingCallInvite(evt) {
|
||||
const payload = evt?.payload || {};
|
||||
const callId = String(payload.callId || '').trim();
|
||||
const fromLogin = String(payload.fromLogin || '').trim();
|
||||
const fromSessionId = String(payload.fromSessionId || '').trim();
|
||||
if (!callId || !fromLogin || !fromSessionId) return;
|
||||
|
||||
if (activeCallId && activeCallId !== callId) {
|
||||
try {
|
||||
await authService.callSignalToSession({
|
||||
toLogin: fromLogin,
|
||||
targetSessionId: fromSessionId,
|
||||
callId,
|
||||
type: TYPES.DECLINE_BUSY,
|
||||
data: 'busy',
|
||||
});
|
||||
} catch {}
|
||||
return;
|
||||
}
|
||||
|
||||
let call = getCall(callId);
|
||||
if (!call) {
|
||||
call = {
|
||||
callId,
|
||||
peerLogin: fromLogin,
|
||||
direction: 'in',
|
||||
phase: 'incoming',
|
||||
statusText: `Вам звонит ${fromLogin}`,
|
||||
remoteSessionId: fromSessionId,
|
||||
timers: {},
|
||||
startedAtMs: nowMs(),
|
||||
connectedAtMs: 0,
|
||||
pc: null,
|
||||
localStream: null,
|
||||
audioSenders: [],
|
||||
muted: false,
|
||||
connectionRouteLabel: '',
|
||||
reconnectInProgress: false,
|
||||
reconnectAttempts: 0,
|
||||
debugMode: false,
|
||||
debugRunId: '',
|
||||
debugRole: '',
|
||||
pendingRemoteIceCandidates: [],
|
||||
initialOfferInProgress: false,
|
||||
initialOfferSent: false,
|
||||
};
|
||||
calls.set(callId, call);
|
||||
}
|
||||
|
||||
activeCallId = callId;
|
||||
setStatus(call, `Вам звонит ${fromLogin}`, 'incoming');
|
||||
ensureIncomingNotification(fromLogin);
|
||||
|
||||
try {
|
||||
await sendSignal(call, TYPES.RINGING, 'ringing');
|
||||
} catch {}
|
||||
|
||||
call.timers.incoming20s = setTimeout(async () => {
|
||||
if (!calls.has(callId)) return;
|
||||
try {
|
||||
await sendSignal(call, TYPES.TIMEOUT, 'timeout_20s');
|
||||
} catch {}
|
||||
await finalizeCall(call, { localReasonCode: 'no_answer', debugReason: 'incoming_timeout_20s' });
|
||||
}, 20000);
|
||||
await handleIncomingInvitePayload(evt?.payload || {}, { source: 'ws' });
|
||||
}
|
||||
|
||||
export async function acceptIncomingCall() {
|
||||
@@ -1534,3 +1550,32 @@ export async function hangupActiveCall() {
|
||||
notifyRemoteHangup: true,
|
||||
});
|
||||
}
|
||||
|
||||
export async function handleIncomingCallPush(payload = {}) {
|
||||
if (!isIncomingCallPushFresh(payload)) return;
|
||||
await handleIncomingInvitePayload(payload, { source: 'push' });
|
||||
}
|
||||
|
||||
export async function handleStopCallPush(payload = {}) {
|
||||
const callId = String(payload?.callId || '').trim();
|
||||
if (!callId) return;
|
||||
const call = getCall(callId);
|
||||
if (!call) return;
|
||||
const reason = String(payload?.reason || 'stop_call_push').trim() || 'stop_call_push';
|
||||
await finalizeCall(call, {
|
||||
localReasonCode: call.connectedAtMs ? 'completed' : 'no_answer',
|
||||
debugReason: `stop_call_push:${reason}`,
|
||||
});
|
||||
}
|
||||
|
||||
export async function handleCallPushAction(action, payload = {}) {
|
||||
const normalized = String(action || '').trim().toLowerCase();
|
||||
if (normalized !== 'accept' && normalized !== 'decline') return;
|
||||
if (!isIncomingCallPushFresh(payload)) return;
|
||||
await handleIncomingCallPush(payload);
|
||||
if (normalized === 'accept') {
|
||||
await acceptIncomingCall();
|
||||
return;
|
||||
}
|
||||
await declineIncomingCall();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user