diff --git a/src/components/Episode/Episode.Transcript.tsx b/src/components/Episode/Episode.Transcript.tsx index 2875eb4..e0b04e4 100644 --- a/src/components/Episode/Episode.Transcript.tsx +++ b/src/components/Episode/Episode.Transcript.tsx @@ -8,9 +8,6 @@ const EpisodeTranscript = ({ episode }: { episode: LPE.Podcast.Document }) => { return ( <> - - Transcript - ) diff --git a/src/components/Episode/Footer/Episode.RelatedEpisodes.tsx b/src/components/Episode/Footer/Episode.RelatedEpisodes.tsx index 962e805..901a312 100644 --- a/src/components/Episode/Footer/Episode.RelatedEpisodes.tsx +++ b/src/components/Episode/Footer/Episode.RelatedEpisodes.tsx @@ -28,25 +28,10 @@ const RelatedEpisodes = ({ podcastSlug, relatedEpisodes }: props) => { {relatedEpisodes.slice(0, showIndex).map((episode, idx: number) => ( ))} diff --git a/src/components/Podcasts/Episodes.List.tsx b/src/components/Podcasts/Episodes.List.tsx index 9845aeb..b758262 100644 --- a/src/components/Podcasts/Episodes.List.tsx +++ b/src/components/Podcasts/Episodes.List.tsx @@ -1,57 +1,37 @@ import styled from '@emotion/styled' import { LPE } from '../../types/lpe.types' -import { PostCard } from '@/components/PostCard/PostCard' -import { Grid, GridItem } from '../Grid/Grid' +import { PostsGrid, PostsGridProps } from '../PostsGrid' interface Props { header?: React.ReactNode episodes: LPE.Podcast.Document[] - show?: LPE.Podcast.Show - divider?: boolean - isFeatured?: boolean + shows?: LPE.Podcast.Show[] + bordered?: boolean + size?: PostsGridProps['size'] + cols?: number + displayShow?: boolean } export default function EpisodesList({ + shows = [], header, episodes, - show, - divider = false, - isFeatured = false, + bordered = false, + cols = 4, + size = 'small', + displayShow = true, }: Props) { return ( {header} - - {episodes.map((episode) => ( - - - - ))} - + ) } @@ -61,13 +41,3 @@ const EpisodeListContainer = styled.div` flex-direction: column; padding-top: 16px; ` - -const EpisodesContainer = styled(Grid)` - gap: 36px 16px; -` - -const PostCardContainer = styled(GridItem)<{ divider: boolean }>` - padding-block: 24px; - border-top: ${({ divider }) => - divider ? '1px solid rgb(var(--lsd-border-primary))' : 'none'}; -` diff --git a/src/components/Podcasts/PodcastShowCard.tsx b/src/components/Podcasts/PodcastShowCard.tsx index c28a8c0..0661a83 100644 --- a/src/components/Podcasts/PodcastShowCard.tsx +++ b/src/components/Podcasts/PodcastShowCard.tsx @@ -25,7 +25,10 @@ export default function PodcastShowCard({ {show.title} - {show.description} + ) diff --git a/src/components/Podcasts/Podcasts.Lists.tsx b/src/components/Podcasts/Podcasts.Lists.tsx index 7b51802..66afe48 100644 --- a/src/components/Podcasts/Podcasts.Lists.tsx +++ b/src/components/Podcasts/Podcasts.Lists.tsx @@ -31,7 +31,10 @@ export default function PodcastsLists({ shows }: Props) { {show.numberOfEpisodes} EP - {show.description} + diff --git a/src/components/PostCard/PostCard.tsx b/src/components/PostCard/PostCard.tsx index 22e3b65..e5f36a1 100644 --- a/src/components/PostCard/PostCard.tsx +++ b/src/components/PostCard/PostCard.tsx @@ -39,6 +39,7 @@ export type PostCardProps = CommonProps & data: PostDataProps contentType: LPE.PostType size?: 'xxsmall' | 'xsmall' | 'small' | 'medium' | 'large' + displayPodcastShow?: boolean } export const PostCard = (_props: PostCardProps) => { @@ -56,6 +57,7 @@ export const PostCard = (_props: PostCardProps) => { }, size = 'small', contentType, + displayPodcastShow = true, ...props } = _props @@ -118,7 +120,7 @@ export const PostCard = (_props: PostCardProps) => { /> ) - const showElement = podcastShowDetails && ( + const showElement = displayPodcastShow && podcastShowDetails && ( { PostCard.toData = (post: LPE.Post.Document, shows: LPE.Podcast.Show[] = []) => { const show = post.type === 'podcast' - ? post.show || shows.find((show) => show.id === post.showId) + ? post.show || shows.find((show) => show.id === post.showId) || shows[0] : undefined return { diff --git a/src/components/PostsGrid/PostsGrid.tsx b/src/components/PostsGrid/PostsGrid.tsx index a0e125e..009faf7 100644 --- a/src/components/PostsGrid/PostsGrid.tsx +++ b/src/components/PostsGrid/PostsGrid.tsx @@ -6,14 +6,18 @@ import { chunkArray } from '../../utils/array.utils' import { PostCard, PostCardProps } from '../PostCard' export type PostsGridProps = Partial> & { + shows?: LPE.Podcast.Show[] posts?: LPE.Post.Document[] + displayPodcastShow?: boolean } export const PostsGrid: React.FC = ({ cols = 4, size = 'small', posts = [], + shows = [], bordered = false, + displayPodcastShow = true, ...props }) => { const groups = useMemo(() => chunkArray(posts, cols), [posts, cols]) @@ -28,7 +32,8 @@ export const PostsGrid: React.FC = ({ size={size} className="post-card" contentType={post.type} - data={PostCard.toData(post)} + displayPodcastShow={displayPodcastShow} + data={PostCard.toData(post, shows)} /> ))} @@ -50,7 +55,7 @@ const Container = styled.div<{ > .row { display: grid; grid-template-columns: repeat(${props.cols}, 1fr); - gap: 0 32px; + gap: 0 16px; & > div { padding: 24px 0; diff --git a/src/containers/PodcastShowContainer.tsx b/src/containers/PodcastShowContainer.tsx index e3d3321..99cfad2 100644 --- a/src/containers/PodcastShowContainer.tsx +++ b/src/containers/PodcastShowContainer.tsx @@ -1,12 +1,11 @@ import { Grid, GridItem } from '@/components/Grid/Grid' -import styled from '@emotion/styled' -import { LPE } from '../types/lpe.types' import EpisodesList from '@/components/Podcasts/Episodes.List' -import { Button, Typography } from '@acid-info/lsd-react' -import PodcastShowCard from '@/components/Podcasts/PodcastShowCard' import PodcastSection from '@/components/Podcasts/Podcast.Section' -import { api } from '@/services/api.service' -import { useState } from 'react' +import PodcastShowCard from '@/components/Podcasts/PodcastShowCard' +import { Button, Typography } from '@acid-info/lsd-react' +import styled from '@emotion/styled' +import { useRecentEpisodes } from '../queries/useRecentEpisodes.query' +import { LPE } from '../types/lpe.types' interface Props { show: LPE.Podcast.Show @@ -16,25 +15,12 @@ interface Props { const PodcastShowContainer = (props: Props) => { const { show, latestEpisodes, highlightedEpisodes } = props - const [latest, setLatest] = useState(latestEpisodes) - const [page, setPage] = useState(2) - const [showSeeMore, setShowSeeMore] = useState(true) - const handleLoadMore = async () => { - const response = await api.getLatestEpisodes({ - page: page, - limit: 9, - showSlug: show.slug, - }) - - if (response.data.length === 0) { - setShowSeeMore(false) - return - } - - setLatest((prev) => [...prev, ...response.data]) - setPage((prev) => prev + 1) - } + const query = useRecentEpisodes({ + limit: 8, + showSlug: show.slug, + initialData: latestEpisodes, + }) return ( <> @@ -43,17 +29,26 @@ const PodcastShowContainer = (props: Props) => { All episodes} + cols={2} + size="medium" + shows={[show]} + displayShow={false} episodes={highlightedEpisodes} - show={show} - isFeatured={true} + header={All episodes} /> - + - {showSeeMore && ( - + {query.hasNextPage && ( + query.fetchNextPage()}> See more episodes )} diff --git a/src/containers/PodcastShowsPreview/PodcastShowsPreview.tsx b/src/containers/PodcastShowsPreview/PodcastShowsPreview.tsx index 84cd1fc..4984a7c 100644 --- a/src/containers/PodcastShowsPreview/PodcastShowsPreview.tsx +++ b/src/containers/PodcastShowsPreview/PodcastShowsPreview.tsx @@ -82,11 +82,15 @@ export const PodcastShowsPreview: React.FC = ({
diff --git a/src/containers/PodcastsContainer.tsx b/src/containers/PodcastsContainer.tsx index 6ffb674..707bd44 100644 --- a/src/containers/PodcastsContainer.tsx +++ b/src/containers/PodcastsContainer.tsx @@ -1,12 +1,12 @@ import { Grid, GridItem } from '@/components/Grid/Grid' -import styled from '@emotion/styled' -import { LPE } from '../types/lpe.types' -import PodcastsLists from '@/components/Podcasts/Podcasts.Lists' -import EpisodesList from '@/components/Podcasts/Episodes.List' -import { Button, Typography } from '@acid-info/lsd-react' import { LogosCircleIcon } from '@/components/Icons/LogosCircleIcon' -import Link from 'next/link' +import EpisodesList from '@/components/Podcasts/Episodes.List' import PodcastSection from '@/components/Podcasts/Podcast.Section' +import PodcastsLists from '@/components/Podcasts/Podcasts.Lists' +import { Button, Typography } from '@acid-info/lsd-react' +import styled from '@emotion/styled' +import Link from 'next/link' +import { LPE } from '../types/lpe.types' interface Props { shows: LPE.Podcast.Show[] @@ -23,13 +23,16 @@ const PodcastsContainer = (props: Props) => { Latest Episodes} + cols={2} + size="medium" episodes={highlightedEpisodes.slice(0, 2)} - isFeatured={true} + header={Latest Episodes} /> @@ -52,8 +55,9 @@ const PodcastsContainer = (props: Props) => { } + shows={[show]} + displayShow={false} episodes={show.episodes as LPE.Podcast.Document[]} - show={show} /> ))} diff --git a/src/pages/api/podcasts/[showSlug]/episodes/index.ts b/src/pages/api/podcasts/[showSlug]/episodes/index.ts index 7ca0500..574163d 100644 --- a/src/pages/api/podcasts/[showSlug]/episodes/index.ts +++ b/src/pages/api/podcasts/[showSlug]/episodes/index.ts @@ -7,11 +7,11 @@ export default async function handler( res: NextApiResponse, ) { const { - query: { page = 1, limit = 10, showSlug }, + query: { skip = 0, limit = 10, showSlug }, } = req const response = await unbodyApi.getLatestEpisodes({ - page: parseInt(page, 1), + skip: parseInt(skip, 0), limit: parseInt(limit, 10), showSlug: Array.isArray(showSlug) ? showSlug[0] : showSlug, }) diff --git a/src/pages/podcasts/[showSlug]/index.tsx b/src/pages/podcasts/[showSlug]/index.tsx index 99c2ab0..ebf3c7a 100644 --- a/src/pages/podcasts/[showSlug]/index.tsx +++ b/src/pages/podcasts/[showSlug]/index.tsx @@ -1,9 +1,9 @@ import { SEO } from '@/components/SEO' +import PodcastsLayout from '@/layouts/PodcastsLayout/Podcasts.layout' import { GetStaticPropsContext } from 'next' import { useRouter } from 'next/router' import { ReactNode } from 'react' import { LPE } from '../../../types/lpe.types' -import PodcastsLayout from '@/layouts/PodcastsLayout/Podcasts.layout' import PodcastShowContainer from '@/containers/PodcastShowContainer' import unbodyApi from '@/services/unbody/unbody.service' @@ -83,15 +83,13 @@ export const getStaticProps = async ({ params }: GetStaticPropsContext) => { const { data: latestEpisodes, errors: latestEpisodesErros } = await unbodyApi.getLatestEpisodes({ showSlug: showSlug as string, - page: 1, - limit: 9, + limit: 8, }) // TODO : error handling const { data: highlightedEpisodes, errors: highlightedEpisodesErrors } = await unbodyApi.getHighlightedEpisodes({ showSlug: showSlug as string, - page: 1, limit: 2, }) diff --git a/src/queries/useRecentEpisodes.query.ts b/src/queries/useRecentEpisodes.query.ts new file mode 100644 index 0000000..72b8322 --- /dev/null +++ b/src/queries/useRecentEpisodes.query.ts @@ -0,0 +1,58 @@ +import { useInfiniteQuery } from '@tanstack/react-query' +import { useMemo } from 'react' +import { api } from '../services/api.service' +import { LPE } from '../types/lpe.types' + +export const useRecentEpisodes = ({ + initialData = [], + limit = 10, + showSlug, +}: { + initialData?: LPE.Podcast.Document[] + showSlug: string + limit?: number +}) => { + const query = useInfiniteQuery( + ['recent-episodes', showSlug, initialData, limit], + async ({ pageParam }) => { + const firstPageLimit = initialData.length + const _limit = pageParam === 1 ? firstPageLimit : limit + const skip = + pageParam === 1 ? 0 : (pageParam - 2) * limit + firstPageLimit + + return api + .getLatestEpisodes({ skip, limit: _limit, showSlug }) + .then((res) => ({ + page: pageParam, + posts: res.data, + hasMore: res.data.length !== 0, + })) + }, + { + initialData: { + pageParams: [1], + pages: [ + { + page: 1, + hasMore: true, + posts: initialData, + }, + ], + }, + getNextPageParam: (lastPage, all) => + lastPage.hasMore ? lastPage.page + 1 : undefined, + getPreviousPageParam: (firstPage) => + firstPage ? firstPage.page - 1 : undefined, + }, + ) + + const posts = useMemo( + () => (query.data?.pages || []).flatMap((page) => page.posts), + [query.data], + ) + + return { + posts, + ...query, + } +} diff --git a/src/queries/useRecentPosts.query.ts b/src/queries/useRecentPosts.query.ts index 2584be3..10f8b79 100644 --- a/src/queries/useRecentPosts.query.ts +++ b/src/queries/useRecentPosts.query.ts @@ -21,7 +21,7 @@ export const useRecentPosts = ({ return api.getRecentPosts({ skip, limit: _limit }).then((res) => ({ page: pageParam, posts: res.data, - hasMore: res.data.length === _limit, + hasMore: res.data.length !== 0, })) }, { diff --git a/src/services/api.service.ts b/src/services/api.service.ts index 5febcb6..6257b14 100644 --- a/src/services/api.service.ts +++ b/src/services/api.service.ts @@ -17,15 +17,15 @@ export class ApiService { }) getLatestEpisodes = async ({ - page = 1, + skip = 0, limit = 10, showSlug, }: { - page?: number + skip?: number limit?: number showSlug: string }): Promise> => - fetch(`/api/podcasts/${showSlug}/episodes?page=${page}&limit=${limit}`) + fetch(`/api/podcasts/${showSlug}/episodes?skip=${skip}&limit=${limit}`) .then((res) => res.json()) .catch((e) => { console.error(e) diff --git a/src/services/unbody/unbody.helpers.ts b/src/services/unbody/unbody.helpers.ts index 78b1520..f9a9af5 100644 --- a/src/services/unbody/unbody.helpers.ts +++ b/src/services/unbody/unbody.helpers.ts @@ -19,9 +19,9 @@ export class UnbodyHelpers { static args = { limit: (value: number): any => String(value), skip: (value: number): any => String(value), - page: (page: number, limit: number = 10) => ({ + page: (skip: number, limit: number = 10) => ({ limit: this.args.limit(limit), - skip: this.args.skip(Math.max(0, page - 1) * limit), + skip: this.args.skip(skip), }), wherePath: (path: Array) => { const input = path.filter((p) => p && typeof p === 'string') as string[] diff --git a/src/services/unbody/unbody.service.ts b/src/services/unbody/unbody.service.ts index 25d24fd..9f6bf3c 100644 --- a/src/services/unbody/unbody.service.ts +++ b/src/services/unbody/unbody.service.ts @@ -130,13 +130,11 @@ export class UnbodyService { private fetchAllEpisodes = async () => { const result: LPE.Podcast.Document[] = [] - let page = 0 + let skip = 0 const limit = 50 while (true) { - page++ - const { data } = await this.getPodcastEpisodes({ - page, + skip, limit, highlighted: 'exclude', includeDrafts: false, @@ -153,6 +151,8 @@ export class UnbodyService { ) if (data.length === 0) break + + skip += 50 } return result @@ -161,13 +161,11 @@ export class UnbodyService { private fetchAllArticles = async () => { const result: LPE.Article.Data[] = [] - let page = 0 + let skip = 0 const limit = 50 while (true) { - page++ - const { data } = await this.getArticles({ - page, + skip, limit, highlighted: 'exclude', includeDrafts: false, @@ -183,6 +181,7 @@ export class UnbodyService { ) if (data.length === 0) break + skip += limit } return result @@ -233,7 +232,7 @@ export class UnbodyService { }, 0) getArticles = ({ - page = 1, + skip = 0, limit = 10, slug, toc = false, @@ -244,7 +243,7 @@ export class UnbodyService { highlighted = 'include', }: { slug?: string - page?: number + skip?: number limit?: number toc?: boolean filter?: GetPostsQueryVariables['filter'] @@ -273,7 +272,7 @@ export class UnbodyService { }, } : {}), - ...this.helpers.args.page(page, limit), + ...this.helpers.args.page(skip, limit), filter: { operator: 'And', operands: [ @@ -327,7 +326,6 @@ export class UnbodyService { async () => this.getArticles({ limit: 1, - page: 1, slug, toc: true, includeDrafts, @@ -339,15 +337,15 @@ export class UnbodyService { ) getHighlightedArticles = ({ - page = 1, + skip = 0, limit = 10, }: { - page?: number + skip?: number limit?: number }) => this.getArticles({ limit, - page, + skip, toc: true, highlighted: 'only', textBlocks: true, @@ -356,16 +354,16 @@ export class UnbodyService { getRelatedArticles = ({ id, - page = 1, + skip = 0, limit = 10, }: { id: string - page?: number + skip?: number limit?: number }) => this.getArticles({ limit, - page, + skip, toc: true, nearObject: id, textBlocks: true, @@ -395,7 +393,7 @@ export class UnbodyService { 'index', showSlug && showSlug, ]), - ...this.helpers.args.page(1, 50), + ...this.helpers.args.page(0, 50), imageBlocks: true, textBlocks: true, mentions: true, @@ -414,7 +412,6 @@ export class UnbodyService { episodes: populateEpisodes ? await this.getPodcastEpisodes({ showSlug: show.slug, - page: 1, limit: episodesLimit, highlighted: 'include', }).then((res) => res.data) @@ -448,8 +445,8 @@ export class UnbodyService { ) getPodcastEpisodes = ({ - page = 1, limit = 10, + skip = 0, slug, showSlug = '', toc = false, @@ -463,7 +460,7 @@ export class UnbodyService { }: { slug?: string showSlug?: string - page?: number + skip?: number limit?: number toc?: boolean filter?: GetPostsQueryVariables['filter'] @@ -493,7 +490,7 @@ export class UnbodyService { }, } : {}), - ...this.helpers.args.page(page, limit), + ...this.helpers.args.page(skip, limit), filter: { operator: 'And', operands: [ @@ -570,7 +567,7 @@ export class UnbodyService { const { data } = await this.getPodcastEpisodes({ slug, showSlug, - page: 1, + skip: 0, limit: 1, toc, textBlocks, @@ -584,19 +581,19 @@ export class UnbodyService { }, null) getHighlightedEpisodes = ({ - page = 1, + skip = 0, limit = 10, showSlug = '', textBlocks = false, }: { - page?: number + skip?: number limit?: number showSlug?: string textBlocks?: boolean }) => this.handleRequest(async () => { const { data } = await this.getPodcastEpisodes({ - page, + skip, limit, showSlug, textBlocks, @@ -611,7 +608,7 @@ export class UnbodyService { getRelatedEpisodes = ({ id, - page = 1, + skip = 0, limit = 10, showSlug = '', toc = false, @@ -620,7 +617,7 @@ export class UnbodyService { }: { id: string toc?: boolean - page?: number + skip?: number limit?: number showSlug?: string textBlocks?: boolean @@ -629,7 +626,7 @@ export class UnbodyService { }) => this.handleRequest(async () => { const { data } = await this.getPodcastEpisodes({ - page, + skip, limit, toc, showSlug, @@ -646,15 +643,15 @@ export class UnbodyService { getLatestEpisodes = async ({ showSlug, - page = 1, + skip = 0, limit = 10, }: { showSlug?: string - page?: number + skip?: number limit?: number }) => this.getPodcastEpisodes({ - page, + skip, limit, showSlug, highlighted: 'include', @@ -691,7 +688,7 @@ export class UnbodyService { const { data } = await this.client.query({ query: GetHomepagePostsDocument, variables: { - ...this.helpers.args.page(1, 10), + ...this.helpers.args.page(0, 10), filter: this.helpers.args.wherePublished(true), }, }) @@ -715,14 +712,14 @@ export class UnbodyService { this.handleRequest<{ slug: string }[]>(async () => { const result: { slug: string }[] = [] - let page = 1 + let skip = 0 while (true) { const res = await this.client.query({ query: GetAllArticleSlugsDocument, variables: { filter: UnbodyHelpers.args.wherePublished(true), - ...this.helpers.args.page(page, 50), + ...this.helpers.args.page(skip, 50), }, }) @@ -732,7 +729,7 @@ export class UnbodyService { result.push(...docs.map((doc) => ({ slug: doc!.slug! }))) - page++ + skip += 50 } return result @@ -783,7 +780,7 @@ export class UnbodyService { UnbodyHelpers.args.wherePublished(published), ], }, - ...this.helpers.args.page(1, 10), + ...this.helpers.args.page(0, 10), }, }) @@ -843,7 +840,7 @@ export class UnbodyService { } = await this.client.query({ query: SearchBlocksDocument, variables: { - ...this.helpers.args.page(1, 20), + ...this.helpers.args.page(0, 20), imageFilter: filter, textFilter: filter, ...(nearText