UI: иконки управления звонком

This commit is contained in:
AidarKC
2026-07-24 15:02:56 +04:00
parent 4d9e42205f
commit 69faf55b2b
3 changed files with 184 additions and 33 deletions
+97 -24
View File
@@ -21,6 +21,9 @@ let hangupBtn = null;
let minimizeBtn = null;
let barEl = null;
let barTextEl = null;
let barActionsEl = null;
let barMuteBtn = null;
let barHangupBtn = null;
let unbind = null;
let tickerId = 0;
let currentSnapshot = null;
@@ -55,6 +58,59 @@ function buildBarText(snapshot) {
return `Звонок с ${peer} · ${elapsed}`;
}
function stopEvent(event) {
event.preventDefault();
event.stopPropagation();
}
function getIconSvg(name) {
if (name === 'mic-on') {
return '<svg viewBox="0 0 24 24" aria-hidden="true"><rect x="9" y="3.5" width="6" height="11" rx="3" fill="none" stroke="currentColor" stroke-width="1.8"/><path d="M6 11.5a6 6 0 0 0 12 0" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/><path d="M12 17.5v3" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/><path d="M8.5 20.5h7" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>';
}
if (name === 'mic-off') {
return '<svg viewBox="0 0 24 24" aria-hidden="true"><rect x="9" y="3.5" width="6" height="11" rx="3" fill="none" stroke="currentColor" stroke-width="1.8"/><path d="M6 11.5a6 6 0 0 0 7.2 5.88" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/><path d="M12 17.5v3" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/><path d="M8.5 20.5h7" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/><path d="M5 5l14 14" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round"/></svg>';
}
if (name === 'hangup') {
return '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M7 7l10 10M17 7 7 17" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"/></svg>';
}
if (name === 'minimize') {
return '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 6v12M7.5 10.5 12 6l4.5 4.5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>';
}
return '';
}
function setButtonIcon(button, iconName, label) {
if (!button) return;
button.innerHTML = getIconSvg(iconName);
button.setAttribute('aria-label', label);
button.title = label;
}
function updateMuteButtons(muted) {
const iconName = muted ? 'mic-off' : 'mic-on';
const label = muted ? 'Включить микрофон' : 'Выключить микрофон';
setButtonIcon(muteBtn, iconName, label);
setButtonIcon(barMuteBtn, iconName, label);
if (muteBtn) muteBtn.dataset.muted = muted ? '1' : '0';
if (barMuteBtn) barMuteBtn.dataset.muted = muted ? '1' : '0';
}
async function handleMuteClick(event) {
stopEvent(event);
const nowMuted = event.currentTarget?.dataset?.muted === '1';
await setMicMuted(!nowMuted);
}
async function handleEndCallClick(event) {
stopEvent(event);
if (!currentSnapshot) return;
if (currentSnapshot.canDecline && !currentSnapshot.canHangup) {
await declineIncomingCall();
return;
}
await hangupActiveCall();
}
function setMinimized(nextValue) {
minimized = Boolean(nextValue);
render();
@@ -81,18 +137,33 @@ function ensureUi() {
rootEl.className = 'call-ui-root';
rootEl.hidden = true;
barEl = document.createElement('button');
barEl.type = 'button';
barEl = document.createElement('div');
barEl.className = 'call-minimized-bar';
barEl.hidden = true;
barEl.addEventListener('click', () => {
barTextEl = document.createElement('button');
barTextEl.type = 'button';
barTextEl.className = 'call-minimized-bar-text';
barTextEl.addEventListener('click', () => {
if (!currentSnapshot) return;
setMinimized(false);
});
barTextEl = document.createElement('span');
barTextEl.className = 'call-minimized-bar-text';
barEl.append(barTextEl);
barActionsEl = document.createElement('div');
barActionsEl.className = 'call-minimized-bar-actions';
barMuteBtn = document.createElement('button');
barMuteBtn.type = 'button';
barMuteBtn.className = 'call-icon-btn call-icon-btn--bar call-icon-btn--secondary';
barMuteBtn.addEventListener('click', handleMuteClick);
barHangupBtn = document.createElement('button');
barHangupBtn.type = 'button';
barHangupBtn.className = 'call-icon-btn call-icon-btn--bar call-icon-btn--danger';
barHangupBtn.addEventListener('click', handleEndCallClick);
barActionsEl.append(barMuteBtn, barHangupBtn);
barEl.append(barTextEl, barActionsEl);
overlayEl = document.createElement('div');
overlayEl.className = 'call-overlay';
@@ -109,9 +180,10 @@ function ensureUi() {
minimizeBtn = document.createElement('button');
minimizeBtn.type = 'button';
minimizeBtn.className = 'secondary-btn call-overlay-minimize-btn';
minimizeBtn.textContent = 'Свернуть';
minimizeBtn.addEventListener('click', () => {
minimizeBtn.className = 'call-icon-btn call-icon-btn--secondary call-overlay-minimize-btn';
setButtonIcon(minimizeBtn, 'minimize', 'Свернуть звонок');
minimizeBtn.addEventListener('click', (event) => {
stopEvent(event);
if (!currentSnapshot) return;
setMinimized(true);
});
@@ -124,12 +196,8 @@ function ensureUi() {
muteBtn = document.createElement('button');
muteBtn.type = 'button';
muteBtn.className = 'secondary-btn';
muteBtn.textContent = 'Микрофон';
muteBtn.addEventListener('click', async () => {
const nowMuted = muteBtn.dataset.muted === '1';
await setMicMuted(!nowMuted);
});
muteBtn.className = 'call-icon-btn call-icon-btn--secondary';
muteBtn.addEventListener('click', handleMuteClick);
acceptBtn = document.createElement('button');
acceptBtn.type = 'button';
@@ -141,19 +209,17 @@ function ensureUi() {
declineBtn = document.createElement('button');
declineBtn.type = 'button';
declineBtn.className = 'destructive-btn';
declineBtn.textContent = 'Сбросить';
declineBtn.className = 'call-icon-btn call-icon-btn--danger';
setButtonIcon(declineBtn, 'hangup', 'Отклонить звонок');
declineBtn.addEventListener('click', async () => {
await declineIncomingCall();
});
hangupBtn = document.createElement('button');
hangupBtn.type = 'button';
hangupBtn.className = 'destructive-btn';
hangupBtn.textContent = 'Положить трубку';
hangupBtn.addEventListener('click', async () => {
await hangupActiveCall();
});
hangupBtn.className = 'call-icon-btn call-icon-btn--danger';
setButtonIcon(hangupBtn, 'hangup', 'Положить трубку');
hangupBtn.addEventListener('click', handleEndCallClick);
controls.append(muteBtn, acceptBtn, declineBtn, hangupBtn);
headerEl.append(titleEl, minimizeBtn);
@@ -171,8 +237,7 @@ function applyExpandedState(snapshot) {
statusEl.textContent = snapshot.statusText || '';
const muted = Boolean(snapshot.muted);
muteBtn.dataset.muted = muted ? '1' : '0';
muteBtn.textContent = muted ? 'Включить микрофон' : 'Выключить микрофон';
updateMuteButtons(muted);
muteBtn.hidden = !snapshot.canMute;
muteBtn.disabled = !snapshot.canMute;
@@ -186,6 +251,14 @@ function applyMinimizedState(snapshot) {
overlayEl.hidden = true;
barEl.hidden = false;
barTextEl.textContent = buildBarText(snapshot);
const muted = Boolean(snapshot.muted);
updateMuteButtons(muted);
barMuteBtn.hidden = !snapshot.canMute;
barMuteBtn.disabled = !snapshot.canMute;
const canEnd = snapshot.canHangup || snapshot.canDecline;
barHangupBtn.hidden = !canEnd;
barHangupBtn.disabled = !canEnd;
setButtonIcon(barHangupBtn, 'hangup', snapshot.canDecline && !snapshot.canHangup ? 'Отклонить звонок' : 'Положить трубку');
}
function syncShellState() {