From 389ab50f30831b49b87eee0a8706af70c09c6005 Mon Sep 17 00:00:00 2001 From: Diptesh Choudhuri Date: Mon, 25 Sep 2023 22:13:19 +0530 Subject: [PATCH] Optimize loading media item details (#365) * fix(backend): incorrect average rating being displayed * feat(frontend): load media specifics in a different query * refactor(frontend): change query names * refactor(frontend): move image data to secondary query * fix(frontend): remove allow features * build(backend): bump version * fix(frontend): do not wait for user media details to load --- Cargo.lock | 2 +- apps/backend/Cargo.toml | 2 +- apps/backend/src/miscellaneous/resolver.rs | 11 +- apps/frontend/src/pages/media/item/index.tsx | 362 ++++++++++-------- libs/generated/src/graphql/backend/gql.ts | 9 +- libs/generated/src/graphql/backend/graphql.ts | 22 +- ...Details.gql => MediaAdditionalDetails.gql} | 18 +- .../src/backend/queries/MediaMainDetails.gql | 20 + 8 files changed, 262 insertions(+), 184 deletions(-) rename libs/graphql/src/backend/queries/{MediaDetails.gql => MediaAdditionalDetails.gql} (82%) create mode 100644 libs/graphql/src/backend/queries/MediaMainDetails.gql diff --git a/Cargo.lock b/Cargo.lock index cafc64e246..b27e87ec02 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4464,7 +4464,7 @@ checksum = "dc31bd9b61a32c31f9650d18add92aa83a49ba979c143eefd27fe7177b05bd5f" [[package]] name = "ryot" -version = "2.18.5" +version = "2.18.6" dependencies = [ "anyhow", "apalis", diff --git a/apps/backend/Cargo.toml b/apps/backend/Cargo.toml index 2c6034e73b..502e272221 100644 --- a/apps/backend/Cargo.toml +++ b/apps/backend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ryot" -version = "2.18.5" +version = "2.18.6" edition = "2021" repository = "https://github.com/IgnisDa/ryot" license = "GPL-V3" diff --git a/apps/backend/src/miscellaneous/resolver.rs b/apps/backend/src/miscellaneous/resolver.rs index a12552b572..9a89847db8 100644 --- a/apps/backend/src/miscellaneous/resolver.rs +++ b/apps/backend/src/miscellaneous/resolver.rs @@ -1695,10 +1695,13 @@ impl MiscellaneousService { let average_rating = if reviews.is_empty() { None } else { - Some( - reviews.iter().flat_map(|r| r.rating).sum::() - / Decimal::from(reviews.len()), - ) + let total_rating = reviews.iter().flat_map(|r| r.rating).collect_vec(); + let sum = total_rating.iter().sum::(); + if sum == dec!(0) { + None + } else { + Some(sum / Decimal::from(total_rating.iter().len())) + } }; Ok(UserMediaDetails { diff --git a/apps/frontend/src/pages/media/item/index.tsx b/apps/frontend/src/pages/media/item/index.tsx index 1031fc88e5..ed2d07621e 100644 --- a/apps/frontend/src/pages/media/item/index.tsx +++ b/apps/frontend/src/pages/media/item/index.tsx @@ -54,7 +54,6 @@ import { type DeleteSeenItemMutationVariables, DeployUpdateMetadataJobDocument, type DeployUpdateMetadataJobMutationVariables, - MediaDetailsDocument, MergeMetadataDocument, type MergeMetadataMutationVariables, MetadataLot, @@ -69,6 +68,8 @@ import { type ToggleMediaMonitorMutationVariables, UserMediaDetailsDocument, UserReviewScale, + MediaMainDetailsDocument, + MediaAdditionalDetailsDocument, } from "@ryot/generated/graphql/backend/graphql"; import { changeCase, formatDateToNaiveDate } from "@ryot/ts-utils"; import { @@ -469,9 +470,12 @@ const Page: NextPageWithLayout = () => { const mediaDetails = useQuery({ queryKey: ["mediaDetails", metadataId], queryFn: async () => { - const { mediaDetails } = await gqlClient.request(MediaDetailsDocument, { - metadataId, - }); + const { mediaDetails } = await gqlClient.request( + MediaMainDetailsDocument, + { + metadataId, + }, + ); return mediaDetails; }, staleTime: Infinity, @@ -485,6 +489,20 @@ const Page: NextPageWithLayout = () => { setActiveTab("overview"); }, }); + const mediaSpecifics = useQuery({ + queryKey: ["mediaSpecifics", metadataId], + queryFn: async () => { + const { mediaDetails } = await gqlClient.request( + MediaAdditionalDetailsDocument, + { + metadataId, + }, + ); + return mediaDetails; + }, + staleTime: Infinity, + enabled: !!metadataId, + }); const userMediaDetails = useQuery({ queryKey: ["userMediaDetails", metadataId], queryFn: async () => { @@ -638,17 +656,14 @@ const Page: NextPageWithLayout = () => { ); }; - return coreDetails.data && - mediaDetails.data && - preferences.data && - userMediaDetails.data ? ( + return coreDetails.data && mediaDetails.data && preferences.data ? ( <> {mediaDetails.data.title} | Ryot { ) : undefined} {mediaDetails.data.title} - {userMediaDetails.data.collections.length > 0 ? ( + {userMediaDetails.data && + userMediaDetails.data.collections.length > 0 ? ( {userMediaDetails.data.collections.map((col) => ( { {formatter.format(mediaDetails.data.genres.slice(0, 5))} ) : undefined} - {mediaDetails.data.bookSpecifics?.pages ? ( + {mediaSpecifics.data?.bookSpecifics?.pages ? ( {" "} - • {mediaDetails.data.bookSpecifics.pages} pages + • {mediaSpecifics.data.bookSpecifics.pages} pages ) : undefined} - {mediaDetails.data.podcastSpecifics?.totalEpisodes ? ( + {mediaSpecifics.data?.podcastSpecifics?.totalEpisodes ? ( {" "} - • {mediaDetails.data.podcastSpecifics.totalEpisodes} episodes + • {mediaSpecifics.data.podcastSpecifics.totalEpisodes} episodes ) : undefined} - {mediaDetails.data.animeSpecifics?.episodes ? ( + {mediaSpecifics.data?.animeSpecifics?.episodes ? ( {" "} - • {mediaDetails.data.animeSpecifics.episodes} episodes + • {mediaSpecifics.data.animeSpecifics.episodes} episodes ) : undefined} - {mediaDetails.data.mangaSpecifics?.chapters ? ( + {mediaSpecifics.data?.mangaSpecifics?.chapters ? ( {" "} - • {mediaDetails.data.mangaSpecifics.chapters} chapters + • {mediaSpecifics.data.mangaSpecifics.chapters} chapters ) : undefined} - {mediaDetails.data.mangaSpecifics?.volumes ? ( + {mediaSpecifics.data?.mangaSpecifics?.volumes ? ( {" "} - • {mediaDetails.data.mangaSpecifics.volumes} volumes + • {mediaSpecifics.data.mangaSpecifics.volumes} volumes ) : undefined} - {mediaDetails.data.movieSpecifics?.runtime ? ( + {mediaSpecifics.data?.movieSpecifics?.runtime ? ( {" "} •{" "} {humanizer.humanize( - mediaDetails.data.movieSpecifics.runtime * 1000 * 60, + mediaSpecifics.data.movieSpecifics.runtime * 1000 * 60, )} ) : undefined} - {mediaDetails.data.showSpecifics ? ( + {mediaSpecifics.data?.showSpecifics ? ( {" "} - • {mediaDetails.data.showSpecifics.seasons.length} seasons + • {mediaSpecifics.data.showSpecifics.seasons.length} seasons ) : undefined} - {mediaDetails.data.audioBookSpecifics?.runtime ? ( + {mediaSpecifics.data?.audioBookSpecifics?.runtime ? ( {" "} •{" "} {humanizer.humanize( - mediaDetails.data.audioBookSpecifics.runtime * 1000 * 60, + mediaSpecifics.data.audioBookSpecifics.runtime * 1000 * 60, )} ) : undefined} @@ -783,7 +799,7 @@ const Page: NextPageWithLayout = () => { ) : undefined} {mediaDetails.data.providerRating || - userMediaDetails.data.averageRating ? ( + (userMediaDetails.data && userMediaDetails.data.averageRating) ? ( {mediaDetails.data.providerRating ? ( { ) : undefined} - {userMediaDetails.data.averageRating ? ( + {userMediaDetails.data && userMediaDetails.data.averageRating ? ( { ) : undefined} ) : undefined} - {userMediaDetails.data.reminder ? ( + {userMediaDetails.data && userMediaDetails.data.reminder ? ( } variant="outline" @@ -893,7 +909,7 @@ const Page: NextPageWithLayout = () => { ) : undefined} - {userMediaDetails.data.inProgress ? ( + {userMediaDetails.data && userMediaDetails.data.inProgress ? ( } variant="outline"> You are currently {getVerb(Verb.Read, mediaDetails.data.lot)} ing this ({userMediaDetails.data.inProgress.progress}%) @@ -913,7 +929,8 @@ const Page: NextPageWithLayout = () => { }> Actions - {userMediaDetails.data.history.length > 0 ? ( + {userMediaDetails.data && + userMediaDetails.data.history.length > 0 ? ( } @@ -921,12 +938,12 @@ const Page: NextPageWithLayout = () => { History ) : undefined} - {mediaDetails.data.showSpecifics ? ( + {mediaSpecifics.data?.showSpecifics ? ( }> Seasons ) : undefined} - {mediaDetails.data.podcastSpecifics ? ( + {mediaSpecifics.data?.podcastSpecifics ? ( } @@ -935,6 +952,7 @@ const Page: NextPageWithLayout = () => { ) : undefined} {!coreDetails.data.reviewsDisabled && + userMediaDetails.data && userMediaDetails.data.reviews.length > 0 ? ( { Reviews ) : undefined} - {mediaDetails.data.suggestions.length > 0 ? ( + {(mediaSpecifics.data?.suggestions.length || 0) > 0 ? ( }> Suggestions ) : undefined} {!coreDetails.data.videosDisabled && - mediaDetails.data.assets.videos.length > 0 ? ( + (mediaSpecifics.data?.assets.videos.length || 0) > 0 ? ( }> Videos @@ -971,7 +989,7 @@ const Page: NextPageWithLayout = () => { No overview available )} - {mediaDetails.data.creators.map((c) => ( + {mediaSpecifics.data?.creators.map((c) => ( {c.name} { spacing="lg" breakpoints={[{ minWidth: "md", cols: 2 }]} > - {userMediaDetails.data.inProgress ? ( + {userMediaDetails.data && userMediaDetails.data.inProgress ? ( { opened={progressModalOpened} lot={mediaDetails.data.lot} total={ - mediaDetails.data.audioBookSpecifics?.runtime || - mediaDetails.data.bookSpecifics?.pages || - mediaDetails.data.movieSpecifics?.runtime || - mediaDetails.data.mangaSpecifics?.chapters || - mediaDetails.data.animeSpecifics?.episodes || - mediaDetails.data.visualNovelSpecifics?.length + mediaSpecifics.data?.audioBookSpecifics?.runtime || + mediaSpecifics.data?.bookSpecifics?.pages || + mediaSpecifics.data?.movieSpecifics?.runtime || + mediaSpecifics.data?.mangaSpecifics?.chapters || + mediaSpecifics.data?.animeSpecifics?.episodes || + mediaSpecifics.data?.visualNovelSpecifics?.length } /> ) : undefined} @@ -1062,7 +1080,8 @@ const Page: NextPageWithLayout = () => { mediaDetails.data.lot === MetadataLot.Podcast ? ( <> Shows and podcasts - {userMediaDetails.data.nextEpisode ? ( + {userMediaDetails.data && + userMediaDetails.data.nextEpisode ? ( <> { @@ -1109,7 +1128,8 @@ const Page: NextPageWithLayout = () => { ) : undefined} - {userMediaDetails.data.history.length !== 0 ? ( + {userMediaDetails.data && + userMediaDetails.data.history.length !== 0 ? ( ) : ( @@ -1118,7 +1138,8 @@ const Page: NextPageWithLayout = () => { )} ) : undefined} - {userMediaDetails.data.inProgress ? ( + {userMediaDetails.data && + userMediaDetails.data.inProgress ? ( <> In progress { href={withQuery(APP_ROUTES.media.postReview, { metadataId, showSeasonNumber: - userMediaDetails.data.nextEpisode?.seasonNumber ?? + (userMediaDetails.data && + userMediaDetails.data.nextEpisode?.seasonNumber) ?? undefined, showEpisodeNumber: mediaDetails.data.lot === MetadataLot.Show - ? userMediaDetails.data.nextEpisode - ?.episodeNumber ?? undefined + ? (userMediaDetails.data && + userMediaDetails.data.nextEpisode + ?.episodeNumber) ?? + undefined : undefined, podcastEpisodeNumber: mediaDetails.data.lot === MetadataLot.Podcast - ? userMediaDetails.data.nextEpisode - ?.episodeNumber ?? undefined + ? (userMediaDetails.data && + userMediaDetails.data.nextEpisode + ?.episodeNumber) ?? + undefined : undefined, })} passHref @@ -1229,10 +1255,12 @@ const Page: NextPageWithLayout = () => { }); }} > - {userMediaDetails.data.isMonitored ? "Stop" : "Start"}{" "} + {userMediaDetails.data && userMediaDetails.data.isMonitored + ? "Stop" + : "Start"}{" "} monitoring - {userMediaDetails.data.reminder ? ( + {userMediaDetails.data && userMediaDetails.data.reminder ? ( - - - ))} + ))} - {mediaDetails.data.showSpecifics ? ( + {mediaSpecifics.data?.showSpecifics ? ( - {mediaDetails.data.showSpecifics.seasons.map((s) => ( + {mediaSpecifics.data.showSpecifics.seasons.map((s) => ( { name={`${s.seasonNumber}. ${s.name}`} displayIndicator={ s.episodes.length > 0 && - s.episodes.every((e) => - userMediaDetails.data.history.some( - (h) => - h.progress === 100 && - h.showInformation && - h.showInformation.episode === - e.episodeNumber && - h.showInformation.season === s.seasonNumber, - ), + s.episodes.every( + (e) => + userMediaDetails.data && + userMediaDetails.data.history.some( + (h) => + h.progress === 100 && + h.showInformation && + h.showInformation.episode === + e.episodeNumber && + h.showInformation.season === + s.seasonNumber, + ), ) ? 1 : 0 @@ -1441,15 +1487,17 @@ const Page: NextPageWithLayout = () => { name={`${e.episodeNumber}. ${e.name}`} publishDate={e.publishDate} displayIndicator={ - userMediaDetails.data.history.filter( - (h) => - h.progress === 100 && - h.showInformation && - h.showInformation.episode === - e.episodeNumber && - h.showInformation.season === - s.seasonNumber, - ).length + (userMediaDetails.data && + userMediaDetails.data.history.filter( + (h) => + h.progress === 100 && + h.showInformation && + h.showInformation.episode === + e.episodeNumber && + h.showInformation.season === + s.seasonNumber, + ).length) || + 0 } >