feat(files): the Files page — the library in one aligned list, its folders in a rail

Files is the one library: every done file, the ones shared with me, or one
folder's, picked in a side rail (sources, own folders, the storage meter) and
mirrored to ?folder=. One table with every kind of value in its own column;
Copy link, Share and Save in sight, every action on a right click: play,
cut, rename in the row, move to an own folder or back home, keep, details,
re-download, delete.

The Downloads page keeps only the queue until the tray replaces it. The old
/api/downloads library routes are gone: list shared, upload, the details
PATCH, delete, the file and the usage now live under /api/files, and a
listed file says which source it came from. The job's "expired" flag is gone
with the state it described.
This commit is contained in:
2026-10-11 05:34:25 +02:00
parent 1a1665b8ec
commit 85aa917b38
65 changed files with 2295 additions and 1044 deletions
+6 -3
View File
@@ -1,4 +1,4 @@
import type { FeedFilters } from "./api";
import type { FeedFilters, FileListParams } from "./api";
// Single source of truth for React Query keys. Every key root lives here exactly once, so a typo
// at one of the ~200 call sites can no longer mint an orphan key that silently never invalidates
@@ -64,11 +64,14 @@ export const qk = {
// --- sharing ---
shareRecipients: () => ["share-recipients"] as const,
// --- files (the library) ---
// Every listing's key starts with "files", so one invalidation reaches each folder, search and sort.
files: (p?: FileListParams) => (p ? (["files", p] as const) : (["files"] as const)),
fileFolders: () => ["file-folders"] as const,
storageUsage: () => ["storage-usage"] as const,
// --- downloads ---
downloads: () => ["downloads"] as const,
downloadsShared: () => ["downloads-shared"] as const,
downloadIndex: () => ["download-index"] as const,
downloadUsage: () => ["download-usage"] as const,
downloadProfiles: () => ["download-profiles"] as const,
downloadCredentials: () => ["download-credentials"] as const,
downloadLinks: (jobId: number) => ["download-links", jobId] as const,