implement vote-to-reset, screenshotting, and fix system messages

This commit is contained in:
Elijah R 2024-02-03 21:53:10 -05:00
parent 659d3baaea
commit 901109a389
3 changed files with 98 additions and 6 deletions

View File

@ -7,6 +7,7 @@ import TurnStatus from "./protocol/TurnStatus.js";
import Keyboard from "simple-keyboard";
import { OSK_buttonToKeysym } from "./keyboard";
import "simple-keyboard/build/css/index.css";
import VoteStatus from "./protocol/VoteStatus.js";
// Elements
const w = window as any;
@ -27,7 +28,15 @@ const elements = {
turnBtnText: document.getElementById("turnBtnText") as HTMLSpanElement,
turnstatus: document.getElementById("turnstatus") as HTMLParagraphElement,
osk: window.document.getElementById("oskBtn") as HTMLButtonElement,
oskContainer: document.getElementById("osk-container") as HTMLDivElement
oskContainer: document.getElementById("osk-container") as HTMLDivElement,
screenshotButton: document.getElementById("screenshotButton") as HTMLButtonElement,
voteResetButton: document.getElementById("voteResetButton") as HTMLButtonElement,
voteResetPanel: document.getElementById("voteResetPanel") as HTMLDivElement,
voteYesBtn: document.getElementById("voteYesBtn") as HTMLButtonElement,
voteNoBtn: document.getElementById("voteNoBtn") as HTMLButtonElement,
voteYesLabel: document.getElementById("voteYesLabel") as HTMLSpanElement,
voteNoLabel: document.getElementById("voteNoLabel") as HTMLSpanElement,
votetime: document.getElementById("votetime") as HTMLSpanElement,
}
/* Start OSK */
@ -224,8 +233,10 @@ const users : {
user : User,
element : HTMLTableRowElement
}[] = [];
var turnInterval : number | undefined = undefined;;
var turnInterval : number | undefined = undefined;
var voteInterval : number | undefined = undefined;
var turnTimer = 0;
var voteTimer = 0;
// Active VM
var VM : CollabVMClient | null = null;
@ -294,6 +305,9 @@ function openVM(vm : VM) {
}
}));
listeners.push(VM!.on('turn', status => turnUpdate(status)));
listeners.push(VM!.on('vote', (status : VoteStatus) => voteUpdate(status)));
listeners.push(VM!.on('voteend', () => voteEnd()));
listeners.push(VM!.on('votecd', cd => window.alert(`Please wait ${cd} seconds before starting another vote.`)));
listeners.push(VM!.on('close', () => {
if (!expectedClose) alert("You have been disconnected from the server");
for (var l of listeners) l();
@ -352,7 +366,7 @@ function loadList() {
function sortVMList() {
cards.sort(function(a, b) {
return a.getAttribute("data-cvm-node")! > b.getAttribute("data-cvm-node")! ? 1 : -1;
return a.children[0].getAttribute("data-cvm-node")! > b.children[0].getAttribute("data-cvm-node")! ? 1 : -1;
});
elements.vmlist.children[0].innerHTML = "";
cards.forEach((c) => elements.vmlist.children[0].appendChild(c));
@ -408,11 +422,11 @@ function chatMessage(username : string, message : string) {
eval(curr.text)
}
});
}
tr.appendChild(td);
elements.chatList.appendChild(tr);
elements.chatListDiv.scrollTop = elements.chatListDiv.scrollHeight;
}
}
function addUser(user : User) {
var olduser = users.find(u => u.user === user);
@ -504,6 +518,27 @@ function turnUpdate(status : TurnStatus) {
sortUserList();
}
function voteUpdate(status : VoteStatus) {
clearInterval(voteInterval);
elements.voteResetPanel.style.display = "block";
elements.voteYesLabel.innerText = status.yesVotes.toString();
elements.voteNoLabel.innerText = status.noVotes.toString();
voteTimer = Math.floor(status.timeToEnd / 1000);
voteInterval = setInterval(() => updateVoteEndTime(), 1000);
updateVoteEndTime();
}
function updateVoteEndTime() {
voteTimer--;
elements.votetime.innerText = voteTimer.toString();
if (voteTimer === 0) clearInterval(voteInterval);
}
function voteEnd() {
clearInterval(voteInterval);
elements.voteResetPanel.style.display = "none";
}
function turnIntervalCb() {
turnTimer--;
setTurnStatus();
@ -538,6 +573,15 @@ elements.changeUsernameBtn.addEventListener('click', () => {
elements.takeTurnBtn.addEventListener('click', () => {
VM?.turn(turn === -1);
});
elements.screenshotButton.addEventListener('click', () => {
if (!VM) return;
VM.canvas.toBlob(blob => {
open(URL.createObjectURL(blob!), '_blank');
})
});
elements.voteResetButton.addEventListener('click', () => VM?.vote(true));
elements.voteYesBtn.addEventListener('click', () => VM?.vote(true));
elements.voteNoBtn.addEventListener('click', () => VM?.vote(false));
elements.osk.addEventListener('click', () => elements.oskContainer.classList.toggle('d-none'));

View File

@ -6,6 +6,7 @@ import { Rank } from "./Permissions.js";
import TurnStatus from "./TurnStatus.js";
import Mouse from "./mouse.js";
import GetKeysym from '../keyboard.js';
import VoteStatus from "./VoteStatus.js";
export default class CollabVMClient {
// Fields
@ -18,6 +19,7 @@ export default class CollabVMClient {
private username : string | null = null;
private mouse : Mouse = new Mouse();
private rank : Rank = Rank.Unregistered;
private voteStatus : VoteStatus | null = null;
// events that are used internally and not exposed
private emitter;
// public events
@ -63,6 +65,7 @@ export default class CollabVMClient {
capture: true
});
this.canvas.addEventListener('keydown', (e : KeyboardEvent) => {
e.preventDefault();
if (this.users.find(u => u.username === this.username)?.turn === -1 && this.rank !== Rank.Admin) return;
var keysym = GetKeysym(e.keyCode, e.key, e.location);
if (keysym === null) return;
@ -71,6 +74,7 @@ export default class CollabVMClient {
capture: true
});
this.canvas.addEventListener('keyup', (e : KeyboardEvent) => {
e.preventDefault();
if (this.users.find(u => u.username === this.username)?.turn === -1 && this.rank !== Rank.Admin) return;
var keysym = GetKeysym(e.keyCode, e.key, e.location);
if (keysym === null) return;
@ -218,6 +222,35 @@ export default class CollabVMClient {
} as TurnStatus)
break;
}
case "vote": {
switch (msgArr[1]) {
case "0":
// Vote started
case "1":
// Vote updated
var timeToEnd = parseInt(msgArr[2]);
var yesVotes = parseInt(msgArr[3]);
var noVotes = parseInt(msgArr[4]);
// Some server implementations dont send data for status 0, and some do
if (Number.isNaN(timeToEnd) || Number.isNaN(yesVotes) || Number.isNaN(noVotes)) return;
this.voteStatus = {
timeToEnd: timeToEnd,
yesVotes: yesVotes,
noVotes: noVotes,
};
this.publicEmitter.emit('vote', this.voteStatus);
break;
case "2":
// Vote ended
this.voteStatus = null;
this.publicEmitter.emit('voteend');
break;
case "3":
// Cooldown
this.publicEmitter.emit('votecd', parseInt(msgArr[2]));
break;
}
}
}
}
@ -299,5 +332,15 @@ export default class CollabVMClient {
this.send("key", keysym.toString(), down ? "1" : "0");
}
// Get vote status
getVoteStatus() : VoteStatus | null {
return this.voteStatus;
}
// Start a vote, or vote
vote(vote : boolean) {
this.send("vote", vote ? "1" : "0");
}
on = (event : string | number, cb: (...args: any) => void) => this.publicEmitter.on(event, cb);
}

View File

@ -0,0 +1,5 @@
export default interface VoteStatus {
timeToEnd: number;
yesVotes: number;
noVotes: number;
}