build: add eslint, i18n, daisyui
parent
5f9ee2e3fd
commit
01c0478a15
|
|
@ -5,7 +5,7 @@
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<link href="/src/main.css" rel="stylesheet" />
|
<link href="/src/main.css" rel="stylesheet" />
|
||||||
<title>Tauri + React + TS</title>
|
<title>s3si.ts</title>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,17 @@
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "tsc && vite build",
|
"build": "tsc && vite build",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"tauri": "tauri"
|
"tauri": "tauri",
|
||||||
|
"lint": "eslint --max-warnings=0 src"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tauri-apps/api": "^1.2.0",
|
"@tauri-apps/api": "^1.2.0",
|
||||||
|
"daisyui": "^2.51.3",
|
||||||
|
"i18next": "^22.4.10",
|
||||||
|
"i18next-browser-languagedetector": "^7.0.1",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
|
"react-i18next": "^12.2.0",
|
||||||
"react-icons": "^4.8.0",
|
"react-icons": "^4.8.0",
|
||||||
"react-router-dom": "^6.8.2"
|
"react-router-dom": "^6.8.2"
|
||||||
},
|
},
|
||||||
|
|
@ -23,10 +28,30 @@
|
||||||
"@types/react-dom": "^18.0.6",
|
"@types/react-dom": "^18.0.6",
|
||||||
"@vitejs/plugin-react": "^3.1.0",
|
"@vitejs/plugin-react": "^3.1.0",
|
||||||
"autoprefixer": "^10.4.13",
|
"autoprefixer": "^10.4.13",
|
||||||
|
"eslint": "^8.35.0",
|
||||||
|
"eslint-config-react-app": "^7.0.1",
|
||||||
"postcss": "^8.4.21",
|
"postcss": "^8.4.21",
|
||||||
"tailwindcss": "^3.2.7",
|
"tailwindcss": "^3.2.7",
|
||||||
"typescript": "^4.9.5",
|
"typescript": "^4.9.5",
|
||||||
"vite": "^4.1.4",
|
"vite": "^4.1.4",
|
||||||
|
"vite-plugin-eslint": "^1.8.1",
|
||||||
"vite-tsconfig-paths": "^4.0.5"
|
"vite-tsconfig-paths": "^4.0.5"
|
||||||
|
},
|
||||||
|
"eslintConfig": {
|
||||||
|
"extends": "react-app"
|
||||||
|
},
|
||||||
|
"pnpm": {
|
||||||
|
"packageExtensions": {
|
||||||
|
"eslint-plugin-flowtype": {
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@babel/plugin-syntax-flow": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@babel/plugin-transform-react-jsx": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
3169
gui/pnpm-lock.yaml
3169
gui/pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
|
|
@ -71,8 +71,8 @@
|
||||||
"fullscreen": false,
|
"fullscreen": false,
|
||||||
"resizable": true,
|
"resizable": true,
|
||||||
"title": "s3si.ts",
|
"title": "s3si.ts",
|
||||||
"width": 800,
|
"width": 400,
|
||||||
"height": 600,
|
"height": 300,
|
||||||
"visible": false
|
"visible": false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,23 @@
|
||||||
|
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";
|
||||||
import { Layout } from "components/Layout";
|
import { Layout } from "components/Layout";
|
||||||
import { Home } from "pages/Home";
|
import { Home } from "pages/Home";
|
||||||
import { useEffect } from "react";
|
import { Settings } from "pages/Settings";
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getCurrent().show()
|
try {
|
||||||
|
getCurrent().show().catch(e => console.error(e))
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
}
|
||||||
}, [])
|
}, [])
|
||||||
return (
|
return (
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path='/' element={<Layout />}>
|
<Route path='/' element={<Layout />}>
|
||||||
<Route index element={<Home />} />
|
<Route index element={<Home />} />
|
||||||
|
<Route path='/settings' element={<Settings />} />
|
||||||
</Route>
|
</Route>
|
||||||
</Routes>
|
</Routes>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import { Outlet } from "react-router-dom";
|
||||||
|
|
||||||
export const Layout: React.FC = () => {
|
export const Layout: React.FC = () => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className='w-full h-full flex flex-col'>
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,6 @@ import { AiOutlineLoading3Quarters } from 'react-icons/ai';
|
||||||
type LoadingProps = {
|
type LoadingProps = {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Loading: React.FC<LoadingProps> = ({ }) => {
|
export const Loading: React.FC<LoadingProps> = () => {
|
||||||
return <AiOutlineLoading3Quarters className='animate-spin my-2 mx-auto' />
|
return <AiOutlineLoading3Quarters className='animate-spin my-2 mx-auto' />
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
import i18next from 'i18next';
|
||||||
|
import LanguageDetector from 'i18next-browser-languagedetector';
|
||||||
|
import { initReactI18next } from 'react-i18next';
|
||||||
|
import translation from './translation/en.json';
|
||||||
|
|
||||||
|
export const resources = {
|
||||||
|
en: {
|
||||||
|
translation,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
i18next
|
||||||
|
.use(initReactI18next)
|
||||||
|
.use(LanguageDetector)
|
||||||
|
.init({
|
||||||
|
debug: import.meta.env.DEV,
|
||||||
|
resources,
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"title": "Welcome {{name}}, to react using react-i18next fully type-safe",
|
||||||
|
"description": {
|
||||||
|
"part1": "This is a simple example.",
|
||||||
|
"part2": "😉"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -16,6 +16,16 @@
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
-webkit-text-size-adjust: 100%;
|
-webkit-text-size-adjust: 100%;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
#root {
|
||||||
|
height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
|
|
|
||||||
|
|
@ -10,4 +10,3 @@ ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
</React.StrictMode>
|
</React.StrictMode>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,42 +1,13 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { WebviewWindow } from '@tauri-apps/api/window'
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Loading } from 'components/Loading'
|
|
||||||
import { JSONRPCClient, S3SIService, StdioTransport } from 'jsonrpc';
|
|
||||||
import { invoke } from '@tauri-apps/api';
|
|
||||||
import { emit } from '@tauri-apps/api/event';
|
|
||||||
|
|
||||||
const client = new JSONRPCClient<S3SIService>({
|
export const Home: React.FC = () => {
|
||||||
transport: new StdioTransport()
|
const { t } = useTranslation();
|
||||||
}).getProxy();
|
|
||||||
|
|
||||||
export const Home: React.FC = ({ }) => {
|
|
||||||
const onHello = async () => {
|
|
||||||
const result = await client.loginSteps();
|
|
||||||
console.log(result)
|
|
||||||
if (result.error) {
|
|
||||||
throw new Error(result.error.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
const login: string | null = await invoke('open_login_window', {
|
|
||||||
url: result.result.url
|
|
||||||
})
|
|
||||||
if (login === null || login === '') {
|
|
||||||
console.log('user cancel login');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const loginResult: { url: string } = JSON.parse(login);
|
|
||||||
const sessionToken = await client.loginSteps({
|
|
||||||
authCodeVerifier: result.result.authCodeVerifier,
|
|
||||||
login: loginResult.url,
|
|
||||||
})
|
|
||||||
if (sessionToken.error) {
|
|
||||||
throw new Error(sessionToken.error.message);
|
|
||||||
}
|
|
||||||
console.log('sessionToken', sessionToken.result);
|
|
||||||
}
|
|
||||||
return <>
|
return <>
|
||||||
Hello world! <Loading />
|
<div className='card m-2 h-full'>
|
||||||
<button onClick={onHello}>Hello</button>
|
{t('欢迎!')}
|
||||||
|
<a href='/settings' className='btn btn-primary'>{t('配置')}</a>
|
||||||
|
</div>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
import React from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { AiOutlineLeft } from 'react-icons/ai';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
|
export const Settings: React.FC = () => {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const onSave = async () => {
|
||||||
|
}
|
||||||
|
|
||||||
|
return <>
|
||||||
|
<div className='card m-2 h-full'>
|
||||||
|
<h2 className="card-title text-center w-full"><button onClick={() => navigate('/')}><AiOutlineLeft /></button>{t('配置')}</h2>
|
||||||
|
<div className='card'>
|
||||||
|
<div className="form-control w-full max-w-xs mb-4">
|
||||||
|
<label className="label">
|
||||||
|
<span className="label-text">{t('stat.ink API密钥')}</span>
|
||||||
|
<span className="label-text-alt"><a
|
||||||
|
className='underline'
|
||||||
|
target='_blank'
|
||||||
|
rel='noopener noreferrer'
|
||||||
|
href='https://stat.ink/profile'
|
||||||
|
title={t('打开 stat.ink') ?? undefined}
|
||||||
|
>{t('stat.ink')}</a></span>
|
||||||
|
</label>
|
||||||
|
<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>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
import { fs } from "@tauri-apps/api"
|
||||||
|
import { appConfigDir, join } from '@tauri-apps/api/path'
|
||||||
|
|
||||||
|
const configDir = appConfigDir().then(c => join(c, 'config.json'));
|
||||||
|
|
||||||
|
export const useConfig = () => {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
import { invoke } from "@tauri-apps/api";
|
||||||
|
import { JSONRPCClient, S3SIService, StdioTransport } from "jsonrpc";
|
||||||
|
import { useCallback } from "react";
|
||||||
|
|
||||||
|
const client = new JSONRPCClient<S3SIService>({
|
||||||
|
transport: new StdioTransport()
|
||||||
|
}).getProxy();
|
||||||
|
|
||||||
|
export const useLogin = () => {
|
||||||
|
const login = useCallback(async () => {
|
||||||
|
const result = await client.loginSteps();
|
||||||
|
if (result.error) {
|
||||||
|
throw new Error(result.error.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
const login: string | null = await invoke('open_login_window', {
|
||||||
|
url: result.result.url
|
||||||
|
})
|
||||||
|
if (login === null || login === '') {
|
||||||
|
console.log('user cancel login');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const loginResult: { url: string } = JSON.parse(login);
|
||||||
|
const sessionToken = await client.loginSteps({
|
||||||
|
authCodeVerifier: result.result.authCodeVerifier,
|
||||||
|
login: loginResult.url,
|
||||||
|
})
|
||||||
|
if (sessionToken.error) {
|
||||||
|
throw new Error(sessionToken.error.message);
|
||||||
|
}
|
||||||
|
return sessionToken.result;
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return {
|
||||||
|
login
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -6,5 +6,5 @@ module.exports = {
|
||||||
theme: {
|
theme: {
|
||||||
extend: {},
|
extend: {},
|
||||||
},
|
},
|
||||||
plugins: [],
|
plugins: [require("daisyui")],
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
import { defineConfig } from "vite";
|
import { defineConfig } from "vite";
|
||||||
import react from "@vitejs/plugin-react";
|
import react from "@vitejs/plugin-react";
|
||||||
import tsconfigPaths from 'vite-tsconfig-paths'
|
import tsconfigPaths from 'vite-tsconfig-paths';
|
||||||
|
import eslint from 'vite-plugin-eslint';
|
||||||
|
|
||||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [react(), tsconfigPaths()],
|
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