mirror of
https://github.com/spartanz51/tutabridge.git
synced 2026-06-24 10:54:32 +02:00
- Expose the sync limit in the config panel: "Fetch all mail" checkbox (sync_limit=0) + a max-per-folder number input. - Config fields are editable while the bridge runs; Save persists anytime, a Restart button (stop+start) applies changes. - Fix the in-process restart: stop() was fire-and-forget and left IMAP/SMTP tasks holding their ports, so the next start failed to bind and aborted the new syncer before it listed folders (empty store). stop() now aborts all three tasks and awaits full teardown.
103 lines
3.1 KiB
TypeScript
103 lines
3.1 KiB
TypeScript
import { useState } from "react";
|
|
import { useBridge } from "./hooks/useBridge";
|
|
import { Dashboard } from "./components/Dashboard";
|
|
import { ConnectionPanel } from "./components/ConnectionPanel";
|
|
import { ConfigPanel } from "./components/ConfigPanel";
|
|
import { LogsPanel } from "./components/LogsPanel";
|
|
import { statusLabel, isError } from "./types";
|
|
import "./App.css";
|
|
|
|
type Tab = "dashboard" | "connection" | "config" | "logs";
|
|
|
|
function App() {
|
|
const [tab, setTab] = useState<Tab>("dashboard");
|
|
const bridge = useBridge();
|
|
|
|
const status = bridge.status;
|
|
const isRunning = status === "Running";
|
|
const isStarting = status === "Starting";
|
|
const statusColor = isRunning
|
|
? "var(--green)"
|
|
: isStarting
|
|
? "var(--orange)"
|
|
: status && isError(status)
|
|
? "var(--red)"
|
|
: "var(--gray)";
|
|
|
|
return (
|
|
<div className="app">
|
|
<header className="app-header">
|
|
<div className="header-left">
|
|
<h1>TutaBridge</h1>
|
|
<div className="header-status">
|
|
<span className="status-dot" style={{ background: statusColor }} />
|
|
<span className="header-status-text">
|
|
{status ? statusLabel(status) : "Loading..."}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<nav className="tabs">
|
|
<button
|
|
className={tab === "dashboard" ? "active" : ""}
|
|
onClick={() => setTab("dashboard")}
|
|
>
|
|
Dashboard
|
|
</button>
|
|
<button
|
|
className={tab === "connection" ? "active" : ""}
|
|
onClick={() => setTab("connection")}
|
|
>
|
|
Connection
|
|
</button>
|
|
<button
|
|
className={tab === "config" ? "active" : ""}
|
|
onClick={() => setTab("config")}
|
|
>
|
|
Config
|
|
</button>
|
|
<button
|
|
className={tab === "logs" ? "active" : ""}
|
|
onClick={() => setTab("logs")}
|
|
>
|
|
Logs{bridge.logs.length > 0 ? ` (${bridge.logs.length})` : ""}
|
|
</button>
|
|
</nav>
|
|
</header>
|
|
<main className="app-content">
|
|
{tab === "dashboard" && (
|
|
<Dashboard
|
|
status={bridge.status}
|
|
stats={bridge.stats}
|
|
hasSavedSession={bridge.hasSavedSession}
|
|
loading={bridge.loading}
|
|
onStart={bridge.startBridge}
|
|
onStop={bridge.stopBridge}
|
|
/>
|
|
)}
|
|
{tab === "connection" && (
|
|
<ConnectionPanel
|
|
config={bridge.config}
|
|
status={bridge.status}
|
|
bridgePassword={bridge.bridgePassword}
|
|
onRegeneratePassword={bridge.regenerateBridgePassword}
|
|
/>
|
|
)}
|
|
{tab === "config" && (
|
|
<ConfigPanel
|
|
config={bridge.config}
|
|
status={bridge.status}
|
|
loading={bridge.loading}
|
|
onSave={bridge.saveConfig}
|
|
onRestart={bridge.restartBridge}
|
|
/>
|
|
)}
|
|
{tab === "logs" && (
|
|
<LogsPanel logs={bridge.logs} onClear={bridge.clearLogs} />
|
|
)}
|
|
</main>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default App;
|