mirror of
https://github.com/fallenbagel/jellyseerr.git
synced 2026-01-01 04:08:45 -05:00
feat: add genre/studio/network view to Discover results (#1067)
* feat: add genres view to movie/series Discover results * feat: add studio/network view to movie/series Discover results * fix: remove with_release_type filter, since it is removing valid/desired results
This commit is contained in:
@@ -1,16 +1,23 @@
|
||||
import React, { useContext } from 'react';
|
||||
import { useSWRInfinite } from 'swr';
|
||||
import useSWR, { useSWRInfinite } from 'swr';
|
||||
import type { MovieResult } from '../../../server/models/Search';
|
||||
import ListView from '../Common/ListView';
|
||||
import { LanguageContext } from '../../context/LanguageContext';
|
||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import Header from '../Common/Header';
|
||||
import useSettings from '../../hooks/useSettings';
|
||||
import { MediaStatus } from '../../../server/constants/media';
|
||||
import PageTitle from '../Common/PageTitle';
|
||||
import { useRouter } from 'next/router';
|
||||
import {
|
||||
TmdbStudio,
|
||||
TmdbGenre,
|
||||
} from '../../../server/api/themoviedb/interfaces';
|
||||
|
||||
const messages = defineMessages({
|
||||
discovermovies: 'Popular Movies',
|
||||
genreMovies: '{genre} Movies',
|
||||
studioMovies: '{studio} Movies',
|
||||
});
|
||||
|
||||
interface SearchResult {
|
||||
@@ -21,16 +28,27 @@ interface SearchResult {
|
||||
}
|
||||
|
||||
const DiscoverMovies: React.FC = () => {
|
||||
const router = useRouter();
|
||||
const intl = useIntl();
|
||||
const settings = useSettings();
|
||||
const { locale } = useContext(LanguageContext);
|
||||
|
||||
const { data: genres } = useSWR<TmdbGenre[]>('/api/v1/genres/movie');
|
||||
const genre = genres?.find((g) => g.id === Number(router.query.genreId));
|
||||
|
||||
const { data: studio } = useSWR<TmdbStudio>(
|
||||
`/api/v1/studio/${router.query.studioId}`
|
||||
);
|
||||
|
||||
const { data, error, size, setSize } = useSWRInfinite<SearchResult>(
|
||||
(pageIndex: number, previousPageData: SearchResult | null) => {
|
||||
if (previousPageData && pageIndex + 1 > previousPageData.totalPages) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return `/api/v1/discover/movies?page=${pageIndex + 1}&language=${locale}`;
|
||||
return `/api/v1/discover/movies?page=${pageIndex + 1}&language=${locale}${
|
||||
genre ? `&genre=${genre.id}` : ''
|
||||
}${studio ? `&studio=${studio.id}` : ''}`;
|
||||
},
|
||||
{
|
||||
initialSize: 3,
|
||||
@@ -68,13 +86,17 @@ const DiscoverMovies: React.FC = () => {
|
||||
const isReachingEnd =
|
||||
isEmpty || (data && data[data.length - 1]?.results.length < 20);
|
||||
|
||||
const title = genre
|
||||
? intl.formatMessage(messages.genreMovies, { genre: genre.name })
|
||||
: studio
|
||||
? intl.formatMessage(messages.studioMovies, { studio: studio.name })
|
||||
: intl.formatMessage(messages.discovermovies);
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageTitle title={intl.formatMessage(messages.discovermovies)} />
|
||||
<PageTitle title={title} />
|
||||
<div className="mt-1 mb-5">
|
||||
<Header>
|
||||
<FormattedMessage {...messages.discovermovies} />
|
||||
</Header>
|
||||
<Header>{title}</Header>
|
||||
</div>
|
||||
<ListView
|
||||
items={titles}
|
||||
|
||||
@@ -1,16 +1,23 @@
|
||||
import React, { useContext } from 'react';
|
||||
import { useSWRInfinite } from 'swr';
|
||||
import useSWR, { useSWRInfinite } from 'swr';
|
||||
import type { TvResult } from '../../../server/models/Search';
|
||||
import ListView from '../Common/ListView';
|
||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import { LanguageContext } from '../../context/LanguageContext';
|
||||
import Header from '../Common/Header';
|
||||
import useSettings from '../../hooks/useSettings';
|
||||
import { MediaStatus } from '../../../server/constants/media';
|
||||
import PageTitle from '../Common/PageTitle';
|
||||
import { useRouter } from 'next/router';
|
||||
import {
|
||||
TmdbGenre,
|
||||
TmdbNetwork,
|
||||
} from '../../../server/api/themoviedb/interfaces';
|
||||
|
||||
const messages = defineMessages({
|
||||
discovertv: 'Popular Series',
|
||||
genreSeries: '{genre} Series',
|
||||
networkSeries: '{network} Series',
|
||||
});
|
||||
|
||||
interface SearchResult {
|
||||
@@ -21,16 +28,27 @@ interface SearchResult {
|
||||
}
|
||||
|
||||
const DiscoverTv: React.FC = () => {
|
||||
const router = useRouter();
|
||||
const intl = useIntl();
|
||||
const settings = useSettings();
|
||||
const { locale } = useContext(LanguageContext);
|
||||
|
||||
const { data: genres } = useSWR<TmdbGenre[]>('/api/v1/genres/tv');
|
||||
const genre = genres?.find((g) => g.id === Number(router.query.genreId));
|
||||
|
||||
const { data: network } = useSWR<TmdbNetwork>(
|
||||
`/api/v1/network/${router.query.networkId}`
|
||||
);
|
||||
|
||||
const { data, error, size, setSize } = useSWRInfinite<SearchResult>(
|
||||
(pageIndex: number, previousPageData: SearchResult | null) => {
|
||||
if (previousPageData && pageIndex + 1 > previousPageData.totalPages) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return `/api/v1/discover/tv?page=${pageIndex + 1}&language=${locale}`;
|
||||
return `/api/v1/discover/tv?page=${pageIndex + 1}&language=${locale}${
|
||||
genre ? `&genre=${genre.id}` : ''
|
||||
}${network ? `&network=${network.id}` : ''}`;
|
||||
},
|
||||
{
|
||||
initialSize: 3,
|
||||
@@ -67,13 +85,17 @@ const DiscoverTv: React.FC = () => {
|
||||
const isReachingEnd =
|
||||
isEmpty || (data && data[data.length - 1]?.results.length < 20);
|
||||
|
||||
const title = genre
|
||||
? intl.formatMessage(messages.genreSeries, { genre: genre.name })
|
||||
: network
|
||||
? intl.formatMessage(messages.networkSeries, { network: network.name })
|
||||
: intl.formatMessage(messages.discovertv);
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageTitle title={intl.formatMessage(messages.discovertv)} />
|
||||
<PageTitle title={title} />
|
||||
<div className="mt-1 mb-5">
|
||||
<Header>
|
||||
<FormattedMessage {...messages.discovertv} />
|
||||
</Header>
|
||||
<Header>{title}</Header>
|
||||
</div>
|
||||
<ListView
|
||||
items={titles}
|
||||
|
||||
@@ -2,7 +2,7 @@ import React, { useContext } from 'react';
|
||||
import { useSWRInfinite } from 'swr';
|
||||
import type { TvResult } from '../../../server/models/Search';
|
||||
import ListView from '../Common/ListView';
|
||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import { LanguageContext } from '../../context/LanguageContext';
|
||||
import Header from '../Common/Header';
|
||||
import useSettings from '../../hooks/useSettings';
|
||||
@@ -73,9 +73,7 @@ const DiscoverTvUpcoming: React.FC = () => {
|
||||
<>
|
||||
<PageTitle title={intl.formatMessage(messages.upcomingtv)} />
|
||||
<div className="mt-1 mb-5">
|
||||
<Header>
|
||||
<FormattedMessage {...messages.upcomingtv} />
|
||||
</Header>
|
||||
<Header>{intl.formatMessage(messages.upcomingtv)}</Header>
|
||||
</div>
|
||||
<ListView
|
||||
items={titles}
|
||||
|
||||
@@ -7,7 +7,7 @@ import type {
|
||||
} from '../../../server/models/Search';
|
||||
import ListView from '../Common/ListView';
|
||||
import { LanguageContext } from '../../context/LanguageContext';
|
||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import Header from '../Common/Header';
|
||||
import useSettings from '../../hooks/useSettings';
|
||||
import { MediaStatus } from '../../../server/constants/media';
|
||||
@@ -78,9 +78,7 @@ const Trending: React.FC = () => {
|
||||
<>
|
||||
<PageTitle title={intl.formatMessage(messages.trending)} />
|
||||
<div className="mt-1 mb-5">
|
||||
<Header>
|
||||
<FormattedMessage {...messages.trending} />
|
||||
</Header>
|
||||
<Header>{intl.formatMessage(messages.trending)}</Header>
|
||||
</div>
|
||||
<ListView
|
||||
items={titles}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useSWRInfinite } from 'swr';
|
||||
import type { MovieResult } from '../../../server/models/Search';
|
||||
import ListView from '../Common/ListView';
|
||||
import { LanguageContext } from '../../context/LanguageContext';
|
||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import Header from '../Common/Header';
|
||||
import useSettings from '../../hooks/useSettings';
|
||||
import { MediaStatus } from '../../../server/constants/media';
|
||||
@@ -73,9 +73,7 @@ const UpcomingMovies: React.FC = () => {
|
||||
<>
|
||||
<PageTitle title={intl.formatMessage(messages.upcomingmovies)} />
|
||||
<div className="mt-1 mb-5">
|
||||
<Header>
|
||||
<FormattedMessage {...messages.upcomingmovies} />
|
||||
</Header>
|
||||
<Header>{intl.formatMessage(messages.upcomingmovies)}</Header>
|
||||
</div>
|
||||
<ListView
|
||||
items={titles}
|
||||
|
||||
@@ -3,7 +3,7 @@ import useSWR from 'swr';
|
||||
import TmdbTitleCard from '../TitleCard/TmdbTitleCard';
|
||||
import Slider from '../Slider';
|
||||
import Link from 'next/link';
|
||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import type { MediaResultsResponse } from '../../../server/interfaces/api/mediaInterfaces';
|
||||
import type { RequestResultsResponse } from '../../../server/interfaces/api/requestInterfaces';
|
||||
import RequestCard from '../RequestCard';
|
||||
@@ -42,9 +42,7 @@ const Discover: React.FC = () => {
|
||||
<div className="mt-6 mb-4 md:flex md:items-center md:justify-between">
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="inline-flex items-center text-xl leading-7 text-gray-300 hover:text-white sm:text-2xl sm:leading-9 sm:truncate">
|
||||
<span>
|
||||
<FormattedMessage {...messages.recentlyAdded} />
|
||||
</span>
|
||||
<span>{intl.formatMessage(messages.recentlyAdded)}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -64,9 +62,7 @@ const Discover: React.FC = () => {
|
||||
<div className="flex-1 min-w-0">
|
||||
<Link href="/requests">
|
||||
<a className="inline-flex items-center text-xl leading-7 text-gray-300 hover:text-white sm:text-2xl sm:leading-9 sm:truncate">
|
||||
<span>
|
||||
<FormattedMessage {...messages.recentrequests} />
|
||||
</span>
|
||||
<span>{intl.formatMessage(messages.recentrequests)}</span>
|
||||
<svg
|
||||
className="w-6 h-6 ml-2"
|
||||
fill="none"
|
||||
|
||||
@@ -187,7 +187,19 @@ const MovieDetails: React.FC<MovieDetailsProps> = ({ movie }) => {
|
||||
}
|
||||
|
||||
if (data.genres.length) {
|
||||
movieAttributes.push(data.genres.map((g) => g.name).join(', '));
|
||||
movieAttributes.push(
|
||||
data.genres
|
||||
.map((g) => (
|
||||
<Link href={`/discover/movies/genre/${g.id}`} key={`genre-${g.id}`}>
|
||||
<a className="hover:underline">{g.name}</a>
|
||||
</Link>
|
||||
))
|
||||
.reduce((prev, curr) => (
|
||||
<>
|
||||
{prev}, {curr}
|
||||
</>
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -660,7 +672,13 @@ const MovieDetails: React.FC<MovieDetailsProps> = ({ movie }) => {
|
||||
{intl.formatMessage(messages.studio)}
|
||||
</span>
|
||||
<span className="flex-1 text-sm text-right text-gray-400">
|
||||
{data.productionCompanies[0]?.name}
|
||||
<Link
|
||||
href={`/discover/movies/studio/${data.productionCompanies[0].id}`}
|
||||
>
|
||||
<a className="hover:underline">
|
||||
{data.productionCompanies[0].name}
|
||||
</a>
|
||||
</Link>
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -189,7 +189,19 @@ const TvDetails: React.FC<TvDetailsProps> = ({ tv }) => {
|
||||
}
|
||||
|
||||
if (data.genres.length) {
|
||||
seriesAttributes.push(data.genres.map((g) => g.name).join(', '));
|
||||
seriesAttributes.push(
|
||||
data.genres
|
||||
.map((g) => (
|
||||
<Link href={`/discover/tv/genre/${g.id}`} key={`genre-${g.id}`}>
|
||||
<a className="hover:underline">{g.name}</a>
|
||||
</Link>
|
||||
))
|
||||
.reduce((prev, curr) => (
|
||||
<>
|
||||
{prev}, {curr}
|
||||
</>
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
const isComplete =
|
||||
@@ -684,7 +696,20 @@ const TvDetails: React.FC<TvDetailsProps> = ({ tv }) => {
|
||||
{intl.formatMessage(messages.network)}
|
||||
</span>
|
||||
<span className="flex-1 text-sm text-right text-gray-400">
|
||||
{data.networks.map((n) => n.name).join(', ')}
|
||||
{data.networks
|
||||
.map((n) => (
|
||||
<Link
|
||||
href={`/discover/tv/network/${n.id}`}
|
||||
key={`network-${n.id}`}
|
||||
>
|
||||
<a className="hover:underline">{n.name}</a>
|
||||
</Link>
|
||||
))
|
||||
.reduce((prev, curr) => (
|
||||
<>
|
||||
{prev}, {curr}
|
||||
</>
|
||||
))}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -17,11 +17,15 @@
|
||||
"components.Discover.discover": "Discover",
|
||||
"components.Discover.discovermovies": "Popular Movies",
|
||||
"components.Discover.discovertv": "Popular Series",
|
||||
"components.Discover.genreMovies": "{genre} Movies",
|
||||
"components.Discover.genreSeries": "{genre} Series",
|
||||
"components.Discover.networkSeries": "{network} Series",
|
||||
"components.Discover.nopending": "No Pending Requests",
|
||||
"components.Discover.popularmovies": "Popular Movies",
|
||||
"components.Discover.populartv": "Popular Series",
|
||||
"components.Discover.recentlyAdded": "Recently Added",
|
||||
"components.Discover.recentrequests": "Recent Requests",
|
||||
"components.Discover.studioMovies": "{studio} Movies",
|
||||
"components.Discover.trending": "Trending",
|
||||
"components.Discover.upcoming": "Upcoming Movies",
|
||||
"components.Discover.upcomingmovies": "Upcoming Movies",
|
||||
|
||||
9
src/pages/discover/movies/genre/[genreId]/index.tsx
Normal file
9
src/pages/discover/movies/genre/[genreId]/index.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import React from 'react';
|
||||
import { NextPage } from 'next';
|
||||
import DiscoverMovies from '../../../../../components/Discover/DiscoverMovies';
|
||||
|
||||
const DiscoverMoviesGenrePage: NextPage = () => {
|
||||
return <DiscoverMovies />;
|
||||
};
|
||||
|
||||
export default DiscoverMoviesGenrePage;
|
||||
9
src/pages/discover/movies/studio/[studioId]/index.tsx
Normal file
9
src/pages/discover/movies/studio/[studioId]/index.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import React from 'react';
|
||||
import { NextPage } from 'next';
|
||||
import DiscoverMovies from '../../../../../components/Discover/DiscoverMovies';
|
||||
|
||||
const DiscoverMoviesStudioPage: NextPage = () => {
|
||||
return <DiscoverMovies />;
|
||||
};
|
||||
|
||||
export default DiscoverMoviesStudioPage;
|
||||
9
src/pages/discover/tv/genre/[genreId]/index.tsx
Normal file
9
src/pages/discover/tv/genre/[genreId]/index.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import React from 'react';
|
||||
import { NextPage } from 'next';
|
||||
import DiscoverTv from '../../../../../components/Discover/DiscoverTv';
|
||||
|
||||
const DiscoverTvGenrePage: NextPage = () => {
|
||||
return <DiscoverTv />;
|
||||
};
|
||||
|
||||
export default DiscoverTvGenrePage;
|
||||
9
src/pages/discover/tv/network/[networkId]/index.tsx
Normal file
9
src/pages/discover/tv/network/[networkId]/index.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import React from 'react';
|
||||
import { NextPage } from 'next';
|
||||
import DiscoverTv from '../../../../../components/Discover/DiscoverTv';
|
||||
|
||||
const DiscoverTvNetworkPage: NextPage = () => {
|
||||
return <DiscoverTv />;
|
||||
};
|
||||
|
||||
export default DiscoverTvNetworkPage;
|
||||
Reference in New Issue
Block a user