From 8751fbaeda962ec69d8e96de0a49af601fc78e19 Mon Sep 17 00:00:00 2001 From: Borys Levytskyi Date: Tue, 11 Nov 2025 08:57:57 -0500 Subject: [PATCH] Analytics for themes --- src/shell/AppState.ts | 37 +++++++++++++---------- src/shell/module.tsx | 68 +++++++++++++++++++++++++------------------ 2 files changed, 60 insertions(+), 45 deletions(-) diff --git a/src/shell/AppState.ts b/src/shell/AppState.ts index a8f77c5..e89a375 100644 --- a/src/shell/AppState.ts +++ b/src/shell/AppState.ts @@ -21,7 +21,8 @@ export type CommandResultView = { view: ViewFactory }; -export type AppStateChangeHandler = (state: AppState) => void; +export type AppStateAttribute = keyof AppState; +export type AppStateChangeHandler = (state: AppState, attribute : AppStateAttribute) => void; type ViewFactory = () => JSX.Element; @@ -65,12 +66,12 @@ export default class AppState { const key = generateKey(); this.commandResults.unshift({ key, input, view }); log.debug(`command result added: ${input}`); - this.triggerChanged(); + this.triggerChanged('commandResults'); } clearCommandResults() { this.commandResults = []; - this.triggerChanged(); + this.triggerChanged('commandResults'); } removeResult(index: number) { @@ -78,68 +79,72 @@ export default class AppState { return; this.commandResults.splice(index, 1); - this.triggerChanged(); + this.triggerChanged('commandResults'); } toggleEmphasizeBytes(value?: boolean) { this.emphasizeBytes = value !== null && value !== undefined ? value : !this.emphasizeBytes; - this.triggerChanged(); + this.triggerChanged('emphasizeBytes'); } onChange(handler: AppStateChangeHandler) { this.changeHandlers.push(handler); } - triggerChanged() { - this.changeHandlers.forEach(h => h(this)); + triggerChanged(attribute: AppStateAttribute) { + this.changeHandlers.forEach(h => h(this, attribute)); } setUiTheme(theme: string) { + + if(this.uiTheme === theme) + return; + this.uiTheme = theme; - this.triggerChanged(); + this.triggerChanged('uiTheme'); } toggleDebugMode() { this.debugMode = !this.debugMode; - this.triggerChanged(); + this.triggerChanged('debugMode'); } toggleShowSettings() { this.showSettings = !this.showSettings; - this.triggerChanged(); + this.triggerChanged('showSettings'); } toggleAnnotateTypes(value?: boolean) { this.annotateTypes = value !== null && value !== undefined ? value : !this.annotateTypes; - this.triggerChanged(); + this.triggerChanged('annotateTypes'); } toggleDimExtrBits() { this.dimExtraBits = !this.dimExtraBits; - this.triggerChanged(); + this.triggerChanged('dimExtraBits'); } toggleCenteredLayout(value?: boolean) { this.centeredLayout = value !== null && value !== undefined ? value : !this.centeredLayout; - this.triggerChanged(); + this.triggerChanged('centeredLayout'); } registerVisit() { this.pageVisitsCount++; - this.triggerChanged(); + this.triggerChanged('pageVisitsCount'); } onDonationClicked(): boolean { if (this.donationClicked === true) return false; this.donationClicked = true; - this.triggerChanged(); + this.triggerChanged('donationClicked'); return true; } setCookieDisclaimerHidden(value: boolean) { this.cookieDisclaimerHidden = value; - this.triggerChanged(); + this.triggerChanged('cookieDisclaimerHidden'); } getPersistData(): PersistedAppData { diff --git a/src/shell/module.tsx b/src/shell/module.tsx index 66ebe32..fcfdbd4 100644 --- a/src/shell/module.tsx +++ b/src/shell/module.tsx @@ -25,10 +25,7 @@ const shellModule = { cmd.command("light", () => appState.setUiTheme('light')); cmd.command("midnight", () => appState.setUiTheme('midnight')); cmd.command("settings", () => appState.toggleShowSettings()); - cmd.command("bladerunner", () => { - appState.setUiTheme('bladerunner'); - sendAnalyticsEvent({eventCategory: "UI", eventAction: "ThemeChanged", eventLabel: "bladerunner"}); - }); + cmd.command("bladerunner", () => appState.setUiTheme('bladerunner')); cmd.command("bladerunner-easter", (c: CommandInput) => { document.querySelector('.app-root')!.scrollTo(0, 0); cmd.execute("bladerunner"); @@ -70,38 +67,51 @@ const shellModule = { }); }); - if(appState.env !== 'prod') { - + if(appState.env !== 'prod') { // Default command for development purposes - cmd.command({ - canHandle: (s: string) => s.indexOf('default') === 0, - handle: (s: CommandInput) => { - - const executeCommand = (c: string) => { - - if(c.length === 0) { - return "Default comand: " + localStorage.getItem(STARTUP_COMMAND_KEY); - } - else if(c === 'clear') { - localStorage.removeItem(STARTUP_COMMAND_KEY); - return "Default startup command cleared"; - } - - localStorage.setItem(STARTUP_COMMAND_KEY, c); - return `Default startup command saved: ${c}`; - }; - - const command = s.input.substring(7).trim(); - const result = executeCommand(command); - appState.addCommandResult(s.input, () => ); - } - }); + registerDefaultCommand(cmd, appState); }; cmd.onError((input: string, err: Error) => appState.addCommandResult(input, () => )); + + registerStateChangeHandlers(appState); } } export default shellModule; +function registerStateChangeHandlers(appState: AppState) { + appState.onChange((state: AppState, attribute: keyof AppState) => { + if (attribute === 'uiTheme') { + sendAnalyticsEvent({ eventCategory: "UI", eventAction: `set_theme_${state.uiTheme}` }); + } + }); +} + +function registerDefaultCommand(cmd: CmdShell, appState: AppState) { + cmd.command({ + canHandle: (s: string) => s.indexOf('default') === 0, + handle: (s: CommandInput) => { + + const executeCommand = (c: string) => { + + if (c.length === 0) { + return "Default comand: " + localStorage.getItem(STARTUP_COMMAND_KEY); + } + else if (c === 'clear') { + localStorage.removeItem(STARTUP_COMMAND_KEY); + return "Default startup command cleared"; + } + + localStorage.setItem(STARTUP_COMMAND_KEY, c); + return `Default startup command saved: ${c}`; + }; + + const command = s.input.substring(7).trim(); + const result = executeCommand(command); + appState.addCommandResult(s.input, () => ); + } + }); +} +