Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ Displays a track map with the current position of the cars on track and the trac

We welcome contributions to the IRDashies project! If you have any ideas, suggestions, or bug reports, please open an issue or submit a pull request.

Join our discord here: https://discord.gg/E3KKB6W4
Join our discord here: https://discord.gg/YMAqduF2Ft

## License

Expand Down
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
"productName": "irdashies",
"version": "0.0.7",
"description": "iRacing Dashboards & Overlays",
"repository": {
"type": "git",
"url": "https://github.com/tariknz/irdashies"
},
"main": ".vite/build/main.js",
"scripts": {
"start": "electron-forge start --enable-logging",
Expand Down
4 changes: 4 additions & 0 deletions src/app/bridge/dashboard/dashboardBridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,8 @@ export async function publishDashboardUpdates(overlayManager: OverlayManager) {
overlayManager.closeOrCreateWindows(dashboard);
overlayManager.publishMessage('dashboardUpdated', dashboard);
});

ipcMain.handle('toggleLockOverlays', () => {
return overlayManager.toggleLockOverlays();
});
}
3 changes: 3 additions & 0 deletions src/app/bridge/rendererExposeBridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,8 @@ export function exposeBridge() {
saveDashboard: (value: DashboardLayout) => {
ipcRenderer.send('saveDashboard', value);
},
toggleLockOverlays: () => {
return ipcRenderer.invoke('toggleLockOverlays');
},
} as DashboardBridge);
}
2 changes: 1 addition & 1 deletion src/frontend/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ const AppRoutes = () => {
/>
);
})}
<Route path="/settings" element={<Settings />} />
<Route path="/settings/*" element={<Settings />} />
</Routes>
);
};
Expand Down
1 change: 1 addition & 0 deletions src/frontend/components/EditMode/EditMode.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const mockBridge: (editMode: boolean) => DashboardBridge = (editMode) => ({
onEditModeToggled: (callback) => {
callback(editMode);
},
toggleLockOverlays: () => Promise.resolve(true),
});

export const Primary = {
Expand Down
7 changes: 2 additions & 5 deletions src/frontend/components/Settings/Settings.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useDashboard } from '@irdashies/context';
import { SettingsForm } from './SettingsForm';
import { SettingsLayout } from './SettingsLayout';

export const Settings = () => {
const { currentDashboard, onDashboardUpdated } = useDashboard();
Expand All @@ -8,9 +8,6 @@ export const Settings = () => {
}

return (
<SettingsForm
currentDashboard={currentDashboard}
onDashboardUpdated={onDashboardUpdated}
/>
<SettingsLayout />
);
};
27 changes: 0 additions & 27 deletions src/frontend/components/Settings/SettingsForm.stories.tsx

This file was deleted.

53 changes: 0 additions & 53 deletions src/frontend/components/Settings/SettingsForm.tsx

This file was deleted.

58 changes: 58 additions & 0 deletions src/frontend/components/Settings/SettingsLayout.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { Meta, StoryObj } from '@storybook/react';
import { MemoryRouter, Route, Routes } from 'react-router-dom';
import { DashboardProvider } from '@irdashies/context';
import { SettingsLayout } from './SettingsLayout';
import { mockDashboardBridge } from './__mocks__/mockBridge';

interface StoryProps {
initialPath: string;
}

const meta: Meta<typeof SettingsLayout> = {
component: SettingsLayout,
decorators: [
(Story, context) => {
const { initialPath = 'standings' } =
context.args as StoryProps;
return (
<DashboardProvider bridge={mockDashboardBridge}>
<MemoryRouter initialEntries={[initialPath]}>
<Routes>
<Route
path="/settings/*"
element={
<div style={{ height: '100vh' }}>
<Story />
</div>
}
/>
</Routes>
</MemoryRouter>
</DashboardProvider>
);
},
],
};

export default meta;

type Story = StoryObj<typeof SettingsLayout>;

export const Default: Story = {
args: {
initialPath: '/settings/standings',
},
};

// Add more stories for different routes
export const RelativeRoute: Story = {
args: {
initialPath: '/settings/relative',
},
};

export const WeatherRoute: Story = {
args: {
initialPath: '/settings/weather',
},
};
140 changes: 140 additions & 0 deletions src/frontend/components/Settings/SettingsLayout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import { Gear, Lock, LockOpen } from '@phosphor-icons/react';
import { Link, Route, Routes, useLocation } from 'react-router-dom';
import { StandingsSettings } from './sections/StandingsSettings';
import { RelativeSettings } from './sections/RelativeSettings';
import { WeatherSettings } from './sections/WeatherSettings';
import { TrackMapSettings } from './sections/TrackMapSettings';
import { AdvancedSettings } from './sections/AdvancedSettings';
import { InputSettings } from './sections/InputSettings';
import { AboutSettings } from './sections/AboutSettings';
import { useDashboard } from '@irdashies/context';
import { useState } from 'react';

export const SettingsLayout = () => {
const location = useLocation();
const { bridge, editMode } = useDashboard();
const [isLocked, setIsLocked] = useState(!editMode);

const isActive = (path: string) => {
return location.pathname === `/settings${path}`;
};

const menuItemClass = (path: string) =>
`block w-full p-2 rounded cursor-pointer ${
isActive(path) ? 'bg-slate-700' : 'hover:bg-slate-700'
}`;

const handleToggleLock = async () => {
const locked = await bridge.toggleLockOverlays();
setIsLocked(locked);
};

return (
<div className="flex flex-col gap-4 bg-slate-700 p-4 rounded-md w-full h-full">
<div className="flex flex-row gap-4 items-center justify-between">
<div className="flex flex-row gap-4 items-center">
<Gear size={32} weight="bold" />
<h1 className="text-2xl font-bold">Overlay Setup</h1>
</div>
<button
onClick={handleToggleLock}
className="flex flex-row gap-2 items-center px-3 py-2 rounded bg-slate-800 hover:bg-slate-600 transition-colors"
>
{isLocked ? (
<>
<Lock size={20} weight="bold" />
<span>Edit Layout</span>
</>
) : (
<>
<LockOpen size={20} weight="bold" />
<span>Editing Layout</span>
</>
)}
</button>
</div>
<div className="flex flex-row gap-4 flex-1">
{/* Left Column - Widget Menu */}
<div className="w-1/3 bg-slate-800 p-4 rounded-md flex flex-col">
<ul className="flex flex-col gap-2 flex-1">
<li>
<Link to="/settings/input" className={menuItemClass('/input')}>
Input Traces
</Link>
</li>
<li>
<Link
to="/settings/standings"
className={menuItemClass('/standings')}
>
Standings
</Link>
</li>
<li>
<Link
to="/settings/relative"
className={menuItemClass('/relative')}
>
Relative
</Link>
</li>
<li>
<Link
to="/settings/weather"
className={menuItemClass('/weather')}
>
Weather
</Link>
</li>
<li>
<Link to="/settings/map" className={menuItemClass('/track-map')}>
<div className="flex flex-row gap-2 items-center">
Track Map
<span className="text-xs bg-yellow-600 text-yellow-100 px-2 py-0.5 rounded-full flex flex-row gap-1 items-center">
Experimental
</span>
</div>
</Link>
</li>
</ul>
{/* Advanced settings pushed to bottom */}
<div className="mt-auto pt-4 border-t border-slate-700 flex flex-col gap-2">
<Link
to="/settings/advanced"
className={menuItemClass('/advanced')}
>
Advanced
</Link>
<Link
to="/settings/about"
className={menuItemClass('/about')}
>
About
</Link>
</div>
</div>

{/* Right Column - Widget Settings */}
<div className="w-2/3 bg-slate-800 p-4 rounded-md">
<Routes>
<Route path="standings" element={<StandingsSettings />} />
<Route path="relative" element={<RelativeSettings />} />
<Route path="weather" element={<WeatherSettings />} />
<Route path="map" element={<TrackMapSettings />} />
<Route path="input" element={<InputSettings />} />
<Route path="advanced" element={<AdvancedSettings />} />
<Route path="about" element={<AboutSettings />} />
<Route
path="*"
element={
<div className="text-slate-400">
Select a widget from the left to customize its settings
</div>
}
/>
</Routes>
</div>
</div>
</div>
);
};
24 changes: 24 additions & 0 deletions src/frontend/components/Settings/__mocks__/mockBridge.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import type { DashboardBridge } from '@irdashies/types';
import { defaultDashboard } from '../../../../app/storage/defaultDashboard';

export const mockDashboardBridge: DashboardBridge = {
reloadDashboard: () => {
// noop
},
saveDashboard: () => {
// noop
},
dashboardUpdated: (callback) => {
callback(defaultDashboard);
return () => {
// noop
};
},
onEditModeToggled: (callback) => {
callback(false);
return () => {
// noop
};
},
toggleLockOverlays: () => Promise.resolve(true),
};
Loading