feat: Implement settings

This commit is contained in:
LynithDev 2024-06-18 18:50:16 +02:00
parent bf4511e4e3
commit af177152f0
9 changed files with 66 additions and 27 deletions

View file

@ -15,6 +15,12 @@ const CURRENT_FORMAT_VERSION: u32 = 1;
pub struct Settings {
/// A OneLauncher [`Theme`] managed by the core GUI.
pub theme: Theme,
/// Does not ask for confirmation when closing the OneLauncher GUI
#[serde(default)]
pub hide_close_prompt: bool,
/// Disables animations in the OneLauncher GUI
#[serde(default)]
pub disable_animations: bool,
/// A global fullscreen Minecraft state.
#[serde(default)]
pub force_fullscreen: bool,
@ -89,6 +95,8 @@ impl Settings {
} else {
let settings = Self {
theme: Theme::Dark,
hide_close_prompt: false,
disable_animations: false,
force_fullscreen: false,
resolution: Resolution::default(),
java_versions: JavaVersions::new(),

View file

@ -13,7 +13,7 @@ fn main() {
"get_cluster",
"get_manifest",
"get_settings",
"change_settings"
"set_settings"
]),
),
)

View file

@ -9,5 +9,5 @@ permissions = [
"allow-get-manifest",
"allow-login-msa",
"allow-get-settings",
"allow-change-settings"
"allow-set-settings"
]

View file

@ -5,10 +5,9 @@ use std::str::FromStr;
use chrono::DateTime;
use onelauncher::data::{ClusterMeta, Loader, PackageData, Settings};
use onelauncher::store::{Cluster, ClusterPath};
use onelauncher::{cluster, State};
use onelauncher::{cluster, settings};
use serde::{Deserialize, Serialize};
use specta::Type;
use tokio::sync::RwLock;
use uuid::Uuid;
#[macro_export]
@ -24,7 +23,7 @@ macro_rules! collect_commands {
get_cluster,
get_clusters,
get_settings,
change_settings
set_settings,
])
}
};
@ -129,12 +128,11 @@ pub async fn get_clusters() -> Result<HashMap<ClusterPath, Cluster>, String> {
#[specta::specta]
#[tauri::command]
pub async fn get_settings() -> Result<Settings, String> {
Ok(State::get().await?.settings.read().await.clone())
settings::get().await.map_err(|err| err.into())
}
#[specta::specta]
#[tauri::command]
pub async fn change_settings(settings: Settings) -> Result<(), String> {
State::get().await?.settings.write().await.clone_from(&settings);
Ok(())
pub async fn set_settings(settings: Settings) -> Result<(), String> {
settings::set(settings).await.map_err(|err| err.into())
}

View file

@ -9,10 +9,11 @@ import { SettingsProvider } from './hooks/useSettings';
import { bridge } from '~index';
function App(props: ParentProps) {
if (!bridge.commands.isDev())
if (!bridge.commands.isDev()) {
document.addEventListener('contextmenu', (event) => {
event.preventDefault();
});
}
return (
<SettingsProvider>

View file

@ -4,6 +4,7 @@ import type { JSX } from 'solid-js';
import { createSignal, onMount } from 'solid-js';
import Modal from './overlay/Modal';
import Button from './base/Button';
import useSettingsContext from '~ui/hooks/useSettings';
interface TitlebarButtonProps {
icon: (any: any) => JSX.Element;
@ -23,6 +24,7 @@ function TitlebarButton(props: TitlebarButtonProps) {
function WindowFrame() {
const [isModalVisible, setModalVisible] = createSignal(false);
const settings = useSettingsContext();
const maximize = () => Window.getCurrent().toggleMaximize();
const minimize = () => Window.getCurrent().minimize();
@ -31,10 +33,10 @@ function WindowFrame() {
onMount(() => {
Window.getCurrent().onCloseRequested((event) => {
// if (appSettings.settings.closeDialog) {
// event.preventDefault();
// setModalVisible(true);
// }
if (settings.hide_close_prompt !== true) {
event.preventDefault();
setModalVisible(true);
}
});
});

View file

@ -1,10 +1,17 @@
import { type Context, type ParentProps, type Resource, Show, createContext, useContext } from 'solid-js';
import { type Context, type ParentProps, type Resource, Show, createContext, createEffect, useContext } from 'solid-js';
import useCommand from './useCommand';
import type { Settings } from '~bindings';
import { bridge } from '~index';
const SettingsContext = createContext<Settings>() as Context<Settings>;
/**
* Sync the launcher state (CSS, text etc) with the current settings
*/
export function syncSettings(settings: Settings) {
document.body.classList.toggle('reduce-motion', settings.disable_animations);
}
export function getSettings(): Resource<Settings> | undefined {
const [resource] = useCommand(bridge.commands.getSettings);
return resource;
@ -13,6 +20,11 @@ export function getSettings(): Resource<Settings> | undefined {
export function SettingsProvider(props: ParentProps) {
const settings = getSettings();
createEffect(() => {
if (settings !== undefined && settings() !== undefined)
syncSettings(settings!()!);
});
return (
<Show when={settings !== undefined && settings() !== undefined}>
<SettingsContext.Provider value={settings!()!}>
@ -28,7 +40,21 @@ export function useSettingsContext() {
if (!context)
throw new Error('useSettingsContext should be called inside its SettingsProvider');
return context;
const contextProxy = new Proxy(context, {
set(target, key, value) {
// @ts-expect-error typescript
target[key] = value;
save(target); // TODO: Possibly make a "Save confirmation" instead of writing to the settings file every time?
return true;
},
});
return contextProxy;
}
function save(settings: Settings) {
bridge.commands.setSettings(settings);
syncSettings(settings);
}
export default useSettingsContext;

View file

@ -4,8 +4,11 @@ import ScrollableContainer from '~ui/components/ScrollableContainer';
import Button from '~ui/components/base/Button';
import Toggle from '~ui/components/base/Toggle';
import Sidebar from '~ui/components/Sidebar';
import useSettingsContext from '~ui/hooks/useSettings';
function SettingsAppearance() {
const settings = useSettingsContext();
return (
<Sidebar.Page>
<h1>Appearance</h1>
@ -27,7 +30,10 @@ function SettingsAppearance() {
description="Toggle all animations in the launcher."
icon={<Speedometer04Icon />}
>
<Toggle />
<Toggle
defaultChecked={settings.disable_animations ?? false}
onChecked={value => settings.disable_animations = value}
/>
</SettingsRow>
</ScrollableContainer>
</Sidebar.Page>

View file

@ -1,31 +1,29 @@
import { XIcon } from '@untitled-theme/icons-solid';
import { createEffect } from 'solid-js';
import SettingsRow from '../../../components/SettingsRow';
import ScrollableContainer from '~ui/components/ScrollableContainer';
import Toggle from '~ui/components/base/Toggle';
import Sidebar from '~ui/components/Sidebar';
import { bridge } from '~index';
import useCommand from '~ui/hooks/useCommand';
import useSettingsContext from '~ui/hooks/useSettings';
function SettingsGeneral() {
const settings = useSettingsContext();
createEffect(() => {
console.log(settings);
});
return (
<Sidebar.Page>
<h1>General</h1>
<ScrollableContainer>
<SettingsRow
title="Show Close Dialog"
description="Show a confirmation dialog when closing the launcher."
title="Hide Close Dialog"
description="Hide the close confirmation dialog when closing the launcher."
icon={<XIcon />}
>
<Toggle />
<Toggle
defaultChecked={settings.hide_close_prompt ?? true}
onChecked={value => settings.hide_close_prompt = value}
/>
</SettingsRow>
</ScrollableContainer>
</Sidebar.Page>
);