Stepanova Asya 1 year ago
parent
commit
b76594afc3

+ 15 - 0
package-lock.json

@@ -3139,6 +3139,11 @@
         "is-string": "^1.0.7"
       }
     },
+    "array-move": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/array-move/-/array-move-4.0.0.tgz",
+      "integrity": "sha512-+RY54S8OuVvg94THpneQvFRmqWdAHeqtMzgMW6JNurHxe8rsS07cHQdfGkXnTUXiBcyZ0j3SiDIxxj0RPiqCkQ=="
+    },
     "array-union": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
@@ -9795,6 +9800,16 @@
         "workbox-webpack-plugin": "^6.4.1"
       }
     },
+    "react-sortable-hoc": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/react-sortable-hoc/-/react-sortable-hoc-2.0.0.tgz",
+      "integrity": "sha512-JZUw7hBsAHXK7PTyErJyI7SopSBFRcFHDjWW5SWjcugY0i6iH7f+eJkY8cJmGMlZ1C9xz1J3Vjz0plFpavVeRg==",
+      "requires": {
+        "@babel/runtime": "^7.2.0",
+        "invariant": "^2.2.4",
+        "prop-types": "^15.5.7"
+      }
+    },
     "react-transition-group": {
       "version": "4.4.2",
       "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.2.tgz",

+ 2 - 0
package.json

@@ -10,6 +10,7 @@
     "@testing-library/jest-dom": "^5.16.2",
     "@testing-library/react": "^12.1.2",
     "@testing-library/user-event": "^13.5.0",
+    "array-move": "^4.0.0",
     "bootstrap": "^5.1.3",
     "react": "^17.0.2",
     "react-bootstrap": "^2.1.2",
@@ -18,6 +19,7 @@
     "react-redux": "^7.2.6",
     "react-router-dom": "^5.3.0",
     "react-scripts": "5.0.0",
+    "react-sortable-hoc": "^2.0.0",
     "redux": "^4.1.2",
     "redux-thunk": "^2.4.1",
     "sass": "^1.49.7",

+ 2 - 19
src/App.js

@@ -1,18 +1,12 @@
 import './App.scss';
 import React from 'react';
 import 'bootstrap/dist/css/bootstrap.min.css';
-import {Router, Link} from 'react-router-dom';
+import {Router} from 'react-router-dom';
 import createHistory from "history/createBrowserHistory";
-//import thunk from 'redux-thunk';
 import {Provider, connect}   from 'react-redux';
-//import {createStore, combineReducers, applyMiddleware} from 'redux';
 import { store } from './store/store';
 import { actionAllPlaylists, actionUsersPlaylists } from './store/promiseReducer';
-import { Header } from './components/header';
 import { Main } from './components/Routs';
-import {CAllPlaylists} from './components/Playlist';
-
-
 
 
 export let history = createHistory();
@@ -22,17 +16,6 @@ store.getState().auth.token && store.dispatch(actionAllPlaylists());
 store.getState().auth.token && store.dispatch(actionUsersPlaylists(store.getState().auth?.user?.id));
 
 
-// export const CPlaylistById = connect(state => ({playlist: state.promise.plstById?.payload || {}}), )(PlaylistById);
-
-                                              
-
-export const Aside = ({children}) => 
-  <div>
-    
-    <CAllPlaylists/>
-  </div>
-
-
 const CRoutes = connect(state => ({auth : state.auth?.token}))(Main)
 
 
@@ -43,7 +26,7 @@ function App() {
     <Provider store ={store}>
     
       <CRoutes/>
-      {/* {store.auth?.token && <СNowPlayingPlayer/>} */}
+
     </Provider>
     </Router>
     

+ 3 - 0
src/components/EditProfile.js

@@ -7,6 +7,7 @@ import { actionAuthLogin } from '../store/authReducer';
 import {Link} from 'react-router-dom';
 import Form from 'react-bootstrap/Form';
 import {Accordion} from "react-bootstrap";
+import {RunToast} from "./Toast";
 
 
 export async function sendForm (url, data) {
@@ -19,9 +20,11 @@ export async function sendForm (url, data) {
       }).then(res => res.json())
       .then(data => {
           if(data.user) {
+            RunToast('bg-success','Success', 'Profile updated')
             store.dispatch(actionAuthLogin(store.getState().auth?.token, data.user));
             return data
           } else if(data.login){
+            RunToast('bg-danger','Error', 'Login should be unique')
             return data.login[0];
           } else{
             return data;

+ 7 - 6
src/components/Playlist.js

@@ -8,8 +8,9 @@ import { store } from '../store/store';
 import { actionAllPlaylists } from '../store/promiseReducer';
 
 
-export const PlaylistsAll = ({playlists}) => 
-{
+export const PlaylistsAll = ({playlists}) => {
+let text = window.location.href.split('/')[3];
+console.log(text + 'text')
 const getAnswer =  () => {
   store.dispatch(actionAllPlaylists());
 };
@@ -19,9 +20,9 @@ useEffect(() => {
 }, []);
 
 return (<>
-  <div className="d-flex justify-content-between align-items-center py-3">
-    <h3 className="text-uppercase"> <FontAwesomeIcon icon={faCompactDisc} className="me-2"/>Playlists</h3>
-  </div>
+  {/* <div className="d-flex justify-content-between align-items-center py-3">
+    <h3 className="text-uppercase"> <FontAwesomeIcon icon={faCompactDisc} className="me-2"/>{(text === 'allplaylists') ? 'Playlists' : "Albums"}</h3>
+  </div> */}
 <div className='RootCategories d-flex justify-content-start flex-wrap'>
   {playlists.map((playlist, i) => <Playlist key={i} playlist={playlist}/>)}
 </div>
@@ -29,4 +30,4 @@ return (<>
 }
 
 
-export const CAllPlaylists = connect(state => ({playlists: state.promise.allPlaylists?.payload?.playlists?.data || []}), )(PlaylistsAll);
+// export const CAllPlaylists = connect(state => ({playlists: state.promise.allPlaylists?.payload?.playlists?.data || []}), )(PlaylistsAll);

+ 9 - 6
src/components/Routs.js

@@ -3,15 +3,16 @@ import { history } from '../App';
 import {LoginForm} from './authorization';
 import {CRegisterForm} from './authRegistration';
 import { store } from '../store/store';
-import { Aside } from '../App';
+import { ToastNotify } from './Toast';
 import { CUserPage } from '../pages/userPage';
-// import { UserPage} from './userPage';
 import {CPlaylistById}  from './playlistById';
 import {СNowPlayingPlayer} from './playing'
 import { CEditProfile } from './EditProfile';
 import { Header } from './header';
 import { CArtistPage } from '../pages/artistPage';
 import { CAlbumPage } from '../pages/albumPage';
+import { CSearchPage } from '../pages/searchPage';
+import { AllPlaylistsPage } from '../pages/allPlaylistsPage';
 
 export const Main = ({auth}) =>
 
@@ -28,14 +29,16 @@ export const Main = ({auth}) =>
           {!auth && <Redirect from='/playlist' to={'/login'} exact />}
           {!auth && <Redirect from='/artists' to={'/login'} exact />}
           {!auth && <Redirect from='/albums' to={'/login'} exact />}
+          {!auth && <Redirect from='/search' to={'/login'} exact />}
           <Route path={'/login'} component={LoginForm} />
           <Route path={'/register'} component={CRegisterForm}/>
           <Route path={'/editprofile'} component={CEditProfile}/>
-          <Route path={'/allplaylists'} component={Aside}/>
+          <Route path={'/allplaylists'} component={AllPlaylistsPage}/>
           <Route path={'/playlist'} component={CPlaylistById} />
           <Route path={'/user'} component={CUserPage} />
           <Route path={'/artists'} component={CArtistPage} />
           <Route path={'/albums'} component={CAlbumPage} />
+          <Route path={'/search'} component={CSearchPage} />
           <Route exact path="/">{auth ? <Redirect to="/user"/> : <Redirect to="/login" /> }</Route>
         </Switch>
       </div>
@@ -47,11 +50,11 @@ const Content = ({children}) =>
   <>
   {store.getState().auth?.token && <Header/>}
     <section className='d-flex justify-content-center container-fluid pt-3'>
+      <ToastNotify/>
       <div className={store.getState().auth?.token ? 'col-7 pe-3' : 'col-12 pe-3'}>
         {children}
       </div>
-      {/* <div className='col-5'> */}
-        {store.getState().auth?.token && <СNowPlayingPlayer className='col-5'/>}
-      {/* </div> */}
+
+      {store.getState().auth?.token && <СNowPlayingPlayer className='col-5'/>}
     </section>
   </>

+ 1 - 1
src/components/createPlaylist.js

@@ -25,7 +25,7 @@ export const CreatePlaylist = (props) => {
     let result = await sendForm('playlists/create', data);
     console.log(result);
     store.dispatch(actionUsersPlaylists(store.getState().auth?.user?.id));
-    //history.push(`/playlist/${result.playlist.id}`)
+    history.push(`/playlist/${result.playlist.id}`)
   }
   const PreViewImage = (image) => {
     if (image && typeof (image) !== "string") {

+ 1 - 0
src/components/header.js

@@ -27,6 +27,7 @@ export const Header = ({children}) =>
                 <Nav className="me-auto">
                     <Link to={'/user'} className="nav-link">Home</Link>
                     <Link to={'/allplaylists'} className="nav-link">Playlists</Link>
+                    <Link to={'/search'} className="nav-link">Search</Link>
                     {/*<Nav.Link href="/allplaylists">Albums</Nav.Link>*/}
                     {/*<Nav.Link href="/allplaylists">Artists</Nav.Link>*/}
                 </Nav>

+ 1 - 2
src/components/playing.js

@@ -30,7 +30,7 @@ export let NowPlayingPlayer = (props) => {
         if (props.currentTime) audio.currentTime = newCurrent
     }, [newCurrent]);
     
-    let album_photo = props.track?.album?.photo
+    let album_photo = props.track?.album?.photo || img_album;
     const [key, setKey] = useState('home');
     return(
         <div className="player col-xxl-3 col-lg-5 ps-3">
@@ -46,7 +46,6 @@ export let NowPlayingPlayer = (props) => {
                             <div className="details w-100">
                                 <div className="now-playing"></div>
                                 <div className="track-art" style={{backgroundImage:`url(${album_photo})`}} ></div>
-                                {/*<div className="track-name">{props.track?.name|| 'Track Name'  }</div>*/}
                                 <div className="track-name w-100 text-center">
                                     <div className='line w-75'>
                                         <div className='second w-100'>

+ 16 - 20
src/components/tracklist.js

@@ -6,23 +6,22 @@ import { Link } from "react-router-dom";
 import { actionRemoveTrackFromQueue } from "../store/playerReducer";
 import { store } from "../store/store";
 import { sendForm } from "./SendForm";
-
-
+  
 const Track = ({track}) => 
 <>
 <tr>
-<td width={30} data-id={track.id}>
-    <div className="col">
-        <Button variant="outline-light" className='rounded-5'  title='Play' onClick={async () => {
-            // console.log(playlist.tracks, playlist?.tracks[playlist?.tracks.indexOf(track)]);
-            // playlist.tracks && store.dispatch(actionFullSetPlaylist(playlist?.tracks));
-            // playlist.tracks ? store.dispatch(actionFullSetTrack(playlist?.tracks[playlist?.tracks.indexOf(track)])) : store.dispatch(actionFullSetTrack(track))
-            // store.dispatch(actionFullPlay());
-        }}>
-            <FontAwesomeIcon className='' icon={faPlay}/>
-        </Button>
-    </div>
-</td>
+    <td width={30} data-id={track.id}>
+        <div className="col">
+            <Button variant="outline-light" className='rounded-5'  title='Play' onClick={async () => {
+                // console.log(playlist.tracks, playlist?.tracks[playlist?.tracks.indexOf(track)]);
+                // store.dispatch(actionFullSetPlaylist(playlist?.tracks));
+                // playlist.tracks ? store.dispatch(actionFullSetTrack(playlist?.tracks[playlist?.tracks.indexOf(track)])) : store.dispatch(actionFullSetTrack(track))
+                // store.dispatch(actionFullPlay());
+            }}>
+                <FontAwesomeIcon className='' icon={faPlay}/>
+            </Button>
+        </div>
+    </td>
     <td>          
         <Link className="link-light" to='#'>  {track.name}</Link>
     </td>
@@ -30,9 +29,6 @@ const Track = ({track}) =>
         <FontAwesomeIcon icon={faXmark} onClick={() => store.dispatch(actionRemoveTrackFromQueue(track))}/>
     </td>
 </tr>
-
-
-    {/* <div>{track?.name} - {track?.id3?.artist}</div> */}
 </>
 
 const TrackList = ({tracks}) => {
@@ -43,17 +39,17 @@ const TrackList = ({tracks}) => {
                 <tr>
                     <th scope="col" width={30}></th>
                     <th scope="col">Track name</th>
-                    {/* <th scope="col">Artist</th> */}
-                    {/* <th scope="col">Album</th> */}
                     <th scope='col'>Action</th>
                 </tr>
         </thead>
             <tbody>
-                {tracks.map((track, i) => <Track track={track}/>)}
+                {tracks.map((track, i) => <Track key={i} track={track}/>)}
             </tbody>
         </table>
         </>
     )
 }
 
+
+
 export const CTrackList = connect(state => ({tracks: state.player?.playlist || []} ), )(TrackList);

+ 1 - 1
src/components/userPlaylists.js

@@ -12,7 +12,7 @@ import Button from "react-bootstrap/Button";
 
 
 export const Playlist = ({playlist}) => {
-  let link = ((window.location.href.split('/')[3]) === 'artists' ? 'albums' : 'playlist');
+  let link = (((window.location.href.split('/')[3]) === 'artists' || (window.location.href.split('/')[3]) === 'search') ? 'albums' : 'playlist');
   return(
   <div className="">
       <div className="me-4 mb-4 p-4 playlist-img-box rounded-5 position-relative"

+ 8 - 0
src/index.css

@@ -268,4 +268,12 @@ i.fa-step-backward{
   color: #1e2125;
   background-color: #e9ecef;
   cursor: pointer;
+}
+.artist-search-name {
+  text-align: center;
+  overflow: hidden;
+  z-index: 1;
+  font-size: 1rem;
+  max-width: 100px;
+  max-height: 100px;
 }

+ 15 - 2
src/pages/artistPage.js

@@ -4,8 +4,10 @@ import { useEffect } from "react";
 import { PlaylistsAll } from "../components/Playlist";
 import { TracksAll } from "../components/Tracks";
 import { actionArtistById } from "../store/promiseReducer";
+import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
+import {faCompactDisc, faHeadphonesSimple} from "@fortawesome/free-solid-svg-icons";
 
-export const ArtistPage = ({artist}) => {
+const ArtistPage = ({artist}) => {
     let id = window.location.href.split('/')[4];
     const getAnswer =  () => {
       store.dispatch(actionArtistById(id));
@@ -16,7 +18,18 @@ export const ArtistPage = ({artist}) => {
     }, []);
 
 return(<>
- <h1>{artist.name}</h1>
+  <div className="d-flex justify-content-center flex-column align-items-center">
+      <div className="playlist-img-box rounded-circle position-relative"
+           style={{backgroundImage: `url(${artist.photo ?? "https://www.pngitem.com/pimgs/m/427-4279206_title-detail-mexican-musician-logo-hd-png-download.png"})`, backgroundSize: "cover", backgroundRepeat: "no-repeat", backgroundPosition: "center", width:250, height:250}}
+      ></div>
+      <h1>{artist.name}</h1>
+      <p className='text-white-50 mb-2'><FontAwesomeIcon className='me-2' icon={faHeadphonesSimple} />{artist?.tracks?.length} Tracks</p>
+      <p className='text-white-50 mb-2'><FontAwesomeIcon className='me-2' icon={faCompactDisc} />{artist?.albums?.length} Albums</p>
+  </div>
+  
+  <div className="d-flex justify-content-between align-items-center py-3">
+      <h3 className="text-uppercase"> <FontAwesomeIcon icon={faCompactDisc} className="me-2"/>Albums</h3>
+  </div>
  <CArtistPlaylists/>
  <CArtistTracks/>
  </>)

+ 5 - 1
src/store/promiseReducer.js

@@ -79,4 +79,8 @@ export const actionPlaylistById = (_id) => {
 };
 
 export const actionArtistById = (_id) => 
-actionPromise('artistById', gql('/artists/'+_id))
+actionPromise('artistById', gql('/artists/'+_id))
+
+
+export const actionSearch = (search) => 
+actionPromise('searchResult', gql('/search?search='+search))