feat: add config
parent
01c0478a15
commit
830456ea62
|
|
@ -13,7 +13,7 @@ edition = "2021"
|
||||||
tauri-build = { version = "1.2", features = [] }
|
tauri-build = { version = "1.2", features = [] }
|
||||||
|
|
||||||
[dependencies]
|
[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 = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
tokio = { version = "1.0", features = ["time"] }
|
tokio = { version = "1.0", features = ["time"] }
|
||||||
|
|
|
||||||
|
|
@ -39,10 +39,14 @@
|
||||||
},
|
},
|
||||||
"fs": {
|
"fs": {
|
||||||
"scope": [
|
"scope": [
|
||||||
"$APPCONFIG",
|
"$APPCONFIG/*",
|
||||||
"$APPDATA",
|
"$APPDATA/*",
|
||||||
"$APPCACHE"
|
"$APPCACHE/*"
|
||||||
]
|
],
|
||||||
|
"all": true
|
||||||
|
},
|
||||||
|
"path": {
|
||||||
|
"all": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"bundle": {
|
"bundle": {
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'i18n/config';
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { getCurrent } from "@tauri-apps/api/window";
|
import { getCurrent } from "@tauri-apps/api/window";
|
||||||
import { Routes, Route } from "react-router-dom";
|
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 React from 'react'
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { AiOutlineLeft } from 'react-icons/ai';
|
import { AiOutlineLeft } from 'react-icons/ai';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import { getConfig } from 'services/config';
|
||||||
|
|
||||||
export const Settings: React.FC = () => {
|
export const Settings: React.FC = () => {
|
||||||
|
const { loading, result, error } = usePromise(getConfig);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const onSave = async () => {
|
const onSave = async () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return <div className='card m-2 h-full'>{t('加载中...')}</div>
|
||||||
|
}
|
||||||
|
|
||||||
return <>
|
return <>
|
||||||
<div className='card m-2 h-full'>
|
<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='card'>
|
||||||
<div className="form-control w-full max-w-xs mb-4">
|
<div className="form-control w-full max-w-xs mb-4">
|
||||||
<label className="label">
|
<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" />
|
<input type="text" placeholder={t('长度为43') ?? undefined} className="input input-bordered w-full max-w-xs" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button className='btn btn-primary' onClick={onSave}>{t('保存')}</button>
|
<button className='btn btn-primary w-20' onClick={onSave}>{t('保存')}</button>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
|
@ -1,8 +1,35 @@
|
||||||
import { fs } from "@tauri-apps/api"
|
import { fs } from "@tauri-apps/api"
|
||||||
import { appConfigDir, join } from '@tauri-apps/api/path'
|
import { appConfigDir, join } from '@tauri-apps/api/path'
|
||||||
|
import { State } from '../../../src/state';
|
||||||
|
|
||||||
const configDir = appConfigDir().then(c => join(c, 'config.json'));
|
const configFile = appConfigDir().then(c => join(c, 'config.json'));
|
||||||
|
const profileDir = appConfigDir().then(c => join(c, 'profile'));
|
||||||
export const useConfig = () => {
|
|
||||||
|
|
||||||
|
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/
|
// https://vitejs.dev/config/
|
||||||
export default defineConfig({
|
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`
|
// Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build`
|
||||||
// prevent vite from obscuring rust errors
|
// prevent vite from obscuring rust errors
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue