App.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. import React from 'react';
  2. import { Switch, Route } from 'react-router-dom'
  3. import logo from './chad-logo-256.png';
  4. import './App.css';
  5. import Selector from './Selector.js';
  6. import Player from './Player.js';
  7. import Playlist from './Playlist.js';
  8. import Sound from 'react-sound';
  9. import MediaSession from './MediaSession.js';
  10. const fetchOpts = { credentials: 'same-origin' }
  11. class App extends React.Component {
  12. constructor(props) {
  13. super(props);
  14. this.state = {
  15. sound: {url: '', playStatus: Sound.status.STOPPED},
  16. activeTrack: -1,
  17. };
  18. }
  19. activateTrack(activeTrack) {
  20. if (!Array.isArray(this.state.tracks)) {
  21. return;
  22. }
  23. if (activeTrack < 0 || activeTrack >= this.state.tracks.length) {
  24. activeTrack = -1;
  25. }
  26. this.setState({activeTrack})
  27. }
  28. componentDidUpdate(prevProps, prevState) {
  29. const {activeTrack} = this.state;
  30. if (activeTrack !== prevState.activeTrack) {
  31. if (activeTrack !== -1 && Array.isArray(this.state.tracks)) {
  32. const track = this.state.tracks[activeTrack];
  33. if (!track) {
  34. console.log('Bad activeTrack', this.state.tracks, activeTrack);
  35. } else {
  36. this.setState({
  37. sound: {
  38. url: track.url,
  39. position: 0,
  40. playStatus: Sound.status.PLAYING
  41. },
  42. metadata: {
  43. title: track.title,
  44. artist: track.artist,
  45. album: track.album.album,
  46. cover: track.album.cover
  47. }
  48. })
  49. }
  50. } else {
  51. this.setState({
  52. sound: {url: '', position: 0, playStatus: Sound.status.STOPPED},
  53. metadata: null
  54. })
  55. }
  56. }
  57. }
  58. handleActivateTrack = (activeTrack) => this.activateTrack(activeTrack)
  59. handleControlPrev = () => this.activateTrack(this.state.activeTrack - 1)
  60. handleControlNext = () => this.activateTrack(this.state.activeTrack + 1)
  61. handleControlPlayPause = () => {
  62. const playStatus = (this.state.sound.playStatus === Sound.status.PLAYING
  63. ? Sound.status.PAUSED
  64. : Sound.status.PLAYING);
  65. this.setState({sound: Object.assign({}, this.state.sound, {playStatus: playStatus})});
  66. }
  67. handleControlStop = () => {
  68. this.setState({
  69. sound: Object.assign({}, this.state.sound, {
  70. playStatus: Sound.status.STOPPED,
  71. position: 0}),
  72. activeTrack: -1});
  73. }
  74. handleSoundError = (errorCode, description) => {
  75. console.log('sound error', errorCode, description)
  76. // try next track, TODO: better error handling
  77. this.handleControlNext();
  78. }
  79. handleSoundPlaying = (sound) => {
  80. this.setState({sound: Object.assign({}, this.state.sound, {
  81. position: sound.position,
  82. duration: sound.duration
  83. })})
  84. }
  85. handleSoundFinished = () => {
  86. console.log('sound finished');
  87. this.setState({sound: Object.assign({}, this.state.sound, {playStatus: Sound.status.STOPPED})});
  88. this.handleControlNext();
  89. }
  90. fetchAlbumTracks(album) {
  91. return fetch(`/album/${album.id}/tracks`, fetchOpts)
  92. .then(res => (res.ok ? res.json() : Promise.reject({message:res.statusText})))
  93. .then(tracks => tracks.map(t => Object.assign(t, {album})))
  94. }
  95. onPlayAlbum = (album) => {
  96. this.fetchAlbumTracks(album)
  97. .then(tracks => this.setState({tracks, activeTrack: 0}))
  98. .catch(error => this.setState({tracks: error.message}));
  99. this.setState({tracks: "Loading"})
  100. }
  101. render() {
  102. const playlist = (<Playlist
  103. open={this.state.playlistOpen}
  104. tracks={this.state.tracks}
  105. activeTrack={this.state.activeTrack}
  106. onActivateTrack={this.handleActivateTrack} />)
  107. const selector = (<Selector onPlayAlbum={this.onPlayAlbum} />)
  108. return (
  109. <div className="App">
  110. <header className="App-header">
  111. <img src={logo} className="App-logo" alt="logo" />
  112. <h1 className="App-title">Chad Music</h1>
  113. </header>
  114. <Switch>
  115. <Route exact path="/" render={() => selector} />
  116. <Route path="/playlist" render={() => playlist} />
  117. </Switch>
  118. <Player
  119. {...this.state.sound}
  120. {...this.state.metadata}
  121. onPlaylistToggle={() => this.setState({playlistOpen: !this.state.playlistOpen})}
  122. onPrev={this.handleControlPrev}
  123. onPlayPause={this.handleControlPlayPause}
  124. onStop={this.handleControlStop}
  125. onNext={this.handleControlNext} />
  126. <Sound {...this.state.sound}
  127. onError={this.handleSoundError}
  128. onLoading={this.handleSoundLoading}
  129. onPlaying={this.handleSoundPlaying}
  130. onFinishedPlaying={this.handleSoundFinished} />
  131. <MediaSession
  132. {...this.state.metadata}
  133. onPlay={this.handleControlPlayPause}
  134. onPause={this.handleControlPlayPause}
  135. onPreviousTrack={this.handleControlPrev}
  136. onNextTrack={this.handleControlNext}
  137. />
  138. </div>
  139. );
  140. }
  141. }
  142. export default App;