diff --git a/package.json b/package.json index c354e4f..55feef4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "real-debrid-downloader", - "version": "1.5.28", + "version": "1.5.29", "description": "Real-Debrid Downloader Desktop (Electron + React + TypeScript)", "main": "build/main/main/main.js", "author": "Sucukdeluxe", diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx index 67a861c..f221552 100644 --- a/src/renderer/App.tsx +++ b/src/renderer/App.tsx @@ -8,6 +8,7 @@ import type { DownloadItem, DownloadStats, DuplicatePolicy, + HistoryEntry, PackageEntry, StartConflictEntry, UiSnapshot, @@ -15,7 +16,7 @@ import type { UpdateInstallProgress } from "../shared/types"; -type Tab = "collector" | "downloads" | "statistics" | "settings"; +type Tab = "collector" | "downloads" | "history" | "statistics" | "settings"; type SettingsSubTab = "allgemein" | "accounts" | "entpacken" | "geschwindigkeit" | "bereinigung" | "updates"; interface CollectorTab { @@ -480,6 +481,25 @@ export function App(): ReactElement { const [linkPopup, setLinkPopup] = useState(null); const [selectedIds, setSelectedIds] = useState>(new Set()); const [deleteConfirm, setDeleteConfirm] = useState<{ ids: Set; dontAsk: boolean } | null>(null); + const [historyEntries, setHistoryEntries] = useState([]); + const [historyCollapsed, setHistoryCollapsed] = useState>({}); + + // Load history when tab changes to history + useEffect(() => { + if (tab !== "history") return; + const loadHistory = async (): Promise => { + try { + const entries = await window.rd.getHistory(); + console.log("History loaded:", entries); + if (mountedRef.current && entries) { + setHistoryEntries(entries); + } + } catch (err) { + console.error("Failed to load history:", err); + } + }; + void loadHistory(); + }, [tab]); const currentCollectorTab = collectorTabs.find((t) => t.id === activeCollectorTab) ?? collectorTabs[0]; @@ -1960,6 +1980,7 @@ export function App(): ReactElement { +
{tab === "downloads" && ( @@ -2138,6 +2159,67 @@ export function App(): ReactElement { )} + {tab === "history" && ( +
+
+ {historyEntries.length} Paket{historyEntries.length !== 1 ? "e" : ""} im Verlauf + {historyEntries.length > 0 && ( + + )} +
+ {historyEntries.length === 0 &&
Noch keine abgeschlossenen Pakete im Verlauf.
} + {historyEntries.map((entry) => { + const collapsed = historyCollapsed[entry.id] ?? true; + return ( +
+
setHistoryCollapsed((prev) => ({ ...prev, [entry.id]: !collapsed }))} style={{ cursor: "pointer" }}> +
+
+ +

{entry.name}

+
+ {entry.status === "completed" ? "100%" : "-"} + {humanSize(entry.totalBytes)} + {humanSize(entry.downloadedBytes)} + {entry.provider ? providerLabels[entry.provider] : "-"} + {entry.status === "completed" ? "Abgeschlossen" : "Gelöscht"} + - +
+
+
+ {!collapsed && ( +
+
+ Abgeschlossen am + {new Date(entry.completedAt).toLocaleString("de-DE")} + Dateien + {entry.fileCount} Datei{entry.fileCount !== 1 ? "en" : ""} + Gesamtgröße + {humanSize(entry.totalBytes)} + Heruntergeladen + {humanSize(entry.downloadedBytes)} + Dauer + {entry.durationSeconds >= 3600 ? `${Math.floor(entry.durationSeconds / 3600)}h ${Math.floor((entry.durationSeconds % 3600) / 60)}min` : entry.durationSeconds >= 60 ? `${Math.floor(entry.durationSeconds / 60)}min ${entry.durationSeconds % 60}s` : `${entry.durationSeconds}s`} + Durchschnitt + {entry.durationSeconds > 0 ? formatSpeedMbps(Math.round(entry.downloadedBytes / entry.durationSeconds)) : "-"} + Provider + {entry.provider ? providerLabels[entry.provider] : "-"} + Zielordner + {entry.outputDir || "-"} + Status + {entry.status === "completed" ? "Abgeschlossen" : "Gelöscht"} +
+
+ +
+
+ )} +
+ ); + })} +
+ )} + {tab === "statistics" && (
diff --git a/src/renderer/styles.css b/src/renderer/styles.css index 4c43752..9f2023c 100644 --- a/src/renderer/styles.css +++ b/src/renderer/styles.css @@ -1102,6 +1102,60 @@ body, background: linear-gradient(90deg, #22c55e, #4ade80); } +/* History Tab */ +.history-view { + display: flex; + flex-direction: column; + gap: 6px; +} + +.history-toolbar { + display: flex; + align-items: center; + justify-content: space-between; + padding: 5px 12px; + background: var(--card); + border: 1px solid var(--border); + border-radius: 8px; + font-size: 12px; + font-weight: 700; + color: var(--muted); +} + +.history-card { + cursor: default; +} + +.history-details { + padding: 10px 12px; + border-top: 1px solid color-mix(in srgb, var(--border) 40%, transparent); +} + +.history-detail-grid { + display: grid; + grid-template-columns: 140px 1fr; + gap: 4px 16px; + font-size: 13px; +} + +.history-label { + color: var(--muted); + font-weight: 600; +} + +.history-path { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.history-actions { + margin-top: 10px; + display: flex; + justify-content: flex-end; + gap: 8px; +} + table { width: 100%; table-layout: fixed;