Telegram Web K with changes to work inside I2P
https://web.telegram.i2p/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
228 lines
8.3 KiB
228 lines
8.3 KiB
/* |
|
* https://github.com/morethanwords/tweb |
|
* Copyright (C) 2019-2021 Eduard Kuzmenko |
|
* https://github.com/morethanwords/tweb/blob/master/LICENSE |
|
*/ |
|
|
|
import type { ApiFileManager, DownloadMediaOptions, DownloadOptions } from "../mtproto/apiFileManager"; |
|
import deferredPromise, { CancellablePromise } from "../../helpers/cancellablePromise"; |
|
import { Document, InputFile, Photo, PhotoSize } from "../../layer"; |
|
import { getFileNameByLocation } from "../../helpers/fileName"; |
|
import getFileNameForUpload from "../../helpers/getFileNameForUpload"; |
|
import { AppManagers } from "./managers"; |
|
import rootScope from "../rootScope"; |
|
import { MOUNT_CLASS_TO } from "../../config/debug"; |
|
import getDocumentDownloadOptions from "./utils/docs/getDocumentDownloadOptions"; |
|
import getPhotoDownloadOptions from "./utils/photos/getPhotoDownloadOptions"; |
|
import createDownloadAnchor from "../../helpers/dom/createDownloadAnchor"; |
|
import noop from "../../helpers/noop"; |
|
import getDownloadMediaDetails from "./utils/download/getDownloadMediaDetails"; |
|
import getDownloadFileNameFromOptions from "./utils/download/getDownloadFileNameFromOptions"; |
|
import { AppMessagesManager } from "./appMessagesManager"; |
|
|
|
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 DownloadUrl = CancellablePromise<string>; |
|
export type DownloadJson = CancellablePromise<any>; |
|
//export type Download = DownloadBlob/* | DownloadJson */; |
|
export type Download = DownloadBlob | DownloadUrl/* | DownloadJson */; |
|
|
|
export type Progress = {done: number, fileName: string, total: number, offset: number}; |
|
export type ProgressCallback = (details: Progress) => void; |
|
|
|
type DownloadType = 'url' | 'blob' | 'void'; |
|
|
|
export class AppDownloadManager { |
|
private downloads: {[fileName: string]: {main: Download, url?: Download, blob?: Download, void?: Download}} = {}; |
|
private progress: {[fileName: string]: Progress} = {}; |
|
// private progressCallbacks: {[fileName: string]: Array<ProgressCallback>} = {}; |
|
private managers: AppManagers; |
|
|
|
public construct(managers: AppManagers) { |
|
this.managers = managers; |
|
rootScope.addEventListener('download_progress', (details) => { |
|
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.main.notifyAll(details); |
|
} |
|
}); |
|
} |
|
|
|
private getNewDeferred<T>(fileName: string, type?: DownloadType) { |
|
const deferred = deferredPromise<T>(); |
|
|
|
let download = this.downloads[fileName]; |
|
if(!download) { |
|
download = this.downloads[fileName] = { |
|
main: deferred as any |
|
}; |
|
|
|
deferred.cancel = () => { |
|
//try { |
|
const error = new Error('Download canceled'); |
|
error.name = 'AbortError'; |
|
|
|
this.managers.apiFileManager.cancelDownload(fileName); |
|
|
|
deferred.reject(error); |
|
deferred.cancel = () => {}; |
|
/* } catch(err) { |
|
|
|
} */ |
|
}; |
|
|
|
deferred.catch(() => { |
|
this.clearDownload(fileName); |
|
}).finally(() => { |
|
delete this.progress[fileName]; |
|
// delete this.progressCallbacks[fileName]; |
|
}); |
|
} else { |
|
const main = download.main; |
|
(['cancel', 'addNotifyListener', 'notify', 'notifyAll'] as (keyof CancellablePromise<void>)[]).forEach((key) => { |
|
if(!main[key]) { |
|
return; |
|
} |
|
|
|
// @ts-ignore |
|
deferred[key] = main[key].bind(main); |
|
}); |
|
} |
|
|
|
return download[type] = deferred as any; |
|
} |
|
|
|
public getNewDeferredForUpload<T extends Promise<any>>(fileName: string, promise: T) { |
|
const deferred = this.getNewDeferred<InputFile>(fileName); |
|
promise.then(deferred.resolve, deferred.reject); |
|
|
|
deferred.finally(() => { |
|
this.clearDownload(fileName); |
|
}); |
|
|
|
return deferred as CancellablePromise<Awaited<T>>; |
|
} |
|
|
|
private clearDownload(fileName: string) { |
|
delete this.downloads[fileName]; |
|
} |
|
|
|
public getUpload(fileName: string): ReturnType<AppMessagesManager['sendFile']>['promise'] { |
|
let deferred: CancellablePromise<any> = this.getDownload(fileName); |
|
if(deferred) { |
|
return deferred; |
|
} |
|
|
|
deferred = this.getNewDeferred(fileName); |
|
this.managers.appMessagesManager.getUploadPromise(fileName).then(deferred.resolve, deferred.reject); |
|
return deferred; |
|
} |
|
|
|
/* public fakeDownload(fileName: string, value: Blob | string) { |
|
const deferred = this.getNewDeferred<Blob>(fileName); |
|
if(typeof(value) === 'string') { |
|
fetch(value) |
|
.then((response) => response.blob()) |
|
.then((blob) => deferred.resolve(blob)); |
|
} else { |
|
deferred.resolve(value); |
|
} |
|
|
|
return deferred; |
|
} */ |
|
|
|
private d(fileName: string, getPromise: () => Promise<any>, type?: DownloadType) { |
|
let deferred = this.getDownload(fileName, type); |
|
if(deferred) return deferred; |
|
|
|
deferred = this.getNewDeferred<Blob>(fileName, type); |
|
getPromise().then(deferred.resolve, deferred.reject); |
|
return deferred; |
|
} |
|
|
|
public download(options: DownloadOptions): DownloadBlob { |
|
const fileName = getDownloadFileNameFromOptions(options); |
|
return this.d(fileName, () => this.managers.apiFileManager.download(options), 'blob') as any; |
|
} |
|
|
|
public downloadMedia(options: DownloadMediaOptions, type: DownloadType = 'blob'): DownloadBlob { |
|
const {downloadOptions, fileName} = getDownloadMediaDetails(options); |
|
return this.d(fileName, () => { |
|
const cb = type === 'url' ? this.managers.apiFileManager.downloadMediaURL : (type === 'void' ? this.managers.apiFileManager.downloadMediaVoid : this.managers.apiFileManager.downloadMedia); |
|
return cb(options); |
|
}, type) as any; |
|
} |
|
|
|
public downloadMediaURL(options: DownloadMediaOptions): DownloadUrl { |
|
return this.downloadMedia(options, 'url') as any; |
|
} |
|
|
|
public downloadMediaVoid(options: DownloadMediaOptions): DownloadBlob { |
|
return this.downloadMedia(options, 'void'); |
|
} |
|
|
|
public upload(file: File | Blob, fileName?: string, promise?: Promise<any>) { |
|
if(!fileName) { |
|
fileName = getFileNameForUpload(file); |
|
} |
|
|
|
if(!promise) { |
|
promise = this.managers.apiFileManager.upload({file, fileName}); |
|
} |
|
|
|
const deferred = this.getNewDeferredForUpload(fileName, promise); |
|
return deferred as any as CancellablePromise<InputFile>; |
|
} |
|
|
|
public getDownload(fileName: string, type?: DownloadType) { |
|
const d = this.downloads[fileName]; |
|
return d && d[type]; |
|
} |
|
|
|
// public addProgressCallback(fileName: string, callback: ProgressCallback) { |
|
// const progress = this.progress[fileName]; |
|
// (this.progressCallbacks[fileName] ?? (this.progressCallbacks[fileName] = [])).push(callback); |
|
|
|
// if(progress) { |
|
// callback(progress); |
|
// } |
|
// } |
|
|
|
public downloadToDisc(options: DownloadMediaOptions) { |
|
const media = options.media; |
|
const isDocument = media._ === 'document'; |
|
if(!isDocument && !options.thumb) { |
|
options.thumb = (media as Photo.photo).sizes.slice().pop() as PhotoSize.photoSize; |
|
} |
|
|
|
const promise = this.downloadMedia(options); |
|
promise.then((blob) => { |
|
const url = URL.createObjectURL(blob); |
|
const downloadOptions = isDocument ? |
|
getDocumentDownloadOptions(media) : |
|
getPhotoDownloadOptions(media as any, options.thumb); |
|
const fileName = (options.media as Document.document).file_name || getFileNameByLocation(downloadOptions.location); |
|
createDownloadAnchor(url, fileName, () => { |
|
URL.revokeObjectURL(url); |
|
}); |
|
}, noop); |
|
|
|
return promise; |
|
} |
|
} |
|
|
|
const appDownloadManager = new AppDownloadManager(); |
|
MOUNT_CLASS_TO && (MOUNT_CLASS_TO.appDownloadManager = appDownloadManager); |
|
export default appDownloadManager;
|
|
|