chore: port useSettings
i have to refactor settings one more time
This commit is contained in:
parent
855d421eaa
commit
7d1be7f2d3
5 changed files with 257 additions and 75 deletions
138
apps/onelauncher/frontend/src/hooks/useSettings.tsx
Normal file
138
apps/onelauncher/frontend/src/hooks/useSettings.tsx
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
/* eslint-disable no-console -- ok */
|
||||
/* eslint-disable react-hooks/rules-of-hooks -- stfu */
|
||||
/* eslint-disable ts/naming-convention -- please shut the fuck up i need to see if its working or not */
|
||||
import type { Settings } from '@/bindings.gen';
|
||||
import { bindings } from '@/main';
|
||||
import { useCommand } from '@onelauncher/common';
|
||||
import React, { createContext, useCallback, useContext, useEffect, useState } from 'react';
|
||||
|
||||
interface SettingsControllerType {
|
||||
settings: Settings | undefined;
|
||||
saveChangedSettings: () => void;
|
||||
settingsToSave: Partial<Settings> | null;
|
||||
setSettingsToSave: (settings: Partial<Settings> | null) => void;
|
||||
settingsChanged: boolean;
|
||||
setSettingsChanged: (changed: boolean) => void;
|
||||
createSetting: <K extends keyof Settings, V = Settings[K]>(name: K, value: V) => [V, (value: V) => void];
|
||||
}
|
||||
|
||||
const SettingsContext = createContext<SettingsControllerType | null>(null);
|
||||
|
||||
interface SettingsProviderProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export function SettingsProvider({ children }: SettingsProviderProps) {
|
||||
const { data: settings } = useCommand('readSettings', bindings.core.readSettings);
|
||||
const [settingsChanged, setSettingsChanged] = useState(false);
|
||||
const [settingsToSave, setSettingsToSave] = useState<Partial<Settings> | null>(null);
|
||||
|
||||
const filteredSettingsToSave = settingsToSave
|
||||
? Object.fromEntries(
|
||||
Object.entries(settingsToSave),
|
||||
) as Partial<Settings>
|
||||
: {};
|
||||
|
||||
const patchedSettings: Settings = {
|
||||
...settings,
|
||||
...filteredSettingsToSave,
|
||||
} as Settings;
|
||||
|
||||
const writeQuery = useCommand('writeSettings', () => bindings.core.writeSettings(patchedSettings), {
|
||||
enabled: false,
|
||||
subscribed: false,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const handleBeforeUnload = () => {
|
||||
setSettingsChanged(false);
|
||||
};
|
||||
|
||||
window.addEventListener('beforeunload', handleBeforeUnload);
|
||||
return () => window.removeEventListener('beforeunload', handleBeforeUnload);
|
||||
}, []);
|
||||
|
||||
const saveChangedSettings = () => {
|
||||
if (!settings)
|
||||
return;
|
||||
|
||||
console.log(settings);
|
||||
console.log(settingsToSave);
|
||||
|
||||
writeQuery.refetch();
|
||||
|
||||
setSettingsChanged(false);
|
||||
};
|
||||
|
||||
const handleSetSettingsToSave = useCallback((newSettings: Partial<Settings> | null) => {
|
||||
setSettingsToSave(newSettings);
|
||||
setSettingsChanged(true);
|
||||
}, []);
|
||||
|
||||
const createSetting = useCallback(<K extends keyof Settings, V = Settings[K]>(
|
||||
name: K,
|
||||
initialValue: V,
|
||||
): [V, (value: V) => void] => {
|
||||
const [value, setValue] = useState<V>(initialValue);
|
||||
|
||||
useEffect(() => {
|
||||
const didChange = settings?.[name] !== value;
|
||||
console.log(`did change? ${didChange}`);
|
||||
|
||||
if (didChange && settings) {
|
||||
setSettingsToSave(prev => ({
|
||||
...prev,
|
||||
[name]: value,
|
||||
}));
|
||||
setSettingsChanged(true);
|
||||
}
|
||||
}, [value, name]);
|
||||
|
||||
return [value, setValue];
|
||||
}, [settings]);
|
||||
|
||||
const controller: SettingsControllerType = {
|
||||
settings,
|
||||
saveChangedSettings,
|
||||
settingsToSave,
|
||||
setSettingsToSave: handleSetSettingsToSave,
|
||||
settingsChanged,
|
||||
setSettingsChanged,
|
||||
createSetting,
|
||||
};
|
||||
|
||||
if (settings === undefined)
|
||||
return null;
|
||||
|
||||
return (
|
||||
<SettingsContext.Provider value={controller}>
|
||||
{children}
|
||||
|
||||
{settingsChanged && (
|
||||
<div className="pointer-events-none absolute bottom-5 left-1/2 w-full flex flex-col items-center justify-center -translate-x-1/2">
|
||||
<div className="pointer-events-auto flex flex-row gap-1 border border-component-border rounded-md bg-component-bg p-3 px-4">
|
||||
You have unsaved changes!
|
||||
<button
|
||||
className="text-brand filter-brightness-130 hover:underline"
|
||||
onClick={saveChangedSettings}
|
||||
type="button"
|
||||
>
|
||||
Save Now
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</SettingsContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function useSettings(): SettingsControllerType {
|
||||
const context = useContext(SettingsContext);
|
||||
|
||||
if (!context)
|
||||
throw new Error('useSettings should be called inside its SettingsProvider');
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
export default useSettings;
|
||||
|
|
@ -6,6 +6,7 @@ import ReactDOM from 'react-dom/client';
|
|||
import { createTauRPCProxy } from './bindings.gen';
|
||||
import { NotificationProvider } from './hooks/useNotification';
|
||||
|
||||
import { SettingsProvider } from './hooks/useSettings';
|
||||
import { routeTree } from './routeTree.gen';
|
||||
import './fonts';
|
||||
import 'overlayscrollbars/overlayscrollbars.css';
|
||||
|
|
@ -56,9 +57,11 @@ if (rootElement && !rootElement.innerHTML) {
|
|||
root.render(
|
||||
<StrictMode>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<NotificationProvider>
|
||||
<RouterProvider router={router} />
|
||||
</NotificationProvider>
|
||||
<SettingsProvider>
|
||||
<NotificationProvider>
|
||||
<RouterProvider router={router} />
|
||||
</NotificationProvider>
|
||||
</SettingsProvider>
|
||||
</QueryClientProvider>
|
||||
</StrictMode>,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,83 +1,16 @@
|
|||
import Modal from '@/components/overlay/Modal';
|
||||
import useNotifications from '@/hooks/useNotification';
|
||||
import { bindings } from '@/main';
|
||||
import { useCommand } from '@onelauncher/common';
|
||||
import { Button, Dropdown, TextField } from '@onelauncher/common/components';
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
import { BracketsEllipsesIcon } from '@untitled-theme/icons-react';
|
||||
|
||||
export const Route = createFileRoute('/app/browser/')({
|
||||
component: RouteComponent,
|
||||
});
|
||||
|
||||
function RouteComponent() {
|
||||
const { create, list, clear } = useNotifications();
|
||||
const result = useCommand('read_settings', bindings.core.readSettings);
|
||||
|
||||
const addSimpleNotif = () => {
|
||||
create({
|
||||
message: 'This is a simple notification message',
|
||||
title: 'Simple Notification',
|
||||
});
|
||||
};
|
||||
|
||||
const addProgressNotif = () => {
|
||||
create({
|
||||
message: 'Download in progress...',
|
||||
title: 'Downloading File',
|
||||
fraction: Math.floor(Math.random() * 100),
|
||||
});
|
||||
};
|
||||
|
||||
const addErrorNotif = () => {
|
||||
create({
|
||||
message: 'Something went wrong while processing your request',
|
||||
title: 'Error',
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p>asdasdas</p>
|
||||
|
||||
<TextField />
|
||||
|
||||
{/* we can open the modal but we cant close it for some reason */}
|
||||
<Modal.Trigger>
|
||||
<Button>Open Modal</Button>
|
||||
|
||||
<Modal>
|
||||
<p>sadsadsad</p>
|
||||
</Modal>
|
||||
</Modal.Trigger>
|
||||
|
||||
<Dropdown label="Select a version">
|
||||
<Dropdown.Item>Slmalr</Dropdown.Item>
|
||||
<Dropdown.Item>Slmalr 2</Dropdown.Item>
|
||||
</Dropdown>
|
||||
|
||||
<div className="flex gap-2 flex-wrap">
|
||||
<Button onClick={addSimpleNotif}>Add Simple Notification</Button>
|
||||
<Button onClick={addProgressNotif}>Add Progress Notification</Button>
|
||||
<Button onClick={addErrorNotif}>Add Error Notification</Button>
|
||||
<Button onClick={clear}>Clear All Notifications</Button>
|
||||
</div>
|
||||
|
||||
<div className="mt-4">
|
||||
<h3 className="font-semibold mb-2">
|
||||
Current Notifications (
|
||||
{Object.keys(list).length}
|
||||
):
|
||||
</h3>
|
||||
<pre className="text-xs bg-component-bg-hover p-2 rounded overflow-auto max-h-40">
|
||||
{JSON.stringify(list, null, 2)}
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<div className="mt-4">
|
||||
<h3>Settings</h3>
|
||||
<pre className="text-xs bg-component-bg-hover p-2 rounded overflow-auto max-h-40">
|
||||
{JSON.stringify(result.data, null, 2)}
|
||||
</pre>
|
||||
<div className="flex flex-col items-center justify-center h-full">
|
||||
<BracketsEllipsesIcon className="size-40" />
|
||||
<h3 className="text-xl">WIP</h3>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
108
apps/onelauncher/frontend/src/routes/app/dev.tsx
Normal file
108
apps/onelauncher/frontend/src/routes/app/dev.tsx
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
import Modal from '@/components/overlay/Modal';
|
||||
import useNotifications from '@/hooks/useNotification';
|
||||
import useSettings from '@/hooks/useSettings';
|
||||
import { bindings } from '@/main';
|
||||
import { useCommand } from '@onelauncher/common';
|
||||
import { Button, Dropdown, Switch, TextField } from '@onelauncher/common/components';
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
|
||||
export const Route = createFileRoute('/app/dev')({
|
||||
component: RouteComponent,
|
||||
});
|
||||
|
||||
function RouteComponent() {
|
||||
const { create, list, clear } = useNotifications();
|
||||
const { settings, createSetting } = useSettings();
|
||||
const result = useCommand('read_settings', bindings.core.readSettings);
|
||||
|
||||
const addSimpleNotif = () => {
|
||||
create({
|
||||
message: 'This is a simple notification message',
|
||||
title: 'Simple Notification',
|
||||
});
|
||||
};
|
||||
|
||||
const addProgressNotif = () => {
|
||||
create({
|
||||
message: 'Download in progress...',
|
||||
title: 'Downloading File',
|
||||
fraction: Math.floor(Math.random() * 100),
|
||||
});
|
||||
};
|
||||
|
||||
const addErrorNotif = () => {
|
||||
create({
|
||||
message: 'Something went wrong while processing your request',
|
||||
title: 'Error',
|
||||
});
|
||||
};
|
||||
|
||||
const [parallel, setParallel] = createSetting('allow_parallel_running_clusters', settings?.allow_parallel_running_clusters);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p>this is really became my test place fr</p>
|
||||
|
||||
<TextField />
|
||||
|
||||
{/* we can open the modal but we cant close it for some reason */}
|
||||
<Modal.Trigger>
|
||||
<Button>Open Modal</Button>
|
||||
|
||||
<Modal>
|
||||
<p>sadsadsad</p>
|
||||
</Modal>
|
||||
</Modal.Trigger>
|
||||
|
||||
<Dropdown label="Select a version">
|
||||
<Dropdown.Item>Slmalr</Dropdown.Item>
|
||||
<Dropdown.Item>Slmalr 2</Dropdown.Item>
|
||||
</Dropdown>
|
||||
|
||||
<div className="flex gap-2 flex-wrap">
|
||||
<Button onClick={addSimpleNotif}>Add Simple Notification</Button>
|
||||
<Button onClick={addProgressNotif}>Add Progress Notification</Button>
|
||||
<Button onClick={addErrorNotif}>Add Error Notification</Button>
|
||||
<Button onClick={clear}>Clear All Notifications</Button>
|
||||
</div>
|
||||
|
||||
<div className="mt-4">
|
||||
<h3 className="font-semibold mb-2">
|
||||
Current Notifications (
|
||||
{Object.keys(list).length}
|
||||
):
|
||||
</h3>
|
||||
<pre className="text-xs bg-component-bg-hover p-2 rounded overflow-auto max-h-40">
|
||||
{JSON.stringify(list, null, 2)}
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-row">
|
||||
<div className="w-1/2">
|
||||
<div className="mt-4">
|
||||
<h3>Settings</h3>
|
||||
<pre className="text-xs bg-component-bg-hover p-2 rounded overflow-auto max-h-40">
|
||||
{JSON.stringify(result.data, null, 2)}
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<div className="mt-4">
|
||||
<h3>Settings again but this time its from useSettings hook</h3>
|
||||
<pre className="text-xs bg-component-bg-hover p-2 rounded overflow-auto max-h-40">
|
||||
{JSON.stringify(settings, null, 2)}
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="w-1/2">
|
||||
<div className="ml-2">
|
||||
<pre>
|
||||
{parallel ? 'Parallel running is enabled' : 'Parallel running is disabled'}
|
||||
</pre>
|
||||
<Switch defaultSelected={parallel} onChange={val => setParallel(val)} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -101,11 +101,11 @@ export function GameSettings(props: GameSettingProps) {
|
|||
<div className="flex flex-row items-center gap-x-2">
|
||||
<TextField
|
||||
className="text-center"
|
||||
defaultValue={settings.mem_max?.toString()}
|
||||
onChange={(e) => {
|
||||
settings.mem_max = Number(e.target.value);
|
||||
}}
|
||||
type="number"
|
||||
value={settings.mem_max ?? 2048}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue