diff --git a/src/components/Authors/Authors.tsx b/src/components/Authors/Authors.tsx index 0ba8529..85bc251 100644 --- a/src/components/Authors/Authors.tsx +++ b/src/components/Authors/Authors.tsx @@ -8,19 +8,22 @@ export enum AuthorsDirection { ROW = 'row', } -const Authors = ({ - authors, - email, - gap = 12, - flexDirection = AuthorsDirection.ROW, -}: { +export type AuthorsProps = React.ComponentProps & { authors: LPE.Author.Document[] email: boolean gap?: number flexDirection?: AuthorsDirection +} + +const Authors: React.FC = ({ + authors, + email, + gap = 12, + flexDirection = AuthorsDirection.ROW, + ...props }) => { return authors?.length > 0 ? ( - + {authors.map((author, index) => index < authors.length - 1 ? ( diff --git a/src/components/Podcasts/PodcastShowCard.tsx b/src/components/Podcasts/PodcastShowCard.tsx index 5a4a6a7..0183477 100644 --- a/src/components/Podcasts/PodcastShowCard.tsx +++ b/src/components/Podcasts/PodcastShowCard.tsx @@ -11,13 +11,16 @@ export enum Size { LARGE = 'large', } -interface Props { +export type PodcastShowCardProps = React.ComponentProps & { show: LPE.Podcast.Show } -export default function PodcastShowCard({ show }: Props) { +export default function PodcastShowCard({ + show, + ...props +}: PodcastShowCardProps) { return ( - + {show.title} diff --git a/src/components/PostCard/PostCard.Cover.tsx b/src/components/PostCard/PostCard.Cover.tsx index f3035d7..c3d8377 100644 --- a/src/components/PostCard/PostCard.Cover.tsx +++ b/src/components/PostCard/PostCard.Cover.tsx @@ -1,25 +1,24 @@ -import React, { FC } from 'react' import { ResponsiveImage, ResponsiveImageProps, } from '@/components/ResponsiveImage/ResponsiveImage' import { LPE } from '@/types/lpe.types' import Link from 'next/link' +import { FC } from 'react' -interface Props { +export type PostCardCoverProps = React.ComponentProps & { imageProps: ResponsiveImageProps imageData: LPE.Image.Document playIcon?: boolean - link: string } -export const PostCardCover: FC = ({ - link, +export const PostCardCover: FC = ({ imageProps, imageData, playIcon, + ...props }) => { return ( - + ) diff --git a/src/components/PostCard/PostCard.Label.tsx b/src/components/PostCard/PostCard.Label.tsx index cfddeb2..f614260 100644 --- a/src/components/PostCard/PostCard.Label.tsx +++ b/src/components/PostCard/PostCard.Label.tsx @@ -4,14 +4,14 @@ import styled from '@emotion/styled' import { LPE } from '@/types/lpe.types' import PostType = LPE.PostType -interface Props { +export type Props = React.ComponentProps & { contentType: PostType date: Date | null } -export const PostCardLabel: FC = ({ contentType, date }) => { +export const PostCardLabel: FC = ({ contentType, date, ...props }) => { return ( - + {contentType.toUpperCase()} @@ -35,5 +35,4 @@ const Container = styled.div` flex-direction: row; align-items: center; gap: 8px; - margin-bottom: 8px; ` diff --git a/src/components/PostCard/PostCard.ShowDetails.tsx b/src/components/PostCard/PostCard.ShowDetails.tsx index a3e7ead..ee9b2c2 100644 --- a/src/components/PostCard/PostCard.ShowDetails.tsx +++ b/src/components/PostCard/PostCard.ShowDetails.tsx @@ -1,36 +1,45 @@ import { LPE } from '@/types/lpe.types' -import Link from 'next/link' import { Typography } from '@acid-info/lsd-react' -import { LogosCircleIcon } from '../Icons/LogosCircleIcon' +import { css } from '@emotion/react' import styled from '@emotion/styled' import Image from 'next/image' +import Link from 'next/link' -export interface PostCardShowDetailsProps { +export type PostCardShowDetailsProps = Partial< + React.ComponentProps +> & { title: string slug: string episodeNumber: number logo?: LPE.Image.Document podcast: LPE.Podcast.Show + size?: 'small' | 'medium' } // TODO -export const PostCardShowDetails = (props: PostCardShowDetailsProps) => { - const { slug, episodeNumber, podcast } = props - +export const PostCardShowDetails = ({ + slug, + episodeNumber, + podcast, + size = 'medium', + ...props +}: PostCardShowDetailsProps) => { return ( - + {podcast && ( <> - {podcast.logo.alt} {podcast.title} - {episodeNumber} EP + {size !== 'small' && ( + {episodeNumber} EP + )} )} @@ -54,3 +63,7 @@ const PodcastInfo = styled.div` const CustomLink = styled(Link)` text-decoration: none; ` + +const Logo = styled(Image)` + border-radius: 100%; +` diff --git a/src/components/PostCard/PostCard.tsx b/src/components/PostCard/PostCard.tsx index 7fcd437..22e3b65 100644 --- a/src/components/PostCard/PostCard.tsx +++ b/src/components/PostCard/PostCard.tsx @@ -10,22 +10,18 @@ import { AuthorsDirection } from '../Authors/Authors' import { ResponsiveImageProps } from '../ResponsiveImage/ResponsiveImage' -import { PostCardLabel } from './PostCard.Label' import { PostCardCover } from '@/components/PostCard/PostCard.Cover' import { PostCardShowDetails, PostCardShowDetailsProps, } from '@/components/PostCard/PostCard.ShowDetails' +import { css } from '@emotion/react' +import { PostCardLabel } from './PostCard.Label' export type PostAppearanceProps = { imageProps?: ResponsiveImageProps } -export enum PodcastType { - NETWORK_STATE = 'network-state', - HASHING_IT_OUT = 'hashing-it-out', -} - export type PostDataProps = { slug: string date: Date | null @@ -41,8 +37,8 @@ export type PostCardProps = CommonProps & React.HTMLAttributes & { appearance?: PostAppearanceProps data: PostDataProps - isFeatured?: boolean contentType: LPE.PostType + size?: 'xxsmall' | 'xsmall' | 'small' | 'medium' | 'large' } export const PostCard = (_props: PostCardProps) => { @@ -58,91 +54,252 @@ export const PostCard = (_props: PostCardProps) => { tags = [], podcastShowDetails, }, + size = 'small', contentType, - isFeatured = false, ...props } = _props const link = contentType === LPE.PostTypes.Article ? `/article/${slug}` - : `/podcasts/${slug}` + : `/podcasts/${podcastShowDetails?.slug}/${slug}` + + const coverImageElement = coverImage && ( + + ) + + const labelElement = ( + + ) + + const titleElement = ( + + + {title} + + + ) + + const subtitleElement = subtitle && ( + + {subtitle} + + ) + + const authorsElement = authors && authors.length > 0 && ( + + ) + + const showElement = podcastShowDetails && ( + + ) + + const tagsElement = tags.length > 0 && return ( - - {coverImage && ( - + + {size === 'large' ? ( + <> +
+ {labelElement} + {titleElement} + {subtitleElement} + {authorsElement} + {showElement} + {tagsElement} +
+
{coverImageElement}
+ + ) : ( + <> + {coverImageElement} + {labelElement} + {titleElement} + {subtitleElement} + {authorsElement} + {showElement} + {tagsElement} + )} - - - - - - {title} - - - - {subtitle && ( - - {subtitle} - - )} - - {authors && authors.length > 0 && ( - - )} - - {podcastShowDetails && } - {tags.length > 0 && }
) } -const Container = styled.div` +PostCard.toData = (post: LPE.Post.Document, shows: LPE.Podcast.Show[] = []) => { + const show = + post.type === 'podcast' + ? post.show || shows.find((show) => show.id === post.showId) + : undefined + + return { + date: + post.type === 'podcast' + ? post.publishedAt + ? new Date(post.publishedAt) + : null + : post.modifiedAt + ? new Date(post.modifiedAt) + : null, + slug: post.slug, + title: post.title, + authors: post.type === 'article' ? post.authors : [], + coverImage: post.coverImage, + subtitle: (post.type === 'article' && post.subtitle) || '', + tags: post.tags, + ...(post.type === 'podcast' && show + ? { + podcastShowDetails: { + episodeNumber: post.episodeNumber, + title: show.title, + slug: show.slug, + podcast: show, + }, + } + : {}), + } +} + +const Container = styled.div>` display: flex; flex-direction: column; position: 'relative'; gap: 16px; -` -const CustomTypography = styled(Typography)` - text-overflow: ellipsis; - overflow: hidden; - word-break: break-word; -` - -const PodcastAuthor = styled.div` - display: flex; - align-items: center; - gap: 12px; -` - -const TitleLink = styled(Link)` - text-decoration: none; - width: fit-content; -` - -const Subtitle = styled(CustomTypography)` - @media (min-width: 768px) { + .label { + margin-bottom: -8px; } - @media (max-width: 768px) { - font-size: 14px; - line-height: 20px; - } -` -const Title = styled(CustomTypography)` - @media (max-width: 768px) { - font-size: 28px; - line-height: 36px; + .titleLink { + text-decoration: none; + width: fit-content; } + + .title, + .subtitle { + text-overflow: ellipsis; + overflow: hidden; + word-break: break-word; + } + + .title { + @media (max-width: 768px) { + font-size: 28px; + line-height: 36px; + } + } + + .subtitle { + @media (max-width: 768px) { + font-size: 14px; + line-height: 20px; + } + } + + ${({ size }) => + size === 'xxsmall' && + css` + .label { + } + + .title { + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + max-height: calc(2 * var(--lsd-h6-lineHeight)); + } + + .subtitle { + display: none; + } + + .coverImage { + display: none; + } + + .tags { + display: none; + } + + .authors, + .showDetails { + flex-grow: 1; + display: flex; + } + `} + + ${({ size }) => + size === 'xsmall' && + css` + .label { + margin-bottom: -px; + } + + .title { + } + + .subtitle { + display: none; + } + + .coverImage { + } + + .tags { + margin-top: 8px; + } + + .authors, + .showDetails { + } + `} + + ${({ size }) => size === 'small' && css``} + + ${({ size }) => size === 'medium' && css``} + + ${({ size }) => + size === 'large' && + css` + display: grid; + grid-template-columns: repeat(2, 1fr); + + > * { + display: flex; + flex-direction: column; + position: 'relative'; + gap: 16px; + } + `} ` diff --git a/src/components/PostsGrid/PostsGrid.tsx b/src/components/PostsGrid/PostsGrid.tsx new file mode 100644 index 0000000..a0e125e --- /dev/null +++ b/src/components/PostsGrid/PostsGrid.tsx @@ -0,0 +1,112 @@ +import { css } from '@emotion/react' +import styled from '@emotion/styled' +import React, { useMemo } from 'react' +import { LPE } from '../../types/lpe.types' +import { chunkArray } from '../../utils/array.utils' +import { PostCard, PostCardProps } from '../PostCard' + +export type PostsGridProps = Partial> & { + posts?: LPE.Post.Document[] +} + +export const PostsGrid: React.FC = ({ + cols = 4, + size = 'small', + posts = [], + bordered = false, + ...props +}) => { + const groups = useMemo(() => chunkArray(posts, cols), [posts, cols]) + + return ( + + {groups.map((group, index) => ( +
+ {group.map((post) => ( +
+ +
+ ))} +
+ ))} +
+ ) +} + +const Container = styled.div<{ + cols: number + bordered: boolean + size: PostCardProps['size'] +}>` + display: grid; + gap: 16px 0; + + ${(props) => css` + > .row { + display: grid; + grid-template-columns: repeat(${props.cols}, 1fr); + gap: 0 32px; + + & > div { + padding: 24px 0; + border-top: ${props.bordered ? '1px' : '0'} solid + rgb(var(--lsd-border-primary)); + } + } + `} + + ${(props) => + props.size === 'xxsmall' && + css` + > .row { + padding: 24px 0; + gap: 0 32px; + + & > div { + border-top: 0; + padding: 0; + position: relative; + } + + & > div:not(:last-child)::after { + content: ' '; + height: 100%; + width: 1px; + background: rgb(var(--lsd-border-primary)); + position: absolute; + top: 0; + right: -16px; + display: ${props.bordered ? 'block' : 'none'}; + } + } + `} + + ${(props) => + props.size === 'xsmall' && + css` + > .row { + gap: 0 16px; + + & > div { + box-sizing: border-box; + border-top: 0; + } + + & > div:last-child { + } + & > div:not(:last-child) { + } + } + `} + + ${(props) => props.size === 'small' && css``} + + ${(props) => props.size === 'medium' && css``} + + ${(props) => props.size === 'large' && css``} +` diff --git a/src/components/PostsGrid/index.ts b/src/components/PostsGrid/index.ts new file mode 100644 index 0000000..3ddc0c9 --- /dev/null +++ b/src/components/PostsGrid/index.ts @@ -0,0 +1 @@ +export * from './PostsGrid' diff --git a/src/components/Tags/Tags.tsx b/src/components/Tags/Tags.tsx index 02061f5..0c4b281 100644 --- a/src/components/Tags/Tags.tsx +++ b/src/components/Tags/Tags.tsx @@ -3,13 +3,17 @@ import styled from '@emotion/styled' import Link from 'next/link' import { useRouter } from 'next/router' -const Tags = ({ tags, className }: { tags: string[]; className?: string }) => { +export type TagsProps = React.ComponentProps & { + tags: string[] +} + +const Tags: React.FC = ({ tags, className, ...props }) => { const router = useRouter() const { query } = router const { topics } = query return tags?.length > 0 ? ( - + {tags.map((tag, idx) => ( , + HTMLDivElement +> & { + data: { + tags: string[] + shows: LPE.Podcast.Show[] + latest: LPE.Post.Document[] + highlighted: LPE.Post.Document[] + } +} + +export const HomePage: React.FC = ({ + data, + data: { highlighted = [], shows = [], tags = [], latest = [] }, + ...props +}) => { + const query = useRecentPosts({ initialData: latest, limit: 10 }) + + const [group1, group2] = useMemo( + () => [[query.posts.slice(0, 5)], chunkArray(query.posts.slice(5), 4, 2)], + [query.posts], + ) + + return ( + + + + {group2.map((group, index) => ( + + ))} + {query.hasNextPage && ( +
+ +
+ )} + + +
+ ) +} + +const Root = styled('div')` + width: 100%; + display: flex; + flex-direction: column; + + .load-more { + width: 100%; + text-align: center; + + button { + width: 340px; + } + } + + .podcasts { + margin-top: 40px; + } +` diff --git a/src/containers/HomePage/index.ts b/src/containers/HomePage/index.ts new file mode 100644 index 0000000..1fe31eb --- /dev/null +++ b/src/containers/HomePage/index.ts @@ -0,0 +1 @@ +export * from './HomePage' diff --git a/src/containers/PodcastShowsPreview/PodcastShowsPreview.tsx b/src/containers/PodcastShowsPreview/PodcastShowsPreview.tsx new file mode 100644 index 0000000..84cd1fc --- /dev/null +++ b/src/containers/PodcastShowsPreview/PodcastShowsPreview.tsx @@ -0,0 +1,175 @@ +import { ArrowDownIcon, Button, Typography } from '@acid-info/lsd-react' +import styled from '@emotion/styled' +import clsx from 'clsx' +import Image from 'next/image' +import Link from 'next/link' +import React from 'react' +import { PostsGrid } from '../../components/PostsGrid' +import { LPE } from '../../types/lpe.types' + +export type PodcastShowsPreviewProps = React.DetailedHTMLProps< + React.HTMLAttributes, + HTMLDivElement +> & { + data: { + shows: LPE.Podcast.Show[] + } +} + +export const PodcastShowsPreview: React.FC = ({ + data, + data: { shows = [] }, + ...props +}) => { + return ( + +
+ Podcasts + + + +
+
1 && 'podcasts__shows--bordered', + )} + > + {shows.slice(0, 2).map((show) => ( +
+
+ {show.title} + + {show.title} + + {show.description && ( + + )} + + + + + + + {show.hosts.length > 0 && ( + Hosted by: {show.hosts[0].name} + )} + {show.numberOfEpisodes} EP + +
+ +
+ + +
+
+ ))} +
+
+ ) +} + +const Root = styled('div')` + & .podcasts { + &__header { + padding: 16px 0; + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; + border-top: 1px solid rgb(var(--lsd-border-primary)); + border-bottom: 1px solid rgb(var(--lsd-border-primary)); + } + + &__shows { + display: grid; + grid-template-columns: repeat(2, 1fr); + padding-top: 24px; + + & > div:first-child { + border-right: 1px solid rgb(var(--lsd-border-primary)); + padding-right: 16px; + } + + &--bordered { + & > div:last-child { + padding-left: 16px; + } + } + } + + &__show-card { + margin-top: 24px; + align-items: flex-start; + } + + &__show-logo { + width: 56px; + height: 56px; + border-radius: 100%; + } + + &__show-title { + margin-top: 16px; + } + + &__show-description { + margin-top: 8px; + } + + &__show-link { + display: block; + margin-top: 24px; + text-decoration: none; + } + + &__show-hosts { + display: block; + margin-top: 200px; + + span:not(:last-child) { + &:after { + content: '•'; + margin: 0 8px; + text-decoration: none; + display: inline-block; + } + } + } + + &__show-episodes { + > div:not(:last-child) { + border-bottom: 1px solid rgb(var(--lsd-border-primary)); + } + } + } +` diff --git a/src/containers/PodcastShowsPreview/index.ts b/src/containers/PodcastShowsPreview/index.ts new file mode 100644 index 0000000..d514bb4 --- /dev/null +++ b/src/containers/PodcastShowsPreview/index.ts @@ -0,0 +1 @@ +export * from './PodcastShowsPreview' diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 92528a1..253f748 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -4,6 +4,7 @@ import { uiConfigs } from '@/configs/ui.configs' import { SearchBarProvider } from '@/context/searchbar.context' import { DefaultLayout } from '@/layouts/DefaultLayout' import { css, Global } from '@emotion/react' +import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { NextComponentType, NextPageContext } from 'next' import type { AppProps } from 'next/app' import Head from 'next/head' @@ -23,6 +24,8 @@ type AppLayoutProps

= AppProps & { Component: NextLayoutComponentType } +const queryClient = new QueryClient() + export default function App({ Component, pageProps }: AppLayoutProps) { const hydrated = useHydrated() @@ -94,11 +97,13 @@ export default function App({ Component, pageProps }: AppLayoutProps) { } `} /> - - - {getLayout()} - - {hydrated && } + + + + {getLayout()} + + {hydrated && } + ) } diff --git a/src/pages/api/posts/index.ts b/src/pages/api/posts/index.ts index b721cbe..15d38dd 100644 --- a/src/pages/api/posts/index.ts +++ b/src/pages/api/posts/index.ts @@ -7,11 +7,11 @@ export default async function handler( res: NextApiResponse, ) { const { - query: { page = 1, limit = 10 }, + query: { skip = 0, limit = 10 }, } = req const response = await unbodyApi.getRecentPosts({ - page: parseInt(page, 1), + skip: parseInt(skip, 0), limit: parseInt(limit, 10), }) diff --git a/src/pages/index.tsx b/src/pages/index.tsx index b89b648..5b22c6b 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -1,34 +1,11 @@ -import { FeaturedPost } from '@/components/FeaturedPost' -import { PostsList } from '@/components/PostList/PostList' -import { Section } from '@/components/Section/Section' -import { useSearchBarContext } from '@/context/searchbar.context' -import { PostListLayout } from '@/types/ui.types' -import { useEffect } from 'react' +import { GetStaticProps, NextPage } from 'next' import SEO from '../components/SEO/SEO' -import { api } from '../services/api.service' +import { HomePage, HomePageProps } from '../containers/HomePage' import unbodyApi from '../services/unbody/unbody.service' -import { LPE } from '../types/lpe.types' -type Props = { - posts: LPE.Article.Data[] - featured: LPE.Article.Data - error: string | null - tags: string[] -} - -export default function Home({ posts, featured, tags }: Props) { - const { setTags } = useSearchBarContext() - - useEffect(() => { - setTags(tags) - }, [setTags, tags]) - - useEffect(() => { - api - .getLatestEpisodes({ showSlug: 'hashing-it-out', page: 1, limit: 1 }) - .then((res) => console.log(res)) - }, []) +type PageProps = Pick +const Page: NextPage = (props) => { return ( <> - {featured && ( -

- -
- )} -
- -
+ ) } -export const getStaticProps = async () => { - const { - data: { posts, highlighted }, - errors, - } = await unbodyApi.getHomepagePosts() +export const getStaticProps: GetStaticProps = async () => { + const { data: tags = [] } = await unbodyApi.getTopics(true) + const { data: highlighted } = await unbodyApi.getHighlightedPosts() + const { data: latest = [] } = await unbodyApi.getRecentPosts({ + skip: 0, + limit: 15, + }) - const { data: topics, errors: topicErrors } = await unbodyApi.getTopics() + const { data: _shows = [] } = await unbodyApi.getPodcastShows({ + populateEpisodes: true, + episodesLimit: 10, + }) + + const shows = [..._shows].sort((a, b) => (a.title > b.title ? -1 : 1)) return { props: { - posts, - errors, - featured: highlighted, - tags: topics || [], + data: { + tags, + shows, + latest, + highlighted, + }, }, } } + +export default Page diff --git a/src/pages/podcasts/index.tsx b/src/pages/podcasts/index.tsx index 0979b6d..c3fff57 100644 --- a/src/pages/podcasts/index.tsx +++ b/src/pages/podcasts/index.tsx @@ -1,12 +1,10 @@ import { SEO } from '@/components/SEO' +import PodcastsContainer from '@/containers/PodcastsContainer' +import PodcastsLayout from '@/layouts/PodcastsLayout/Podcasts.layout' +import unbodyApi from '@/services/unbody/unbody.service' import { GetStaticPropsContext } from 'next' import { ReactNode } from 'react' import { LPE } from '../../types/lpe.types' -import PodcastsLayout from '@/layouts/PodcastsLayout/Podcasts.layout' -import PodcastsContainer from '@/containers/PodcastsContainer' - -import TEMP_DATA from './podcasts-temp-data.json' -import unbodyApi from '@/services/unbody/unbody.service' type PodcastsProps = { shows: LPE.Podcast.Show[] diff --git a/src/queries/useRecentPosts.query.ts b/src/queries/useRecentPosts.query.ts new file mode 100644 index 0000000..2584be3 --- /dev/null +++ b/src/queries/useRecentPosts.query.ts @@ -0,0 +1,54 @@ +import { useInfiniteQuery } from '@tanstack/react-query' +import { useMemo } from 'react' +import { api } from '../services/api.service' +import { LPE } from '../types/lpe.types' + +export const useRecentPosts = ({ + initialData = [], + limit = 10, +}: { + initialData?: LPE.Post.Document[] + limit?: number +}) => { + const query = useInfiniteQuery( + ['latest-posts', 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.getRecentPosts({ skip, limit: _limit }).then((res) => ({ + page: pageParam, + posts: res.data, + hasMore: res.data.length === _limit, + })) + }, + { + 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/services/api.service.ts b/src/services/api.service.ts index 8eb3e5c..5febcb6 100644 --- a/src/services/api.service.ts +++ b/src/services/api.service.ts @@ -3,13 +3,13 @@ import { LPE } from '../types/lpe.types' export class ApiService { getRecentPosts = async ({ - page = 1, + skip = 0, limit = 10, }: { - page?: number + skip?: number limit?: number }): Promise> => - fetch(`/api/posts?page=${page}&limit=${limit}`) + fetch(`/api/posts?skip=${skip}&limit=${limit}`) .then((res) => res.json()) .catch((e) => { console.error(e) diff --git a/src/services/unbody/dataTypes/PodcastShowDocument.dataType.ts b/src/services/unbody/dataTypes/PodcastShowDocument.dataType.ts index 3e64cdb..ef2ebe4 100644 --- a/src/services/unbody/dataTypes/PodcastShowDocument.dataType.ts +++ b/src/services/unbody/dataTypes/PodcastShowDocument.dataType.ts @@ -25,7 +25,8 @@ export const PodcastShowDataType: UnbodyDataTypeConfig< if (!original) return data as any const description = data.content.find( - (block) => block.labels.length === 0 && block.type === 'text', + (block) => + block.labels.length === 0 && block.type === 'text' && block.order > 2, ) const image = data.content.find( diff --git a/src/services/unbody/unbody.service.ts b/src/services/unbody/unbody.service.ts index 58ac27b..25d24fd 100644 --- a/src/services/unbody/unbody.service.ts +++ b/src/services/unbody/unbody.service.ts @@ -59,7 +59,22 @@ export class UnbodyService { } = { posts: [] } constructor(private apiKey: string, private projectId: string) { - const cache = new InMemoryCache({}) + const cache = new InMemoryCache({ + typePolicies: { + Query: { + fields: { + Get: { + merge(existing = {}, incoming) { + return { + ...existing, + ...incoming, + } + }, + }, + }, + }, + }, + }) this.client = new ApolloClient({ uri: 'https://graphql.unbody.io', @@ -382,7 +397,7 @@ export class UnbodyService { ]), ...this.helpers.args.page(1, 50), imageBlocks: true, - textBlocks: false, + textBlocks: true, mentions: true, }, }) @@ -647,17 +662,16 @@ export class UnbodyService { }) getRecentPosts = async ({ - page = 1, limit = 10, + skip = 0, }: { - page?: number limit?: number + skip?: number }) => this.handleRequest(async () => { const { posts } = await this.loadInitialData() - const startIndex = (page - 1) * limit - return posts.slice(startIndex, startIndex + limit) + return posts.slice(skip, skip + limit) }, []) getHighlightedPosts = async () => diff --git a/src/utils/array.utils.ts b/src/utils/array.utils.ts new file mode 100644 index 0000000..1491f6c --- /dev/null +++ b/src/utils/array.utils.ts @@ -0,0 +1,18 @@ +export const chunkArray = (arr: T[], ...pattern: number[]): T[][] => { + const result: T[][] = [] + + let index = 0 + let iteration = 0 + + while (index < arr.length) { + const take = pattern[iteration % pattern.length] + const elements = arr.slice(index, index + take) + + result.push(elements) + + iteration++ + index += elements.length + } + + return result +}