diff --git a/resources/lang/en.json b/resources/lang/en.json index e5fc91da5..e45598c07 100644 --- a/resources/lang/en.json +++ b/resources/lang/en.json @@ -268,6 +268,8 @@ "public_lobby": { "join": "Join next Game", "waiting": "players waiting", + "waiting_for_players": "Waiting for players", + "starting_game": "Starting game…", "teams_Duos": "of 2 (Duos)", "teams_Trios": "of 3 (Trios)", "teams_Quads": "of 4 (Quads)", diff --git a/src/client/PublicLobby.ts b/src/client/PublicLobby.ts index 2e79119cb..363540dd2 100644 --- a/src/client/PublicLobby.ts +++ b/src/client/PublicLobby.ts @@ -20,7 +20,10 @@ export class PublicLobby extends LitElement { @state() public isLobbyHighlighted: boolean = false; @state() private isButtonDebounced: boolean = false; @state() private mapImages: Map = new Map(); + @state() private joiningDotIndex: number = 0; + private lobbiesInterval: number | null = null; + private joiningInterval: number | null = null; private currLobby: GameInfo | null = null; private debounceDelay: number = 750; private lobbyIDToStart = new Map(); @@ -45,20 +48,18 @@ export class PublicLobby extends LitElement { clearInterval(this.lobbiesInterval); this.lobbiesInterval = null; } + this.stopJoiningAnimation(); } private async fetchAndUpdateLobbies(): Promise { try { this.lobbies = await this.fetchLobbies(); this.lobbies.forEach((l) => { - // Store the start time on first fetch because endpoint is cached, causing - // the time to appear irregular. if (!this.lobbyIDToStart.has(l.gameID)) { const msUntilStart = l.msUntilStart ?? 0; this.lobbyIDToStart.set(l.gameID, msUntilStart + Date.now()); } - // Load map image if not already loaded if (l.gameConfig && !this.mapImages.has(l.gameID)) { this.loadMapImage(l.gameID, l.gameConfig.gameMap); } @@ -70,7 +71,6 @@ export class PublicLobby extends LitElement { private async loadMapImage(gameID: GameID, gameMap: string) { try { - // Convert string to GameMapType enum value const mapType = gameMap as GameMapType; const data = terrainMapFileLoader.getMapData(mapType); this.mapImages.set(gameID, await data.webpPath()); @@ -106,6 +106,7 @@ export class PublicLobby extends LitElement { public stop() { if (this.lobbiesInterval !== null) { this.isLobbyHighlighted = false; + this.stopJoiningAnimation(); clearInterval(this.lobbiesInterval); this.lobbiesInterval = null; } @@ -115,13 +116,11 @@ export class PublicLobby extends LitElement { if (this.lobbies.length === 0) return html``; const lobby = this.lobbies[0]; - if (!lobby?.gameConfig) { - return; - } + if (!lobby?.gameConfig) return html``; + const start = this.lobbyIDToStart.get(lobby.gameID) ?? 0; const timeRemaining = Math.max(0, Math.floor((start - Date.now()) / 1000)); - - // Format time to show minutes and seconds + const isStarting = timeRemaining <= 2; const timeDisplay = renderDuration(timeRemaining); const teamCount = @@ -177,17 +176,26 @@ export class PublicLobby extends LitElement { >
- ${translateText("public_lobby.join")} + ${this.currLobby + ? isStarting + ? html`${translateText("public_lobby.starting_game")}` + : html`${translateText("public_lobby.waiting_for_players")} + ${[0, 1, 2] + .map((i) => (i === this.joiningDotIndex ? "•" : "·")) + .join("")}` + : translateText("public_lobby.join")}
- ${fullModeLabel} - ${translateText( - `map.${lobby.gameConfig.gameMap.toLowerCase().replace(/[\s.]+/g, "")}`, - )} + + ${fullModeLabel} + + + ${translateText( + `map.${lobby.gameConfig.gameMap + .toLowerCase() + .replace(/[\s.]+/g, "")}`, + )} +
@@ -205,6 +213,24 @@ export class PublicLobby extends LitElement { leaveLobby() { this.isLobbyHighlighted = false; this.currLobby = null; + this.stopJoiningAnimation(); + } + + private startJoiningAnimation() { + if (this.joiningInterval !== null) return; + + this.joiningDotIndex = 0; + this.joiningInterval = window.setInterval(() => { + this.joiningDotIndex = (this.joiningDotIndex + 1) % 3; + }, 500); + } + + private stopJoiningAnimation() { + if (this.joiningInterval !== null) { + clearInterval(this.joiningInterval); + this.joiningInterval = null; + } + this.joiningDotIndex = 0; } private getTeamSize( @@ -270,14 +296,9 @@ export class PublicLobby extends LitElement { } private lobbyClicked(lobby: GameInfo) { - if (this.isButtonDebounced) { - return; - } + if (this.isButtonDebounced) return; - // Set debounce state this.isButtonDebounced = true; - - // Reset debounce after delay setTimeout(() => { this.isButtonDebounced = false; }, this.debounceDelay); @@ -285,6 +306,7 @@ export class PublicLobby extends LitElement { if (this.currLobby === null) { this.isLobbyHighlighted = true; this.currLobby = lobby; + this.startJoiningAnimation(); this.dispatchEvent( new CustomEvent("join-lobby", { detail: {