feat(files): the Downloads page goes — the tray, a Storage tab and a Downloads settings tab take its place
- The Downloads module, its rail icon and badge are gone. A bookmark, the remembered page and an old
history entry that name it land on Files (urlState.resolvePage); `g d` goes to Files too.
- Users page: a Storage tab — the disk in numbers, every user's use against their own limit with
their file count, every user's downloads; aligned tables, one menu per row (edit / reset quota,
open source, copy, the owner's quota). The admin storage rows carry quota + file count, the admin
job list its owner.
- Settings: a Downloads tab — the download formats in one list (duplicate a built-in, edit or delete
your own) and the site sign-ins, moved from Account.
- "View" on an added download, a clip or a converting upload opens the tray on that row (unfolded,
back if closed; a finished one opens as a file; under a player it opens without taking focus).
- The download dialog opens on the format used last.
- DataTable: `pager={false}` for a short list, `cardRow` to give a lone "…" its own card line.
- Welcome and README speak of Files; a new Files screenshot.
This commit is contained in:
@@ -4,7 +4,18 @@ import { qk } from "../lib/queryKeys";
|
||||
import { formatDate, formatDateTime, formatUnit } from "../lib/format";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { Activity, Bell, Check, Loader2, Monitor, RefreshCw, Trash2, User } from "lucide-react";
|
||||
import {
|
||||
Activity,
|
||||
Bell,
|
||||
Check,
|
||||
Download,
|
||||
Loader2,
|
||||
Monitor,
|
||||
RefreshCw,
|
||||
Trash2,
|
||||
User,
|
||||
} from "lucide-react";
|
||||
import { DownloadFormats } from "./ProfileEditor";
|
||||
import DiagnosticsSettings from "./DiagnosticsSettings";
|
||||
import { SCHEMES, type Scheme, type ThemePrefs } from "../lib/theme";
|
||||
import { api, HttpError, type Me } from "../lib/api";
|
||||
@@ -50,10 +61,11 @@ export interface PrefsController {
|
||||
saveState: PrefsSaveState;
|
||||
}
|
||||
|
||||
type TabId = "appearance" | "notifications" | "account" | "diagnostics";
|
||||
type TabId = "appearance" | "notifications" | "downloads" | "account" | "diagnostics";
|
||||
const TABS: { id: TabId; icon: typeof Monitor }[] = [
|
||||
{ id: "appearance", icon: Monitor },
|
||||
{ id: "notifications", icon: Bell },
|
||||
{ id: "downloads", icon: Download },
|
||||
{ id: "account", icon: User },
|
||||
{ id: "diagnostics", icon: Activity },
|
||||
];
|
||||
@@ -101,6 +113,7 @@ export default function SettingsPanel() {
|
||||
<div {...panel.panelProps(tab)} className="flex-1 min-w-0 p-4">
|
||||
{tab === "appearance" && <Appearance prefs={prefs} />}
|
||||
{tab === "notifications" && <Notifications prefs={prefs} />}
|
||||
{tab === "downloads" && <Downloads />}
|
||||
{tab === "account" && <Account me={me} onOpenWizard={openWizard} />}
|
||||
{tab === "diagnostics" && <DiagnosticsSettings />}
|
||||
</div>
|
||||
@@ -615,7 +628,7 @@ function PlexWatchSync() {
|
||||
// (e.g. a private Facebook group you belong to) so downloads from it use YOUR login. Write-only —
|
||||
// the stored blob is never shown back; each row shows only the host + whether it's set + when.
|
||||
// A cookie-authed download is kept private to your account (never cache-shared with another user).
|
||||
// Exported for a focused DOM test (write-only behaviour); rendered inside Account below.
|
||||
// Exported for a focused DOM test (write-only behaviour); rendered on the Downloads tab below.
|
||||
export function DownloadAuth() {
|
||||
const { t, i18n } = useTranslation();
|
||||
const qc = useQueryClient();
|
||||
@@ -796,6 +809,23 @@ export function AccountHeader({ me }: { me: Me }) {
|
||||
);
|
||||
}
|
||||
|
||||
// How this account downloads: its formats (the download dialog's choices) and its sign-ins to sites
|
||||
// that need one. Preferences of the downloading, not of the account's identity.
|
||||
function Downloads() {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<>
|
||||
<Section title={t("settings.downloads.formats")}>
|
||||
<p className="text-xs text-muted leading-relaxed mb-2">
|
||||
{t("settings.downloads.formatsIntro")}
|
||||
</p>
|
||||
<DownloadFormats />
|
||||
</Section>
|
||||
<DownloadAuth />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function Account({ me, onOpenWizard }: { me: Me; onOpenWizard: () => void }) {
|
||||
const { t } = useTranslation();
|
||||
const confirm = useConfirm();
|
||||
@@ -825,8 +855,6 @@ function Account({ me, onOpenWizard }: { me: Me; onOpenWizard: () => void }) {
|
||||
|
||||
{me.role === "admin" && me.plex_enabled && <PlexWatchSync />}
|
||||
|
||||
<DownloadAuth />
|
||||
|
||||
{me.google_enabled ? (
|
||||
// YouTube access requires Google OAuth; hidden when this instance has no Google configured.
|
||||
<Section title={t("settings.account.youtubeAccess")}>
|
||||
|
||||
Reference in New Issue
Block a user