Add fullscreen support: HUD button (desktop/Android) + iOS Add to Home Screen banner#3688
Conversation
WalkthroughAdds an iOS-only "add to home screen" banner with a guided modal and persistence, localization strings for iOS instructions and fullscreen labels, and a fullscreen toggle in the game HUD with Fullscreen API integration and reactive fullscreen state. Changes
Sequence DiagramsequenceDiagram
participant User
participant GameModeSelector
participant IOSBanner as ios-add-to-home-screen-banner
participant BrowserAPIs as Browser APIs
User->>GameModeSelector: Open game selector
GameModeSelector->>IOSBanner: Render component
IOSBanner->>BrowserAPIs: Check Platform.isIOS & standalone/display-mode
alt Not iOS or Standalone
IOSBanner->>IOSBanner: Do not render
else iOS & Not standalone
IOSBanner->>BrowserAPIs: Read localStorage["ios_a2hs_banner_dismissed"]
alt Dismissed
IOSBanner->>IOSBanner: Hide banner
else Not dismissed
IOSBanner->>BrowserAPIs: Parse iOS/Safari version from userAgent
IOSBanner->>User: Show banner (How / Later / Never)
alt User -> How
IOSBanner->>IOSBanner: Open modal guide (version-specific steps)
User->>IOSBanner: Click "Got it"
IOSBanner->>IOSBanner: Close modal
end
alt User -> Never
IOSBanner->>BrowserAPIs: localStorage["ios_a2hs_banner_dismissed"]=true
IOSBanner->>IOSBanner: Hide permanently
else User -> Later
IOSBanner->>IOSBanner: Hide for session
end
end
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ 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. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (4)
.gitignore (1)
13-13: Change unrelated to PR objectives.Adding
NOTES.mdto.gitignoreis a valid change for keeping developer notes out of version control. However, this appears unrelated to the fullscreen support feature described in the PR objectives. Consider including unrelated changes like this in a separate commit or PR to keep the history clean and focused.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.gitignore at line 13, The .gitignore change adding "NOTES.md" is unrelated to the fullscreen support work; remove that line from the current commit/PR by editing .gitignore to delete the "NOTES.md" entry (or revert that hunk), create a separate commit that adds the NOTES.md ignore entry, and open a separate PR for that commit so the fullscreen feature PR only contains relevant changes; reference the .gitignore file and the "NOTES.md" entry when making the edits and commits.src/client/components/IOSAddToHomeScreenBanner.ts (3)
42-45: Consider wrapping localStorage access in try/catch.
localStorage.getItem()can throw in Safari private browsing mode or when storage is full. The component would fail to render if this throws.🛡️ Proposed defensive fix
connectedCallback() { super.connectedCallback(); - this.dismissed = localStorage.getItem(DISMISSED_KEY) === "true"; + try { + this.dismissed = localStorage.getItem(DISMISSED_KEY) === "true"; + } catch { + // localStorage may be unavailable in private browsing + this.dismissed = false; + } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/client/components/IOSAddToHomeScreenBanner.ts` around lines 42 - 45, The connectedCallback currently reads localStorage.getItem(DISMISSED_KEY) directly which can throw (e.g., Safari private mode); wrap the access in a try/catch inside connectedCallback, default this.dismissed to false on error, and optionally log or ignore the exception. Update references to DISMISSED_KEY and this.dismissed in connectedCallback so the component still renders safely if localStorage access fails.
47-50: Same localStorage concern applies to thenever()method.If
setItemthrows, the banner will hide for the session but fail to persist the dismissal.🛡️ Proposed defensive fix
private never() { - localStorage.setItem(DISMISSED_KEY, "true"); + try { + localStorage.setItem(DISMISSED_KEY, "true"); + } catch { + // Persist fails in private browsing; session-only dismissal still works + } this.dismissed = true; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/client/components/IOSAddToHomeScreenBanner.ts` around lines 47 - 50, The never() method currently calls localStorage.setItem(DISMISSED_KEY, "true") without guarding against exceptions; wrap that call in a try/catch so any thrown error won’t prevent the in-memory dismissal. In the catch block swallow or log the error (e.g., console.warn/processLogger) and always set this.dismissed = true; reference the never() method, DISMISSED_KEY constant, and this.dismissed property when making the change.
52-54: Minor:later_naming with trailing underscore.The underscore suffix avoids collision with the reserved word
later(thoughlateris not actually reserved in JS/TS). Consider renaming tohandleLaterordismissForSessionfor clarity.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/client/components/IOSAddToHomeScreenBanner.ts` around lines 52 - 54, Rename the private method later_ to a clearer name (e.g., handleLater or dismissForSession) and update all references to it; specifically rename the method later_() { this.later = true; } to handleLater() { this.later = true; } (or dismissForSession) and update any event handlers, bindings, tests, or usages that call later_ to the new name so the component's behavior remains identical. Ensure method visibility (private) and any type annotations remain unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In @.gitignore:
- Line 13: The .gitignore change adding "NOTES.md" is unrelated to the
fullscreen support work; remove that line from the current commit/PR by editing
.gitignore to delete the "NOTES.md" entry (or revert that hunk), create a
separate commit that adds the NOTES.md ignore entry, and open a separate PR for
that commit so the fullscreen feature PR only contains relevant changes;
reference the .gitignore file and the "NOTES.md" entry when making the edits and
commits.
In `@src/client/components/IOSAddToHomeScreenBanner.ts`:
- Around line 42-45: The connectedCallback currently reads
localStorage.getItem(DISMISSED_KEY) directly which can throw (e.g., Safari
private mode); wrap the access in a try/catch inside connectedCallback, default
this.dismissed to false on error, and optionally log or ignore the exception.
Update references to DISMISSED_KEY and this.dismissed in connectedCallback so
the component still renders safely if localStorage access fails.
- Around line 47-50: The never() method currently calls
localStorage.setItem(DISMISSED_KEY, "true") without guarding against exceptions;
wrap that call in a try/catch so any thrown error won’t prevent the in-memory
dismissal. In the catch block swallow or log the error (e.g.,
console.warn/processLogger) and always set this.dismissed = true; reference the
never() method, DISMISSED_KEY constant, and this.dismissed property when making
the change.
- Around line 52-54: Rename the private method later_ to a clearer name (e.g.,
handleLater or dismissForSession) and update all references to it; specifically
rename the method later_() { this.later = true; } to handleLater() { this.later
= true; } (or dismissForSession) and update any event handlers, bindings, tests,
or usages that call later_ to the new name so the component's behavior remains
identical. Ensure method visibility (private) and any type annotations remain
unchanged.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 4d1a6594-40aa-47a0-b04c-965eac0f782d
⛔ Files ignored due to path filters (2)
resources/images/ExitFullscreenIconWhite.svgis excluded by!**/*.svgresources/images/FullscreenIconWhite.svgis excluded by!**/*.svg
📒 Files selected for processing (5)
.gitignoreresources/lang/en.jsonsrc/client/GameModeSelector.tssrc/client/components/IOSAddToHomeScreenBanner.tssrc/client/graphics/layers/GameRightSidebar.ts
|
Caution Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted. Error details |
evanpelle
left a comment
There was a problem hiding this comment.
could we instead add this to the news modal? and then clicking it can open a modal that explains how to play full screen?
Idk, it seems that news modal is for news, whereas this is a crushing feature, which is proved by some players even taking name "unplayable on iOS" |
Comments? |
Merge, maybe? |
Resolves #3685
Description:
Adds fullscreen support for both desktop and mobile:
Desktop / Android — a fullscreen toggle button in the in-game HUD (right sidebar), next to the settings button. Icon switches between expand/compress depending on current state, synced with
fullscreenchangeevent (works with F11 too). Hidden on browsers that don't supportdocument.fullscreenEnabled.iOS — since Safari doesn't support the Fullscreen API, a dismissible banner is shown on the main screen (above the lobby cards) explaining how to add the game to the Home Screen for a fullscreen experience. The banner includes:
All user-facing strings are wired through
translateText()with keys added toen.json.Please complete the following:
UI changes:
For Fullscreen API supported browsers:
CleanShot.2026-04-16.at.00.02.59.mp4
For safari on ios: (add to homescreen modal)
Please put your Discord username so you can be contacted if a bug or regression is found:
fghjk_60845