App.js 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. import logo from './logo.svg';
  2. import './App.css';
  3. import React, {useCallback} from 'react'
  4. import thunk from 'redux-thunk';
  5. import {createStore, combineReducers, applyMiddleware} from 'redux';
  6. import {Provider, connect} from 'react-redux';
  7. import {useDropzone} from 'react-dropzone'
  8. const actionPending = name => ({type: 'PROMISE', status: 'PENDING', name})
  9. const actionResolved = (name, payload) => ({type: 'PROMISE', status: 'RESOLVED', name, payload})
  10. const actionRejected = (name, error) => ({type: 'PROMISE', status: 'REJECTED', name, error})
  11. const actionPromise = (name, promise) =>
  12. async dispatch => {
  13. dispatch(actionPending(name)) // 1. {delay1000: {status: 'PENDING'}}
  14. try{
  15. let payload = await promise
  16. dispatch(actionResolved(name, payload))
  17. return payload
  18. }
  19. catch(error){
  20. dispatch(actionRejected(name, error))
  21. }
  22. }
  23. const getGQL = url =>
  24. (query, variables = {}) =>
  25. fetch(url, {
  26. method: 'POST',
  27. headers: {
  28. "Content-Type": "application/json",
  29. ...(localStorage.authToken ? { "Authorization": "Bearer " + localStorage.authToken } :
  30. {})
  31. },
  32. body: JSON.stringify({ query, variables })
  33. })
  34. .then(res => res.json())
  35. .then(data => {
  36. if (data.errors && !data.data)
  37. throw new Error(JSON.stringify(data.errors))
  38. return data.data[Object.keys(data.data)[0]]
  39. })
  40. const backendURL = "http://player.asmer.fs.a-level.com.ua"
  41. const gql = getGQL(backendURL + '/graphql')
  42. function jwtDecode(token) {
  43. try {
  44. let decoded = token.split('.')
  45. decoded = decoded[1]
  46. decoded = atob(decoded)
  47. decoded = JSON.parse(decoded)
  48. return decoded
  49. } catch (e) {
  50. return;
  51. }
  52. }
  53. const actionLoadFile = (file, type) => {
  54. let fd = new FormData()
  55. console.log('TYPE', type)
  56. fd.append(type === 'upload'? 'photo' : type, file)
  57. return (
  58. actionPromise('loadFile', fetch(backendURL + `/${type}`,{
  59. method: "POST",
  60. headers: localStorage.authToken ? {Authorization: 'Bearer ' + localStorage.authToken} : {},
  61. body: fd
  62. })
  63. .then(res => res.json())
  64. )
  65. )
  66. }
  67. const actionAboutMe = () => {
  68. let _id = jwtDecode(localStorage.authToken).sub.id
  69. return (
  70. actionPromise('aboutUser', gql(`
  71. query($userId: String!) {
  72. UserFindOne(query: $userId){
  73. login, _id, avatar {_id, url, originalFileName}
  74. }
  75. }
  76. `, { userId: JSON.stringify([{_id}]) }))
  77. )
  78. }
  79. const actionSetAvatar = (file) =>
  80. async (dispatch, getState) => {
  81. await dispatch(actionLoadFile(file, 'upload'))
  82. let picId = getState().promise?.loadFile?.payload?._id
  83. let userId = jwtDecode(localStorage.authToken).sub.id
  84. await dispatch(actionPromise('setAvatar', gql(`
  85. mutation {
  86. UserUpsert(user:{_id: "${userId}", avatar: {_id: "${picId}"}}){
  87. _id, login, avatar{
  88. _id, url
  89. }
  90. }
  91. }
  92. `)))
  93. dispatch(actionAboutMe())
  94. }
  95. const actionGetUserTracks = () => {
  96. //let _id = '5fe35e1ce926687ee86b0a3f' //newUserId
  97. let _id = jwtDecode(localStorage.authToken).sub.id
  98. return(
  99. actionPromise('userTracks', gql(`
  100. query getUserTracks($ownerId: String!) {
  101. TrackFind(query: $ownerId) {
  102. _id, originalFileName, url,
  103. id3 { title, artist, album }
  104. }
  105. }
  106. `, { ownerId: JSON.stringify([{ ___owner: _id }]) } ))
  107. )
  108. }
  109. const actionGetPlaylistById = (_id) =>
  110. actionPromise('playlistTracks', gql(`
  111. query playlistById($playlistId: String!) {
  112. PlaylistFind(query: $playlistId) {
  113. _id,
  114. name,
  115. tracks { _id, url, originalFileName }
  116. }
  117. }
  118. `, { playlistId: JSON.stringify([{ _id }]) }))
  119. const actionGetTrackById = (_id) =>
  120. actionPromise('track', gql(`
  121. query trackById($trackId: String!) {
  122. TrackFind(query: $trackId) {
  123. _id, url, originalFileName
  124. }
  125. }
  126. `, { trackId: JSON.stringify([{ _id }]) }))
  127. //add track to playlist
  128. const actionAddTrack = file =>
  129. async (dispatch, getState) => {
  130. let playlistId = "61e3506ac2305e2f502aca03"
  131. await dispatch(actionLoadFile(file, 'track'))
  132. await dispatch(actionGetTrackById(getState().promise.loadFile.payload?._id))
  133. await dispatch(actionGetPlaylistById(playlistId))
  134. let arr = [...getState().promise.track.payload]
  135. arr.push(...getState().promise.playlistTracks.payload[0].tracks)
  136. console.log('newarr', arr)
  137. //let updated = JSON.stringify(arr)
  138. //let trackId = getState().promise?.loadFile?.payload?._id
  139. // await dispatch(actionPromise('trackToPlaylist', gql(`
  140. // mutation {
  141. // PlaylistUpsert(playlist:{ _id: "${playlistId}", tracks: {_id: "${trackId}"}}) {
  142. // _id, name, tracks { _id, originalFileName }
  143. // }
  144. // }
  145. // `)))
  146. await dispatch(actionPromise('trackToPlaylist', gql(`
  147. mutation {
  148. PlaylistUpsert(playlist:{ _id: "${playlistId}", tracks: [{_id: "61e4b26ec2305e2f502acaae"}, {_id: "61e4a4cac2305e2f502aca98"}] })] {
  149. _id, name, tracks { _id, originalFileName }
  150. }
  151. }
  152. `)))
  153. // await dispatch(actionPromise('trackToPlaylist', gql(`
  154. // mutation {
  155. // PlaylistUpsert(playlist:{ _id: $playlistId, tracks: $tracks}}) {
  156. // _id, name, tracks { _id, originalFileName }
  157. // }
  158. // }
  159. // `, { playlistId:JSON.stringify([{ playlistId }]), tracks: JSON.stringify(arr) })))
  160. dispatch(actionGetUserTracks())
  161. }
  162. function promiseReducer(state={}, {type, name, status, payload, error}){
  163. if (type === 'PROMISE'){
  164. return { ...state, [name]:{status, payload, error} }
  165. }
  166. return state
  167. }
  168. const store = createStore(combineReducers({ promise: promiseReducer }), applyMiddleware(thunk))
  169. store.subscribe(() => console.log(store.getState()))
  170. function MyDropzone({uploadAvatar, userData, onLoad}) {
  171. const onDrop = useCallback(acceptedFiles => {
  172. uploadAvatar(acceptedFiles[0])
  173. }, [])
  174. const {getRootProps, getInputProps, isDragActive} = useDropzone({onDrop})
  175. return (
  176. <div {...getRootProps()} style={{backgroundColor:'mediumseagreen'}}>
  177. <input {...getInputProps()} />
  178. {
  179. isDragActive ?
  180. <p>Drop the files here ...</p> :
  181. <p>Drag 'n' drop some IMAGE here, or click to select files</p>
  182. }
  183. </div>
  184. )
  185. }
  186. const ConnectDropzone = connect(null, {uploadAvatar: actionSetAvatar, onLoad: actionLoadFile, userData: actionAboutMe})(MyDropzone)
  187. function TrackDropzone({uploadTrack, userData, onLoad}) {
  188. const onDrop = useCallback(acceptedFiles => {
  189. uploadTrack(acceptedFiles[0])
  190. }, [])
  191. const {getRootProps, getInputProps, isDragActive} = useDropzone({onDrop})
  192. return (
  193. <div {...getRootProps()} style={{backgroundColor:'mediumvioletred'}}>
  194. <input {...getInputProps()} />
  195. {
  196. isDragActive ?
  197. <p>Drop the files here ...</p> :
  198. <p>Drag 'n' drop some TRACK here, or click to select files</p>
  199. }
  200. </div>
  201. )
  202. }
  203. const ConnectTrackDropzone = connect(null, {uploadTrack: actionAddTrack, onLoad: actionLoadFile, userData: actionAboutMe})(TrackDropzone)
  204. function App() {
  205. return (
  206. <Provider store={store}>
  207. <div className="App">
  208. <ConnectDropzone />
  209. <ConnectTrackDropzone />
  210. </div>
  211. </Provider>
  212. );
  213. }
  214. export default App;