diff --git a/package-lock.json b/package-lock.json index 29f6f8e5..4694cfb8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "signal-range", - "version": "1.0.0", + "version": "1.0.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "signal-range", - "version": "1.0.0", + "version": "1.0.1", "license": "AGPL-3.0", "dependencies": { "@supabase/supabase-js": "^2.81.1", diff --git a/package.json b/package.json index 54821ce4..b8c7ff56 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "signal-range", - "version": "1.0.0", + "version": "1.0.1", "description": "Signal Range: Space Electronic Warfare Lab", "main": "dist/index.js", "scripts": { diff --git a/src/pages/base-page.ts b/src/pages/base-page.ts index 5c3b0e20..643e0232 100644 --- a/src/pages/base-page.ts +++ b/src/pages/base-page.ts @@ -2,26 +2,23 @@ import { BaseElement } from "@app/components/base-element"; import { EventBus } from "@app/events/event-bus"; import { DualTransmissionViolationData, Events, ObjectiveFailedData, ScenarioTimeExpiredData } from "@app/events/events"; import { Logger } from "@app/logging/logger"; -import { Character } from "@app/modal/character-enum"; import { DialogHistoryManager } from "@app/modal/dialog-history-manager"; import { DialogManager } from "@app/modal/dialog-manager"; import { LevelCompleteModal } from "@app/modal/level-complete-modal"; import { ObjectiveFailedModal } from "@app/modal/objective-failed-modal"; import { QuizModal } from "@app/modal/quiz-modal"; +import { TimePenaltyToast } from "@app/modal/time-penalty-toast"; import { ObjectivesManager } from "@app/objectives/objectives-manager"; import { NavigationOptions, Router } from "@app/router"; import { ScenarioManager } from "@app/scenario-manager"; import { ScenarioDialogManager } from "@app/scenarios/scenario-dialog-manager"; import { ScenarioCompletionHandler } from "@app/scoring/scenario-completion-handler"; import { ScoreCalculator } from "@app/scoring/score-calculator"; -import { TimePenaltyToast } from "@app/modal/time-penalty-toast"; import { SimulationManager } from "@app/simulation/simulation-manager"; import { AppState } from "@app/sync/storage"; -import { Auth } from "@app/user-account/auth"; import { ProgressSaveManager } from "@app/user-account/progress-save-manager"; import { ScenarioProgressEntry } from "@app/user-account/types"; import { getUserDataService } from "@app/user-account/user-data-service"; -import { getAssetUrl } from "@app/utils/asset-url"; export abstract class BasePage extends BaseElement { abstract id: string; @@ -120,9 +117,6 @@ export abstract class BasePage extends BaseElement { 'Introduction', introClip.emotion ); - - // Schedule login prompt dialog to show 5 seconds after intro dialog is closed - this.scheduleLoginPrompt_(); } } @@ -195,47 +189,6 @@ export abstract class BasePage extends BaseElement { } } - /** - * Schedule login prompt dialog to show 5 seconds after the intro dialog is closed - */ - protected scheduleLoginPrompt_(): void { - // Check periodically if the intro dialog has been closed - const checkDialogClosed = setInterval(() => { - const dialogManager = DialogManager.getInstance(); - - if (!dialogManager.isShowing()) { - // Dialog is closed, clear the interval and schedule the login prompt - clearInterval(checkDialogClosed); - - // Wait 5 seconds, then check if user is logged in - setTimeout(async () => { - const isLoggedIn = await Auth.isLoggedIn(); - - if (!isLoggedIn) { - // User is not logged in, show the login prompt dialog - dialogManager.show( - ` -

- Hey, normally you make an account on the computer and log what you are doing. -

-

- If you want to keep your notes on your desk, that's up to you, but just know none of us will have any idea what you did today if you ask us tomorrow! -

- -

- (You can make an account in the top right corner of the screen in order to save your progress automatically. It's free and only takes a minute!) -

- `, - Character.CHARLIE_BROOKS, - getAssetUrl('/assets/campaigns/login-first.mp3'), - 'Login Reminder' - ); - } - }, 5000); - } - }, 100); // Check every 100ms - } - /** * Clean up progress save manager and completion handler. * Call this in subclass destroy() methods. diff --git a/src/pages/campaign-selection.css b/src/pages/campaign-selection.css index e36f5132..7b0012c6 100644 --- a/src/pages/campaign-selection.css +++ b/src/pages/campaign-selection.css @@ -30,6 +30,37 @@ text-transform: uppercase; } +.login-warning { + display: flex; + align-items: center; + justify-content: center; + gap: 0.75rem; + margin-top: 1.25rem; + padding: 0.75rem 1.25rem; + background: linear-gradient(135deg, rgba(255, 152, 0, 0.15) 0%, rgba(255, 152, 0, 0.1) 100%); + border: 1px solid rgba(255, 152, 0, 0.4); + border-radius: 6px; + max-width: 700px; +} + +.login-warning-icon { + font-size: 1.25rem; + color: #ff9800; + flex-shrink: 0; +} + +.login-warning-text { + font-size: 0.875rem; + color: #ddd; + line-height: 1.5; + text-transform: none; + letter-spacing: normal; +} + +.login-warning-text strong { + color: #ff9800; +} + .campaign-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(400px, 1fr)); diff --git a/src/pages/campaign-selection.ts b/src/pages/campaign-selection.ts index 2e53998e..7b985d8b 100644 --- a/src/pages/campaign-selection.ts +++ b/src/pages/campaign-selection.ts @@ -4,6 +4,7 @@ import { qs } from "@app/engine/utils/query-selector"; import { Logger } from "@app/logging/logger"; import { Router } from "@app/router"; import { sandboxData } from "@app/scenarios/sandbox"; +import { Auth } from "@app/user-account/auth"; import { getUserDataService } from "@app/user-account/user-data-service"; import { getAssetUrl } from "@app/utils/asset-url"; import { html } from "../engine/utils/development/formatter"; @@ -59,6 +60,10 @@ export class CampaignSelectionPage extends BasePage { const { App } = await import('../app'); await App.authReady; + // Check if user is logged in and update the warning visibility + const isLoggedIn = await Auth.isLoggedIn(); + this.updateLoginWarning_(!isLoggedIn); + const userDataService = getUserDataService(); const progressResponse = await userDataService.getAllScenariosProgress().catch(() => null); @@ -72,6 +77,18 @@ export class CampaignSelectionPage extends BasePage { } catch (error) { Logger.error('Failed to load user progress:', error); // Continue without progress info - user may not be authenticated + // Show the login warning if we couldn't load user data + this.updateLoginWarning_(true); + } + } + + /** + * Show or hide the login warning message + */ + private updateLoginWarning_(show: boolean): void { + const warning = this.dom_.querySelector('.login-warning') as HTMLElement; + if (warning) { + warning.style.display = show ? 'flex' : 'none'; } } @@ -111,6 +128,13 @@ export class CampaignSelectionPage extends BasePage {

Signal Range Training

Select a campaign to begin your training
+
diff --git a/src/scenario-manager.ts b/src/scenario-manager.ts index 384777a2..7f670ad1 100644 --- a/src/scenario-manager.ts +++ b/src/scenario-manager.ts @@ -26,6 +26,12 @@ import { Character, Emotion } from './modal/character-enum'; import { ScenarioData } from './ScenarioData'; import { sandboxData } from './scenarios/sandbox'; +declare global { + interface Window { + DEVELOPER_MODE?: boolean; + } +} + export interface DialogClip { text: string; character: Character; @@ -148,6 +154,10 @@ export function isScenarioLocked(scenario: ScenarioData, completedScenarioIds: s return false; } + if (window.DEVELOPER_MODE) { + return false; + } + return !scenario.prerequisiteScenarioIds.every(prereqId => completedScenarioIds.includes(prereqId) );