коммит на всякий случай
This commit is contained in:
parent
ee17998948
commit
2fa22ec4c4
2
.env
2
.env
@ -1,2 +1,2 @@
|
|||||||
REACT_APP_DATABASE='http://localhost:3000/'
|
REACT_APP_DATABASE='http://localhost:3001/'
|
||||||
REACT_APP_RECAPTCHA_SITE_KEY='6LcZ-kkqAAAAAFdmy2tD1gKFdjxb0D71w5VMaisr'
|
REACT_APP_RECAPTCHA_SITE_KEY='6LcZ-kkqAAAAAFdmy2tD1gKFdjxb0D71w5VMaisr'
|
18
README.md
18
README.md
@ -1,18 +1,6 @@
|
|||||||
# PromoCursed
|
# PromoCursed
|
||||||
|
## Курсовая работа
|
||||||
|
|
||||||
ВАЖНАЯ ИНФОРМАЦИЯ!!!
|
### Описание
|
||||||
|
|
||||||
По какой-то причине при передаче объекта песни в CurrentTrack и получение свойств объекта в этом компонента
|
Веб-приложение для прослушивания музыки.
|
||||||
появляются ошибки. Их не было до какого-то момента, но я не успел к сроку выявить причины (я долго дебажил),
|
|
||||||
но результата это не принесло. До сих пор причины для меня остаются, к сожалению, загадкой...
|
|
||||||
|
|
||||||
Что было добавлено/исправлено после очной демонстрации:
|
|
||||||
|
|
||||||
1. Компоненты были раскиданы по папкам
|
|
||||||
2. Переименовал некоторые папки (например, API),
|
|
||||||
3. Добавил новые компоненты
|
|
||||||
4. Роутинг и страницы
|
|
||||||
5. Валидация форм
|
|
||||||
|
|
||||||
Должен был добавить плеер с воспроизведением песен, но из-за ошибок, про которые я написал выше, не удалось.
|
|
||||||
Пришлось жестко передать объект песни прямо в коде компонента.
|
|
||||||
|
75
data.json
75
data.json
@ -7,7 +7,7 @@
|
|||||||
"band_name": "Nevroz",
|
"band_name": "Nevroz",
|
||||||
"albumid": "1",
|
"albumid": "1",
|
||||||
"album_name": "Album 1",
|
"album_name": "Album 1",
|
||||||
"source": "/src/songs/Nirvana_-_Smells_Like_Teen_Spirit_75941061.mp3",
|
"source": "http://localhost:3001/songs_sources/nirvana.mp3",
|
||||||
"cover": "https://cdn1.ozone.ru/s3/multimedia-t/6893834213.jpg",
|
"cover": "https://cdn1.ozone.ru/s3/multimedia-t/6893834213.jpg",
|
||||||
"playlists": [
|
"playlists": [
|
||||||
"1",
|
"1",
|
||||||
@ -23,8 +23,8 @@
|
|||||||
"band_name": "Nevroz",
|
"band_name": "Nevroz",
|
||||||
"albumid": "1",
|
"albumid": "1",
|
||||||
"album_name": "Album 1",
|
"album_name": "Album 1",
|
||||||
"source": "/src/songs/Blur_-_Song_2_47967381.mp3",
|
"source": "http://localhost:3001/songs_sources/blur.mp3",
|
||||||
"cover": "https://cdn1.ozone.ru/s3/multimedia-t/6893834213.jpg",
|
"cover": "https://lastfm.freetls.fastly.net/i/u/ar0/101e39a435244b05cbf9d3af6ddf8c74.jpg",
|
||||||
"playlists": [
|
"playlists": [
|
||||||
"2"
|
"2"
|
||||||
],
|
],
|
||||||
@ -38,7 +38,7 @@
|
|||||||
"band_name": "noizemchik",
|
"band_name": "noizemchik",
|
||||||
"albumid": "2",
|
"albumid": "2",
|
||||||
"album_name": "Ругань из-за Стёпы",
|
"album_name": "Ругань из-за Стёпы",
|
||||||
"source": "/src/songs/Noice_MC_-_Rugan_iz-za_steny_63872179.mp3",
|
"source": "http://localhost:3001/songs_sources/noizemc.mp3",
|
||||||
"cover": "https://sun9-11.userapi.com/impg/yfCwuWXI6NkFVC2HvMlegM2qWLlenkeiiRyvNQ/jpH8m1wlLqs.jpg?size=604x604&quality=95&sign=60a1d1877b49631fb6078db88715fc88&c_uniq_tag=UKrig2vg02reyBH_ML0OKeqt5gm_2jpwEXD7NJBUXoQ&type=album"
|
"cover": "https://sun9-11.userapi.com/impg/yfCwuWXI6NkFVC2HvMlegM2qWLlenkeiiRyvNQ/jpH8m1wlLqs.jpg?size=604x604&quality=95&sign=60a1d1877b49631fb6078db88715fc88&c_uniq_tag=UKrig2vg02reyBH_ML0OKeqt5gm_2jpwEXD7NJBUXoQ&type=album"
|
||||||
,
|
,
|
||||||
"playlists": [
|
"playlists": [
|
||||||
@ -49,15 +49,15 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "4",
|
"id": "4",
|
||||||
"song_name": "Crazy Frog",
|
"song_name": "Хот вилз",
|
||||||
"band_id": "3",
|
"band_id": "1",
|
||||||
"band_name": "DSPD",
|
"band_name": "DSPD",
|
||||||
"albumid": "3",
|
"albumid": "1",
|
||||||
"album_name": "Album 3",
|
"album_name": "Album 1",
|
||||||
"source": "/src/songs/DSPD_-_Crazy_Frog_75801748.mp3",
|
"source": "http://localhost:3001/songs_sources/hotweelz.mp3",
|
||||||
"cover": "https://avatars.yandex.net/get-music-content/10129881/f3cf1afc.a.30561322-1/m1000x1000?webp=falseh",
|
"cover": "https://avatars.yandex.net/get-music-content/10129881/f3cf1afc.a.30561322-1/m1000x1000?webp=falseh",
|
||||||
"playlists": [
|
"playlists": [
|
||||||
"2", "3"
|
"1", "2"
|
||||||
],
|
],
|
||||||
"genreid": "1",
|
"genreid": "1",
|
||||||
"genre_name": "Rock"
|
"genre_name": "Rock"
|
||||||
@ -69,8 +69,8 @@
|
|||||||
"band_name": "DSPD",
|
"band_name": "DSPD",
|
||||||
"albumid": "3",
|
"albumid": "3",
|
||||||
"album_name": "Album 3",
|
"album_name": "Album 3",
|
||||||
"source": "/src/songs/DSPD feat. даня хренников - Кем я стал_(audio-lord.ru).mp3",
|
"source": "http://localhost:3001/songs_sources/kemstal.mp3",
|
||||||
"cover": "https://avatars.yandex.net/get-music-content/10129881/f3cf1afc.a.30561322-1/m1000x1000?webp=falseh",
|
"cover": "https://avatars.yandex.net/get-music-content/6058982/7e431a83.a.23910092-1/m1000x1000?webp=false",
|
||||||
"playlists": [
|
"playlists": [
|
||||||
"3"
|
"3"
|
||||||
],
|
],
|
||||||
@ -79,28 +79,29 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "6",
|
"id": "6",
|
||||||
"song_name": "Smells Like Poop",
|
"song_name": "American Idiotus",
|
||||||
"band_id": "1",
|
"band_id": "3",
|
||||||
"band_name": "Nevroz",
|
"band_name": "Red Day",
|
||||||
"albumid": "1",
|
"albumid": "3",
|
||||||
"album_name": "Album 1",
|
"album_name": "Album 3",
|
||||||
"source": "/src/songs/Nirvana_-_Smells_Like_Teen_Spirit_75941061.mp3",
|
"source": "http://localhost:3001/songs_sources/greenday.mp3",
|
||||||
"cover": "https://avatars.yandex.net/get-music-content/10129881/f3cf1afc.a.30561322-1/m1000x1000?webp=falseh",
|
"cover": "https://avatars.yandex.net/get-music-content/32236/d3846188.a.1001691-1/m1000x1000?webp=false",
|
||||||
"playlists": [
|
"playlists": [
|
||||||
"1", "2"
|
"2", "3"
|
||||||
],
|
],
|
||||||
"genreid": "1",
|
"genreid": "1",
|
||||||
"genre_name": "Rock"
|
"genre_name": "Rock"
|
||||||
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "7",
|
"id": "7",
|
||||||
"song_name": "Song 3",
|
"song_name": "Ulyanofication'",
|
||||||
"band_id": "1",
|
"band_id": "1",
|
||||||
"band_name": "Nevroz",
|
"band_name": "Yellow Warm Russian Tomatoes",
|
||||||
"albumid": "1",
|
"albumid": "1",
|
||||||
"album_name": "Album 1",
|
"album_name": "Album 1",
|
||||||
"source": "/src/songs/Blur_-_Song_2_47967381.mp3",
|
"source": "http://localhost:3001/songs_sources/pepers.mp3",
|
||||||
"cover": "https://avatars.yandex.net/get-music-content/10129881/f3cf1afc.a.30561322-1/m1000x1000?webp=falseh",
|
"cover": "https://avatars.mds.yandex.net/i?id=8d01018aa8cac0da9845314da42e050ab8e61713-12569754-images-thumbs&n=13",
|
||||||
"playlists": [
|
"playlists": [
|
||||||
"2"
|
"2"
|
||||||
],
|
],
|
||||||
@ -109,13 +110,13 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "8",
|
"id": "8",
|
||||||
"song_name": "Ругань из-за Стёпы",
|
"song_name": "СУМАСШЕДШИЙ ПОЕЗД!!!!",
|
||||||
"band_id": "2",
|
"band_id": "2",
|
||||||
"band_name": "noizemchik",
|
"band_name": "СТАРИНА ОЗЗИ ОЗБОРН (МУЗЫКА ДЬЯВОЛА)",
|
||||||
"albumid": "2",
|
"albumid": "2",
|
||||||
"album_name": "Ругань из-за Стёпы",
|
"album_name": "Ругань из-за Стёпы",
|
||||||
"source": "/src/songs/Noice_MC_-_Rugan_iz-za_steny_63872179.mp3",
|
"source": "http://localhost:3001/songs_sources/ozzy.mp3",
|
||||||
"cover": "https://avatars.yandex.net/get-music-content/10129881/f3cf1afc.a.30561322-1/m1000x1000?webp=falseh"
|
"cover": "https://avatars.mds.yandex.net/i?id=95adb2fda305e542621b7d9757d70ddd_l-4937470-images-thumbs&n=13"
|
||||||
,
|
,
|
||||||
"playlists": [
|
"playlists": [
|
||||||
"1", "3"
|
"1", "3"
|
||||||
@ -125,13 +126,13 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "9",
|
"id": "9",
|
||||||
"song_name": "Crazy Frog",
|
"song_name": "Downstairs to Heaven",
|
||||||
"band_id": "3",
|
"band_id": "3",
|
||||||
"band_name": "DSPD",
|
"band_name": "Эйси Диси",
|
||||||
"albumid": "3",
|
"albumid": "3",
|
||||||
"album_name": "Album 3",
|
"album_name": "Album 3",
|
||||||
"source": "/src/songs/DSPD_-_Crazy_Frog_75801748.mp3",
|
"source": "http://localhost:3001/songs_sources/acdc.mp3",
|
||||||
"cover": "https://avatars.yandex.net/get-music-content/10129881/f3cf1afc.a.30561322-1/m1000x1000?webp=falseh",
|
"cover": "https://avatars.mds.yandex.net/get-entity_search/509339/292583724/S600xU",
|
||||||
"playlists": [
|
"playlists": [
|
||||||
"2", "3"
|
"2", "3"
|
||||||
],
|
],
|
||||||
@ -140,13 +141,13 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "10",
|
"id": "10",
|
||||||
"song_name": "Кем я стал",
|
"song_name": "Ай воз мэйд фор лавин ю беби",
|
||||||
"band_id": "3",
|
"band_id": "3",
|
||||||
"band_name": "DSPD",
|
"band_name": "KISS",
|
||||||
"albumid": "3",
|
"albumid": "3",
|
||||||
"album_name": "Album 3",
|
"album_name": "Album 3",
|
||||||
"source": "/src/songs/DSPD feat. даня хренников - Кем я стал_(audio-lord.ru).mp3",
|
"source": "http://localhost:3001/songs_sources/kiss.mp3",
|
||||||
"cover": "https://avatars.yandex.net/get-music-content/10129881/f3cf1afc.a.30561322-1/m1000x1000?webp=falseh",
|
"cover": "https://avatars.dzeninfra.ru/get-zen_doc/10073791/pub_64ab1cf9ec43747a7a73a3e0_64ab1d726ba4132c8f8b37b6/scale_1200",
|
||||||
"playlists": [
|
"playlists": [
|
||||||
"3"
|
"3"
|
||||||
],
|
],
|
||||||
@ -160,7 +161,7 @@
|
|||||||
"band_name": "DSPD",
|
"band_name": "DSPD",
|
||||||
"albumid": "3",
|
"albumid": "3",
|
||||||
"album_name": "Album 3",
|
"album_name": "Album 3",
|
||||||
"source": "/src/songs/DSPD feat. даня хренников - Кем я стал_(audio-lord.ru).mp3",
|
"source": "http://localhost:3001/songs_sources/kemstal.mp3",
|
||||||
"cover": "https://avatars.yandex.net/get-music-content/10129881/f3cf1afc.a.30561322-1/m1000x1000?webp=falseh",
|
"cover": "https://avatars.yandex.net/get-music-content/10129881/f3cf1afc.a.30561322-1/m1000x1000?webp=falseh",
|
||||||
"playlists": [
|
"playlists": [
|
||||||
"3"
|
"3"
|
||||||
|
@ -33,7 +33,7 @@
|
|||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "react-dotenv && set port=5000 && react-scripts start",
|
"start": "react-dotenv && set port=5000 && react-scripts start",
|
||||||
"json-server": "npx json-server data.json --port 3000",
|
"json-server": "npx json-server data.json --port 3001 --static ./public/songs_sources",
|
||||||
"build": "react-dotenv && react-scripts build",
|
"build": "react-dotenv && react-scripts build",
|
||||||
"test": "react-dotenv && react-scripts test",
|
"test": "react-dotenv && react-scripts test",
|
||||||
"eject": "react-dotenv && react-scripts eject",
|
"eject": "react-dotenv && react-scripts eject",
|
||||||
|
BIN
public/songs_sources/acdc.mp3
Normal file
BIN
public/songs_sources/acdc.mp3
Normal file
Binary file not shown.
BIN
public/songs_sources/greenday.mp3
Normal file
BIN
public/songs_sources/greenday.mp3
Normal file
Binary file not shown.
BIN
public/songs_sources/hotweelz.mp3
Normal file
BIN
public/songs_sources/hotweelz.mp3
Normal file
Binary file not shown.
BIN
public/songs_sources/kiss.mp3
Normal file
BIN
public/songs_sources/kiss.mp3
Normal file
Binary file not shown.
BIN
public/songs_sources/ozzy.mp3
Normal file
BIN
public/songs_sources/ozzy.mp3
Normal file
Binary file not shown.
BIN
public/songs_sources/pepers.mp3
Normal file
BIN
public/songs_sources/pepers.mp3
Normal file
Binary file not shown.
@ -27,7 +27,7 @@ export async function getSongs() {
|
|||||||
|
|
||||||
export async function getSong(id: string) {
|
export async function getSong(id: string) {
|
||||||
|
|
||||||
return await axios.get<IPlaylist[]>(`${localhost}songs/` + id);
|
return await axios.get<ISong>(`${localhost}songs/` + id);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
34
src/App.css
34
src/App.css
@ -77,7 +77,12 @@
|
|||||||
-webkit-box-shadow: 0px -26px 19px 0px rgba(34, 60, 80, 0.2);
|
-webkit-box-shadow: 0px -26px 19px 0px rgba(34, 60, 80, 0.2);
|
||||||
-moz-box-shadow: 0px -26px 19px 0px rgba(34, 60, 80, 0.2);
|
-moz-box-shadow: 0px -26px 19px 0px rgba(34, 60, 80, 0.2);
|
||||||
box-shadow: 0px -26px 19px 0px rgba(34, 60, 80, 0.2);
|
box-shadow: 0px -26px 19px 0px rgba(34, 60, 80, 0.2);
|
||||||
|
transform-origin: bottom center;
|
||||||
|
animation: fadeInAndMoveUp 0.2s ease-out forwards;
|
||||||
|
}
|
||||||
|
@keyframes fadeInAndMoveUp {
|
||||||
|
from { opacity: 0; transform: translateY(50px); }
|
||||||
|
to { opacity: 1; transform: translateY(0); }
|
||||||
}
|
}
|
||||||
|
|
||||||
.song-cover {
|
.song-cover {
|
||||||
@ -92,20 +97,40 @@
|
|||||||
.song-table-row {
|
.song-table-row {
|
||||||
margin-top: 50px !important;
|
margin-top: 50px !important;
|
||||||
margin-bottom: 50px !important;
|
margin-bottom: 50px !important;
|
||||||
|
padding: 30px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.song-table-row:hover {
|
.song-table-row:hover {
|
||||||
outline: #9d0000 solid 3px !important;
|
outline: #9d0000 solid 3px !important;
|
||||||
outline-offset: -1px !important;
|
outline-offset: -2px !important;
|
||||||
|
color: #7c0000 !important;
|
||||||
|
background-color: #fcfcfc !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.song-table-row-active {
|
||||||
|
outline-offset: -2px !important;
|
||||||
|
|
||||||
|
background-color: #e7e7e7 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-table-tbody {
|
||||||
|
padding: 40 !important;
|
||||||
|
}
|
||||||
|
.ant-table-tbody > tr > td {
|
||||||
|
padding: 40 !important;
|
||||||
|
}
|
||||||
|
|
||||||
.ant-table-cell {
|
.ant-table-cell {
|
||||||
padding: 8px !important;
|
padding: 8px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ant-table-tbody > tr {
|
||||||
|
padding-top: 20px !important;
|
||||||
|
padding-bottom: 20px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.ant-table-cell:nth-child(1) {
|
.ant-table-cell:nth-child(1) {
|
||||||
padding: 2px !important;
|
padding: 1px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.play-song-button {
|
.play-song-button {
|
||||||
@ -142,6 +167,7 @@
|
|||||||
color: grey;
|
color: grey;
|
||||||
font-size: 30px !important;
|
font-size: 30px !important;
|
||||||
padding-right: 5px;
|
padding-right: 5px;
|
||||||
|
height: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.current-track-button {
|
.current-track-button {
|
||||||
|
12
src/App.tsx
12
src/App.tsx
@ -7,21 +7,29 @@ import { Loginpage } from './pages/Loginpage';
|
|||||||
import { Registerpage } from './pages/Registerpage';
|
import { Registerpage } from './pages/Registerpage';
|
||||||
import { Profilepage } from './pages/Profilepage';
|
import { Profilepage } from './pages/Profilepage';
|
||||||
import { Layout } from './components/layoutComponents/Layout';
|
import { Layout } from './components/layoutComponents/Layout';
|
||||||
|
import { CurrentSongProvider } from './contexts/SongContexts/SongContextProvider';
|
||||||
|
import { PlayingProvider } from './contexts/SongContexts/PlayingProvider';
|
||||||
|
import { VolumeProvider } from './contexts/VolumeContexts/VolumeProvider';
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
||||||
<div className="App bg-slate-100">
|
<div className="App bg-slate-100">
|
||||||
<div id="app" className='bg-white'>
|
<div id="app" className='bg-white'>
|
||||||
|
<CurrentSongProvider songId=''>
|
||||||
|
<PlayingProvider isPlaying={false}>
|
||||||
|
<VolumeProvider volumeValue={30}>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path='/' element={<Layout />}>
|
<Route path='/' element={<Layout />}>
|
||||||
<Route index element={<Homepage song_id='2'/>} />
|
<Route index element={<Homepage />} />
|
||||||
<Route path='login' element={<Loginpage />} />
|
<Route path='login' element={<Loginpage />} />
|
||||||
<Route path='profile' element={<Profilepage />} />
|
<Route path='profile' element={<Profilepage />} />
|
||||||
</Route>
|
</Route>
|
||||||
|
|
||||||
</Routes>
|
</Routes>
|
||||||
|
</VolumeProvider>
|
||||||
|
</PlayingProvider>
|
||||||
|
</CurrentSongProvider>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
@ -8,10 +8,16 @@ import { AdBlock } from './AdBlock';
|
|||||||
import { Footer } from './Footer';
|
import { Footer } from './Footer';
|
||||||
import { Header } from './Header';
|
import { Header } from './Header';
|
||||||
import { MenuBlock } from './MenuBlock';
|
import { MenuBlock } from './MenuBlock';
|
||||||
|
import { useEffect, useRef } from 'react';
|
||||||
|
import { usePlayingContext } from '../../contexts/SongContexts/PlayingProvider';
|
||||||
|
|
||||||
|
|
||||||
export function Layout() {
|
export function Layout() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<><Header />
|
|
||||||
|
<>
|
||||||
|
<Header />
|
||||||
<div id='main-content'>
|
<div id='main-content'>
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</div>
|
</div>
|
||||||
|
@ -17,6 +17,7 @@ interface MenuBlockProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function MenuBlock({playlists, songs, albums, genres}: MenuBlockProps) {
|
export function MenuBlock({playlists, songs, albums, genres}: MenuBlockProps) {
|
||||||
|
|
||||||
const newSongsTab = {
|
const newSongsTab = {
|
||||||
label: 'Новинки',
|
label: 'Новинки',
|
||||||
key: 'New',
|
key: 'New',
|
||||||
@ -39,7 +40,8 @@ export function MenuBlock({playlists, songs, albums, genres}: MenuBlockProps) {
|
|||||||
key: 'Recomendations',
|
key: 'Recomendations',
|
||||||
children: songs? <NewSongsBlock /> : null
|
children: songs? <NewSongsBlock /> : null
|
||||||
};
|
};
|
||||||
const tabs = [recTab, newSongsTab, chartTab, playlistsTab];
|
const tabs = [recTab, newSongsTab, chartTab];
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex" style={{display: 'flex', alignSelf: 'flex-start', width: 'fit-content'}}>
|
<div className="flex" style={{display: 'flex', alignSelf: 'flex-start', width: 'fit-content'}}>
|
||||||
|
@ -7,7 +7,7 @@ import { PlayCircleFilled, PlayCircleOutlined } from '@ant-design/icons';
|
|||||||
import { getSongs } from '../../API/api';
|
import { getSongs } from '../../API/api';
|
||||||
import Title from 'antd/es/typography/Title';
|
import Title from 'antd/es/typography/Title';
|
||||||
import { GetColumns } from './Templates/songsTemplate';
|
import { GetColumns } from './Templates/songsTemplate';
|
||||||
import { getColumnsWithNumber } from './Templates/numeredSongsTemplate';
|
import { GetColumnsWithNumber } from './Templates/numeredSongsTemplate';
|
||||||
|
|
||||||
// Функция для генерации номеров строк
|
// Функция для генерации номеров строк
|
||||||
const generateRowNumbers = (rows: any[]) =>
|
const generateRowNumbers = (rows: any[]) =>
|
||||||
@ -30,16 +30,19 @@ export function ChartSongsBlock() {
|
|||||||
return (
|
return (
|
||||||
<div className="song-block flex border-slate-100">
|
<div className="song-block flex border-slate-100">
|
||||||
<Table
|
<Table
|
||||||
title={() => <Title level={4}>Чарт</Title>}
|
title={() => <Title style={{textAlign: 'center'}} level={4}>Чарт</Title>}
|
||||||
style={{ width: '100%', backgroundColor: 'transparent' }}
|
style={{ width: '100%', backgroundColor: 'transparent', overflowX: 'auto' }}
|
||||||
className="songs-table menu-block"
|
className="songs-table menu-block"
|
||||||
dataSource={numberedSongs}
|
dataSource={numberedSongs}
|
||||||
columns={getColumnsWithNumber(numberedSongs)}
|
columns={GetColumnsWithNumber(numberedSongs)}
|
||||||
showHeader={false}
|
showHeader={false}
|
||||||
pagination={false}
|
pagination={false}
|
||||||
rowClassName={() => 'song-table-row'}
|
rowClassName={() => 'song-table-row chart-row'}
|
||||||
rowKey={(record) => record.id}
|
rowKey={(record) => record.id}
|
||||||
|
scroll={{ x: true }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -3,16 +3,21 @@ import { render } from '@testing-library/react';
|
|||||||
import {ISong} from '../../models/IModels';
|
import {ISong} from '../../models/IModels';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { Table, Button, Empty } from 'antd';
|
import { Table, Button, Empty } from 'antd';
|
||||||
import { PlayCircleFilled, PlayCircleOutlined } from '@ant-design/icons';
|
import { Loading3QuartersOutlined, PlayCircleFilled, PlayCircleOutlined } from '@ant-design/icons';
|
||||||
import { getSongs } from '../../API/api';
|
import { getSongs } from '../../API/api';
|
||||||
import Title from 'antd/es/typography/Title';
|
import Title from 'antd/es/typography/Title';
|
||||||
import { GetColumns } from './Templates/songsTemplate';
|
import { GetColumns } from './Templates/songsTemplate';
|
||||||
|
import { Spin } from "antd";
|
||||||
|
import { useCurrentSongContext } from '../../contexts/SongContexts/SongContextProvider';
|
||||||
|
import { usePlayingContext } from '../../contexts/SongContexts/PlayingProvider';
|
||||||
|
|
||||||
export function NewSongsBlock() {
|
export function NewSongsBlock() {
|
||||||
|
|
||||||
const [songs, setSongs] = useState<ISong[]>([]);
|
const [songs, setSongs] = useState<ISong[]>([]);
|
||||||
|
|
||||||
|
const currentSongId = useCurrentSongContext();
|
||||||
|
const isPlaying = usePlayingContext();
|
||||||
|
|
||||||
const fetchData = async () => {
|
const fetchData = async () => {
|
||||||
const response = getSongs();
|
const response = getSongs();
|
||||||
setSongs((await response).data);
|
setSongs((await response).data);
|
||||||
@ -25,6 +30,7 @@ export function NewSongsBlock() {
|
|||||||
return (
|
return (
|
||||||
<div className="song-block flex border-slate-100">
|
<div className="song-block flex border-slate-100">
|
||||||
|
|
||||||
|
<Spin indicator={<Loading3QuartersOutlined spin style={{color: '#9d0000', fontSize: 30}} />} spinning={songs.length === 0}>
|
||||||
<Table
|
<Table
|
||||||
title={() => <Title level={4}>Горячие новинки</Title>}
|
title={() => <Title level={4}>Горячие новинки</Title>}
|
||||||
style={{width: '100%', backgroundColor: 'transparent'}}
|
style={{width: '100%', backgroundColor: 'transparent'}}
|
||||||
@ -33,9 +39,11 @@ export function NewSongsBlock() {
|
|||||||
columns={GetColumns(songs)}
|
columns={GetColumns(songs)}
|
||||||
showHeader={false}
|
showHeader={false}
|
||||||
pagination={false}
|
pagination={false}
|
||||||
rowClassName={() => 'song-table-row'}
|
rowClassName={(record) => isPlaying ? (record.id === currentSongId.songId ? 'song-table-row-active' : 'song-table-row') : 'song-table-row'}
|
||||||
rowKey={(s: ISong) => s.id}
|
rowKey={(s: ISong) => s.id}
|
||||||
|
rowHoverable={false}
|
||||||
/>
|
/>
|
||||||
|
</Spin>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
@ -1,8 +1,18 @@
|
|||||||
import { Button } from "antd";
|
import { Button } from "antd";
|
||||||
import { ISong } from "../../../models/IModels";
|
import { ISong } from "../../../models/IModels";
|
||||||
import PlayCircleFilled from "@ant-design/icons/lib/icons/PlayCircleFilled";
|
import { useState } from "react";
|
||||||
|
import { useCurrentSongContext } from "../../../contexts/SongContexts/SongContextProvider";
|
||||||
|
import { usePlayingContext } from "../../../contexts/SongContexts/PlayingProvider";
|
||||||
|
|
||||||
export function getColumnsWithNumber(songs: ISong[]) {
|
const useColumnsWithNumber = (songs: ISong[]) => {
|
||||||
|
|
||||||
|
const {isPlaying, setIsPlaying} = usePlayingContext();
|
||||||
|
const { songId, setSongId } = useCurrentSongContext();
|
||||||
|
|
||||||
|
const handlePlayClick = (songId: string) => {
|
||||||
|
setIsPlaying(!isPlaying);
|
||||||
|
setSongId(songId);
|
||||||
|
};
|
||||||
|
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
@ -23,21 +33,23 @@ export function getColumnsWithNumber(songs: ISong[]) {
|
|||||||
width: 100,
|
width: 100,
|
||||||
|
|
||||||
render: (text: string, song: ISong) => (
|
render: (text: string, song: ISong) => (
|
||||||
<Button data-content="▷" className='play-song-button rounded' style={{background: 'transparent', display: 'contents'}}>
|
<Button data-content={!isPlaying && song.id === songId ? '┃┃' : "▷"}
|
||||||
|
className='play-song-button rounded' style={{background: 'transparent', display: 'contents'}}
|
||||||
|
onClick={() => handlePlayClick(song.id)}>
|
||||||
|
|
||||||
<img
|
<img
|
||||||
className="rounded "
|
className="rounded "
|
||||||
src={song.cover}
|
src={song.cover}
|
||||||
alt={song.song_name}
|
alt={song.song_name}
|
||||||
/>
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '',
|
title: '',
|
||||||
dataIndex: 'song_name',
|
dataIndex: 'song_name',
|
||||||
key: 'song_name',
|
key: 'song_name',
|
||||||
width: 350
|
width: 400
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '',
|
title: '',
|
||||||
@ -49,8 +61,12 @@ export function getColumnsWithNumber(songs: ISong[]) {
|
|||||||
title: '',
|
title: '',
|
||||||
dataIndex: 'album_name',
|
dataIndex: 'album_name',
|
||||||
key: 'album_name',
|
key: 'album_name',
|
||||||
width: 400
|
width: 350
|
||||||
}
|
}
|
||||||
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function GetColumnsWithNumber(songs: ISong[]) {
|
||||||
|
return useColumnsWithNumber(songs);
|
||||||
|
}
|
||||||
|
@ -1,15 +1,21 @@
|
|||||||
import { Button } from "antd";
|
import { Button } from "antd";
|
||||||
import { ISong } from "../../../models/IModels";
|
import { ISong } from "../../../models/IModels";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { PauseCircleFilled, PlayCircleFilled } from "@ant-design/icons";
|
import { height } from "@mui/system";
|
||||||
|
import { useCurrentSongContext } from "../../../contexts/SongContexts/SongContextProvider";
|
||||||
|
import { usePlayingContext } from "../../../contexts/SongContexts/PlayingProvider";
|
||||||
|
|
||||||
const useColumns = (songs: ISong[])=> {
|
const useColumns = (songs: ISong[])=> {
|
||||||
const [isPlaying, setIsPlaying] = useState(false);
|
|
||||||
|
|
||||||
const handlePlayClick = () => {
|
const {isPlaying, setIsPlaying} = usePlayingContext();
|
||||||
|
const { songId, setSongId } = useCurrentSongContext();
|
||||||
|
|
||||||
|
const handlePlayClick = (songId: string) => {
|
||||||
setIsPlaying(!isPlaying);
|
setIsPlaying(!isPlaying);
|
||||||
|
setSongId(songId);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
title: 'Play',
|
title: 'Play',
|
||||||
@ -18,8 +24,12 @@ const useColumns = (songs: ISong[]) => {
|
|||||||
width: 100,
|
width: 100,
|
||||||
|
|
||||||
render: (text: string, song: ISong) => (
|
render: (text: string, song: ISong) => (
|
||||||
// Тут в дата-контент вставляется JSON коды иконок
|
<Button
|
||||||
<Button data-content={isPlaying ? "\u23EF\uFE0F" : "▷"} className='play-song-button rounded' style={{background: 'transparent', display: 'contents'}} onClick={handlePlayClick}>
|
data-content={isPlaying && song.id === songId ? '┃┃' : "▷"}
|
||||||
|
className='play-song-button rounded'
|
||||||
|
style={{background: 'transparent', display: 'contents'}}
|
||||||
|
onClick={() => handlePlayClick(song.id)}
|
||||||
|
>
|
||||||
|
|
||||||
<img
|
<img
|
||||||
className="rounded"
|
className="rounded"
|
||||||
@ -27,8 +37,6 @@ const useColumns = (songs: ISong[]) => {
|
|||||||
alt={song.song_name}
|
alt={song.song_name}
|
||||||
/>
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
|
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -56,4 +64,3 @@ const useColumns = (songs: ISong[]) => {
|
|||||||
export function GetColumns(songs: ISong[]) {
|
export function GetColumns(songs: ISong[]) {
|
||||||
return useColumns(songs);
|
return useColumns(songs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,73 +1,218 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState, useRef } from 'react';
|
||||||
import { Button, Col, Grid, Row, Table } from 'antd';
|
import { Button, Col, Grid, Menu, Row, Slider, Space, Table } from 'antd';
|
||||||
import { BackwardFilled, FastForwardFilled, FastBackwardFilled, PlayCircleFilled, PlayCircleOutlined, StepBackwardFilled, StepForwardFilled, HeartOutlined, ShareAltOutlined, PauseCircleFilled, PauseCircleOutlined } from '@ant-design/icons';
|
import { FastForwardFilled, FastBackwardFilled, PlayCircleOutlined, HeartOutlined, ShareAltOutlined, PauseCircleOutlined, SoundOutlined, MutedOutlined, SoundFilled } from '@ant-design/icons';
|
||||||
import { ISong, SongProps } from '../../models/IModels';
|
import { ISong, SongProps } from '../../models/IModels';
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
import useSound from 'use-sound';
|
import { useCurrentSongContext } from '../../contexts/SongContexts/SongContextProvider';
|
||||||
|
import { usePlayingContext } from '../../contexts/SongContexts/PlayingProvider';
|
||||||
|
import Dropdown from 'antd/es/dropdown/dropdown';
|
||||||
|
import Text from 'antd/es/typography/Text';
|
||||||
|
import { useVolumeContext } from '../../contexts/VolumeContexts/VolumeProvider';
|
||||||
|
|
||||||
const Song = styled.div`
|
const Song = styled.div`
|
||||||
align-items: center;
|
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
width: 75%;`
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 0 0px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background-color: #fff;
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
padding: 0 10px;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
type CurrentSongProps = {
|
type CurrentSongProps = {
|
||||||
song: ISong
|
song: ISong
|
||||||
|
songs: ISong[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export function CurrentTrack({ song }: CurrentSongProps) {
|
const SongName = styled.span`
|
||||||
|
display: inline-block;
|
||||||
|
max-width: 200px; /* Максимальная ширина */
|
||||||
|
white-space: nowrap; /* Предотвращает перенос слов на новую строку */
|
||||||
|
overflow: hidden; /* Скрывает текст, выходящий за пределы контейнера */
|
||||||
|
text-overflow: ellipsis; /* Добавляет точку-эллипсис */
|
||||||
|
padding-right: 20px; /* Добавляем небольшой отступ справа для точки */
|
||||||
|
`;
|
||||||
|
|
||||||
const [isPlaying, setIsPlaying] = useState(false);
|
export function CurrentTrack({ song , songs}: CurrentSongProps) {
|
||||||
|
|
||||||
const soundUrl = song?.source || ''; // Пустая строка в случае отсутствия source
|
const audioPlayer = useRef<HTMLAudioElement | null>(null);
|
||||||
|
|
||||||
|
const soundUrl = song?.source;
|
||||||
|
|
||||||
|
const Playlist = songs;
|
||||||
|
|
||||||
const [play, { pause, duration, sound }] = useSound(soundUrl, { volume: 0.5 });
|
const {isPlaying, setIsPlaying} = usePlayingContext();
|
||||||
|
|
||||||
|
const { songId, setSongId } = useCurrentSongContext();
|
||||||
|
|
||||||
const playingButton = () => {
|
const { volumeValue: volume, setVolumeValue: setVolume } = useVolumeContext();
|
||||||
|
|
||||||
|
const [ elapsed, setElapsed] = useState(0);
|
||||||
|
|
||||||
|
const [ duration, setDuration ] = useState(0);
|
||||||
|
|
||||||
|
const [ muted, setMuted ] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
if (isPlaying) {
|
if (isPlaying) {
|
||||||
pause();
|
audioPlayer.current?.play();
|
||||||
setIsPlaying(false);
|
}
|
||||||
} else {
|
else {
|
||||||
play();
|
audioPlayer.current?.pause();
|
||||||
setIsPlaying(true);
|
}
|
||||||
|
}, [isPlaying]);
|
||||||
|
|
||||||
|
const togglePlay = () => {
|
||||||
|
if (audioPlayer.current && audioPlayer.current.src) {
|
||||||
|
setIsPlaying(!isPlaying);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const prevTrack = () => {
|
||||||
|
if (parseInt(songId) > 0) {
|
||||||
|
const newId = (parseInt(songId) - 1).toString();
|
||||||
|
setSongId(newId);
|
||||||
|
togglePlay();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const nextTrack = () => {
|
||||||
|
if (parseInt(songId) < songs.length - 1) {
|
||||||
|
const newId = (parseInt(songId) + 1).toString();
|
||||||
|
setSongId(newId);
|
||||||
|
togglePlay();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (song) {
|
if (song) {
|
||||||
play();
|
audioPlayer.current?.play();
|
||||||
setIsPlaying(true);
|
setIsPlaying(true);
|
||||||
}
|
}
|
||||||
}, [song]);
|
}, [song]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (audioPlayer.current) {
|
||||||
|
audioPlayer.current.muted = muted;
|
||||||
|
}
|
||||||
|
}, [muted]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (audioPlayer.current) {
|
||||||
|
audioPlayer.current.volume = volume / 100;
|
||||||
|
|
||||||
|
if (isPlaying) {
|
||||||
|
setInterval(() => {
|
||||||
|
const duration = Math.floor(audioPlayer?.current?.duration ?? 0);
|
||||||
|
const currentTime = Math.floor(audioPlayer?.current?.currentTime ?? 0);
|
||||||
|
|
||||||
|
setDuration(duration - currentTime);
|
||||||
|
setElapsed(currentTime);
|
||||||
|
|
||||||
|
if (currentTime === duration) {
|
||||||
|
nextTrack();
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [volume, isPlaying]);
|
||||||
|
|
||||||
|
const formatTime = (time: number) => {
|
||||||
|
if (time) {
|
||||||
|
const minutes = Math.floor(time / 60);
|
||||||
|
const seconds = Math.floor(time % 60);
|
||||||
|
return `${minutes}:${seconds < 10 ? `0${seconds}` : seconds}`;
|
||||||
|
}
|
||||||
|
return '0:00';
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Song>
|
<Song>
|
||||||
<div className="current-track">
|
<audio ref={audioPlayer} src={soundUrl} />
|
||||||
<Row className="w-full h-full bg-white opacity-90 rounded" style={{ display: 'flex', alignItems: 'center' }}>
|
<div key={song.id} className="current-track">
|
||||||
<Col span={4} className="flex flex-direction-row justify-center">
|
<span className="w-full h-full bg-white opacity-90 rounded" style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||||
<Button type="link" icon={<FastBackwardFilled className="player-button" />}></Button>
|
<span className='inline-flex' style={{ justifyContent: 'flex-center', alignSelf: 'flex-center', width: '27%' }}>
|
||||||
|
<span className="flex flex-direction-row" style={{ alignItems: 'center', paddingLeft: 10, paddingRight: 10 }}>
|
||||||
|
<Button type="link" icon={<FastBackwardFilled className="player-button" />} onClick={prevTrack}></Button>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
type="link"
|
type="link"
|
||||||
icon={isPlaying ? <PauseCircleOutlined className="player-button" onClick={playingButton} /> : <PlayCircleOutlined className="player-button" onClick={playingButton} />}
|
icon={isPlaying ?
|
||||||
></Button>
|
<PauseCircleOutlined
|
||||||
<Button type="link" icon={<FastForwardFilled className="player-button" style={{ fontSize: 30 }} />}></Button>
|
className="player-button"
|
||||||
</Col>
|
onClick={togglePlay} /> : <PlayCircleOutlined className="player-button" onClick={togglePlay} />}
|
||||||
<Col style={{ width: 80 }}>
|
/>
|
||||||
|
|
||||||
|
<Button type="link" icon={<FastForwardFilled className="player-button" style={{ fontSize: 30 }} />} onClick={nextTrack}></Button>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span style={{ width: 80, display: 'flex', alignSelf: 'flex-end' }}>
|
||||||
<img src={song.cover} alt={song.cover} className="current-song-cover" />
|
<img src={song.cover} alt={song.cover} className="current-song-cover" />
|
||||||
</Col>
|
</span>
|
||||||
<Col span={16}>
|
|
||||||
<Row style={{ paddingLeft: 10 }}>{song.song_name}</Row>
|
<span className="flex flex-direction-column justify-center items-start" style={{ paddingRight: 5, flexDirection: 'column'}} >
|
||||||
<Row style={{ paddingLeft: 10 }}>{song.band_name}</Row>
|
<SongName style={{ paddingLeft: 10}}>{song.song_name}</SongName>
|
||||||
</Col>
|
<SongName style={{ paddingLeft: 10 }}>{song.band_name}</SongName>
|
||||||
<Col>
|
</span>
|
||||||
|
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span className='inline-flex' style={{ alignItems: 'center', width: '100%', justifyContent: 'stretch', flexDirection: 'row' }}>
|
||||||
|
<Text style={{ width: '5%' }}>{formatTime(elapsed)}</Text>
|
||||||
|
<Slider
|
||||||
|
max={audioPlayer.current?.duration ?? 0}
|
||||||
|
step={1}
|
||||||
|
defaultValue={0}
|
||||||
|
value={elapsed}
|
||||||
|
styles={ { track: { backgroundColor: '#ad0000' }, rail: { backgroundColor: '#e9e9e9' }, handle: { color: '#ad0000',} }}
|
||||||
|
style={{ width: '100%', justifyContent: 'flex-center', alignSelf: 'flex-center' }}
|
||||||
|
tooltip={{ open: false }}
|
||||||
|
onChange={(value) => {
|
||||||
|
if (audioPlayer.current) {
|
||||||
|
audioPlayer.current.currentTime = value;
|
||||||
|
if (value >= audioPlayer.current.duration) {
|
||||||
|
nextTrack();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Text style={{ width: '5%' }}>{formatTime(audioPlayer.current?.duration ?? 0)}</Text>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span className='inline-flex' style={{ width: '14%', paddingRight: 10, justifyContent: 'flex-end', alignItems: 'center' }}>
|
||||||
|
<Dropdown overlay={<Menu>
|
||||||
|
<Menu.Item key="Slider">
|
||||||
|
<Slider
|
||||||
|
styles={ { track: { backgroundColor: '#ad0000' }, rail: { backgroundColor: '#e9e9e9' }, handle: { color: '#ad0000',} }}
|
||||||
|
defaultValue={30} value={volume} style={{ width: 150, color: 'red' }} onChange={(value) => setVolume(value)} />
|
||||||
|
</Menu.Item>
|
||||||
|
</Menu>}
|
||||||
|
trigger={['hover']}
|
||||||
|
placement='top'>
|
||||||
|
<Button type="link" style={{ width: 30}}
|
||||||
|
icon={ muted || volume === 0 ?
|
||||||
|
<SoundFilled
|
||||||
|
style={{ color: '#ad0000'}} className="current-track-button" /> : volume <= 25 ?
|
||||||
|
<SoundFilled className="current-track-button" /> : <SoundOutlined className="current-track-button" />}
|
||||||
|
onClick={() => setMuted(!muted)}
|
||||||
|
/>
|
||||||
|
</Dropdown>
|
||||||
<Button type="link" icon={<HeartOutlined className="current-track-button" />}></Button>
|
<Button type="link" icon={<HeartOutlined className="current-track-button" />}></Button>
|
||||||
<Button type="link" icon={<ShareAltOutlined className="current-track-button" />}></Button>
|
<Button type="link" icon={<ShareAltOutlined className="current-track-button" />}></Button>
|
||||||
</Col>
|
</span>
|
||||||
</Row>
|
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</Song>
|
</Song>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
1
src/contexts/AppContexts/AppContextProvider.tsx
Normal file
1
src/contexts/AppContexts/AppContextProvider.tsx
Normal file
@ -0,0 +1 @@
|
|||||||
|
export {}
|
32
src/contexts/SongContexts/PlayingProvider.tsx
Normal file
32
src/contexts/SongContexts/PlayingProvider.tsx
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import React, { createContext, useContext, ReactNode, useState } from "react";
|
||||||
|
|
||||||
|
interface PlayingProviderProps {
|
||||||
|
children: ReactNode;
|
||||||
|
isPlaying: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PlayingContextValue {
|
||||||
|
isPlaying: boolean;
|
||||||
|
setIsPlaying: (isPlaying: boolean) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const PlayingContext = createContext<PlayingContextValue | undefined>(undefined);
|
||||||
|
|
||||||
|
export function PlayingProvider({ children, isPlaying }: PlayingProviderProps) {
|
||||||
|
const [isPlayingSong, setIsPlaying] = useState(isPlaying);
|
||||||
|
console.log(isPlayingSong);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PlayingContext.Provider value={{ isPlaying: isPlayingSong, setIsPlaying }}>
|
||||||
|
{children}
|
||||||
|
</PlayingContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const usePlayingContext = function(): PlayingContextValue {
|
||||||
|
const context = useContext(PlayingContext);
|
||||||
|
if (context === undefined) {
|
||||||
|
throw new Error("usePlayingContext must be used within a PlayingProvider");
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
};
|
31
src/contexts/SongContexts/SongContextProvider.tsx
Normal file
31
src/contexts/SongContexts/SongContextProvider.tsx
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import React, { createContext, useContext, ReactNode, useState } from "react";
|
||||||
|
|
||||||
|
interface CurrentSongProviderProps {
|
||||||
|
children: ReactNode;
|
||||||
|
songId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SongContextValue {
|
||||||
|
songId: string;
|
||||||
|
setSongId: (songId: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const CurrentSongContext = createContext<SongContextValue | undefined>(undefined);
|
||||||
|
|
||||||
|
export const CurrentSongProvider: React.FC<CurrentSongProviderProps> = ({ children, songId }) => {
|
||||||
|
const [currentSongId, setCurrentSongId] = useState(songId);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<CurrentSongContext.Provider value={{ songId: currentSongId, setSongId: setCurrentSongId }}>
|
||||||
|
{children}
|
||||||
|
</CurrentSongContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export const useCurrentSongContext = function(): SongContextValue {
|
||||||
|
const context = useContext(CurrentSongContext);
|
||||||
|
if (context === undefined) {
|
||||||
|
throw new Error("useCurrentSongContext must be used within a CurrentSongProvider");
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
};
|
31
src/contexts/VolumeContexts/VolumeProvider.tsx
Normal file
31
src/contexts/VolumeContexts/VolumeProvider.tsx
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import React, { createContext, useContext, ReactNode, useState } from "react";
|
||||||
|
|
||||||
|
interface VolumeProviderProps {
|
||||||
|
children: ReactNode;
|
||||||
|
volumeValue: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SongContextValue {
|
||||||
|
volumeValue: number;
|
||||||
|
setVolumeValue: (value: number) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const VolumeContext = createContext<SongContextValue | undefined>(undefined);
|
||||||
|
|
||||||
|
export const VolumeProvider: React.FC<VolumeProviderProps> = ({ children, volumeValue: value }) => {
|
||||||
|
const [volumeId, setVolumeId] = useState(value);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<VolumeContext.Provider value={{ volumeValue: volumeId, setVolumeValue: setVolumeId }}>
|
||||||
|
{children}
|
||||||
|
</VolumeContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export const useVolumeContext = function(): SongContextValue {
|
||||||
|
const context = useContext(VolumeContext);
|
||||||
|
if (context === undefined) {
|
||||||
|
throw new Error("useVolumeContext must be used within a VolumeProvider");
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
};
|
@ -1,13 +1,13 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { getSongs, getGenres, getPlaylists, getAds } from "../API/api";
|
import { getSongs, getGenres, getPlaylists, getAds, getSong } from "../API/api";
|
||||||
import { AdBlock } from "../components/layoutComponents/AdBlock";
|
import { AdBlock } from "../components/layoutComponents/AdBlock";
|
||||||
import { MenuBlock } from "../components/layoutComponents/MenuBlock";
|
import { MenuBlock } from "../components/layoutComponents/MenuBlock";
|
||||||
import { CurrentTrack } from "../components/songComponents/CurrentTrack";
|
import { CurrentTrack } from "../components/songComponents/CurrentTrack";
|
||||||
import { IPlaylist, ISong, IAlbum, IGenre, IAdvertisement, IBand } from "../models/IModels";
|
import { IPlaylist, ISong, IAlbum, IGenre, IAdvertisement, IBand } from "../models/IModels";
|
||||||
|
import { useCurrentSongContext } from "../contexts/SongContexts/SongContextProvider";
|
||||||
|
|
||||||
|
|
||||||
export function Homepage({song_id = "0"}) {
|
export function Homepage() {
|
||||||
const [currentSong, setCurrentSong] = useState<ISong | null>(null);
|
|
||||||
|
|
||||||
const [albums, setAlbums] = useState<IAlbum[]>([]);
|
const [albums, setAlbums] = useState<IAlbum[]>([]);
|
||||||
const [songs, setSongs] = useState<ISong[]>([]);
|
const [songs, setSongs] = useState<ISong[]>([]);
|
||||||
@ -17,6 +17,8 @@ export function Homepage({song_id = "0"}) {
|
|||||||
const [playlists, setPlaylist] = React.useState<IPlaylist[]>([]);
|
const [playlists, setPlaylist] = React.useState<IPlaylist[]>([]);
|
||||||
const [genres, setGenres] = React.useState<IGenre[]>([]);
|
const [genres, setGenres] = React.useState<IGenre[]>([]);
|
||||||
|
|
||||||
|
const currentSongId = useCurrentSongContext();
|
||||||
|
|
||||||
const fetchData = async () => {
|
const fetchData = async () => {
|
||||||
const [responseSongs, responseAds, responsePlaylists, responseGenres] = await Promise.all([
|
const [responseSongs, responseAds, responsePlaylists, responseGenres] = await Promise.all([
|
||||||
getSongs(),
|
getSongs(),
|
||||||
@ -35,10 +37,15 @@ export function Homepage({song_id = "0"}) {
|
|||||||
fetchData();
|
fetchData();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const currentSong = songs.find(song => song.id === currentSongId.songId);
|
||||||
|
console.log('Айди песни:');
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<MenuBlock playlists={[]} songs={songs} albums={albums} genres={[]}/><AdBlock ads={ads}/>
|
<MenuBlock playlists={[]} songs={songs} albums={albums} genres={[]}/><AdBlock ads={ads}/>
|
||||||
{currentSong && <CurrentTrack song={currentSong} />}
|
{currentSong && <CurrentTrack song={currentSong} songs={songs}/>}
|
||||||
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
@ -3,7 +3,6 @@ import { Form, Input, Button, Checkbox } from "antd";
|
|||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import ReCAPTCHA from "react-google-recaptcha";
|
import ReCAPTCHA from "react-google-recaptcha";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import FormItem from "antd/es/form/FormItem";
|
|
||||||
|
|
||||||
export function Registerpage() {
|
export function Registerpage() {
|
||||||
const [verified, setVerified] = useState(false);
|
const [verified, setVerified] = useState(false);
|
||||||
|
Loading…
Reference in New Issue
Block a user