Discord(et al.) embedded URLs (#2740)

## Description:

Changes URL embeds within other platforms, e.g. Discord, WhatsApp & X.

Updates game URLs to `/game/<code>` instead of `/#join=<code>` (required
for embedded URLs). An added benefit of this is that you would be able
to change a url from `openfront.io/game/RQDUy8nP?replay` to
`api.openfront.io/game/RQDUy8nP?replay` (add api. In front) and be in
the right place for the API data.

Updates URLs when joining/leaving private lobbies

Appends a random string to the end of the URL when inside a private
lobby and options change - this is to force discord to update the
embedded details.

Updates URL in different game states to ?lobby / ?live and ?replay.
These do nothing other than being used as a _cache-busting_ solution.


-----------------------------------------------
### **Lobby Info**

Discord:
<img width="556" height="487" alt="image"
src="https://github.com/user-attachments/assets/efd4a06d-506c-4036-9403-ee7c9a669e21"
/>

WhatsApp:
<img width="353" height="339" alt="image"
src="https://github.com/user-attachments/assets/3b2d0c69-988c-424f-9dee-f4e6a6868f6b"
/>


x.com:
<img width="588" height="325" alt="image"
src="https://github.com/user-attachments/assets/d9e78169-20be-4a3e-8df4-8ad41d08a750"
/>


-------------------------
### **Game Win Details**
Discord:
<img width="506" height="468" alt="image"
src="https://github.com/user-attachments/assets/69947774-c943-4a50-b470-5634ed3bf3d7"
/>

WhatsApp:
<img width="770" height="132" alt="image"
src="https://github.com/user-attachments/assets/eec28bf8-bf64-4ab8-954e-03dfdd1aae40"
/>

x.com
<img width="584" height="350" alt="image"
src="https://github.com/user-attachments/assets/168063e2-b707-422b-b7a1-0025f3ebeb92"
/>


## 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

## Please put your Discord username so you can be contacted if a bug or
regression is found:

w.o.n
This commit is contained in:
Ryan
2026-01-13 19:48:00 -08:00
committed by GitHub
parent c80ccaece9
commit 247c78151c
15 changed files with 832 additions and 345 deletions
+36 -14
View File
@@ -1,7 +1,7 @@
import version from "resources/version.txt?raw";
import { UserMeResponse } from "../core/ApiSchemas";
import { EventBus } from "../core/EventBus";
import { GameRecord, GameStartInfo, ID } from "../core/Schemas";
import { GAME_ID_REGEX, GameRecord, GameStartInfo } from "../core/Schemas";
import { GameEnv } from "../core/configuration/Config";
import { getServerConfigFromClient } from "../core/configuration/ConfigLoader";
import { GameType } from "../core/game/Game";
@@ -193,6 +193,7 @@ declare global {
interface DocumentEventMap {
"join-lobby": CustomEvent<JoinLobbyEvent>;
"kick-player": CustomEvent;
"join-changed": CustomEvent;
}
}
@@ -607,6 +608,7 @@ class Client {
onHashUpdate();
});
window.addEventListener("hashchange", onHashUpdate);
window.addEventListener("join-changed", onHashUpdate);
function updateSliderProgress(slider: HTMLInputElement) {
const percent =
@@ -632,7 +634,7 @@ class Client {
// Check if CrazyGames SDK is enabled first (no hash needed in CrazyGames)
if (crazyGamesSDK.isOnCrazyGames()) {
const lobbyId = crazyGamesSDK.getInviteGameId();
if (lobbyId && ID.safeParse(lobbyId).success) {
if (lobbyId && GAME_ID_REGEX.test(lobbyId)) {
window.showPage?.("page-join-private-lobby");
this.joinModal?.open(lobbyId);
console.log(`CrazyGames: joining lobby ${lobbyId} from invite param`);
@@ -708,14 +710,16 @@ class Client {
return;
}
// Fallback to hash-based join for non-CrazyGames environments
if (decodedHash.startsWith("#join=")) {
const lobbyId = decodedHash.substring(6); // Remove "#join="
if (lobbyId && ID.safeParse(lobbyId).success) {
window.showPage?.("page-join-private-lobby");
this.joinModal?.open(lobbyId);
console.log(`joining lobby ${lobbyId}`);
}
const pathMatch = window.location.pathname.match(
/^\/(?:w\d+\/)?game\/([^/]+)/,
);
const lobbyId =
pathMatch && GAME_ID_REGEX.test(pathMatch[1]) ? pathMatch[1] : null;
if (lobbyId) {
window.showPage?.("page-join-private-lobby");
this.joinModal.open(lobbyId);
console.log(`joining lobby ${lobbyId}`);
return;
}
if (decodedHash.startsWith("#affiliate=")) {
const affiliateCode = decodedHash.replace("#affiliate=", "");
@@ -738,6 +742,7 @@ class Client {
document.body.classList.remove("in-game");
}
const config = await getServerConfigFromClient();
this.updateJoinUrlForShare(lobby.gameID, config);
const pattern = this.userSettings.getSelectedPatternName(
await fetchCosmetics(),
@@ -778,15 +783,16 @@ class Client {
"host-lobby-modal",
"join-private-lobby-modal",
"game-starting-modal",
"game-top-bar",
"help-modal",
"user-setting",
"territory-patterns-modal",
"language-modal",
"news-modal",
"flag-input-modal",
"account-button",
"stats-button",
"token-login",
"matchmaking-modal",
"lang-selector",
].forEach((tag) => {
@@ -817,7 +823,7 @@ class Client {
this.gutterAds.hide();
},
() => {
this.joinModal?.close();
this.joinModal.close();
this.publicLobby.stop();
incrementGamesPlayed();
@@ -833,11 +839,27 @@ class Client {
if (window.location.hash === "" || window.location.hash === "#") {
history.replaceState(null, "", window.location.origin + "#refresh");
}
history.pushState(null, "", `#join=${lobby.gameID}`);
history.pushState(
null,
"",
`/${config.workerPath(lobby.gameID)}/game/${lobby.gameID}?live`,
);
},
);
}
private updateJoinUrlForShare(
lobbyId: string,
config: Awaited<ReturnType<typeof getServerConfigFromClient>>,
) {
const targetUrl = `/${config.workerPath(lobbyId)}/game/${lobbyId}`;
const currentUrl = window.location.pathname;
if (currentUrl !== targetUrl) {
history.replaceState(null, "", targetUrl);
}
}
private async handleLeaveLobby(/* event: CustomEvent */) {
if (this.gameStop === null) {
return;