Skip to content

Commit

Permalink
feat: add trending section and page (#36)
Browse files Browse the repository at this point in the history
  • Loading branch information
hobroker authored May 8, 2022
1 parent 6e117ad commit 1d29aa3
Show file tree
Hide file tree
Showing 22 changed files with 341 additions and 81 deletions.
12 changes: 8 additions & 4 deletions schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,6 @@ type Preference {
}

type Query {
allUsers: User!
discoverShows(input: DiscoverShowsInput!): [PartialShow!]!
fullShow(input: FullShowInput!): FullShow!
getMyReview(input: GetReviewInput!): Review
Expand All @@ -99,17 +98,18 @@ type Query {
getStatsSummary: [StatsSummaryItem!]!
getWatchlist: [Watchlist!]!
listGenres: [Genre!]
listTrending(input: TrendingInput!): [PartialShow!]!
listUpNext: [Episode!]!
listUpcoming: [Episode!]!
me: User!
search(input: SearchInput!): [PartialShow!]!
}

type Review {
content: String!
content: String
id: Int!
rating: Int!
title: String!
rating: Int
title: String
user: User!
}

Expand Down Expand Up @@ -159,6 +159,10 @@ input ToggleGenrePreferenceInput {
genreId: Int!
}

input TrendingInput {
page: Int
}

input UpsertEpisodeInput {
episodeId: Int!
isWatched: Boolean!
Expand Down
9 changes: 7 additions & 2 deletions src/components/IndefiniteLoading.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react';
import { Box, CircularProgress } from '@mui/material';
import { Sx } from '../utils/types';

export enum IndefiniteLoadingSize {
Small = 24,
Expand All @@ -8,10 +9,14 @@ export enum IndefiniteLoadingSize {

interface Props {
size?: IndefiniteLoadingSize;
sx?: Sx;
}

const IndefiniteLoading = ({ size = IndefiniteLoadingSize.Large }: Props) => (
<Box sx={{ display: 'flex', justifyContent: 'center' }}>
const IndefiniteLoading = ({
size = IndefiniteLoadingSize.Large,
sx,
}: Props) => (
<Box sx={{ display: 'flex', justifyContent: 'center', ...sx }}>
<CircularProgress color="inherit" size={size} thickness={4} />
</Box>
);
Expand Down
3 changes: 3 additions & 0 deletions src/features/home/components/HomePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import UpNext from '../features/upnext/components/UpNext';
import WhenAnonymous from '../../user/components/WhenAnonymous';
import StatsSummary from '../../stats/components/summary/StatsSummary';
import StatsSummaryProvider from '../../stats/contexts/StatsSummaryContext';
import Trending from '../../shows/features/trending/components/section/Trending';
import LoginAlert from './LoginAlert';

const HomePage = () => (
Expand All @@ -23,6 +24,8 @@ const HomePage = () => (
</StatsSummaryProvider>
<Divider />
<Upcoming />
<Divider />
<Trending />
</WhenLoggedIn>
</PageWrapper>
);
Expand Down
5 changes: 2 additions & 3 deletions src/features/navigation/components/Navigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,8 @@ const Navigation = () => (
</SearchProvider>
</Box>
<Box sx={{ mr: 1 }}>
<Button onClick={() => {}} href={StaticRoute.MyShows}>
My Shows
</Button>
<Button href={StaticRoute.Trending}>Trending</Button>
<Button href={StaticRoute.MyShows}>My Shows</Button>
</Box>
<Box sx={{ display: 'flex' }}>
<NotificationsBadge />
Expand Down
6 changes: 4 additions & 2 deletions src/features/review/components/ReviewForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { Box, Button, Stack, TextField, Typography } from '@mui/material';
import { useForm } from 'react-hook-form';
import * as yup from 'yup';
import React, { useContext } from 'react';
import { pick } from 'ramda';
import useYupValidationResolver from '../../join/hooks/useYupValidationResolver';
import { ReviewContext } from '../contexts/ReviewContext';
import ShowRating from './ShowRating';
Expand All @@ -23,7 +22,10 @@ interface Props {

const ReviewForm = ({ toggleIsFormOpen }: Props) => {
const { ownReview, upsertReview } = useContext(ReviewContext);
const defaultValues = ownReview && pick(['title', 'content'], ownReview);
const defaultValues = ownReview && {
title: ownReview.title || '',
content: ownReview.content || '',
};
const resolver = useYupValidationResolver(validationSchema);
const {
register,
Expand Down
4 changes: 3 additions & 1 deletion src/features/review/components/ReviewsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,9 @@ const ReviewsList = ({ toggleIsFormOpen }: Props) => {
component="span"
sx={{ display: 'flex', alignItems: 'center', gap: 1 }}
>
<RatingInfo value={review.rating} variant="body1" />
{review.rating && (
<RatingInfo value={review.rating} variant="body1" />
)}
{review.title}
</Box>
}
Expand Down
2 changes: 1 addition & 1 deletion src/features/review/components/ShowRating.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const ShowRating = () => {
);

useEffect(() => {
if (!ownReview) return;
if (!ownReview || !ownReview.rating) return;

setRating(ownReview.rating);
// eslint-disable-next-line react-hooks/exhaustive-deps
Expand Down
2 changes: 1 addition & 1 deletion src/features/review/contexts/ReviewContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ const ReviewProvider = ({ children }: Props) => {
}, [data]);

useEffect(() => {
if (!ratingData) return;
if (!ratingData || !ratingData.getRating.rating) return;

setRating(ratingData.getRating.rating);
}, [ratingData]);
Expand Down
18 changes: 0 additions & 18 deletions src/features/review/hooks/useRating.ts

This file was deleted.

2 changes: 2 additions & 0 deletions src/features/router/components/Router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ import LoginPage from '../../join/components/LoginPage';
import RegisterPage from '../../join/components/RegisterPage';
import ShowPage from '../../shows/components/show-page/ShowPage';
import MyShowsPage from '../../shows/features/my-shows/components/MyShowsPage';
import TrendingPage from '../../shows/features/trending/components/page/TrendingPage';
import PrivateOutlet from './PrivateOutlet';

const Router = () => (
<Routes>
<Route path={StaticRoute.Home} element={<HomePage />} />
<Route path={StaticRoute.Trending} element={<TrendingPage />} />
<Route path={StaticRoute.Login} element={<LoginPage />} />
<Route path={StaticRoute.Register} element={<RegisterPage />} />
<Route path={StaticRoute.Welcome} element={<PrivateOutlet />}>
Expand Down
1 change: 1 addition & 0 deletions src/features/router/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export enum StaticRoute {
Login = '/login',
Register = '/join',
MyShows = '/my-shows',
Trending = '/trending',
}

export enum __DynamicRoute {
Expand Down
6 changes: 3 additions & 3 deletions src/features/search/contexts/SearchContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ import {
} from 'react';
import { noop } from '../../../utils/fp';
import {
SearchShowFragment,
ShowSummaryFragment,
useSearchLazyQuery,
} from '../../../generated/graphql';

interface ContextType {
query: string;
setQuery: (search: string) => void;
results: SearchShowFragment[];
results: ShowSummaryFragment[];
loading: boolean;
reset: () => void;
}
Expand All @@ -33,7 +33,7 @@ const SearchContext = createContext<ContextType>({

const SearchProvider = ({ children }: Props) => {
const [query, setQuery] = useState('');
const [results, setResults] = useState<SearchShowFragment[]>([]);
const [results, setResults] = useState<ShowSummaryFragment[]>([]);
const [searchShowsQuery, { data, loading }] = useSearchLazyQuery();
const reset = useCallback(() => {
setQuery('');
Expand Down
11 changes: 1 addition & 10 deletions src/features/search/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,7 @@ import { gql } from '@apollo/client';
export const QUERY_SEARCH = gql`
query Search($query: String!) {
search(input: { query: $query }) {
...SearchShow
...ShowSummary
}
}
`;

export const PARTIAL_SEARCH_SHOW_FRAGMENT = gql`
fragment SearchShow on PartialShow {
externalId
name
description
wideImage
}
`;
15 changes: 14 additions & 1 deletion src/features/shows/components/ShowsCollection.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
import React from 'react';
import { Box } from '@mui/material';
import { PartialShow } from '../../../generated/graphql';
import TallCardPlaceholder from '../components/card/TallCardPlaceholder';
import TallCardCollection from '../components/card/TallCardCollection';
import TallShowCard from '../components/TallShowCard';
import IndefiniteLoading from '../../../components/IndefiniteLoading';

interface Props {
shows: Pick<PartialShow, 'externalId' | 'tallImage' | 'name'>[];
loading?: boolean;
loadingIndicator?: boolean;
scroll: boolean;
}

const ShowsCollection = ({ shows, loading = false, scroll }: Props) => (
const ShowsCollection = ({
shows,
loading = false,
loadingIndicator = false,
scroll,
}: Props) => (
<TallCardCollection
loading={loading}
PlaceholderComponent={TallCardPlaceholder}
Expand All @@ -24,6 +32,11 @@ const ShowsCollection = ({ shows, loading = false, scroll }: Props) => (
name={name}
/>
))}
{loadingIndicator && (
<Box>
<IndefiniteLoading sx={{ height: '100%', alignItems: 'center' }} />
</Box>
)}
</TallCardCollection>
);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { useCallback, useContext, useEffect, useRef } from 'react';
import { Box } from '@mui/material';
import TrendingUpIcon from '@mui/icons-material/TrendingUp';
import { TrendingContext } from '../../contexts/TrendingContext';
import Section from '../../../../../../components/Section';
import ShowsCollection from '../../../../components/ShowsCollection';

const TrendingContent = () => {
const { shows, loading, fetchNextPage } = useContext(TrendingContext);
const loader = useRef(null);

const handleObserver = useCallback(
([target]) => {
if (target.isIntersecting && !loading) {
fetchNextPage();
}
},
[fetchNextPage, loading],
);

useEffect(() => {
const observer = new IntersectionObserver(handleObserver, {
root: null,
rootMargin: '1px',
threshold: 0,
});

if (loader.current) observer.observe(loader.current);
}, [handleObserver]);

return (
<Box>
<Section title="Trending" icon={<TrendingUpIcon />}>
<ShowsCollection
shows={shows}
loading={false}
scroll={false}
loadingIndicator={loading}
/>
</Section>
<div ref={loader} />
</Box>
);
};

export default TrendingContent;
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import TrendingProvider from '../../contexts/TrendingContext';
import PageWrapper from '../../../../../../components/PageWrapper';
import TrendingContent from './TrendingContent';

const TrendingPage = () => (
<PageWrapper>
<TrendingProvider>
<TrendingContent />
</TrendingProvider>
</PageWrapper>
);

export default TrendingPage;
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import TrendingProvider from '../../contexts/TrendingContext';
import TrendingSection from './TrendingSection';

const Trending = () => (
<TrendingProvider>
<TrendingSection />
</TrendingProvider>
);

export default Trending;
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { useContext } from 'react';
import TrendingUpIcon from '@mui/icons-material/TrendingUp';
import { TrendingContext } from '../../contexts/TrendingContext';
import Section from '../../../../../../components/Section';
import ShowsCollection from '../../../../components/ShowsCollection';

const TrendingSection = () => {
const { shows, loading } = useContext(TrendingContext);

return (
<Section title="Trending" icon={<TrendingUpIcon />}>
<ShowsCollection shows={shows} loading={loading} scroll />
</Section>
);
};

export default TrendingSection;
Loading

0 comments on commit 1d29aa3

Please sign in to comment.