some VC updates
This commit is contained in:
parent
ac724bd8dd
commit
3b848d42b7
5 changed files with 128 additions and 22 deletions
|
@ -1124,12 +1124,26 @@ class Channel extends SnowFlake {
|
|||
console.log("vid", elm);
|
||||
box.append(elm);
|
||||
}
|
||||
makeBig(box: HTMLElement) {
|
||||
const par = box.parentElement;
|
||||
if (!par) return;
|
||||
if (par.children[0] !== box || !box.classList.contains("bigBox")) {
|
||||
box.classList.add("bigBox");
|
||||
if (par.children[0] !== box) {
|
||||
par.children[0].classList.remove("bigBox");
|
||||
}
|
||||
} else {
|
||||
par.children[0].classList.remove("bigBox");
|
||||
}
|
||||
par.prepend(box);
|
||||
}
|
||||
decorateLive(id: string) {
|
||||
if (!this.voice) return;
|
||||
const box = this.liveMap.get(id);
|
||||
if (!box) return;
|
||||
box.innerHTML = "";
|
||||
const live = this.voice.getLive(id);
|
||||
const self = id === this.localuser.user.id;
|
||||
if (!this.voice.open) {
|
||||
const span = document.createElement("span");
|
||||
span.textContent = I18n.vc.joinForStream();
|
||||
|
@ -1137,18 +1151,27 @@ class Channel extends SnowFlake {
|
|||
} else if (live) {
|
||||
const leave = document.createElement("button");
|
||||
leave.classList.add("leave");
|
||||
leave.textContent = I18n.vc.leavestream();
|
||||
leave.onclick = () => {
|
||||
this.voice?.leaveLive(id);
|
||||
leave.textContent = self ? I18n.vc.stopstream() : I18n.vc.leavestream();
|
||||
leave.onclick = (e) => {
|
||||
e.stopImmediatePropagation();
|
||||
if (self) {
|
||||
this.voice?.stopStream();
|
||||
} else {
|
||||
this.voice?.leaveLive(id);
|
||||
}
|
||||
};
|
||||
box.append(live, leave);
|
||||
} else {
|
||||
} else if (!self) {
|
||||
const joinB = document.createElement("button");
|
||||
joinB.textContent = I18n.vc.joinstream();
|
||||
joinB.classList.add("joinb");
|
||||
box.append(joinB);
|
||||
joinB.onclick = () => {
|
||||
if (!this.voice) return;
|
||||
box.innerHTML = "";
|
||||
const span = document.createElement("span");
|
||||
span.textContent = I18n.vc.joiningStream();
|
||||
box.append(span);
|
||||
this.voice.joinLive(id);
|
||||
};
|
||||
}
|
||||
|
@ -1194,6 +1217,9 @@ class Channel extends SnowFlake {
|
|||
} else if (!live && change.live && box) {
|
||||
const livediv = document.createElement("div");
|
||||
this.liveMap.set(id, livediv);
|
||||
livediv.onclick = () => {
|
||||
this.makeBig(livediv);
|
||||
};
|
||||
box.parentElement?.prepend(livediv);
|
||||
this.decorateLive(id);
|
||||
}
|
||||
|
@ -1217,6 +1243,9 @@ class Channel extends SnowFlake {
|
|||
async makeUserBox(user: User, users: HTMLElement) {
|
||||
const memb = Member.resolveMember(user, this.guild);
|
||||
const box = document.createElement("div");
|
||||
box.onclick = () => {
|
||||
this.makeBig(box);
|
||||
};
|
||||
this.boxMap.set(user.id, box);
|
||||
if (user.accent_color != undefined) {
|
||||
box.style.setProperty(
|
||||
|
@ -1297,6 +1326,7 @@ class Channel extends SnowFlake {
|
|||
updateVideoIcon();
|
||||
video.onclick = async () => {
|
||||
if (!this.voice) return;
|
||||
if (!this.voice.open) return;
|
||||
if (this.localuser.voiceFactory?.video) {
|
||||
this.voice.stopVideo();
|
||||
} else {
|
||||
|
@ -1317,23 +1347,39 @@ class Channel extends SnowFlake {
|
|||
video.classList.add("callVoiceIcon");
|
||||
|
||||
const updateLiveIcon = () => {
|
||||
lspan.classList.remove("svg-video", "svg-novideo");
|
||||
lspan.classList.add(true ? "svg-stream" : "svg-stopstream");
|
||||
lspan.classList.remove("svg-stream", "svg-stopstream");
|
||||
lspan.classList.add(this.voice?.isLive() ? "svg-stopstream" : "svg-stream");
|
||||
};
|
||||
const live = document.createElement("div");
|
||||
const lspan = document.createElement("span");
|
||||
live.append(lspan);
|
||||
updateLiveIcon();
|
||||
live.onclick = async () => {
|
||||
const stream = await navigator.mediaDevices.getDisplayMedia();
|
||||
this.voice?.createLive(this.localuser.user.id, stream);
|
||||
if (!this.voice?.open) return;
|
||||
if (this.voice?.isLive()) {
|
||||
this.voice?.stopStream();
|
||||
} else {
|
||||
const stream = await navigator.mediaDevices.getDisplayMedia();
|
||||
const v = await this.voice?.createLive(stream);
|
||||
console.log(v);
|
||||
}
|
||||
updateLiveIcon();
|
||||
};
|
||||
live.classList.add("callVoiceIcon");
|
||||
|
||||
buttonRow.append(mute, call, video, live);
|
||||
buttonRow.append(mute, video, live, call);
|
||||
|
||||
const users = document.createElement("div");
|
||||
const mut = new MutationObserver(() => {
|
||||
const arr = Array.from(users.children);
|
||||
const box = arr.find((_) => _.classList.contains("bigBox"));
|
||||
if (box && arr[0] !== box) {
|
||||
users.prepend(box);
|
||||
}
|
||||
});
|
||||
mut.observe(users, {
|
||||
childList: true,
|
||||
});
|
||||
users.classList.add("voiceUsers");
|
||||
|
||||
this.voice.userids.forEach(async (_, id) => {
|
||||
|
@ -1351,6 +1397,7 @@ class Channel extends SnowFlake {
|
|||
this.boxVid(id, vid);
|
||||
};
|
||||
this.voice.onGotStream = (_vid, id) => {
|
||||
updateLiveIcon();
|
||||
this.decorateLive(id);
|
||||
};
|
||||
this.voice.onconnect = () => {
|
||||
|
@ -1365,6 +1412,7 @@ class Channel extends SnowFlake {
|
|||
|
||||
this.voice.onLeave = () => {
|
||||
updateCallIcon();
|
||||
updateVideoIcon();
|
||||
for (const [id] of this.boxMap) {
|
||||
this.purgeVid(id);
|
||||
}
|
||||
|
|
1
src/webpage/icons/stopstream.svg
Normal file
1
src/webpage/icons/stopstream.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg width="48" height="48" xmlns="http://www.w3.org/2000/svg"><g style="opacity:1"><path d="M6.9 1.8a4 4 0 0 0-4 4v21.6a4 4 0 0 0 4 4h33.9a4 4 0 0 0 4-4V5.8a4 4 0 0 0-4-4zm5 3a2 2 0 0 1 1.1.5L23.2 14l10.1-8.7a2 2 0 0 1 1.5-.4 2 2 0 0 1 1.3.7 2 2 0 0 1-.2 2.8l-9.6 8.2 9.6 8.3a2 2 0 0 1 .2 2.8 2 2 0 0 1-2.8.2l-10.1-8.6L13 27.9a2 2 0 0 1-2.9-.2 2 2 0 0 1 .3-2.8l9.6-8.3-9.7-8.2a2 2 0 0 1-.2-2.8 2 2 0 0 1 1.4-.7 2 2 0 0 1 .2 0z" style="baseline-shift:baseline;display:inline;overflow:visible;vector-effect:none;stroke-linecap:round;stroke-linejoin:round;enable-background:accumulate;stop-color:#000;stop-opacity:1"/></g><path style="fill:#000;stroke:#000;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none" d="M21.8 28.2h3.3v10.7h-3.3z"/><path style="fill:#000;stroke:#000;stroke-width:6;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none" d="M11.9 41.7h23.6v2.2H11.9z"/></svg>
|
After Width: | Height: | Size: 920 B |
|
@ -218,6 +218,7 @@ body {
|
|||
.leave:hover {
|
||||
background: color-mix(in srgb, var(--red) 85%, white);
|
||||
}
|
||||
|
||||
.voiceUsers > * {
|
||||
background: var(--accent_color, var(--primary-bg));
|
||||
border-radius: 8px;
|
||||
|
@ -225,11 +226,11 @@ body {
|
|||
box-sizing: border-box;
|
||||
margin: 8px;
|
||||
width: 340px;
|
||||
height: 220px;
|
||||
aspect-ratio: 3/2;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
img {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
|
@ -237,12 +238,20 @@ body {
|
|||
}
|
||||
video {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
.voiceUsers:has(.bigBox) > * {
|
||||
width: 220px;
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.bigBox {
|
||||
width: min(80%, 600px) !important;
|
||||
height: unset;
|
||||
margin: 0 calc((100% - min(80%, 600px)) / 2);
|
||||
}
|
||||
|
||||
.buttonRow > * {
|
||||
margin-right: 6px;
|
||||
width: 54px;
|
||||
|
@ -529,6 +538,11 @@ textarea {
|
|||
mask: url(/icons/stream.svg);
|
||||
mask-size: contain !important;
|
||||
}
|
||||
.svg-stopstream {
|
||||
mask: url(/icons/stopstream.svg);
|
||||
mask-size: contain !important;
|
||||
background-color: var(--red);
|
||||
}
|
||||
.svg-video {
|
||||
mask: url(/icons/video.svg);
|
||||
mask-size: contain !important;
|
||||
|
|
|
@ -70,6 +70,7 @@ class VoiceFactory {
|
|||
},
|
||||
});
|
||||
}
|
||||
|
||||
updateSelf() {
|
||||
if (this.currentVoice && this.currentVoice.open) {
|
||||
this.handleGateway({
|
||||
|
@ -111,6 +112,16 @@ class VoiceFactory {
|
|||
op: 4,
|
||||
};
|
||||
}
|
||||
leaveLive() {
|
||||
const userid = this.settings.id;
|
||||
const stream_key = `${this.curGuild === "@me" ? "call" : `guild:${this.curGuild}`}:${this.curChan}:${userid}`;
|
||||
this.handleGateway({
|
||||
op: 19,
|
||||
d: {
|
||||
stream_key,
|
||||
},
|
||||
});
|
||||
}
|
||||
live = new Map<string, (res: Voice) => void>();
|
||||
steamTokens = new Map<string, Promise<[string, string]>>();
|
||||
steamTokensRes = new Map<string, (res: [string, string]) => void>();
|
||||
|
@ -134,7 +145,8 @@ class VoiceFactory {
|
|||
}
|
||||
islive = false;
|
||||
liveStream?: MediaStream;
|
||||
async createLive(userid: string, stream: MediaStream) {
|
||||
async createLive(stream: MediaStream) {
|
||||
const userid = this.settings.id;
|
||||
this.islive = true;
|
||||
this.liveStream = stream;
|
||||
const stream_key = `${this.curGuild === "@me" ? "call" : `guild:${this.curGuild}`}:${this.curChan}:${userid}`;
|
||||
|
@ -207,6 +219,9 @@ class VoiceFactory {
|
|||
};
|
||||
|
||||
voice2.gotStream(voice, user);
|
||||
console.warn(voice2);
|
||||
const res = this.live.get(create.d.stream_key);
|
||||
if (res) res(voice);
|
||||
}
|
||||
}
|
||||
streamServerUpdate(update: streamServerUpdate) {
|
||||
|
@ -410,6 +425,7 @@ class Voice {
|
|||
if (!ld) throw new Error("localDescription isn't defined");
|
||||
const parsed = Voice.parsesdp(ld.sdp);
|
||||
const group = parsed.atr.get("group");
|
||||
console.warn(parsed);
|
||||
if (!group) throw new Error("group isn't in sdp");
|
||||
const [_, ...bundles] = (group.entries().next().value as [string, string])[0].split(" ");
|
||||
bundles[bundles.length - 1] = bundles[bundles.length - 1].replace("\r", "");
|
||||
|
@ -508,13 +524,19 @@ a=rtcp-mux\r`;
|
|||
const sendOffer = async () => {
|
||||
console.trace("neg need");
|
||||
await pc.setLocalDescription();
|
||||
console.log("set local");
|
||||
|
||||
const senders = this.senders.difference(this.ssrcMap);
|
||||
console.log(senders, this.ssrcMap);
|
||||
for (const sender of senders) {
|
||||
for (const thing of (await sender.getStats()) as Map<string, any>) {
|
||||
console.log(sender);
|
||||
const d = (await sender.getStats()) as Map<string, any>;
|
||||
console.log([...d]);
|
||||
for (const thing of d) {
|
||||
if (thing[1].ssrc) {
|
||||
this.ssrcMap.set(sender, thing[1].ssrc);
|
||||
this.makeOp12(sender);
|
||||
console.warn("ssrc");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -532,14 +554,20 @@ a=rtcp-mux\r`;
|
|||
this.status = "Done";
|
||||
}
|
||||
};
|
||||
function logState(thing: string, message = "") {
|
||||
console.log(thing + (message ? ":" + message : ""));
|
||||
}
|
||||
pc.addEventListener("negotiationneeded", async () => {
|
||||
logState("negotiationneeded");
|
||||
await sendOffer();
|
||||
console.log(this.ssrcMap);
|
||||
});
|
||||
pc.addEventListener("signalingstatechange", async () => {
|
||||
logState("signalingstatechange", pc.signalingState);
|
||||
detectDone();
|
||||
while (!this.counter) await new Promise((res) => setTimeout(res, 100));
|
||||
if (this.pc && this.counter) {
|
||||
console.warn("in here :3");
|
||||
if (pc.signalingState === "have-local-offer") {
|
||||
const counter = this.counter;
|
||||
const remote: {sdp: string; type: RTCSdpType} = {
|
||||
|
@ -549,17 +577,20 @@ a=rtcp-mux\r`;
|
|||
console.log(remote);
|
||||
await pc.setRemoteDescription(remote);
|
||||
}
|
||||
} else {
|
||||
console.warn("uh oh!");
|
||||
}
|
||||
});
|
||||
pc.addEventListener("connectionstatechange", async () => {
|
||||
logState("connectionstatechange", pc.connectionState);
|
||||
detectDone();
|
||||
if (pc.connectionState === "connecting") {
|
||||
await pc.setLocalDescription();
|
||||
}
|
||||
});
|
||||
pc.addEventListener("icegatheringstatechange", async () => {
|
||||
logState("icegatheringstatechange", pc.iceGatheringState);
|
||||
detectDone();
|
||||
console.log("icegatheringstatechange", pc.iceGatheringState, this.pc, this.counter);
|
||||
if (this.pc && this.counter) {
|
||||
if (pc.iceGatheringState === "complete") {
|
||||
pc.setLocalDescription();
|
||||
|
@ -567,6 +598,7 @@ a=rtcp-mux\r`;
|
|||
}
|
||||
});
|
||||
pc.addEventListener("iceconnectionstatechange", async () => {
|
||||
logState("iceconnectionstatechange", pc.iceConnectionState);
|
||||
detectDone();
|
||||
if (pc.iceConnectionState === "checking") {
|
||||
sendOffer();
|
||||
|
@ -754,14 +786,17 @@ a=rtcp-mux\r`;
|
|||
}
|
||||
liveMap = new Map<string, HTMLVideoElement>();
|
||||
voiceMap = new Map<string, Voice>();
|
||||
isLive() {
|
||||
return !!this.voiceMap.get(this.userid);
|
||||
}
|
||||
getLive(id: string) {
|
||||
return this.liveMap.get(id);
|
||||
}
|
||||
joinLive(id: string) {
|
||||
return this.owner.joinLive(id);
|
||||
}
|
||||
createLive(id: string, stream: MediaStream) {
|
||||
return this.owner.createLive(id, stream);
|
||||
createLive(stream: MediaStream) {
|
||||
return this.owner.createLive(stream);
|
||||
}
|
||||
leaveLive(id: string) {
|
||||
const v = this.voiceMap.get(id);
|
||||
|
@ -771,6 +806,10 @@ a=rtcp-mux\r`;
|
|||
this.liveMap.delete(id);
|
||||
this.onLeaveStream(id);
|
||||
}
|
||||
stopStream() {
|
||||
this.leaveLive(this.userid);
|
||||
this.owner.leaveLive();
|
||||
}
|
||||
onLeaveStream = (_user: string) => {};
|
||||
onGotStream = (_v: HTMLVideoElement, _user: string) => {};
|
||||
gotStream(voice: Voice, user: string) {
|
||||
|
@ -1168,7 +1207,9 @@ a=rtcp-mux\r`;
|
|||
console.warn("leave");
|
||||
this.open = false;
|
||||
this.status = "Left voice chat";
|
||||
if (!this.settings.stream) this.owner.video = false;
|
||||
this.onLeave();
|
||||
|
||||
for (const thing of this.liveMap) {
|
||||
this.leaveLive(thing[0]);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue