From 677f61735d849ea223485858c004d078caad711e Mon Sep 17 00:00:00 2001 From: Tarik Alani Date: Fri, 25 Apr 2025 16:35:55 +1200 Subject: [PATCH 1/5] add demo mode toggle --- .../components/Settings/SettingsLayout.tsx | 53 +++++++++++++------ 1 file changed, 36 insertions(+), 17 deletions(-) diff --git a/src/frontend/components/Settings/SettingsLayout.tsx b/src/frontend/components/Settings/SettingsLayout.tsx index 3254722..fdda2c6 100644 --- a/src/frontend/components/Settings/SettingsLayout.tsx +++ b/src/frontend/components/Settings/SettingsLayout.tsx @@ -1,4 +1,4 @@ -import { Gear, Lock, LockOpen } from '@phosphor-icons/react'; +import { Gear, Lock, LockOpen, PresentationChart } from '@phosphor-icons/react'; import { Link, Route, Routes, useLocation } from 'react-router-dom'; import { StandingsSettings } from './sections/StandingsSettings'; import { RelativeSettings } from './sections/RelativeSettings'; @@ -14,6 +14,7 @@ export const SettingsLayout = () => { const location = useLocation(); const { bridge, editMode } = useDashboard(); const [isLocked, setIsLocked] = useState(!editMode); + const [isDemoMode, setIsDemoMode] = useState(false); const isActive = (path: string) => { return location.pathname === `/settings${path}`; @@ -36,22 +37,40 @@ export const SettingsLayout = () => {

Overlay Setup

- +
+ + +
{/* Left Column - Widget Menu */} From 7faa9e9ac690bf7c5400db4f198dbb9c79ec939a Mon Sep 17 00:00:00 2001 From: Tarik Alani Date: Fri, 25 Apr 2025 16:46:31 +1200 Subject: [PATCH 2/5] demo mode wip --- src/app/bridge/iracingSdk/iracingSdkBridge.ts | 63 ++++++++++++------- .../iracingSdk/mock-data/mockSdkBridge.ts | 1 + src/app/bridge/iracingSdk/setup.ts | 34 +++++++++- src/app/bridge/rendererExposeBridge.ts | 3 + .../components/Settings/SettingsLayout.tsx | 5 +- .../DashboardContext/DashboardContext.tsx | 11 ++++ src/types/dashboardBridge.ts | 1 + 7 files changed, 89 insertions(+), 29 deletions(-) diff --git a/src/app/bridge/iracingSdk/iracingSdkBridge.ts b/src/app/bridge/iracingSdk/iracingSdkBridge.ts index 58b3052..fe2d050 100644 --- a/src/app/bridge/iracingSdk/iracingSdkBridge.ts +++ b/src/app/bridge/iracingSdk/iracingSdkBridge.ts @@ -1,50 +1,65 @@ import { IRacingSDK } from 'irsdk-node'; import { TelemetrySink } from './telemetrySink'; import { OverlayManager } from 'src/app/overlayManager'; +import type { IrSdkBridge, Session, Telemetry } from '@irdashies/types'; const TIMEOUT = 1000; export async function publishIRacingSDKEvents( telemetrySink: TelemetrySink, overlayManager: OverlayManager -) { +): Promise { console.log('Loading iRacing SDK bridge...'); - setInterval(async () => { + let shouldStop = false; + const runningStateInterval = setInterval(async () => { const isSimRunning = await IRacingSDK.IsSimRunning(); console.log('Sending running state to window', isSimRunning); overlayManager.publishMessage('runningState', isSimRunning); }, 2000); - while (true) { - if (await IRacingSDK.IsSimRunning()) { - console.log('iRacing is running'); - const sdk = new IRacingSDK(); - sdk.autoEnableTelemetry = true; + // Start the telemetry loop in the background + (async () => { + while (!shouldStop) { + if (await IRacingSDK.IsSimRunning()) { + console.log('iRacing is running'); + const sdk = new IRacingSDK(); + sdk.autoEnableTelemetry = true; - await sdk.ready(); + await sdk.ready(); - while (sdk.waitForData(TIMEOUT)) { - const telemetry = sdk.getTelemetry(); - const session = sdk.getSessionData(); - await new Promise((resolve) => setTimeout(resolve, 1000 / 60)); + while (!shouldStop && sdk.waitForData(TIMEOUT)) { + const telemetry = sdk.getTelemetry(); + const session = sdk.getSessionData(); + await new Promise((resolve) => setTimeout(resolve, 1000 / 60)); - if (telemetry) { - overlayManager.publishMessage('telemetry', telemetry); - telemetrySink.addTelemetry(telemetry); - } + if (telemetry) { + overlayManager.publishMessage('telemetry', telemetry); + telemetrySink.addTelemetry(telemetry); + } - if (session) { - overlayManager.publishMessage('sessionData', session); - telemetrySink.addSession(session); + if (session) { + overlayManager.publishMessage('sessionData', session); + telemetrySink.addSession(session); + } } + + console.log('iRacing is no longer publishing telemetry'); + } else { + console.log('iRacing is not running'); } - console.log('iRacing is no longer publishing telemetry'); - } else { - console.log('iRacing is not running'); + await new Promise((resolve) => setTimeout(resolve, TIMEOUT)); } + })(); - await new Promise((resolve) => setTimeout(resolve, TIMEOUT)); - } + return { + onTelemetry: (callback: (value: Telemetry) => void) => callback({} as Telemetry), + onSessionData: (callback: (value: Session) => void) => callback({} as Session), + onRunningState: (callback: (value: boolean) => void) => callback(false), + stop: () => { + shouldStop = true; + clearInterval(runningStateInterval); + } + }; } diff --git a/src/app/bridge/iracingSdk/mock-data/mockSdkBridge.ts b/src/app/bridge/iracingSdk/mock-data/mockSdkBridge.ts index e2bc6fe..1f90048 100644 --- a/src/app/bridge/iracingSdk/mock-data/mockSdkBridge.ts +++ b/src/app/bridge/iracingSdk/mock-data/mockSdkBridge.ts @@ -18,4 +18,5 @@ export async function publishIRacingSDKEvents( bridge.onRunningState((running) => { overlayManager.publishMessage('runningState', running); }); + return bridge; } diff --git a/src/app/bridge/iracingSdk/setup.ts b/src/app/bridge/iracingSdk/setup.ts index b460858..44dccb6 100644 --- a/src/app/bridge/iracingSdk/setup.ts +++ b/src/app/bridge/iracingSdk/setup.ts @@ -1,18 +1,48 @@ import { OverlayManager } from 'src/app/overlayManager'; import { TelemetrySink } from './telemetrySink'; +import { ipcMain } from 'electron'; +import type { IrSdkBridge } from '@irdashies/types'; + +let isDemoMode = false; +let currentBridge: IrSdkBridge | undefined; export async function iRacingSDKSetup( telemetrySink: TelemetrySink, overlayManager: OverlayManager +) { + // Listen for demo mode toggle events + ipcMain.on('toggleDemoMode', async (_, value: boolean) => { + isDemoMode = value; + // Stop the current bridge if it exists + if (currentBridge) { + currentBridge.stop(); + currentBridge = undefined; + } + // Reload the bridge with new mode + await setupBridge(telemetrySink, overlayManager); + }); + + await setupBridge(telemetrySink, overlayManager); +} + +async function setupBridge( + telemetrySink: TelemetrySink, + overlayManager: OverlayManager ) { try { + // Stop any existing bridge + if (currentBridge) { + currentBridge.stop(); + currentBridge = undefined; + } + const module = - process.platform === 'darwin' + isDemoMode || process.platform === 'darwin' ? await import('./mock-data/mockSdkBridge') : await import('./iracingSdkBridge'); const { publishIRacingSDKEvents } = module; - await publishIRacingSDKEvents(telemetrySink, overlayManager); + currentBridge = await publishIRacingSDKEvents(telemetrySink, overlayManager); } catch (err) { console.error(`Failed to load bridge`); throw err; diff --git a/src/app/bridge/rendererExposeBridge.ts b/src/app/bridge/rendererExposeBridge.ts index 743aa25..05c5882 100644 --- a/src/app/bridge/rendererExposeBridge.ts +++ b/src/app/bridge/rendererExposeBridge.ts @@ -51,5 +51,8 @@ export function exposeBridge() { getAppVersion: () => { return ipcRenderer.invoke('getAppVersion'); }, + toggleDemoMode: (value: boolean) => { + ipcRenderer.send('toggleDemoMode', value); + }, } as DashboardBridge); } diff --git a/src/frontend/components/Settings/SettingsLayout.tsx b/src/frontend/components/Settings/SettingsLayout.tsx index fdda2c6..a5e21ac 100644 --- a/src/frontend/components/Settings/SettingsLayout.tsx +++ b/src/frontend/components/Settings/SettingsLayout.tsx @@ -12,9 +12,8 @@ import { useState } from 'react'; export const SettingsLayout = () => { const location = useLocation(); - const { bridge, editMode } = useDashboard(); + const { bridge, editMode, isDemoMode, toggleDemoMode } = useDashboard(); const [isLocked, setIsLocked] = useState(!editMode); - const [isDemoMode, setIsDemoMode] = useState(false); const isActive = (path: string) => { return location.pathname === `/settings${path}`; @@ -39,7 +38,7 @@ export const SettingsLayout = () => {