feat: upcoming/trending list views and larger title cards

This commit is contained in:
sct
2020-11-17 09:18:45 +00:00
parent 185ac2648f
commit 94eaaf96b4
17 changed files with 457 additions and 219 deletions

View File

@@ -0,0 +1,21 @@
import React from 'react';
interface HeaderProps {
extraMargin?: number;
}
const Header: React.FC<HeaderProps> = ({ children, extraMargin = 0 }) => {
return (
<div className="md:flex md:items-center md:justify-between mt-8 mb-8">
<div className={`flex-1 min-w-0 mx-${extraMargin}`}>
<h2 className="text-2xl font-bold leading-7 text-cool-gray-100 sm:text-4xl sm:leading-9 truncate sm:overflow-visible">
<span className="bg-clip-text text-transparent bg-gradient-to-br from-indigo-400 to-purple-400">
{children}
</span>
</h2>
</div>
</div>
);
};
export default Header;

View File

@@ -31,7 +31,7 @@ const ListView: React.FC<ListViewProps> = ({
No Results No Results
</div> </div>
)} )}
<ul className="grid grid-cols-2 gap-6 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5"> <ul className="grid grid-cols-2 gap-6 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-7">
{items?.map((title) => { {items?.map((title) => {
let titleCard: React.ReactNode; let titleCard: React.ReactNode;
@@ -47,6 +47,7 @@ const ListView: React.FC<ListViewProps> = ({
userScore={title.voteAverage} userScore={title.voteAverage}
year={title.releaseDate} year={title.releaseDate}
mediaType={title.mediaType} mediaType={title.mediaType}
canExpand
/> />
); );
break; break;
@@ -61,12 +62,17 @@ const ListView: React.FC<ListViewProps> = ({
userScore={title.voteAverage} userScore={title.voteAverage}
year={title.firstAirDate} year={title.firstAirDate}
mediaType={title.mediaType} mediaType={title.mediaType}
canExpand
/> />
); );
break; break;
case 'person': case 'person':
titleCard = ( titleCard = (
<PersonCard name={title.name} profilePath={title.profilePath} /> <PersonCard
name={title.name}
profilePath={title.profilePath}
canExpand
/>
); );
break; break;
} }
@@ -82,12 +88,12 @@ const ListView: React.FC<ListViewProps> = ({
})} })}
{isLoading && {isLoading &&
!isReachingEnd && !isReachingEnd &&
[...Array(10)].map((_item, i) => ( [...Array(20)].map((_item, i) => (
<li <li
key={`placeholder-${i}`} key={`placeholder-${i}`}
className="col-span-1 flex flex-col text-center items-center" className="col-span-1 flex flex-col text-center items-center"
> >
<TitleCard.Placeholder /> <TitleCard.Placeholder canExpand />
</li> </li>
))} ))}
</ul> </ul>

View File

@@ -4,9 +4,10 @@ import type { MovieResult } from '../../../server/models/Search';
import ListView from '../Common/ListView'; import ListView from '../Common/ListView';
import { LanguageContext } from '../../context/LanguageContext'; import { LanguageContext } from '../../context/LanguageContext';
import { defineMessages, FormattedMessage } from 'react-intl'; import { defineMessages, FormattedMessage } from 'react-intl';
import Header from '../Common/Header';
const messages = defineMessages({ const messages = defineMessages({
discovermovies: 'Discover Movies', discovermovies: 'Popular Movies',
}); });
interface SearchResult { interface SearchResult {
@@ -55,13 +56,9 @@ const DiscoverMovies: React.FC = () => {
return ( return (
<> <>
<div className="md:flex md:items-center md:justify-between mb-8 mt-6"> <Header>
<div className="flex-1 min-w-0">
<h2 className="text-xl leading-7 text-white sm:text-2xl sm:leading-9 sm:truncate">
<FormattedMessage {...messages.discovermovies} /> <FormattedMessage {...messages.discovermovies} />
</h2> </Header>
</div>
</div>
<ListView <ListView
items={titles} items={titles}
isEmpty={isEmpty} isEmpty={isEmpty}

View File

@@ -1,12 +1,13 @@
import React, { useContext } from 'react'; import React, { useContext } from 'react';
import { useSWRInfinite } from 'swr'; import { useSWRInfinite } from 'swr';
import { TvResult } from '../../../server/models/Search'; import type { TvResult } from '../../../server/models/Search';
import ListView from '../Common/ListView'; import ListView from '../Common/ListView';
import { defineMessages, FormattedMessage } from 'react-intl'; import { defineMessages, FormattedMessage } from 'react-intl';
import { LanguageContext } from '../../context/LanguageContext'; import { LanguageContext } from '../../context/LanguageContext';
import Header from '../Common/Header';
const messages = defineMessages({ const messages = defineMessages({
discovertv: 'Discover Series', discovertv: 'Popular Series',
}); });
interface SearchResult { interface SearchResult {
@@ -52,13 +53,9 @@ const DiscoverTv: React.FC = () => {
return ( return (
<> <>
<div className="md:flex md:items-center md:justify-between mb-8 mt-6"> <Header>
<div className="flex-1 min-w-0">
<h2 className="text-xl leading-7 text-white sm:text-2xl sm:leading-9 sm:truncate">
<FormattedMessage {...messages.discovertv} /> <FormattedMessage {...messages.discovertv} />
</h2> </Header>
</div>
</div>
<ListView <ListView
items={titles} items={titles}
isEmpty={isEmpty} isEmpty={isEmpty}

View File

@@ -0,0 +1,81 @@
import React, { useContext } from 'react';
import { useSWRInfinite } from 'swr';
import type {
MovieResult,
TvResult,
PersonResult,
} from '../../../server/models/Search';
import ListView from '../Common/ListView';
import { LanguageContext } from '../../context/LanguageContext';
import { defineMessages, FormattedMessage } from 'react-intl';
import Header from '../Common/Header';
const messages = defineMessages({
trending: 'Trending',
});
interface SearchResult {
page: number;
totalResults: number;
totalPages: number;
results: (MovieResult | TvResult | PersonResult)[];
}
const Trending: React.FC = () => {
const { locale } = useContext(LanguageContext);
const { data, error, size, setSize } = useSWRInfinite<SearchResult>(
(pageIndex: number, previousPageData: SearchResult | null) => {
if (previousPageData && pageIndex + 1 > previousPageData.totalPages) {
return null;
}
return `/api/v1/discover/trending?page=${
pageIndex + 1
}&language=${locale}`;
},
{
initialSize: 3,
}
);
const isLoadingInitialData = !data && !error;
const isLoadingMore =
isLoadingInitialData ||
(size > 0 && data && typeof data[size - 1] === 'undefined');
const fetchMore = () => {
setSize(size + 1);
};
if (error) {
return <div>{error}</div>;
}
const titles = data?.reduce(
(a, v) => [...a, ...v.results],
[] as (MovieResult | TvResult | PersonResult)[]
);
const isEmpty = !isLoadingInitialData && titles?.length === 0;
const isReachingEnd =
isEmpty || (data && data[data.length - 1]?.results.length < 20);
return (
<>
<Header>
<FormattedMessage {...messages.trending} />
</Header>
<ListView
items={titles}
isEmpty={isEmpty}
isLoading={
isLoadingInitialData || (isLoadingMore && (titles?.length ?? 0) > 0)
}
isReachingEnd={isReachingEnd}
onScrollBottom={fetchMore}
/>
</>
);
};
export default Trending;

View File

@@ -0,0 +1,77 @@
import React, { useContext } from 'react';
import { useSWRInfinite } from 'swr';
import type { MovieResult } from '../../../server/models/Search';
import ListView from '../Common/ListView';
import { LanguageContext } from '../../context/LanguageContext';
import { defineMessages, FormattedMessage } from 'react-intl';
import Header from '../Common/Header';
const messages = defineMessages({
upcomingmovies: 'Upcoming Movies',
});
interface SearchResult {
page: number;
totalResults: number;
totalPages: number;
results: MovieResult[];
}
const UpcomingMovies: React.FC = () => {
const { locale } = useContext(LanguageContext);
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/upcoming?page=${
pageIndex + 1
}&language=${locale}`;
},
{
initialSize: 3,
}
);
const isLoadingInitialData = !data && !error;
const isLoadingMore =
isLoadingInitialData ||
(size > 0 && data && typeof data[size - 1] === 'undefined');
const fetchMore = () => {
setSize(size + 1);
};
if (error) {
return <div>{error}</div>;
}
const titles = data?.reduce(
(a, v) => [...a, ...v.results],
[] as MovieResult[]
);
const isEmpty = !isLoadingInitialData && titles?.length === 0;
const isReachingEnd =
isEmpty || (data && data[data.length - 1]?.results.length < 20);
return (
<>
<Header>
<FormattedMessage {...messages.upcomingmovies} />
</Header>
<ListView
items={titles}
isEmpty={isEmpty}
isLoading={
isLoadingInitialData || (isLoadingMore && (titles?.length ?? 0) > 0)
}
isReachingEnd={isReachingEnd}
onScrollBottom={fetchMore}
/>
</>
);
};
export default UpcomingMovies;

View File

@@ -77,27 +77,11 @@ const Discover: React.FC = () => {
<> <>
<div className="md:flex md:items-center md:justify-between mb-4 mt-6"> <div className="md:flex md:items-center md:justify-between mb-4 mt-6">
<div className="flex-1 min-w-0"> <div className="flex-1 min-w-0">
<Link href="/recent"> <div className="inline-flex text-xl leading-7 text-cool-gray-300 hover:text-white sm:text-2xl sm:leading-9 sm:truncate items-center">
<a className="inline-flex text-xl leading-7 text-cool-gray-300 hover:text-white sm:text-2xl sm:leading-9 sm:truncate items-center">
<span> <span>
<FormattedMessage {...messages.recentlyAdded} /> <FormattedMessage {...messages.recentlyAdded} />
</span> </span>
<svg </div>
className="w-6 h-6 ml-2"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M13 9l3 3m0 0l-3 3m3-3H8m13 0a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
</a>
</Link>
</div> </div>
</div> </div>
<Slider <Slider

View File

@@ -4,15 +4,21 @@ interface PersonCardProps {
name: string; name: string;
subName?: string; subName?: string;
profilePath?: string; profilePath?: string;
canExpand?: boolean;
} }
const PersonCard: React.FC<PersonCardProps> = ({ const PersonCard: React.FC<PersonCardProps> = ({
name, name,
subName, subName,
profilePath, profilePath,
canExpand = false,
}) => { }) => {
return ( return (
<div className="relative w-36 sm:w-36 md:w-44 bg-cool-gray-600 rounded-lg text-white shadow-lg hover:bg-cool-gray-500 transition ease-in-out duration-150 cursor-pointer"> <div
className={`relative ${
canExpand ? 'w-full' : 'w-36 sm:w-36 md:w-44'
} bg-cool-gray-600 rounded-lg text-white shadow-lg hover:bg-cool-gray-500 transition ease-in-out duration-150 cursor-pointer`}
>
<div style={{ paddingBottom: '150%' }}> <div style={{ paddingBottom: '150%' }}>
<div className="absolute inset-0 flex flex-col items-center justify-center"> <div className="absolute inset-0 flex flex-col items-center justify-center">
{profilePath && ( {profilePath && (

View File

@@ -1,8 +1,16 @@
import React from 'react'; import React from 'react';
const Placeholder: React.FC = () => { interface PlaceholderProps {
canExpand?: boolean;
}
const Placeholder: React.FC<PlaceholderProps> = ({ canExpand = false }) => {
return ( return (
<div className="relative animate-pulse rounded-lg bg-cool-gray-700 w-36 sm:w-36 md:w-44 "> <div
className={`relative animate-pulse rounded-lg bg-cool-gray-700 ${
canExpand ? 'w-full' : 'w-36 sm:w-36 md:w-44'
}`}
>
<div className="w-full" style={{ paddingBottom: '150%' }} /> <div className="w-full" style={{ paddingBottom: '150%' }} />
</div> </div>
); );

View File

@@ -19,7 +19,7 @@ interface TitleCardProps {
userScore: number; userScore: number;
mediaType: MediaType; mediaType: MediaType;
status?: MediaStatus; status?: MediaStatus;
requestId?: number; canExpand?: boolean;
} }
const TitleCard: React.FC<TitleCardProps> = ({ const TitleCard: React.FC<TitleCardProps> = ({
@@ -30,7 +30,7 @@ const TitleCard: React.FC<TitleCardProps> = ({
title, title,
status, status,
mediaType, mediaType,
requestId, canExpand = false,
}) => { }) => {
const [isUpdating, setIsUpdating] = useState(false); const [isUpdating, setIsUpdating] = useState(false);
const [currentStatus, setCurrentStatus] = useState(status); const [currentStatus, setCurrentStatus] = useState(status);
@@ -55,7 +55,7 @@ const TitleCard: React.FC<TitleCardProps> = ({
const closeModal = useCallback(() => setShowRequestModal(false), []); const closeModal = useCallback(() => setShowRequestModal(false), []);
return ( return (
<div className="w-36 sm:w-36 md:w-44"> <div className={canExpand ? 'w-full' : 'w-36 sm:w-36 md:w-44'}>
<RequestModal <RequestModal
tmdbId={id} tmdbId={id}
show={showRequestModal} show={showRequestModal}

View File

@@ -7,6 +7,7 @@ import Button from '../Common/Button';
import { useIntl, defineMessages, FormattedMessage } from 'react-intl'; import { useIntl, defineMessages, FormattedMessage } from 'react-intl';
import axios from 'axios'; import axios from 'axios';
import { useToasts } from 'react-toast-notifications'; import { useToasts } from 'react-toast-notifications';
import Header from '../Common/Header';
const messages = defineMessages({ const messages = defineMessages({
edituser: 'Edit User', edituser: 'Edit User',
@@ -143,15 +144,9 @@ const UserEdit: React.FC = () => {
]; ];
return ( return (
<div className="py-6 px-4 space-y-6 sm:p-6 lg:pb-8"> <>
<div className="md:flex md:items-center md:justify-between mt-8 mb-6"> <Header extraMargin={4}>Edit User</Header>
<div className="flex-1 min-w-0"> <div className="px-4 space-y-6 sm:p-6 lg:pb-8">
<h2 className="text-2xl font-bold leading-7 text-cool-gray-100 sm:text-3xl sm:leading-9 sm:truncate">
<FormattedMessage {...messages.edituser} />
</h2>
</div>
</div>
<div className="flex flex-col space-y-6 lg:flex-row lg:space-y-0 lg:space-x-6 text-white"> <div className="flex flex-col space-y-6 lg:flex-row lg:space-y-0 lg:space-x-6 text-white">
<div className="flex-grow space-y-6"> <div className="flex-grow space-y-6">
<div className="space-y-1"> <div className="space-y-1">
@@ -237,7 +232,10 @@ const UserEdit: React.FC = () => {
<div <div
className={`relative flex items-start first:mt-0 mt-4 ${ className={`relative flex items-start first:mt-0 mt-4 ${
(permissionOption.permission !== Permission.ADMIN && (permissionOption.permission !== Permission.ADMIN &&
hasPermission(Permission.ADMIN, currentPermission)) || hasPermission(
Permission.ADMIN,
currentPermission
)) ||
(currentUser?.id !== 1 && (currentUser?.id !== 1 &&
permissionOption.permission === Permission.ADMIN) || permissionOption.permission === Permission.ADMIN) ||
(!currentHasPermission(Permission.MANAGE_SETTINGS) && (!currentHasPermission(Permission.MANAGE_SETTINGS) &&
@@ -255,7 +253,8 @@ const UserEdit: React.FC = () => {
type="checkbox" type="checkbox"
className="form-checkbox h-4 w-4 text-indigo-600 transition duration-150 ease-in-out" className="form-checkbox h-4 w-4 text-indigo-600 transition duration-150 ease-in-out"
disabled={ disabled={
(permissionOption.permission !== Permission.ADMIN && (permissionOption.permission !==
Permission.ADMIN &&
hasPermission( hasPermission(
Permission.ADMIN, Permission.ADMIN,
currentPermission currentPermission
@@ -321,6 +320,7 @@ const UserEdit: React.FC = () => {
</div> </div>
</div> </div>
</div> </div>
</>
); );
}; };

View File

@@ -8,6 +8,7 @@ import Button from '../Common/Button';
import { hasPermission } from '../../../server/lib/permissions'; import { hasPermission } from '../../../server/lib/permissions';
import { Permission } from '../../hooks/useUser'; import { Permission } from '../../hooks/useUser';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import Header from '../Common/Header';
const UserList: React.FC = () => { const UserList: React.FC = () => {
const router = useRouter(); const router = useRouter();
@@ -19,13 +20,7 @@ const UserList: React.FC = () => {
return ( return (
<> <>
<div className="md:flex md:items-center md:justify-between mt-8 mb-6"> <Header extraMargin={4}>User List</Header>
<div className="flex-1 min-w-0 mx-4">
<h2 className="text-2xl font-bold leading-7 text-cool-gray-100 sm:text-3xl sm:leading-9 sm:truncate">
User List
</h2>
</div>
</div>
<div className="flex flex-col"> <div className="flex flex-col">
<div className="my-2 overflow-x-auto -mx-6 sm:-mx-6 md:mx-4 lg:mx-4"> <div className="my-2 overflow-x-auto -mx-6 sm:-mx-6 md:mx-4 lg:mx-4">
<div className="py-2 align-middle inline-block min-w-full sm:px-6 lg:px-8"> <div className="py-2 align-middle inline-block min-w-full sm:px-6 lg:px-8">

View File

@@ -1,15 +1,16 @@
{ {
"components.Discover.discovermovies": "Discover Movies", "components.Discover.discovermovies": "Popular Movies",
"components.Discover.discovertv": "Discover Series", "components.Discover.discovertv": "Popular Series",
"components.Discover.nopending": "No Pending Requests", "components.Discover.nopending": "No Pending Requests",
"components.Discover.popularmovies": "Popular Movies", "components.Discover.popularmovies": "Popular Movies",
"components.Discover.populartv": "Popular Series", "components.Discover.populartv": "Popular Series",
"components.Discover.recentlyAdded": "Recently Added", "components.Discover.recentlyAdded": "Recently Added",
"components.Discover.recentrequests": "Recent Requests", "components.Discover.recentrequests": "Recent Requests",
"components.Discover.trending": "Trending",
"components.Discover.upcoming": "Upcoming Movies", "components.Discover.upcoming": "Upcoming Movies",
"components.Layout.LanguagePicker.changelanguage": "Change Language", "components.Layout.LanguagePicker.changelanguage": "Change Language",
"components.Layout.SearchInput.searchPlaceholder": "Search Movies & TV", "components.Layout.SearchInput.searchPlaceholder": "Search Movies & TV",
"components.Layout.Sidebar.dashboard": "Dashboard", "components.Layout.Sidebar.dashboard": "Discover",
"components.Layout.Sidebar.requests": "Requests", "components.Layout.Sidebar.requests": "Requests",
"components.Layout.Sidebar.settings": "Settings", "components.Layout.Sidebar.settings": "Settings",
"components.Layout.Sidebar.users": "Users", "components.Layout.Sidebar.users": "Users",
@@ -76,6 +77,29 @@
"components.TvDetails.status": "Status", "components.TvDetails.status": "Status",
"components.TvDetails.unavailable": "Unavailable", "components.TvDetails.unavailable": "Unavailable",
"components.TvDetails.userrating": "User Rating", "components.TvDetails.userrating": "User Rating",
"components.UserEdit.admin": "Admin",
"components.UserEdit.adminDescription": "Full administrator access. Bypasses all permission checks.",
"components.UserEdit.autoapprove": "Auto Approve",
"components.UserEdit.autoapproveDescription": "Grants auto approval for any requests made by this user.",
"components.UserEdit.avatar": "Avatar",
"components.UserEdit.edituser": "Edit User",
"components.UserEdit.email": "Email",
"components.UserEdit.managerequests": "Manage Requests",
"components.UserEdit.managerequestsDescription": "Grants permission to manage Overseerr requests. This includes approving and denying requests.",
"components.UserEdit.permissions": "Permissions",
"components.UserEdit.request": "Request",
"components.UserEdit.requestDescription": "Grants permission to make requests for movies or tv shows.",
"components.UserEdit.save": "Save",
"components.UserEdit.saving": "Saving...",
"components.UserEdit.settings": "Manage Settings",
"components.UserEdit.settingsDescription": "Grants permission to modify all Overseerr settings. User must have this permission to be able to grant it to others.",
"components.UserEdit.userfail": "Something went wrong saving the user.",
"components.UserEdit.username": "Username",
"components.UserEdit.users": "Manage Users",
"components.UserEdit.usersDescription": "Grants permission to manage Overseerr users. Users with this permission cannot modify users with Administrator privilege, or grant it.",
"components.UserEdit.usersaved": "User succesfully saved",
"components.UserEdit.vote": "Vote",
"components.UserEdit.voteDescription": "Grants permission to vote on requests (voting not yet implemented)",
"pages.internalServerError": "{statusCode} - Internal Server Error", "pages.internalServerError": "{statusCode} - Internal Server Error",
"pages.oops": "Oops", "pages.oops": "Oops",
"pages.pageNotFound": "404 - Page Not Found", "pages.pageNotFound": "404 - Page Not Found",

View File

@@ -6,6 +6,7 @@
"components.Discover.populartv": "人気のテレビ番組", "components.Discover.populartv": "人気のテレビ番組",
"components.Discover.recentlyAdded": "", "components.Discover.recentlyAdded": "",
"components.Discover.recentrequests": "最近のリクエスト", "components.Discover.recentrequests": "最近のリクエスト",
"components.Discover.trending": "",
"components.Discover.upcoming": "", "components.Discover.upcoming": "",
"components.Layout.LanguagePicker.changelanguage": "言語", "components.Layout.LanguagePicker.changelanguage": "言語",
"components.Layout.SearchInput.searchPlaceholder": "作品名で検索", "components.Layout.SearchInput.searchPlaceholder": "作品名で検索",
@@ -76,6 +77,29 @@
"components.TvDetails.status": "", "components.TvDetails.status": "",
"components.TvDetails.unavailable": "", "components.TvDetails.unavailable": "",
"components.TvDetails.userrating": "", "components.TvDetails.userrating": "",
"components.UserEdit.admin": "",
"components.UserEdit.adminDescription": "",
"components.UserEdit.autoapprove": "",
"components.UserEdit.autoapproveDescription": "",
"components.UserEdit.avatar": "",
"components.UserEdit.edituser": "",
"components.UserEdit.email": "",
"components.UserEdit.managerequests": "",
"components.UserEdit.managerequestsDescription": "",
"components.UserEdit.permissions": "",
"components.UserEdit.request": "",
"components.UserEdit.requestDescription": "",
"components.UserEdit.save": "",
"components.UserEdit.saving": "",
"components.UserEdit.settings": "",
"components.UserEdit.settingsDescription": "",
"components.UserEdit.userfail": "",
"components.UserEdit.username": "",
"components.UserEdit.users": "",
"components.UserEdit.usersDescription": "",
"components.UserEdit.usersaved": "",
"components.UserEdit.vote": "",
"components.UserEdit.voteDescription": "",
"pages.internalServerError": "", "pages.internalServerError": "",
"pages.oops": "ああ", "pages.oops": "ああ",
"pages.pageNotFound": "", "pages.pageNotFound": "",

View File

@@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import { NextPage } from 'next'; import { NextPage } from 'next';
import DiscoverMovies from '../../components/Discover/DiscoverMovies'; import DiscoverMovies from '../../../components/Discover/DiscoverMovies';
const DiscoverMoviesPage: NextPage = () => { const DiscoverMoviesPage: NextPage = () => {
return <DiscoverMovies />; return <DiscoverMovies />;

View File

@@ -0,0 +1,9 @@
import React from 'react';
import { NextPage } from 'next';
import UpcomingMovies from '../../../components/Discover/Upcoming';
const UpcomingMoviesPage: NextPage = () => {
return <UpcomingMovies />;
};
export default UpcomingMoviesPage;

View File

@@ -0,0 +1,9 @@
import React from 'react';
import type { NextPage } from 'next';
import Trending from '../../components/Discover/Trending';
const TrendingPage: NextPage = () => {
return <Trending />;
};
export default TrendingPage;