Hash-based routing (#1198)

## Description:

Implement hash-based routing.

Fixes #1111

## Please complete the following:

- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
- [x] I understand that submitting code with bugs that could have been
caught through manual testing blocks releases and new features for all
contributors

---------

Co-authored-by: Scott Anderson <662325+scottanderson@users.noreply.github.com>
This commit is contained in:
Scott Anderson
2025-06-16 21:45:53 -04:00
committed by GitHub
parent b17b925b3b
commit ec4ef01d99
6 changed files with 36 additions and 24 deletions
-6
View File
@@ -54,7 +54,6 @@
"node-addon-api": "^8.1.0",
"node-gyp": "^10.2.0",
"obscenity": "^0.4.3",
"page": "^1.3.7",
"pg": "^8.13.3",
"priority-queue-typescript": "^1.0.1",
"prom-client": "^15.1.3",
@@ -17919,11 +17918,6 @@
"integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
"license": "BlueOak-1.0.0"
},
"node_modules/page": {
"version": "1.3.7",
"resolved": "https://registry.npmjs.org/page/-/page-1.3.7.tgz",
"integrity": "sha512-1MzNKSvcVePQDErGsfK22xmtdD8AQNj5g8U3OWUJJdlP5wd7yVxCLFbJutMkI5j9pRT/ZCn5kS8Rr6em6LIXsA=="
},
"node_modules/pako": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
-1
View File
@@ -129,7 +129,6 @@
"node-addon-api": "^8.1.0",
"node-gyp": "^10.2.0",
"obscenity": "^0.4.3",
"page": "^1.3.7",
"pg": "^8.13.3",
"priority-queue-typescript": "^1.0.1",
"prom-client": "^15.1.3",
+1 -1
View File
@@ -538,7 +538,7 @@ export class HostLobbyModal extends LitElement {
try {
//TODO: Convert id to url and copy
await navigator.clipboard.writeText(
`${location.origin}/join/${this.lobbyId}`,
`${location.origin}#join=${this.lobbyId}`,
);
this.copySuccess = true;
setTimeout(() => {
+29 -14
View File
@@ -1,6 +1,5 @@
import page from "page";
import favicon from "../../resources/images/Favicon.svg";
import { GameRecord, GameStartInfo } from "../core/Schemas";
import { GameRecord, GameStartInfo, ID } from "../core/Schemas";
import { getServerConfigFromClient } from "../core/configuration/ConfigLoader";
import { GameType } from "../core/game/Game";
import { UserSettings } from "../core/game/UserSettings";
@@ -247,20 +246,25 @@ class Client {
} else {
document.documentElement.classList.remove("dark");
}
page("/join/:lobbyId", (ctx) => {
if (ctx.init && sessionStorage.getItem("inLobby")) {
// On page reload, go back home
page("/");
return;
// Attempt to join lobby
this.handleHash();
const onHashUpdate = () => {
// Reset the UI to its initial state
this.joinModal.close();
if (this.gameStop !== null) {
this.handleLeaveLobby();
}
const lobbyId = ctx.params.lobbyId;
this.joinModal.open(lobbyId);
// Attempt to join lobby
this.handleHash();
};
console.log(`joining lobby ${lobbyId}`);
});
// Handle browser navigation & manual hash edits
window.addEventListener("popstate", onHashUpdate);
window.addEventListener("hashchange", onHashUpdate);
page();
function updateSliderProgress(slider) {
const percent =
((slider.value - slider.min) / (slider.max - slider.min)) * 100;
@@ -275,6 +279,18 @@ class Client {
});
}
private handleHash() {
const { hash } = window.location;
if (hash.startsWith("#")) {
const params = new URLSearchParams(hash.slice(1));
const lobbyId = params.get("join");
if (lobbyId && ID.safeParse(lobbyId).success) {
this.joinModal.open(lobbyId);
console.log(`joining lobby ${lobbyId}`);
}
}
}
private async handleJoinLobby(event: CustomEvent) {
const lobby = event.detail as JoinLobbyEvent;
console.log(`joining lobby ${lobby.gameID}`);
@@ -343,8 +359,7 @@ class Client {
});
if (event.detail.gameConfig?.gameType !== GameType.Singleplayer) {
window.history.pushState({}, "", `/join/${lobby.gameID}`);
sessionStorage.setItem("inLobby", "true");
history.pushState(null, "", `#join=${lobby.gameID}`);
}
},
);
+5 -1
View File
@@ -28,12 +28,16 @@ function getToken(): string | null {
const token = params.get("token");
if (token) {
localStorage.setItem("token", token);
params.delete("token");
params.toString();
}
// Clean the URL
history.replaceState(
null,
"",
window.location.pathname + window.location.search,
window.location.pathname +
window.location.search +
(params.size > 0 ? "#" + params.toString() : ""),
);
}
return localStorage.getItem("token");
+1 -1
View File
@@ -161,7 +161,7 @@ const EmojiSchema = z
.number()
.nonnegative()
.max(flattenedEmojiTable.length - 1);
const ID = z
export const ID = z
.string()
.regex(/^[a-zA-Z0-9]+$/)
.length(8);