Skip to content
This repository was archived by the owner on Jun 28, 2021. It is now read-only.
Prev Previous commit
Next Next commit
move logic outside of tracker
  • Loading branch information
mmahalwy committed Jun 25, 2016
commit d1a18bc3c6e4d0debde8c6e4e393cee8335ecee0
151 changes: 7 additions & 144 deletions src/components/Audioplayer/Track/index.js
Original file line number Diff line number Diff line change
@@ -1,163 +1,26 @@
import React, { Component, PropTypes } from 'react';
import ReactDOM from 'react-dom';

const style = require('./style.scss');

export default class Track extends Component {
static propTypes = {
file: PropTypes.object.isRequired,
isStarted: PropTypes.bool.isRequired,
doStop: PropTypes.func,
onEnd: PropTypes.func.isRequired,
shouldRepeat: PropTypes.bool.isRequired,
surah: PropTypes.object.isRequired,
currentAyah: PropTypes.string.isRequired
progress: PropTypes.number.isRequired,
onTrackChange: PropTypes.func.isRequired
};

state = {
progress: 0,
currentTime: 0,
listeners: {}
};

componentDidMount() {
this.setState({ mounted: true }); // eslint-disable-line react/no-did-mount-set-state
if (this.props.file && __CLIENT__) {
this.onFileLoad(this.props.file);
}
}

shouldComponentUpdate(nextProps, nextState) {
return [
this.props.file.src !== nextProps.file.src,
this.props.isStarted !== nextProps.isStarted,
this.props.shouldRepeat !== nextProps.shouldRepeat,
this.state.progress !== nextState.progress, // do we need this??
this.state.currentTime !== nextState.currentTime // do we need this??
].some(test => test);
}

componentDidUpdate(prevProps) {
if (this.props.file.src !== prevProps.file.src) {
if (!prevProps.file.paused) {
prevProps.file.pause();
}
prevProps.file.currentTime = 0; // eslint-disable-line no-param-reassign

this.onFileUnload(prevProps.file);
this.setState({ progress: 0 }); // eslint-disable-line react/no-did-update-set-state
this.onFileLoad(this.props.file);

}
}

componentWillUnmount() {
this.setState({ mounted: false }); // TODO, yeah, this is bad but we unmount and mount when
this.state.mounted = false; // we lazy load, and it causes problems...
// trace memory profile count
this.onFileUnload(this.props.file);
}

onFileLoad(file) {
// Preload file
file.setAttribute('preload', 'auto');

const loadeddata = () => {
// Default current time to zero. This will change
file.currentTime = 0; // eslint-disable-line no-param-reassign
};
file.onloadeddata = loadeddata; // eslint-disable-line no-param-reassign

const timeupdate = () => {
if (!this.state.mounted) return; // TODO needed?

const progress = (
file.currentTime /
file.duration * 100
);

this.setState({ progress });
};

file.ontimeupdate = timeupdate; // eslint-disable-line no-param-reassign

const ended = () => {
const { shouldRepeat, onEnd, isStarted, doStop, currentAyah, surah } = this.props;

// if we're on the last ayah, do a full stop at the playback end
if (currentAyah === `${surah.id}:${surah.ayat}`) return doStop();

if (isStarted && shouldRepeat) {
file.pause();
file.currentTime = 0; // eslint-disable-line no-param-reassign
file.play();
} else {
onEnd();
}

return false;
};

file.onended = ended; // eslint-disable-line no-param-reassign

const play = () => {
if (!this.state.mounted) return;

const { progress } = this.state;
const currentTime = progress / 100 * file.duration;

this.setState({ currentTime });
};

file.onplay = play; // eslint-disable-line no-param-reassign

return false;
}

onFileUnload(file) {
if (!file.paused) {
file.pause();
}
}

onTrackerMove = (event) => {
const { file } = this.props;
handleClick = (event) => {
const { onTrackChange } = this.props;

const fraction = (
event.nativeEvent.offsetX /
ReactDOM.findDOMNode(this).parentElement.getBoundingClientRect().width
this.refs.container.getBoundingClientRect().width
);

this.setState({
progress: fraction * 100,
currentTime: fraction * file.duration
});

file.currentTime = (
fraction * file.duration
);
return onTrackChange(fraction);
}

render() {
const { progress } = this.state;
const { isStarted, file } = this.props;

if (file.readyState >= 3) {
// the Math.round bit prevents us from trying to play again when we're
// effectively at the end of the audio file; this should allow
// shouldRepeat to work without getting overridden:
// ...but at the time I monkey-patched it, so we might be able to get
// rid of it since we cleaned up? Let's not for now...
if (
isStarted &&
file.paused &&
file.readyState >= 3 &&
Math.round(file.currentTime) !== Math.round(file.duration)) {
file.play();
} else if (!isStarted && !file.paused) {
file.pause();
}
}
const { progress } = this.props;

return (
<div className={style.track} onClick={this.onTrackerMove}>
Expand Down
112 changes: 86 additions & 26 deletions src/components/Audioplayer/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,10 @@ import scroller from '../../utils/scroller';
const style = require('./style.scss');

@connect(
state => ({
files: state.audioplayer.files,
segments: state.audioplayer.segments,
(state, ownProps) => ({
files: state.audioplayer.files[ownProps.surah.id],
segments: state.audioplayer.segments[ownProps.surah.id],
ayahIds: Object.keys(state.audioplayer.files[ownProps.surah.id]),
currentAyah: state.ayahs.current,
currentWord: state.ayahs.currentWord,
surahId: state.audioplayer.surahId,
Expand All @@ -48,25 +49,8 @@ const style = require('./style.scss');
toggleScroll,
setCurrentAyah,
setCurrentWord,
buildOnClient
},
(stateProps, dispatchProps, ownProps) => {
if (!stateProps.isSupported) {
return {
...stateProps, ...dispatchProps, ...ownProps
};
}

const files = stateProps.files[stateProps.surahId];
const ayahIds = files ? Object.keys(files) : [];
const segments = stateProps.segments[stateProps.surahId];

return {
...stateProps, ...dispatchProps, ...ownProps,
files,
segments,
ayahIds
};
buildOnClient,
update
}
)
export default class Audioplayer extends Component {
Expand All @@ -87,37 +71,50 @@ export default class Audioplayer extends Component {
setCurrentWord: PropTypes.func.isRequired,
start: PropTypes.func.isRequired,
stop: PropTypes.func.isRequired,
update: PropTypes.func.isRequired,
toggleRepeat: PropTypes.func.isRequired,
toggleScroll: PropTypes.func.isRequired,
ayahIds: PropTypes.array,
isStarted: PropTypes.bool,
progress: PropTypes.number,
currentTime: PropTypes.number,
duration: PropTypes.number
duration: PropTypes.number,
currentFile: PropTypes.string
};

static defaultProps = {
className: 'col-md-3'
};

componentDidMount() {
const { isLoadedOnClient, buildOnClient, surah } = this.props; // eslint-disable-line no-shadow
const { isLoadedOnClient, buildOnClient, surah, files, currentFile } = this.props; // eslint-disable-line no-shadow, max-len

debug('component:Audioplayer', 'componentDidMount');

if (!isLoadedOnClient && __CLIENT__) {
debug('component:Audioplayer', 'componentDidMount on client');

return buildOnClient(surah.id);
buildOnClient(surah.id);

if (files[currentFile]) {
return this.handleAddFileListeners(files[currentFile]);
}

return false;
}

return false;
}

componentWillUnmount() {
const { files, currentFile } = this.props;
debug('component:Audioplayer', 'componentWillUnmount');

return stop();
if (files[currentFile]) {
return this.handleRemoveFileListeneres(files[currentFile]);
}

return false;
}

getCurrentAyah() {
Expand Down Expand Up @@ -242,6 +239,69 @@ export default class Audioplayer extends Component {
this.props.toggleScroll();
}

handleAddFileListeners(file) {
const { update } = this.props; // eslint-disable-line no-shadow

// Preload file
file.setAttribute('preload', 'auto');

const onLoadeddata = () => {
// Default current time to zero. This will change
file.currentTime = 0; // eslint-disable-line no-param-reassign

return update({
duration: file.duration
});
};

const onTimeupdate = () => {
const progress = (
file.currentTime /
file.duration * 100
);

return update({
progress,
currentTime: file.currentTime
});
};

const onEnded = () => {
const { shouldRepeat } = this.props;

if (shouldRepeat) {
file.pause();
file.currentTime = 0; // eslint-disable-line no-param-reassign
return file.play();
}

if (file.readyState >= 3 && file.paused) {
file.pause();
}

return update({
isPlaying: false
});
};

const onPlay = () => {};

file.onloadeddata = onLoadeddata; // eslint-disable-line no-param-reassign
file.ontimeupdate = onTimeupdate; // eslint-disable-line no-param-reassign
file.onplay = onPlay; // eslint-disable-line no-param-reassign
file.onended = onEnded; // eslint-disable-line no-param-reassign

return file;
}

handleRemoveFileListeneres(file) {
file.pause();
file.onloadeddata = null; // eslint-disable-line no-param-reassign
file.ontimeupdate = null; // eslint-disable-line no-param-reassign
file.onplay = null; // eslint-disable-line no-param-reassign
file.onended = null; // eslint-disable-line no-param-reassign
}

renderPlayStopButtons() {
const { isStarted, stop } = this.props; // eslint-disable-line no-shadow

Expand Down
12 changes: 8 additions & 4 deletions src/redux/modules/audioplayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,19 +39,23 @@ export default function reducer(state = initialState, action = {}) {
switch (action.type) {
case BUILD_ON_CLIENT:
audioFromHash = buildAudioFromHash(state.files[action.surahId], state.userAgent);
files = Object.assign({}, state.files[action.surahId], audioFromHash.files);
segments = Object.assign({}, state.segments[action.surahId], audioFromHash.segments);

return {
...state,
isLoadedOnClient: true,
files: {
...state.files,
[action.surahId]: files
[action.surahId]: {
...state.files[action.surahId],
...audioFromHash.files
}
},
segments: {
...state.segments,
[action.surahId]: segments
[action.surahId]: {
...state.segments[action.surahId],
...audioFromHash.segments
}
}
};
case AYAHS_CLEAR_CURRENT:
Expand Down