Browse Source

Upload fix

Fixed upload preloader
master
morethanwords 4 years ago
parent
commit
8a5a91b3c4
  1. 2
      src/components/audio.ts
  2. 58
      src/components/preloader.ts
  3. 4
      src/components/wrappers.ts
  4. 5
      src/lib/appManagers/appDocsManager.ts
  5. 56
      src/lib/appManagers/appDownloadManager.ts
  6. 10
      src/lib/appManagers/appImManager.ts
  7. 136
      src/lib/appManagers/appMessagesManager.ts
  8. 67
      src/lib/mtproto/apiFileManager.ts
  9. 1
      src/lib/mtproto/mtproto.worker.ts
  10. 4
      src/lib/mtproto/mtprotoworker.ts
  11. 14
      src/types.d.ts
  12. 2
      webpack.common.js

2
src/components/audio.ts

@ -303,7 +303,7 @@ export default class AudioElement extends HTMLElement {
downloadDiv.innerHTML = '<div class="tgico-download"></div>'; downloadDiv.innerHTML = '<div class="tgico-download"></div>';
} }
if(doc.type != 'audio' && !uploading) { if(doc.type != 'audio' || uploading) {
this.append(downloadDiv); this.append(downloadDiv);
} }

58
src/components/preloader.ts

@ -53,37 +53,41 @@ export default class ProgressivePreloader {
} }
} }
public attach(elem: Element, reset = true, promise?: CancellablePromise<any>, append = true) { public attachPromise(promise: CancellablePromise<any>) {
if(promise/* && false */) { this.promise = promise;
this.promise = promise;
const tempID = --this.tempID; const tempID = --this.tempID;
const onEnd = () => { const onEnd = () => {
promise.notify = null; promise.notify = null;
if(tempID == this.tempID) { if(tempID == this.tempID) {
this.detach(); this.detach();
this.promise = promise = null; this.promise = promise = null;
}
};
//promise.catch(onEnd);
promise.finally(onEnd);
if(promise.addNotifyListener) {
promise.addNotifyListener((details: {done: number, total: number}) => {
/* if(details.done >= details.total) {
onEnd();
} */
if(tempID != this.tempID) return;
//console.log('preloader download', promise, details);
const percents = details.done / details.total * 100;
this.setProgress(percents);
});
} }
};
//promise.catch(onEnd);
promise.finally(onEnd);
if(promise.addNotifyListener) {
promise.addNotifyListener((details: {done: number, total: number}) => {
/* if(details.done >= details.total) {
onEnd();
} */
if(tempID != this.tempID) return;
//console.log('preloader download', promise, details);
const percents = details.done / details.total * 100;
this.setProgress(percents);
});
}
}
public attach(elem: Element, reset = true, promise?: CancellablePromise<any>, append = true) {
if(promise/* && false */) {
this.attachPromise(promise);
} }
this.detached = false; this.detached = false;

4
src/components/wrappers.ts

@ -59,7 +59,7 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
let img: HTMLImageElement; let img: HTMLImageElement;
if(message) { if(message) {
if(doc.type == 'video') { if(doc.type == 'video' && doc.thumbs?.length) {
return wrapPhoto(doc, message, container, boxWidth, boxHeight, withTail, isOut, lazyLoadQueue, middleware); return wrapPhoto(doc, message, container, boxWidth, boxHeight, withTail, isOut, lazyLoadQueue, middleware);
} }
@ -390,7 +390,7 @@ export function wrapPhoto(photo: MTPhoto | MTDocument, message: any, container:
}); });
}; };
return cacheContext.downloaded ? load() : lazyLoadQueue.push({div: container, load: load, wasSeen: true}); return cacheContext.downloaded || !lazyLoadQueue ? load() : lazyLoadQueue.push({div: container, load: load, wasSeen: true});
} }
export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, onlyThumb, emoji, width, height, withThumb, loop}: { export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, onlyThumb, emoji, width, height, withThumb, loop}: {

5
src/lib/appManagers/appDocsManager.ts

@ -124,7 +124,10 @@ class AppDocsManager {
if((doc.type == 'gif' && doc.size > 8e6) || doc.type == 'audio' || doc.type == 'video') { if((doc.type == 'gif' && doc.size > 8e6) || doc.type == 'audio' || doc.type == 'video') {
doc.supportsStreaming = true; doc.supportsStreaming = true;
doc.url = this.getFileURL(doc);
if(!doc.url) {
doc.url = this.getFileURL(doc);
}
} }
if(!doc.file_name) { if(!doc.file_name) {

56
src/lib/appManagers/appDownloadManager.ts

@ -3,6 +3,7 @@ import apiManager from "../mtproto/mtprotoworker";
import { deferredPromise, CancellablePromise } from "../polyfill"; import { deferredPromise, CancellablePromise } from "../polyfill";
import type { DownloadOptions } from "../mtproto/apiFileManager"; import type { DownloadOptions } from "../mtproto/apiFileManager";
import { getFileNameByLocation } from "../bin_utils"; import { getFileNameByLocation } from "../bin_utils";
import { InputFile } from "../../types";
export type ResponseMethodBlob = 'blob'; export type ResponseMethodBlob = 'blob';
export type ResponseMethodJson = 'json'; export type ResponseMethodJson = 'json';
@ -23,6 +24,8 @@ export class AppDownloadManager {
private progress: {[fileName: string]: Progress} = {}; private progress: {[fileName: string]: Progress} = {};
private progressCallbacks: {[fileName: string]: Array<ProgressCallback>} = {}; private progressCallbacks: {[fileName: string]: Array<ProgressCallback>} = {};
private uploadID = 0;
constructor() { constructor() {
$rootScope.$on('download_progress', (e) => { $rootScope.$on('download_progress', (e) => {
const details = e.detail as {done: number, fileName: string, total: number, offset: number}; const details = e.detail as {done: number, fileName: string, total: number, offset: number};
@ -40,39 +43,56 @@ export class AppDownloadManager {
}); });
} }
public download(options: DownloadOptions, responseMethod?: ResponseMethodBlob): DownloadBlob; private getNewDeferred(fileName: string) {
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>(); 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 = () => {
const error = new Error('Download canceled'); const error = new Error('Download canceled');
error.name = 'AbortError'; error.name = 'AbortError';
apiManager.cancelDownload(fileName); apiManager.cancelDownload(fileName);
delete this.downloads[fileName]; this.clearDownload(fileName);
delete this.progress[fileName];
delete this.progressCallbacks[fileName];
deferred.reject(error); deferred.reject(error);
deferred.cancel = () => {}; deferred.cancel = () => {};
}; };
deferred.finally(() => {
delete this.progress[fileName];
delete this.progressCallbacks[fileName];
});
return this.downloads[fileName] = deferred; return this.downloads[fileName] = deferred;
} }
private clearDownload(fileName: string) {
delete this.downloads[fileName];
}
public download(options: DownloadOptions): DownloadBlob {
const fileName = getFileNameByLocation(options.location, {fileName: options.fileName});
if(this.downloads.hasOwnProperty(fileName)) return this.downloads[fileName];
const deferred = this.getNewDeferred(fileName);
apiManager.downloadFile(options).then(deferred.resolve, deferred.reject);
//console.log('Will download file:', fileName, url);
return deferred;
}
public upload(file: File | Blob) {
const fileName = /* (file as File).name || */'upload-' + this.uploadID++;
const deferred = this.getNewDeferred(fileName);
apiManager.uploadFile({file, fileName}).then(deferred.resolve, deferred.reject);
deferred.finally(() => {
this.clearDownload(fileName);
});
return deferred as any as CancellablePromise<InputFile>;
}
public getDownload(fileName: string) { public getDownload(fileName: string) {
return this.downloads[fileName]; return this.downloads[fileName];
} }

10
src/lib/appManagers/appImManager.ts

@ -39,6 +39,7 @@ import appAudio from '../../components/appAudio';
import appPollsManager from './appPollsManager'; import appPollsManager from './appPollsManager';
import { ripple } from '../../components/ripple'; import { ripple } from '../../components/ripple';
import { horizontalMenu } from '../../components/horizontalMenu'; import { horizontalMenu } from '../../components/horizontalMenu';
import AudioElement from '../../components/audio';
//console.log('appImManager included33!'); //console.log('appImManager included33!');
@ -2251,15 +2252,14 @@ export class AppImManager {
case 'audio': case 'audio':
case 'voice': case 'voice':
case 'document': { case 'document': {
let doc = appDocsManager.getDoc(message.id); const doc = appDocsManager.getDoc(message.id);
this.log('will wrap pending doc:', doc); this.log('will wrap pending doc:', doc);
let docDiv = wrapDocument(doc, false, true, message.id); const docDiv = wrapDocument(doc, false, true, message.id);
if(doc.type == 'audio' || doc.type == 'voice') { if(doc.type == 'audio' || doc.type == 'voice') {
// @ts-ignore (docDiv as AudioElement).preloader = preloader;
docDiv.preloader = preloader;
} else { } else {
let icoDiv = docDiv.querySelector('.audio-download, .document-ico'); const icoDiv = docDiv.querySelector('.audio-download, .document-ico');
preloader.attach(icoDiv, false); preloader.attach(icoDiv, false);
} }

136
src/lib/appManagers/appMessagesManager.ts

@ -23,6 +23,7 @@ import searchIndexManager from '../searchIndexManager';
import { MTDocument, MTPhotoSize } from "../../types"; import { MTDocument, MTPhotoSize } from "../../types";
import { logger, LogLevels } from "../logger"; import { logger, LogLevels } from "../logger";
import type {ApiFileManager} from '../mtproto/apiFileManager'; import type {ApiFileManager} from '../mtproto/apiFileManager';
import appDownloadManager from "./appDownloadManager";
//console.trace('include'); //console.trace('include');
@ -1121,9 +1122,9 @@ export class AppMessagesManager {
flags |= 256; flags |= 256;
} }
let preloader = new ProgressivePreloader(null, true); const preloader = new ProgressivePreloader(null, true);
var media = { const media = {
_: 'messageMediaPending', _: 'messageMediaPending',
type: attachType, type: attachType,
file_name: fileName || apiFileName, file_name: fileName || apiFileName,
@ -1132,22 +1133,10 @@ export class AppMessagesManager {
preloader: preloader, preloader: preloader,
w: options.width, w: options.width,
h: options.height, h: options.height,
url: options.objectURL, url: options.objectURL
progress: {
percent: 1,
total: file.size,
done: 0,
cancel: () => {}
}
};
preloader.preloader.onclick = () => {
this.log('cancelling upload', media);
this.setTyping('sendMessageCancelAction');
media.progress.cancel();
}; };
var message: any = { const message: any = {
_: 'message', _: 'message',
id: messageID, id: messageID,
from_id: fromID, from_id: fromID,
@ -1168,7 +1157,7 @@ export class AppMessagesManager {
pending: true pending: true
}; };
var toggleError = (on: boolean) => { const toggleError = (on: boolean) => {
if(on) { if(on) {
message.error = true; message.error = true;
} else { } else {
@ -1178,10 +1167,10 @@ export class AppMessagesManager {
$rootScope.$broadcast('messages_pending'); $rootScope.$broadcast('messages_pending');
}; };
var uploaded = false, let uploaded = false,
uploadPromise: ReturnType<ApiFileManager['uploadFile']> = null; uploadPromise: ReturnType<ApiFileManager['uploadFile']> = null;
let invoke = (flags: number, inputMedia: any) => { const invoke = (flags: number, inputMedia: any) => {
this.setTyping('sendMessageCancelAction'); this.setTyping('sendMessageCancelAction');
return apiManager.invokeApi('messages.sendMedia', { return apiManager.invokeApi('messages.sendMedia', {
@ -1221,9 +1210,9 @@ export class AppMessagesManager {
flags |= 128; // clear_draft flags |= 128; // clear_draft
if(isDocument) { if(isDocument) {
let {id, access_hash, file_reference} = file as MTDocument; const {id, access_hash, file_reference} = file as MTDocument;
let inputMedia = { const inputMedia = {
_: 'inputMediaDocument', _: 'inputMediaDocument',
flags: 0, flags: 0,
id: { id: {
@ -1236,16 +1225,13 @@ export class AppMessagesManager {
invoke(flags, inputMedia); invoke(flags, inputMedia);
} else if(file instanceof File || file instanceof Blob) { } else if(file instanceof File || file instanceof Blob) {
let deferred = deferredPromise<void>(); const deferred = deferredPromise<void>();
this.sendFilePromise.then(() => { this.sendFilePromise.then(() => {
if(!uploaded || message.error) { if(!uploaded || message.error) {
uploaded = false; uploaded = false;
//uploadPromise = apiFileManager.uploadFile(file); uploadPromise = appDownloadManager.upload(file);
uploadPromise = fetch('/upload', { preloader.attachPromise(uploadPromise);
method: 'POST',
body: file
}).then(res => res.json());
} }
uploadPromise && uploadPromise.then((inputFile) => { uploadPromise && uploadPromise.then((inputFile) => {
@ -1277,28 +1263,23 @@ export class AppMessagesManager {
toggleError(true); toggleError(true);
}); });
uploadPromise.notify = (progress: {done: number, total: number}) => { uploadPromise.addNotifyListener((progress: {done: number, total: number}) => {
this.log('upload progress', progress); this.log('upload progress', progress);
media.progress.done = progress.done; const percents = Math.max(1, Math.floor(100 * progress.done / progress.total));
media.progress.percent = Math.max(1, Math.floor(100 * progress.done / progress.total)); this.setTyping({_: actionName, progress: percents | 0});
this.setTyping({_: actionName, progress: media.progress.percent | 0}); });
preloader.setProgress(media.progress.percent); // lol, nice
$rootScope.$broadcast('history_update', {peerID: peerID}); uploadPromise.catch(err => {
}; if(err.name === 'AbortError' && !uploaded) {
this.log('cancelling upload', media);
media.progress.cancel = () => {
if(!uploaded) {
deferred.resolve(); deferred.resolve();
uploadPromise.cancel();
this.cancelPendingMessage(randomIDS); this.cancelPendingMessage(randomIDS);
this.setTyping('sendMessageCancelAction');
} }
};
// @ts-ignore
uploadPromise['finally'](() => {
deferred.resolve();
preloader.detach();
}); });
uploadPromise.finally(deferred.resolve);
}); });
this.sendFilePromise = deferred; this.sendFilePromise = deferred;
@ -1382,12 +1363,6 @@ export class AppMessagesManager {
_: 'messageMediaPending', _: 'messageMediaPending',
type: 'album', type: 'album',
preloader: preloader, preloader: preloader,
progress: {
percent: 1,
total: file.size,
done: 0,
cancel: () => {}
},
document: undefined as any, document: undefined as any,
photo: undefined as any photo: undefined as any
}; };
@ -1442,12 +1417,6 @@ export class AppMessagesManager {
media.photo = photo; media.photo = photo;
} }
preloader.preloader.onclick = () => {
this.log('cancelling upload', media);
this.setTyping('sendMessageCancelAction');
media.progress.cancel();
};
let message = { let message = {
_: 'message', _: 'message',
id: messageID, id: messageID,
@ -1509,36 +1478,52 @@ export class AppMessagesManager {
let inputs: any[] = []; let inputs: any[] = [];
for(let i = 0, length = files.length; i < length; ++i) { for(let i = 0, length = files.length; i < length; ++i) {
let file = files[i]; const file = files[i];
let message = messages[i]; const message = messages[i];
let media = message.media; const media = message.media;
let preloader = media.preloader; const preloader = media.preloader;
let actionName = file.type.indexOf('video/') === 0 ? 'sendMessageUploadVideoAction' : 'sendMessageUploadPhotoAction'; const actionName = file.type.indexOf('video/') === 0 ? 'sendMessageUploadVideoAction' : 'sendMessageUploadPhotoAction';
let deferred = deferredPromise<void>(); const deferred = deferredPromise<void>();
let canceled = false;
let apiFileName: string;
if(file.type.indexOf('video/') === 0) {
apiFileName = 'video.mp4';
} else {
apiFileName = 'photo.' + file.type.split('/')[1];
}
await this.sendFilePromise; await this.sendFilePromise;
this.sendFilePromise = deferred; this.sendFilePromise = deferred;
if(!uploaded || message.error) { if(!uploaded || message.error) {
uploaded = false; uploaded = false;
//uploadPromise = apiFileManager.uploadFile(file); uploadPromise = appDownloadManager.upload(file);
uploadPromise = fetch('/upload', { preloader.attachPromise(uploadPromise);
method: 'POST',
body: file
}).then(res => res.json());
} }
uploadPromise.notify = (progress: {done: number, total: number}) => { uploadPromise.addNotifyListener((progress: {done: number, total: number}) => {
this.log('upload progress', progress); this.log('upload progress', progress);
media.progress.percent = Math.max(1, Math.floor(100 * progress.done / progress.total)); const percents = Math.max(1, Math.floor(100 * progress.done / progress.total));
this.setTyping({_: actionName, progress: media.progress.percent | 0}); this.setTyping({_: actionName, progress: percents | 0});
preloader.setProgress(media.progress.percent); // lol, nice });
$rootScope.$broadcast('history_update', {peerID: peerID});
}; uploadPromise.catch(err => {
if(err.name === 'AbortError' && !uploaded) {
this.log('cancelling upload item', media);
canceled = true;
}
});
await uploadPromise.then((inputFile) => { await uploadPromise.then((inputFile) => {
this.log('appMessagesManager: sendAlbum file uploaded:', inputFile); this.log('appMessagesManager: sendAlbum file uploaded:', inputFile);
if(canceled) {
return;
}
inputFile.name = apiFileName;
let inputMedia: any; let inputMedia: any;
let details = options.sendFileDetails[i]; let details = options.sendFileDetails[i];
if(details.duration) { if(details.duration) {
@ -1568,6 +1553,10 @@ export class AppMessagesManager {
peer: inputPeer, peer: inputPeer,
media: inputMedia media: inputMedia
}).then(messageMedia => { }).then(messageMedia => {
if(canceled) {
return;
}
let inputMedia: any; let inputMedia: any;
if(messageMedia.photo) { if(messageMedia.photo) {
let photo = messageMedia.photo; let photo = messageMedia.photo;
@ -1598,7 +1587,6 @@ export class AppMessagesManager {
this.log('appMessagesManager: sendAlbum uploadPromise.finally!'); this.log('appMessagesManager: sendAlbum uploadPromise.finally!');
deferred.resolve(); deferred.resolve();
preloader.detach();
} }
uploaded = true; uploaded = true;

67
src/lib/mtproto/apiFileManager.ts

@ -5,7 +5,7 @@ import FileManager from "../filemanager";
import apiManager from "./apiManager"; import apiManager from "./apiManager";
import { deferredPromise, CancellablePromise } from "../polyfill"; import { deferredPromise, CancellablePromise } from "../polyfill";
import { logger, LogLevels } from "../logger"; import { logger, LogLevels } from "../logger";
import { InputFileLocation, FileLocation, UploadFile } from "../../types"; import { InputFileLocation, FileLocation, UploadFile, InputFile } from "../../types";
import { isSafari } from "../../helpers/userAgent"; import { isSafari } from "../../helpers/userAgent";
import cryptoWorker from "../crypto/cryptoworker"; import cryptoWorker from "../crypto/cryptoworker";
import { notifySomeone, notifyAll } from "../../helpers/context"; import { notifySomeone, notifyAll } from "../../helpers/context";
@ -33,6 +33,10 @@ export class ApiFileManager {
[fileName: string]: CancellablePromise<Blob> [fileName: string]: CancellablePromise<Blob>
} = {}; } = {};
public uploadPromises: {
[fileName: string]: CancellablePromise<InputFile>
} = {};
public downloadPulls: { public downloadPulls: {
[x: string]: Array<{ [x: string]: Array<{
cb: () => Promise<UploadFile | void>, cb: () => Promise<UploadFile | void>,
@ -108,8 +112,8 @@ export class ApiFileManager {
} }
public cancelDownload(fileName: string) { public cancelDownload(fileName: string) {
const promise = this.cachedDownloadPromises[fileName]; const promise = this.cachedDownloadPromises[fileName] || this.uploadPromises[fileName];
if(promise) { if(promise && !promise.isRejected && !promise.isFulfilled) {
promise.cancel(); promise.cancel();
return true; return true;
} }
@ -401,10 +405,11 @@ export class ApiFileManager {
return this.getFileStorage().deleteFile(fileName); return this.getFileStorage().deleteFile(fileName);
} }
public uploadFile(file: Blob | File) { public uploadFile({file, fileName}: {file: Blob | File, fileName: string}) {
var fileSize = file.size, const fileSize = file.size,
isBigFile = fileSize >= 10485760, isBigFile = fileSize >= 10485760;
canceled = false,
let canceled = false,
resolved = false, resolved = false,
doneParts = 0, doneParts = 0,
partSize = 262144, // 256 Kb partSize = 262144, // 256 Kb
@ -418,27 +423,27 @@ export class ApiFileManager {
activeDelta = 1; activeDelta = 1;
} }
var totalParts = Math.ceil(fileSize / partSize); const totalParts = Math.ceil(fileSize / partSize);
const fileID: [number, number] = [nextRandomInt(0xFFFFFFFF), nextRandomInt(0xFFFFFFFF)];
var fileID = [nextRandomInt(0xFFFFFFFF), nextRandomInt(0xFFFFFFFF)]; let _part = 0;
var _part = 0, const resultInputFile: InputFile = {
resultInputFile = { _: isBigFile ? 'inputFileBig' : 'inputFile',
_: isBigFile ? 'inputFileBig' : 'inputFile', id: fileID,
id: fileID, parts: totalParts,
parts: totalParts, name: fileName,
name: file instanceof File ? file.name : '', md5_checksum: ''
md5_checksum: ''
}; };
let deferredHelper: { const deferredHelper: {
resolve?: (input: typeof resultInputFile) => void, resolve?: (input: typeof resultInputFile) => void,
reject?: (error: any) => void, reject?: (error: any) => void,
notify?: (details: {done: number, total: number}) => void notify?: (details: {done: number, total: number}) => void
} = { } = {
notify: (details: {done: number, total: number}) => {} notify: (details: {done: number, total: number}) => {}
}; };
let deferred: CancellablePromise<typeof resultInputFile> = new Promise((resolve, reject) => { const deferred: CancellablePromise<typeof resultInputFile> = new Promise((resolve, reject) => {
if(totalParts > 3000) { if(totalParts > 3000) {
return reject({type: 'FILE_TOO_BIG'}); return reject({type: 'FILE_TOO_BIG'});
} }
@ -459,13 +464,13 @@ export class ApiFileManager {
errorHandler = () => {}; errorHandler = () => {};
}; };
let method = isBigFile ? 'upload.saveBigFilePart' : 'upload.saveFilePart'; const method = isBigFile ? 'upload.saveBigFilePart' : 'upload.saveFilePart';
for(let offset = 0; offset < fileSize; offset += partSize) { for(let offset = 0; offset < fileSize; offset += partSize) {
let part = _part++; // 0, 1 const part = _part++; // 0, 1
this.downloadRequest('upload', () => { this.downloadRequest('upload', () => {
return new Promise<void>((uploadResolve, uploadReject) => { return new Promise<void>((uploadResolve, uploadReject) => {
var reader = new FileReader(); const reader = new FileReader();
var blob = file.slice(offset, offset + partSize); const blob = file.slice(offset, offset + partSize);
reader.onloadend = (e) => { reader.onloadend = (e) => {
if(canceled) { if(canceled) {
@ -475,6 +480,7 @@ export class ApiFileManager {
if(e.target.readyState != FileReader.DONE) { if(e.target.readyState != FileReader.DONE) {
this.log.error('wrong readyState!'); this.log.error('wrong readyState!');
uploadReject();
return; return;
} }
@ -494,18 +500,19 @@ export class ApiFileManager {
uploadResolve(); uploadResolve();
//////this.log('Progress', doneParts * partSize / fileSize); //////this.log('Progress', doneParts * partSize / fileSize);
deferred.notify({done: doneParts * partSize, total: fileSize});
if(doneParts >= totalParts) { if(doneParts >= totalParts) {
deferred.resolve(resultInputFile); deferred.resolve(resultInputFile);
resolved = true; resolved = true;
} else {
deferred.notify({done: doneParts * partSize, total: fileSize});
} }
}, errorHandler); }, errorHandler);
}; };
reader.readAsArrayBuffer(blob); reader.readAsArrayBuffer(blob);
}); });
}, activeDelta); }, activeDelta).catch(errorHandler);
} }
deferred.cancel = () => { deferred.cancel = () => {
@ -516,7 +523,15 @@ export class ApiFileManager {
} }
}; };
return deferred; deferred.notify = (progress: {done: number, total: number}) => {
notifyAll({progress: {fileName, ...progress}});
};
deferred.finally(() => {
delete this.uploadPromises[fileName];
});
return this.uploadPromises[fileName] = deferred;
} }
} }

1
src/lib/mtproto/mtproto.worker.ts

@ -106,6 +106,7 @@ ctx.addEventListener('message', async(e) => {
}); });
case 'cancelDownload': case 'cancelDownload':
case 'uploadFile':
case 'downloadFile': { case 'downloadFile': {
try { try {
// @ts-ignore // @ts-ignore

4
src/lib/mtproto/mtprotoworker.ts

@ -219,6 +219,10 @@ class ApiManagerProxy extends CryptoWorkerMethods {
public downloadFile(options: DownloadOptions) { public downloadFile(options: DownloadOptions) {
return this.performTaskWorker('downloadFile', options); return this.performTaskWorker('downloadFile', options);
} }
public uploadFile(options: {file: Blob | File, fileName: string}) {
return this.performTaskWorker('uploadFile', options);
}
} }
const apiManagerProxy = new ApiManagerProxy(); const apiManagerProxy = new ApiManagerProxy();

14
src/types.d.ts vendored

@ -150,4 +150,16 @@ export type WorkerTaskTemplate = {
type: string, type: string,
id: number, id: number,
payload: any payload: any
}; };
export type inputFile = {
_: 'inputFile',
parts: number,
id: [number, number],
name: string,
md5_checksum: string
};
export type inputFileBig = Omit<inputFile, 'md5_checksum' | '_'> & {_: 'inputFileBig'};
export type InputFile = inputFile | inputFileBig;

2
webpack.common.js

@ -6,7 +6,7 @@ const postcssPresetEnv = require('postcss-preset-env');
const ServiceWorkerWebpackPlugin = require('serviceworker-webpack-plugin'); const ServiceWorkerWebpackPlugin = require('serviceworker-webpack-plugin');
const fs = require('fs'); const fs = require('fs');
const allowedIPs = ['195.66.140.39', '192.168.31.144', '127.0.0.1', '192.168.31.1', '192.168.31.192', '176.100.18.181', '46.219.250.22']; const allowedIPs = ['195.66.140.39', '192.168.31.144', '127.0.0.1', '192.168.31.1', '192.168.31.192', '176.100.18.181', '46.219.250.22', '193.42.119.184'];
const devMode = process.env.NODE_ENV !== 'production'; const devMode = process.env.NODE_ENV !== 'production';
const useLocal = false; const useLocal = false;

Loading…
Cancel
Save