2018-02-26 19:37:12 +00:00
|
|
|
// @flow
|
|
|
|
|
|
|
|
import React, { Component } from 'react';
|
|
|
|
import { connect } from 'react-redux';
|
|
|
|
|
|
|
|
import { Audio } from '../../media';
|
|
|
|
import type { AudioElement } from '../../media';
|
|
|
|
|
|
|
|
import { _addAudioElement, _removeAudioElement } from '../actions';
|
|
|
|
import type { Sound } from '../reducer';
|
|
|
|
|
|
|
|
/**
|
|
|
|
* {@link SoundCollection}'s properties.
|
|
|
|
*/
|
|
|
|
type Props = {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Dispatches {@link _ADD_AUDIO_ELEMENT} Redux action which will store the
|
|
|
|
* {@link AudioElement} for a sound in the Redux store.
|
|
|
|
*/
|
|
|
|
_addAudioElement: Function,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Dispatches {@link _REMOVE_AUDIO_ELEMENT} Redux action which will remove
|
|
|
|
* the sound's {@link AudioElement} from the Redux store.
|
|
|
|
*/
|
|
|
|
_removeAudioElement: Function,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* It's the 'base/sounds' reducer's state mapped to a property. It's used to
|
|
|
|
* render audio elements for every registered sound.
|
|
|
|
*/
|
|
|
|
_sounds: Map<string, Sound>
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Collections of all global sounds used by the app for playing audio
|
|
|
|
* notifications in response to various events. It renders <code>Audio</code>
|
|
|
|
* element for each sound registered in the base/sounds feature. When the audio
|
|
|
|
* resource is loaded it will emit add/remove audio element actions which will
|
|
|
|
* attach the element to the corresponding {@link Sound} instance in the Redux
|
|
|
|
* state. When that happens the sound can be played using the {@link playSound}
|
|
|
|
* action.
|
|
|
|
*/
|
|
|
|
class SoundCollection extends Component<Props> {
|
|
|
|
/**
|
|
|
|
* Implements React's {@link Component#render()}.
|
|
|
|
*
|
|
|
|
* @inheritdoc
|
|
|
|
* @returns {ReactElement}
|
|
|
|
*/
|
|
|
|
render() {
|
|
|
|
let key = 0;
|
|
|
|
const sounds = [];
|
|
|
|
|
|
|
|
for (const [ soundId, sound ] of this.props._sounds.entries()) {
|
2018-05-16 15:03:10 +00:00
|
|
|
const { options, src } = sound;
|
|
|
|
|
2018-02-26 19:37:12 +00:00
|
|
|
sounds.push(
|
|
|
|
React.createElement(
|
|
|
|
Audio, {
|
|
|
|
key,
|
|
|
|
setRef: this._setRef.bind(this, soundId),
|
2018-05-16 15:03:10 +00:00
|
|
|
src,
|
|
|
|
loop: options.loop
|
2018-02-26 19:37:12 +00:00
|
|
|
}));
|
|
|
|
key += 1;
|
|
|
|
}
|
|
|
|
|
2018-05-22 14:22:16 +00:00
|
|
|
return sounds;
|
2018-02-26 19:37:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the (reference to the) {@link AudioElement} object which implements
|
|
|
|
* the audio playback functionality.
|
|
|
|
*
|
|
|
|
* @param {string} soundId - The sound Id for the audio element for which
|
|
|
|
* the callback is being executed.
|
|
|
|
* @param {AudioElement} element - The {@link AudioElement} instance
|
|
|
|
* which implements the audio playback functionality.
|
|
|
|
* @protected
|
|
|
|
* @returns {void}
|
|
|
|
*/
|
|
|
|
_setRef(soundId: string, element: ?AudioElement) {
|
|
|
|
if (element) {
|
|
|
|
this.props._addAudioElement(soundId, element);
|
|
|
|
} else {
|
|
|
|
this.props._removeAudioElement(soundId);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Maps (parts of) the Redux state to {@code SoundCollection}'s props.
|
|
|
|
*
|
|
|
|
* @param {Object} state - The redux state.
|
|
|
|
* @private
|
|
|
|
* @returns {{
|
|
|
|
* _sounds: Map<string, Sound>
|
|
|
|
* }}
|
|
|
|
*/
|
|
|
|
function _mapStateToProps(state) {
|
|
|
|
return {
|
|
|
|
_sounds: state['features/base/sounds']
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Maps dispatching of some actions to React component props.
|
|
|
|
*
|
|
|
|
* @param {Function} dispatch - Redux action dispatcher.
|
|
|
|
* @private
|
|
|
|
* @returns {{
|
|
|
|
* _addAudioElement: void,
|
|
|
|
* _removeAudioElement: void
|
|
|
|
* }}
|
|
|
|
*/
|
|
|
|
export function _mapDispatchToProps(dispatch: Function) {
|
|
|
|
return {
|
|
|
|
/**
|
|
|
|
* Dispatches action to store the {@link AudioElement} for
|
|
|
|
* a {@link Sound} identified by given <tt>soundId</tt> in the Redux
|
|
|
|
* store, so that the playback can be controlled through the Redux
|
|
|
|
* actions.
|
|
|
|
*
|
|
|
|
* @param {string} soundId - A global identifier which will be used to
|
|
|
|
* identify the {@link Sound} instance for which an audio element will
|
|
|
|
* be added.
|
|
|
|
* @param {AudioElement} audioElement - The {@link AudioElement}
|
|
|
|
* instance that will be stored in the Redux state of the base/sounds
|
|
|
|
* feature, as part of the {@link Sound} object. At that point the sound
|
|
|
|
* will be ready for playback.
|
|
|
|
* @private
|
|
|
|
* @returns {void}
|
|
|
|
*/
|
|
|
|
_addAudioElement(soundId: string, audioElement: AudioElement) {
|
|
|
|
dispatch(_addAudioElement(soundId, audioElement));
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Dispatches action to remove {@link AudioElement} from the Redux
|
|
|
|
* store for specific {@link Sound}, because it is no longer part of
|
|
|
|
* the DOM tree and the audio resource will be released.
|
|
|
|
*
|
|
|
|
* @param {string} soundId - The id of the {@link Sound} instance for
|
|
|
|
* which an {@link AudioElement} will be removed from the Redux store.
|
|
|
|
* @private
|
|
|
|
* @returns {void}
|
|
|
|
*/
|
|
|
|
_removeAudioElement(soundId: string) {
|
|
|
|
dispatch(_removeAudioElement(soundId));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2019-03-19 15:42:25 +00:00
|
|
|
// $FlowExpectedError
|
2018-02-26 19:37:12 +00:00
|
|
|
export default connect(_mapStateToProps, _mapDispatchToProps)(SoundCollection);
|