mirror of
https://github.com/acid-info/logos-press-engine.git
synced 2025-02-23 06:38:27 +00:00
feat: implement API endpoints for podcasts page
This commit is contained in:
parent
c635c16291
commit
c80c3c8eec
@ -1,19 +1,18 @@
|
||||
import styled from '@emotion/styled'
|
||||
import { LPE } from '../../types/lpe.types'
|
||||
import { PodcastType, PostCard } from '@/components/PostCard/PostCard'
|
||||
import { PostCard } from '@/components/PostCard/PostCard'
|
||||
|
||||
interface Props {
|
||||
header?: React.ReactNode
|
||||
episodes: LPE.Podcast.Document[]
|
||||
podcast: PodcastType
|
||||
show?: LPE.Podcast.Show
|
||||
}
|
||||
|
||||
export default function EpisodesList({ header, episodes, podcast }: Props) {
|
||||
export default function EpisodesList({ header, episodes, show }: Props) {
|
||||
return (
|
||||
<EpisodeListContainer>
|
||||
{header}
|
||||
<EpisodesContainer>
|
||||
{/* Featured */}
|
||||
{episodes.slice(0, 2).map((episode) => (
|
||||
<PostCard
|
||||
key={episode.id}
|
||||
@ -21,48 +20,22 @@ export default function EpisodesList({ header, episodes, podcast }: Props) {
|
||||
data={{
|
||||
authors: episode.authors,
|
||||
date: episode.publishedAt ? new Date(episode.publishedAt) : null,
|
||||
slug: `${podcast}/${episode.slug}`,
|
||||
slug: episode.show?.slug
|
||||
? `${episode.show?.slug}/${episode.slug}`
|
||||
: `${show?.slug}/${episode.slug}`,
|
||||
title: episode.title,
|
||||
coverImage: episode.coverImage,
|
||||
tags: episode.tags,
|
||||
podcastShowDetails: {
|
||||
title: episode.title,
|
||||
slug: `${podcast}/${episode.slug}`,
|
||||
slug: `${episode.show?.slug}`,
|
||||
episodeNumber: episode.episodeNumber,
|
||||
podcast: podcast,
|
||||
podcast: episode.show as LPE.Podcast.Show,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</EpisodesContainer>
|
||||
{episodes.length > 2 && (
|
||||
<EpisodesContainer>
|
||||
{episodes.slice(2, 6).map((episode) => (
|
||||
<PostContainer key={episode.id}>
|
||||
<PostCard
|
||||
key={episode.id}
|
||||
contentType={LPE.PostTypes.Podcast}
|
||||
data={{
|
||||
authors: episode.authors,
|
||||
date: episode.publishedAt
|
||||
? new Date(episode.publishedAt)
|
||||
: null,
|
||||
slug: `${podcast}/${episode.slug}`,
|
||||
title: episode.title,
|
||||
coverImage: episode.coverImage,
|
||||
tags: episode.tags,
|
||||
podcastShowDetails: {
|
||||
title: episode.title,
|
||||
slug: `${podcast}/${episode.slug}`,
|
||||
episodeNumber: episode.episodeNumber,
|
||||
podcast: podcast,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</PostContainer>
|
||||
))}
|
||||
</EpisodesContainer>
|
||||
)}
|
||||
</EpisodeListContainer>
|
||||
)
|
||||
}
|
||||
@ -79,8 +52,3 @@ const EpisodesContainer = styled.div`
|
||||
gap: 16px;
|
||||
padding-top: 24px;
|
||||
`
|
||||
|
||||
const PostContainer = styled.div`
|
||||
border-top: 1px solid rgb(var(--lsd-border-primary));
|
||||
padding-top: 24px;
|
||||
`
|
||||
|
42
src/components/Podcasts/Podcast.Host.tsx
Normal file
42
src/components/Podcasts/Podcast.Host.tsx
Normal file
@ -0,0 +1,42 @@
|
||||
import { Typography } from '@acid-info/lsd-react'
|
||||
import styled from '@emotion/styled'
|
||||
import React from 'react'
|
||||
import { LPE } from '../../types/lpe.types'
|
||||
|
||||
interface Props {
|
||||
show: LPE.Podcast.Show
|
||||
}
|
||||
|
||||
export default function PodcastHost({ show }: Props) {
|
||||
return (
|
||||
<HostedBy>
|
||||
<Typography variant="body2">
|
||||
Hosted by:
|
||||
{show?.hosts?.map((host) => (
|
||||
<Host key={host.name} variant="body2">
|
||||
{host.name}
|
||||
</Host>
|
||||
))}
|
||||
</Typography>
|
||||
</HostedBy>
|
||||
)
|
||||
}
|
||||
|
||||
const HostedBy = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
`
|
||||
|
||||
const Host = styled(Typography)`
|
||||
margin-left: 8px;
|
||||
&:not(:last-child) {
|
||||
&:after {
|
||||
content: '•';
|
||||
margin-left: 8px;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
`
|
@ -1,12 +1,10 @@
|
||||
import { Tags } from '@/components/Tags'
|
||||
import { Typography } from '@acid-info/lsd-react'
|
||||
import styled from '@emotion/styled'
|
||||
import Link from 'next/link'
|
||||
import React, { useMemo } from 'react'
|
||||
import React from 'react'
|
||||
import { LPE } from '../../types/lpe.types'
|
||||
|
||||
import { ResponsiveImage } from '../ResponsiveImage/ResponsiveImage'
|
||||
import { LogosCircleIcon } from '../Icons/LogosCircleIcon'
|
||||
import PodcastHost from './Podcast.Host'
|
||||
|
||||
export enum Size {
|
||||
SMALL = 'small',
|
||||
@ -23,16 +21,7 @@ export default function PodcastShowCard({ show }: Props) {
|
||||
<LogosCircleIcon width={73} height={73} />
|
||||
<ShowData>
|
||||
<Typography variant="h3">{show.title}</Typography>
|
||||
<HostedBy>
|
||||
<Typography variant="body2">
|
||||
Hosted by:
|
||||
{show.hosts.map((host) => (
|
||||
<Host key={host.name} variant="body2">
|
||||
{host.name}
|
||||
</Host>
|
||||
))}
|
||||
</Typography>
|
||||
</HostedBy>
|
||||
<PodcastHost show={show} />
|
||||
<Description variant="body2">{show.description}</Description>
|
||||
</ShowData>
|
||||
</Container>
|
||||
@ -53,26 +42,6 @@ const ShowData = styled.div`
|
||||
align-items: center;
|
||||
`
|
||||
|
||||
const HostedBy = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-top: 16px;
|
||||
`
|
||||
|
||||
const Host = styled(Typography)`
|
||||
margin-left: 8px;
|
||||
&:not(:last-child) {
|
||||
&:after {
|
||||
content: '•';
|
||||
margin-left: 8px;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const Description = styled(Typography)`
|
||||
margin-top: 16px;
|
||||
`
|
||||
|
@ -2,29 +2,28 @@ import styled from '@emotion/styled'
|
||||
import { LPE } from '../../types/lpe.types'
|
||||
import { Button, Typography } from '@acid-info/lsd-react'
|
||||
import Link from 'next/link'
|
||||
import { LogosCircleIcon } from '../Icons/LogosCircleIcon'
|
||||
import { HashingItOutIcon } from '../Icons/HashingItOutIcon'
|
||||
import PodcastHost from './Podcast.Host'
|
||||
import Image from 'next/image'
|
||||
|
||||
interface Props {
|
||||
shows: LPE.Podcast.Show[]
|
||||
}
|
||||
|
||||
export default function PodcastsList({ shows }: Props) {
|
||||
export default function PodcastsLists({ shows }: Props) {
|
||||
return (
|
||||
<PodcastsContainer>
|
||||
<PodcastListsContainer>
|
||||
{shows &&
|
||||
shows.map((show) => (
|
||||
<PodcastCard key={show.id}>
|
||||
{show.slug === 'network-state' ? (
|
||||
<LogosCircleIcon width={56} height={56} />
|
||||
) : (
|
||||
<HashingItOutIcon width={56} height={56} />
|
||||
)}
|
||||
<Image
|
||||
src={show.logo.url}
|
||||
width={56}
|
||||
height={56}
|
||||
alt={show.logo.alt}
|
||||
/>
|
||||
<Typography variant="h3">{show.title}</Typography>
|
||||
<Row>
|
||||
<Typography variant="body2">
|
||||
Hosted by: {show.hosts[0].name}
|
||||
</Typography>
|
||||
<PodcastHost show={show} />
|
||||
<Typography variant="body2">•</Typography>
|
||||
<Typography variant="body2">
|
||||
{show.numberOfEpisodes} EP
|
||||
@ -36,11 +35,11 @@ export default function PodcastsList({ shows }: Props) {
|
||||
</Link>
|
||||
</PodcastCard>
|
||||
))}
|
||||
</PodcastsContainer>
|
||||
</PodcastListsContainer>
|
||||
)
|
||||
}
|
||||
|
||||
const PodcastsContainer = styled.article`
|
||||
const PodcastListsContainer = styled.div`
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
`
|
@ -1,37 +1,39 @@
|
||||
import { LPE } from '@/types/lpe.types'
|
||||
import Link from 'next/link'
|
||||
import { PodcastType } from './PostCard'
|
||||
import { Typography } from '@acid-info/lsd-react'
|
||||
import { LogosCircleIcon } from '../Icons/LogosCircleIcon'
|
||||
import styled from '@emotion/styled'
|
||||
import Image from 'next/image'
|
||||
|
||||
export interface PostCardShowDetailsProps {
|
||||
title: string
|
||||
slug: string
|
||||
episodeNumber: number
|
||||
logo?: LPE.Image.Document
|
||||
podcast: PodcastType
|
||||
podcast: LPE.Podcast.Show
|
||||
}
|
||||
|
||||
// TODO
|
||||
export const PostCardShowDetails = (props: PostCardShowDetailsProps) => {
|
||||
const { slug, episodeNumber, podcast } = props
|
||||
const isNetWokrState = podcast === PodcastType.NETWORK_STATE
|
||||
|
||||
return (
|
||||
<CustomLink href={`/podcasts/${slug}`}>
|
||||
<Container>
|
||||
{isNetWokrState ? (
|
||||
<LogosCircleIcon width={38} height={38} />
|
||||
) : (
|
||||
<LogosCircleIcon width={38} height={38} />
|
||||
{podcast && (
|
||||
<>
|
||||
<Image
|
||||
src={podcast?.logo?.url}
|
||||
width={38}
|
||||
height={38}
|
||||
alt={podcast.logo.alt}
|
||||
/>
|
||||
<PodcastInfo>
|
||||
<Typography variant="body2">{podcast.title}</Typography>
|
||||
<Typography variant="body3">{episodeNumber} EP</Typography>
|
||||
</PodcastInfo>
|
||||
</>
|
||||
)}
|
||||
<PodcastInfo>
|
||||
<Typography variant="body2">
|
||||
{isNetWokrState ? 'State of Network' : 'Hashing It Out'}
|
||||
</Typography>
|
||||
<Typography variant="body3">{episodeNumber} EP</Typography>
|
||||
</PodcastInfo>
|
||||
</Container>
|
||||
</CustomLink>
|
||||
)
|
||||
|
@ -21,7 +21,6 @@ const PodcastShowContainer = (props: Props) => {
|
||||
<EpisodesList
|
||||
header={<Typography variant="body2">Latest Episodes</Typography>}
|
||||
episodes={latestEpisodes}
|
||||
podcast={PodcastType.NETWORK_STATE}
|
||||
/>
|
||||
</PodcastsBodyContainer>
|
||||
</PodcastsGrid>
|
||||
|
@ -1,91 +1,56 @@
|
||||
import { Grid, GridItem } from '@/components/Grid/Grid'
|
||||
import styled from '@emotion/styled'
|
||||
import { LPE } from '../types/lpe.types'
|
||||
import PodcastsList from '@/components/Podcasts/Podcasts.List'
|
||||
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 { HashingItOutIcon } from '@/components/Icons/HashingItOutIcon'
|
||||
import { PodcastType } from '@/components/PostCard/PostCard'
|
||||
|
||||
interface Props {
|
||||
shows: LPE.Podcast.Show[]
|
||||
latestEpisodes: LPE.Podcast.Document[]
|
||||
highlightedEpisodes: LPE.Podcast.Document[]
|
||||
}
|
||||
|
||||
const PodcastsContainer = (props: Props) => {
|
||||
const { shows, latestEpisodes } = props
|
||||
const podcast = 'network-state' // TODO : get from API
|
||||
|
||||
const networkState =
|
||||
shows.find((show) => show.slug === PodcastType.NETWORK_STATE) ?? shows[0]
|
||||
|
||||
const hashingItOut =
|
||||
shows.find((show) => show.slug === PodcastType.HASHING_IT_OUT) ?? shows[1]
|
||||
const { shows, highlightedEpisodes } = props
|
||||
|
||||
return (
|
||||
<PodcastsGrid>
|
||||
<PodcastsBodyContainer className={'w-16'}>
|
||||
<PodcastsList shows={shows} />
|
||||
<PodcastsLists shows={shows} />
|
||||
|
||||
<Section>
|
||||
<EpisodesList
|
||||
header={<EpisodeListHeader>Latest Episodes</EpisodeListHeader>}
|
||||
episodes={latestEpisodes}
|
||||
podcast={podcast as PodcastType}
|
||||
episodes={highlightedEpisodes}
|
||||
/>
|
||||
</Section>
|
||||
|
||||
<Section>
|
||||
<EpisodesList
|
||||
header={
|
||||
<EpisodeListHeader>
|
||||
<Show>
|
||||
<LogosCircleIcon width={38} height={38} />
|
||||
<PodcastInfo>
|
||||
<Typography variant="body1">
|
||||
{networkState.title}
|
||||
</Typography>
|
||||
<Typography variant="body3">
|
||||
{networkState.numberOfEpisodes} EP
|
||||
</Typography>
|
||||
</PodcastInfo>
|
||||
</Show>
|
||||
<Link href={`/podcasts/${networkState.slug}`}>
|
||||
<Button size="small">See all episodes</Button>
|
||||
</Link>
|
||||
</EpisodeListHeader>
|
||||
}
|
||||
episodes={latestEpisodes.slice(0, 4)}
|
||||
podcast={PodcastType.NETWORK_STATE}
|
||||
/>
|
||||
</Section>
|
||||
|
||||
<Section>
|
||||
<EpisodesList
|
||||
header={
|
||||
<EpisodeListHeader>
|
||||
<Show>
|
||||
<HashingItOutIcon width={38} height={38} />
|
||||
<PodcastInfo>
|
||||
<Typography variant="body1">
|
||||
{hashingItOut.title}
|
||||
</Typography>
|
||||
<Typography variant="body3">
|
||||
{hashingItOut.numberOfEpisodes} EP
|
||||
</Typography>
|
||||
</PodcastInfo>
|
||||
</Show>
|
||||
<Link href={`/podcasts/${hashingItOut.slug}`}>
|
||||
<Button size="small">See all episodes</Button>
|
||||
</Link>
|
||||
</EpisodeListHeader>
|
||||
}
|
||||
episodes={latestEpisodes.slice(0, 4)}
|
||||
podcast={PodcastType.HASHING_IT_OUT}
|
||||
/>
|
||||
</Section>
|
||||
{shows.map((show) => (
|
||||
<Section key={show.id}>
|
||||
<EpisodesList
|
||||
header={
|
||||
<EpisodeListHeader>
|
||||
<Show>
|
||||
<LogosCircleIcon width={38} height={38} />
|
||||
<PodcastInfo>
|
||||
<Typography variant="body1">{show.title}</Typography>
|
||||
<Typography variant="body3">
|
||||
{show.numberOfEpisodes} EP
|
||||
</Typography>
|
||||
</PodcastInfo>
|
||||
</Show>
|
||||
<Link href={`/podcasts/${show.slug}`}>
|
||||
<Button size="small">See all episodes</Button>
|
||||
</Link>
|
||||
</EpisodeListHeader>
|
||||
}
|
||||
episodes={show.episodes as LPE.Podcast.Document[]}
|
||||
show={show}
|
||||
/>
|
||||
</Section>
|
||||
))}
|
||||
</PodcastsBodyContainer>
|
||||
</PodcastsGrid>
|
||||
)
|
||||
|
@ -6,14 +6,19 @@ 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[]
|
||||
latestEpisodes: LPE.Podcast.Document[]
|
||||
highlightedEpisodes: LPE.Podcast.Document[]
|
||||
errors: string | null
|
||||
}
|
||||
|
||||
const PodcastShowPage = ({ shows, latestEpisodes, errors }: PodcastsProps) => {
|
||||
const PodcastShowPage = ({
|
||||
shows,
|
||||
highlightedEpisodes,
|
||||
errors,
|
||||
}: PodcastsProps) => {
|
||||
if (!shows) return null
|
||||
if (errors) return <div>{errors}</div>
|
||||
|
||||
@ -26,25 +31,43 @@ const PodcastShowPage = ({ shows, latestEpisodes, errors }: PodcastsProps) => {
|
||||
pagePath={`/podcasts`}
|
||||
tags={[]}
|
||||
/>
|
||||
<PodcastsContainer shows={shows} latestEpisodes={latestEpisodes} />
|
||||
<PodcastsContainer
|
||||
shows={shows}
|
||||
highlightedEpisodes={highlightedEpisodes}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export const getStaticProps = async ({ params }: GetStaticPropsContext) => {
|
||||
const { shows, latestEpisodes } = TEMP_DATA
|
||||
// TODO : error handling
|
||||
const { data: podcastShowsData, errors: podcastShowsErrors } =
|
||||
await unbodyApi.getPodcastShows({ populateEpisodes: true })
|
||||
|
||||
if (!shows) {
|
||||
// TODO : error handling
|
||||
const { data: highlightedEpisodesData, errors: highlightedEpisodesErrors } =
|
||||
await unbodyApi.getHighlightedEpisodes({})
|
||||
|
||||
if (!podcastShowsData) {
|
||||
return {
|
||||
notFound: true,
|
||||
props: { why: 'no article' },
|
||||
props: { why: 'no podcasts' },
|
||||
}
|
||||
}
|
||||
|
||||
const podcastShows = JSON.parse(
|
||||
JSON.stringify(podcastShowsData).replace(/null/g, '""'),
|
||||
)
|
||||
|
||||
const highlightedEpisodes = JSON.parse(
|
||||
JSON.stringify(highlightedEpisodesData).replace(/null/g, '""'),
|
||||
)
|
||||
|
||||
return {
|
||||
props: {
|
||||
shows,
|
||||
latestEpisodes,
|
||||
shows: podcastShows,
|
||||
highlightedEpisodes,
|
||||
// errors,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user