diff --git a/src/components/appMediaPlaybackController.ts b/src/components/appMediaPlaybackController.ts index a277eae7..553457e4 100644 --- a/src/components/appMediaPlaybackController.ts +++ b/src/components/appMediaPlaybackController.ts @@ -4,6 +4,7 @@ import appDocsManager, {MyDocument} from "../lib/appManagers/appDocsManager"; import { CancellablePromise, deferredPromise } from "../helpers/cancellablePromise"; import { isSafari } from "../helpers/userAgent"; import { MOUNT_CLASS_TO } from "../lib/mtproto/mtproto_config"; +import { isInDOM } from "../helpers/dom"; // TODO: если удалить сообщение, и при этом аудио будет играть - оно не остановится, и можно будет по нему перейти вникуда @@ -177,6 +178,13 @@ class AppMediaPlaybackController { } onPause = (e: Event) => { + /* const target = e.target as HTMLMediaElement; + if(!isInDOM(target)) { + this.container.append(target); + target.play(); + return; + } */ + rootScope.broadcast('audio_pause'); }; diff --git a/src/components/preloader.ts b/src/components/preloader.ts index e07bf8a4..0c57002d 100644 --- a/src/components/preloader.ts +++ b/src/components/preloader.ts @@ -237,6 +237,8 @@ export default class ProgressivePreloader { } public detach() { + //return; + this.detached = true; //return; diff --git a/src/components/wrappers.ts b/src/components/wrappers.ts index 98d1f80d..070c09ba 100644 --- a/src/components/wrappers.ts +++ b/src/components/wrappers.ts @@ -1,3 +1,4 @@ +import type Chat from './chat/chat'; import { getEmojiToneIndex } from '../emoji'; import { readBlobAsText } from '../helpers/blob'; import { deferredPromise } from '../helpers/cancellablePromise'; @@ -7,7 +8,6 @@ import { formatBytes } from '../helpers/number'; import { isAppleMobile, isSafari } from '../helpers/userAgent'; import { PhotoSize } from '../layer'; import appDocsManager, { MyDocument } from "../lib/appManagers/appDocsManager"; -import { DownloadBlob } from '../lib/appManagers/appDownloadManager'; import appMessagesManager from '../lib/appManagers/appMessagesManager'; import appPhotosManager, { MyPhoto } from '../lib/appManagers/appPhotosManager'; import LottieLoader from '../lib/lottieLoader'; @@ -27,9 +27,9 @@ import './middleEllipsis'; import { nextRandomInt } from '../helpers/random'; import RichTextProcessor from '../lib/richtextprocessor'; import appImManager from '../lib/appManagers/appImManager'; -import Chat from './chat/chat'; import { SearchSuperContext } from './appSearchSuper.'; import rootScope from '../lib/rootScope'; +import { onVideoLoad } from '../helpers/files'; const MAX_VIDEO_AUTOPLAY_SIZE = 50 * 1024 * 1024; // 50 MB @@ -108,28 +108,28 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai video.remove(); } */ - const video = document.createElement('video'); + const video = /* doc.type === 'round' ? appMediaPlaybackController.addMedia(message.peerId, doc, message.mid) as HTMLVideoElement : */document.createElement('video'); video.classList.add('media-video'); video.muted = true; video.setAttribute('playsinline', 'true'); if(doc.type === 'round') { - //video.muted = true; + //video.classList.add('z-depth-1'); const globalVideo = appMediaPlaybackController.addMedia(message.peerId, doc, message.mid); video.classList.add('z-depth-1'); - video.addEventListener('canplay', () => { - if(globalVideo.currentTime > 0) { + onVideoLoad(video).then(() => { + if(globalVideo.currentTime !== globalVideo.duration) { video.currentTime = globalVideo.currentTime; } if(!globalVideo.paused) { // с закоментированными настройками - хром выключал видео при скролле, для этого нужно было включить видео - выйти из диалога, зайти заново и проскроллить вверх - /* video.autoplay = true; - video.loop = false; */ + //video.autoplay = true; + //video.loop = false; video.play(); } - }, {once: true}); + }); const clear = () => { //console.log('clearing video'); @@ -160,14 +160,18 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai const onVideoPlay = (e: Event) => { //console.log('video play event', e); - globalVideo.currentTime = video.currentTime; - globalVideo.play(); + if(globalVideo.paused) { + globalVideo.currentTime = video.currentTime; + globalVideo.play(); + } }; + // * this will fire when video unmounts const onVideoPause = (e: Event) => { //console.trace('video pause event', e); if(isInDOM(video)) { globalVideo.pause(); + globalVideo.currentTime = video.currentTime; } else { clear(); } @@ -229,22 +233,25 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai return; } + let loadPromise: Promise = Promise.resolve(); let preloader: ProgressivePreloader; if(message?.media?.preloader) { // means upload preloader = message.media.preloader as ProgressivePreloader; preloader.attach(container, false); } else if(!doc.downloaded && !doc.supportsStreaming) { - const promise = appDocsManager.downloadDoc(doc, lazyLoadQueue?.queueId); + const promise = loadPromise = appDocsManager.downloadDoc(doc, lazyLoadQueue?.queueId); preloader = new ProgressivePreloader({ attachMethod: 'prepend' }); preloader.attach(container, true, promise); - await promise; + //if(doc.type !== 'round') { + await promise; - if(middleware && !middleware()) { - return; - } + if(middleware && !middleware()) { + return; + } + //} } else if(doc.supportsStreaming) { preloader = new ProgressivePreloader({ cancelable: false, @@ -255,13 +262,17 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai preloader.detach(); }, {once: true}); } + + /* if(doc.type === 'round') { + return; + } */ //console.log('loaded doc:', doc, doc.url, container); const deferred = deferredPromise(); //if(doc.type == 'gif'/* || true */) { - video.addEventListener(isAppleMobile ? 'loadeddata' : 'canplay', () => { + onVideoLoad(video).then(() => { /* if(!video.paused) { video.pause(); } */ @@ -273,7 +284,7 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai //setTimeout(() => { deferred.resolve(); //}, 5000); - }, {once: true}); + }); //} if(doc.type === 'video') { @@ -286,22 +297,24 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai deferred.resolve(); }); - renderImageFromUrl(video, doc.url); - - if(doc.type === 'round') { - video.dataset.ckin = 'circle'; - video.dataset.overlay = '1'; - new VideoPlayer(video); - } else { + if(doc.type !== 'round') { video.muted = true; video.loop = true; //video.play(); video.autoplay = true; } + + renderImageFromUrl(video, doc.url); - return deferred; + return Promise.all([loadPromise, deferred]); }; + if(doc.type === 'round') { + video.dataset.ckin = 'circle'; + video.dataset.overlay = '1'; + new VideoPlayer(video, undefined, undefined, doc.duration); + } + /* if(doc.size >= 20e6 && !doc.downloaded) { let downloadDiv = document.createElement('div'); downloadDiv.classList.add('download'); diff --git a/src/helpers/animation.ts b/src/helpers/animation.ts index 5485e77a..bfeea441 100644 --- a/src/helpers/animation.ts +++ b/src/helpers/animation.ts @@ -11,6 +11,22 @@ interface AnimationInstance { type AnimationInstanceKey = any; const instances: Map = new Map(); +export function createAnimationInstance(key: AnimationInstanceKey) { + cancelAnimationByKey(key); + + const instance: AnimationInstance = { + isCancelled: false, + deferred: deferredPromise() + }; + + instances.set(key, instance); + instance.deferred.then(() => { + instances.delete(key); + }); + + return instance; +} + export function getAnimationInstance(key: AnimationInstanceKey) { return instances.get(key); } @@ -20,15 +36,12 @@ export function cancelAnimationByKey(key: AnimationInstanceKey) { if(instance) { instance.isCancelled = true; instance.deferred.resolve(); - instances.delete(key); } } export function animateSingle(tick: Function, key: AnimationInstanceKey, instance?: AnimationInstance) { if(!instance) { - cancelAnimationByKey(key); - instance = { isCancelled: false, deferred: deferredPromise() }; - instances.set(key, instance); + instance = createAnimationInstance(key); } fastRaf(() => { diff --git a/src/helpers/files.ts b/src/helpers/files.ts index b86929c5..c5b5432f 100644 --- a/src/helpers/files.ts +++ b/src/helpers/files.ts @@ -1,4 +1,5 @@ import { pause } from "./schedulers"; +import { isAppleMobile } from "./userAgent"; export function preloadVideo(url: string): Promise { return new Promise((resolve, reject) => { @@ -35,4 +36,15 @@ export async function createPosterForVideo(url: string): Promise, createPosterFromVideo(video), ]); +} + +export function onVideoLoad(video: HTMLVideoElement) { + return new Promise((resolve) => { + if(video.readyState >= video.HAVE_METADATA) { + resolve(); + return; + } + + video.addEventListener(isAppleMobile ? 'loadeddata' : 'canplay', () => resolve(), {once: true}); + }); } \ No newline at end of file diff --git a/src/lib/cacheStorage.ts b/src/lib/cacheStorage.ts index 1a0fa55e..8dbad495 100644 --- a/src/lib/cacheStorage.ts +++ b/src/lib/cacheStorage.ts @@ -82,7 +82,7 @@ export default class CacheStorageController { reject(); //console.warn('CACHESTORAGE TIMEOUT'); rejected = true; - }, 5e3); + }, 15e3); try { const cache = await this.openDatabase(); diff --git a/src/lib/mediaPlayer.ts b/src/lib/mediaPlayer.ts index 3ae3047c..aebd680d 100644 --- a/src/lib/mediaPlayer.ts +++ b/src/lib/mediaPlayer.ts @@ -1,8 +1,10 @@ -import { cancelEvent } from "../helpers/dom"; +import { attachClickEvent, cancelEvent } from "../helpers/dom"; import appMediaPlaybackController from "../components/appMediaPlaybackController"; import { isAppleMobile } from "../helpers/userAgent"; import { isTouchSupported } from "../helpers/touchSupport"; import RangeSelector from "../components/rangeSelector"; +import { animateSingle } from "../helpers/animation"; +import { onVideoLoad } from "../helpers/files"; type SUPEREVENT = MouseEvent | TouchEvent; @@ -167,7 +169,7 @@ export default class VideoPlayer { /* private videoParent: HTMLElement; private videoWhichChild: number; */ - constructor(public video: HTMLVideoElement, play = false, streamable = false) { + constructor(public video: HTMLVideoElement, play = false, streamable = false, duration?: number) { this.wrapper = document.createElement('div'); this.wrapper.classList.add('ckin__player'); @@ -176,7 +178,7 @@ export default class VideoPlayer { this.skin = video.dataset.ckin ?? 'default'; - this.stylePlayer(); + this.stylePlayer(duration); if(this.skin === 'default') { let controls = this.wrapper.querySelector('.default__controls.ckin__controls') as HTMLDivElement; @@ -199,7 +201,7 @@ export default class VideoPlayer { } } - private stylePlayer() { + private stylePlayer(initDuration: number) { const {wrapper: player, video, skin} = this; player.classList.add(skin); @@ -365,11 +367,12 @@ export default class VideoPlayer { const circle = player.querySelector('.progress-ring__circle') as SVGCircleElement; const radius = circle.r.baseVal.value; const circumference = 2 * Math.PI * radius; - timeDuration = player.querySelector('.circle-time') as HTMLElement; - const iconVolume = player.querySelector('.iconVolume') as HTMLDivElement; + timeDuration = wrapper.firstElementChild as HTMLElement; + const iconVolume = wrapper.lastElementChild as HTMLElement; circle.style.strokeDasharray = circumference + ' ' + circumference; circle.style.strokeDashoffset = '' + circumference; - circle.addEventListener('click', () => { + attachClickEvent(circle as any, (e) => { + cancelEvent(e); this.togglePlay(); }); @@ -377,24 +380,33 @@ export default class VideoPlayer { const offset = circumference - video.currentTime / video.duration * circumference; circle.style.strokeDashoffset = '' + offset; - if(video.paused) { - clearInterval(updateInterval); - } + return !video.paused; + }; + + const timeUpdate = () => { + const timeLeft = String((video.duration - video.currentTime) | 0).toHHMMSS(); + if(timeLeft != '0') timeDuration.innerHTML = timeLeft; }; video.addEventListener('play', () => { iconVolume.style.display = 'none'; - updateInterval = window.setInterval(update, 20); + + animateSingle(update, circle); + update(); }); video.addEventListener('pause', () => { iconVolume.style.display = ''; }); - let updateInterval = 0; video.addEventListener('timeupdate', () => { - const timeLeft = String((video.duration - video.currentTime) | 0).toHHMMSS(); - if(timeLeft != '0') timeDuration.innerHTML = timeLeft; + timeUpdate(); + }); + + onVideoLoad(video).then(() => { + if(!video.currentTime || video.currentTime === video.duration) return; + update(); + timeUpdate(); }); } @@ -406,10 +418,10 @@ export default class VideoPlayer { this.wrapper.classList.remove('is-playing'); }); - if(video.duration > 0) { - timeDuration.innerHTML = String(Math.round(video.duration)).toHHMMSS(); + if(video.duration || initDuration) { + timeDuration.innerHTML = String(Math.round(video.duration || initDuration)).toHHMMSS(); } else { - video.addEventListener('loadeddata', () => { + onVideoLoad(video).then(() => { timeDuration.innerHTML = String(Math.round(video.duration)).toHHMMSS(); }); } diff --git a/src/scss/partials/_avatar.scss b/src/scss/partials/_avatar.scss index 31950875..20b6644c 100644 --- a/src/scss/partials/_avatar.scss +++ b/src/scss/partials/_avatar.scss @@ -78,8 +78,8 @@ avatar-element { } &.emoji { - width: calc(1.125rem / var(--multiplier)); - height: calc(1.125rem / var(--multiplier)); + width: calc(1.125rem / var(--multiplier)) !important; + height: calc(1.125rem / var(--multiplier)) !important; vertical-align: middle !important; } } diff --git a/src/scss/partials/_chatBubble.scss b/src/scss/partials/_chatBubble.scss index 912f8778..aaf0a2e2 100644 --- a/src/scss/partials/_chatBubble.scss +++ b/src/scss/partials/_chatBubble.scss @@ -667,13 +667,6 @@ $bubble-margin: .25rem; } } - &.round { - .attachment { - max-height: 200px; - max-width: 200px; - } - } - .web { padding-top: 1px; margin: 4px 0 -5px 1px;