feat: add config
parent
01c0478a15
commit
830456ea62
|
|
@ -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"] }
|
||||
|
|
|
|||
|
|
@ -39,10 +39,14 @@
|
|||
},
|
||||
"fs": {
|
||||
"scope": [
|
||||
"$APPCONFIG",
|
||||
"$APPDATA",
|
||||
"$APPCACHE"
|
||||
]
|
||||
"$APPCONFIG/*",
|
||||
"$APPDATA/*",
|
||||
"$APPCACHE/*"
|
||||
],
|
||||
"all": true
|
||||
},
|
||||
"path": {
|
||||
"all": true
|
||||
}
|
||||
},
|
||||
"bundle": {
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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 <p>Loading...</p>;
|
||||
* }
|
||||
* if (error) {
|
||||
* return <p>Error: {error.message}</p>;
|
||||
* }
|
||||
* return <p>Result: {result}</p>;
|
||||
*/
|
||||
export function usePromise<T>(factory: () => Promise<T>) {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [result, setResult] = useState<T | undefined>(undefined);
|
||||
const [error, setError] = useState<any | undefined>(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 };
|
||||
}
|
||||
|
|
@ -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]);
|
||||
};
|
||||
|
|
@ -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 <div className='card m-2 h-full'>{t('加载中...')}</div>
|
||||
}
|
||||
|
||||
return <>
|
||||
<div className='card m-2 h-full'>
|
||||
<h2 className="card-title text-center w-full"><button onClick={() => navigate('/')}><AiOutlineLeft /></button>{t('配置')}</h2>
|
||||
<h2 className="card-title" data-tauri-drag-region><button onClick={() => navigate('/')}><AiOutlineLeft /></button>{t('配置')}</h2>
|
||||
<div className='card'>
|
||||
<div className="form-control w-full max-w-xs mb-4">
|
||||
<label className="label">
|
||||
|
|
@ -27,7 +34,7 @@ export const Settings: React.FC = () => {
|
|||
<input type="text" placeholder={t('长度为43') ?? undefined} className="input input-bordered w-full max-w-xs" />
|
||||
</div>
|
||||
</div>
|
||||
<button className='btn btn-primary' onClick={onSave}>{t('保存')}</button>
|
||||
<button className='btn btn-primary w-20' onClick={onSave}>{t('保存')}</button>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
|
|
@ -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<Config> {
|
||||
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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue