Browse Source

=> Tracks, edit playlist

Stepanova Asya 1 year ago
parent
commit
f342d87181

+ 42 - 0
src/components/Tracks.js

@@ -0,0 +1,42 @@
+import {connect}   from 'react-redux';
+import {actionFullSetTrack, actionFullPlay} from '../store/playerReducer';
+import { store } from '../store/store';
+
+let i = 1;
+export let audio = new Audio();
+
+const Track = ({track: {name, file, id3, id} = {} }, key) => 
+<tr>
+    <th scope="row">{i++}</th>
+    <td>          
+        <div onClick={async () => {
+            audio.src = `http://player-api/storage/tracks/${file}`;   
+            store.dispatch(actionFullSetTrack({name, file, id3, id}));
+            store.dispatch(actionFullPlay());
+        }}>
+            {name}
+        </div>
+    </td>
+    <td>
+        <span>{id3.artist}</span>
+    </td>
+    <td>{id3.getAlbum}</td>
+</tr>
+
+
+const TracksAll = ({tracks=[]}) => 
+<table className="table table-dark table-striped table-dark table-hover">
+    <thead>
+        <tr>
+            <th scope="col">#</th>
+            <th scope="col">Track name</th>
+            <th scope="col">Artist</th>
+            <th scope="col">Album</th>
+        </tr>
+</thead>
+    <tbody>
+        {tracks.map((tracks, i) => <Track key={i} track={tracks}/>)}
+    </tbody>
+</table>
+
+export const СAllTracks = connect(state => ({tracks: state.promise.plstById?.payload?.tracks || []}), )(TracksAll);

+ 1 - 1
src/components/createPlaylist.js

@@ -55,7 +55,7 @@ export const CreatePlaylist = (props) => {
                   <label  className="form-label">Image</label>
                   <input type="file" name="picture" accept="image/*" id="file" className='form-control mb-3' onChange={(e) => setImage(e.target.files[0])} multiple={false}/>
                   <input className="form-check-input me-3" type="checkbox" id="flexCheckIndeterminate" checked={privat} onChange={e => setPrivat(e.target.checked? 1 : 0)}/>
-                  <label className="form-check-label" for="flexCheckIndeterminate">Private?</label>    
+                  <label className="form-check-label" >Private?</label>    
               </div>
               <div className="w-50">
                   <label  className="form-label">Name</label><br/>

+ 4 - 5
src/components/header.js

@@ -1,11 +1,10 @@
-import image from '../images/card.png';
 import logo from '../images/musicico.png';
-import logoutimg from '../images/logout.png';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
+import {faArrowRightFromBracket} from '@fortawesome/free-solid-svg-icons';
 import {connect}   from 'react-redux';
 import { actionAuthLogout } from '../store/authReducer';
-
 import React from 'react';
-const REACT_VERSION = React.version;
+
 
 
 
@@ -37,7 +36,7 @@ export const Header = ({children}) =>
 </header>
 
 const ImgLogout = () => {
-    return <img alt='///' className='logoimg' src={logoutimg}/>
+    return <FontAwesomeIcon icon={faArrowRightFromBracket} className='fa-2x'/>  
 }
 
 const CLogout = connect(state => ({children: <ImgLogout/>}), 

+ 24 - 27
src/components/playing.js

@@ -4,6 +4,7 @@ import {faVolumeDown, faVolumeUp, faRandom, faStepBackward, faStopCircle, faStep
 import { store } from '../store/store';
 import {actionFullGetDuration, actionFullSetTrack, actionFullPlay, actionFullPause, actionFullSetVolume, actionFullSetCurrentTime } from '../store/playerReducer';
 import {Provider, connect}   from 'react-redux';
+import { audio } from './Tracks';
 
 function msToTime(duration) {
     let hours,minutes,seconds;
@@ -19,14 +20,13 @@ function msToTime(duration) {
     }
 
 export let NowPlayingPlayer = (props) => {
-    const [volume, setVolume] = useState(10);
-    // let duration;
+    const [volume, setVolume] = useState(20);
+    const [newCurrent, setNewCurrent] = useState(0)
 
-    // useEffect(() => {
-    //     if (!store.getState().player?.duration) {
-    //         duration = msToTime(store.getState()?.player?.track?.src?.duration)
-    //     }
-    // }, []);
+    audio.ontimeupdate = () =>  store.dispatch(actionFullSetCurrentTime(audio.currentTime));
+    useEffect(() => {
+        if (props.currentTime) audio.currentTime = newCurrent
+    }, [newCurrent])
 
 return(
 <div className="player">
@@ -39,12 +39,18 @@ return(
         </div>
 
         <div className="slider-container duration">
-            <span className="current-time">00:00</span>
-            <input type='range' min={1} max='100' value='0' className="seek-slider" 
-            // onChange={(e) => setDuration(e.target.value)}
+            <span className="current-time">{props.currentTime ? 
+            `0${((props.currentTime - props.currentTime % 60) / 60 % 60).toFixed() !== 'NaN' ? 
+            ((props.currentTime - props.currentTime % 60) / 60 % 60).toFixed() : '0'}:${(props.currentTime % 60).toFixed() > 9 ?
+             '' : '0'}${(props.currentTime % 60).toFixed() !== 'NaN' ? (props.currentTime % 60).toFixed() : '0'}` : '--:--'}
+             </span>
 
+            <input type='range' min={0} max={props.duration} className="seek-slider" 
+            value={props.currentTime || 0} onChange={(e) => setNewCurrent(e.target.value)}
+             onMouseUp={() => store.dispatch(actionFullPlay())} onMouseDown={() => store.dispatch(actionFullPause())}
             />
-            <span className="total-duration">{msToTime(props.duration) !== 'NaN'? msToTime(props.duration) : '00:00'}</span>
+
+            <span className="total-duration">{props.track?.id3?.time || (msToTime(props.duration) !== 'NaN:NaN'? msToTime(props.duration) : '00:00')}</span>
             
         </div>
 
@@ -56,10 +62,7 @@ return(
                 if (store.getState()?.player?.track) store.dispatch(actionFullSetVolume(volume)) }}
 
              />
-             
-            
-            <FontAwesomeIcon icon={faVolumeUp} />
-            
+            <FontAwesomeIcon icon={faVolumeUp} />  
         </div>
 
         <div className="buttons">
@@ -68,7 +71,7 @@ return(
             >
                 <FontAwesomeIcon icon={faRandom} className='fa-2x'/>
             </div>
-            <div className="random-track" 
+            <div className="prev-track" 
             // onClick={prevTrack()}
             >
                 <FontAwesomeIcon icon={faStepBackward} className='fa-2x'/>
@@ -78,10 +81,8 @@ return(
               onClick={() => {
                 if(store.getState()?.player?.isPlaying === true) {
                     store.dispatch(actionFullPause());
-                    //setPlay(true)
                  } else{
                     store.dispatch(actionFullPlay());
-                    //setPlay(false)
                  } 
                 }}
             >
@@ -103,12 +104,8 @@ return(
     </div>
 </div>)
 }
-function mapStateToProps (state) {
-    return {
-        track: state.player?.track,
-        duration: state.player?.duration,
-        
-    }
-  }
-  //export const СNowPlayingPlayer = connect(mapStateToProps)(NowPlayingPlayer)
-  export const СNowPlayingPlayer = connect(state => ({track: state.player?.track || [], duration: state.player?.duration || [], isPlaying: state.player?.isPlaying || false}) )(NowPlayingPlayer);
+
+  export const СNowPlayingPlayer = connect(state => ({track: state.player?.track || [], 
+    duration: state.player?.duration || '00:00',
+     isPlaying: state.player?.isPlaying || false,
+    currentTime: state.player?.currentTime || '00:00' }) )(NowPlayingPlayer);

+ 75 - 51
src/components/playlistById.js

@@ -1,13 +1,13 @@
 import React, {useState, useEffect} from 'react';
-import { ReactDOM } from 'react';
 import { store } from '../store/store';
 import { actionPlaylistById} from '../store/promiseReducer';
 import Button from 'react-bootstrap/Button';
 import Modal from 'react-bootstrap/Modal';
-import {Provider, connect}   from 'react-redux';
-import { actionFullSetTrack, actionFullPlay} from '../store/playerReducer';
+// import {connect}   from 'react-redux';
+// import {actionFullSetTrack, actionFullPlay} from '../store/playerReducer';
 import { Header } from './header';
 import { СNowPlayingPlayer } from './playing';
+import {СAllTracks } from './Tracks'
 
 
 
@@ -33,7 +33,7 @@ function sendForm (url, data) {
 
 
 
-function ShowModal  (props)  {
+function LoadTrackModal  (props)  {
     const [tracks, setTrack] = useState(null);
 
     const PostLoadTracks = async(event)  =>{
@@ -74,46 +74,6 @@ return(
      </Modal.Footer>
   </Modal>)
 }
-let i =1;
-export let audio = new Audio();
-const Track = ({track: {name, file, id3, id} = {} }, key) => 
-    
-<tr>
-    <th scope="row">{i++}</th>
-    <td>          
-        <button onClick={() => {
-            audio.src = `http://player-api/storage/tracks/${file}`;
-                
-            store.dispatch(actionFullSetTrack({name, file, id3, id}));
-            store.dispatch(actionFullPlay());
-        }}>
-            {name}
-        </button>
-    </td>
-    <td>
-        <a href='#/artist'>{id3.artist}</a>
-    </td>
-    <td>{id3.getAlbum}</td>
-</tr>
-
-
-const TracksAll = ({tracks=[]}) => 
-    <table className="table table-dark table-striped table-dark table-hover">
-        <thead>
-            <tr>
-                <th scope="col">#</th>
-                <th scope="col">Track name</th>
-                <th scope="col">Artist</th>
-                <th scope="col">Album</th>
-            </tr>
-    </thead>
-        <tbody>
-            {tracks.map((tracks, i) => <Track key={i} track={tracks}/>)}
-        </tbody>
-    </table>
-
-const СAllTracks = connect(state => ({tracks: state.promise.plstById?.payload?.tracks || []}), )(TracksAll);
-
 
 export const PlaylistById = ({playlist = {}}) => {
     let id = window.location.href.split('/')[4];
@@ -127,6 +87,7 @@ export const PlaylistById = ({playlist = {}}) => {
     }, []);
 
     const [modalShow, setModalShow] = React.useState(false);
+    const [modalTrackShow, setModalTrackShow] = React.useState(false);
   
   return(
     <>
@@ -139,13 +100,15 @@ export const PlaylistById = ({playlist = {}}) => {
                 <img src={playlist.photo} width ='250px' alt='...'/>
             </div>
             <div>
-                <p className='h4'>Название плейлиста: {playlist.name}</p>
-                <p>Автор плейлиста: {playlist.user_name}</p>
-                
+                <p className='h4'>Playlist name: {playlist.name}</p>
+                <p>Playlist author: {playlist.user_name}</p>
+                <span onClick={() => setModalTrackShow(true)}>Edit Playlist</span>
+                <EditPlaylistModal  show={modalTrackShow} playlist={playlist}
+                        onHide={() => setModalTrackShow(false)}></EditPlaylistModal>
                 <p>{playlist?.tracks?.length} треков</p> 
                 <div className='d-flex'>
-                <div> <button type="button" className="btn btn-light me-2">Перемешать</button></div>
-                <div> <button type="button" className="btn btn-light">Добавить в библиотеку</button></div>
+                {/* <div> <button type="button" className="btn btn-light me-2">Shake</button></div> */}
+                {/* <div> <button type="button" className="btn btn-light">Добавить в библиотеку</button></div> */}
                 </div>
                 {playlist.user_id === store.getState().auth.user.id? 
                 (<>
@@ -153,7 +116,8 @@ export const PlaylistById = ({playlist = {}}) => {
                         Add Tracks
                     </Button>
 
-                    <ShowModal
+                    <LoadTrackModal
+                        
                         show={modalShow}
                         onHide={() => setModalShow(false)}
                     /></>) : <></>}
@@ -167,4 +131,64 @@ export const PlaylistById = ({playlist = {}}) => {
       
       
       </>
-  )}
+  )}
+
+
+  function EditPlaylistModal  (props)  {
+    const [name, setName] = useState(props.playlist?.name);
+    const [description, setDescription] = useState(props.playlist?.description);
+    //const [privat, setPrivat] = useState(0);
+    const [image, setImage] = useState(props.playlist?.photo);
+
+    const PostEditPlaylist = async(event)  =>{
+        event.preventDefault();
+        const data = new FormData();
+
+        data.append("name", name || props.playlist?.name);
+        data.append("description", description || props.playlist?.description);
+        data.append("private", props.playlist?.private);
+        data.append("photo",  image, (image.name?  image.name : props.playlist?.photo));
+
+        sendForm('playlists/' + props.playlist?.id + '/edit', data);  
+    }
+
+    return(
+    <Modal
+        {...props}
+        size="lg"
+        aria-labelledby="contained-modal-title-vcenter"
+        centered
+    >
+        <Modal.Header closeButton> 
+        <Modal.Title id="contained-modal-title-vcenter">
+        Add Tracks
+        </Modal.Title> 
+        </Modal.Header>
+        <Modal.Body >
+        <form onSubmit={PostEditPlaylist} className="authorization center align-items-center justify-content-center  d-flex" id='loadTracksForm'>
+        <div className="border p-3 col-9">
+        <h4 className="w-100 text-center">Create Playlist</h4>
+            <hr/>
+            <div className="d-flex justify-content-between">
+              <div className="w-auto">
+                  <label  className="form-label">Image</label>
+                  <input type="file" name="picture" accept="image/*" id="file" className='form-control mb-3' onChange={(e) => setImage(e.target.files[0])} multiple={false}/>
+                  {/* <input className="form-check-input me-3" type="checkbox" id="flexCheckIndeterminate" checked={privat} onChange={e => setPrivat(e.target.checked? 1 : 0)}/>
+                  <label className="form-check-label" >Private?</label>     */}
+              </div>
+              <div className="w-50">
+                  <label  className="form-label">Name</label><br/>
+                  <input type="text" id="username" className='input form-control mb-3' value={name} onChange={e => setName(e.target.value)}/>
+                  <label  className="form-label">Description</label>
+                  <textarea type="password" id="password" className='form-control mb-3' value={description} onChange={e => setDescription(e.target.value)}/>
+              </div>
+            </div>
+            <button type='submit' className="btn btn-outline-danger" onClick={props.onHide} >Create</button>
+        </div>
+        </form>
+        </Modal.Body>
+        <Modal.Footer> 
+        <Button variant="outline-danger" type='submit' form='loadTracksForm' onClick={props.onHide}>Save</Button> 
+        </Modal.Footer>
+    </Modal>)
+}

+ 30 - 24
src/components/userPage.js

@@ -1,12 +1,12 @@
-import React, {useState, useEffect} from 'react';
+import React, {useEffect} from 'react';
 import {Link} from 'react-router-dom';
-import {Provider, connect}   from 'react-redux';
+import {connect}   from 'react-redux';
 import { actionUsersPlaylists } from '../store/promiseReducer';
 import { actionFullSetPlaylist } from '../store/playerReducer';
 import { store } from '../store/store';
 import image from '../images/card.png';
 import Modal from 'react-bootstrap/Modal';
-import Button from 'react-bootstrap/Button';
+// import Button from 'react-bootstrap/Button';
 import {CreatePlaylist} from './createPlaylist'
 import { Header } from './header';
 
@@ -30,13 +30,13 @@ import { Header } from './header';
 //       })
 // }
 
-const Playlist = ({playlist: {id, user_id, name, photo, description} = {}}) => 
+const Playlist = ({playlist = {}}) => 
   <div className="col-sm-3">
-    <Link className="card" to= {`/playlist/${id}`} onClick={() => store.dispatch(actionFullSetPlaylist({id, user_id, name, photo, description}) )}>
-      <img src={photo || image} className="card-img-top" alt="..."/>
+    <Link className="card" to= {`/playlist/${playlist.id}`} onClick={() => store.dispatch(actionFullSetPlaylist({playlist}) )}>
+      <img src={playlist.photo || image} className="card-img-top" alt="..."/>
       <div className="card-body">
-        <h5 className="card-title"> {name}</h5>
-        <p className="card-text">{description? description :  '.' }</p>
+        <h5 className="card-title"> {playlist.name}</h5>
+        <p className="card-text">{playlist.description? playlist.description :  '.' }</p>
         <button className="btn btn-primary" >Go somewhere</button>
       </div>
     </Link>
@@ -44,10 +44,26 @@ const Playlist = ({playlist: {id, user_id, name, photo, description} = {}}) =>
   
 
 
-export const UsersPlaylistsAll = ({playlists= []}) => 
+export const UsersPlaylistsAll = ({playlists= []}) => {
+  const [modalShow, setModalShow] = React.useState(false);
+  return (
+<>
+
   <div className='RootCategories row'>
+    <div className="col-sm-3 border border-white d-flex align-items-center justify-content-center" onClick={() => setModalShow(true)}>
+      <h3>Create new Playlist</h3>
+    </div>
+
+      <MyVerticallyCenteredModal
+        show={modalShow}
+        onHide={() => setModalShow(false)}
+      />
+        
+  
     {playlists.map((playlist, i) => <Playlist key={i} playlist={playlist}/>)}
   </div> 
+</>)
+}
 
 const СUsersPlaylists = connect(state => ({playlists: state.promise.usersPlaylists?.payload?.playlists|| []}), )(UsersPlaylistsAll);
                                               
@@ -77,7 +93,7 @@ function MyVerticallyCenteredModal(props) {
 
 
 export const UserPage = () => {
-let id = store.getState().auth.user.id;
+let id = store.getState().auth?.user?.id;
   const getAnswer = async () => {
     await store.dispatch(actionUsersPlaylists(id));	
   };
@@ -86,27 +102,17 @@ let id = store.getState().auth.user.id;
     getAnswer();
   }, []);
 
-const [modalShow, setModalShow] = React.useState(false);
-
     return(<>
     <Header/>
     <div className='d-flex container align-items-center justify-content-center'>
-        <div className='col'>
-            <img className='col-sm-3' alt='...' src={image}/>
+        <div className=''>
+            <img className='m-4' alt='...' src={image} width='150px'/>
         </div>
-        <div className='col'>
-            <h3>{store.getState().auth.user.name}</h3>
+        <div className=''>
+            <h3>{store.getState().auth?.user?.name}</h3>
             <a href='/change'>Edit Profile</a>
         </div>
     </div>
-    <Button variant="primary" onClick={() => setModalShow(true)}>
-        Create new Playlist
-    </Button>
-
-      <MyVerticallyCenteredModal
-        show={modalShow}
-        onHide={() => setModalShow(false)}
-      />
 
     <h3>My playlists:</h3>
     <СUsersPlaylists/>

+ 5 - 4
src/store/playerReducer.js

@@ -1,5 +1,5 @@
 //import { store } from '../store/store';
-import { audio } from '../components/playlistById';
+import { audio } from '../components/Tracks';
 
 export const playerReducer = function(state = {}, {type, duration, track, playlist, playlistIndex, currentTime, volume}) {
     if (!state) {
@@ -55,14 +55,16 @@ export const actionFullPlay = () =>
     dispatch => {  
         audio.play();
         dispatch(actionPlay());
-        dispatch(actionFullGetDuration(audio.duration))
+        dispatch(actionFullGetDuration(audio.duration));
+        actionFullSetCurrentTime(audio.currentTime);
     }
 
 
-
 const actionPause = () => ({type:'PAUSE'})
 export const actionFullPause = () =>
     dispatch => {
+        dispatch(actionFullGetDuration(audio.duration));
+
         audio.pause();
         dispatch(actionPause());
     }
@@ -77,7 +79,6 @@ export const actionFullSetVolume = (volume) =>
 const actionSetTrack = (track) => ({type:'SET_TRACK', track})
 export const actionFullSetTrack = (track) =>
     dispatch => {
-        //audio.src = `http://player-api/storage/tracks/${track.file}`;
         dispatch(actionSetTrack(track));
         dispatch(actionFullPlay());
     }