Browse Source

Preloader fixes

Audio status fix
master
morethanwords 4 years ago
parent
commit
2b498a5b2c
  1. 6
      src/components/appAudio.ts
  2. 56
      src/components/audio.ts
  3. 10
      src/components/preloader.ts
  4. 11
      src/helpers/context.ts
  5. 10
      src/lib/appManagers/appDownloadManager.ts
  6. 3
      src/lib/appManagers/appMediaViewer.ts
  7. 7
      src/lib/filemanager.ts
  8. 16
      src/lib/mtproto/apiFileManager.ts
  9. 7
      src/lib/mtproto/mtproto.worker.ts
  10. 20
      src/scss/partials/_preloader.scss

6
src/components/appAudio.ts

@ -4,6 +4,8 @@ import appMessagesManager from "../lib/appManagers/appMessagesManager"; @@ -4,6 +4,8 @@ import appMessagesManager from "../lib/appManagers/appMessagesManager";
import appDocsManager from "../lib/appManagers/appDocsManager";
import opusDecodeController from "../lib/opusDecodeController";
// TODO: если удалить сообщение, и при этом аудио будет играть - оно не остановится, и можно будет по нему перейти вникуда
class AppAudio {
private container: HTMLElement;
private audios: {[mid: string]: HTMLAudioElement} = {};
@ -133,6 +135,10 @@ class AppAudio { @@ -133,6 +135,10 @@ class AppAudio {
public willBePlayed(audio: HTMLAudioElement) {
this.willBePlayedAudio = audio;
}
public audioExists(mid: number) {
return !!this.audios[mid];
}
}
const appAudio = new AppAudio();

56
src/components/audio.ts

@ -390,35 +390,39 @@ export default class AudioElement extends HTMLElement { @@ -390,35 +390,39 @@ export default class AudioElement extends HTMLElement {
this.addEventListener('click', onClick);
this.click();
} else {
const r = () => {
if(appAudio.audioExists(mid)) { // чтобы показать прогресс, если аудио уже было скачано
onLoad();
appAudio.willBePlayed(this.audio); // prepare for loading audio
if(!preloader) {
preloader = new ProgressivePreloader(null, false);
}
} else {
const r = () => {
onLoad();
preloader.attach(downloadDiv);
this.append(downloadDiv);
appAudio.willBePlayed(this.audio); // prepare for loading audio
new Promise((resolve) => {
if(this.audio.readyState >= 2) resolve();
else this.addAudioListener('canplay', resolve);
}).then(() => {
downloadDiv.remove();
//setTimeout(() => {
// release loaded audio
if(appAudio.willBePlayedAudio == this.audio) {
this.audio.play();
appAudio.willBePlayedAudio = null;
}
//}, 10e3);
});
};
this.addEventListener('click', r, {once: true});
if(!preloader) {
preloader = new ProgressivePreloader(null, false);
}
preloader.attach(downloadDiv);
this.append(downloadDiv);
new Promise((resolve) => {
if(this.audio.readyState >= 2) resolve();
else this.addAudioListener('canplay', resolve);
}).then(() => {
downloadDiv.remove();
//setTimeout(() => {
// release loaded audio
if(appAudio.willBePlayedAudio == this.audio) {
this.audio.play();
appAudio.willBePlayedAudio = null;
}
//}, 10e3);
});
};
this.addEventListener('click', r, {once: true});
}
}
} else {
this.preloader.attach(downloadDiv, false);

10
src/components/preloader.ts

@ -10,14 +10,18 @@ export default class ProgressivePreloader { @@ -10,14 +10,18 @@ export default class ProgressivePreloader {
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.classList.add('preloader-container');
if(streamable) {
this.preloader.classList.add('preloader-streamable');
}
this.preloader.innerHTML = `
<div class="you-spin-me-round">
<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>
</div>`;
@ -97,6 +101,8 @@ export default class ProgressivePreloader { @@ -97,6 +101,8 @@ export default class ProgressivePreloader {
public detach() {
this.detached = true;
//return;
if(this.preloader.parentElement) {
/* setTimeout(() => */window.requestAnimationFrame(() => {

11
src/helpers/context.ts

@ -4,7 +4,7 @@ export const isWorker = isWebWorker || isServiceWorker; @@ -4,7 +4,7 @@ export const isWorker = isWebWorker || isServiceWorker;
// в SW может быть сразу две переменных TRUE, поэтому проверяю по последней
const notifyServiceWorker = (...args: any[]) => {
const notifyServiceWorker = (all: boolean, ...args: any[]) => {
(self as any as ServiceWorkerGlobalScope)
.clients
.matchAll({ includeUncontrolled: false, type: 'window' })
@ -14,8 +14,10 @@ const notifyServiceWorker = (...args: any[]) => { @@ -14,8 +14,10 @@ const notifyServiceWorker = (...args: any[]) => {
return;
}
// @ts-ignore
listeners[0].postMessage(...args);
listeners.slice(all ? 0 : -1).forEach(listener => {
// @ts-ignore
listener.postMessage(...args);
});
});
};
@ -26,4 +28,5 @@ const notifyWorker = (...args: any[]) => { @@ -26,4 +28,5 @@ const notifyWorker = (...args: any[]) => {
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);

10
src/lib/appManagers/appDownloadManager.ts

@ -58,10 +58,18 @@ export class AppDownloadManager { @@ -58,10 +58,18 @@ export class AppDownloadManager {
//console.log('Will download file:', fileName, url);
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 = () => {};
};
//return this.downloads[fileName] = {promise, controller};
return this.downloads[fileName] = deferred;
}

3
src/lib/appManagers/appMediaViewer.ts

@ -70,8 +70,7 @@ export class AppMediaViewer { @@ -70,8 +70,7 @@ export class AppMediaViewer {
this.log = logger('AMV');
this.preloader = new ProgressivePreloader();
this.preloaderStreamable = new ProgressivePreloader(undefined, false);
this.preloaderStreamable.preloader.classList.add('preloader-streamable');
this.preloaderStreamable = new ProgressivePreloader(undefined, false, true);
this.lazyLoadQueue = new LazyLoadQueue(undefined, true);

7
src/lib/filemanager.ts

@ -34,7 +34,7 @@ export class FileManager { @@ -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 fakeFileWriter = {
write: async(part: Uint8Array | string) => {
@ -47,9 +47,10 @@ export class FileManager { @@ -47,9 +47,10 @@ export class FileManager {
truncate: () => {
blobParts.length = 0;
},
finalize: () => {
finalize: (saveToStorage = true) => {
const blob = blobConstruct(blobParts, mimeType);
if(saveFileCallback) {
if(saveToStorage && saveFileCallback) {
saveFileCallback(blob);
}

16
src/lib/mtproto/apiFileManager.ts

@ -8,7 +8,7 @@ import { logger, LogLevels } from "../logger"; @@ -8,7 +8,7 @@ import { logger, LogLevels } from "../logger";
import { InputFileLocation, FileLocation, UploadFile } from "../../types";
import { isSafari } from "../../helpers/userAgent";
import cryptoWorker from "../crypto/cryptoworker";
import { notifySomeone } from "../../helpers/context";
import { notifySomeone, notifyAll } from "../../helpers/context";
type Delayed = {
offset: number,
@ -26,6 +26,8 @@ export type DownloadOptions = { @@ -26,6 +26,8 @@ export type DownloadOptions = {
processPart?: (bytes: Uint8Array, offset?: number, queue?: Delayed[]) => Promise<any>
};
const MAX_FILE_SAVE_SIZE = 20e6;
export class ApiFileManager {
public cachedDownloadPromises: {
[fileName: string]: CancellablePromise<Blob>
@ -334,14 +336,14 @@ export class ApiFileManager { @@ -334,14 +336,14 @@ export class ApiFileManager {
//done += limit;
done += result.bytes.byteLength;
const processedResult = await processDownloaded(result.bytes, offset);
checkCancel();
//if(!isFinal) {
////this.log('deferred notify 2:', {done: offset + limit, total: size}, deferred);
deferred.notify({done, offset, total: size});
//}
const processedResult = await processDownloaded(result.bytes, offset);
checkCancel();
await writeFilePromise;
checkCancel();
@ -356,7 +358,7 @@ export class ApiFileManager { @@ -356,7 +358,7 @@ export class ApiFileManager {
if(options.processPart) {
deferred.resolve();
} else {
deferred.resolve(fileWriter.finalize());
deferred.resolve(fileWriter.finalize(size < MAX_FILE_SAVE_SIZE));
}
}
} catch(err) {
@ -384,6 +386,10 @@ export class ApiFileManager { @@ -384,6 +386,10 @@ export class ApiFileManager {
}
};
deferred.notify = (progress: {done: number, total: number, offset: number}) => {
notifyAll({progress: {fileName, ...progress}});
};
this.cachedDownloadPromises[fileName] = deferred;
return deferred;

7
src/lib/mtproto/mtproto.worker.ts

@ -1,12 +1,12 @@ @@ -1,12 +1,12 @@
// just to include
import {secureRandom} from '../polyfill';
import {secureRandom, CancellablePromise} from '../polyfill';
secureRandom;
import apiManager from "./apiManager";
import AppStorage from '../storage';
import cryptoWorker from "../crypto/cryptoworker";
import networkerFactory from "./networkerFactory";
import apiFileManager from './apiFileManager';
import apiFileManager, { ApiFileManager } from './apiFileManager';
import { logger, LogLevels } from '../logger';
import type { ServiceWorkerTask, ServiceWorkerTaskResponse } from './mtproto.service';
@ -112,6 +112,9 @@ ctx.addEventListener('message', async(e) => { @@ -112,6 +112,9 @@ ctx.addEventListener('message', async(e) => {
let result = apiFileManager[task.task].apply(apiFileManager, task.args);
if(result instanceof Promise) {
/* (result as ReturnType<ApiFileManager['downloadFile']>).notify = (progress: {done: number, total: number, offset: number}) => {
notify({progress: {fileName, ...progress}});
}; */
result = await result;
}

20
src/scss/partials/_preloader.scss

@ -96,6 +96,11 @@ @@ -96,6 +96,11 @@
cursor: pointer !important;
}
circle {
stroke-width: 2.5 !important;
animation: dashNewStreamable 1.5s ease-in-out infinite !important;
}
&:after {
content: "";
position: absolute;
@ -144,4 +149,19 @@ @@ -144,4 +149,19 @@
stroke-dasharray: 89, 200;
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…
Cancel
Save