aboutsummaryrefslogtreecommitdiff
path: root/static/api/images.js
diff options
context:
space:
mode:
authorNathan Reiner <nathan@nathanreiner.xyz>2025-11-15 15:12:19 +0100
committerNathan Reiner <nathan@nathanreiner.xyz>2025-11-15 15:12:19 +0100
commit0016aaa197697ec5ff38dfb3f63ac8b6f74b48e0 (patch)
tree688930dcc978bcadfc01dac4ff09908452d802a4 /static/api/images.js
parent69488029a8eafc55d42ae7e65ba8ddeebcfb8454 (diff)
backend: add image upload
Diffstat (limited to 'static/api/images.js')
-rw-r--r--static/api/images.js43
1 files changed, 43 insertions, 0 deletions
diff --git a/static/api/images.js b/static/api/images.js
index 915aae6..4a41a3c 100644
--- a/static/api/images.js
+++ b/static/api/images.js
@@ -1,6 +1,43 @@
import * as sfw from 'sfw';
const { Input } = sfw.element.native;
+class FileUploader {
+ constructor(url) {
+ this.onprogress = () => {}
+ this.ondone = () => {}
+ this.url = url;
+ this.sessions = [];
+ }
+
+ send(...files) {
+ let count = 0;
+ this.sessions = this.sessions.concat(files.map(
+ file => new Promise((resolve) => {
+ const xhr = new XMLHttpRequest();
+ xhr.upload.addEventListener("progress", (event) => {
+ if (event.lengthComputable) {
+ this.onprogress(file, event.loaded, event.total)
+ }
+ });
+
+ xhr.addEventListener("loadend", () => {
+ count += 1;
+
+ resolve(xhr.readyState === 4 && xhr.status === 200);
+
+ if (count == files.length) {
+ this.ondone();
+ }
+ });
+
+ xhr.open("POST", this.url, true);
+ xhr.setRequestHeader("Content-Type", "application/octet-stream");
+ xhr.send(file.slice());
+ })
+ ));
+ }
+}
+
export async function upload_to_timeline() {
const input = Input.new({
type: 'file',
@@ -8,6 +45,12 @@ export async function upload_to_timeline() {
accept: 'image/jpeg',
})
input.click();
+
+ const uploader = new FileUploader('/api/image/upload');
+
+ input.onchange = async () => {
+ uploader.send(...input.files);
+ }
}
export async function upload_to_profile() {