Skip to content

Conversation

@VariableVince
Copy link
Contributor

@VariableVince VariableVince commented Jan 24, 2026

Description:

Fix for v29. PR #2397 added confirmation dialog on browser back button, back to v29. However the confirm dialog wasn't working in three cases: 1) not displayed on mobile, you were instead directly returned to the main page. 2) Also the dialog wasn't shown during spawn phase and 3) not shown directly after spawn phase before first click on the map.

This PR fixes all three issues. The confirm dialog is still only shown during a game, not elsewhere.

But this PR introduces one small unpreventable edge case regression. This is explained in the code comments. On back button click, then cancel, then back button click again, browser can block popState event for user safety. So nothing happens on repeated back button clicks on desktop and on mobile the browser back button is greyed out/blocked.
But after user then clicks somewhere on the page, the back button triggers popState and thus the confirm modal again.
Only way to prevent popState blocking by the browser was the previous way of doing it; only have pushState after !isConfirmed, but that breaks the confirm dialog on mobile. Also this edge case isn't something the average user will notice, normally they won't click the back button right away after first clicking it and then cancelling it. They'll do some clicks playing the game before deciding to click the back button again, which prevents popState from being blocked by the browser. And the better way to leave a game is still the Exit button in the top right so they'll have a way out even when the browser blocks the back button temporarily.

BEFORE:
https://github.com/user-attachments/assets/90a01887-59cf-436a-91a3-3f617cf8a89e

AFTER:
https://github.com/user-attachments/assets/7c3c340e-2bd6-4785-8bbf-ba0705a5940c

Please complete the following:

  • I have added screenshots for all UI updates
  • I process any text displayed to the user through translateText() and I've added it to the en.json file
  • I have added relevant tests to the test directory
  • 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:

tryout33

…hase and before first click after spawn phase
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 24, 2026

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main fix: restoring and fixing the confirmation dialog for the back button on mobile and during spawn phase.
Description check ✅ Passed The description is directly related to the changeset, explaining the fix for three specific cases where the confirmation dialog wasn't working and documenting a known edge-case regression.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/client/Main.ts (1)

595-611: Prevent hashchange event after canceled back button in active game.

When user presses back with an active game and cancels the confirmation, pushState at line 602 can trigger a hashchange event, which runs onHashUpdateonJoinChangedhandleLeaveLobby, defeating the cancel. Add a simple guard flag to prevent this race condition.

Proposed fix
    const onHashUpdate = () => {
+     if (preventHashUpdate) {
+       preventHashUpdate = false;
+       return;
+     }
      // Reset the UI to its initial state
      this.joinModal?.close();

      onJoinChanged();
    };

    const onPopState = () => {
      if (this.currentUrl !== null && this.gameStop !== null) {
        console.info("Game is active");

        if (!this.gameStop()) {
          console.info("Player is active, ask before leaving game");

          // Rollback navigator history (here so the confirm dialog also works on mobile)
+         preventHashUpdate = true;
          history.pushState(null, "", this.currentUrl);

          const isConfirmed = confirm(
            translateText("help_modal.exit_confirmation"),
          );

          if (!isConfirmed) {
            return;
          }

coderabbitai[bot]
coderabbitai bot previously approved these changes Jan 24, 2026
@VariableVince VariableVince marked this pull request as ready for review January 24, 2026 17:34
@VariableVince VariableVince requested a review from a team as a code owner January 24, 2026 17:34
@VariableVince
Copy link
Contributor Author

Also further partially handles #1877 as a follow-up fix after PR #2777

@VariableVince VariableVince marked this pull request as draft January 24, 2026 17:44
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/client/Main.ts (1)

595-618: Add a guard flag to prevent hashchange after canceling back navigation

When the user cancels leaving the game, the history manipulation triggers a hashchange event that executes onHashUpdate and calls onJoinChanged(), which can exit the game despite the user canceling. Use a flag to skip the next hashchange when canceling, matching the previous implementation pattern.

🔧 Suggested fix
+    let preventHashUpdate = false;
+
     const onHashUpdate = () => {
+      if (preventHashUpdate) {
+        preventHashUpdate = false;
+        return;
+      }
       // Reset the UI to its initial state
       this.joinModal?.close();

       onJoinChanged();
     };

     const onPopState = () => {
       if (this.currentUrl !== null && this.gameStop !== null) {
         console.info("Game is active");

         if (!this.gameStop()) {
           console.info("Player is active, ask before leaving game");

+          preventHashUpdate = true;
           // Rollback navigator history to stay in game on cancel
           // Before calling confirm so dialog also shows on mobile
           history.replaceState(null, "", this.currentUrl);

           const isConfirmed = confirm(
             translateText("help_modal.exit_confirmation"),
           );

           if (!isConfirmed) {
             // Rebuild stack:
             // - prevent being put back to homepage when clicking back button again after cancelling
             // - replaceState instead of 2x pushState for cleaner history
             // Can't prevent browser blocking popState on clicking back button again without in-game click first
             // - preventable by only having pushState this.currentUrl here, which breaks confirm on mobile
             history.replaceState(null, "", window.location.origin + "#refresh");
             history.pushState(null, "", this.currentUrl);
             return;
           }

coderabbitai[bot]
coderabbitai bot previously approved these changes Jan 24, 2026
@VariableVince VariableVince marked this pull request as draft January 24, 2026 19:55
coderabbitai[bot]
coderabbitai bot previously approved these changes Jan 24, 2026
@deshack
Copy link
Contributor

deshack commented Jan 25, 2026

Points 2 and maybe 3 are by design and consistency with the exit button: you need to confirm the action only if you are active in the game. Otherwise, exit immediately.

// Rollback navigator history to stay in game on cancel
// Before calling confirm so dialog also shows on mobile
history.pushState(null, "", window.location.origin + "#refresh");
history.pushState(null, "", this.currentUrl);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I'm not mistaken, you are rolling navigation back even if the user confirms he wants to exit the game.
Am I right?

@deshack
Copy link
Contributor

deshack commented Jan 25, 2026

Nevermind, I think I got your point

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Bugfix Fixes a bug

Projects

Status: Triage

Development

Successfully merging this pull request may close these issues.

3 participants