Skip to content

Commit

Permalink
feat: upcoming/trending list views and larger title cards
Browse files Browse the repository at this point in the history
  • Loading branch information
sct committed Nov 17, 2020
1 parent 185ac26 commit 94eaaf9
Show file tree
Hide file tree
Showing 17 changed files with 456 additions and 218 deletions.
21 changes: 21 additions & 0 deletions src/components/Common/Header/index.tsx
Original file line number Diff line number Diff line change
@@ -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;
14 changes: 10 additions & 4 deletions src/components/Common/ListView/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const ListView: React.FC<ListViewProps> = ({
No Results
</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) => {
let titleCard: React.ReactNode;

Expand All @@ -47,6 +47,7 @@ const ListView: React.FC<ListViewProps> = ({
userScore={title.voteAverage}
year={title.releaseDate}
mediaType={title.mediaType}
canExpand
/>
);
break;
Expand All @@ -61,12 +62,17 @@ const ListView: React.FC<ListViewProps> = ({
userScore={title.voteAverage}
year={title.firstAirDate}
mediaType={title.mediaType}
canExpand
/>
);
break;
case 'person':
titleCard = (
<PersonCard name={title.name} profilePath={title.profilePath} />
<PersonCard
name={title.name}
profilePath={title.profilePath}
canExpand
/>
);
break;
}
Expand All @@ -82,12 +88,12 @@ const ListView: React.FC<ListViewProps> = ({
})}
{isLoading &&
!isReachingEnd &&
[...Array(10)].map((_item, i) => (
[...Array(20)].map((_item, i) => (
<li
key={`placeholder-${i}`}
className="col-span-1 flex flex-col text-center items-center"
>
<TitleCard.Placeholder />
<TitleCard.Placeholder canExpand />
</li>
))}
</ul>
Expand Down
13 changes: 5 additions & 8 deletions src/components/Discover/DiscoverMovies.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ 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({
discovermovies: 'Discover Movies',
discovermovies: 'Popular Movies',
});

interface SearchResult {
Expand Down Expand Up @@ -55,13 +56,9 @@ const DiscoverMovies: React.FC = () => {

return (
<>
<div className="md:flex md:items-center md:justify-between mb-8 mt-6">
<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} />
</h2>
</div>
</div>
<Header>
<FormattedMessage {...messages.discovermovies} />
</Header>
<ListView
items={titles}
isEmpty={isEmpty}
Expand Down
15 changes: 6 additions & 9 deletions src/components/Discover/DiscoverTv.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import React, { useContext } from 'react';
import { useSWRInfinite } from 'swr';
import { TvResult } from '../../../server/models/Search';
import type { TvResult } from '../../../server/models/Search';
import ListView from '../Common/ListView';
import { defineMessages, FormattedMessage } from 'react-intl';
import { LanguageContext } from '../../context/LanguageContext';
import Header from '../Common/Header';

const messages = defineMessages({
discovertv: 'Discover Series',
discovertv: 'Popular Series',
});

interface SearchResult {
Expand Down Expand Up @@ -52,13 +53,9 @@ const DiscoverTv: React.FC = () => {

return (
<>
<div className="md:flex md:items-center md:justify-between mb-8 mt-6">
<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} />
</h2>
</div>
</div>
<Header>
<FormattedMessage {...messages.discovertv} />
</Header>
<ListView
items={titles}
isEmpty={isEmpty}
Expand Down
81 changes: 81 additions & 0 deletions src/components/Discover/Trending.tsx
Original file line number Diff line number Diff line change
@@ -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;
77 changes: 77 additions & 0 deletions src/components/Discover/Upcoming.tsx
Original file line number Diff line number Diff line change
@@ -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;
26 changes: 5 additions & 21 deletions src/components/Discover/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,27 +77,11 @@ const Discover: React.FC = () => {
<>
<div className="md:flex md:items-center md:justify-between mb-4 mt-6">
<div className="flex-1 min-w-0">
<Link href="/recent">
<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>
<FormattedMessage {...messages.recentlyAdded} />
</span>
<svg
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 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>
<FormattedMessage {...messages.recentlyAdded} />
</span>
</div>
</div>
</div>
<Slider
Expand Down
8 changes: 7 additions & 1 deletion src/components/PersonCard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,21 @@ interface PersonCardProps {
name: string;
subName?: string;
profilePath?: string;
canExpand?: boolean;
}

const PersonCard: React.FC<PersonCardProps> = ({
name,
subName,
profilePath,
canExpand = false,
}) => {
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 className="absolute inset-0 flex flex-col items-center justify-center">
{profilePath && (
Expand Down
12 changes: 10 additions & 2 deletions src/components/TitleCard/Placeholder.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
import React from 'react';

const Placeholder: React.FC = () => {
interface PlaceholderProps {
canExpand?: boolean;
}

const Placeholder: React.FC<PlaceholderProps> = ({ canExpand = false }) => {
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>
);
Expand Down
6 changes: 3 additions & 3 deletions src/components/TitleCard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ interface TitleCardProps {
userScore: number;
mediaType: MediaType;
status?: MediaStatus;
requestId?: number;
canExpand?: boolean;
}

const TitleCard: React.FC<TitleCardProps> = ({
Expand All @@ -30,7 +30,7 @@ const TitleCard: React.FC<TitleCardProps> = ({
title,
status,
mediaType,
requestId,
canExpand = false,
}) => {
const [isUpdating, setIsUpdating] = useState(false);
const [currentStatus, setCurrentStatus] = useState(status);
Expand All @@ -55,7 +55,7 @@ const TitleCard: React.FC<TitleCardProps> = ({
const closeModal = useCallback(() => setShowRequestModal(false), []);

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
tmdbId={id}
show={showRequestModal}
Expand Down

0 comments on commit 94eaaf9

Please sign in to comment.