diff --git a/api_v1_voice.php b/api_v1_voice.php index dd125d4..2a576d8 100644 --- a/api_v1_voice.php +++ b/api_v1_voice.php @@ -138,8 +138,13 @@ if ($action === "poll") { $p_file = room_participants_file($room); $ps = read_json_file($p_file); + $is_muted = isset($_REQUEST["is_muted"]) ? (int)$_REQUEST["is_muted"] : 0; + $is_deafened = isset($_REQUEST["is_deafened"]) ? (int)$_REQUEST["is_deafened"] : 0; + if (isset($ps[$my_id])) { $ps[$my_id]["last_seen"] = now_ms(); + $ps[$my_id]["is_muted"] = $is_muted; + $ps[$my_id]["is_deafened"] = $is_deafened; } $stale_time = now_ms() - 10000; @@ -151,8 +156,8 @@ if ($action === "poll") { // Update DB last_seen if ($current_user_id > 0) { try { - $stmt = db()->prepare("UPDATE voice_sessions SET last_seen = ? WHERE user_id = ?"); - $stmt->execute([now_ms(), $current_user_id]); + $stmt = db()->prepare("UPDATE voice_sessions SET last_seen = ?, is_muted = ?, is_deafened = ? WHERE user_id = ?"); + $stmt->execute([now_ms(), $is_muted, $is_deafened, $current_user_id]); } catch (Exception $e) {} } @@ -218,7 +223,7 @@ if ($action === "list_all") { try { $stmt = db()->prepare(" - SELECT vs.channel_id, vs.user_id, u.username, u.display_name, u.avatar_url + SELECT vs.channel_id, vs.user_id, vs.is_muted, vs.is_deafened, u.username, u.display_name, u.avatar_url FROM voice_sessions vs JOIN users u ON vs.user_id = u.id WHERE vs.last_seen > ? diff --git a/assets/css/discord.css b/assets/css/discord.css index ee53328..686bb12 100644 --- a/assets/css/discord.css +++ b/assets/css/discord.css @@ -926,8 +926,19 @@ body { /* Voice active state */ .voice-item.active, .voice-item.connected { - background-color: rgba(35, 165, 89, 0.1); - color: #23a559 !important; + background-color: var(--active); + color: var(--text-primary); +} +.user-actions .btn:hover { + background-color: var(--hover); + color: var(--text-primary) !important; +} +.user-actions .btn.active { + color: #f23f43 !important; +} +.user-actions a:hover { + background-color: var(--hover); + border-radius: 4px; } .voice-user { diff --git a/assets/js/voice.js b/assets/js/voice.js index fa78177..088aa43 100644 --- a/assets/js/voice.js +++ b/assets/js/voice.js @@ -13,6 +13,8 @@ class VoiceChannel { this.myPeerId = null; this.pollInterval = null; this.remoteAudios = {}; // userId -> Audio element + this.isMuted = false; + this.isDeafened = false; this.audioContext = null; this.analyser = null; @@ -140,7 +142,7 @@ class VoiceChannel { if (!this.myPeerId || !this.currentChannelId) return; try { - const resp = await fetch(`api_v1_voice.php?action=poll&room=${this.currentChannelId}&peer_id=${this.myPeerId}`); + const resp = await fetch(`api_v1_voice.php?action=poll&room=${this.currentChannelId}&peer_id=${this.myPeerId}&is_muted=${this.isMuted ? 1 : 0}&is_deafened=${this.isDeafened ? 1 : 0}`); const data = await resp.json(); if (data.success) { @@ -230,6 +232,7 @@ class VoiceChannel { remoteAudio.autoplay = true; remoteAudio.style.display = 'none'; remoteAudio.srcObject = stream; + remoteAudio.muted = this.isDeafened; document.body.appendChild(remoteAudio); this.remoteAudios[userId] = remoteAudio; @@ -404,10 +407,53 @@ class VoiceChannel { } setMute(mute) { + this.isMuted = mute; if (this.localStream) { console.log('Setting mute to:', mute); this.localStream.getAudioTracks().forEach(track => { track.enabled = !mute; }); } + this.updateUserPanelButtons(); + } + + toggleMute() { + this.setMute(!this.isMuted); + } + + toggleDeafen() { + this.isDeafened = !this.isDeafened; + console.log('Setting deafen to:', this.isDeafened); + Object.values(this.remoteAudios).forEach(audio => { + audio.muted = this.isDeafened; + }); + // If we deafen, we usually also mute in Discord + if (this.isDeafened && !this.isMuted) { + this.setMute(true); + } else if (!this.isDeafened && this.isMuted) { + // Not necessarily unmute when undeafen, but often expected + // Let's just update UI + } + this.updateUserPanelButtons(); + } + + updateUserPanelButtons() { + const btnMute = document.getElementById('btn-panel-mute'); + const btnDeafen = document.getElementById('btn-panel-deafen'); + + if (btnMute) { + btnMute.classList.toggle('active', this.isMuted); + btnMute.style.color = this.isMuted ? '#f23f43' : 'var(--text-muted)'; + btnMute.innerHTML = this.isMuted ? + '' : + ''; + } + + if (btnDeafen) { + btnDeafen.classList.toggle('active', this.isDeafened); + btnDeafen.style.color = this.isDeafened ? '#f23f43' : 'var(--text-muted)'; + btnDeafen.innerHTML = this.isDeafened ? + '' : + ''; + } } leave() { @@ -506,6 +552,7 @@ class VoiceChannel { } updateSpeakingUI(userId, isSpeaking) { + userId = String(userId); if (isSpeaking) { this.speakingUsers.add(userId); } else { @@ -548,8 +595,9 @@ class VoiceChannel { const listEl = container.querySelector('.voice-users-list'); if (listEl) { data.channels[channelId].forEach(p => { - const isSpeaking = window.voiceHandler && window.voiceHandler.speakingUsers.has(p.user_id); - VoiceChannel.renderUserToUI(listEl, p.user_id, p.display_name || p.username, p.avatar_url, isSpeaking); + const pid = String(p.user_id); + const isSpeaking = window.voiceHandler && window.voiceHandler.speakingUsers.has(pid); + VoiceChannel.renderUserToUI(listEl, p.user_id, p.display_name || p.username, p.avatar_url, isSpeaking, p.is_muted, p.is_deafened); }); } } @@ -561,16 +609,25 @@ class VoiceChannel { } } - static renderUserToUI(container, userId, username, avatarUrl, isSpeaking = false) { + static renderUserToUI(container, userId, username, avatarUrl, isSpeaking = false, isMuted = false, isDeafened = false) { const userEl = document.createElement('div'); userEl.className = 'voice-user small text-muted d-flex align-items-center mb-1'; userEl.dataset.userId = userId; userEl.style.paddingLeft = '8px'; const avatarStyle = avatarUrl ? `background-image: url('${avatarUrl}'); background-size: cover;` : "background-color: #555;"; const boxShadow = isSpeaking ? 'box-shadow: 0 0 0 2px #23a559;' : ''; + + let icons = ''; + if (isDeafened) { + icons += ''; + } else if (isMuted) { + icons += ''; + } + userEl.innerHTML = `
- ${username} + ${username} + ${icons} `; container.appendChild(userEl); } diff --git a/contact.php b/contact.php new file mode 100644 index 0000000..4ffba5b --- /dev/null +++ b/contact.php @@ -0,0 +1,115 @@ + + + + + + +This is for testing purposes only — Flatlogic does not guarantee usage of the mail server. Please set up your own SMTP in .env with our AI Agent.
+