index.js 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. import * as action from '../../actions'
  2. import { useEffect, useState, useCallback } from 'react';
  3. import { connect } from 'react-redux';
  4. import { useDropzone } from 'react-dropzone'
  5. import { sortableContainer, sortableElement } from 'react-sortable-hoc';
  6. import { arrayMoveImmutable } from 'array-move';
  7. import { faPlay, faPause} from "@fortawesome/free-solid-svg-icons";
  8. import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
  9. const Track = ({ track, playlist, player, setTrack, playTrack, pauseTrack }) => {
  10. let [_player, setPlayer] = useState()
  11. let [isPlay, setPlay] = useState(true)
  12. useEffect(() => setPlayer(player), [player])
  13. return (
  14. <li className={isPlay && _player?.isPlaying && _player.track?._id === track._id ? 'playlist__track highlightPaleGreen' : 'playlist__track'}>
  15. {isPlay && _player?.isPlaying && _player.track?._id === track._id ?
  16. <button
  17. className='highlightYellow'
  18. onClick={() => { pauseTrack(); setPlay(false) }}
  19. ><FontAwesomeIcon style={{pointerEvents:'none'}} icon={faPause} /></button> :
  20. <button
  21. className='highlightGreen'
  22. onClick={() => {
  23. if (track?._id !== _player?.track?._id) setTrack(track, playlist)
  24. playTrack()
  25. setPlay(true)
  26. }}
  27. ><FontAwesomeIcon style={{pointerEvents:'none'}} icon={faPlay} /></button>
  28. }
  29. <div>
  30. <small className='artist'>{track.id3.artist || 'Artist: unknown'}</small>
  31. <small className='album'>{track.id3.album || 'Album: unknown'}</small>
  32. <small className='title'>{track.id3.title || track.originalFileName}</small>
  33. </div>
  34. </li>
  35. )
  36. }
  37. const TrackConnect = connect(
  38. state => ({
  39. player: state.player || {},
  40. }),
  41. {
  42. setTrack: action.setTrack,
  43. pauseTrack: action.pauseTrack,
  44. playTrack: action.playTrack
  45. }
  46. )(Track)
  47. const SortableItem = sortableElement(TrackConnect);
  48. const SortableContainer = sortableContainer(({ children }) => { return <ul>{children}</ul> });
  49. const Playlist = ({ player, playlist, setPlaylist, updPlaylist, setIndex }) => {
  50. let [_tracks, setTracks] = useState()
  51. let [_player, setPlayer] = useState()
  52. useEffect(() => setPlayer(player), [player])
  53. useEffect(() => setTracks(playlist[0]?.tracks), [playlist])
  54. const onSortEnd = ({ oldIndex, newIndex }) => {
  55. let newArr = arrayMoveImmutable(_tracks, oldIndex, newIndex)
  56. setTracks(newArr)
  57. updPlaylist(playlist[0]._id, newArr.map(track => ({ _id: track._id })))
  58. if(_player?.playlist?._id === playlist[0]?._id) {
  59. setPlaylist({..._player.playlist, 'tracks': newArr})
  60. if (_player?.track) setIndex(newArr.map((item) => item._id).indexOf(_player?.track?._id))
  61. }
  62. };
  63. return (
  64. <>
  65. <h2 className='highlightGreen'>{playlist[0]?.name || 'Playlist'}</h2>
  66. <SortableContainer onSortEnd={onSortEnd}>
  67. {(_tracks || []).map((track, index) => <SortableItem index={index} track={track} playlist={playlist[0]} />)}
  68. </SortableContainer>
  69. </>
  70. )
  71. }
  72. export const PlaylistConnect = connect(
  73. state => ({
  74. playlist: state.promise.playlistTracks?.payload || [],
  75. player: state.player || {}
  76. }),
  77. {
  78. updPlaylist: action.actionUpdatePlaylist,
  79. setPlaylist: action.setPlaylist,
  80. setIndex: action.setIndex
  81. }
  82. )(Playlist)
  83. const PlaylistTrackDropzone = ({ playlist, uploadTrack }) => {
  84. let [playlistId, setPlaylistId] = useState()
  85. useEffect(() => {
  86. setPlaylistId(playlist[0]?._id)
  87. }, [playlist])
  88. const onDrop = useCallback(acceptedFiles => {
  89. uploadTrack(acceptedFiles[0], playlistId)
  90. }, [uploadTrack, playlistId])
  91. const { getRootProps, isDragActive } = useDropzone({ onDrop })
  92. return (
  93. <div
  94. {...getRootProps()}
  95. className='playlist'
  96. style={{ border: `${isDragActive ? '1px solid yellow' : '1px solid transparent'}` }}
  97. >
  98. <small className='lightText'>{isDragActive ? '...drop here' : 'to upload: drag and drop track here'}</small>
  99. <PlaylistConnect />
  100. </div>
  101. )
  102. }
  103. const PlaylistTrackDropzoneConnect = connect(
  104. state => ({ playlist: state.promise.playlistTracks?.payload || [] }),
  105. { uploadTrack: action.actionUploadUserTrack }
  106. )(PlaylistTrackDropzone)
  107. const PlaylistPage = ({ match: { params: { _id } }, getTracks }) => {
  108. useEffect(() => { getTracks(_id.substring(1)) }, [_id, getTracks])
  109. return (<PlaylistTrackDropzoneConnect />)
  110. }
  111. export const PlaylistPageConnect = connect(null, { getTracks: action.actionGetPlaylistById })(PlaylistPage)
  112. const UserTracks = ({ user, tracks }) => {
  113. let tracksRev = [...tracks].reverse()
  114. return (
  115. <>
  116. <h2 className='highlightYellow'>{user.login || 'My'} uploaded tracks:</h2>
  117. <ul>{(tracksRev || []).map(track => <TrackConnect track={track} playlist={tracksRev} />)}</ul>
  118. </>
  119. )
  120. }
  121. const UserTracksConnect = connect(state => ({
  122. tracks: state.promise.userTracks?.payload || [],
  123. user: state.promise.userData?.payload || {}
  124. })
  125. )(UserTracks)
  126. const UserTracksDropzone = ({ onLoad }) => {
  127. const onDrop = useCallback(acceptedFiles => {
  128. onLoad(acceptedFiles[0])
  129. }, [onLoad])
  130. const { getRootProps, isDragActive } = useDropzone({ onDrop })
  131. return (
  132. <div
  133. {...getRootProps()}
  134. className='playlist'
  135. style={{ height: 'fit-content', border: `${isDragActive ? '1px solid yellow' : '1px solid black'}` }}
  136. >
  137. <small className='lightText'>{isDragActive ? '...drop here' : 'to upload: drag and drop track here'}</small>
  138. <UserTracksConnect />
  139. </div>
  140. )
  141. }
  142. export const UserTrackDropzoneConnect = connect(null, { onLoad: action.actionUploadUserTrack })(UserTracksDropzone)
  143. const UserTracksPage = ({ match: { params: { _id } }, getUserTracks }) => {
  144. useEffect(() => { getUserTracks() }, [_id, getUserTracks])
  145. return (<UserTrackDropzoneConnect />)
  146. }
  147. export const UserTracksPageConnect = connect(null, { getUserTracks: action.actionGetUserTracks })(UserTracksPage)