feat: add components for episode

This commit is contained in:
jinhojang6 2023-08-16 11:18:42 +09:00
parent 8ccd877d66
commit 297c49e06a
9 changed files with 201 additions and 61 deletions

View File

@ -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

View File

@ -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>

View File

@ -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

View File

@ -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} />

View File

@ -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 {

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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>
)
}