From 830456ea62f3d1ebc0618709da4ec624b57639e7 Mon Sep 17 00:00:00 2001 From: spacemeowx2 Date: Mon, 6 Mar 2023 21:32:00 +0800 Subject: [PATCH] feat: add config --- gui/src-tauri/Cargo.toml | 2 +- gui/src-tauri/tauri.conf.json | 12 ++++++---- gui/src/App.tsx | 1 + gui/src/hooks/usePromise.ts | 40 ++++++++++++++++++++++++++++++++++ gui/src/hooks/useWindowSize.ts | 31 ++++++++++++++++++++++++++ gui/src/pages/Settings.tsx | 11 ++++++++-- gui/src/services/config.ts | 33 +++++++++++++++++++++++++--- gui/vite.config.ts | 6 ++++- 8 files changed, 125 insertions(+), 11 deletions(-) create mode 100644 gui/src/hooks/usePromise.ts create mode 100644 gui/src/hooks/useWindowSize.ts diff --git a/gui/src-tauri/Cargo.toml b/gui/src-tauri/Cargo.toml index 5c46677..2941aa6 100644 --- a/gui/src-tauri/Cargo.toml +++ b/gui/src-tauri/Cargo.toml @@ -13,7 +13,7 @@ edition = "2021" tauri-build = { version = "1.2", features = [] } [dependencies] -tauri = { version = "1.2", features = ["shell-execute", "shell-open", "shell-sidecar", "window-all"] } +tauri = { version = "1.2", features = ["fs-all", "path-all", "shell-execute", "shell-open", "shell-sidecar", "window-all"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" tokio = { version = "1.0", features = ["time"] } diff --git a/gui/src-tauri/tauri.conf.json b/gui/src-tauri/tauri.conf.json index bac3573..99a0ebd 100644 --- a/gui/src-tauri/tauri.conf.json +++ b/gui/src-tauri/tauri.conf.json @@ -39,10 +39,14 @@ }, "fs": { "scope": [ - "$APPCONFIG", - "$APPDATA", - "$APPCACHE" - ] + "$APPCONFIG/*", + "$APPDATA/*", + "$APPCACHE/*" + ], + "all": true + }, + "path": { + "all": true } }, "bundle": { diff --git a/gui/src/App.tsx b/gui/src/App.tsx index bb15a5d..eadea29 100644 --- a/gui/src/App.tsx +++ b/gui/src/App.tsx @@ -1,3 +1,4 @@ +import 'i18n/config'; import { useEffect } from "react"; import { getCurrent } from "@tauri-apps/api/window"; import { Routes, Route } from "react-router-dom"; diff --git a/gui/src/hooks/usePromise.ts b/gui/src/hooks/usePromise.ts new file mode 100644 index 0000000..a89ce41 --- /dev/null +++ b/gui/src/hooks/usePromise.ts @@ -0,0 +1,40 @@ +import { useState } from "react"; + +/** + * A hook that returns a promise and its state. + * + * The promise is only created once, and the state is updated when the promise resolves or rejects. + * + * @param factory A function that returns a promise. + * @returns An object containing the promise's state and result. + * @example + * const { loading, result, error } = usePromise(() => fetch('https://example.com') + * .then(response => response.text()) + * ); + * if (loading) { + * return

Loading...

; + * } + * if (error) { + * return

Error: {error.message}

; + * } + * return

Result: {result}

; + */ +export function usePromise(factory: () => Promise) { + const [loading, setLoading] = useState(true); + const [result, setResult] = useState(undefined); + const [error, setError] = useState(undefined); + const [promise] = useState(() => { + const promise = factory(); + if (!promise || typeof promise.then !== "function") { + throw new Error("The factory function must return a promise."); + } + return promise + .then(setResult) + .catch(setError) + .finally(() => { + setLoading(false); + }); + }); + + return { loading, result, error, promise }; +} diff --git a/gui/src/hooks/useWindowSize.ts b/gui/src/hooks/useWindowSize.ts new file mode 100644 index 0000000..461cdaa --- /dev/null +++ b/gui/src/hooks/useWindowSize.ts @@ -0,0 +1,31 @@ +import { getCurrent, LogicalSize, appWindow } from '@tauri-apps/api/window' +import { useEffect, useRef } from 'react'; + +/** + * Sets the window size, and disable resizable, and restores it on unmount. + */ +export const useWindowSize = ({ w, h }: { w: number, h: number }) => { + const oldSize = useRef<{ w: number, h: number }>(); + + useEffect(() => { + const run = async () => { + const factor = await appWindow.scaleFactor(); + const outerSize = (await getCurrent().outerSize()).toLogical(factor); + oldSize.current = { + w: outerSize.width, + h: outerSize.height + }; + + await getCurrent().setResizable(false); + await getCurrent().setSize(new LogicalSize(w, h)); + } + run(); + return () => { + const size = oldSize.current; + if (size) { + getCurrent().setSize(new LogicalSize(size.w, size.h)); + getCurrent().setResizable(true); + } + } + }, [oldSize, w, h]); +}; diff --git a/gui/src/pages/Settings.tsx b/gui/src/pages/Settings.tsx index f823c62..83ff952 100644 --- a/gui/src/pages/Settings.tsx +++ b/gui/src/pages/Settings.tsx @@ -1,17 +1,24 @@ +import { usePromise } from 'hooks/usePromise'; import React from 'react' import { useTranslation } from 'react-i18next'; import { AiOutlineLeft } from 'react-icons/ai'; import { useNavigate } from 'react-router-dom'; +import { getConfig } from 'services/config'; export const Settings: React.FC = () => { + const { loading, result, error } = usePromise(getConfig); const navigate = useNavigate(); const { t } = useTranslation(); const onSave = async () => { } + if (loading) { + return
{t('加载中...')}
+ } + return <>
-

{t('配置')}

+

{t('配置')}

- +
} \ No newline at end of file diff --git a/gui/src/services/config.ts b/gui/src/services/config.ts index 2f06aa4..4e9966b 100644 --- a/gui/src/services/config.ts +++ b/gui/src/services/config.ts @@ -1,8 +1,35 @@ import { fs } from "@tauri-apps/api" import { appConfigDir, join } from '@tauri-apps/api/path' +import { State } from '../../../src/state'; -const configDir = appConfigDir().then(c => join(c, 'config.json')); - -export const useConfig = () => { +const configFile = appConfigDir().then(c => join(c, 'config.json')); +const profileDir = appConfigDir().then(c => join(c, 'profile')); +export type Profile = { + state: State, +} + +export type Config = { +} + +const defaultConfig: Config = { +} + +export async function initFiles() { + await fs.createDir(await profileDir, { recursive: true }); + await configFile; +} +initFiles().catch(console.error); + +export async function getConfig(): Promise { + const config = await fs.readTextFile(await configFile); + try { + return JSON.parse(config); + } catch (e) { + return defaultConfig; + } +} + +export async function setConfig(config: Config) { + await fs.writeTextFile(await configFile, JSON.stringify(config)); } diff --git a/gui/vite.config.ts b/gui/vite.config.ts index 8493565..cb16e10 100644 --- a/gui/vite.config.ts +++ b/gui/vite.config.ts @@ -5,7 +5,11 @@ import eslint from 'vite-plugin-eslint'; // https://vitejs.dev/config/ export default defineConfig({ - plugins: [react(), tsconfigPaths(), eslint()], + plugins: [ + react(), + tsconfigPaths(), + eslint(), + ], // Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build` // prevent vite from obscuring rust errors