fix(files): an upload is announced once, as an upload, and a finish shows in Files at once
An upload that came back done was announced twice: by the upload dialog ("File uploaded") and
again by the tray, which saw a job turn done ("Downloaded: …"). The dialog now marks its job as
announced, so the queue keeps it for the tray but does not say it again. An upload the worker
converted first is still the tray's to announce, now as "Uploaded: …".
A finished job's notice offered "Open" while Files still listed the folder without it (its own
poll runs every 15 s): the tray now refreshes the library the moment it sees a finish.
This commit is contained in:
@@ -7,6 +7,7 @@ import type { QueueJob } from "./api";
|
||||
import {
|
||||
clearFinished,
|
||||
forgetFinished,
|
||||
noteAnnounced,
|
||||
noteQueue,
|
||||
resetQueueWatch,
|
||||
takeUnannounced,
|
||||
@@ -41,6 +42,14 @@ describe("downloadQueue · a finish", () => {
|
||||
expect(takeUnannounced().map((j) => j.id)).toEqual([5]);
|
||||
});
|
||||
|
||||
it("a finish its own action announced is kept for the tray but not announced again", () => {
|
||||
noteQueue([]);
|
||||
noteAnnounced(7); // an upload, done in the answer that made it
|
||||
noteQueue([job(7, "done"), job(8, "done")]);
|
||||
expect(takeUnannounced().map((j) => j.id)).toEqual([8]);
|
||||
expect(finishedIds()).toEqual([7, 8]);
|
||||
});
|
||||
|
||||
it("a failure is not a finish; closing the tray forgets what finished", () => {
|
||||
noteQueue([job(1, "running"), job(2, "running")]);
|
||||
noteQueue([job(1, "error"), job(2, "done")]);
|
||||
|
||||
@@ -33,6 +33,8 @@ let lastSeen: Map<number, string> | null = null;
|
||||
const finished = createStore<QueueJob[]>([]);
|
||||
const unannounced = createStore<QueueJob[]>([]);
|
||||
const revealing = createStore<Reveal | null>(null);
|
||||
// Finishes this tab already said out loud where they happened: an upload that answered done.
|
||||
const announcedHere = new Set<number>();
|
||||
|
||||
/** A job to show in the tray, asked for at `at` (ms). */
|
||||
export interface Reveal {
|
||||
@@ -50,9 +52,17 @@ export function noteQueue(jobs: QueueJob[]): void {
|
||||
if (done.length === 0) return;
|
||||
const ids = new Set(done.map((j) => j.id));
|
||||
finished.set((list) => [...done, ...list.filter((j) => !ids.has(j.id))]);
|
||||
unannounced.set((list) => [...list, ...done]);
|
||||
// `delete` answers whether it was there: each id is spent on the one look that finds it done.
|
||||
const news = done.filter((j) => !announcedHere.delete(j.id));
|
||||
if (news.length) unannounced.set((list) => [...list, ...news]);
|
||||
}
|
||||
|
||||
/** A finish its own action announced already (an upload done in the answer that made it): the tray
|
||||
* still keeps it, but the done notice would say it a second time — and as a download. */
|
||||
export const noteAnnounced = (id: number): void => {
|
||||
announcedHere.add(id);
|
||||
};
|
||||
|
||||
/** The queue, noted (see above). Every reader of `qk.downloads()` fetches with this. */
|
||||
export async function fetchQueue(): Promise<QueueJob[]> {
|
||||
const jobs = await api.downloads();
|
||||
@@ -96,5 +106,6 @@ export function resetQueueWatch(): void {
|
||||
lastSeen = null;
|
||||
finished.set([]);
|
||||
unannounced.set([]);
|
||||
announcedHere.clear();
|
||||
revealing.set(null);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user