From 476fc542a50b4b020fc77d1132a8a3e95748dac9 Mon Sep 17 00:00:00 2001 From: MathMan05 Date: Wed, 14 May 2025 11:24:14 -0500 Subject: [PATCH] voice progress --- src/webpage/channel.ts | 2 +- src/webpage/icons/mic.svg | 1 + src/webpage/icons/micmute.svg | 1 + src/webpage/index.html | 5 +-- src/webpage/jsontypes.ts | 64 +++++++++++++++++++++++++++-------- src/webpage/localuser.ts | 43 +++++++++++++++++++++-- src/webpage/style.css | 26 ++++++++++---- src/webpage/voice.ts | 31 ++++++++--------- 8 files changed, 131 insertions(+), 42 deletions(-) create mode 100644 src/webpage/icons/mic.svg create mode 100644 src/webpage/icons/micmute.svg diff --git a/src/webpage/channel.ts b/src/webpage/channel.ts index b47ee69..42ceb82 100644 --- a/src/webpage/channel.ts +++ b/src/webpage/channel.ts @@ -1124,7 +1124,7 @@ class Channel extends SnowFlake { loading.classList.add("loading"); this.rendertyping(); this.localuser.getSidePannel(); - if (this.voice && localStorage.getItem("Voice enabled")) { + if (this.voice && this.localuser.voiceAllowed) { this.localuser.joinVoice(this); } (document.getElementById("typebox") as HTMLDivElement).contentEditable = "" + this.canMessage; diff --git a/src/webpage/icons/mic.svg b/src/webpage/icons/mic.svg new file mode 100644 index 0000000..533378f --- /dev/null +++ b/src/webpage/icons/mic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/webpage/icons/micmute.svg b/src/webpage/icons/micmute.svg new file mode 100644 index 0000000..e41993b --- /dev/null +++ b/src/webpage/icons/micmute.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/webpage/index.html b/src/webpage/index.html index 2dfe5d4..3afb6a5 100644 --- a/src/webpage/index.html +++ b/src/webpage/index.html @@ -58,8 +58,9 @@ -
- +
+
+
diff --git a/src/webpage/jsontypes.ts b/src/webpage/jsontypes.ts index 7f4dd37..0f8f6d0 100644 --- a/src/webpage/jsontypes.ts +++ b/src/webpage/jsontypes.ts @@ -1,4 +1,4 @@ -type readyjson = { +interface readyjson { op: 0; t: "READY"; s: number; @@ -120,7 +120,39 @@ type readyjson = { flags: number; }; }; -}; +} +interface readySuplemental { + op: 0; + t: "READY_SUPPLEMENTAL"; + s: number; + d: { + merged_presences: { + guilds: []; + friends: []; + }; + merged_members: []; + lazy_private_channels: []; + guilds: { + voice_states: { + user_id: string; + suppress: boolean; + session_id: string; + self_video: boolean; + self_mute: boolean; + self_deaf: boolean; + self_stream: boolean; + request_to_speak_timestamp: null; + mute: boolean; + deaf: boolean; + channel_id: string; //weird reasons, don't question it too much + guild_id: string; + }[]; + id: string; + embedded_activities: []; + }[]; + disclose: []; + }; +} interface banObj { reason: string | null; user: { @@ -508,6 +540,7 @@ type roleCreate = { s: 6; }; type wsjson = + | readySuplemental | roleCreate | { op: 0; @@ -704,22 +737,23 @@ type memberChunk = { chunk_count: number; not_found: string[]; }; +export type voiceStatus = { + guild_id: string; + channel_id: string; + user_id: string; + member?: memberjson; + session_id: string; + deaf: boolean; + mute: boolean; + self_deaf: boolean; + self_mute: boolean; + self_video: boolean; + suppress: boolean; +}; type voiceupdate = { op: 0; t: "VOICE_STATE_UPDATE"; - d: { - guild_id: string; - channel_id: string; - user_id: string; - member: memberjson; - session_id: string; - deaf: boolean; - mute: boolean; - self_deaf: boolean; - self_mute: boolean; - self_video: boolean; - suppress: boolean; - }; + d: voiceStatus; s: number; }; type voiceserverupdate = { diff --git a/src/webpage/localuser.ts b/src/webpage/localuser.ts index 129f945..21fe074 100644 --- a/src/webpage/localuser.ts +++ b/src/webpage/localuser.ts @@ -243,6 +243,21 @@ class Localuser { if (this.perminfo.user.disableColors === undefined) this.perminfo.user.disableColors = true; this.updateTranslations(); } + readysup = false; + get voiceAllowed() { + return this.readysup || localStorage.getItem("Voice enabled"); + } + mute = true; + deaf = false; + updateMic() { + const mic = document.getElementById("mic") as HTMLElement; + mic.classList.remove("svg-mic", "svg-micmute"); + if (this.mute) { + mic.classList.add("svg-micmute"); + } else { + mic.classList.add("svg-mic"); + } + } async gottenReady(ready: readyjson): Promise { await I18n.done; this.initialized = true; @@ -273,6 +288,12 @@ class Localuser { members[thing[0].guild_id] = thing[0]; } } + this.updateMic(); + const mic = document.getElementById("mic") as HTMLElement; + mic.onclick = () => { + this.mute = !this.mute; + this.updateMic(); + }; for (const thing of ready.d.guilds) { const temp = new Guild(thing, this, members[thing.id]); this.guilds.push(temp); @@ -722,9 +743,22 @@ class Localuser { this.memberListUpdate(temp); break; } + case "READY_SUPPLEMENTAL": + { + temp.d.guilds.forEach((_) => + _.voice_states.forEach((status) => { + if (this.voiceFactory && status.channel_id) { + this.voiceFactory.voiceStateUpdate(status); + console.log(status); + } + }), + ); + this.readysup = temp.d.guilds.length !== 0; + } + break; case "VOICE_STATE_UPDATE": if (this.voiceFactory) { - this.voiceFactory.voiceStateUpdate(temp); + this.voiceFactory.voiceStateUpdate(temp.d); } break; @@ -838,7 +872,9 @@ class Localuser { async joinVoice(channel: Channel) { if (!this.voiceFactory) return; if (!this.ws) return; - this.ws.send(JSON.stringify(this.voiceFactory.joinVoice(channel.id, channel.guild.id))); + this.ws.send( + JSON.stringify(this.voiceFactory.joinVoice(channel.id, channel.guild.id, this.mute)), + ); return undefined; } changeVCStatus(status: string) { @@ -891,6 +927,9 @@ class Localuser { if (!guild) return; const channel = this.channelfocus; if (!channel) return; + if (channel.voice && this.voiceAllowed) { + return; + } if (list) { const counts = new Map(); for (const thing of list.d.ops[0].items) { diff --git a/src/webpage/style.css b/src/webpage/style.css index b27f670..32eb23f 100644 --- a/src/webpage/style.css +++ b/src/webpage/style.css @@ -538,6 +538,21 @@ textarea { aspect-ratio: 1/1; flex-shrink: 0; } +.svg-mic { + height: 22px; + width: 22px; + margin: 6px; + mask: url(/icons/mic.svg); + mask-size: contain !important; +} +.svg-micmute { + height: 22px; + width: 22px; + margin: 6px; + mask: url(/icons/micmute.svg); + background-color: var(--red); + mask-size: contain !important; +} .mobileback { visibility: hidden; height: 0px; @@ -1186,10 +1201,6 @@ span.instanceStatus { #userinfo { min-width: 50%; padding: 0 6px; - background: var(--user-info-bg); - color: var(--user-info-text); - border-radius: 8px; - box-sizing: border-box; gap: 6px; cursor: pointer; } @@ -1205,11 +1216,14 @@ span.instanceStatus { #status { font-size: 0.8em; } -#user-actions { +#user-actions > * { border-radius: 50%; cursor: pointer; + background: var(--user-info-bg); + color: var(--user-info-text); + box-sizing: border-box; } -#user-actions:hover { +#user-actions > :hover { background: var(--dock-hover); } #settings { diff --git a/src/webpage/voice.ts b/src/webpage/voice.ts index ee743d6..6458616 100644 --- a/src/webpage/voice.ts +++ b/src/webpage/voice.ts @@ -1,5 +1,4 @@ -import {memberjson, sdpback, voiceserverupdate, voiceupdate, webRTCSocket} from "./jsontypes.js"; - +import {memberjson, sdpback, voiceserverupdate, voiceStatus, webRTCSocket} from "./jsontypes.js"; class VoiceFactory { settings: {id: string}; constructor(usersettings: VoiceFactory["settings"]) { @@ -28,7 +27,7 @@ class VoiceFactory { } onJoin = (_voice: Voice) => {}; onLeave = (_voice: Voice) => {}; - joinVoice(channelId: string, guildId: string) { + joinVoice(channelId: string, guildId: string, self_mute = false) { const voice = this.voiceChannels.get(channelId); if (this.currentVoice && this.currentVoice.ws) { this.currentVoice.leave(); @@ -42,7 +41,7 @@ class VoiceFactory { d: { guild_id: guildId, channel_id: channelId, - self_mute: false, //todo + self_mute, self_deaf: false, //todo self_video: false, //What is this? I have some guesses flags: 2, //????? @@ -51,16 +50,16 @@ class VoiceFactory { }; } userMap = new Map(); - voiceStateUpdate(update: voiceupdate) { - const prev = this.userMap.get(update.d.user_id); + voiceStateUpdate(update: voiceStatus) { + const prev = this.userMap.get(update.user_id); console.log(prev, this.userMap); if (prev) { - prev.disconnect(update.d.user_id); + prev.disconnect(update.user_id); this.onLeave(prev); } - const voice = this.voiceChannels.get(update.d.channel_id); + const voice = this.voiceChannels.get(update.channel_id); if (voice) { - this.userMap.set(update.d.user_id, voice); + this.userMap.set(update.user_id, voice); voice.voiceupdate(update); } } @@ -696,11 +695,11 @@ a=rtcp-mux\r`; } onMemberChange = (_member: memberjson | string, _joined: boolean) => {}; userids = new Map(); - async voiceupdate(update: voiceupdate) { + async voiceupdate(update: voiceStatus) { console.log("Update!"); - this.userids.set(update.d.member.id, {deaf: update.d.deaf, muted: update.d.mute}); - this.onMemberChange(update.d.member, true); - if (update.d.member.id === this.userid && this.open) { + this.userids.set(update.user_id, {deaf: update.deaf, muted: update.mute}); + this.onMemberChange(update?.member || update.user_id, true); + if (update.user_id === this.userid && this.open) { if (!update) { this.status = "bad responce from WS"; return; @@ -737,9 +736,9 @@ a=rtcp-mux\r`; JSON.stringify({ op: 0, d: { - server_id: update.d.guild_id, - user_id: update.d.user_id, - session_id: update.d.session_id, + server_id: update.guild_id, + user_id: update.user_id, + session_id: update.session_id, token: this.urlobj.token, video: false, streams: [