diff --git a/src/generated/graphql.ts b/src/generated/graphql.ts index a193b93..bd4ad21 100644 --- a/src/generated/graphql.ts +++ b/src/generated/graphql.ts @@ -194,7 +194,9 @@ export type PlaylistSearchResult = EntitySearchResult & { export type PlaylistTrack = { id: Scalars['String']; + next: Maybe; playlist: Playlist; + previous: Maybe; video: Video; }; @@ -868,14 +870,14 @@ export type PlaylistDetailPagePlaylistQueryVariables = Exact<{ }>; -export type PlaylistDetailPagePlaylistQuery = { playlist: { id: string, name: string, visibility: PlaylistVisibility, tracks_count: number, forward: Array<{ id: string, video: { id: number, basename: string, tags: string, entries: Array<{ id: number, version: number | null, theme: { id: number, type: string, sequence: number | null, anime: { year: number | null, season: string | null, slug: string, name: string, images: Array<{ link: string, facet: string | null }> }, group: { name: string, slug: string } | null, song: { title: string | null, performances: Array<{ as: string | null, artist: { slug: string, name: string } }> } | null } }>, audio: { basename: string } } }>, user: { name: string } } | null }; +export type PlaylistDetailPagePlaylistQuery = { playlist: { id: string, name: string, visibility: PlaylistVisibility, tracks_count: number, tracks: Array<{ id: string, video: { id: number, basename: string, tags: string, entries: Array<{ id: number, version: number | null, theme: { id: number, type: string, sequence: number | null, anime: { year: number | null, season: string | null, slug: string, name: string, images: Array<{ link: string, facet: string | null }> }, group: { name: string, slug: string } | null, song: { title: string | null, performances: Array<{ as: string | null, artist: { slug: string, name: string } }> } | null } }>, audio: { basename: string } }, previous: { id: string } | null, next: { id: string } | null }>, user: { name: string } } | null }; export type PlaylistDetailPageMeQueryVariables = Exact<{ [key: string]: never; }>; export type PlaylistDetailPageMeQuery = { me: { user: { name: string } | null } }; -export type PlaylistDetailPagePlaylistFragment = { id: string, name: string, visibility: PlaylistVisibility, tracks_count: number, forward: Array<{ id: string, video: { id: number, basename: string, tags: string, entries: Array<{ id: number, version: number | null, theme: { id: number, type: string, sequence: number | null, anime: { year: number | null, season: string | null, slug: string, name: string, images: Array<{ link: string, facet: string | null }> }, group: { name: string, slug: string } | null, song: { title: string | null, performances: Array<{ as: string | null, artist: { slug: string, name: string } }> } | null } }>, audio: { basename: string } } }>, user: { name: string } }; +export type PlaylistDetailPagePlaylistFragment = { id: string, name: string, visibility: PlaylistVisibility, tracks_count: number, tracks: Array<{ id: string, video: { id: number, basename: string, tags: string, entries: Array<{ id: number, version: number | null, theme: { id: number, type: string, sequence: number | null, anime: { year: number | null, season: string | null, slug: string, name: string, images: Array<{ link: string, facet: string | null }> }, group: { name: string, slug: string } | null, song: { title: string | null, performances: Array<{ as: string | null, artist: { slug: string, name: string } }> } | null } }>, audio: { basename: string } }, previous: { id: string } | null, next: { id: string } | null }>, user: { name: string } }; type PlaylistDetailPageUser_UserAuth_Fragment = { name: string }; @@ -888,7 +890,7 @@ export type PlaylistDetailPageQueryVariables = Exact<{ }>; -export type PlaylistDetailPageQuery = { playlist: { id: string, name: string, visibility: PlaylistVisibility, tracks_count: number, forward: Array<{ id: string, video: { id: number, basename: string, tags: string, entries: Array<{ id: number, version: number | null, theme: { id: number, type: string, sequence: number | null, anime: { year: number | null, season: string | null, slug: string, name: string, images: Array<{ link: string, facet: string | null }> }, group: { name: string, slug: string } | null, song: { title: string | null, performances: Array<{ as: string | null, artist: { slug: string, name: string } }> } | null } }>, audio: { basename: string } } }>, user: { name: string } } | null, me: { user: { name: string } | null } }; +export type PlaylistDetailPageQuery = { playlist: { id: string, name: string, visibility: PlaylistVisibility, tracks_count: number, tracks: Array<{ id: string, video: { id: number, basename: string, tags: string, entries: Array<{ id: number, version: number | null, theme: { id: number, type: string, sequence: number | null, anime: { year: number | null, season: string | null, slug: string, name: string, images: Array<{ link: string, facet: string | null }> }, group: { name: string, slug: string } | null, song: { title: string | null, performances: Array<{ as: string | null, artist: { slug: string, name: string } }> } | null } }>, audio: { basename: string } }, previous: { id: string } | null, next: { id: string } | null }>, user: { name: string } } | null, me: { user: { name: string } | null } }; export type GalleryPageQueryVariables = Exact<{ [key: string]: never; }>; diff --git a/src/lib/common/animethemes/api.ts b/src/lib/common/animethemes/api.ts index 0fab554..e8b11f7 100644 --- a/src/lib/common/animethemes/api.ts +++ b/src/lib/common/animethemes/api.ts @@ -76,6 +76,8 @@ export const INCLUDES = { PlaylistTrack: { video: "video", playlist: "playlist", + previous: "previous", + next: "next", }, UserAuth: { permissions: "permissions", @@ -113,7 +115,7 @@ const ALLOWED_INCLUDES: Record> = { "animethemes.group", "images", "resources", - "studios", + "studios.images", "animethemes.animethemeentries.videos.audio", "animethemes.animethemeentries.videos.videoscript", "animethemes.animethemeentries.videos.tracks.playlist", @@ -179,6 +181,13 @@ const ALLOWED_INCLUDES: Record> = { Playlist: [ "user", "tracks", + "tracks.video.animethemeentries.animetheme.song.artists", + "tracks.video.audio", + "tracks.video.animethemeentries.animetheme.anime.images", + "tracks.video", + "tracks.video.animethemeentries.animetheme.group", + "tracks.previous", + "tracks.next", ], PlaylistTrack: [ "video.animethemeentries.animetheme.anime.images", diff --git a/src/lib/common/animethemes/type-defs.ts b/src/lib/common/animethemes/type-defs.ts index d90996b..2622cea 100644 --- a/src/lib/common/animethemes/type-defs.ts +++ b/src/lib/common/animethemes/type-defs.ts @@ -261,6 +261,8 @@ const typeDefs = ` id: String! video: Video! playlist: Playlist! + previous: PlaylistTrack + next: PlaylistTrack } interface User { diff --git a/src/pages/playlist/[playlistId]/index.tsx b/src/pages/playlist/[playlistId]/index.tsx index f9f4847..ad9dc05 100644 --- a/src/pages/playlist/[playlistId]/index.tsx +++ b/src/pages/playlist/[playlistId]/index.tsx @@ -50,7 +50,7 @@ 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 createVideoSlug from "utils/createVideoSlug"; import { useRouter } from "next/router"; const StyledDesktopOnly = styled.div` @@ -112,6 +112,29 @@ function getRankComparator(name: string): Comparator { } } +type LinkedList = Array<{ + id: string; + previous?: { id: string } | null; + next?: { id: string } | null; +} & T>; + +function sortLinkedList(list: LinkedList) { + const lookUp = list.reduce((prev, curr) => { + prev[curr.id] = curr; + return prev; + }, {} as Record); + + let next = list.find((item) => !item.previous); + const sortedList = []; + + while (next) { + sortedList.push(next); + next = next.next ? lookUp[next.next.id] : undefined; + } + + return sortedList; +} + interface PlaylistDetailPageProps extends SharedPageProps, RequiredNonNullable {} interface PlaylistDetailPageParams extends ParsedUrlQuery { @@ -140,6 +163,8 @@ export default function PlaylistDetailPage({ playlist: initialPlaylist, me: init throw new Error("Playlist was removed or user lost auth."); } + data.playlist.tracks = sortLinkedList(data.playlist.tracks); + return data.playlist; }, { fallbackData: initialPlaylist } @@ -170,7 +195,7 @@ export default function PlaylistDetailPage({ playlist: initialPlaylist, me: init const isOwner = me.user?.name === playlist.user.name; const isRanking = playlist.name.startsWith("[#] "); - const tracks = [...playlist.forward].map((track, index) => ({ ...track, rank: index + 1 })); + const tracks = [...playlist.tracks].map((track, index) => ({ ...track, rank: index + 1 })); const tracksSorted = [...tracks].sort( sortTransformed( getComparator(sortBy) ?? getRankComparator(sortBy), @@ -224,7 +249,7 @@ export default function PlaylistDetailPage({ playlist: initialPlaylist, me: init async function updateTrackOrder(newTracks: typeof tracks) { await mutate({ ...playlist, - forward: newTracks, + tracks: newTracks, }, { revalidate: false }); } @@ -307,8 +332,8 @@ export default function PlaylistDetailPage({ playlist: initialPlaylist, me: init { + key={JSON.stringify(playlist.tracks.slice(0, 4))} + resourcesWithImages={playlist.tracks.slice(0, 4).flatMap((track) => { const anime = track.video.entries[0].theme?.anime; return anime ? [anime] : []; @@ -399,7 +424,7 @@ PlaylistDetailPage.fragments = { name visibility tracks_count - forward { + tracks { id video { ...VideoSummaryCardVideo @@ -413,6 +438,12 @@ PlaylistDetailPage.fragments = { } } } + previous { + id + } + next { + id + } } user { name @@ -453,6 +484,8 @@ export const getServerSideProps: GetServerSideProps