소스 검색

my playlists tracks find

mfdok43 2 년 전
부모
커밋
8792b04871

+ 87 - 18
src/actions/index.js

@@ -2,9 +2,6 @@ import {actionPromise, store} from '../reducers'
 import {actionAuthLogin} from '../reducers'
 import {all, takeEvery, put, call,select} from 'redux-saga/effects';
 import {promiseWorker} from "../reducers/promiseReducer";
-import jwtDecode from "jwt-decode";
-
-
 
 
 const getGQL = url =>
@@ -102,13 +99,12 @@ export const actionUserFind = () => actionPromise('userFind', gql(`query {
             }
         }`))
 
-
-export const actionTrackFindByOwner = (myid) => actionPromise('trackFindByOwner1', gql(`query trackFindByOwner($q: String){
-            TrackFind(query: $q){
-                 _id url originalFileName
-            }
-        }`, { q: JSON.stringify([{ ___owner: myid }]) }))
-
+// export const actionTrackFindByOwner = (myid) => actionPromise('trackFindByOwner1', gql(`query trackFindByOwner($q: String){
+//             TrackFind(query: $q){
+//                  _id url originalFileName
+//             }
+//         }`, { q: JSON.stringify([{ ___owner: myid }]) }))
+//
 
 
 export const actionFindMyTracks = () =>
@@ -133,6 +129,24 @@ export function* findMyTracksWatcher() {
 
 
 
+export const actionCreatePlaylist = (name) =>
+    ({type:'CREATE_PLAYLIST', name})
+
+export function* createPlaylistWorker (action) {
+    let {name} = action
+    let {auth} = yield select()
+    yield call(promiseWorker,actionPromise('createPlaylist', gql(`mutation p($playlist:PlaylistInput) {
+         PlaylistUpsert(playlist:$playlist) {
+         _id 
+         }
+      }`, {playlist: {name}})))
+    yield put (actionPlaylistFindByOwner())
+}
+
+
+export function* createPlaylistWatcher() {
+    yield takeEvery ('CREATE_PLAYLIST',createPlaylistWorker)
+}
 
 
 
@@ -145,14 +159,23 @@ export const actionTracksFind = () =>
 }`))
 
 
-
-
 export const actionPlaylistFindByOwner = () =>
-    actionPromise('playlistFindByOwner1', gql(`query playlistFindByOwner ($u: String) {
-    PlaylistFind(query: $u) {
-        _id name 
+    ({type:'FIND_MY_PLAYLISTS'})
+
+export function* playlistFindByOwnerWorker () {
+    let { auth } = yield select();
+    let userId = auth?.payload?.sub?.id;
+    yield call (promiseWorker,    actionPromise('playlistFindByOwner1', gql(`query playlistFindByOwner ($q: String) {
+    PlaylistFind(query: $q) {
+        _id name owner {login}
     }
-}`, { q: JSON.stringify([{ ___owner: '61cda244e9472933a6785efc' }]) }))
+}`, { q: JSON.stringify([{ ___owner: userId }]) })))
+
+}
+
+export function* playlistFindByOwnerWatcher() {
+    yield takeEvery ('FIND_MY_PLAYLISTS',playlistFindByOwnerWorker)
+}
 
 
 
@@ -189,6 +212,7 @@ export function* aboutMeWatcher() {
 
 
 
+
 export const actionUploadTrack = (file) => {
     let fd = new FormData();
     fd.append("track", file);
@@ -221,6 +245,39 @@ export const actionUploadImage = (file) => {
 };
 
 
+
+
+
+export const actionSetTrackToPlaylist = (file) =>
+    ({type:'SET_TRACK_TO_PLAYLIST', file})
+
+export function* setTrackToPlaylistWorker (action) {
+    const {file} = action
+    let result = yield call(promiseWorker,actionUploadTrack(file));
+    let trackId = result._id;
+    let { route } = yield select();
+    let playlistId = route.params?._id
+    console.log(playlistId)
+    yield call(
+        promiseWorker,actionPromise('createPlaylist', gql(`mutation p($playlist:PlaylistInput) {
+         PlaylistUpsert(playlist:$playlist) {
+         _id 
+         }
+      }`, {playlist: {_id: playlistId,tracks:{_id: trackId }}}))
+    );
+    yield put(actionAboutMe());
+};
+
+
+export function* setTrackToPlaylistWatcher() {
+    yield takeEvery ('SET_TRACK_TO_PLAYLIST', setTrackToPlaylistWorker)
+}
+
+
+
+
+
+
 export const actionSetAvatar = (file) =>
     ({type:'SET_AVATAR', file})
 
@@ -238,8 +295,7 @@ export function* setAvatarWorker (action) {
         UserUpsert(user:{_id: $userId, avatar: {_id: $imageId}}){_id}}`,
                 { userId: userId, imageId: imageId }
             )
-        )
-    );
+        ));
     yield put(actionAboutMe());
 };
 
@@ -247,3 +303,16 @@ export function* setAvatarWatcher() {
     yield takeEvery ('SET_AVATAR',setAvatarWorker)
 }
 
+
+export const queries = {
+    "/user/:_id": match => ({name: 'trackFindByOwner1',
+                                query: `query trackFindByOwner($q: String){
+        TrackFind(query: $q){ _id url originalFileName}}`,
+                            variables: { q: JSON.stringify([{ ___owner: match.params._id }]) } }),
+
+    "/myplaylist/:_id": match => ({name: 'trackFindByPlaylist',
+                                   query: `query trackFindByPlaylist($q: String){
+        TrackFind(query: $q){ _id url originalFileName}}`,
+                            variables:{ q: JSON.stringify([{ ___playlist: match.params._id }]) }  }),
+}
+

+ 2 - 1
src/pages/allTracks.js

@@ -1,4 +1,4 @@
-import {actionTracksFind, actionUserFind, backURL} from "../actions";
+import {actionTracksFind, backURL} from "../actions";
 import {connect} from "react-redux";
 import {store} from "../reducers";
 
@@ -14,4 +14,5 @@ const AllTracks = ({tracks}={}) =>
 
 
 export const CAllTracks = connect(state => ({tracks: state.promise.findAllTracks1?.payload || []}))(AllTracks)
+
 store.dispatch(actionTracksFind())

+ 1 - 1
src/pages/allUsers.js

@@ -116,7 +116,7 @@ const defaultPlaylists = [
 
 
 const User = ({playlist:{_id, login}={}}) =>
-<li><Link to={`/playlist/${_id}`}>{login}</Link></li>
+<li><Link to={`/user/${_id}`}>{login}</Link></li>
 
 
 const AllUsers = ({playlists=defaultPlaylists}) =>{

+ 2 - 7
src/pages/header/header-build.js

@@ -10,8 +10,7 @@ import {history} from "../../App";
 import {actionFullLogin, gql} from "../../actions";
 import {takeEvery, select, call,put} from "redux-saga/effects";
 import {promiseWorker} from "../../reducers/promiseReducer";
-
-
+import {CTrackSearch} from "../search";
 
 
 export const actionSetUserPassword = (password) =>
@@ -49,16 +48,12 @@ const ChangePasswordForm = ({onChangePassword}) => {
 export const CChangePasswordForm = connect(null,{onChangePassword:actionSetUserPassword})(ChangePasswordForm)
 
 
-
-
-
-
 export const Header = () =>
     <header className="Header">
         <Logo />
         <Link to={`/music`}><h2>Все песни</h2></Link>
         <Link to={`/mymusic`}> <h2>Моя музыка</h2></Link>
-        <h2>Поиск🔎</h2>
+        <h2><CTrackSearch /></h2>
         <Switch>
             <Route path="/login" component={CLoginForm}/>
             <Route path="/registration" component={CRegForm}/>

+ 18 - 9
src/pages/main.js

@@ -1,9 +1,13 @@
 import {Route, Redirect, Switch} from 'react-router-dom';
+
 import {PageMain} from "./pageMain";
-import {CUserTracks} from "./userTracks";
+import {UserTracks} from "./userTracks";
 import {CAllTracks} from "./allTracks";
 import {CAllUsers} from "./allUsers";
 import {CMyPlaylists} from "./myPlaylists";
+import {CProtectedRoute,CRRoute} from "../reducers/routeReducer";
+import {CSearchResult} from "./search";
+import {MyPlaylistTracks} from "./myPlaylists";
 
 export const Aside = ({children}) =>
     <div className='Aside'>
@@ -18,17 +22,22 @@ export const Content = ({children}) =>
 export const Main = () =>
     <main>
         <Aside>
-            <Route path="/music" component={CAllUsers}/>
-            <Route path="/mymusic" component={CMyPlaylists}/>
-            <Route path='/' component={CAllUsers} exact/>
+            <CProtectedRoute roles={["anon", "user"]} path="/music" component={CAllUsers}/>
+            <CProtectedRoute roles={["anon", "user"]} path="/user/:_id" component={CAllUsers}/>
+            <CProtectedRoute roles={["anon", "user"]} path="/mymusic" component={CMyPlaylists}/>
+            <CProtectedRoute roles={["anon", "user"]} path="/myplaylist/:_id" component={CMyPlaylists}/>
+            <CProtectedRoute roles={["anon", "user"]} path='/' component={CAllUsers} exact/>
         </Aside>
         <Content>
             <Redirect from='/main' to='/'/>
             <Switch>
-                <Route path="/music" component={CAllTracks}/>
-                <Route path='/' component={PageMain} exact/>
-                <Route path="/playlist/:_id" component={CUserTracks}/>
-                <Route path='/mymusic' component={PageMain}/>
+                <CProtectedRoute roles={["anon", "user"]} path="/search" component={CSearchResult}/>
+                <CProtectedRoute roles={["anon", "user"]} path="/music" component={CAllTracks}/>
+                <CProtectedRoute roles={["anon", "user"]} path='/' component={PageMain} exact/>
+                <CProtectedRoute roles={["anon", "user"]} path="/myplaylist/:_id" component={MyPlaylistTracks}/>
+                <CProtectedRoute roles={["anon", "user"]} path="/user/:_id" component={UserTracks}/>
+                <CProtectedRoute roles={["anon", "user"]} path='/mymusic' component={PageMain}/>
             </Switch>
         </Content>
-   </main>
+   </main>
+

+ 25 - 18
src/pages/myPlaylists.js

@@ -1,33 +1,38 @@
-import {actionPlaylistFindByOwner, gql, setAvatarWorker} from "../actions";
+import {actionCreatePlaylist, actionPlaylistFindByOwner, backURL} from "../actions";
 import {connect} from "react-redux";
 import {Link} from "react-router-dom";
 import {call, select, takeEvery} from "redux-saga/effects";
 import {actionPromise, promiseWorker} from "../reducers/promiseReducer";
 import {useState} from "react";
 import {history} from "../App";
+import {store} from "../reducers";
+import {CTrack} from "./player";
+import {CTrackDropZone} from "./pageMain";
 
 
-export const actionCreatePlaylist = (name) =>
-    ({type:'CREATE_PLAYLIST', name})
+const Track = ({track:{_id,url,originalFileName}={}}) =>
+    <div className='Tracks'>
+        <audio controls src={backURL+'/'+url}></audio> <strong>{originalFileName}</strong>
+    </div>
 
-export function* createPlaylistWorker (action) {
-    let {name} = action
-    let {auth} = yield select()
-    let userId = auth.payload?.sub?.id
-    yield call(promiseWorker,actionPromise('createPlaylist', gql(`mutation reg($playlist:PlaylistInput) {
-         PlaylistUpsert(playlist:$playlist) {
-         _id 
-         }
-      }`, {playlist: {userId, name}})))
-}
+const MyTracks = ({tracks}={}) =>
+    <div>
+        {(tracks || []).map(track => <Track track={track}/>)}
+    </div>
 
+const CMyTracks = connect(state => ({tracks: state.promise.trackFindByPlaylist?.payload || []}))(MyTracks)
+
+
+export const MyPlaylistTracks = ({match:{params:{_id}}}) =>
+    <>
+        <h1>{_id}</h1>
+        <CTrackDropZone />
+        <CMyTracks />
+    </>
 
-export function* createPlaylistWatcher() {
-    yield takeEvery ('CREATE_PLAYLIST',createPlaylistWorker)
-}
 
 const Playlist = ({playlist:{_id, name}={}}) =>
-    <li><Link to={`/playlist/${_id}`}>{name}</Link></li>
+    <li><Link to={`/myplaylist/${_id}`}>{name}</Link></li>
 
 const MyPlaylists =  ({playlists={},onCreatePlaylist}) => {
     const [p, setP] = useState ('')
@@ -42,4 +47,6 @@ return (
 )
 }
 
-export const CMyPlaylists = connect(state => ({playlists: state.promise.playlistFindByOwner1?.payload || []}),{onCreatePlaylist:actionCreatePlaylist})(MyPlaylists)
+export const CMyPlaylists = connect(state => ({playlists: state.promise.playlistFindByOwner1?.payload || []}),{onCreatePlaylist:actionCreatePlaylist})(MyPlaylists)
+
+store.dispatch(actionPlaylistFindByOwner())

+ 10 - 1
src/pages/pageMain.js

@@ -5,6 +5,7 @@ import {connect} from "react-redux";
 import {actionUploadTrack, actionFindMyTracks, backURL} from "../actions";
 import {CPreloaded} from "./preloader";
 import {CPlayer} from "./player";
+import {actionSetTrackToPlaylist} from "../actions";
 
 
 const defaultTrack = {
@@ -16,6 +17,13 @@ const defaultTrack = {
     }
 }
 
+
+
+
+
+
+
+
 export const PageMain = () =>
     <div>
     <h1>Моя музыка</h1>
@@ -27,6 +35,7 @@ function TrackDropZone({ onLoad }) {
     const onDrop = useCallback((acceptedFiles) => {
         // Do something with the files
         onLoad(acceptedFiles[0]);
+        store.dispatch(actionSetTrackToPlaylist(acceptedFiles[0]))
     }, []);
     const { getRootProps, getInputProps, isDragActive } = useDropzone({ onDrop });
 
@@ -49,7 +58,7 @@ function TrackDropZone({ onLoad }) {
 }
 
 
-const CTrackDropZone = connect (null, {onLoad: actionUploadTrack}) (TrackDropZone)
+export const CTrackDropZone = connect (null, {onLoad: actionUploadTrack}) (TrackDropZone)
 
 store.dispatch(actionFindMyTracks())
 

+ 1 - 1
src/pages/player.js

@@ -37,7 +37,7 @@ const defaultTracks = [
 
 
 //<audio controls src={backURL+'/'+url}></audio>
-const Track = ({track:{id,url,originalFileName},trackPlay,trackStop}) => {
+export const Track = ({track:{id,url,originalFileName},trackPlay,trackStop}) => {
      let audio = new Audio()
     let audioSrc = backURL + '/'+ url
     const [isPlaying, setIsPlaying] = useState(false);

+ 56 - 0
src/pages/search.js

@@ -0,0 +1,56 @@
+import {actionPromise} from "../reducers";
+import {gql} from "../actions";
+import {useState} from "react";
+import {Link} from "react-router-dom";
+import {connect} from "react-redux";
+import {CTrack} from "./player";
+
+const actionTrackSearch = (word) => (
+    actionPromise('trackFind', gql(`query trackById($q: String) {
+      TrackFind(query: $q) {
+          _id url originalFileName
+      }
+  }`, {q: JSON.stringify([
+                {
+                    $or: [{originalFileName: `/${word}/`}]
+                },
+                {
+                    sort: [{originalFileName: 1}]
+                }
+            ])
+        }
+    ))
+)
+
+
+const TrackSearch = ({ onSearch }) => {
+    const [value, setValue] = useState("");
+    return (
+        <div>
+            <input
+                type="text"
+                placeholder="Поиск..."
+                value={value}
+                onChange={(e) => setValue(e.target.value)}
+            />
+            <Link to="/search">
+                <button onClick={() => onSearch(value)}>Найти</button>
+            </Link>
+        </div>
+    );
+};
+
+export const CTrackSearch = connect(null,{onSearch:actionTrackSearch})(TrackSearch)
+
+const SearchResult = ({ trackFind }) => {
+    return (
+        <>
+            {trackFind.map((track) => (
+                <CTrack track={track} />
+            ))}
+        </>
+    );
+};
+export const CSearchResult = connect((state) => ({
+    trackFind: state.promise.trackFind?.payload || [],
+}))(SearchResult);

+ 21 - 11
src/pages/userTracks.js

@@ -3,6 +3,8 @@ import {actionTrackFindByOwner} from "../actions";
 import {backURL} from "../actions";
 import {useEffect} from 'react';
 import {CPreloaded} from "./preloader";
+import {store} from "../reducers";
+
 
 
 const defaultTracks = [
@@ -42,17 +44,25 @@ const Tracks = ({tracks}={}) =>
         {(tracks || []).map(track => <Track track={track}/>)}
     </div>
 
-const CTracks = connect(state => ({tracks: state.promise.trackFindByOwner1?.payload}))(Tracks)
+const CTracks = connect(state => ({tracks: state.promise.trackFindByOwner1?.payload || []}))(Tracks)
+
+
+export const UserTracks = ({match:{params:{_id}}}) =>
+    <>
+        <h1>{_id}</h1>
+        <CTracks />
+    </>
 
 
-export const UserTracks = ({match:{params:{_id}}, getData}, history) => {
-    useEffect(() => {
-        getData(_id)
-    }, [_id])
-    return (
-       // <CPreloaded name='trackFindByOwner1'>
-        <CTracks/>
-        // </CPreloaded>
-    )}
+// export const UserTracks = ({match:{params:{_id}}, getData}, history) => {
+//     useEffect(() => {
+//         getData(_id)
+//     }, [_id])
+//     return (
+//        //
+//         <CTracks/>
+//         // </CPreloaded>
+//     )}
+//
+// export const CUserTracks = connect(null, {getData: actionTrackFindByOwner})(UserTracks)
 
-export const CUserTracks = connect(null, {getData: actionTrackFindByOwner})(UserTracks)

+ 60 - 0
src/reducers/routeReducer.js

@@ -0,0 +1,60 @@
+import {promiseWorker,actionPromise} from "./promiseReducer";
+import {gql} from "../actions";
+import {takeEvery, call} from 'redux-saga/effects';
+import {queries} from "../actions";
+import {connect}   from 'react-redux';
+import {Route, Redirect} from 'react-router-dom';
+
+export function routeReducer(state={}, {type, match}) {
+    if (type === 'ROUTE') {
+        return match
+    }
+    return state
+}
+
+function* routeWorker({match}) {
+    console.log(match)
+    if (match.path in queries) {
+        const {name, query, variables} = queries[match.path](match)
+        yield call(promiseWorker, actionPromise(name, gql(query, variables)))
+    }
+}
+
+export function* routeWatcher() {
+    yield takeEvery('ROUTE', routeWorker)
+}
+
+const RRoute = ({ action, component:Component, ...routeProps}) => {
+    const WrapperComponent = (componentProps) => {
+        action(componentProps.match)
+        return <Component {...componentProps} />
+    }
+    return <Route {...routeProps} component={WrapperComponent} />
+}
+export const CRRoute = connect(null, {action: match => ({type: 'ROUTE', match})})(RRoute)
+
+const ProtectedRoute = ({ fallback='/',
+                            roles=["admin"],
+                            auth,
+                            component:Component,
+                            ...routeProps}) => {
+
+    const WrapperComponent = (componentProps) => {
+        let aclArr = auth?.payload?.sub?.acl
+        if (!aclArr) {
+            aclArr = ['anon']
+        }
+        let crossArr = [];
+        for (const role of roles) {
+            crossArr = [...crossArr, ...aclArr.filter(aclItem => aclItem === role)]
+        }
+        if (crossArr.length === 0) {
+            return <Redirect to={fallback} />
+        } else {
+            return <Component {...componentProps} />
+        }
+    }
+    return <CRRoute {...routeProps} component={WrapperComponent} />
+}
+
+export const CProtectedRoute = connect(state => ({auth: state.auth}))(ProtectedRoute)

+ 13 - 5
src/reducers/store.js

@@ -1,6 +1,7 @@
 import {applyMiddleware, combineReducers, createStore} from "redux";
 import {authReducer} from "./authReducer";
 import {promiseReducer,promiseWatcher} from "./promiseReducer";
+import {routeReducer, routeWatcher} from "./routeReducer";
 import {playerReducer} from "./playerReducer";
 import {
     loginWatcher,
@@ -9,19 +10,23 @@ import {
     aboutMeWatcher,
     findMyTracksWatcher,
     actionAboutMe,
-    actionFindMyTracks
+    playlistFindByOwnerWatcher,
+    setTrackToPlaylistWatcher,
+    actionFindMyTracks,
 } from "../actions";
 import {localStoredReducer} from "./localStoredReducer";
 import createSagaMiddleware from 'redux-saga';
 import {all} from 'redux-saga/effects';
-import {createPlaylistWatcher} from "../pages/myPlaylists";
+import {createPlaylistWatcher} from "../actions";
 // import {setUserPasswordWatcher} from "../pages/header/header-build";
 
+
 const sagaMiddleware = createSagaMiddleware()
 export const store = createStore(combineReducers({promise: localStoredReducer(promiseReducer, 'promise'),
                                                              auth: localStoredReducer(authReducer, 'auth'),
-                                                           player: localStoredReducer(playerReducer, 'player')}),
-                                                              applyMiddleware(sagaMiddleware))
+                                                           player: localStoredReducer(playerReducer, 'player'),
+                                                            route: localStoredReducer(routeReducer,'route')}),
+                                                                   applyMiddleware(sagaMiddleware))
 
 // export const store = createStore(combineReducers({promise: promiseReducer,
 //         auth: authReducer}),
@@ -36,7 +41,10 @@ function* rootSaga(){
          setAvatarWatcher(),
          aboutMeWatcher(),
          createPlaylistWatcher(),
-        findMyTracksWatcher(),
+         findMyTracksWatcher(),
+         routeWatcher(),
+          playlistFindByOwnerWatcher(),
+          setTrackToPlaylistWatcher(),
          // setUserPasswordWatcher(),
     ])
 }