build: add eslint, i18n, daisyui

main
spacemeowx2 2023-03-06 20:21:29 +08:00 committed by imspace
parent 5f9ee2e3fd
commit 01c0478a15
17 changed files with 3313 additions and 103 deletions

View File

@ -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>

View File

@ -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
}
}
}
}
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -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
} }
] ]

View File

@ -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>
); );

View File

@ -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>
); );

View File

@ -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' />
} }

18
gui/src/i18n/config.ts Normal file
View File

@ -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,
});

View File

@ -0,0 +1,7 @@
{
"title": "Welcome {{name}}, to react using react-i18next fully type-safe",
"description": {
"part1": "This is a simple example.",
"part2": "😉"
}
}

View File

@ -3,24 +3,34 @@
@tailwind utilities; @tailwind utilities;
:root { :root {
font-family: Inter, Avenir, Helvetica, Arial, sans-serif; font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
font-size: 16px; font-size: 16px;
line-height: 24px; line-height: 24px;
font-weight: 400; font-weight: 400;
color: #0f0f0f; color: #0f0f0f;
background-color: #f6f6f6; background-color: #f6f6f6;
font-synthesis: none; font-synthesis: none;
text-rendering: optimizeLegibility; text-rendering: optimizeLegibility;
-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) {
:root { :root {
color: #f6f6f6; color: #f6f6f6;
background-color: #2f2f2f; background-color: #2f2f2f;
} }
} }

View File

@ -10,4 +10,3 @@ ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
</BrowserRouter> </BrowserRouter>
</React.StrictMode> </React.StrictMode>
); );

View File

@ -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>
</> </>
} }

View File

@ -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>
</>
}

View File

@ -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 = () => {
}

37
gui/src/services/s3si.ts Normal file
View File

@ -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
}
}

View File

@ -6,5 +6,5 @@ module.exports = {
theme: { theme: {
extend: {}, extend: {},
}, },
plugins: [], plugins: [require("daisyui")],
} }

View File

@ -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