first commit

This commit is contained in:
qpismont 2024-08-11 22:33:12 +02:00
commit 9c56e27b43
42 changed files with 6236 additions and 0 deletions

24
.gitignore vendored Normal file
View file

@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

3
.vscode/extensions.json vendored Normal file
View file

@ -0,0 +1,3 @@
{
"recommendations": ["tauri-apps.tauri-vscode", "rust-lang.rust-analyzer"],
}

7
README.md Normal file
View file

@ -0,0 +1,7 @@
# Tauri + React + Typescript
This template should help get you started developing with Tauri, React and Typescript in Vite.
## Recommended IDE Setup
- [VS Code](https://code.visualstudio.com/) + [Tauri](https://marketplace.visualstudio.com/items?itemName=tauri-apps.tauri-vscode) + [rust-analyzer](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer)

12
biome.json Normal file
View file

@ -0,0 +1,12 @@
{
"$schema": "https://biomejs.dev/schemas/1.8.3/schema.json",
"organizeImports": {
"enabled": true
},
"linter": {
"enabled": true,
"rules": {
"recommended": true
}
}
}

BIN
bun.lockb Executable file

Binary file not shown.

14
index.html Normal file
View file

@ -0,0 +1,14 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Tauri + React + Typescript</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

1052
package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

30
package.json Normal file
View file

@ -0,0 +1,30 @@
{
"name": "trepa-uploader",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview",
"tauri": "tauri"
},
"dependencies": {
"@tauri-apps/api": "^2.0.0-rc.0",
"@tauri-apps/plugin-shell": "^2.0.0-rc.0",
"bootstrap": "^5.3.3",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-router-dom": "^6.26.0",
"reactstrap": "^9.2.2"
},
"devDependencies": {
"@biomejs/biome": "1.8.3",
"@tauri-apps/cli": "2.0.0-rc.3",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@vitejs/plugin-react": "^4.3.1",
"typescript": "^5.5.4",
"vite": "^5.4.0"
}
}

7
src-tauri/.gitignore vendored Normal file
View file

@ -0,0 +1,7 @@
# Generated by Cargo
# will have compiled files and executables
/target/
# Generated by Tauri
# will have schema files for capabilities auto-completion
/gen/schemas

4722
src-tauri/Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

23
src-tauri/Cargo.toml Normal file
View file

@ -0,0 +1,23 @@
[package]
name = "trepa-uploader"
version = "0.0.0"
description = "A Tauri App"
authors = ["you"]
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[build-dependencies]
tauri-build = { version = "2.0.0-rc", features = [] }
[dependencies]
tauri = { version = "2.0.0-rc", features = [] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
tauri-plugin-shell = "2.0.0-rc.0"
anyhow = "1.0"
reqwest = "0.12"
[features]
# This feature is used for production builds or when a dev server is not specified, DO NOT REMOVE!!
custom-protocol = ["tauri/custom-protocol"]

3
src-tauri/build.rs Normal file
View file

@ -0,0 +1,3 @@
fn main() {
tauri_build::build()
}

View file

@ -0,0 +1,13 @@
{
"identifier": "migrated",
"description": "permissions that were migrated from v1",
"local": true,
"windows": [
"main"
],
"permissions": [
"core:default",
"shell:allow-open",
"shell:default"
]
}

BIN
src-tauri/icons/128x128.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

BIN
src-tauri/icons/32x32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 974 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 903 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
src-tauri/icons/icon.icns Normal file

Binary file not shown.

BIN
src-tauri/icons/icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

BIN
src-tauri/icons/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

55
src-tauri/src/main.rs Normal file
View file

@ -0,0 +1,55 @@
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
use tauri::{async_runtime::Mutex, Manager, State};
struct MyError(anyhow::Error);
impl<T> From<T> for MyError
where
T: Into<anyhow::Error>,
{
fn from(value: T) -> Self {
Self(value.into())
}
}
impl serde::Serialize for MyError {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(&self.0.to_string())
}
}
#[derive(Default)]
struct AppState {
url: Option<String>,
token: Option<String>,
}
#[tauri::command]
async fn login(
url: &str,
username: &str,
password: &str,
state: State<'_, Mutex<AppState>>,
) -> Result<(), MyError> {
println!("{}", url);
Ok(())
}
fn main() {
tauri::Builder::default()
.plugin(tauri_plugin_shell::init())
.setup(|app| {
app.manage(Mutex::new(AppState::default()));
Ok(())
})
.invoke_handler(tauri::generate_handler![login])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}

36
src-tauri/tauri.conf.json Normal file
View file

@ -0,0 +1,36 @@
{
"build": {
"beforeDevCommand": "bun run dev",
"beforeBuildCommand": "bun run build",
"frontendDist": "../dist",
"devUrl": "http://localhost:1420"
},
"bundle": {
"active": true,
"targets": "all",
"icon": [
"icons/32x32.png",
"icons/128x128.png",
"icons/128x128@2x.png",
"icons/icon.icns",
"icons/icon.ico"
],
"createUpdaterArtifacts": "v1Compatible"
},
"productName": "trepa-uploader",
"version": "0.0.0",
"identifier": "com.qpismont.trepauploader",
"plugins": {},
"app": {
"security": {
"csp": null
},
"windows": [
{
"title": "trepa-uploader",
"width": 800,
"height": 600
}
]
}
}

0
src/App.css Normal file
View file

25
src/App.tsx Normal file
View file

@ -0,0 +1,25 @@
import { useState } from "react";
import { invoke } from "@tauri-apps/api/core";
import "./App.css";
import { Container } from "reactstrap";
import AccountGuard from "./guards/AccountGuard";
function App() {
const [greetMsg, setGreetMsg] = useState("");
const [name, setName] = useState("");
async function greet() {
// Learn more about Tauri commands at https://tauri.app/v1/guides/features/command
//setGreetMsg(await invoke("greet", { name }));
}
return (
<Container style={{ height: "100%" }}>
<AccountGuard>
<p>Hello world</p>
</AccountGuard>
</Container>
);
}
export default App;

View file

@ -0,0 +1,50 @@
import { createContext, useEffect, useRef, useState } from "react";
import type { Account } from "../types";
import { listen, type UnlistenFn } from "@tauri-apps/api/event";
interface AccountContextProps {
children: JSX.Element | JSX.Element[];
}
interface AccountContextData {
account: Account | null;
setAccount: (account: Account | null) => void;
}
export const AccountContext = createContext<AccountContextData>({
account: null,
setAccount: () => {},
});
export default function AccountContextProvider(props: AccountContextProps) {
const [account, setAccount] = useState<Account | null>(null);
const unlistenRef = useRef<UnlistenFn | null>(null);
useEffect(() => {
listenLoginEvent();
return () => {
unlistenLoginEvent();
};
}, []);
function listenLoginEvent() {
listen<Account>("on-login", (event) => {
setAccount(event.payload);
// biome-ignore lint/suspicious/noAssignInExpressions: <explanation>
}).then((unlistenFn) => (unlistenRef.current = unlistenFn));
}
function unlistenLoginEvent() {
if (unlistenRef.current) {
unlistenRef.current();
}
}
return (
<AccountContext.Provider value={{ account: account, setAccount }}>
{props.children}
</AccountContext.Provider>
);
}

59
src/forms/LoginForm.tsx Normal file
View file

@ -0,0 +1,59 @@
import { invoke } from "@tauri-apps/api/core";
import { useState } from "react";
import { Button, Form, FormGroup, Input } from "reactstrap";
export default function LoginForm() {
const [formData, setFormData] = useState<Record<string, string>>({});
function handleOnInputChange(event: React.ChangeEvent<HTMLInputElement>) {
const name = event.target.name;
const value = event.target.value;
setFormData({
...formData,
[name]: value,
});
}
function handleOnLoginBtnClick(event: React.MouseEvent<HTMLButtonElement>) {
event.preventDefault();
invoke("login", { ...formData });
}
return (
<Form>
<FormGroup>
<Input
placeholder="URL vers le l'API du projet"
name="url"
onChange={handleOnInputChange}
value={formData.url || ""}
/>
</FormGroup>
<FormGroup>
<Input
placeholder="Nom d'utilisateur"
name="username"
onChange={handleOnInputChange}
value={formData.username || ""}
/>
</FormGroup>
<FormGroup>
<Input
type="password"
placeholder="Mot de passe"
name="password"
onChange={handleOnInputChange}
value={formData.password || ""}
/>
</FormGroup>
<FormGroup>
<Button onClick={handleOnLoginBtnClick}>Connexion</Button>
</FormGroup>
</Form>
);
}

View file

@ -0,0 +1,26 @@
import { useContext } from "react";
import { AccountContext } from "../contexts/AccountContext";
import LoginForm from "../forms/LoginForm";
import { Col, Row } from "reactstrap";
interface AccountGuardProps {
children: JSX.Element | JSX.Element[];
}
export default function AccountGuard({ children }: AccountGuardProps) {
const { account } = useContext(AccountContext);
return (
<>
{account ? (
<>{children}</>
) : (
<Row>
<Col xs={6}>
<LoginForm />
</Col>
</Row>
)}
</>
);
}

14
src/main.tsx Normal file
View file

@ -0,0 +1,14 @@
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import "bootstrap/dist/css/bootstrap.min.css";
import AccountContextProvider from "./contexts/AccountContext";
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
<React.StrictMode>
<AccountContextProvider>
<App />
</AccountContextProvider>
</React.StrictMode>,
);

4
src/types.ts Normal file
View file

@ -0,0 +1,4 @@
export interface Account {
username: string;
roleId: number;
}

1
src/vite-env.d.ts vendored Normal file
View file

@ -0,0 +1 @@
/// <reference types="vite/client" />

25
tsconfig.json Normal file
View file

@ -0,0 +1,25 @@
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src"],
"references": [{ "path": "./tsconfig.node.json" }]
}

10
tsconfig.node.json Normal file
View file

@ -0,0 +1,10 @@
{
"compilerOptions": {
"composite": true,
"skipLibCheck": true,
"module": "ESNext",
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true
},
"include": ["vite.config.ts"]
}

21
vite.config.ts Normal file
View file

@ -0,0 +1,21 @@
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
// https://vitejs.dev/config/
export default defineConfig(async () => ({
plugins: [react()],
// Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build`
//
// 1. prevent vite from obscuring rust errors
clearScreen: false,
// 2. tauri expects a fixed port, fail if that port is not available
server: {
port: 1420,
strictPort: true,
watch: {
// 3. tell vite to ignore watching `src-tauri`
ignored: ["**/src-tauri/**"],
},
},
}));