Display progress bar when uploading file

This commit is contained in:
Zaid Abdulla committed 2021-07-27 14:18:35 -07:00
1 parent 1a53a320da
commit 6ce91df127
3 files changed
+101 -80

No files matched your search

+3 -1
View File
@@ -59,8 +59,10 @@ const getAuthorizer = memoize(
} }
); );
koa.on("error", async (err) => { koa.on("error", async (err, ctx) => {
setTimeout(() => { setTimeout(() => {
// Ignored errors resulting from aborted requests
if (ctx && ctx.req.aborted) return;
throw err; throw err;
}); });
}); });
+10
View File
@@ -787,3 +787,13 @@ span.parameter-value {
background-size: 100% 100%; background-size: 100% 100%;
} }
} }
.progress {
background-color: #eee;
overflow: hidden;
height: 3px;
& > .progress-bar {
height: 100%;
background-color: #b12d5c;
}
}
+88 -79
View File
@@ -73,58 +73,32 @@ const unpackSmartQuery = memoize((query) => {
}); });
}); });
interface ValidationErrors { function upload(
[prop: string]: string; file: File,
} headers: Record<string, string>,
abortSignal?: AbortSignal,
function putActionHandler(action, _object): Promise<ValidationErrors> { progressListener?: (e: ProgressEvent) => void
return new Promise((resolve, reject) => { ): Promise<void> {
const object = Object.assign({}, _object); headers = Object.assign(
if (action === "save") { {
const file = object["file"] ? object["file"][0] : null; "Content-Type": "application/octet-stream",
delete object["file"]; },
headers
if (!file) return void resolve({ file: "File not selected" }); );
return store.xhrRequest({
const id = file.name; method: "PUT",
headers: headers,
store url: `api/files/${encodeURIComponent(file.name)}`,
.resourceExists("files", id) serialize: (body) => body, // Identity function to prevent JSON.parse on blob data
.then((exists) => { body: file,
if (exists) { config: (xhr) => {
store.setTimestamp(Date.now()); if (progressListener)
return void resolve({ file: "File already exists" }); xhr.upload.addEventListener("progress", progressListener);
} if (abortSignal) {
const headers = Object.assign( if (abortSignal.aborted) xhr.abort();
{ abortSignal.addEventListener("abort", () => xhr.abort());
"Content-Type": "application/octet-stream", }
Accept: "application/octet-stream", },
},
object
);
store
.xhrRequest({
method: "PUT",
headers: headers,
url: `api/files/${encodeURIComponent(id)}`,
serialize: (body) => body, // Identity function to prevent JSON.parse on blob data
body: file,
})
.then(() => {
notifications.push(
"success",
`File ${exists ? "updated" : "created"}`
);
store.setTimestamp(Date.now());
resolve(null);
})
.catch(reject);
})
.catch(reject);
} else {
reject(new Error("Undefined action"));
}
}); });
} }
@@ -224,42 +198,77 @@ export const component: ClosureComponent = (): Component => {
title: "Create new file", title: "Create new file",
onclick: () => { onclick: () => {
let cb: () => Children = null; let cb: () => Children = null;
const abortController = new AbortController();
let progress = -1;
const comp = m( const comp = m(
putFormComponent, putFormComponent,
Object.assign( Object.assign(
{ {
actionHandler: (action, object) => { actionHandler: async (action, _object) => {
return new Promise<void>((resolve) => { if (action !== "save")
putActionHandler(action, object) throw new Error("Undefined action");
.then((errors) => { const object = Object.assign({}, _object);
const errorList = errors const file = object["file"]
? Object.values(errors) ? object["file"][0]
: []; : null;
if (errorList.length) { delete object["file"];
for (const err of errorList) if (!file) {
notifications.push("error", err); notifications.push("error", "File not selected");
} else { return;
overlay.close(cb); }
}
resolve(); if (await store.resourceExists("files", file.name)) {
}) store.setTimestamp(Date.now());
.catch((err) => { notifications.push("error", "File already exists");
notifications.push("error", err.message); return;
resolve(); }
});
}); const progressListener = (e: ProgressEvent): void => {
progress = e.loaded / e.total;
m.redraw();
};
progress = 0;
try {
await upload(
file,
object,
abortController.signal,
progressListener
);
store.setTimestamp(Date.now());
notifications.push("success", "File created");
overlay.close(cb);
} catch (err) {
notifications.push("error", err.message);
}
progress = -1;
}, },
}, },
formData formData
) )
); );
cb = () => comp; cb = () => {
overlay.open( if (progress < 0) return [null, comp];
cb, return [
() => m(
!comp.state["current"]["modified"] || "div.progress",
confirm("You have unsaved changes. Close anyway?") m("div.progress-bar", {
); style: `width: ${Math.trunc(progress * 100)}%`,
})
),
comp,
];
};
overlay.open(cb, () => {
if (
comp.state["current"]["modified"] &&
!confirm("You have unsaved changes. Close anyway?")
)
return false;
abortController.abort();
return true;
});
}, },
}, },
"New" "New"