index.js 5.7 KB

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