feat: implement related articles search view

This commit is contained in:
jinhojang6 2023-05-11 15:13:58 +09:00
parent 52778669d7
commit fa8237303f
No known key found for this signature in database
GPG Key ID: 174E6BE4D37C8295
20 changed files with 160 additions and 47 deletions

View File

@ -49,7 +49,6 @@ export default function ArticleBody({ data }: Props) {
}, [blocks])
const _blocks = useMemo(() => {
console.log(getContentBlocks(blocks))
return getContentBlocks(blocks).map((block, idx) => (
<RenderArticleBlock key={'block-' + idx} block={block} />
))

View File

@ -0,0 +1,34 @@
import Link from 'next/link'
import { Grid, GridItem } from '../Grid/Grid'
import styled from '@emotion/styled'
import Post, { PostDataProps } from '../Post/Post'
type Props = {
post: PostDataProps
}
const FeaturedPost = ({ post }: Props) => {
return (
<Grid>
<GridItem className="w-16">
<PostLink href={`/article/${post.remoteId}`}>
<PostWrapper>
<Post data={post} />
</PostWrapper>
</PostLink>
</GridItem>
</Grid>
)
}
const PostWrapper = styled.div`
padding: 16px 0;
border-top: 1px solid rgb(var(--lsd-theme-primary));
width: 100%;
`
const PostLink = styled(Link)`
text-decoration: none;
`
export default FeaturedPost

View File

@ -0,0 +1 @@
export { default as FeaturedPost } from './FeaturedPost'

View File

@ -34,6 +34,10 @@ export const GridItem = styled.div`
grid-column: span 8;
}
&.w-16 {
grid-column: span 16;
}
@media (max-width: 768px) {
grid-column: span 16 !important;
}

View File

@ -0,0 +1,13 @@
import styled from '@emotion/styled'
import { uiConfigs } from '@/configs/ui.configs'
import { PropsWithChildren } from 'react'
const Main = ({ children }: PropsWithChildren) => {
return <Container>{children}</Container>
}
const Container = styled.main`
margin-block: ${uiConfigs.postSectionMargin}px;
`
export default Main

View File

@ -0,0 +1 @@
export { default as Main } from './Main'

View File

@ -4,17 +4,33 @@ import { PropsWithChildren } from 'react'
type Props = PropsWithChildren<{
title: string
matches?: number
}>
export const Section = (props: any) => {
export const Section = ({ title, matches, children, ...props }: Props) => {
return (
<section>
<Title>{props.title}</Title>
{props.children}
<section {...props}>
<Container>
<Typography genericFontFamily="sans-serif" variant="body2">
{title}
</Typography>
{matches && (
<>
<Typography variant="body2"></Typography>
<Typography genericFontFamily="sans-serif" variant="body2">
{matches} matches
</Typography>
</>
)}
</Container>
{children}
</section>
)
}
const Title = styled(Typography)`
const Container = styled.div`
display: flex;
align-items: center;
gap: 8px;
padding: 0 16px;
`

View File

@ -14,7 +14,7 @@ type Props = {
export default function TableOfContents({ contents, ...props }: Props) {
const articleContainer = useArticleContainerContext()
const { tocIndex, setTocIndex } = articleContainer
const dy = uiConfigs.navbarRenderedHeight + uiConfigs.postMarginTop
const dy = uiConfigs.navbarRenderedHeight + uiConfigs.postSectionMargin
const { sticky, stickyRef, height } = useSticky<HTMLDivElement>(dy)
@ -86,7 +86,8 @@ const Contents = styled.div<{ height: number }>`
flex-direction: column;
overflow-y: auto;
height: calc(
100vh - ${uiConfigs.navbarRenderedHeight + uiConfigs.postMarginTop + 40}px
100vh -
${uiConfigs.navbarRenderedHeight + uiConfigs.postSectionMargin + 40}px
);
&::-webkit-scrollbar {

View File

@ -1,4 +1,4 @@
export const uiConfigs = {
navbarRenderedHeight: 45,
postMarginTop: 78,
postSectionMargin: 78,
}

View File

@ -3,11 +3,13 @@ import styled from '@emotion/styled'
import { useState } from 'react'
import { uiConfigs } from '@/configs/ui.configs'
import { ArticleContainerContext } from '@/containers/ArticleContainer.Context'
import { UnbodyGoogleDoc } from '@/lib/unbody/unbody.types'
import { UnbodyGoogleDoc, UnbodyTocItem } from '@/lib/unbody/unbody.types'
import ArticleBody from '@/components/Article/ArticleBody'
interface Props {
data: UnbodyGoogleDoc
data: UnbodyGoogleDoc & {
toc: UnbodyTocItem[]
}
}
const ArticleContainer = (props: Props) => {
@ -30,7 +32,6 @@ const ArticleContainer = (props: Props) => {
const Container = styled.div`
display: flex;
justify-content: center;
margin-top: ${uiConfigs.postMarginTop}px;
`
const Right = styled.aside`

View File

@ -6,6 +6,7 @@ import { Searchbar } from '@/components/Searchbar'
import { ESearchScope } from '@/types/ui.types'
import styles from './Article.layout.module.css'
import { Footer } from '@/components/Footer'
import { Main } from '@/components/Main'
export default function ArticleLayout(props: PropsWithChildren<any>) {
const isDarkState = useIsDarkState()
@ -16,7 +17,7 @@ export default function ArticleLayout(props: PropsWithChildren<any>) {
<NavbarFiller />
<Searchbar searchScope={ESearchScope.ARTICLE} />
</header>
<main>{props.children}</main>
<Main>{props.children}</Main>
<Footer />
</>
)

View File

@ -5,6 +5,7 @@ import { Hero } from '@/components/Hero'
import { NavbarFiller } from '@/components/Navbar/NavbarFiller'
import { Searchbar } from '@/components/Searchbar'
import { Footer } from '@/components/Footer'
import { Main } from '@/components/Main'
export default function DefaultLayout(props: PropsWithChildren<any>) {
const isDarkState = useIsDarkState()
@ -17,7 +18,7 @@ export default function DefaultLayout(props: PropsWithChildren<any>) {
<Hero />
<Searchbar />
</header>
<main>{props.children}</main>
<Main>{props.children}</Main>
<Footer />
</>
)

View File

@ -0,0 +1,3 @@
.header > nav {
border-bottom: none;
}

View File

@ -0,0 +1,24 @@
import { Navbar } from '@/components/Navbar'
import useIsDarkState from '@/states/isDarkState/isDarkState'
import { PropsWithChildren } from 'react'
import { NavbarFiller } from '@/components/Navbar/NavbarFiller'
import { Searchbar } from '@/components/Searchbar'
import { ESearchScope } from '@/types/ui.types'
import styles from './Search.layout.module.css'
import { Footer } from '@/components/Footer'
import { Main } from '@/components/Main'
export default function SearchLayout(props: PropsWithChildren<any>) {
const isDarkState = useIsDarkState()
return (
<>
<header className={styles.header}>
<Navbar isDark={isDarkState.get()} toggle={isDarkState.toggle} />
<NavbarFiller />
<Searchbar searchScope={ESearchScope.ARTICLE} />
</header>
<Main>{props.children}</Main>
<Footer />
</>
)
}

View File

@ -0,0 +1 @@
export { default as SearchLayout } from './Search.layout'

View File

@ -1,12 +1,11 @@
import api from '@/services/unbody.service'
import Post, { PostDataProps } from '@/components/Post/Post'
import { PostDataProps } from '@/components/Post/Post'
import { PostsList } from '@/components/PostList/PostList'
import { Section } from '@/components/Section/Section'
import { UnbodyGoogleDoc, UnbodyImageBlock } from '@/lib/unbody/unbody.types'
import { ESearchStatus } from '@/types/ui.types'
import { GetStaticProps } from 'next'
import { getArticleCover } from '@/utils/data.utils'
import { FeaturedPost } from '@/components/FeaturedPost'
type Props = {
posts: PostDataProps[]
@ -17,10 +16,9 @@ type Props = {
export default function Home({ posts, featured }: Props) {
return (
<>
{/*@TODO @jinho, wht PostContainer should recive an array of postData instead of only One?*/}
{featured && (
<Section title={'Featured'}>
<Post data={featured} />
<FeaturedPost post={featured} />
</Section>
)}
<Section title={'Latest posts'}>
@ -43,24 +41,20 @@ export const getStaticProps = async () => {
remoteId: featured.remoteId,
date: featured.modifiedAt,
title: featured.title,
description: featured.summary,
description: featured.subtitle, // TODO: summary is not available
author: 'Jinho',
tags: featured.tags,
...(featured.blocks && featured.blocks!.length > 0
? { coverImage: featured.blocks![0] as UnbodyImageBlock }
: {}),
coverImage: getArticleCover(featured.blocks),
}
: null,
posts: posts.map((post) => ({
remoteId: post.remoteId,
date: post.modifiedAt,
title: post.title,
description: post.summary,
description: post.subtitle, // TODO: summary is not available
author: 'Jinho',
tags: post.tags,
...(post.blocks && post.blocks!.length > 0
? { coverImage: post.blocks![0] as UnbodyImageBlock }
: {}),
coverImage: getArticleCover(post.blocks),
})),
errors,
},

View File

@ -21,8 +21,12 @@ import {
extractTopicsFromQuery,
} from '@/utils/search.utils'
import { useRouter } from 'next/router'
import { useEffect, useRef, useState } from 'react'
import { ReactNode, useEffect, useRef, useState } from 'react'
import Link from 'next/link'
import { SearchLayout } from '@/layouts/SearchLayout'
import { Section } from '@/components/Section/Section'
import { PostsList } from '@/components/PostList/PostList'
import { getArticleCover } from '@/utils/data.utils'
interface SearchPageProps {
articles: SearchResultItem<UnbodyGoogleDoc>[]
@ -73,24 +77,22 @@ export default function SearchPage({
return (
<div>
<section>
<strong>Related articles</strong>
<hr />
<div>
{articles.loading && <div>...</div>}
{!articles.error && articles.data && articles.data.length > 0 ? (
articles.data.map((article: SearchResultItem<UnbodyGoogleDoc>) => (
<div key={article.doc.remoteId}>
<h2>{article.doc.title}</h2>
<p>{article.doc.summary}</p>
</div>
))
) : (
<div>Nothing found</div>
)}
</div>
</section>
<br />
{articles.data?.length && (
<Section title={'Related Articles'} matches={articles.data?.length}>
<PostsList
posts={articles.data.map((article) => ({
remoteId: article.doc.remoteId,
date: article.doc.modifiedAt,
title: article.doc.title,
description: article.doc.subtitle, // TODO: summary is not available
author: 'Jinho',
tags: article.doc.tags,
coverImage: getArticleCover(article.doc.blocks),
}))}
/>
</Section>
)}
<section>
<strong>Related content blocks</strong>
<hr />
@ -156,6 +158,10 @@ export default function SearchPage({
)
}
SearchPage.getLayout = function getLayout(page: ReactNode) {
return <SearchLayout>{page}</SearchLayout>
}
export async function getStaticProps() {
const { data: articles = [] } = await unbodyApi.searchArticles()
const { data: blocks = [] } = await unbodyApi.serachBlocks()

View File

@ -10,6 +10,7 @@ export const getArticlePostQuery = (args: UnbodyGetFilters = defaultArgs) =>
sourceId
remoteId
title
subtitle
summary
tags
createdAt

View File

@ -12,6 +12,7 @@ export const getHomePagePostsQuery = (args: UnbodyGetFilters = defaultArgs) =>
GetGoogleDocQuery(args)(`
remoteId
title
subtitle
summary
tags
createdAt
@ -21,6 +22,8 @@ export const getHomePagePostsQuery = (args: UnbodyGetFilters = defaultArgs) =>
...on ImageBlock{
url
alt
order
__typename
}
}
`)

View File

@ -7,9 +7,18 @@ export const getSearchArticlesQuery = (args: UnbodyGetFilters = defaultArgs) =>
GetGoogleDocQuery(args)(`
remoteId
title
subtitle
summary
tags
modifiedAt
blocks{
...on ImageBlock{
url
alt
order
__typename
}
}
_additional{
certainty
}