@zohodesk/dot
Version:
In this Library, we Provide Some Basic Components to Build Your Application
580 lines (538 loc) • 17 kB
JavaScript
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}`
}))));
}
}