UNPKG

@zohodesk/dot

Version:

In this Library, we Provide Some Basic Components to Build Your Application

580 lines (538 loc) 17 kB
import React from 'react'; import { Container, Box } from '@zohodesk/components/lib/Layout'; import Timer from "./Timer/Timer"; import Icon from '@zohodesk/icons/lib/Icon'; import { AudioPlayer_propTypes } from "./propTypes/propTypes"; import { AudioPlayer_defaultProps } from "./propTypes/defaultProps"; import { openInNewTabWithNoopener } from "./utils/utils"; import { getExtensionFromFileName } from "./../AttachmentViewer/Attachment"; import style from "./AudioPlayer.module.css"; export default class AudioPlayer extends React.Component { constructor(props) { super(props); this.audioPlayer = null; this.removeEventListeners = this.removeEventListeners.bind(this); this.setAudioPlayerRef = this.setAudioPlayerRef.bind(this); } componentDidMount() { const { AudioPlayerChild } = this.refs; this.audioPlayer.addEventListener('keydown', AudioPlayerChild.handleKeyDown); } componentWillUnmount() { this.removeEventListeners(); } removeEventListeners() { const { AudioPlayerChild } = this.refs; this.audioPlayer.removeEventListener('keydown', AudioPlayerChild.handleKeyDown); } setAudioPlayerRef(ele) { this.audioPlayer = ele; } render() { const { id, isPlay, onClose, src, timerFormat, forwardStepInSec, backwardStepInSec, getAudioDuration, needClose, needDownload, needMuteIcon, onPlay, onPause, onDownloading, onAudioSeeking, onAudioLoading, onError, onMuteUnmute, duration, i18nKeys, dataId, customClass, range } = this.props; return /*#__PURE__*/React.createElement(Container, { dataId: dataId, eleRef: this.setAudioPlayerRef, tabIndex: 0, isCover: false, className: `${customClass}` }, /*#__PURE__*/React.createElement(AudioPlayerChild, { ref: "AudioPlayerChild", id: id, src: src, isPlay: isPlay, onClose: onClose, timerFormat: timerFormat, duration: duration, forwardStepInSec: forwardStepInSec, backwardStepInSec: backwardStepInSec, getAudioDuration: getAudioDuration, needClose: needClose, needDownload: needDownload, needMuteIcon: needMuteIcon, onPlay: onPlay, onPause: onPause, onDownloading: onDownloading, onAudioSeeking: onAudioSeeking, onAudioLoading: onAudioLoading, onError: onError, onMuteUnmute: onMuteUnmute, i18nKeys: i18nKeys, dataId: dataId, range: range })); } } AudioPlayer.defaultProps = AudioPlayer_defaultProps; AudioPlayer.propTypes = AudioPlayer_propTypes; class AudioPlayerChild extends React.Component { constructor(props) { super(props); this.audio = null; this.intervalId = props.intervalId || ''; this.state = { isPlay: props.isPlay, currentHour: '00', currentMinute: '00', currentSecond: '00', sliderRangeValue: 0, audioHours: '00', audioMinutes: '00', audioSeconds: '00', overallSeconds: 0, minValue: 0, step: '1', audioType: 'audio/mpeg', isMuted: false, disableControlIcons: true, isShowDuration: false, disableRangeSlider: false, loadingRange: props.range || 0 }; // Bind all methods this.getAudioDetails = this.getAudioDetails.bind(this); this.onPlayAudio = this.onPlayAudio.bind(this); this.onPauseAudio = this.onPauseAudio.bind(this); this.togglePlayPause = this.togglePlayPause.bind(this); this.updatePlayer = this.updatePlayer.bind(this); this.updateRangeValue = this.updateRangeValue.bind(this); this.updateTimerValues = this.updateTimerValues.bind(this); this.togglePlayer = this.togglePlayer.bind(this); this.getAudioDurationDetails = this.getAudioDurationDetails.bind(this); this.browserCompatible = this.browserCompatible.bind(this); this.onMuteUnmute = this.onMuteUnmute.bind(this); this.handleAudioSeeking = this.handleAudioSeeking.bind(this); this.handleError = this.handleError.bind(this); this.handleAudioLoading = this.handleAudioLoading.bind(this); this.removeEventListeners = this.removeEventListeners.bind(this); this.downloadFile = this.downloadFile.bind(this); this.handleKeyDown = this.handleKeyDown.bind(this); this.handleClosePlayer = this.handleClosePlayer.bind(this); this.updateRange = this.updateRange.bind(this); this.setInputRangeRef = this.setInputRangeRef.bind(this); this.removeEvent = this.removeEvent.bind(this); } componentDidMount() { this.audio.addEventListener('loadedmetadata', this.getAudioDetails); this.audio.addEventListener('canplay', this.browserCompatible); this.audio.addEventListener('seeking', this.handleAudioSeeking); this.audio.addEventListener('waiting', this.handleAudioLoading); this.audio.addEventListener('error', this.handleError); this.audio.addEventListener('ended', this.removeEvent); } componentDidUpdate(prevProps) { if (prevProps.isPlay !== this.props.isPlay && this.props.isPlay === false) { this.onPauseAudio(); } } componentWillUnmount() { this.audio.pause(); clearInterval(this.intervalId); this.removeEventListeners(); } removeEventListeners() { this.audio.removeEventListener('loadedmetadata', this.getAudioDetails); this.audio.removeEventListener('canplay', this.browserCompatible); this.audio.removeEventListener('seeking', this.handleAudioSeeking); this.audio.removeEventListener('waiting', this.handleAudioLoading); this.audio.removeEventListener('error', this.handleError); this.audio.removeEventListener('ended', this.removeEvent); } removeEvent() { setTimeout(this.onPauseAudio, 1000); } getAudioDetails(event) { const { getAudioDuration, src, duration: fallbackDuration } = this.props; const audioType = getExtensionFromFileName(src); let durationCalc = Math.ceil(event.target.duration); const isDurationValid = Number.isFinite(durationCalc) && !Number.isNaN(durationCalc); durationCalc = isDurationValid ? durationCalc : fallbackDuration ? fallbackDuration : 0; const { hours, minutes, seconds } = this.getAudioDurationDetails(durationCalc); const isShowDuration = isDurationValid || fallbackDuration !== undefined; this.setState({ overallSeconds: durationCalc, audioHours: hours, audioMinutes: minutes, audioSeconds: seconds, audioType, disableControlIcons: false, isShowDuration, disableRangeSlider: !isDurationValid }); getAudioDuration && getAudioDuration(hours, minutes, seconds); } getAudioDurationDetails(valueInSec) { let hours = 0, minutes = 0, seconds = 0; if (valueInSec > 0) { hours = Math.floor(valueInSec / 3600); valueInSec %= 3600; minutes = Math.floor(valueInSec / 60); seconds = Math.floor(valueInSec % 60); } return { hours: hours <= 9 ? `0${hours}` : `${hours}`, minutes: minutes <= 9 ? `0${minutes}` : `${minutes}`, seconds: seconds <= 9 ? `0${seconds}` : `${seconds}` }; } togglePlayPause() { this.state.isPlay ? this.onPauseAudio() : this.onPlayAudio(); } onPlayAudio() { const { onPlay, id } = this.props; const { sliderRangeValue, overallSeconds } = this.state; const isRestart = sliderRangeValue === overallSeconds; this.audio.play(); this.intervalId = setInterval(this.updatePlayer, 1000); this.setState({ isPlay: true, isRestart }); onPlay && onPlay(id, 'playing'); } onPauseAudio() { const { onPause, id } = this.props; this.audio.pause(); clearInterval(this.intervalId); this.setState({ isPlay: false }); onPause && onPause(id, 'paused'); } handleClosePlayer() { const { onClose, id } = this.props; this.togglePlayer(); this.onPauseAudio(); onClose && onClose(id, 'closed'); } handleAudioSeeking() { const { onAudioSeeking } = this.props; onAudioSeeking && onAudioSeeking(this.audio.seeking); } handleAudioLoading() { const { onAudioLoading } = this.props; onAudioLoading && onAudioLoading(); } handleError() { const { onError } = this.props; onError && onError(this.audio.error.code); } browserCompatible(playable) { if (playable === '') alert('Your browser is unable to play the audio'); } onMuteUnmute(e) { e && e.preventDefault(); const { isMuted } = this.state; this.audio.muted = !isMuted; const { onMuteUnmute } = this.props; this.setState({ isMuted: !isMuted }); onMuteUnmute && onMuteUnmute(!isMuted); } setInputRangeRef(ele) { const { getRef } = this.props; this.inputRange = ele; getRef && getRef(ele); } updateRange(e) { this.props.updateRange(e.target.value); } updatePlayer(e) { this.updateRangeValue(e?.target?.value, this.updateTimerValues); } updateRangeValue(value, afterUpdateCallback) { let { isPlay, sliderRangeValue, overallSeconds, isRestart } = this.state; sliderRangeValue = Math.max(0, sliderRangeValue); let rangeValue = value || ++sliderRangeValue; if (rangeValue > overallSeconds && !isRestart) { if (isPlay) { this.audio.pause(); this.setState({ isPlay: false }); } this.audio.currentTime = overallSeconds; clearInterval(this.intervalId); rangeValue = overallSeconds; } if (isRestart) { this.audio.currentTime = 0; rangeValue = 0; this.setState({ sliderRangeValue: rangeValue, isRestart: false, isPlay: true }); } if (rangeValue <= overallSeconds) { value && (this.audio.currentTime = value); this.setState({ sliderRangeValue: rangeValue }, () => afterUpdateCallback(rangeValue)); } } updateTimerValues(sliderRangeValue) { const { hours, minutes, seconds } = this.getAudioDurationDetails(Number(sliderRangeValue)); this.setState({ currentHour: hours, currentMinute: minutes, currentSecond: seconds }); } togglePlayer() { this.audio.pause(); this.setState({ isPlay: false }); } handleKeyDown(e) { const { keyCode } = e; const { sliderRangeValue } = this.state; const { forwardStepInSec, backwardStepInSec } = this.props; if (keyCode === 32) this.togglePlayPause();else if (keyCode === 39) this.updatePlayer(sliderRangeValue + forwardStepInSec);else if (keyCode === 37) this.updatePlayer(sliderRangeValue - backwardStepInSec);else if (keyCode === 27) this.togglePlayer(); } downloadFile() { const { onDownloading, src, requestOptions } = this.props; const getFileName = (url, ext) => { const parts = url.split('/'); const name = parts[parts.length - 1].split('?')[0]; return name.includes('.') ? name : `${name}.${ext}`; }; const reqOptions = requestOptions || { credentials: 'include' }; fetch(src, reqOptions).then(resp => resp.blob()).then(blob => { const url = window.URL.createObjectURL(blob); const extnsn = blob.type.split('-')[1] || blob.type.split('/')[1]; const fileName = getFileName(src, extnsn); const a = document.createElement('a'); a.style.display = 'none'; a.href = url; a.download = fileName; document.body.appendChild(a); a.click(); window.URL.revokeObjectURL(url); }).catch(() => openInNewTabWithNoopener(src)); onDownloading && onDownloading(); } render() { const { isPlay, currentHour, currentMinute, currentSecond, audioHours, audioMinutes, audioSeconds, isMuted, disableControlIcons, isShowDuration, disableRangeSlider, sliderRangeValue, overallSeconds, minValue, step } = this.state; const { src, needDownload, needClose, needMuteIcon, id, i18nKeys, dataId } = this.props; const timerFormat = audioHours ? 'hh:mm:ss' : 'mm:ss'; const loadingRange = (sliderRangeValue - minValue) * 100 / (overallSeconds - minValue); const { playTitle = 'Play', pauseTitle = 'Pause', downloadTitle = 'Download', closeTitle = 'Close', muteTitle = 'Mute', unmuteTitle = 'Unmute' } = i18nKeys; return /*#__PURE__*/React.createElement(Container, { align: "vertical", alignBox: "row", className: style.container, isCover: false }, /*#__PURE__*/React.createElement("audio", { id: id, controls: true, preload: "auto", className: style.audioHid, ref: ele => this.audio = ele }, /*#__PURE__*/React.createElement("source", { src: src, type: this.state.audioType })), /*#__PURE__*/React.createElement(Box, { className: `${style.rightBox} ${style.downloadBox} ${style.boxLeftRadius}` }, /*#__PURE__*/React.createElement(Container, { className: disableControlIcons ? style.disable : '', align: "both", onClick: this.togglePlayPause, "data-title": isPlay ? pauseTitle : playTitle, dataId: `${dataId}_playpause` }, /*#__PURE__*/React.createElement(Icon, { name: isPlay ? 'ZD-commentTimerPause' : 'ZD-bcarr', iconClass: `${style.downloadIcon} ${style.iconColor}` }))), /*#__PURE__*/React.createElement(Box, { className: style.timerBox }, /*#__PURE__*/React.createElement(Container, { align: "vertical", alignBox: "row" }, /*#__PURE__*/React.createElement(Timer, { timerFormat: timerFormat, hour: currentHour, minute: currentMinute, second: currentSecond, separator: "colon", className: style.iconColor }), isShowDuration && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Box, { className: style.line }, /*#__PURE__*/React.createElement("span", null, "\xA0"), "/", /*#__PURE__*/React.createElement("span", null, "\xA0")), /*#__PURE__*/React.createElement(Timer, { timerFormat: timerFormat, hour: audioHours, minute: audioMinutes, second: audioSeconds, separator: "colon", className: style.iconColor })))), /*#__PURE__*/React.createElement(Box, { className: `${style.dragtag} ${disableControlIcons || disableRangeSlider ? style.disable : style.cursor}`, flexible: true }, /*#__PURE__*/React.createElement(Container, { className: style.audioRangeContainer }, /*#__PURE__*/React.createElement("span", { className: style.loading, style: { width: `${loadingRange}%` } }), /*#__PURE__*/React.createElement("input", { "data-id": dataId, ref: this.setInputRangeRef, className: style.input, type: "range", value: sliderRangeValue, min: minValue, max: overallSeconds, step: step, onChange: this.updatePlayer }))), needDownload && /*#__PURE__*/React.createElement(Box, { className: `${style.rightBox} ${style.downloadBox} ${!needClose && !needMuteIcon ? style.boxRightRadius : ''}` }, /*#__PURE__*/React.createElement(Container, { className: disableControlIcons ? style.disable : '', align: "both", onClick: this.downloadFile, "data-title": downloadTitle, dataId: `${dataId}_download` }, /*#__PURE__*/React.createElement(Icon, { name: "ZD-downloadNew", iconClass: `${style.downloadIcon} ${style.iconColor}` }))), needMuteIcon && /*#__PURE__*/React.createElement(Box, { className: `${style.rightBox} ${style.downloadBox} ${!needClose ? style.boxRightRadius : ''}` }, /*#__PURE__*/React.createElement(Container, { className: disableControlIcons ? style.disable : '', align: "both", onClick: this.onMuteUnmute, "data-title": isMuted ? unmuteTitle : muteTitle, dataId: `${dataId}_muteUnmute` }, /*#__PURE__*/React.createElement(Icon, { name: isMuted ? 'ZD-mute1New' : 'ZD-kbcatlogovolume', iconClass: `${style.downloadIcon} ${style.iconColor}` }))), needClose && /*#__PURE__*/React.createElement(Box, { className: `${style.closeBox} ${style.boxRightRadius}` }, /*#__PURE__*/React.createElement(Container, { className: disableControlIcons ? style.disable : '', align: "both", onClick: this.handleClosePlayer, "data-title": closeTitle, dataId: `${dataId}_close` }, /*#__PURE__*/React.createElement(Icon, { name: "ZD-cross", iconClass: `${style.pauseIcon} ${style.iconColor}` })))); } }