Preloader fixes
Audio status fix
This commit is contained in:
parent
c7bd824873
commit
2b498a5b2c
@ -4,6 +4,8 @@ import appMessagesManager from "../lib/appManagers/appMessagesManager";
|
|||||||
import appDocsManager from "../lib/appManagers/appDocsManager";
|
import appDocsManager from "../lib/appManagers/appDocsManager";
|
||||||
import opusDecodeController from "../lib/opusDecodeController";
|
import opusDecodeController from "../lib/opusDecodeController";
|
||||||
|
|
||||||
|
// TODO: если удалить сообщение, и при этом аудио будет играть - оно не остановится, и можно будет по нему перейти вникуда
|
||||||
|
|
||||||
class AppAudio {
|
class AppAudio {
|
||||||
private container: HTMLElement;
|
private container: HTMLElement;
|
||||||
private audios: {[mid: string]: HTMLAudioElement} = {};
|
private audios: {[mid: string]: HTMLAudioElement} = {};
|
||||||
@ -133,6 +135,10 @@ class AppAudio {
|
|||||||
public willBePlayed(audio: HTMLAudioElement) {
|
public willBePlayed(audio: HTMLAudioElement) {
|
||||||
this.willBePlayedAudio = audio;
|
this.willBePlayedAudio = audio;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public audioExists(mid: number) {
|
||||||
|
return !!this.audios[mid];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const appAudio = new AppAudio();
|
const appAudio = new AppAudio();
|
||||||
|
@ -390,35 +390,39 @@ export default class AudioElement extends HTMLElement {
|
|||||||
this.addEventListener('click', onClick);
|
this.addEventListener('click', onClick);
|
||||||
this.click();
|
this.click();
|
||||||
} else {
|
} else {
|
||||||
const r = () => {
|
if(appAudio.audioExists(mid)) { // чтобы показать прогресс, если аудио уже было скачано
|
||||||
onLoad();
|
onLoad();
|
||||||
|
} else {
|
||||||
appAudio.willBePlayed(this.audio); // prepare for loading audio
|
const r = () => {
|
||||||
|
onLoad();
|
||||||
if(!preloader) {
|
|
||||||
preloader = new ProgressivePreloader(null, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
preloader.attach(downloadDiv);
|
appAudio.willBePlayed(this.audio); // prepare for loading audio
|
||||||
this.append(downloadDiv);
|
|
||||||
|
|
||||||
new Promise((resolve) => {
|
if(!preloader) {
|
||||||
if(this.audio.readyState >= 2) resolve();
|
preloader = new ProgressivePreloader(null, false);
|
||||||
else this.addAudioListener('canplay', resolve);
|
}
|
||||||
}).then(() => {
|
|
||||||
downloadDiv.remove();
|
preloader.attach(downloadDiv);
|
||||||
|
this.append(downloadDiv);
|
||||||
//setTimeout(() => {
|
|
||||||
// release loaded audio
|
new Promise((resolve) => {
|
||||||
if(appAudio.willBePlayedAudio == this.audio) {
|
if(this.audio.readyState >= 2) resolve();
|
||||||
this.audio.play();
|
else this.addAudioListener('canplay', resolve);
|
||||||
appAudio.willBePlayedAudio = null;
|
}).then(() => {
|
||||||
}
|
downloadDiv.remove();
|
||||||
//}, 10e3);
|
|
||||||
});
|
//setTimeout(() => {
|
||||||
};
|
// release loaded audio
|
||||||
|
if(appAudio.willBePlayedAudio == this.audio) {
|
||||||
this.addEventListener('click', r, {once: true});
|
this.audio.play();
|
||||||
|
appAudio.willBePlayedAudio = null;
|
||||||
|
}
|
||||||
|
//}, 10e3);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
this.addEventListener('click', r, {once: true});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.preloader.attach(downloadDiv, false);
|
this.preloader.attach(downloadDiv, false);
|
||||||
|
@ -10,14 +10,18 @@ export default class ProgressivePreloader {
|
|||||||
|
|
||||||
private promise: CancellablePromise<any> = null;
|
private promise: CancellablePromise<any> = null;
|
||||||
|
|
||||||
constructor(elem?: Element, private cancelable = true) {
|
constructor(elem?: Element, private cancelable = true, streamable = false) {
|
||||||
this.preloader = document.createElement('div');
|
this.preloader = document.createElement('div');
|
||||||
this.preloader.classList.add('preloader-container');
|
this.preloader.classList.add('preloader-container');
|
||||||
|
|
||||||
|
if(streamable) {
|
||||||
|
this.preloader.classList.add('preloader-streamable');
|
||||||
|
}
|
||||||
|
|
||||||
this.preloader.innerHTML = `
|
this.preloader.innerHTML = `
|
||||||
<div class="you-spin-me-round">
|
<div class="you-spin-me-round">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="preloader-circular" viewBox="25 25 50 50">
|
<svg xmlns="http://www.w3.org/2000/svg" class="preloader-circular" viewBox="25 25 50 50">
|
||||||
<circle class="preloader-path-new" cx="50" cy="50" r="23" fill="none" stroke-miterlimit="10"/>
|
<circle class="preloader-path-new" cx="50" cy="50" r="${streamable ? 19 : 23}" fill="none" stroke-miterlimit="10"/>
|
||||||
</svg>
|
</svg>
|
||||||
</div>`;
|
</div>`;
|
||||||
|
|
||||||
@ -97,6 +101,8 @@ export default class ProgressivePreloader {
|
|||||||
|
|
||||||
public detach() {
|
public detach() {
|
||||||
this.detached = true;
|
this.detached = true;
|
||||||
|
|
||||||
|
//return;
|
||||||
|
|
||||||
if(this.preloader.parentElement) {
|
if(this.preloader.parentElement) {
|
||||||
/* setTimeout(() => */window.requestAnimationFrame(() => {
|
/* setTimeout(() => */window.requestAnimationFrame(() => {
|
||||||
|
@ -4,7 +4,7 @@ export const isWorker = isWebWorker || isServiceWorker;
|
|||||||
|
|
||||||
// в SW может быть сразу две переменных TRUE, поэтому проверяю по последней
|
// в SW может быть сразу две переменных TRUE, поэтому проверяю по последней
|
||||||
|
|
||||||
const notifyServiceWorker = (...args: any[]) => {
|
const notifyServiceWorker = (all: boolean, ...args: any[]) => {
|
||||||
(self as any as ServiceWorkerGlobalScope)
|
(self as any as ServiceWorkerGlobalScope)
|
||||||
.clients
|
.clients
|
||||||
.matchAll({ includeUncontrolled: false, type: 'window' })
|
.matchAll({ includeUncontrolled: false, type: 'window' })
|
||||||
@ -14,8 +14,10 @@ const notifyServiceWorker = (...args: any[]) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// @ts-ignore
|
listeners.slice(all ? 0 : -1).forEach(listener => {
|
||||||
listeners[0].postMessage(...args);
|
// @ts-ignore
|
||||||
|
listener.postMessage(...args);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -26,4 +28,5 @@ const notifyWorker = (...args: any[]) => {
|
|||||||
|
|
||||||
const empty = () => {};
|
const empty = () => {};
|
||||||
|
|
||||||
export const notifySomeone = isServiceWorker ? notifyServiceWorker : (isWebWorker ? notifyWorker : empty);
|
export const notifySomeone = isServiceWorker ? notifyServiceWorker.bind(null, false) : (isWebWorker ? notifyWorker : empty);
|
||||||
|
export const notifyAll = isServiceWorker ? notifyServiceWorker.bind(null, true) : (isWebWorker ? notifyWorker : empty);
|
@ -58,10 +58,18 @@ export class AppDownloadManager {
|
|||||||
//console.log('Will download file:', fileName, url);
|
//console.log('Will download file:', fileName, url);
|
||||||
|
|
||||||
deferred.cancel = () => {
|
deferred.cancel = () => {
|
||||||
|
const error = new Error('Download canceled');
|
||||||
|
error.name = 'AbortError';
|
||||||
|
|
||||||
|
apiManager.cancelDownload(fileName);
|
||||||
|
delete this.downloads[fileName];
|
||||||
|
delete this.progress[fileName];
|
||||||
|
delete this.progressCallbacks[fileName];
|
||||||
|
|
||||||
|
deferred.reject(error);
|
||||||
deferred.cancel = () => {};
|
deferred.cancel = () => {};
|
||||||
};
|
};
|
||||||
|
|
||||||
//return this.downloads[fileName] = {promise, controller};
|
|
||||||
return this.downloads[fileName] = deferred;
|
return this.downloads[fileName] = deferred;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,8 +70,7 @@ export class AppMediaViewer {
|
|||||||
this.log = logger('AMV');
|
this.log = logger('AMV');
|
||||||
this.preloader = new ProgressivePreloader();
|
this.preloader = new ProgressivePreloader();
|
||||||
|
|
||||||
this.preloaderStreamable = new ProgressivePreloader(undefined, false);
|
this.preloaderStreamable = new ProgressivePreloader(undefined, false, true);
|
||||||
this.preloaderStreamable.preloader.classList.add('preloader-streamable');
|
|
||||||
|
|
||||||
this.lazyLoadQueue = new LazyLoadQueue(undefined, true);
|
this.lazyLoadQueue = new LazyLoadQueue(undefined, true);
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ export class FileManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public getFakeFileWriter(mimeType: string, saveFileCallback: (blob: Blob) => Promise<Blob>) {
|
public getFakeFileWriter(mimeType: string, saveFileCallback?: (blob: Blob) => Promise<Blob>) {
|
||||||
const blobParts: Array<Uint8Array | string> = [];
|
const blobParts: Array<Uint8Array | string> = [];
|
||||||
const fakeFileWriter = {
|
const fakeFileWriter = {
|
||||||
write: async(part: Uint8Array | string) => {
|
write: async(part: Uint8Array | string) => {
|
||||||
@ -47,9 +47,10 @@ export class FileManager {
|
|||||||
truncate: () => {
|
truncate: () => {
|
||||||
blobParts.length = 0;
|
blobParts.length = 0;
|
||||||
},
|
},
|
||||||
finalize: () => {
|
finalize: (saveToStorage = true) => {
|
||||||
const blob = blobConstruct(blobParts, mimeType);
|
const blob = blobConstruct(blobParts, mimeType);
|
||||||
if(saveFileCallback) {
|
|
||||||
|
if(saveToStorage && saveFileCallback) {
|
||||||
saveFileCallback(blob);
|
saveFileCallback(blob);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ import { logger, LogLevels } from "../logger";
|
|||||||
import { InputFileLocation, FileLocation, UploadFile } from "../../types";
|
import { InputFileLocation, FileLocation, UploadFile } from "../../types";
|
||||||
import { isSafari } from "../../helpers/userAgent";
|
import { isSafari } from "../../helpers/userAgent";
|
||||||
import cryptoWorker from "../crypto/cryptoworker";
|
import cryptoWorker from "../crypto/cryptoworker";
|
||||||
import { notifySomeone } from "../../helpers/context";
|
import { notifySomeone, notifyAll } from "../../helpers/context";
|
||||||
|
|
||||||
type Delayed = {
|
type Delayed = {
|
||||||
offset: number,
|
offset: number,
|
||||||
@ -26,6 +26,8 @@ export type DownloadOptions = {
|
|||||||
processPart?: (bytes: Uint8Array, offset?: number, queue?: Delayed[]) => Promise<any>
|
processPart?: (bytes: Uint8Array, offset?: number, queue?: Delayed[]) => Promise<any>
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const MAX_FILE_SAVE_SIZE = 20e6;
|
||||||
|
|
||||||
export class ApiFileManager {
|
export class ApiFileManager {
|
||||||
public cachedDownloadPromises: {
|
public cachedDownloadPromises: {
|
||||||
[fileName: string]: CancellablePromise<Blob>
|
[fileName: string]: CancellablePromise<Blob>
|
||||||
@ -334,14 +336,14 @@ export class ApiFileManager {
|
|||||||
//done += limit;
|
//done += limit;
|
||||||
done += result.bytes.byteLength;
|
done += result.bytes.byteLength;
|
||||||
|
|
||||||
const processedResult = await processDownloaded(result.bytes, offset);
|
|
||||||
checkCancel();
|
|
||||||
|
|
||||||
//if(!isFinal) {
|
//if(!isFinal) {
|
||||||
////this.log('deferred notify 2:', {done: offset + limit, total: size}, deferred);
|
////this.log('deferred notify 2:', {done: offset + limit, total: size}, deferred);
|
||||||
deferred.notify({done, offset, total: size});
|
deferred.notify({done, offset, total: size});
|
||||||
//}
|
//}
|
||||||
|
|
||||||
|
const processedResult = await processDownloaded(result.bytes, offset);
|
||||||
|
checkCancel();
|
||||||
|
|
||||||
await writeFilePromise;
|
await writeFilePromise;
|
||||||
checkCancel();
|
checkCancel();
|
||||||
|
|
||||||
@ -356,7 +358,7 @@ export class ApiFileManager {
|
|||||||
if(options.processPart) {
|
if(options.processPart) {
|
||||||
deferred.resolve();
|
deferred.resolve();
|
||||||
} else {
|
} else {
|
||||||
deferred.resolve(fileWriter.finalize());
|
deferred.resolve(fileWriter.finalize(size < MAX_FILE_SAVE_SIZE));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
@ -384,6 +386,10 @@ export class ApiFileManager {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
deferred.notify = (progress: {done: number, total: number, offset: number}) => {
|
||||||
|
notifyAll({progress: {fileName, ...progress}});
|
||||||
|
};
|
||||||
|
|
||||||
this.cachedDownloadPromises[fileName] = deferred;
|
this.cachedDownloadPromises[fileName] = deferred;
|
||||||
|
|
||||||
return deferred;
|
return deferred;
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
// just to include
|
// just to include
|
||||||
import {secureRandom} from '../polyfill';
|
import {secureRandom, CancellablePromise} from '../polyfill';
|
||||||
secureRandom;
|
secureRandom;
|
||||||
|
|
||||||
import apiManager from "./apiManager";
|
import apiManager from "./apiManager";
|
||||||
import AppStorage from '../storage';
|
import AppStorage from '../storage';
|
||||||
import cryptoWorker from "../crypto/cryptoworker";
|
import cryptoWorker from "../crypto/cryptoworker";
|
||||||
import networkerFactory from "./networkerFactory";
|
import networkerFactory from "./networkerFactory";
|
||||||
import apiFileManager from './apiFileManager';
|
import apiFileManager, { ApiFileManager } from './apiFileManager';
|
||||||
import { logger, LogLevels } from '../logger';
|
import { logger, LogLevels } from '../logger';
|
||||||
import type { ServiceWorkerTask, ServiceWorkerTaskResponse } from './mtproto.service';
|
import type { ServiceWorkerTask, ServiceWorkerTaskResponse } from './mtproto.service';
|
||||||
|
|
||||||
@ -112,6 +112,9 @@ ctx.addEventListener('message', async(e) => {
|
|||||||
let result = apiFileManager[task.task].apply(apiFileManager, task.args);
|
let result = apiFileManager[task.task].apply(apiFileManager, task.args);
|
||||||
|
|
||||||
if(result instanceof Promise) {
|
if(result instanceof Promise) {
|
||||||
|
/* (result as ReturnType<ApiFileManager['downloadFile']>).notify = (progress: {done: number, total: number, offset: number}) => {
|
||||||
|
notify({progress: {fileName, ...progress}});
|
||||||
|
}; */
|
||||||
result = await result;
|
result = await result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,6 +96,11 @@
|
|||||||
cursor: pointer !important;
|
cursor: pointer !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
circle {
|
||||||
|
stroke-width: 2.5 !important;
|
||||||
|
animation: dashNewStreamable 1.5s ease-in-out infinite !important;
|
||||||
|
}
|
||||||
|
|
||||||
&:after {
|
&:after {
|
||||||
content: "";
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -144,4 +149,19 @@
|
|||||||
stroke-dasharray: 89, 200;
|
stroke-dasharray: 89, 200;
|
||||||
stroke-dashoffset: -286%;
|
stroke-dashoffset: -286%;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes dashNewStreamable {
|
||||||
|
0% {
|
||||||
|
stroke-dasharray: 1, 200;
|
||||||
|
stroke-dashoffset: 0;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
stroke-dasharray: 89, 200;
|
||||||
|
stroke-dashoffset: -35px;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
stroke-dasharray: 89, 200;
|
||||||
|
stroke-dashoffset: -237%;
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user