fix(ui): compact user panel with status icon and room indicator
This commit is contained in:
1 parent
a2be7cf6a5
commit
e687d41574
3 files changed
+51
-17
No files matched your search
+21
-5
@@ -34,7 +34,8 @@ window.__dbg = S; // POC debug handle
|
|||||||
const $ = (sel) => document.querySelector(sel);
|
const $ = (sel) => document.querySelector(sel);
|
||||||
const els = {
|
const els = {
|
||||||
stage: $('#stage'), screens: $('#screens'),
|
stage: $('#stage'), screens: $('#screens'),
|
||||||
spatial: $('#spatial'), status: $('#voice-status'), nick: $('#nick'),
|
spatial: $('#spatial'), status: $('#voice-status'), statusDot: $('#status-dot'),
|
||||||
|
statusRow: $('#status-row'), nick: $('#nick'),
|
||||||
meAvatar: $('#me-avatar'), mic: $('#btn-mic'), deafen: $('#btn-deafen'),
|
meAvatar: $('#me-avatar'), mic: $('#btn-mic'), deafen: $('#btn-deafen'),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -75,7 +76,7 @@ async function joinVoice(room) {
|
|||||||
if (S.room === room || S.connecting) return;
|
if (S.room === room || S.connecting) return;
|
||||||
if (S.room) leaveVoice('switching channel');
|
if (S.room) leaveVoice('switching channel');
|
||||||
S.connecting = true;
|
S.connecting = true;
|
||||||
setStatus(`connecting to ${room}…`);
|
setStatus(`connecting to ${room}…`, 'connecting');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
S.iceServers = await fetchIce();
|
S.iceServers = await fetchIce();
|
||||||
@@ -135,7 +136,22 @@ function leaveVoice(reason) {
|
|||||||
setStatus(reason || 'not connected');
|
setStatus(reason || 'not connected');
|
||||||
}
|
}
|
||||||
|
|
||||||
function setStatus(text) { els.status.textContent = text; }
|
function setStatus(text, state = '') {
|
||||||
|
if (els.status) {
|
||||||
|
// Concise display text
|
||||||
|
let shortText = text;
|
||||||
|
if (text.startsWith('voice connected')) {
|
||||||
|
shortText = S.room ? `#${S.room}` : 'Connected';
|
||||||
|
} else if (text.startsWith('connecting to')) {
|
||||||
|
shortText = 'Connecting…';
|
||||||
|
}
|
||||||
|
els.status.textContent = shortText;
|
||||||
|
}
|
||||||
|
if (els.statusRow) els.statusRow.title = text;
|
||||||
|
if (els.statusDot) {
|
||||||
|
els.statusDot.className = 'status-indicator' + (state ? ` ${state}` : '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// signaling
|
// signaling
|
||||||
@@ -146,7 +162,7 @@ function openSignaling(room) {
|
|||||||
const url = `${proto}://${location.host}/ws?room=${encodeURIComponent(room)}&name=${encodeURIComponent(S.name)}`;
|
const url = `${proto}://${location.host}/ws?room=${encodeURIComponent(room)}&name=${encodeURIComponent(S.name)}`;
|
||||||
const ws = new WebSocket(url);
|
const ws = new WebSocket(url);
|
||||||
S.ws = ws;
|
S.ws = ws;
|
||||||
ws.onerror = () => { S.connecting = false; setStatus('connection failed'); reject(new Error('websocket failed')); };
|
ws.onerror = () => { S.connecting = false; setStatus('connection failed', 'error'); reject(new Error('websocket failed')); };
|
||||||
ws.onmessage = (ev) => {
|
ws.onmessage = (ev) => {
|
||||||
let msg;
|
let msg;
|
||||||
try { msg = JSON.parse(ev.data); } catch { return; }
|
try { msg = JSON.parse(ev.data); } catch { return; }
|
||||||
@@ -185,7 +201,7 @@ function handleSignal(msg) {
|
|||||||
}
|
}
|
||||||
sendMyPos(true);
|
sendMyPos(true);
|
||||||
renderAll();
|
renderAll();
|
||||||
setStatus(`voice connected — ${msg.peers.length + 1} in ${msg.room}`);
|
setStatus(`voice connected — ${msg.peers.length + 1} in ${msg.room}`, 'online');
|
||||||
toast(`Joined ${msg.room}`, 'ok');
|
toast(`Joined ${msg.room}`, 'ok');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-3
@@ -39,8 +39,11 @@
|
|||||||
<div class="me">
|
<div class="me">
|
||||||
<div class="avatar" id="me-avatar">?</div>
|
<div class="avatar" id="me-avatar">?</div>
|
||||||
<div class="me-meta">
|
<div class="me-meta">
|
||||||
<input id="nick" maxlength="24" spellcheck="false" title="Nickname" />
|
<input id="nick" maxlength="24" spellcheck="false" title="Click to edit nickname" />
|
||||||
<span id="voice-status">not connected</span>
|
<div class="status-row" id="status-row" title="Not connected">
|
||||||
|
<span class="status-indicator" id="status-dot"></span>
|
||||||
|
<span id="voice-status">Disconnected</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="user-buttons">
|
<div class="user-buttons">
|
||||||
@@ -96,6 +99,6 @@
|
|||||||
|
|
||||||
<div id="toasts"></div>
|
<div id="toasts"></div>
|
||||||
|
|
||||||
<script src="app.js?v=9"></script>
|
<script src="app.js?v=10"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
+24
-9
@@ -66,25 +66,40 @@ body {
|
|||||||
|
|
||||||
#user-panel {
|
#user-panel {
|
||||||
margin-top: auto; background: var(--bg-0); padding: 8px;
|
margin-top: auto; background: var(--bg-0); padding: 8px;
|
||||||
display: flex; align-items: center; gap: 8px;
|
display: flex; align-items: center; justify-content: space-between; gap: 6px;
|
||||||
|
min-height: 52px;
|
||||||
}
|
}
|
||||||
.me { display: flex; align-items: center; gap: 8px; flex: 1; min-width: 0; }
|
.me { display: flex; align-items: center; gap: 8px; min-width: 0; flex: 1; }
|
||||||
.me-meta { display: flex; flex-direction: column; min-width: 0; }
|
.me-meta { display: flex; flex-direction: column; min-width: 0; flex: 1; overflow: hidden; }
|
||||||
#nick {
|
#nick {
|
||||||
background: transparent; border: none; color: var(--text-0); font: inherit;
|
background: transparent; border: none; color: var(--text-0); font: inherit;
|
||||||
font-weight: 600; width: 100%; padding: 0; outline: none; border-bottom: 1px dashed transparent;
|
font-weight: 600; font-size: 13px; width: 100%; padding: 0; outline: none; border-bottom: 1px dashed transparent;
|
||||||
|
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
#nick:hover, #nick:focus { border-bottom-color: var(--text-2); }
|
#nick:hover, #nick:focus { border-bottom-color: var(--text-2); }
|
||||||
#voice-status { font-size: 11px; color: var(--text-2); }
|
.status-row {
|
||||||
.user-buttons { display: flex; gap: 2px; }
|
display: flex; align-items: center; gap: 4px; font-size: 11px; color: var(--text-2);
|
||||||
|
white-space: nowrap; overflow: hidden; text-overflow: ellipsis; line-height: 1.2;
|
||||||
|
}
|
||||||
|
.status-indicator {
|
||||||
|
width: 7px; height: 7px; border-radius: 50%; background: var(--text-2); flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.status-indicator.online { background: var(--green); }
|
||||||
|
.status-indicator.connecting { background: var(--yellow); }
|
||||||
|
.status-indicator.error { background: var(--red); }
|
||||||
|
#voice-status {
|
||||||
|
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
.user-buttons { display: flex; gap: 2px; flex-shrink: 0; }
|
||||||
.ctl {
|
.ctl {
|
||||||
background: transparent; border: none; color: var(--text-1); font-size: 16px;
|
background: transparent; border: none; color: var(--text-1); font-size: 15px;
|
||||||
width: 32px; height: 32px; border-radius: 6px; cursor: pointer; position: relative;
|
width: 28px; height: 28px; border-radius: 6px; cursor: pointer; position: relative;
|
||||||
|
display: grid; place-items: center; padding: 0;
|
||||||
}
|
}
|
||||||
.ctl:hover { background: var(--bg-3); color: var(--text-0); }
|
.ctl:hover { background: var(--bg-3); color: var(--text-0); }
|
||||||
.ctl.off { color: var(--red); }
|
.ctl.off { color: var(--red); }
|
||||||
.ctl.off::after {
|
.ctl.off::after {
|
||||||
content: ""; position: absolute; left: 7px; top: 15px; width: 18px; height: 2px;
|
content: ""; position: absolute; left: 5px; top: 13px; width: 18px; height: 2px;
|
||||||
background: var(--red); transform: rotate(-40deg); border-radius: 1px;
|
background: var(--red); transform: rotate(-40deg); border-radius: 1px;
|
||||||
}
|
}
|
||||||
.ctl:disabled { opacity: .35; cursor: not-allowed; }
|
.ctl:disabled { opacity: .35; cursor: not-allowed; }
|
||||||
|
|||||||
Reference in new issue
Block a user