Display progress bar when uploading file
This commit is contained in:
1 parent
1a53a320da
commit
6ce91df127
3 files changed
+90
-69
No files matched your search
@@ -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;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
+77
-68
@@ -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;
|
|
||||||
delete object["file"];
|
|
||||||
|
|
||||||
if (!file) return void resolve({ file: "File not selected" });
|
|
||||||
|
|
||||||
const id = file.name;
|
|
||||||
|
|
||||||
store
|
|
||||||
.resourceExists("files", id)
|
|
||||||
.then((exists) => {
|
|
||||||
if (exists) {
|
|
||||||
store.setTimestamp(Date.now());
|
|
||||||
return void resolve({ file: "File already exists" });
|
|
||||||
}
|
|
||||||
const headers = Object.assign(
|
|
||||||
{
|
{
|
||||||
"Content-Type": "application/octet-stream",
|
"Content-Type": "application/octet-stream",
|
||||||
Accept: "application/octet-stream",
|
|
||||||
},
|
},
|
||||||
object
|
headers
|
||||||
);
|
);
|
||||||
|
return store.xhrRequest({
|
||||||
store
|
|
||||||
.xhrRequest({
|
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
headers: headers,
|
headers: headers,
|
||||||
url: `api/files/${encodeURIComponent(id)}`,
|
url: `api/files/${encodeURIComponent(file.name)}`,
|
||||||
serialize: (body) => body, // Identity function to prevent JSON.parse on blob data
|
serialize: (body) => body, // Identity function to prevent JSON.parse on blob data
|
||||||
body: file,
|
body: file,
|
||||||
})
|
config: (xhr) => {
|
||||||
.then(() => {
|
if (progressListener)
|
||||||
notifications.push(
|
xhr.upload.addEventListener("progress", progressListener);
|
||||||
"success",
|
if (abortSignal) {
|
||||||
`File ${exists ? "updated" : "created"}`
|
if (abortSignal.aborted) xhr.abort();
|
||||||
);
|
abortSignal.addEventListener("abort", () => xhr.abort());
|
||||||
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)) {
|
||||||
.catch((err) => {
|
store.setTimestamp(Date.now());
|
||||||
|
notifications.push("error", "File already exists");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
notifications.push("error", err.message);
|
||||||
resolve();
|
}
|
||||||
});
|
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"
|
||||||
|
|||||||
Reference in new issue
Block a user