Browse Source

Audio fixes

Fix loading round video when auto download is off
Added unread status for video messages
master
morethanwords 3 years ago
parent
commit
a15b0807c4
  1. 53
      src/components/appMediaPlaybackController.ts
  2. 3
      src/components/appSearchSuper..ts
  3. 175
      src/components/audio.ts
  4. 4
      src/components/popups/newMedia.ts
  5. 43
      src/components/preloader.ts
  6. 101
      src/components/wrappers.ts
  7. 6
      src/helpers/files.ts
  8. 26
      src/helpers/schedulers.ts
  9. 22
      src/helpers/schedulers/throttleWith.ts
  10. 9
      src/helpers/schedulers/throttleWithRaf.ts
  11. 4
      src/lib/appManagers/appDocsManager.ts
  12. 4
      src/lib/mediaPlayer.ts
  13. 1
      src/lib/rootScope.ts
  14. 2
      src/scss/partials/_audio.scss
  15. 17
      src/scss/style.scss

53
src/components/appMediaPlaybackController.ts

@ -56,6 +56,7 @@ class AppMediaPlaybackController {
[mid: string]: CancellablePromise<void> [mid: string]: CancellablePromise<void>
} }
} = {}; } = {};
private waitingDocumentsForLoad: {[docId: string]: Set<HTMLMediaElement>} = {};
public willBePlayedMedia: HTMLMediaElement; public willBePlayedMedia: HTMLMediaElement;
private searchContext: SearchSuperContext; private searchContext: SearchSuperContext;
@ -88,6 +89,15 @@ class AppMediaPlaybackController {
} }
} }
} }
rootScope.addEventListener('document_downloaded', (doc) => {
const set = this.waitingDocumentsForLoad[doc.id];
if(set) {
for(const media of set) {
this.onMediaDocumentLoad(media);
}
}
});
} }
public seekBackward = (details: MediaSessionActionDetails) => { public seekBackward = (details: MediaSessionActionDetails) => {
@ -124,6 +134,7 @@ class AppMediaPlaybackController {
//media.muted = true; //media.muted = true;
} }
media.dataset.docId = '' + doc.id;
media.dataset.peerId = '' + peerId; media.dataset.peerId = '' + peerId;
media.dataset.mid = '' + mid; media.dataset.mid = '' + mid;
media.dataset.type = doc.type; media.dataset.type = doc.type;
@ -138,6 +149,13 @@ class AppMediaPlaybackController {
media.addEventListener('pause', this.onPause); media.addEventListener('pause', this.onPause);
media.addEventListener('ended', this.onEnded); media.addEventListener('ended', this.onEnded);
const message: Message.message = appMessagesManager.getMessageByPeer(peerId, mid);
if(doc.type !== 'audio' && message?.pFlags.media_unread && message.fromId !== rootScope.myId) {
media.addEventListener('timeupdate', () => {
appMessagesManager.readMessages(peerId, [mid]);
}, {once: true});
}
/* const onError = (e: Event) => { /* const onError = (e: Event) => {
//console.log('appMediaPlaybackController: video onError', e); //console.log('appMediaPlaybackController: video onError', e);
@ -164,7 +182,25 @@ class AppMediaPlaybackController {
//media.autoplay = true; //media.autoplay = true;
//console.log('will set media url:', media, doc, doc.type, doc.url); //console.log('will set media url:', media, doc, doc.type, doc.url);
((!doc.supportsStreaming ? appDocsManager.downloadDoc(doc) : Promise.resolve()) as Promise<any>).then(() => { const cacheContext = appDownloadManager.getCacheContext(doc);
if(doc.supportsStreaming || cacheContext.url) {
this.onMediaDocumentLoad(media);
} else {
let set = this.waitingDocumentsForLoad[doc.id];
if(!set) {
set = this.waitingDocumentsForLoad[doc.id] = new Set();
}
set.add(media);
appDocsManager.downloadDoc(doc);
}
}/* , onError */);
return storage[mid] = media;
}
private onMediaDocumentLoad = (media: HTMLMediaElement) => {
const doc = appDocsManager.getDoc(media.dataset.docId);
if(doc.type === 'audio' && doc.supportsStreaming && SHOULD_USE_SAFARI_FIX) { if(doc.type === 'audio' && doc.supportsStreaming && SHOULD_USE_SAFARI_FIX) {
this.handleSafariStreamable(media); this.handleSafariStreamable(media);
} }
@ -173,11 +209,16 @@ class AppMediaPlaybackController {
const cacheContext = appDownloadManager.getCacheContext(doc); const cacheContext = appDownloadManager.getCacheContext(doc);
media.src = cacheContext.url; media.src = cacheContext.url;
// }, doc.supportsStreaming ? 500e3 : 0); // }, doc.supportsStreaming ? 500e3 : 0);
});
}/* , onError */);
return storage[mid] = media; const set = this.waitingDocumentsForLoad[doc.id];
if(set) {
set.delete(media);
if(!set.size) {
delete this.waitingDocumentsForLoad[doc.id];
}
} }
};
// safari подгрузит последний чанк и песня включится, // safari подгрузит последний чанк и песня включится,
// при этом этот чанк нельзя руками отдать из SW, потому что браузер тогда теряется // при этом этот чанк нельзя руками отдать из SW, потому что браузер тогда теряется
@ -432,10 +473,10 @@ class AppMediaPlaybackController {
media.autoplay = true; media.autoplay = true;
} */ } */
this.resolveWaitingForLoadMedia(peerId, mid); media.play();
setTimeout(() => { setTimeout(() => {
media.play()//.catch(() => {}); this.resolveWaitingForLoadMedia(peerId, mid);
}, 0); }, 0);
}; };

3
src/components/appSearchSuper..ts

@ -720,7 +720,8 @@ export default class AppSearchSuper {
voiceAsMusic: true, voiceAsMusic: true,
showSender, showSender,
searchContext: this.copySearchContext(inputFilter), searchContext: this.copySearchContext(inputFilter),
lazyLoadQueue: this.lazyLoadQueue lazyLoadQueue: this.lazyLoadQueue,
noAutoDownload: true
}); });
if((['audio', 'voice', 'round'] as MyDocument['type'][]).includes(message.media.document.type)) { if((['audio', 'voice', 'round'] as MyDocument['type'][]).includes(message.media.document.type)) {

175
src/components/audio.ts

@ -17,7 +17,7 @@ import rootScope from "../lib/rootScope";
import './middleEllipsis'; import './middleEllipsis';
import { SearchSuperContext } from "./appSearchSuper."; import { SearchSuperContext } from "./appSearchSuper.";
import { cancelEvent } from "../helpers/dom/cancelEvent"; import { cancelEvent } from "../helpers/dom/cancelEvent";
import { attachClickEvent, detachClickEvent } from "../helpers/dom/clickEvent"; import { attachClickEvent } from "../helpers/dom/clickEvent";
import LazyLoadQueue from "./lazyLoadQueue"; import LazyLoadQueue from "./lazyLoadQueue";
import { CancellablePromise, deferredPromise } from "../helpers/cancellablePromise"; import { CancellablePromise, deferredPromise } from "../helpers/cancellablePromise";
import ListenerSetter, { Listener } from "../helpers/listenerSetter"; import ListenerSetter, { Listener } from "../helpers/listenerSetter";
@ -28,10 +28,12 @@ import { MiddleEllipsisElement } from "./middleEllipsis";
import htmlToSpan from "../helpers/dom/htmlToSpan"; import htmlToSpan from "../helpers/dom/htmlToSpan";
import { formatFullSentTime } from "../helpers/date"; import { formatFullSentTime } from "../helpers/date";
import { formatBytes } from "../helpers/number"; import { formatBytes } from "../helpers/number";
import throttleWithRaf from "../helpers/schedulers/throttleWithRaf";
rootScope.addEventListener('messages_media_read', ({mids, peerId}) => { rootScope.addEventListener('messages_media_read', ({mids, peerId}) => {
mids.forEach(mid => { mids.forEach(mid => {
(Array.from(document.querySelectorAll('audio-element[data-mid="' + mid + '"][data-peer-id="' + peerId + '"].is-unread')) as AudioElement[]).forEach(elem => { const attr = `[data-mid="${mid}"][data-peer-id="${peerId}"]`;
(Array.from(document.querySelectorAll(`audio-element.is-unread${attr}, .media-round.is-unread${attr}`)) as AudioElement[]).forEach(elem => {
elem.classList.remove('is-unread'); elem.classList.remove('is-unread');
}); });
}); });
@ -80,11 +82,6 @@ function wrapVoiceMessage(audioEl: AudioElement) {
const message = audioEl.message; const message = audioEl.message;
const doc = (message.media.document || message.media.webpage.document) as MyDocument; const doc = (message.media.document || message.media.webpage.document) as MyDocument;
const isOut = message.fromId === rootScope.myId && message.peerId !== rootScope.myId;
let isUnread = message && message.pFlags.media_unread;
if(isUnread) {
audioEl.classList.add('is-unread');
}
if(message.pFlags.out) { if(message.pFlags.out) {
audioEl.classList.add('is-out'); audioEl.classList.add('is-out');
@ -154,59 +151,28 @@ function wrapVoiceMessage(audioEl: AudioElement) {
let progress = audioEl.querySelector('.audio-waveform') as HTMLDivElement; let progress = audioEl.querySelector('.audio-waveform') as HTMLDivElement;
const onLoad = () => { const onLoad = () => {
let interval = 0;
let lastIndex = 0;
let audio = audioEl.audio; let audio = audioEl.audio;
if(!audio.paused || (audio.currentTime > 0 && audio.currentTime !== audio.duration)) { const onTimeUpdate = () => {
lastIndex = Math.round(audio.currentTime / audio.duration * barCount); const lastIndex = audio.currentTime === audio.duration ? 0 : Math.ceil(audio.currentTime / audio.duration * barCount);
rects.slice(0, lastIndex + 1).forEach(node => node.classList.add('active'));
}
let start = () => {
clearInterval(interval);
interval = window.setInterval(() => {
if(lastIndex > svg.childElementCount || isNaN(audio.duration) || audio.paused) {
clearInterval(interval);
return;
}
lastIndex = Math.round(audio.currentTime / audio.duration * barCount);
//svg.children[lastIndex].setAttributeNS(null, 'fill', '#000'); //svg.children[lastIndex].setAttributeNS(null, 'fill', '#000');
//svg.children[lastIndex].classList.add('active'); #Иногда пропускает полоски.. //svg.children[lastIndex].classList.add('active'); #Иногда пропускает полоски..
rects.slice(0, lastIndex + 1).forEach(node => node.classList.add('active')); rects.forEach((node, idx) => node.classList.toggle('active', idx < lastIndex));
//++lastIndex; //++lastIndex;
//console.log('lastIndex:', lastIndex, audio.currentTime); //console.log('lastIndex:', lastIndex, audio.currentTime);
//}, duration * 1000 / svg.childElementCount | 0/* 63 * duration / 10 */); //}, duration * 1000 / svg.childElementCount | 0/* 63 * duration / 10 */);
}, 20);
}; };
if(!audio.paused) { if(!audio.paused || (audio.currentTime > 0 && audio.currentTime !== audio.duration)) {
start(); onTimeUpdate();
}
audioEl.addAudioListener('play', () => {
if(isUnread && !isOut && audioEl.classList.contains('is-unread')) {
audioEl.classList.remove('is-unread');
appMessagesManager.readMessages(audioEl.message.peerId, [audioEl.message.mid]);
isUnread = false;
} }
//rects.forEach(node => node.classList.remove('active')); const throttledTimeUpdate = throttleWithRaf(onTimeUpdate);
start(); audioEl.addAudioListener('timeupdate', throttledTimeUpdate);
}); audioEl.addAudioListener('ended', throttledTimeUpdate);
audioEl.addAudioListener('pause', () => {
clearInterval(interval);
});
audioEl.addAudioListener('ended', () => {
clearInterval(interval);
rects.forEach(node => node.classList.remove('active'));
});
audioEl.readyPromise.then(() => {
let mousedown = false, mousemove = false; let mousedown = false, mousemove = false;
progress.addEventListener('mouseleave', (e) => { progress.addEventListener('mouseleave', (e) => {
if(mousedown) { if(mousedown) {
@ -221,7 +187,7 @@ function wrapVoiceMessage(audioEl: AudioElement) {
}); });
progress.addEventListener('mousedown', (e) => { progress.addEventListener('mousedown', (e) => {
e.preventDefault(); e.preventDefault();
if(e.button !== 1) return; if(e.button !== 0) return;
if(!audio.paused) { if(!audio.paused) {
audio.pause(); audio.pause();
} }
@ -230,7 +196,7 @@ function wrapVoiceMessage(audioEl: AudioElement) {
mousedown = true; mousedown = true;
}); });
progress.addEventListener('mouseup', (e) => { progress.addEventListener('mouseup', (e) => {
if (mousemove && mousedown) { if(mousemove && mousedown) {
audio.play(); audio.play();
mousedown = false; mousedown = false;
} }
@ -250,17 +216,11 @@ function wrapVoiceMessage(audioEl: AudioElement) {
} }
const scrubTime = offsetX / availW /* width */ * audio.duration; const scrubTime = offsetX / availW /* width */ * audio.duration;
lastIndex = Math.round(scrubTime / audio.duration * barCount);
rects.slice(0, lastIndex + 1).forEach(node => node.classList.add('active'));
for(let i = lastIndex + 1; i < rects.length; ++i) {
rects[i].classList.remove('active')
}
audio.currentTime = scrubTime; audio.currentTime = scrubTime;
} }
});
return () => { return () => {
clearInterval(interval);
progress.remove(); progress.remove();
progress = null; progress = null;
audio = null; audio = null;
@ -367,8 +327,11 @@ function wrapAudio(audioEl: AudioElement) {
function constructDownloadPreloader(tryAgainOnFail = true) { function constructDownloadPreloader(tryAgainOnFail = true) {
const preloader = new ProgressivePreloader({cancelable: true, tryAgainOnFail}); const preloader = new ProgressivePreloader({cancelable: true, tryAgainOnFail});
preloader.construct(); preloader.construct();
if(!tryAgainOnFail) {
preloader.circle.setAttributeNS(null, 'r', '23'); preloader.circle.setAttributeNS(null, 'r', '23');
preloader.totalLength = 143.58203125; preloader.totalLength = 143.58203125;
}
return preloader; return preloader;
} }
@ -388,7 +351,7 @@ export default class AudioElement extends HTMLElement {
private listenerSetter = new ListenerSetter(); private listenerSetter = new ListenerSetter();
private onTypeDisconnect: () => void; private onTypeDisconnect: () => void;
public onLoad: (autoload?: boolean) => void; public onLoad: (autoload?: boolean) => void;
private readyPromise: CancellablePromise<void>; public readyPromise: CancellablePromise<void>;
public render() { public render() {
this.classList.add('audio'); this.classList.add('audio');
@ -414,6 +377,11 @@ export default class AudioElement extends HTMLElement {
const downloadDiv = document.createElement('div'); const downloadDiv = document.createElement('div');
downloadDiv.classList.add('audio-download'); downloadDiv.classList.add('audio-download');
const isUnread = doc.type !== 'audio' && this.message && this.message.pFlags.media_unread;
if(isUnread) {
this.classList.add('is-unread');
}
if(uploading) { if(uploading) {
this.classList.add('is-outgoing'); this.classList.add('is-outgoing');
this.append(downloadDiv); this.append(downloadDiv);
@ -424,11 +392,17 @@ export default class AudioElement extends HTMLElement {
const audioTimeDiv = this.querySelector('.audio-time') as HTMLDivElement; const audioTimeDiv = this.querySelector('.audio-time') as HTMLDivElement;
audioTimeDiv.innerHTML = durationStr; audioTimeDiv.innerHTML = durationStr;
const onLoad = this.onLoad = (autoload = true) => { const onLoad = this.onLoad = (autoload: boolean) => {
this.onLoad = undefined; this.onLoad = undefined;
const audio = this.audio = appMediaPlaybackController.addMedia(this.message.peerId, this.message.media.document || this.message.media.webpage.document, this.message.mid, autoload); const audio = this.audio = appMediaPlaybackController.addMedia(this.message.peerId, this.message.media.document || this.message.media.webpage.document, this.message.mid, autoload);
this.readyPromise = deferredPromise<void>();
if(this.audio.readyState >= 2) this.readyPromise.resolve();
else {
this.addAudioListener('canplay', () => this.readyPromise.resolve(), {once: true});
}
this.onTypeDisconnect = onTypeLoad(); this.onTypeDisconnect = onTypeLoad();
const getTimeStr = () => String(audio.currentTime | 0).toHHMMSS() + (isVoice ? (' / ' + durationStr) : ''); const getTimeStr = () => String(audio.currentTime | 0).toHHMMSS() + (isVoice ? (' / ' + durationStr) : '');
@ -492,52 +466,7 @@ export default class AudioElement extends HTMLElement {
if(!isOutgoing) { if(!isOutgoing) {
let preloader: ProgressivePreloader = this.preloader; let preloader: ProgressivePreloader = this.preloader;
const getDownloadPromise = () => appDocsManager.downloadDoc(doc); onLoad(doc.type !== 'audio' && !this.noAutoDownload);
if(isRealVoice) {
if(!preloader) {
preloader = constructDownloadPreloader();
}
const load = () => {
const download = getDownloadPromise();
preloader.attach(downloadDiv, false, download);
if(!downloadDiv.parentElement) {
this.append(downloadDiv);
}
(download as Promise<any>).then(() => {
detachClickEvent(this, onClick);
onLoad();
downloadDiv.classList.add('downloaded');
setTimeout(() => {
downloadDiv.remove();
}, 200);
});
return {download};
};
// preloader.construct();
preloader.setManual();
preloader.attach(downloadDiv);
preloader.setDownloadFunction(load);
const onClick = (e?: Event) => {
preloader.onClick(e);
};
attachClickEvent(this, onClick);
if(!this.noAutoDownload) {
onClick();
}
} else {
// if(doc.supportsStreaming) {
onLoad(false);
// }
if(doc.thumbs) { if(doc.thumbs) {
const imgs: HTMLImageElement[] = []; const imgs: HTMLImageElement[] = [];
@ -559,32 +488,24 @@ export default class AudioElement extends HTMLElement {
imgs.forEach(img => img.classList.add('audio-thumb')); imgs.forEach(img => img.classList.add('audio-thumb'));
} }
//if(appMediaPlaybackController.mediaExists(mid)) { // чтобы показать прогресс, если аудио уже было скачано const r = (shouldPlay: boolean) => {
//onLoad();
//} else {
const r = (e: Event) => {
if(!this.audio) {
const togglePlay = onLoad(false);
}
if(this.audio.src) { if(this.audio.src) {
return; return;
} }
appMediaPlaybackController.resolveWaitingForLoadMedia(this.message.peerId, this.message.mid); appMediaPlaybackController.resolveWaitingForLoadMedia(this.message.peerId, this.message.mid);
const onDownloadInit = () => {
if(shouldPlay) {
appMediaPlaybackController.willBePlayed(this.audio); // prepare for loading audio appMediaPlaybackController.willBePlayed(this.audio); // prepare for loading audio
if(IS_SAFARI) { if(IS_SAFARI && !this.audio.autoplay) {
this.audio.autoplay = true; this.audio.autoplay = true;
} }
// togglePlay(undefined, true);
this.readyPromise = deferredPromise<void>();
if(this.audio.readyState >= 2) this.readyPromise.resolve();
else {
this.addAudioListener('canplay', () => this.readyPromise.resolve(), {once: true});
} }
};
onDownloadInit();
if(!preloader) { if(!preloader) {
if(doc.supportsStreaming) { if(doc.supportsStreaming) {
@ -610,6 +531,8 @@ export default class AudioElement extends HTMLElement {
pauseListener = this.addAudioListener('pause', () => { pauseListener = this.addAudioListener('pause', () => {
deferred.cancel(); deferred.cancel();
}, {once: true}) as any; }, {once: true}) as any;
onDownloadInit();
}; };
/* if(!this.audio.paused) { /* if(!this.audio.paused) {
@ -625,7 +548,9 @@ export default class AudioElement extends HTMLElement {
preloader = constructDownloadPreloader(); preloader = constructDownloadPreloader();
const load = () => { const load = () => {
const download = getDownloadPromise(); onDownloadInit();
const download = appDocsManager.downloadDoc(doc);
preloader.attach(downloadDiv, false, download); preloader.attach(downloadDiv, false, download);
return {download}; return {download};
}; };
@ -657,9 +582,13 @@ export default class AudioElement extends HTMLElement {
}; };
if(!this.audio?.src) { if(!this.audio?.src) {
attachClickEvent(toggle, r, {once: true, capture: true, passive: false}); if(doc.type !== 'audio' && !this.noAutoDownload) {
r(false);
} else {
attachClickEvent(toggle, () => {
r(true);
}, {once: true, capture: true, passive: false, listenerSetter: this.listenerSetter});
} }
//}
} }
} else if(uploading) { } else if(uploading) {
this.preloader.attach(downloadDiv, false); this.preloader.attach(downloadDiv, false);

4
src/components/popups/newMedia.ts

@ -12,7 +12,7 @@ import { toast } from "../toast";
import { prepareAlbum, wrapDocument } from "../wrappers"; import { prepareAlbum, wrapDocument } from "../wrappers";
import CheckboxField from "../checkboxField"; import CheckboxField from "../checkboxField";
import SendContextMenu from "../chat/sendContextMenu"; import SendContextMenu from "../chat/sendContextMenu";
import { createPosterFromVideo, onVideoLoad } from "../../helpers/files"; import { createPosterFromVideo, onMediaLoad } from "../../helpers/files";
import { MyDocument } from "../../lib/appManagers/appDocsManager"; import { MyDocument } from "../../lib/appManagers/appDocsManager";
import I18n, { i18n, LangPackKey } from "../../lib/langPack"; import I18n, { i18n, LangPackKey } from "../../lib/langPack";
import appDownloadManager from "../../lib/appManagers/appDownloadManager"; import appDownloadManager from "../../lib/appManagers/appDownloadManager";
@ -262,7 +262,7 @@ export default class PopupNewMedia extends PopupElement {
video.pause(); video.pause();
}, {once: true}); }, {once: true});
onVideoLoad(video).then(() => { onMediaLoad(video).then(() => {
params.width = video.videoWidth; params.width = video.videoWidth;
params.height = video.videoHeight; params.height = video.videoHeight;
params.duration = Math.floor(video.duration); params.duration = Math.floor(video.duration);

43
src/components/preloader.ts

@ -179,7 +179,7 @@ export default class ProgressivePreloader {
} }
} else { } else {
if(this.tryAgainOnFail) { if(this.tryAgainOnFail) {
SetTransition(this.preloader, '', true, TRANSITION_TIME); this.attach(this.preloader.parentElement);
fastRaf(() => { fastRaf(() => {
this.setManual(); this.setManual();
}); });
@ -211,13 +211,6 @@ export default class ProgressivePreloader {
} }
public attach(elem: Element, reset = false, promise?: CancellablePromise<any>) { public attach(elem: Element, reset = false, promise?: CancellablePromise<any>) {
//return;
this.detached = false;
/* fastRaf(() => {
if(this.detached) return;
this.detached = false; */
if(this.construct) { if(this.construct) {
this.construct(); this.construct();
} }
@ -226,32 +219,30 @@ export default class ProgressivePreloader {
this.preloader.classList.remove('manual'); this.preloader.classList.remove('manual');
} }
const useRafs = isInDOM(this.preloader) ? 1 : 2; this.detached = false;
if(this.preloader.parentElement !== elem) {
elem[this.attachMethod](this.preloader);
}
if(promise/* && false */) { if(promise/* && false */) {
this.attachPromise(promise); this.attachPromise(promise);
} }
// fastRaf(() => { if(this.detached || this.preloader.parentElement !== elem) {
//console.log('[PP]: attach after rAF', this.detached, performance.now()); const useRafs = isInDOM(this.preloader) ? 1 : 2;
if(this.preloader.parentElement !== elem) {
// if(this.detached) { elem[this.attachMethod](this.preloader);
// return; }
// }
SetTransition(this.preloader, 'is-visible', true, TRANSITION_TIME, undefined, useRafs); SetTransition(this.preloader, 'is-visible', true, TRANSITION_TIME, undefined, useRafs);
// }); }
if(this.cancelable && reset) { if(this.cancelable && reset) {
this.setProgress(0); this.setProgress(0);
} }
//});
} }
public detach() { public detach() {
if(this.detached) {
return;
}
//return; //return;
this.detached = true; this.detached = true;
@ -263,17 +254,17 @@ export default class ProgressivePreloader {
/* if(!this.detached) return; /* if(!this.detached) return;
this.detached = true; */ this.detached = true; */
fastRaf(() => { // fastRaf(() => {
//console.log('[PP]: detach after rAF', this.detached, performance.now()); //console.log('[PP]: detach after rAF', this.detached, performance.now());
if(!this.detached || !this.preloader.parentElement) { // if(!this.detached || !this.preloader.parentElement) {
return; // return;
} // }
SetTransition(this.preloader, 'is-visible', false, TRANSITION_TIME, () => { SetTransition(this.preloader, 'is-visible', false, TRANSITION_TIME, () => {
this.preloader.remove(); this.preloader.remove();
}); }, 1);
}); // });
//})/* , 5e3) */; //})/* , 5e3) */;
} }
} }

101
src/components/wrappers.ts

@ -12,7 +12,7 @@ import { formatFullSentTime } from '../helpers/date';
import mediaSizes, { ScreenSize } from '../helpers/mediaSizes'; import mediaSizes, { ScreenSize } from '../helpers/mediaSizes';
import { formatBytes } from '../helpers/number'; import { formatBytes } from '../helpers/number';
import { IS_SAFARI } from '../environment/userAgent'; import { IS_SAFARI } from '../environment/userAgent';
import { PhotoSize, StickerSet } from '../layer'; import { Message, PhotoSize, StickerSet } from '../layer';
import appDocsManager, { MyDocument } from "../lib/appManagers/appDocsManager"; import appDocsManager, { MyDocument } from "../lib/appManagers/appDocsManager";
import appMessagesManager from '../lib/appManagers/appMessagesManager'; import appMessagesManager from '../lib/appManagers/appMessagesManager';
import appPhotosManager, { MyPhoto } from '../lib/appManagers/appPhotosManager'; import appPhotosManager, { MyPhoto } from '../lib/appManagers/appPhotosManager';
@ -31,7 +31,7 @@ import RichTextProcessor from '../lib/richtextprocessor';
import appImManager from '../lib/appManagers/appImManager'; import appImManager from '../lib/appManagers/appImManager';
import { SearchSuperContext } from './appSearchSuper.'; import { SearchSuperContext } from './appSearchSuper.';
import rootScope from '../lib/rootScope'; import rootScope from '../lib/rootScope';
import { onVideoLoad } from '../helpers/files'; import { onMediaLoad } from '../helpers/files';
import { animateSingle } from '../helpers/animation'; import { animateSingle } from '../helpers/animation';
import renderImageFromUrl from '../helpers/dom/renderImageFromUrl'; import renderImageFromUrl from '../helpers/dom/renderImageFromUrl';
import sequentialDom from '../helpers/sequentialDom'; import sequentialDom from '../helpers/sequentialDom';
@ -77,7 +77,7 @@ mediaSizes.addEventListener('changeScreen', (from, to) => {
export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTail, isOut, middleware, lazyLoadQueue, noInfo, group, onlyPreview, withoutPreloader, loadPromises, noPlayButton, noAutoDownload, size, searchContext}: { export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTail, isOut, middleware, lazyLoadQueue, noInfo, group, onlyPreview, withoutPreloader, loadPromises, noPlayButton, noAutoDownload, size, searchContext}: {
doc: MyDocument, doc: MyDocument,
container?: HTMLElement, container?: HTMLElement,
message?: any, message?: Message.message,
boxWidth?: number, boxWidth?: number,
boxHeight?: number, boxHeight?: number,
withTail?: boolean, withTail?: boolean,
@ -172,6 +172,8 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
const divRound = document.createElement('div'); const divRound = document.createElement('div');
divRound.classList.add('media-round', 'z-depth-1'); divRound.classList.add('media-round', 'z-depth-1');
divRound.dataset.mid = '' + message.mid;
divRound.dataset.peerId = '' + message.peerId;
const size = mediaSizes.active.round; const size = mediaSizes.active.round;
const halfSize = size.width / 2; const halfSize = size.width / 2;
@ -190,6 +192,11 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
spanTime.classList.add('tgico'); spanTime.classList.add('tgico');
const isUnread = message.pFlags.media_unread;
if(isUnread) {
divRound.classList.add('is-unread');
}
const canvas = document.createElement('canvas'); const canvas = document.createElement('canvas');
canvas.width = canvas.height = doc.w/* * window.devicePixelRatio */; canvas.width = canvas.height = doc.w/* * window.devicePixelRatio */;
@ -239,6 +246,10 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
video.classList.add('hide'); video.classList.add('hide');
divRound.classList.remove('is-paused'); divRound.classList.remove('is-paused');
animateSingle(onFrame, canvas); animateSingle(onFrame, canvas);
if(preloader && preloader.preloader && preloader.preloader.classList.contains('manual')) {
preloader.onClick();
}
}; };
const onPaused = () => { const onPaused = () => {
@ -352,10 +363,10 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
const cacheContext = appDownloadManager.getCacheContext(doc); const cacheContext = appDownloadManager.getCacheContext(doc);
const isUpload = !!message?.media?.preloader; const isUpload = !!(message?.media as any)?.preloader;
let preloader: ProgressivePreloader; let preloader: ProgressivePreloader;
if(isUpload) { // means upload if(isUpload) { // means upload
preloader = message.media.preloader as ProgressivePreloader; preloader = (message.media as any).preloader as ProgressivePreloader;
preloader.attach(container, false); preloader.attach(container, false);
noAutoDownload = undefined; noAutoDownload = undefined;
} else if(!cacheContext.downloaded && !doc.supportsStreaming) { } else if(!cacheContext.downloaded && !doc.supportsStreaming) {
@ -369,6 +380,44 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
}); });
} }
const renderDeferred = deferredPromise<void>();
video.addEventListener('error', (e) => {
if(video.error.code !== 4) {
console.error("Error " + video.error.code + "; details: " + video.error.message);
}
if(preloader && !isUpload) {
preloader.detach();
}
if(!renderDeferred.isFulfilled) {
renderDeferred.resolve();
}
}, {once: true});
onMediaLoad(video).then(() => {
if(group) {
animationIntersector.addAnimation(video, group);
}
if(preloader && !isUpload) {
preloader.detach();
}
renderDeferred.resolve();
});
if(doc.type === 'video') {
video.addEventListener('timeupdate', () => {
spanTime.innerText = (video.duration - video.currentTime + '').toHHMMSS(false);
});
}
video.muted = true;
video.loop = true;
//video.play();
video.autoplay = true;
let loadPhotoThumbFunc = noAutoDownload && photoRes?.preloader?.loadFunc; let loadPhotoThumbFunc = noAutoDownload && photoRes?.preloader?.loadFunc;
const load = () => { const load = () => {
if(preloader && noAutoDownload && !withoutPreloader) { if(preloader && noAutoDownload && !withoutPreloader) {
@ -393,20 +442,6 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
} }
} }
video.addEventListener('error', (e) => {
if(video.error.code !== 4) {
console.error("Error " + video.error.code + "; details: " + video.error.message);
}
if(preloader && !isUpload) {
preloader.detach();
}
if(!deferred.isFulfilled) {
deferred.resolve();
}
}, {once: true});
if(!noAutoDownload && loadPhotoThumbFunc) { if(!noAutoDownload && loadPhotoThumbFunc) {
loadPhotoThumbFunc(); loadPhotoThumbFunc();
loadPhotoThumbFunc = null; loadPhotoThumbFunc = null;
@ -414,10 +449,9 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
noAutoDownload = undefined; noAutoDownload = undefined;
const deferred = deferredPromise<void>();
loadPromise.then(() => { loadPromise.then(() => {
if(middleware && !middleware()) { if(middleware && !middleware()) {
deferred.resolve(); renderDeferred.resolve();
return; return;
} }
@ -425,33 +459,10 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
appMediaPlaybackController.resolveWaitingForLoadMedia(message.peerId, message.mid); appMediaPlaybackController.resolveWaitingForLoadMedia(message.peerId, message.mid);
} }
onVideoLoad(video).then(() => {
if(group) {
animationIntersector.addAnimation(video, group);
}
if(preloader && !isUpload) {
preloader.detach();
}
deferred.resolve();
});
if(doc.type === 'video') {
video.addEventListener('timeupdate', () => {
spanTime.innerText = (video.duration - video.currentTime + '').toHHMMSS(false);
});
}
video.muted = true;
video.loop = true;
//video.play();
video.autoplay = true;
renderImageFromUrl(video, cacheContext.url); renderImageFromUrl(video, cacheContext.url);
}, () => {}); }, () => {});
return {download: loadPromise, render: deferred}; return {download: loadPromise, render: renderDeferred};
}; };
if(preloader && !isUpload) { if(preloader && !isUpload) {

6
src/helpers/files.ts

@ -69,14 +69,14 @@ export async function createPosterForVideo(url: string) {
]); ]);
} }
export function onVideoLoad(video: HTMLVideoElement) { export function onMediaLoad(media: HTMLMediaElement, readyState = media.HAVE_METADATA, useCanplayOnIos?: boolean) {
return new Promise<void>((resolve) => { return new Promise<void>((resolve) => {
if(video.readyState >= video.HAVE_METADATA) { if(media.readyState >= readyState) {
resolve(); resolve();
return; return;
} }
video.addEventListener(IS_APPLE_MOBILE ? 'loadeddata' : 'canplay', () => resolve(), {once: true}); media.addEventListener(IS_APPLE_MOBILE && !useCanplayOnIos ? 'loadeddata' : 'canplay', () => resolve(), {once: true});
}); });
} }

26
src/helpers/schedulers.ts

@ -7,12 +7,7 @@
// * Jolly Cobra's schedulers // * Jolly Cobra's schedulers
import { NoneToVoidFunction } from "../types"; import { NoneToVoidFunction } from "../types";
//type Scheduler = typeof requestAnimationFrame | typeof onTickEnd | typeof runNow; /*
/* export function throttleWithRaf<F extends AnyToVoidFunction>(fn: F) {
return throttleWith(fastRaf, fn);
}
export function throttleWithTickEnd<F extends AnyToVoidFunction>(fn: F) { export function throttleWithTickEnd<F extends AnyToVoidFunction>(fn: F) {
return throttleWith(onTickEnd, fn); return throttleWith(onTickEnd, fn);
} }
@ -21,25 +16,6 @@ export function throttleWithNow<F extends AnyToVoidFunction>(fn: F) {
return throttleWith(runNow, fn); return throttleWith(runNow, fn);
} }
export function throttleWith<F extends AnyToVoidFunction>(schedulerFn: Scheduler, fn: F) {
let waiting = false;
let args: Parameters<F>;
return (..._args: Parameters<F>) => {
args = _args;
if (!waiting) {
waiting = true;
schedulerFn(() => {
waiting = false;
// @ts-ignore
fn(...args);
});
}
};
}
export function onTickEnd(cb: NoneToVoidFunction) { export function onTickEnd(cb: NoneToVoidFunction) {
Promise.resolve().then(cb); Promise.resolve().then(cb);
} }

22
src/helpers/schedulers/throttleWith.ts

@ -0,0 +1,22 @@
// * Jolly Cobra's schedulers
import { AnyToVoidFunction } from "../../types";
export default function throttleWith<F extends AnyToVoidFunction>(schedulerFn: AnyToVoidFunction, fn: F) {
let waiting = false;
let args: Parameters<F>;
return (..._args: Parameters<F>) => {
args = _args;
if (!waiting) {
waiting = true;
schedulerFn(() => {
waiting = false;
// @ts-ignore
fn(...args);
});
}
};
}

9
src/helpers/schedulers/throttleWithRaf.ts

@ -0,0 +1,9 @@
// * Jolly Cobra's schedulers
import { AnyToVoidFunction } from "../../types";
import { fastRaf } from "../schedulers";
import throttleWith from "./throttleWith";
export default function throttleWithRaf<F extends AnyToVoidFunction>(fn: F) {
return throttleWith(fastRaf, fn);
}

4
src/lib/appManagers/appDocsManager.ts

@ -369,6 +369,10 @@ export class AppDocsManager {
}); });
} }
download.then(() => {
rootScope.dispatchEvent('document_downloaded', doc);
});
return download; return download;
} }

4
src/lib/mediaPlayer.ts

@ -8,7 +8,7 @@ import appMediaPlaybackController from "../components/appMediaPlaybackController
import { IS_APPLE_MOBILE } from "../environment/userAgent"; import { IS_APPLE_MOBILE } from "../environment/userAgent";
import { IS_TOUCH_SUPPORTED } from "../environment/touchSupport"; import { IS_TOUCH_SUPPORTED } from "../environment/touchSupport";
import RangeSelector from "../components/rangeSelector"; import RangeSelector from "../components/rangeSelector";
import { onVideoLoad } from "../helpers/files"; import { onMediaLoad } from "../helpers/files";
import { cancelEvent } from "../helpers/dom/cancelEvent"; import { cancelEvent } from "../helpers/dom/cancelEvent";
import ListenerSetter from "../helpers/listenerSetter"; import ListenerSetter from "../helpers/listenerSetter";
import ButtonMenu from "../components/buttonMenu"; import ButtonMenu from "../components/buttonMenu";
@ -427,7 +427,7 @@ export default class VideoPlayer extends EventListenerBase<{
if(video.duration || initDuration) { if(video.duration || initDuration) {
timeDuration.innerHTML = String(Math.round(video.duration || initDuration)).toHHMMSS(); timeDuration.innerHTML = String(Math.round(video.duration || initDuration)).toHHMMSS();
} else { } else {
onVideoLoad(video).then(() => { onMediaLoad(video).then(() => {
timeDuration.innerHTML = String(Math.round(video.duration)).toHHMMSS(); timeDuration.innerHTML = String(Math.round(video.duration)).toHHMMSS();
}); });
} }

1
src/lib/rootScope.ts

@ -128,6 +128,7 @@ export type BroadcastEvents = {
'download_start': string, 'download_start': string,
'download_progress': any, 'download_progress': any,
'document_downloaded': MyDocument,
'context_menu_toggle': boolean 'context_menu_toggle': boolean
}; };

2
src/scss/partials/_audio.scss

@ -402,7 +402,7 @@
} }
&.active, &.active,
.audio.is-unread:not(.is-out) & { .audio.is-unread:not(.is-out) .audio-toggle:not(.playing) + & {
opacity: 1; opacity: 1;
} }
} }

17
src/scss/style.scss

@ -1244,15 +1244,30 @@ middle-ellipsis-element {
.video-time { .video-time {
padding: 0 .375rem; padding: 0 .375rem;
background-color: var(--message-highlightning-color) !important; background-color: var(--message-highlightning-color) !important;
&:before, &:after {
margin-left: .25rem;
}
}
&.is-unread .video-time {
&:before {
order: 1;
width: .5rem;
height: .5rem;
background-color: #fff;
border-radius: 50%;
content: " ";
}
} }
&.is-paused .video-time { &.is-paused .video-time {
&:after { &:after {
content: $tgico-nosound; content: $tgico-nosound;
padding-left: .25rem;
display: flex; display: flex;
align-items: center; align-items: center;
font-size: 1.125rem; font-size: 1.125rem;
order: 2;
} }
} }
} }

Loading…
Cancel
Save