refactor: render real data for article page

This commit is contained in:
jinhojang6 2023-05-09 21:04:55 +09:00 committed by Jinho Jang
parent 3e5b8c6ec8
commit 21d3f51323
5 changed files with 105 additions and 47 deletions

View File

@ -2,49 +2,79 @@ import { Tag, Typography } from '@acid-info/lsd-react'
import styled from '@emotion/styled'
import Image from 'next/image'
import { useMemo } from 'react'
import { PostProps } from '../Post'
import { PostImageRatio, PostImageRatioOptions, PostSize } from '../Post/Post'
import { PostImageRatio, PostImageRatioOptions } from '../Post/Post'
import styles from './Article.module.css'
import { Collapse } from '@/components/Collapse'
import { useArticleContainerContext } from '@/containers/ArticleContainer.Context'
import { moreFromAuthor, references, relatedArticles } from './tempData'
import { ArticleReference } from '../ArticleReference'
import { UnbodyGoogleDoc, UnbodyImageBlock } from '@/lib/unbody/unbody.types'
export default function Article({
appearance: { aspectRatio = PostImageRatio.LANDSCAPE } = {},
data: {
coverImage = null,
date: dateStr = '',
title,
blocks,
summary,
author,
authorEmail,
tags = [],
toc = [],
},
...props
}: PostProps) {
interface Props {
data: UnbodyGoogleDoc
}
export default function Article({ data }: Props) {
const { title, summary, blocks, toc, createdAt } = data
const articleContainer = useArticleContainerContext()
const { tocIndex, setTocIndex } = articleContainer
const date = new Date(dateStr)
// temporary data - unbody doesn't provide
const author = 'John Doe'
const authorEmail = 'john@acid.info'
const tags = ['Privacy', 'Blockchain', 'Technology']
const date = new Date(createdAt)
const _thumbnail = useMemo(() => {
const imageBlocks: UnbodyImageBlock[] = blocks.filter(
(block): block is UnbodyImageBlock => block !== null && 'url' in block,
)
const coverImage = imageBlocks.reduce((prev, curr) =>
prev.order < curr.order ? prev : curr,
)
if (!coverImage) return null
return (
<ThumbnailContainer aspectRatio={aspectRatio}>
<ThumbnailContainer aspectRatio={PostImageRatio.LANDSCAPE}>
<Thumbnail fill src={coverImage.url} alt={coverImage.alt} />
</ThumbnailContainer>
)
}, [coverImage])
}, [blocks])
// TODO : using typography for the blocks
const _blocks = useMemo(
() => <Blocks dangerouslySetInnerHTML={{ __html: blocks ?? '' }} />,
[blocks],
)
const _blocks = useMemo(() => {
// Exclude title, subtitle, coverImage
const articleBlocks = blocks.sort((a, b) => a.order - b.order).slice(3)
return articleBlocks.map((block, idx) => {
return 'url' in block ? (
<ThumbnailContainer
key={'block-' + idx}
aspectRatio={PostImageRatio.LANDSCAPE}
>
<Thumbnail fill src={block.url} alt={block.alt} />
</ThumbnailContainer>
) : block.tagName.startsWith('h') ? (
<Headline
variant="body2"
component={block.tagName as any}
genericFontFamily="sans-serif"
key={'block-' + idx}
dangerouslySetInnerHTML={{ __html: block.html }}
/>
) : (
<Paragraph
variant="body1"
component="p"
genericFontFamily="sans-serif"
key={'block-' + idx}
dangerouslySetInnerHTML={{ __html: block.html }}
/>
)
})
}, [blocks])
const _mobileToc = useMemo(
() =>
@ -115,7 +145,7 @@ export default function Article({
)
return (
<ArticleContainer {...props}>
<ArticleContainer>
<div>
<Row>
<Typography variant="body3" genericFontFamily="sans-serif">
@ -132,15 +162,24 @@ export default function Article({
</Row>
</div>
<Title variant={'h1'} genericFontFamily="serif">
{/* assign id for toc scroll */}
<Title
/*
// @ts-ignore */
id={toc[0].href.substring(1)}
variant={'h1'}
genericFontFamily="serif"
>
{title}
</Title>
{_thumbnail}
<CustomTypography variant={'body1'} genericFontFamily="sans-serif">
{/* subtitle returns "" so using summary instead */}
<Summary variant={'body1'} genericFontFamily="sans-serif">
{summary}
</CustomTypography>
</Summary>
{tags.length > 0 && (
<TagContainer>
@ -172,7 +211,7 @@ export default function Article({
{_mobileToc}
{_blocks}
<TextContainer>{_blocks}</TextContainer>
{_references}
@ -209,12 +248,27 @@ const Title = styled(CustomTypography)`
margin-bottom: 24px;
`
const Blocks = styled.div`
white-space: pre-wrap;
const Summary = styled(CustomTypography)`
margin-top: 24px;
`
const TextContainer = styled.div`
display: flex;
flex-direction: column;
gap: 16px;
margin-top: 24px;
margin-bottom: 80px;
`
const Headline = styled(Typography)`
white-space: pre-wrap;
margin-top: 24px;
`
const Paragraph = styled(Typography)`
white-space: pre-wrap;
`
const ThumbnailContainer = styled.div<{
aspectRatio: PostImageRatio
}>`

View File

@ -21,11 +21,14 @@ export default function TableOfContents({ contents, ...props }: Props) {
const handleSectionClick = (index: number) => {
//@ts-ignore
const section = document.getElementById(contents[index].href.substring(1))
section?.scrollIntoView({
const position = section?.getBoundingClientRect()
window.scrollTo({
top: Number(position?.top) + window.scrollY - 100,
behavior: 'smooth',
block: 'start',
inline: 'nearest',
})
setTocIndex(index)
}
@ -60,7 +63,7 @@ const Container = styled.aside<{ dy: number; height: number }>`
flex-direction: column;
width: 162px;
box-sizing: border-box;
height: fit-content;
height: ${(p) => (p.height > 0 ? `${p.height}px` : 'fit-content')};
position: sticky;
top: ${(p) => `${p.dy}px`};
margin-left: 16px;
@ -68,7 +71,6 @@ const Container = styled.aside<{ dy: number; height: number }>`
&.sticky {
top: ${uiConfigs.navbarRenderedHeight + 78 + 1}px;
z-index: 100;
height: ${(p) => `${p.height}px`};
}
// temporary breakpoint

View File

@ -8,22 +8,25 @@ import { UnbodyGoogleDoc } from '@/lib/unbody/unbody.types'
interface Props {
data: UnbodyGoogleDoc
error: string | null
}
const ArticleContainer = (props: Props) => {
const { data } = props
const { data, error } = props
const [tocIndex, setTocIndex] = useState(0)
return (
return !error?.length ? (
<Container>
<ArticleContainerContext.Provider
value={{ tocIndex: tocIndex, setTocIndex: setTocIndex }}
>
<TableOfContents contents={data.toc ?? []} />
{/*<Article data={data} />*/}
<Article data={data} />
<Right />
</ArticleContainerContext.Provider>
</Container>
) : (
<div>{error}</div>
)
}

View File

@ -13,6 +13,7 @@ type ArticleProps = {
export const getStaticProps = async ({ params }: GetStaticPropsContext) => {
const { remoteId } = params!
if (!remoteId) {
return {
notFound: true,
@ -29,14 +30,9 @@ export const getStaticProps = async ({ params }: GetStaticPropsContext) => {
// @jinho lets handle the error directly in thew page component
const ArticlePage = (props: ArticleProps) => {
if (!props.data) return <div>Opps...</div>
if (!props.data) return <div />
return (
<div>{props.data.title}</div>
// <ArticleContainer post={props.data}
// error={props.error}
// />
)
return <ArticleContainer data={props.data} error={props.error} />
}
export async function getStaticPaths() {

View File

@ -19,10 +19,13 @@ export const getArticlePostQuery = (args: UnbodyGetFilters = defaultArgs) =>
...on ImageBlock{
url
alt
order
}
... on TextBlock {
footnotes
html
order
tagName
}
}
`)