diff --git a/server/ui/src/pages/Galerie.tsx b/server/ui/src/pages/Galerie.tsx index d4aec64..ee224d2 100644 --- a/server/ui/src/pages/Galerie.tsx +++ b/server/ui/src/pages/Galerie.tsx @@ -60,7 +60,16 @@ export function Galerie() { const [daten, setDaten] = useState(null) const [laedt, setLaedt] = useState(true) const [gewaehlt, setGewaehlt] = useState>(new Set()) - const [loeschen, setLoeschen] = useState(null) + /** Was geloescht werden soll. + * + * `ids` sind ALLE Gewaehlten, `namen` nur die, deren Pfad wir kennen -- + * also die der offenen Seite. Vorher war das eine einzige Liste, und + * genau daran ist es gescheitert: der Knopf zaehlte die Auswahl, geloescht + * wurde die Schnittmenge mit der SICHTBAREN Seite. Bei 3565 gewaehlten + * Bildern verschwanden 60. */ + const [loeschen, setLoeschen] = useState<{ ids: number[]; namen: MediaItem[] } | null>(null) + /** Wie weit das Loeschen ist -- bei tausenden Dateien dauert es. */ + const [loeschFortschritt, setLoeschFortschritt] = useState(0) const [loescht, setLoescht] = useState(false) // Welche Datei gerade gross zu sehen ist -- als ID und nicht als Objekt: // nach einem Neuladen der Liste zeigt sie sonst auf einen alten Datensatz. @@ -197,23 +206,53 @@ export function Galerie() { async function loeschenAusfuehren() { if (!loeschen) return setLoescht(true) + setLoeschFortschritt(0) - const ergebnis = - loeschen.length === 1 - ? await run( - () => api.deleteMedia(loeschen[0]!.id), - (r) => `${r.deleted} gelöscht.`, - ) - : await run( - () => api.deleteMediaBulk(loeschen.map((i) => i.id)), - (r) => - r.count === loeschen.length - ? `${r.count} Dateien gelöscht.` - : // Der Dienst überspringt, was er nicht findet. - // Diese Lücke zu verschweigen wäre genau die - // Sorte Rückmeldung, die hier nicht vorkommt. - `${r.count} von ${loeschen.length} gelöscht — der Rest war schon weg.`, - ) + // IN HAEPPCHEN, nicht in einem Zug. + // + // Der Dienst verschiebt jede Datei einzeln in den Papierkorb. Bei 3565 + // Bildern ist das eine Anfrage, die Minuten laeuft -- und die faellt + // beim Proxy in die Zeitgrenze, lange bevor sie fertig ist. Dann waere + // die Haelfte geloescht und die Oberflaeche meldete einen Fehler. + // + // 200 je Runde: gross genug, dass es nicht an der Anzahl der Anfragen + // haengt, klein genug fuer eine Antwort in Sekunden. Und nach jeder + // Runde steht im Knopf, wie weit es ist -- bei etwas, das eine Minute + // dauert, ist Stillstand nicht von Haengen zu unterscheiden. + const { note } = useStore.getState() + const PORTION = 200 + const alle = loeschen.ids + let geloescht = 0 + let abbruch: string | null = null + + for (let i = 0; i < alle.length; i += PORTION) { + const teil = alle.slice(i, i + PORTION) + try { + const r = teil.length === 1 + ? await api.deleteMedia(teil[0]!).then(() => ({ count: 1 })) + : await api.deleteMediaBulk(teil) + geloescht += r.count + } catch (e) { + // NICHT WEITERMACHEN. Wenn eine Runde scheitert, scheitern die + // naechsten vermutlich auch, und tausend Fehlermeldungen sagen + // weniger als eine mit der Zahl, die schon durch ist. + abbruch = e instanceof Error ? e.message : 'Unbekannt' + break + } + setLoeschFortschritt(geloescht) + } + + if (abbruch) { + note('fehler', `${geloescht} von ${alle.length} gelöscht, dann brach es ab: ${abbruch}`) + } else if (geloescht === alle.length) { + note('ok', `${geloescht} ${geloescht === 1 ? 'Datei' : 'Dateien'} in den Papierkorb verschoben.`) + } else { + // Der Dienst ueberspringt, was er nicht findet. Diese Luecke zu + // verschweigen waere genau die Sorte Rueckmeldung, die hier nicht + // vorkommt. + note('hinweis', `${geloescht} von ${alle.length} gelöscht — der Rest war schon weg.`) + } + const ergebnis = !abbruch setLoescht(false) setLoeschen(null) @@ -441,11 +480,23 @@ export function Galerie() { {gewaehlt.size > 0 && ( setLoeschen(items.filter((i) => gewaehlt.has(i.id)))} + onClick={() => + setLoeschen({ + // ALLE Gewaehlten, nicht nur die sichtbaren. + ids: [...gewaehlt], + namen: items.filter((i) => gewaehlt.has(i.id)), + }) + } kind={ <> - {gewaehlt.size} löschen + {/* WAEHREND ES LAEUFT die Zahl, die schon + durch ist. Bei tausenden Dateien dauert es + eine Minute, und Stillstand ist von Haengen + nicht zu unterscheiden. */} + {loescht && loeschFortschritt > 0 + ? `${loeschFortschritt} von ${gewaehlt.size} …` + : `${gewaehlt.size} löschen`} } /> @@ -611,7 +662,7 @@ export function Galerie() {