From 32adfa2f79350d8eb5307971b4ee22841182d6b1 Mon Sep 17 00:00:00 2001 From: Skigim Date: Sat, 7 Feb 2026 14:51:02 -0600 Subject: [PATCH] Add requeue button to Ranked victory/defeat modal (#3121) ## Description: Adds a "Play Again" requeue button to the victory/defeat modal for Ranked 1v1 games. When clicked, it navigates the player back to the homepage and automatically opens the matchmaking modal to queue for another ranked match. Changes: - WinModal.ts: Added isRankedGame state, purple "Play Again" button (only shown for ranked 1v1), and _handleRequeue() method - Main.ts: Added ?requeue URL parameter handling to trigger matchmaking modal on page load - en.json: Added "requeue": "Play Again" translation string - added tests to WinModal.test.ts Note: temporarily set isRanked flag to true to get the modal to pop in a solo match on dev server and confirmed that ?requeue URL parameter called _handleRequeue() correctly, which opened the sign in process since actually signing in and queuing for a ranked match isn't possible on dev server. play-again ## 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: skigim --- resources/lang/en.json | 1 + src/client/Main.ts | 26 ++++ src/client/Matchmaking.ts | 2 +- src/client/graphics/layers/WinModal.ts | 23 ++++ tests/client/graphics/layers/WinModal.test.ts | 117 ++++++++++++++++++ 5 files changed, 168 insertions(+), 1 deletion(-) create mode 100644 tests/client/graphics/layers/WinModal.test.ts diff --git a/resources/lang/en.json b/resources/lang/en.json index 9750864e8..2bcf1a731 100644 --- a/resources/lang/en.json +++ b/resources/lang/en.json @@ -718,6 +718,7 @@ "exit": "Exit Game", "keep": "Keep Playing", "spectate": "Spectate", + "requeue": "Play Again", "wishlist": "Wishlist on Steam!", "ofm_winter": "OpenFront Masters Winter Tournament!", "ofm_winter_description": "Join the competitive tournament and compete against the best players", diff --git a/src/client/Main.ts b/src/client/Main.ts index ba6569889..0b29ee0a7 100644 --- a/src/client/Main.ts +++ b/src/client/Main.ts @@ -755,6 +755,32 @@ class Client { if (decodedHash.startsWith("#refresh")) { window.location.href = "/"; } + + // Handle requeue parameter for ranked matchmaking + const searchParams = new URLSearchParams(window.location.search); + if (searchParams.has("requeue")) { + // Remove only the requeue parameter, preserving other params and hash + searchParams.delete("requeue"); + const newUrl = + window.location.pathname + + (searchParams.toString() ? "?" + searchParams.toString() : "") + + window.location.hash; + history.replaceState(null, "", newUrl); + // Wait for matchmaking button to be defined, then trigger its click handler + // This goes through username validation instead of bypassing it + customElements.whenDefined("matchmaking-button").then(() => { + const matchmakingButton = document.querySelector( + "matchmaking-button button", + ) as HTMLButtonElement | null; + if (matchmakingButton) { + matchmakingButton.click(); + } else { + console.warn( + "Requeue requested, but matchmaking button not found in DOM.", + ); + } + }); + } } private async handleJoinLobby(event: CustomEvent) { diff --git a/src/client/Matchmaking.ts b/src/client/Matchmaking.ts index 3fa4af738..2d8635d2d 100644 --- a/src/client/Matchmaking.ts +++ b/src/client/Matchmaking.ts @@ -320,7 +320,7 @@ export class MatchmakingButton extends LitElement { window.showPage?.("page-account"); } - private open() { + public open() { this.matchmakingModal?.open(); } diff --git a/src/client/graphics/layers/WinModal.ts b/src/client/graphics/layers/WinModal.ts index a6334379b..af2765e66 100644 --- a/src/client/graphics/layers/WinModal.ts +++ b/src/client/graphics/layers/WinModal.ts @@ -8,6 +8,7 @@ import { } from "../../../client/Utils"; import { ColorPalette, Pattern } from "../../../core/CosmeticSchemas"; import { EventBus } from "../../../core/EventBus"; +import { RankedType } from "../../../core/game/Game"; import { GameUpdateType } from "../../../core/game/GameUpdates"; import { GameView } from "../../../core/game/GameView"; import { getUserMe } from "../../Api"; @@ -37,6 +38,9 @@ export class WinModal extends LitElement implements Layer { @state() private isWin = false; + @state() + private isRankedGame = false; + @state() private patternContent: TemplateResult | null = null; @@ -75,6 +79,16 @@ export class WinModal extends LitElement implements Layer { > ${translateText("win_modal.exit")} + ${this.isRankedGame + ? html` + + ` + : null}