151 lines
4.5 KiB
TypeScript
151 lines
4.5 KiB
TypeScript
import { SlidePanelField } from "@/components/sheet/slide-panel";
|
|
import { Stack, Text, Group, Avatar } from "@mantine/core";
|
|
import { UseFormReturnType } from "@mantine/form";
|
|
import { TeamInput } from "../../types";
|
|
import { useMemo } from "react";
|
|
import { SpotifyTrack } from "@/lib/spotify/types";
|
|
import SongSearch from "./song-search";
|
|
import DurationPicker from "./duration-picker";
|
|
import SongSummary from "./song-summary";
|
|
import { MusicNote } from "@phosphor-icons/react/dist/ssr";
|
|
import { MusicNoteIcon } from "@phosphor-icons/react";
|
|
|
|
interface Song {
|
|
song_id: string;
|
|
song_name: string;
|
|
song_artist: string;
|
|
song_album: string;
|
|
song_start?: number;
|
|
song_end?: number;
|
|
song_image_url: string;
|
|
duration_ms?: number;
|
|
}
|
|
|
|
interface SongPickerProps {
|
|
form: UseFormReturnType<TeamInput>;
|
|
error?: string;
|
|
}
|
|
|
|
const SongPicker = ({ form, error }: SongPickerProps) => {
|
|
const currentSong = useMemo((): Song | undefined => {
|
|
const values = form.getValues();
|
|
if (values.song_id && values.song_name) {
|
|
return {
|
|
song_id: values.song_id,
|
|
song_name: values.song_name,
|
|
song_artist: values.song_artist || "",
|
|
song_album: values.song_album || "",
|
|
song_start: values.song_start,
|
|
song_end: values.song_end,
|
|
song_image_url: values.song_image_url || "",
|
|
};
|
|
}
|
|
return undefined;
|
|
}, [form.values.song_id, form.values.song_name, form.values.song_artist, form.values.song_album, form.values.song_image_url, form.values.song_start, form.values.song_end]);
|
|
|
|
return (
|
|
<SlidePanelField
|
|
key={"song-picker"}
|
|
value={currentSong}
|
|
formatValue={(song) => <SongSummary song={song} />}
|
|
onChange={(updatedSong: Song) => {
|
|
if (updatedSong) {
|
|
form.setValues({
|
|
song_id: updatedSong.song_id,
|
|
song_name: updatedSong.song_name,
|
|
song_artist: updatedSong.song_artist,
|
|
song_album: updatedSong.song_album,
|
|
song_start: updatedSong.song_start,
|
|
song_end: updatedSong.song_end,
|
|
song_image_url: updatedSong.song_image_url,
|
|
});
|
|
}
|
|
}}
|
|
error={error}
|
|
Component={SongPickerComponent}
|
|
componentProps={{}}
|
|
title={"Select Song"}
|
|
label={"Walkout Song"}
|
|
placeholder={"Select your walkout song"}
|
|
/>
|
|
);
|
|
};
|
|
|
|
interface SongPickerComponentProps {
|
|
value: Song | undefined;
|
|
onChange: (song: Song) => void;
|
|
}
|
|
|
|
const SongPickerComponent = ({ value: song, onChange }: SongPickerComponentProps) => {
|
|
const handleSongSelect = (track: SpotifyTrack) => {
|
|
const defaultStart = 0;
|
|
const defaultEnd = Math.min(15, Math.floor(track.duration_ms / 1000));
|
|
|
|
const newSong: Song = {
|
|
song_id: track.id,
|
|
song_name: track.name,
|
|
song_artist: track.artists.map(a => a.name).join(', '),
|
|
song_album: track.album.name,
|
|
song_image_url: track.album.images[0]?.url || '',
|
|
song_start: defaultStart,
|
|
song_end: defaultEnd,
|
|
duration_ms: track.duration_ms,
|
|
};
|
|
|
|
onChange(newSong);
|
|
};
|
|
|
|
const handleDurationChange = (startSeconds: number, endSeconds: number) => {
|
|
if (song) {
|
|
onChange({
|
|
...song,
|
|
song_start: startSeconds,
|
|
song_end: endSeconds,
|
|
});
|
|
}
|
|
};
|
|
|
|
return (
|
|
<Stack styles={{ root: { width: '100%' }}}>
|
|
<Text size="lg" fw={500}>Search for a Song</Text>
|
|
|
|
<SongSearch onChange={handleSongSelect} />
|
|
|
|
<Stack gap="lg" mt="md">
|
|
<Group gap="sm">
|
|
<Avatar
|
|
src={song?.song_image_url || null}
|
|
size={60}
|
|
radius="md"
|
|
bg="transparent"
|
|
>
|
|
{!song?.song_image_url && <MusicNoteIcon size={24} color="var(--mantine-color-dimmed)" />}
|
|
</Avatar>
|
|
<div>
|
|
<Text size="sm" fw={500} c={song?.song_name ? undefined : "dimmed"}>
|
|
{song?.song_name || "No song selected"}
|
|
</Text>
|
|
<Text size="xs" c="dimmed">
|
|
{song?.song_artist || "Select a song to see details"}
|
|
</Text>
|
|
<Text size="xs" c="dimmed">
|
|
{song?.song_album || ""}
|
|
</Text>
|
|
</div>
|
|
</Group>
|
|
|
|
<Stack gap="xs">
|
|
<DurationPicker
|
|
songDurationMs={song?.duration_ms || 180000}
|
|
initialStart={song?.song_start || 0}
|
|
initialEnd={song?.song_end || 15}
|
|
onChange={handleDurationChange}
|
|
disabled={!song?.song_id}
|
|
/>
|
|
</Stack>
|
|
</Stack>
|
|
</Stack>
|
|
);
|
|
};
|
|
|
|
export default SongPicker; |