/* * https://github.com/morethanwords/tweb * Copyright (C) 2019-2021 Eduard Kuzmenko * https://github.com/morethanwords/tweb/blob/master/LICENSE */ import type { AppMessagesManager } from "../../lib/appManagers/appMessagesManager"; import type ChatTopbar from "./topbar"; import rootScope from "../../lib/rootScope"; import appMediaPlaybackController, { AppMediaPlaybackController } from "../appMediaPlaybackController"; import DivAndCaption from "../divAndCaption"; import PinnedContainer from "./pinnedContainer"; import Chat from "./chat"; import cancelEvent from "../../helpers/dom/cancelEvent"; import { attachClickEvent } from "../../helpers/dom/clickEvent"; import replaceContent from "../../helpers/dom/replaceContent"; import PeerTitle from "../peerTitle"; import { i18n } from "../../lib/langPack"; import { formatFullSentTime } from "../../helpers/date"; import ButtonIcon from "../buttonIcon"; import { DocumentAttribute } from "../../layer"; import MediaProgressLine from "../mediaProgressLine"; import VolumeSelector from "../volumeSelector"; import wrapEmojiText from "../../lib/richTextProcessor/wrapEmojiText"; import { AppManagers } from "../../lib/appManagers/managers"; export default class ChatAudio extends PinnedContainer { private toggleEl: HTMLElement; private progressLine: MediaProgressLine; private volumeSelector: VolumeSelector; private fasterEl: HTMLElement; private repeatEl: HTMLButtonElement; constructor(protected topbar: ChatTopbar, protected chat: Chat, protected managers: AppManagers) { super({ topbar, chat, listenerSetter: topbar.listenerSetter, className: 'audio', divAndCaption: new DivAndCaption( 'pinned-audio', (title: string | HTMLElement | DocumentFragment, subtitle: string | HTMLElement | DocumentFragment) => { replaceContent(this.divAndCaption.title, title); replaceContent(this.divAndCaption.subtitle, subtitle); } ), onClose: () => { appMediaPlaybackController.stop(); }, floating: true }); this.divAndCaption.border.remove(); const prevEl = ButtonIcon('fast_rewind active', {noRipple: true}); const nextEl = ButtonIcon('fast_forward active', {noRipple: true}); const attachClick = (elem: HTMLElement, callback: () => void) => { attachClickEvent(elem, (e) => { cancelEvent(e); callback(); }, {listenerSetter: this.topbar.listenerSetter}); }; attachClick(prevEl, () => { appMediaPlaybackController.previous(); }); attachClick(nextEl, () => { appMediaPlaybackController.next(); }); this.toggleEl = ButtonIcon('', {noRipple: true}); this.toggleEl.classList.add('active', 'pinned-audio-ico', 'tgico'); attachClick(this.toggleEl, () => { appMediaPlaybackController.toggle(); }); this.wrapper.prepend(this.wrapper.firstElementChild, prevEl, this.toggleEl, nextEl); this.volumeSelector = new VolumeSelector(this.listenerSetter, true); const volumeProgressLineContainer = document.createElement('div'); volumeProgressLineContainer.classList.add('progress-line-container'); volumeProgressLineContainer.append(this.volumeSelector.container); const tunnel = document.createElement('div'); tunnel.classList.add('pinned-audio-volume-tunnel'); this.volumeSelector.btn.classList.add('pinned-audio-volume', 'active'); this.volumeSelector.btn.prepend(tunnel); this.volumeSelector.btn.append(volumeProgressLineContainer); this.repeatEl = ButtonIcon('audio_repeat', {noRipple: true}); attachClick(this.repeatEl, () => { const params = appMediaPlaybackController.getPlaybackParams(); if(!params.round) { appMediaPlaybackController.round = true; } else if(params.loop) { appMediaPlaybackController.round = false; appMediaPlaybackController.loop = false; } else { appMediaPlaybackController.loop = !appMediaPlaybackController.loop; } }); const fasterEl = this.fasterEl = ButtonIcon('playback_2x', {noRipple: true}); attachClick(fasterEl, () => { appMediaPlaybackController.playbackRate = fasterEl.classList.contains('active') ? 1 : 1.75; }); this.wrapperUtils.prepend(this.volumeSelector.btn, fasterEl, this.repeatEl); const progressWrapper = document.createElement('div'); progressWrapper.classList.add('pinned-audio-progress-wrapper'); this.progressLine = new MediaProgressLine(undefined, undefined, true, true); this.progressLine.container.classList.add('pinned-audio-progress'); progressWrapper.append(this.progressLine.container); this.wrapper.insertBefore(progressWrapper, this.wrapperUtils); this.topbar.listenerSetter.add(appMediaPlaybackController)('play', this.onMediaPlay); this.topbar.listenerSetter.add(appMediaPlaybackController)('pause', this.onPause); this.topbar.listenerSetter.add(appMediaPlaybackController)('stop', this.onStop); this.topbar.listenerSetter.add(appMediaPlaybackController)('playbackParams', this.onPlaybackParams); const playingDetails = appMediaPlaybackController.getPlayingDetails(); if(playingDetails) { this.onMediaPlay(playingDetails); this.onPlaybackParams(playingDetails.playbackParams); } } public destroy() { if(this.progressLine) { this.progressLine.removeListeners(); } } private onPlaybackParams = (playbackParams: ReturnType) => { this.fasterEl.classList.toggle('active', playbackParams.playbackRate > 1); this.repeatEl.classList.remove('tgico-audio_repeat', 'tgico-audio_repeat_single'); this.repeatEl.classList.add(playbackParams.loop ? 'tgico-audio_repeat_single' : 'tgico-audio_repeat'); this.repeatEl.classList.toggle('active', playbackParams.loop || playbackParams.round); }; private onPause = () => { this.toggleEl.classList.remove('flip-icon'); }; private onStop = () => { this.toggle(true); }; private onMediaPlay = ({doc, message, media, playbackParams}: ReturnType) => { let title: string | HTMLElement | DocumentFragment, subtitle: string | HTMLElement | DocumentFragment; const isMusic = doc.type !== 'voice' && doc.type !== 'round'; if(!isMusic) { title = new PeerTitle({peerId: message.fromId, fromName: message.fwd_from?.from_name}).element; //subtitle = 'Voice message'; subtitle = formatFullSentTime(message.date); } else { const audioAttribute = doc.attributes.find((attr) => attr._ === 'documentAttributeAudio') as DocumentAttribute.documentAttributeAudio; title = wrapEmojiText(audioAttribute?.title ?? doc.file_name); subtitle = audioAttribute?.performer ? wrapEmojiText(audioAttribute.performer) : i18n('AudioUnknownArtist'); } this.fasterEl.classList.toggle('hide', isMusic); this.repeatEl.classList.toggle('hide', !isMusic); this.onPlaybackParams(playbackParams); this.volumeSelector.setVolume(); this.progressLine.setMedia(media); this.fill(title, subtitle, message); // this.toggleEl.classList.add('flip-icon'); this.toggleEl.classList.toggle('flip-icon', !media.paused); this.toggle(false); }; }