Skip to content
6 changes: 3 additions & 3 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Container, Content, Loader, useToaster, Notification, Button, Text } fr
import "rsuite/dist/rsuite.min.css";
import { SignIn } from "./Components/SignIn";
import MainHeader from "./Components/Header";
import { HashRouter, Route, Routes } from "react-router";
import { HashRouter, Route, Routes, useMatch } from "react-router";
import Home from "./Routes/Home";
import Playlists from "./Routes/Playlists";
import Collections from "./Routes/Collections";
Expand All @@ -14,7 +14,7 @@ import Playlist from "./Routes/Playlists/[id]";
import Album from "./Routes/Albums/[id]";
import Search from "./Routes/Search";
import AddItem from "./Components/AddItem";
import Queue from "./Routes/Queue";
import PlayState from "./Routes/PlayState";
import { isElectron, playItem } from "./Util/Helpers";
import { client } from "./Client/client.gen";
import { BaseItemDto, UserDto } from "./Client";
Expand Down Expand Up @@ -203,7 +203,7 @@ function App() {
<HashRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/queue" element={<Queue />} />
<Route path="/queue" element={<PlayState />} />
<Route path="/playlists" element={<Playlists />} />
<Route path="/playlists/:id" element={<Playlist />} />
<Route path="/collections" element={<Collections />} />
Expand Down
2 changes: 1 addition & 1 deletion src/Components/Fallback.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Icon from "./Icon";

export default function Fallback({ icon, text }: { icon: string; text: string }) {
return (
<Center align="middle" justify="center" width={"100%"} height={"100%"}>
<Center alignSelf="middle" justify="center" width={"100%"} height={"100%"}>
<VStack spacing={0}>
<Box alignSelf="center">
<Icon icon={icon} style={{ fontSize: "100px" }} />
Expand Down
5 changes: 4 additions & 1 deletion src/Components/ItemListEntry.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ export function ItemListEntry({
allItems,
setSortable,
parentId,
refresh
refresh,
props
}: {
item: BaseItemDto;
index: number;
Expand All @@ -23,6 +24,7 @@ export function ItemListEntry({
setSortable?: React.Dispatch<React.SetStateAction<boolean>>;
parentId?: string;
refresh?: () => void;
props?: React.HTMLAttributes<HTMLElement>;
}) {
const { setQueue, setPlaybackState } = useContext(GlobalState);
const [isFavorite, setIsFavorite] = useState(item.UserData?.IsFavorite || false);
Expand All @@ -35,6 +37,7 @@ export function ItemListEntry({
onClick={async () => {
playItem(setPlaybackState, setQueue, item, allItems);
}}
{...props}
>
<HStack spacing={15} alignItems="center">
{type == "queue" && (
Expand Down
2 changes: 1 addition & 1 deletion src/Components/PlayBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -475,7 +475,7 @@ export default function PlayBar(props: { state: PlaybackState }) {
/>
{visualizerOpen ? <Visualizer audioContextRef={audioContextRef} gainNodeRef={gainNodeRef} /> : <></>}
{lyricsOpen && <Lyrics state={props.state} position={position} />}
<Footer className={lyricsOpen || visualizerOpen ? "footer-overlay" : ""}>
<Footer className={lyricsOpen || visualizerOpen || location.hash.includes("queue") ? "footer-overlay" : ""}>
<Navbar className="now-playing">
<Col flex={1}>
<Row>
Expand Down
132 changes: 132 additions & 0 deletions src/Routes/PlayState.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import { useContext, useEffect, useRef, useState } from "react";
import { Center, Image, List, Stack, Text, VStack } from "rsuite";
import { GlobalState } from "../App";
import { getCacheStorage, getStorage } from "../storage";
import { ItemListEntry } from "../Components/ItemListEntry";
import Fallback from "../Components/Fallback";
import { getAlbumArt } from "../Util/Formatting";
import localforage from "localforage";
import { Blurhash, BlurhashCanvas } from "react-blurhash";

const cacheStorage = getCacheStorage();
const storage = getStorage();

function Queue() {
const { queue, setQueue, playbackState } = useContext(GlobalState);
const [sortable, setSortable] = useState(false);

const handleSortEnd = ({
oldIndex,
newIndex,
node
}: {
oldIndex: number;
newIndex: number;
collection: number | string;
node: HTMLElement;
}) =>
setQueue(() => {
const moveData = queue!.items.splice(oldIndex, 1);
const newData = [...queue!.items];
newData.splice(newIndex, 0, moveData[0]);
return { ...queue!, items: newData };
});

return (
<Stack.Item flex={1} className="queue" height={"100%"} overflow={"auto"}>
{!queue || !("items" in queue) || queue.items.length == 0 ? (
<Fallback icon="queue_music" text="Queue is empty" />
) : (
<>
<List bordered sortable={sortable} onSort={handleSortEnd}>
{queue.items.map((item, index) => {
return (
<ItemListEntry
props={{
style: {
backgroundColor: playbackState?.item?.Id == item.Id ? "rgba(40, 40, 40, 0.4)" : undefined
}
}}
item={item}
type="queue"
index={index}
key={item.Id}
allItems={queue.items}
setSortable={setSortable}
/>
);
})}
</List>
</>
)}
</Stack.Item>
);
}

export default function PlayState() {
const { playbackState } = useContext(GlobalState);
const [position, setPosition] = useState(0);

useEffect(() => {
let interval = setInterval(() => {
localforage.getItem<number>("position").then((pos) => {
setPosition(pos ?? 0);
});
}, 500);

return () => clearInterval(interval);
});

return (
<Stack
direction={{
xs: "column",
sm: "row"
}}
height={"100%"}
>
{playbackState && playbackState.item && playbackState.item.ImageBlurHashes?.Primary && (
<BlurhashCanvas
hash={playbackState.item.ImageBlurHashes.Primary[Object.keys(playbackState.item.ImageBlurHashes.Primary)[0]]}
width={500}
height={500}
className="background-blurhash"
/>
)}
<Center flex={1} height={"100%"} maxWidth={"50%"}>
{!playbackState || !playbackState.item ? (
<Fallback icon="play_circle_outline" text="Nothing is playing" />
) : (
<>
<VStack justifyContent="space-between" textAlign="center" align="center" spacing={20} width={"100%"} padding={15}>
<Image
borderRadius={"6px"}
maxWidth={"40vw"}
height={"40vh"}
onLoad={(e) => {
(e.target as HTMLElement).style.visibility = "visible";
}}
draggable={false}
userSelect={"none"}
src={
playbackState.item.Type == "Audio"
? getAlbumArt(playbackState.item)
: `${storage.get("serverURL")}/Items/${playbackState.item.Id}/Images/Primary`
}
/>
<VStack spacing={0} userSelect={"none"}>
<Text size="lg" width={"100%"}>
{playbackState.item.Name}
</Text>
<Text muted size="md" width={"100%"}>
{playbackState.item.Artists?.join(", ")}
</Text>
</VStack>
</VStack>
</>
)}
</Center>
<Queue />
</Stack>
);
}
49 changes: 0 additions & 49 deletions src/Routes/Queue.tsx

This file was deleted.

25 changes: 20 additions & 5 deletions src/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ i.material-icons.no-space {
}

.footer-overlay nav {
background-color: rgba(0, 0, 0, 0.8);
background-color: rgba(0, 0, 0, 0.8) !important;
}

*,
Expand Down Expand Up @@ -178,10 +178,6 @@ i.material-icons.no-space {
border-bottom-right-radius: 6px !important;
}

.queue {
height: 100%;
}

.rs-navbar-brand {
cursor: default;
color: unset !important;
Expand All @@ -197,6 +193,25 @@ i.material-icons.no-space {
padding-block-end: 0 !important;
}

.background-blurhash {
position: fixed !important;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
z-index: -2;
filter: brightness(15%) saturate(200%);
}

.queue {
border-radius: var(--rs-radius-md);
outline: 1px var(--rs-border-secondary) solid;
border: none;
.rs-list-item {
background-color: rgba(0, 0, 0, 0.4);
}
}

@media (max-width: 626px) {
.stop-btn {
display: none;
Expand Down