fix(files): review round 1, the page — honest states, the keyboard, the tray's words
- A library past the list's cap is searched and sorted on the server; the count says "the newest". - Each folder and each search opens on its first page (DataTable pageScope, useCardPager scope). - A file dropped where the page takes none never reaches the browser (which would unload the app). - The viewer's arrows wait while a layer opened from it is on top, and scroll a wide text line; a text shows its first 5000 lines and says where the rest is. - Confirms name the file (no more "your downloads"); the asking menu rows end in "…". - The tray names a running step beside its bar, shows a pause mark for paused work, polls slowly while only paused work waits, and forgets a finished file that was deleted. - An unchanged rename sends nothing; a paste opens the download dialog only with nothing on top; the link box never replaces what was typed. - Honest states: folders that failed to load, a file no longer there, the storage figure; the rail says "nothing downloaded" only when there is nothing. - Focus after deleting the open folder and when the format form opens; the format name's label. - ui/Meter (one danger line for both quota bars) and ui/RenameField in the platform layer. - Copy fixes, stale comments and the speculative hub key; a contract test for the list's parameters; e2e: the Files page and the paste intake.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { useState } from "react";
|
||||
import { useEffect, useId, useRef, useState } from "react";
|
||||
import { qk } from "../lib/queryKeys";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import type { TFunction } from "i18next";
|
||||
@@ -93,6 +93,14 @@ export function DownloadFormats() {
|
||||
const [name, setName] = useState("");
|
||||
const [spec, setSpec] = useState<DownloadSpec>(BLANK);
|
||||
const [formOpen, setFormOpen] = useState(false);
|
||||
// Opening the form (New, Edit, Duplicate) puts the keyboard in its name: the form opens away from
|
||||
// the control that asked for it, and "New format" is replaced by it.
|
||||
const [opened, setOpened] = useState(0);
|
||||
const nameField = useRef<HTMLInputElement>(null);
|
||||
const nameId = useId();
|
||||
useEffect(() => {
|
||||
if (opened) nameField.current?.focus();
|
||||
}, [opened]);
|
||||
|
||||
const invalidate = () => void qc.invalidateQueries({ queryKey: qk.downloadProfiles() });
|
||||
const startNew = () => {
|
||||
@@ -100,12 +108,14 @@ export function DownloadFormats() {
|
||||
setName("");
|
||||
setSpec(BLANK);
|
||||
setFormOpen(true);
|
||||
setOpened((n) => n + 1);
|
||||
};
|
||||
const startEdit = (p: DownloadProfile) => {
|
||||
setEditing(p);
|
||||
setName(p.name);
|
||||
setSpec({ ...BLANK, ...p.spec });
|
||||
setFormOpen(true);
|
||||
setOpened((n) => n + 1);
|
||||
};
|
||||
// A new format from an existing one: its settings, and a name of its own to give.
|
||||
const startCopy = (p: DownloadProfile) => {
|
||||
@@ -113,6 +123,7 @@ export function DownloadFormats() {
|
||||
setName("");
|
||||
setSpec({ ...BLANK, ...p.spec });
|
||||
setFormOpen(true);
|
||||
setOpened((n) => n + 1);
|
||||
};
|
||||
|
||||
const save = useMutation({
|
||||
@@ -261,8 +272,12 @@ export function DownloadFormats() {
|
||||
) : (
|
||||
<div className="rounded-xl border border-border p-4 space-y-3">
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-1">{t("downloads.profiles.name")}</label>
|
||||
<label htmlFor={nameId} className="block text-sm font-medium mb-1">
|
||||
{t("downloads.profiles.name")}
|
||||
</label>
|
||||
<Input
|
||||
id={nameId}
|
||||
ref={nameField}
|
||||
className="w-full"
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
|
||||
Reference in New Issue
Block a user