2025-03-30 14:30:33 -03:00
|
|
|
import { useState, useEffect } from "react";
|
2025-03-30 13:38:55 -03:00
|
|
|
import { invoke } from "@tauri-apps/api/core";
|
|
|
|
import "./App.css";
|
|
|
|
|
2025-03-30 14:30:33 -03:00
|
|
|
interface RcloneConfig {
|
|
|
|
name: string;
|
|
|
|
remote_path: string;
|
|
|
|
local_path: string;
|
|
|
|
access_key: string;
|
|
|
|
secret_key: string;
|
|
|
|
}
|
|
|
|
|
|
|
|
interface SyncStatus {
|
|
|
|
name: string;
|
|
|
|
status: string;
|
|
|
|
transferred: string;
|
|
|
|
bytes: string;
|
|
|
|
errors: number;
|
|
|
|
last_updated: string;
|
|
|
|
}
|
|
|
|
|
|
|
|
type Screen = "Main" | "Status";
|
|
|
|
|
2025-03-30 19:28:28 -03:00
|
|
|
function Page() {
|
2025-03-30 14:30:33 -03:00
|
|
|
const [state, setState] = useState({
|
|
|
|
name: "",
|
|
|
|
access_key: "",
|
|
|
|
secret_key: "",
|
|
|
|
status_text: "Enter credentials to set up sync",
|
|
|
|
sync_statuses: [] as SyncStatus[],
|
|
|
|
show_config_dialog: false,
|
|
|
|
show_about_dialog: false,
|
|
|
|
current_screen: "Main" as Screen,
|
|
|
|
});
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
if (state.current_screen === "Status") {
|
|
|
|
const interval = setInterval(() => {
|
|
|
|
// In a real app, you would fetch actual configs here
|
|
|
|
// This is just a mock implementation
|
|
|
|
invoke<SyncStatus>("get_status", { remoteName: "example" })
|
|
|
|
.then((status) => {
|
|
|
|
setState(prev => ({
|
|
|
|
...prev,
|
|
|
|
sync_statuses: [status]
|
|
|
|
}));
|
|
|
|
})
|
|
|
|
.catch(console.error);
|
|
|
|
}, 5000);
|
|
|
|
return () => clearInterval(interval);
|
|
|
|
}
|
|
|
|
}, [state.current_screen]);
|
|
|
|
|
|
|
|
const saveConfig = async () => {
|
|
|
|
if (!state.name || !state.access_key || !state.secret_key) {
|
|
|
|
setState(prev => ({ ...prev, status_text: "All fields are required!" }));
|
|
|
|
return;
|
|
|
|
}
|
2025-03-30 13:38:55 -03:00
|
|
|
|
2025-03-30 14:30:33 -03:00
|
|
|
const config: RcloneConfig = {
|
|
|
|
name: state.name,
|
|
|
|
remote_path: `s3://${state.name}`,
|
|
|
|
local_path: `${window.__TAURI__.path.homeDir}/General Bots/${state.name}`,
|
|
|
|
access_key: state.access_key,
|
|
|
|
secret_key: state.secret_key,
|
|
|
|
};
|
|
|
|
|
|
|
|
try {
|
|
|
|
await invoke("save_config", { config });
|
|
|
|
setState(prev => ({
|
|
|
|
...prev,
|
|
|
|
status_text: "New sync saved!",
|
|
|
|
show_config_dialog: false
|
|
|
|
}));
|
|
|
|
} catch (e) {
|
|
|
|
setState(prev => ({
|
|
|
|
...prev,
|
|
|
|
status_text: `Failed to save config: ${e}`
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const startSync = async () => {
|
|
|
|
// In a real app, you would fetch actual configs here
|
|
|
|
const config: RcloneConfig = {
|
|
|
|
name: state.name || "example",
|
|
|
|
remote_path: `s3://${state.name || "example"}`,
|
|
|
|
local_path: `${window.__TAURI__.path.homeDir}/General Bots/${state.name || "example"}`,
|
|
|
|
access_key: state.access_key || "dummy",
|
|
|
|
secret_key: state.secret_key || "dummy",
|
|
|
|
};
|
|
|
|
|
|
|
|
try {
|
|
|
|
await invoke("start_sync", { config });
|
|
|
|
setState(prev => ({
|
|
|
|
...prev,
|
|
|
|
status_text: "Sync started!"
|
|
|
|
}));
|
|
|
|
} catch (e) {
|
|
|
|
setState(prev => ({
|
|
|
|
...prev,
|
|
|
|
status_text: `Failed to start sync: ${e}`
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const stopSync = async () => {
|
|
|
|
try {
|
|
|
|
await invoke("stop_sync");
|
|
|
|
setState(prev => ({
|
|
|
|
...prev,
|
|
|
|
status_text: "Sync stopped."
|
|
|
|
}));
|
|
|
|
} catch (e) {
|
|
|
|
setState(prev => ({
|
|
|
|
...prev,
|
|
|
|
status_text: `Failed to stop sync: ${e}`
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
};
|
2025-03-30 13:38:55 -03:00
|
|
|
|
|
|
|
return (
|
2025-03-30 14:30:33 -03:00
|
|
|
<div className="app">
|
|
|
|
<div className="menu-bar">
|
|
|
|
<button onClick={() => setState(prev => ({ ...prev, show_config_dialog: true }))}>
|
|
|
|
Add Sync Configuration
|
|
|
|
</button>
|
|
|
|
<button onClick={() => setState(prev => ({ ...prev, show_about_dialog: true }))}>
|
|
|
|
About
|
|
|
|
</button>
|
2025-03-30 13:38:55 -03:00
|
|
|
</div>
|
2025-03-30 14:30:33 -03:00
|
|
|
|
|
|
|
{state.current_screen === "Main" ? (
|
|
|
|
<div className="main-screen">
|
|
|
|
<h1>General Bots</h1>
|
|
|
|
<p>{state.status_text}</p>
|
|
|
|
<button onClick={startSync}>Start Sync</button>
|
|
|
|
<button onClick={stopSync}>Stop Sync</button>
|
|
|
|
<button onClick={() => setState(prev => ({ ...prev, current_screen: "Status" }))}>
|
|
|
|
Show Status
|
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
) : (
|
|
|
|
<div className="status-screen">
|
|
|
|
<h1>Sync Status</h1>
|
|
|
|
<div className="status-list">
|
|
|
|
{state.sync_statuses.map((status, index) => (
|
|
|
|
<div key={index} className="status-item">
|
|
|
|
<h2>{status.name}</h2>
|
|
|
|
<p>Status: {status.status}</p>
|
|
|
|
<p>Transferred: {status.transferred}</p>
|
|
|
|
<p>Bytes: {status.bytes}</p>
|
|
|
|
<p>Errors: {status.errors}</p>
|
|
|
|
<p>Last Updated: {status.last_updated}</p>
|
|
|
|
</div>
|
|
|
|
))}
|
|
|
|
</div>
|
|
|
|
<button onClick={() => setState(prev => ({ ...prev, current_screen: "Main" }))}>
|
|
|
|
Back
|
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
)}
|
|
|
|
|
|
|
|
{state.show_config_dialog && (
|
|
|
|
<div className="dialog">
|
|
|
|
<h2>Add Sync Configuration</h2>
|
|
|
|
<input
|
|
|
|
value={state.name}
|
|
|
|
onChange={(e) => setState(prev => ({ ...prev, name: e.target.value }))}
|
|
|
|
placeholder="Enter sync name"
|
|
|
|
/>
|
|
|
|
<input
|
|
|
|
value={state.access_key}
|
|
|
|
onChange={(e) => setState(prev => ({ ...prev, access_key: e.target.value }))}
|
|
|
|
placeholder="Enter access key"
|
|
|
|
/>
|
|
|
|
<input
|
|
|
|
value={state.secret_key}
|
|
|
|
onChange={(e) => setState(prev => ({ ...prev, secret_key: e.target.value }))}
|
|
|
|
placeholder="Enter secret key"
|
|
|
|
/>
|
|
|
|
<button onClick={saveConfig}>Save</button>
|
|
|
|
<button onClick={() => setState(prev => ({ ...prev, show_config_dialog: false }))}>
|
|
|
|
Cancel
|
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
)}
|
|
|
|
|
|
|
|
{state.show_about_dialog && (
|
|
|
|
<div className="dialog">
|
|
|
|
<h2>About General Bots</h2>
|
|
|
|
<p>Version: 1.0.0</p>
|
|
|
|
<p>A professional-grade sync tool for OneDrive/Dropbox-like functionality.</p>
|
|
|
|
<button onClick={() => setState(prev => ({ ...prev, show_about_dialog: false }))}>
|
|
|
|
Close
|
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
)}
|
|
|
|
</div>
|
2025-03-30 13:38:55 -03:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2025-03-30 19:28:28 -03:00
|
|
|
export default Page;
|