kopia lustrzana https://gitlab.com/soapbox-pub/soapbox
MastodonClient: fallback to XHR for onUploadProgress
rodzic
2ffff220e4
commit
878c3ac54d
|
@ -93,13 +93,9 @@ export class MastodonClient {
|
||||||
body,
|
body,
|
||||||
});
|
});
|
||||||
|
|
||||||
const fetchPromise = this.fetch(request);
|
const response = opts.onUploadProgress
|
||||||
|
? await this.xhr(request, opts)
|
||||||
if (opts.onUploadProgress) {
|
: MastodonResponse.fromResponse(await this.fetch(request));
|
||||||
MastodonClient.fakeProgress(fetchPromise, opts.onUploadProgress);
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = MastodonResponse.fromResponse(await fetchPromise);
|
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new HTTPError(response, request);
|
throw new HTTPError(response, request);
|
||||||
|
@ -109,34 +105,65 @@ export class MastodonClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* `fetch` does not natively support upload progress. Implement a fake progress callback instead.
|
* Perform an XHR request from the native `Request` object and get back a `MastodonResponse`.
|
||||||
* TODO: Replace this with: https://stackoverflow.com/a/69400632
|
* This is needed because unfortunately `fetch` does not support upload progress.
|
||||||
*/
|
*/
|
||||||
private static async fakeProgress(promise: Promise<unknown>, cb: (e: ProgressEvent) => void) {
|
private async xhr(request: Request, opts: Opts = {}): Promise<MastodonResponse> {
|
||||||
const controller = new AbortController();
|
const xhr = new XMLHttpRequest();
|
||||||
|
const { resolve, reject, promise } = Promise.withResolvers<MastodonResponse>();
|
||||||
|
|
||||||
let loaded = 0;
|
xhr.responseType = 'arraybuffer';
|
||||||
const total = 100;
|
|
||||||
|
|
||||||
cb(new ProgressEvent('loadstart', { lengthComputable: true, loaded, total }));
|
xhr.onreadystatechange = () => {
|
||||||
|
if (xhr.readyState !== XMLHttpRequest.DONE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
promise.then(() => {
|
const headers = new Headers(
|
||||||
loaded = 100;
|
xhr.getAllResponseHeaders()
|
||||||
controller.abort();
|
.trim()
|
||||||
cb(new ProgressEvent('loadend', { lengthComputable: true, loaded, total }));
|
.split(/[\r\n]+/)
|
||||||
cb(new ProgressEvent('load', { lengthComputable: true, loaded, total }));
|
.map((line): [string, string] => {
|
||||||
}).catch(() => {
|
const [name, ...rest] = line.split(': ');
|
||||||
loaded = 0;
|
const value = rest.join(': ');
|
||||||
controller.abort();
|
return [name, value];
|
||||||
cb(new ProgressEvent('loadend', { lengthComputable: true, loaded, total }));
|
}),
|
||||||
cb(new ProgressEvent('error', { lengthComputable: true, loaded, total }));
|
);
|
||||||
});
|
|
||||||
|
|
||||||
while (!controller.signal.aborted && loaded < 90) {
|
const response = new MastodonResponse(xhr.response, {
|
||||||
await new Promise(resolve => setTimeout(resolve, 10));
|
status: xhr.status,
|
||||||
loaded += 10;
|
statusText: xhr.statusText,
|
||||||
cb(new ProgressEvent('progress', { lengthComputable: true, loaded, total }));
|
headers,
|
||||||
|
});
|
||||||
|
|
||||||
|
resolve(response);
|
||||||
|
};
|
||||||
|
|
||||||
|
xhr.onerror = () => {
|
||||||
|
reject(new TypeError('Network request failed'));
|
||||||
|
};
|
||||||
|
|
||||||
|
xhr.onabort = () => {
|
||||||
|
reject(new DOMException('The request was aborted', 'AbortError'));
|
||||||
|
};
|
||||||
|
|
||||||
|
if (opts.onUploadProgress) {
|
||||||
|
xhr.upload.onprogress = opts.onUploadProgress;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (opts.signal) {
|
||||||
|
opts.signal.addEventListener('abort', () => xhr.abort(), { once: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
xhr.open(request.method, request.url, true);
|
||||||
|
|
||||||
|
for (const [name, value] of request.headers) {
|
||||||
|
xhr.setRequestHeader(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
xhr.send(await request.arrayBuffer());
|
||||||
|
|
||||||
|
return promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
Ładowanie…
Reference in New Issue