You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

SoundCollection.js 4.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. // @flow
  2. import React, { Component } from 'react';
  3. import { connect } from 'react-redux';
  4. import { Audio } from '../../media';
  5. import type { AudioElement } from '../../media';
  6. import { Fragment } from '../../react';
  7. import { _addAudioElement, _removeAudioElement } from '../actions';
  8. import type { Sound } from '../reducer';
  9. /**
  10. * {@link SoundCollection}'s properties.
  11. */
  12. type Props = {
  13. /**
  14. * Dispatches {@link _ADD_AUDIO_ELEMENT} Redux action which will store the
  15. * {@link AudioElement} for a sound in the Redux store.
  16. */
  17. _addAudioElement: Function,
  18. /**
  19. * Dispatches {@link _REMOVE_AUDIO_ELEMENT} Redux action which will remove
  20. * the sound's {@link AudioElement} from the Redux store.
  21. */
  22. _removeAudioElement: Function,
  23. /**
  24. * It's the 'base/sounds' reducer's state mapped to a property. It's used to
  25. * render audio elements for every registered sound.
  26. */
  27. _sounds: Map<string, Sound>
  28. }
  29. /**
  30. * Collections of all global sounds used by the app for playing audio
  31. * notifications in response to various events. It renders <code>Audio</code>
  32. * element for each sound registered in the base/sounds feature. When the audio
  33. * resource is loaded it will emit add/remove audio element actions which will
  34. * attach the element to the corresponding {@link Sound} instance in the Redux
  35. * state. When that happens the sound can be played using the {@link playSound}
  36. * action.
  37. */
  38. class SoundCollection extends Component<Props> {
  39. /**
  40. * Implements React's {@link Component#render()}.
  41. *
  42. * @inheritdoc
  43. * @returns {ReactElement}
  44. */
  45. render() {
  46. let key = 0;
  47. const sounds = [];
  48. for (const [ soundId, sound ] of this.props._sounds.entries()) {
  49. sounds.push(
  50. React.createElement(
  51. Audio, {
  52. key,
  53. setRef: this._setRef.bind(this, soundId),
  54. src: sound.src
  55. }));
  56. key += 1;
  57. }
  58. return (
  59. <Fragment>
  60. {
  61. sounds
  62. }
  63. </Fragment>
  64. );
  65. }
  66. /**
  67. * Set the (reference to the) {@link AudioElement} object which implements
  68. * the audio playback functionality.
  69. *
  70. * @param {string} soundId - The sound Id for the audio element for which
  71. * the callback is being executed.
  72. * @param {AudioElement} element - The {@link AudioElement} instance
  73. * which implements the audio playback functionality.
  74. * @protected
  75. * @returns {void}
  76. */
  77. _setRef(soundId: string, element: ?AudioElement) {
  78. if (element) {
  79. this.props._addAudioElement(soundId, element);
  80. } else {
  81. this.props._removeAudioElement(soundId);
  82. }
  83. }
  84. }
  85. /**
  86. * Maps (parts of) the Redux state to {@code SoundCollection}'s props.
  87. *
  88. * @param {Object} state - The redux state.
  89. * @private
  90. * @returns {{
  91. * _sounds: Map<string, Sound>
  92. * }}
  93. */
  94. function _mapStateToProps(state) {
  95. return {
  96. _sounds: state['features/base/sounds']
  97. };
  98. }
  99. /**
  100. * Maps dispatching of some actions to React component props.
  101. *
  102. * @param {Function} dispatch - Redux action dispatcher.
  103. * @private
  104. * @returns {{
  105. * _addAudioElement: void,
  106. * _removeAudioElement: void
  107. * }}
  108. */
  109. export function _mapDispatchToProps(dispatch: Function) {
  110. return {
  111. /**
  112. * Dispatches action to store the {@link AudioElement} for
  113. * a {@link Sound} identified by given <tt>soundId</tt> in the Redux
  114. * store, so that the playback can be controlled through the Redux
  115. * actions.
  116. *
  117. * @param {string} soundId - A global identifier which will be used to
  118. * identify the {@link Sound} instance for which an audio element will
  119. * be added.
  120. * @param {AudioElement} audioElement - The {@link AudioElement}
  121. * instance that will be stored in the Redux state of the base/sounds
  122. * feature, as part of the {@link Sound} object. At that point the sound
  123. * will be ready for playback.
  124. * @private
  125. * @returns {void}
  126. */
  127. _addAudioElement(soundId: string, audioElement: AudioElement) {
  128. dispatch(_addAudioElement(soundId, audioElement));
  129. },
  130. /**
  131. * Dispatches action to remove {@link AudioElement} from the Redux
  132. * store for specific {@link Sound}, because it is no longer part of
  133. * the DOM tree and the audio resource will be released.
  134. *
  135. * @param {string} soundId - The id of the {@link Sound} instance for
  136. * which an {@link AudioElement} will be removed from the Redux store.
  137. * @private
  138. * @returns {void}
  139. */
  140. _removeAudioElement(soundId: string) {
  141. dispatch(_removeAudioElement(soundId));
  142. }
  143. };
  144. }
  145. export default connect(_mapStateToProps, _mapDispatchToProps)(SoundCollection);