197 lines
5.8 KiB
TypeScript
197 lines
5.8 KiB
TypeScript
import {
|
|
ActionIcon,
|
|
Box,
|
|
Button,
|
|
Divider,
|
|
Group,
|
|
NativeSelect,
|
|
Stack,
|
|
Text,
|
|
Title
|
|
} from '@mantine/core';
|
|
import {
|
|
SpotifyLogoIcon,
|
|
DevicesIcon,
|
|
SignOutIcon,
|
|
ArrowsClockwiseIcon
|
|
} from '@phosphor-icons/react';
|
|
import { Trans, useLingui } from '@lingui/react/macro';
|
|
import { useSpotify } from '@/lib/spotify/hooks';
|
|
import { useAuth } from '@/contexts/auth-context';
|
|
import Sheet from '@/components/sheet/sheet';
|
|
|
|
interface SpotifySheetProps {
|
|
opened: boolean;
|
|
onClose: () => void;
|
|
}
|
|
|
|
const SpotifySheet: React.FC<SpotifySheetProps> = ({ opened, onClose }) => {
|
|
const { roles } = useAuth();
|
|
const isAdmin = roles?.includes('Admin') || false;
|
|
const spotify = useSpotify();
|
|
const { t } = useLingui();
|
|
|
|
if (!isAdmin || !spotify) return null;
|
|
const {
|
|
isAuthenticated,
|
|
login,
|
|
logout,
|
|
devices,
|
|
activeDevice,
|
|
currentTrack,
|
|
getDevices,
|
|
setActiveDevice,
|
|
isLoading,
|
|
error,
|
|
} = spotify;
|
|
|
|
if (!isAdmin) return null;
|
|
|
|
const handleDeviceChange = (deviceId: string) => {
|
|
if (deviceId && deviceId !== activeDevice?.id) {
|
|
setActiveDevice(deviceId);
|
|
}
|
|
};
|
|
|
|
const refreshDevices = () => {
|
|
getDevices();
|
|
};
|
|
|
|
const handleChange = (opened: boolean) => {
|
|
if (!opened) onClose();
|
|
};
|
|
|
|
return (
|
|
<Sheet
|
|
opened={opened}
|
|
onChange={handleChange}
|
|
title={t`Spotify Controls`}
|
|
>
|
|
<Stack gap="lg">
|
|
{!isAuthenticated ? (
|
|
<>
|
|
<Box>
|
|
<Text size="sm" c="dimmed" mb="md">
|
|
<Trans>Connect your Spotify account</Trans>
|
|
</Text>
|
|
</Box>
|
|
|
|
<Button
|
|
leftSection={<SpotifyLogoIcon size={20} />}
|
|
color="green"
|
|
size="lg"
|
|
onClick={login}
|
|
loading={isLoading}
|
|
fullWidth
|
|
>
|
|
<Trans>Connect with Spotify</Trans>
|
|
</Button>
|
|
</>
|
|
) : (
|
|
<>
|
|
{/* Current track display */}
|
|
{currentTrack && (
|
|
<>
|
|
<Box>
|
|
<Title order={5} mb="xs"><Trans>Now Playing</Trans></Title>
|
|
<Group gap="md">
|
|
{currentTrack.album.images[2] && (
|
|
<img
|
|
src={currentTrack.album.images[2].url}
|
|
alt={currentTrack.album.name}
|
|
style={{ width: 64, height: 64, borderRadius: 4 }}
|
|
/>
|
|
)}
|
|
<Box flex={1}>
|
|
<Text fw={600} size="sm" truncate>
|
|
{currentTrack.name}
|
|
</Text>
|
|
<Text size="xs" c="dimmed" truncate>
|
|
{currentTrack.artists.map(a => a.name).join(', ')}
|
|
</Text>
|
|
<Text size="xs" c="dimmed" truncate>
|
|
{currentTrack.album.name}
|
|
</Text>
|
|
</Box>
|
|
</Group>
|
|
</Box>
|
|
<Divider />
|
|
</>
|
|
)}
|
|
|
|
<Box>
|
|
<Group justify="space-between" align="center" mb="sm">
|
|
<Title order={5}>
|
|
<Group gap="xs">
|
|
<DevicesIcon size={20} />
|
|
<Trans>Select Device</Trans>
|
|
</Group>
|
|
</Title>
|
|
<ActionIcon
|
|
variant="light"
|
|
size="sm"
|
|
onClick={refreshDevices}
|
|
loading={isLoading}
|
|
>
|
|
<ArrowsClockwiseIcon size={16} />
|
|
</ActionIcon>
|
|
</Group>
|
|
|
|
{devices.length > 0 ? (
|
|
<NativeSelect
|
|
value={activeDevice?.id || ''}
|
|
onChange={(event) => handleDeviceChange(event.currentTarget.value)}
|
|
data={[
|
|
{ value: '', label: t`Select a device...` },
|
|
...devices.map(device => ({
|
|
value: device.id,
|
|
label: `${device.name} ${device.is_active ? t`(Active)` : ''} - ${device.type}`,
|
|
})),
|
|
]}
|
|
disabled={isLoading}
|
|
/>
|
|
) : (
|
|
<Text size="sm" c="dimmed">
|
|
<Trans>No devices found.</Trans>
|
|
</Text>
|
|
)}
|
|
|
|
{activeDevice && (
|
|
<Text size="xs" c="dimmed" mt="xs">
|
|
<Trans>Active device: {activeDevice.name}</Trans>
|
|
{activeDevice.volume_percent !== null &&
|
|
t` (Volume: ${activeDevice.volume_percent}%)`
|
|
}
|
|
</Text>
|
|
)}
|
|
</Box>
|
|
|
|
{error && (
|
|
<>
|
|
<Divider />
|
|
<Box>
|
|
<Text size="sm" c="red">
|
|
{error}
|
|
</Text>
|
|
</Box>
|
|
</>
|
|
)}
|
|
|
|
<Divider />
|
|
<Button
|
|
leftSection={<SignOutIcon size={18} />}
|
|
variant="light"
|
|
color="red"
|
|
onClick={logout}
|
|
fullWidth
|
|
>
|
|
<Trans>Disconnect Spotify</Trans>
|
|
</Button>
|
|
</>
|
|
)}
|
|
</Stack>
|
|
</Sheet>
|
|
);
|
|
};
|
|
|
|
export default SpotifySheet; |