feat: Added shuffle feature for playlists (#199)

* Improved page transition when opening video player for the first time.
* Disabled prefetching for index pages.
This commit is contained in:
Mani
2024-03-14 23:24:27 +01:00
parent 5e265b3a92
commit 963707c82e
8 changed files with 63 additions and 9 deletions
@@ -49,7 +49,6 @@ export function AlphabeticalIndex<T extends AlphabeticalIndexItem>({ items, chil
key={firstLetter}
href={`#${firstLetter}`}
passHref
prefetch={false}
legacyBehavior>
<Text as="a" link>{firstLetter.toUpperCase()} </Text>
</Link>
+1
View File
@@ -8,6 +8,7 @@ const axios = Axios.create({
"X-Requested-With": "XMLHttpRequest",
},
withCredentials: true,
withXSRFToken: true,
});
export default axios;
+16 -1
View File
@@ -88,6 +88,8 @@ export default function MyApp({ Component, pageProps }: AppProps) {
});
const currentWatchListItem = watchList.find((item) => item.watchListId === currentWatchListItemId) ?? null;
const [isWaitingForVideoPage, setWaitingForVideoPage] = useState(!isVideoPage);
const [isAutoPlay, setAutoPlay] = useLocalStorageState("auto-play", { defaultValue: false });
const [isForceAutoPlay, setForceAutoPlay] = useState(false);
@@ -125,6 +127,19 @@ export default function MyApp({ Component, pageProps }: AppProps) {
return () => window.removeEventListener("keydown", hotkeyListener);
}, [pageProps.isSearch, router]);
// Prevent the video player from opening in the background while the video page is still loading.
// This should only happen if the video player is opening for the first time and not on
// subsequent page transitions.
if (isVideoPage && currentWatchListItem && isWaitingForVideoPage) {
setWaitingForVideoPage(false);
return null;
} else if (currentWatchListItem === null && !isWaitingForVideoPage) {
setWaitingForVideoPage(true);
return null;
}
if (isVideoPage && currentBasename !== previousBasename) {
setPreviousBasename(currentBasename);
if (currentBasename !== currentWatchListItem?.basename) {
@@ -240,7 +255,7 @@ export default function MyApp({ Component, pageProps }: AppProps) {
<Footer />
</>
) : null}
{currentWatchListItem && (
{currentWatchListItem && !isWaitingForVideoPage && (
<VideoPlayer2
video={currentWatchListItem}
background={!isVideoPage}
+3 -1
View File
@@ -22,7 +22,9 @@ export default function AnimeIndexPage({ animeAll }: AnimeIndexPageProps) {
key={anime.slug}
href={`/anime/${anime.slug}`}
passHref
legacyBehavior>
legacyBehavior
prefetch={false}
>
<Text as="a" block link>{anime.name}</Text>
</Link>
)}
+3 -1
View File
@@ -22,7 +22,9 @@ export default function ArtistIndexPage({ artistAll }: ArtistIndexPageProps) {
key={artist.slug}
href={`/artist/${artist.slug}`}
passHref
legacyBehavior>
legacyBehavior
prefetch={false}
>
<Text as="a" block link>{artist.name}</Text>
</Link>
)}
+34 -3
View File
@@ -31,14 +31,14 @@ import { SEO } from "components/seo";
import { SidebarContainer } from "components/container";
import { MultiCoverImage } from "components/image";
import { Column } from "components/box";
import { Button, FilterToggleButton } from "components/button";
import { Button, FilterToggleButton, IconTextButton } from "components/button";
import { Collapse } from "components/utils";
import { SearchFilterGroup, SearchFilterSortBy } from "components/search-filter";
import styled from "styled-components";
import theme from "theme";
import { DescriptionList } from "components/description-list";
import { Icon } from "components/icon";
import { faEllipsisV, faListMusic, faMinus, faPlus, faTrophy } from "@fortawesome/pro-solid-svg-icons";
import { faEllipsisV, faListMusic, faMinus, faPlus, faShuffle, faTrophy } from "@fortawesome/pro-solid-svg-icons";
import { PlaylistTrackAddDialog } from "components/dialog/PlaylistTrackAddDialog";
import { PlaylistTrackRemoveDialog } from "components/dialog/PlaylistTrackRemoveDialog";
import useSWR from "swr";
@@ -49,6 +49,9 @@ import { PlaylistEditDialog } from "components/dialog/PlaylistEditDialog";
import { Reorder } from "framer-motion";
import axios from "lib/client/axios";
import { FeaturedTheme } from "components/featured-theme";
import { shuffle } from "lodash-es";
import createVideoSlug from "../../../utils/createVideoSlug";
import { useRouter } from "next/router";
const StyledDesktopOnly = styled.div`
gap: 24px;
@@ -59,8 +62,12 @@ const StyledDesktopOnly = styled.div`
`;
const StyledHeader = styled.div`
display: flex;
justify-content: space-between;
align-items: center;
gap: 16px;
& > :last-child {
margin-inline-start: auto;
}
`;
const StyledReorderContainer = styled.div`
// Hack to style the framer motion reorder component
@@ -113,6 +120,7 @@ interface PlaylistDetailPageParams extends ParsedUrlQuery {
export default function PlaylistDetailPage({ playlist: initialPlaylist, me: initialMe }: PlaylistDetailPageProps) {
const { setWatchList, setWatchListFactory, setCurrentWatchListItem, addWatchListItem, addWatchListItemNext } = useContext(PlayerContext);
const router = useRouter();
const { data: playlist, mutate } = useSWR(
["PlaylistDetailPagePlaylist", `/api/playlist/${initialPlaylist.id}`],
@@ -193,6 +201,26 @@ export default function PlaylistDetailPage({ playlist: initialPlaylist, me: init
setCurrentWatchListItem(watchList[initiatingVideoIndex]);
}
function shuffleAll() {
if (tracksSorted.length === 0) {
return;
}
const watchList = shuffle(tracksSorted.map((track) => createWatchListItem(track.video)));
setWatchList(watchList, true);
setWatchListFactory(null);
setCurrentWatchListItem(watchList[0]);
const video = watchList[0];
const entry = video.entries[0];
const theme = entry.theme;
const anime = theme?.anime;
if (anime && entry && video) {
const videoSlug = createVideoSlug(theme, entry, video);
void router.push(`/anime/${anime.slug}/${videoSlug}`);
}
}
async function updateTrackOrder(newTracks: typeof tracks) {
await mutate({
...playlist,
@@ -315,6 +343,9 @@ export default function PlaylistDetailPage({ playlist: initialPlaylist, me: init
Themes
<Text color="text-disabled"> ({playlist.tracks_count})</Text>
</Text>
{tracksSorted.length > 0 && (
<IconTextButton icon={faShuffle} collapsible onClick={shuffleAll}>Shuffle All</IconTextButton>
)}
<FilterToggleButton onClick={toggleShowFilter}/>
</StyledHeader>
<Collapse collapse={!showFilter}>
+3 -1
View File
@@ -22,7 +22,9 @@ export default function SeriesIndexPage({ seriesAll }: SeriesIndexPageProps) {
key={series.slug}
href={`/series/${series.slug}`}
passHref
legacyBehavior>
legacyBehavior
prefetch={false}
>
<Text as="a" block link>{series.name}</Text>
</Link>
)}
+3 -1
View File
@@ -22,7 +22,9 @@ export default function StudioIndexPage({ studioAll }: StudioIndexPageProps) {
key={studio.slug}
href={`/studio/${studio.slug}`}
passHref
legacyBehavior>
legacyBehavior
prefetch={false}
>
<Text as="a" block link>{studio.name}</Text>
</Link>
)}