From 1eed561be5bcd20cc8155505fea7758414f5edf5 Mon Sep 17 00:00:00 2001 From: Evan Date: Wed, 28 Jan 2026 19:43:25 -0800 Subject: [PATCH] Add enzo video tutorial in the help modal (#3059) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description: Add video at top of help section, also show a glowing dot for new players. Screenshot 2026-01-28 at 7 25 23 PM ## 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: evan --- resources/lang/en.json | 2 + src/client/HelpModal.ts | 57 +++++++++++++++++++++++++- src/client/Utils.ts | 2 + src/client/components/DesktopNavBar.ts | 39 +++++++++++++++--- src/client/graphics/layers/WinModal.ts | 7 ++-- 5 files changed, 95 insertions(+), 12 deletions(-) diff --git a/resources/lang/en.json b/resources/lang/en.json index d484d3c6d..ef196e881 100644 --- a/resources/lang/en.json +++ b/resources/lang/en.json @@ -59,6 +59,8 @@ "title": "Release Notes" }, "help_modal": { + "video_tutorial": "Video Tutorial", + "video_tutorial_title": "OpenFront.io Tutorial", "hotkeys": "Hotkeys", "table_key": "Key", "table_action": "Action", diff --git a/src/client/HelpModal.ts b/src/client/HelpModal.ts index cfc2a611b..6b4daacc6 100644 --- a/src/client/HelpModal.ts +++ b/src/client/HelpModal.ts @@ -1,6 +1,6 @@ import { html } from "lit"; -import { customElement, state } from "lit/decorators.js"; -import { translateText } from "../client/Utils"; +import { customElement, query, state } from "lit/decorators.js"; +import { translateText, TUTORIAL_VIDEO_URL } from "../client/Utils"; import { BaseModal } from "./components/BaseModal"; import "./components/Difficulties"; import "./components/Maps"; @@ -9,6 +9,7 @@ import { modalHeader } from "./components/ui/ModalHeader"; @customElement("help-modal") export class HelpModal extends BaseModal { @state() private keybinds: Record = this.getKeybinds(); + @query("#tutorial-video-iframe") private videoIframe?: HTMLIFrameElement; private isKeybindObject(v: unknown): v is { value: string } { return ( @@ -120,6 +121,47 @@ export class HelpModal extends BaseModal { [&_p]:text-gray-300 [&_p]:mb-3 [&_strong]:text-white [&_strong]:font-bold scrollbar-thin scrollbar-thumb-white/20 scrollbar-track-transparent" > + +
+
+ + + +
+

+ ${translateText("help_modal.video_tutorial")} +

+
+
+
+ +
+
@@ -1139,5 +1181,16 @@ export class HelpModal extends BaseModal { protected onOpen(): void { this.keybinds = this.getKeybinds(); + // Restore the video src when modal opens + if (this.videoIframe) { + this.videoIframe.src = TUTORIAL_VIDEO_URL; + } + } + + protected onClose(): void { + // Clear the iframe src to stop video playback + if (this.videoIframe) { + this.videoIframe.src = ""; + } } } diff --git a/src/client/Utils.ts b/src/client/Utils.ts index 2f4e0dafd..9a7454a65 100644 --- a/src/client/Utils.ts +++ b/src/client/Utils.ts @@ -2,6 +2,8 @@ import IntlMessageFormat from "intl-messageformat"; import { MessageType } from "../core/game/Game"; import type { LangSelector } from "./LangSelector"; +export const TUTORIAL_VIDEO_URL = "https://www.youtube.com/embed/EN2oOog3pSs"; + export function renderDuration(totalSeconds: number): string { if (totalSeconds <= 0) return "0s"; const minutes = Math.floor(totalSeconds / 60); diff --git a/src/client/components/DesktopNavBar.ts b/src/client/components/DesktopNavBar.ts index c197306b4..1e13f87c1 100644 --- a/src/client/components/DesktopNavBar.ts +++ b/src/client/components/DesktopNavBar.ts @@ -1,8 +1,13 @@ import { LitElement, html } from "lit"; -import { customElement } from "lit/decorators.js"; +import { customElement, state } from "lit/decorators.js"; +import { getGamesPlayed } from "../Utils"; + +const HELP_SEEN_KEY = "helpSeen"; @customElement("desktop-nav-bar") export class DesktopNavBar extends LitElement { + @state() private _helpSeen = localStorage.getItem(HELP_SEEN_KEY) === "true"; + createRenderRoot() { return this; } @@ -40,6 +45,15 @@ export class DesktopNavBar extends LitElement { }); } + private showHelpDot(): boolean { + return getGamesPlayed() < 10 && !this._helpSeen; + } + + private onHelpClick = () => { + localStorage.setItem(HELP_SEEN_KEY, "true"); + this._helpSeen = true; + }; + render() { return html`