tweb/src/lib/appManagers/appDownloadManager.ts

131 lines
4.3 KiB
TypeScript
Raw Normal View History

import { $rootScope } from "../utils";
import apiManager from "../mtproto/mtprotoworker";
import { deferredPromise, CancellablePromise } from "../polyfill";
import type { DownloadOptions } from "../mtproto/apiFileManager";
import { getFileNameByLocation } from "../bin_utils";
export type ResponseMethodBlob = 'blob';
export type ResponseMethodJson = 'json';
export type ResponseMethod = ResponseMethodBlob | ResponseMethodJson;
/* export type DownloadBlob = {promise: Promise<Blob>, controller: AbortController};
export type DownloadJson = {promise: Promise<any>, controller: AbortController}; */
export type DownloadBlob = CancellablePromise<Blob>;
export type DownloadJson = CancellablePromise<any>;
//export type Download = DownloadBlob/* | DownloadJson */;
export type Download = DownloadBlob/* | DownloadJson */;
export type Progress = {done: number, fileName: string, total: number, offset: number};
export type ProgressCallback = (details: Progress) => void;
export class AppDownloadManager {
private downloads: {[fileName: string]: Download} = {};
private progress: {[fileName: string]: Progress} = {};
private progressCallbacks: {[fileName: string]: Array<ProgressCallback>} = {};
constructor() {
$rootScope.$on('download_progress', (e) => {
const details = e.detail as {done: number, fileName: string, total: number, offset: number};
this.progress[details.fileName] = details;
const callbacks = this.progressCallbacks[details.fileName];
if(callbacks) {
callbacks.forEach(callback => callback(details));
}
const download = this.downloads[details.fileName];
if(download) {
download.notifyAll(details);
}
});
}
public download(options: DownloadOptions, responseMethod?: ResponseMethodBlob): DownloadBlob;
public download(options: DownloadOptions, responseMethod?: ResponseMethodJson): DownloadJson;
public download(options: DownloadOptions, responseMethod: ResponseMethod = 'blob'): DownloadBlob {
const fileName = getFileNameByLocation(options.location, {fileName: options.fileName});
if(this.downloads.hasOwnProperty(fileName)) return this.downloads[fileName];
const deferred = deferredPromise<Blob>();
apiManager.downloadFile(options)
.then(deferred.resolve, deferred.reject)
.finally(() => {
delete this.progressCallbacks[fileName];
});
//console.log('Will download file:', fileName, url);
deferred.cancel = () => {
deferred.cancel = () => {};
};
//return this.downloads[fileName] = {promise, controller};
return this.downloads[fileName] = deferred;
}
public getDownload(fileName: string) {
return this.downloads[fileName];
}
public addProgressCallback(fileName: string, callback: ProgressCallback) {
const progress = this.progress[fileName];
(this.progressCallbacks[fileName] ?? (this.progressCallbacks[fileName] = [])).push(callback);
if(progress) {
callback(progress);
}
}
private createDownloadAnchor(url: string, fileName: string, onRemove?: () => void) {
const a = document.createElement('a');
a.href = url;
a.download = fileName;
a.target = '_blank';
a.style.position = 'absolute';
a.style.top = '1px';
a.style.left = '1px';
document.body.append(a);
try {
var clickEvent = document.createEvent('MouseEvents');
clickEvent.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
a.dispatchEvent(clickEvent);
} catch (e) {
console.error('Download click error', e);
try {
a.click();
} catch (e) {
window.open(url as string, '_blank');
}
}
setTimeout(() => {
a.remove();
onRemove && onRemove();
}, 100);
}
/* public downloadToDisc(fileName: string, url: string) {
this.createDownloadAnchor(url);
return this.download(fileName, url);
} */
public downloadToDisc(options: DownloadOptions, discFileName: string) {
const download = this.download(options);
download/* .promise */.then(blob => {
const objectURL = URL.createObjectURL(blob);
this.createDownloadAnchor(objectURL, discFileName, () => {
URL.revokeObjectURL(objectURL);
});
});
return download;
}
}
export default new AppDownloadManager();