feat: add components for episode
This commit is contained in:
parent
8ccd877d66
commit
297c49e06a
|
@ -1,20 +1,36 @@
|
|||
import { useState } from 'react'
|
||||
import { LPE } from '../../types/lpe.types'
|
||||
import { RenderEpisodeBlock } from './Episode.Block'
|
||||
import styled from '@emotion/styled'
|
||||
import { Button } from '@acid-info/lsd-react'
|
||||
|
||||
type Props = {
|
||||
data: LPE.Podcast.Document
|
||||
}
|
||||
|
||||
const EpisodeBlocks = ({ data }: Props) => {
|
||||
const [showMore, setShowMore] = useState(false)
|
||||
const blocks = data?.transcription
|
||||
|
||||
return blocks?.length ? (
|
||||
return blocks?.length && showMore ? (
|
||||
<>
|
||||
{blocks.map((block, idx) => (
|
||||
<RenderEpisodeBlock key={'block-' + idx} block={block} />
|
||||
))}
|
||||
<ShowButton onClick={() => setShowMore(false)}>Show Less</ShowButton>
|
||||
</>
|
||||
) : blocks?.length && !showMore ? (
|
||||
<>
|
||||
{blocks.slice(0, 3).map((block, idx) => (
|
||||
<RenderEpisodeBlock key={'block-' + idx} block={block} />
|
||||
))}
|
||||
<ShowButton onClick={() => setShowMore(true)}>Show More</ShowButton>
|
||||
</>
|
||||
) : null
|
||||
}
|
||||
|
||||
const ShowButton = styled(Button)`
|
||||
margin-top: 24px;
|
||||
`
|
||||
|
||||
export default EpisodeBlocks
|
||||
|
|
|
@ -9,9 +9,13 @@ interface Props {
|
|||
}
|
||||
|
||||
export default function EpisodeBody({ data }: Props) {
|
||||
const youtube = data?.channels.find(
|
||||
(channel) => channel?.name === LPE.Podcast.ChannelNames.Youtube,
|
||||
)
|
||||
|
||||
return (
|
||||
<EpisodeContainer>
|
||||
<EpisodeHeader {...data} url={data.channels[0].url} />
|
||||
<EpisodeHeader {...data} url={youtube?.url as string} />
|
||||
<EpisodeTranscript data={data} />
|
||||
<EpisodeFooter data={data} />
|
||||
</EpisodeContainer>
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
import styled from '@emotion/styled'
|
||||
|
||||
type Props = {
|
||||
top?: number
|
||||
bottom?: number
|
||||
}
|
||||
|
||||
const EpisodeDivider = ({ top = 32, bottom = 32 }: Props) => {
|
||||
return <Divder top={top} bottom={bottom} />
|
||||
}
|
||||
|
||||
const Divder = styled.hr<{ top: number; bottom: number }>`
|
||||
margin-top: ${({ top }) => top}px;
|
||||
margin-bottom: ${({ bottom }) => bottom}px;
|
||||
height: 1px;
|
||||
width: 100%;
|
||||
color: rgb(var(--lsd-border-primary));
|
||||
`
|
||||
|
||||
export default EpisodeDivider
|
|
@ -1,11 +1,14 @@
|
|||
import styled from '@emotion/styled'
|
||||
import { LPE } from '../../types/lpe.types'
|
||||
import EpisodeBlocks from './Episode.Blocks'
|
||||
import { Typography } from '@acid-info/lsd-react'
|
||||
import EpisodeDivider from './Episode.Divider'
|
||||
|
||||
const EpisodeTranscript = ({ data }: { data: LPE.Podcast.Document }) => {
|
||||
return (
|
||||
<>
|
||||
<Typography component="h2" variant="h2">
|
||||
<EpisodeDivider />
|
||||
<Typography component="h6" variant="h6">
|
||||
Transcript
|
||||
</Typography>
|
||||
<EpisodeBlocks data={data} />
|
||||
|
|
|
@ -1,17 +1,50 @@
|
|||
import styled from '@emotion/styled'
|
||||
import { LPE } from '../../../types/lpe.types'
|
||||
import EpisodeCredits from './Episode.Credits'
|
||||
import EpisodeMoreEpisodes from './Episode.MoreEpisodes'
|
||||
|
||||
const TEMP_MORE_EPISODES = [
|
||||
{
|
||||
id: 1,
|
||||
thumbnail:
|
||||
'https://images.cdn.unbody.io/00f8908f-9dff-456e-9640-13defd9ae433/image/a04e5542-d027-44d5-b914-bd4cadf17d25_image1.png',
|
||||
publishedAt: '2023-07-11T20:30:00.000Z',
|
||||
title: 'Title 1',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
thumbnail:
|
||||
'https://images.cdn.unbody.io/00f8908f-9dff-456e-9640-13defd9ae433/image/a04e5542-d027-44d5-b914-bd4cadf17d25_image1.png',
|
||||
publishedAt: '2023-07-12T20:30:00.000Z',
|
||||
title: 'Title 2',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
thumbnail:
|
||||
'https://images.cdn.unbody.io/00f8908f-9dff-456e-9640-13defd9ae433/image/a04e5542-d027-44d5-b914-bd4cadf17d25_image1.png',
|
||||
publishedAt: '2023-07-13T20:30:00.000Z',
|
||||
title: 'Title 3',
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
thumbnail:
|
||||
'https://images.cdn.unbody.io/00f8908f-9dff-456e-9640-13defd9ae433/image/a04e5542-d027-44d5-b914-bd4cadf17d25_image1.png',
|
||||
publishedAt: '2023-07-14T20:30:00.000Z',
|
||||
title: 'Title 4',
|
||||
},
|
||||
]
|
||||
|
||||
const EpisodeFooter = ({ data }: { data: LPE.Podcast.Document }) => {
|
||||
return (
|
||||
<EpisodeFooterContainer>
|
||||
<EpisodeCredits credits={data.credits} />
|
||||
<EpisodeMoreEpisodes episodes={TEMP_MORE_EPISODES} />
|
||||
</EpisodeFooterContainer>
|
||||
)
|
||||
}
|
||||
|
||||
const EpisodeFooterContainer = styled.div`
|
||||
margin-top: 16px;
|
||||
margin-top: 56px;
|
||||
|
||||
& > div:not(:first-child) > div > button,
|
||||
& > div:not(:first-child) > div {
|
||||
|
|
|
@ -1,5 +1,39 @@
|
|||
const EpisodeMoreEpisode = () => {
|
||||
return null
|
||||
import { Typography } from '@acid-info/lsd-react'
|
||||
import styled from '@emotion/styled'
|
||||
import MoreEpisodesCard from './Episode.MoreEpisodesCard'
|
||||
|
||||
const EpisodeMoreEpisodes = ({ episodes }: any) => {
|
||||
return (
|
||||
<Container>
|
||||
<Typography>More Episodes</Typography>
|
||||
<EpisodeCards>
|
||||
{episodes &&
|
||||
episodes.map((episode: any, idx: number) => (
|
||||
<MoreEpisodesCard
|
||||
key={'more-episode' + idx}
|
||||
thumbnail={episode.thumbnail}
|
||||
title={episode.title}
|
||||
publishedAt={episode.publishedAt}
|
||||
/>
|
||||
))}
|
||||
</EpisodeCards>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
||||
export default EpisodeMoreEpisode
|
||||
const Container = styled.div`
|
||||
margin-top: 72px;
|
||||
border-top: 1px solid rgb(var(--lsd-border-primary));
|
||||
padding-block: 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
`
|
||||
|
||||
const EpisodeCards = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 16px;
|
||||
flex-wrap: wrap;
|
||||
`
|
||||
|
||||
export default EpisodeMoreEpisodes
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
import { Typography } from '@acid-info/lsd-react'
|
||||
import styled from '@emotion/styled'
|
||||
import Image from 'next/image'
|
||||
|
||||
type Props = {
|
||||
thumbnail: string
|
||||
title: string
|
||||
publishedAt: string
|
||||
}
|
||||
|
||||
const MoreEpisodesCard = ({ thumbnail, title, publishedAt }: Props) => {
|
||||
const date = new Date(publishedAt)
|
||||
return (
|
||||
<Container>
|
||||
<ImageContainer>
|
||||
<Image src={thumbnail} fill alt={thumbnail} />
|
||||
</ImageContainer>
|
||||
|
||||
<Row>
|
||||
<Typography variant="body3" genericFontFamily="sans-serif">
|
||||
PODCAST
|
||||
</Typography>
|
||||
<Typography variant="body3">•</Typography>
|
||||
<Typography variant="body3" genericFontFamily="sans-serif">
|
||||
{date &&
|
||||
date.toLocaleString('en-GB', {
|
||||
day: 'numeric',
|
||||
month: 'long', // TODO: Should be uppercase
|
||||
year: 'numeric',
|
||||
})}
|
||||
</Typography>
|
||||
</Row>
|
||||
|
||||
<Typography>{title}</Typography>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
||||
const Container = styled.div`
|
||||
margin-block: 24px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
width: 48%;
|
||||
`
|
||||
|
||||
const ImageContainer = styled.div`
|
||||
width: 100%;
|
||||
height: 190px;
|
||||
position: relative;
|
||||
`
|
||||
|
||||
const Row = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
`
|
||||
|
||||
export default MoreEpisodesCard
|
|
@ -1,9 +1,10 @@
|
|||
import { Authors } from '@/components/Authors'
|
||||
import { Tags } from '@/components/Tags'
|
||||
import { Typography } from '@acid-info/lsd-react'
|
||||
import styled from '@emotion/styled'
|
||||
import { LPE } from '../../../types/lpe.types'
|
||||
import ReactPlayer from 'react-player'
|
||||
import { default as Stats } from '@/components/Article/Article.Stats'
|
||||
import { LogosCircleIcon } from '@/components/Icons/LogosCircleIcon'
|
||||
|
||||
export type EpisodeHeaderProps = LPE.Podcast.Document & { url: string }
|
||||
|
||||
|
@ -11,28 +12,26 @@ const EpisodeHeader = ({
|
|||
title,
|
||||
description,
|
||||
publishedAt,
|
||||
authors,
|
||||
tags,
|
||||
url,
|
||||
}: EpisodeHeaderProps) => {
|
||||
const date = publishedAt ? new Date(publishedAt) : null
|
||||
const date = new Date(publishedAt)
|
||||
return (
|
||||
<EpisodeHeaderContainer>
|
||||
<ReactPlayer url={url} forceVideo={true} controls={true} />
|
||||
<Typography>
|
||||
{date &&
|
||||
date.toLocaleString('en-GB', {
|
||||
day: 'numeric',
|
||||
month: 'long', // TODO: Should be uppercase
|
||||
year: 'numeric',
|
||||
})}
|
||||
</Typography>
|
||||
<PlayerContainer>
|
||||
<ReactPlayer url={url} forceVideo={true} controls={true} />
|
||||
</PlayerContainer>
|
||||
<Stats date={date} readingLength={6} />
|
||||
<EpisodeTitle variant="h1" genericFontFamily="serif" component="h1">
|
||||
{title}
|
||||
</EpisodeTitle>
|
||||
<PodcastName>
|
||||
<LogosCircleIcon width={24} height={24} />
|
||||
Network State Podcast
|
||||
</PodcastName>
|
||||
{description && (
|
||||
<EpisodeSubtitle
|
||||
variant="body1"
|
||||
variant="h6"
|
||||
genericFontFamily="sans-serif"
|
||||
component="div"
|
||||
>
|
||||
|
@ -40,39 +39,13 @@ const EpisodeHeader = ({
|
|||
</EpisodeSubtitle>
|
||||
)}
|
||||
{tags && <Tags tags={tags} />}
|
||||
<AuthorsContainer>
|
||||
<Authors authors={authors} email={true} gap={12} />
|
||||
</AuthorsContainer>
|
||||
</EpisodeHeaderContainer>
|
||||
)
|
||||
}
|
||||
|
||||
const EpisodeHeaderContainer = styled.header`
|
||||
.mobileSummary {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.desktopSummary {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.mobileSummary {
|
||||
display: block;
|
||||
p {
|
||||
font-size: var(--lsd-body3-fontSize);
|
||||
line-height: var(--lsd-body3-lineHeight);
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
hr {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.desktopSummary {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
`
|
||||
|
||||
const CustomTypography = styled(Typography)`
|
||||
|
@ -96,19 +69,15 @@ const EpisodeSubtitle = styled(CustomTypography)`
|
|||
}
|
||||
`
|
||||
|
||||
const AuthorsContainer = styled.div`
|
||||
//margin-block: 24px;
|
||||
margin-top: 24px;
|
||||
const PodcastName = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 20px;
|
||||
`
|
||||
|
||||
const PlayerContainer = styled.div`
|
||||
margin-bottom: 32px;
|
||||
|
||||
@media (max-width: 768px) {
|
||||
margin-top: 16px;
|
||||
margin-bottom: 24px;
|
||||
|
||||
a[href^='mailto:'] {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export default EpisodeHeader
|
||||
|
|
|
@ -12,10 +12,11 @@ const EpisodeContainer = (props: Props) => {
|
|||
|
||||
return (
|
||||
<EpisodeGrid>
|
||||
<Gap className={'w-1'} />
|
||||
<Gap className={'w-4'} />
|
||||
<EpisodeBodyContainer className={'w-8'}>
|
||||
<EpisodeBody data={data} />
|
||||
</EpisodeBodyContainer>
|
||||
<Gap className={'w-4'} />
|
||||
</EpisodeGrid>
|
||||
)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue