BREAKING CHANGE: feat: fix: ll

This commit is contained in:
OfficialDakari 2024-10-18 21:58:47 +05:00
parent 0f101b92e0
commit 36d689e2b6
5 changed files with 128 additions and 12 deletions

View File

@ -38,6 +38,7 @@ export default function AddPlaylistButton({ onCreate }: AddPlaylistButtonProps)
<TextField
name='playlistName'
label='Playlist name'
autoComplete='off'
fullWidth
/>
</DialogContent>

View File

@ -0,0 +1,39 @@
import { Box, Typography, useTheme } from "@mui/material";
import React from "react";
import { MotionBox } from "./MotionComponents";
import { MusicNote } from "@mui/icons-material";
type PlaylistCardProps = {
name: string;
onClick?: () => void;
};
export default function PlaylistCard({ name, onClick }: PlaylistCardProps) {
const theme = useTheme();
return (
<Box
display='flex'
flexDirection='column'
justifyContent='center'
onClick={onClick}
>
<MotionBox
width={120}
height={120}
display='flex'
justifyContent='center'
alignContent='center'
alignItems='center'
bgcolor='primary.main'
color='success.contrastText'
borderRadius={theme.shape.borderRadius}
whileHover={{ borderRadius: '30%', scale: 0.8 }}
whileTap={{ borderRadius: '40%', scale: 0.7, backgroundColor: theme.palette.info.main, rotate: '90deg' }}
>
<MusicNote sx={{ scale: 2 }} />
</MotionBox>
<Typography textAlign='center'>
New playlist
</Typography>
</Box>
);
}

View File

@ -1,25 +1,50 @@
import { Box, Typography, useTheme } from "@mui/material";
import React from "react";
import React, { useEffect, useState } from "react";
import LoadingCard from "../components/LoadingCard";
import Scroll from "../components/Scroll";
import Page, { PageRoot } from "../components/Page";
import AppHeader from "../components/AppHeader";
import AddPlaylistButton from "../components/AddPlaylistButton";
import { createPlaylist, getPlaylists, Playlist } from "../utils/MusicServer";
import useForceUpdate from "../hooks/useForceUpdate";
import PlaylistCard from "../components/PlaylistCard";
import useQueue from "../hooks/useQueue";
export default function Landing() {
const theme = useTheme();
const queue = useQueue();
const [playlists, setPlaylists] = useState<Playlist[]>([]);
const updatePlaylists = () => {
getPlaylists().then(setPlaylists);
};
const handleCreatePlaylist = (name: string) => {
console.log(`Creating playlist ${name}`);
createPlaylist(name);
updatePlaylists();
};
useEffect(() => {
updatePlaylists();
}, [theme]);
return (
<Page>
<AppHeader />
<PageRoot>
<Typography variant='button'>Playlists</Typography>
<Scroll display='flex' gap={theme.spacing(2)}>
{playlists.map(
playlist => (
<PlaylistCard
name={playlist.name}
onClick={() => {
if (playlist.songs.length === 0) return alert('There are no any songs in this playlist');
queue?.setSongs(playlist.songs);
}}
/>
)
)}
<AddPlaylistButton onCreate={handleCreatePlaylist} />
</Scroll>
</PageRoot>

View File

@ -3,17 +3,19 @@ import { PageRoot } from "../components/Page";
import AppHeader from "../components/AppHeader";
import repeatArray from "../utils/repeatArray";
import { LoadingSearchSongCard } from "../components/LoadingCard";
import { saveSong, searchSongs } from "../utils/MusicServer";
import { Box, IconButton, Snackbar } from "@mui/material";
import { addSong, getPlaylists, Playlist, removeSong, saveSong, searchSongs, Song } from "../utils/MusicServer";
import { Box, Checkbox, Dialog, DialogContent, DialogTitle, IconButton, List, ListItem, ListItemButton, ListItemText, Snackbar } from "@mui/material";
import { SearchSongCard } from "../components/SongCard";
import useQueue from "../hooks/useQueue";
import { Close } from "@mui/icons-material";
import { Check, Close, PlaylistAdd } from "@mui/icons-material";
export default function Search() {
const inputRef = useRef<HTMLInputElement>(null);
const [results, setResults] = useState<ReactNode[]>([]);
const queue = useQueue();
const [snackbar, setSnackbar] = useState<string | null>(null);
const [addTo, setAddTo] = useState<Song | null>(null);
const [playlists, setPlaylists] = useState<Playlist[]>([]);
const onKeyUp = async (evt: KeyboardEvent) => {
if (evt.key !== 'Enter') return;
const q = inputRef.current?.value;
@ -35,6 +37,14 @@ export default function Search() {
queue?.addSong(song);
setSnackbar(`Added "${song.name}" to queue.`);
}}
controls={
<IconButton onClick={() => {
setAddTo(song);
getPlaylists().then(setPlaylists);
}}>
<PlaylistAdd />
</IconButton>
}
/>
);
}
@ -43,6 +53,37 @@ export default function Search() {
return (
<>
<Dialog open={addTo ? true : false} onClose={() => setAddTo(null)}>
<DialogTitle>
Add to playlist
</DialogTitle>
<DialogContent>
<List>
{playlists.map(x => {
const added = x.songs.find(y => y.videoId === addTo?.videoId) ? true : false;
return (
<ListItem>
<Checkbox
onClick={() => {
if (!addTo) return;
if (added) {
removeSong(x.id, addTo);
} else {
addSong(x.id, addTo);
setAddTo(null);
}
}}
checked={added}
/>
<ListItemText>
{x.name}
</ListItemText>
</ListItem>
)
})}
</List>
</DialogContent>
</Dialog>
<AppHeader
showSearch
inputProps={{

View File

@ -1,6 +1,6 @@
import { saveAs } from 'file-saver';
const API_BASE = `http://localhost:33223`;
const API_BASE = `https://music-api.extera.xyz`;
export type Artist = {
name: string;
@ -30,6 +30,8 @@ export type Song = {
export type Playlist = {
owner: string;
id: string;
name: string;
songs: Song[];
};
@ -37,7 +39,8 @@ async function doPost(p: string, json: any) {
const jsonString = JSON.stringify(json);
const response = await fetch(API_BASE + p, {
headers: {
'Content-Type': 'application/json'
'Content-Type': 'application/json',
Authorization: localStorage.token ? `Bearer ${localStorage.token}` : ''
},
body: jsonString,
method: 'POST'
@ -47,14 +50,21 @@ async function doPost(p: string, json: any) {
}
async function doGet(p: string) {
const response = await fetch(API_BASE + p);
const response = await fetch(API_BASE + p, {
headers: {
Authorization: localStorage.token ? `Bearer ${localStorage.token}` : ''
}
});
if (!response.ok) throw await response.json();
return await response.json();
}
async function doDelete(p: string) {
const response = await fetch(API_BASE + p, {
method: 'DELETE'
method: 'DELETE',
headers: {
Authorization: localStorage.token ? `Bearer ${localStorage.token}` : ''
}
});
if (!response.ok) throw await response.json();
return await response.json();