-
Notifications
You must be signed in to change notification settings - Fork 41
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
아티클 상세 페이지에서 본문에 해당하는 내용을 서버로부터 가져옵니다. (#132)
* feat : 아티클 상세페이지를 가져올 수 있는 api와 훅을 추가합니다. * feat : header 에서 slug를 가져와 article 을 프리패치합니다. * feat : 아티클 상세 페이지에서 관심사에 따라 컴포넌트를 분리해줍니다. * feat : header 에서 가져오는 params 를 page props 에서 가져올 수 있도록 수정합니다.
- Loading branch information
Showing
12 changed files
with
281 additions
and
138 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import { z } from "zod"; | ||
import { http } from "../../../lib/http"; | ||
import type { ArticleDTO } from "../_types/articles.types"; | ||
import { ArticleResponseSchema } from "../_types/articles.types"; | ||
|
||
export const getArticle = (slug: string): Promise<ArticleDTO | undefined> => { | ||
return http.get({ | ||
url: `/articles/${slug}`, | ||
schema: ArticleResponseSchema, | ||
}); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import type { ArticleDTO } from "../_types/articles.types"; | ||
|
||
interface ArticleActionProps { | ||
article: ArticleDTO; | ||
} | ||
|
||
export function ArticleAction({ article: { article } }: ArticleActionProps): JSX.Element { | ||
const { author } = article; | ||
|
||
return ( | ||
<div className="article-actions"> | ||
<div className="article-meta"> | ||
<a href="profile.html"> | ||
<img alt="" src="http://i.imgur.com/Qr71crq.jpg" /> | ||
</a> | ||
<div className="info"> | ||
<a className="author" href="/"> | ||
{author.username} | ||
</a> | ||
<span className="date">January 20th</span> | ||
</div> | ||
<button className="btn btn-sm btn-outline-secondary" type="button"> | ||
<i className="ion-plus-round" /> | ||
Following {author.username} | ||
</button> | ||
| ||
<button className={`btn btn-sm btn-outline-primary ${article.favorited ? "active" : ""}`} type="button"> | ||
<i className="ion-heart" /> | ||
Favorite Article <span className="counter">({article.favoritesCount})</span> | ||
</button> | ||
{/* <button className="btn btn-sm btn-outline-secondary" type="button"> | ||
<i className="ion-edit" /> Edit Article | ||
</button> | ||
<button className="btn btn-sm btn-outline-danger" type="button"> | ||
<i className="ion-trash-a" /> Delete Article | ||
</button> */} | ||
</div> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import Link from "next/link"; | ||
import { useGetArticle } from "../_hooks/use-get-article"; | ||
import type { ArticleDTO } from "../_types/articles.types"; | ||
|
||
interface ArticleBannerProps { | ||
article: ArticleDTO; | ||
} | ||
|
||
export default function ArticleBanner({ article: { article } }: ArticleBannerProps): JSX.Element { | ||
const { author } = article; | ||
|
||
return ( | ||
<div className="banner"> | ||
<div className="container"> | ||
<h1>{article.title}</h1> | ||
|
||
<div className="article-meta"> | ||
<Link | ||
href={{ | ||
pathname: `/profile/${author.username}`, | ||
}} | ||
> | ||
<img alt="" src={author.image} /> | ||
</Link> | ||
<div className="info"> | ||
<Link | ||
className="author" | ||
href={{ | ||
pathname: `/profile/${author.username}`, | ||
}} | ||
> | ||
{author.username} | ||
</Link> | ||
<span className="date">January 20th</span> | ||
</div> | ||
<button className="btn btn-sm btn-outline-secondary" type="button"> | ||
<i className="ion-plus-round" /> | ||
Follow {article.author.username} <span className="counter">({author.following})</span> | ||
</button> | ||
| ||
<button className={`btn btn-sm btn-outline-primary ${article.favorited ? "active" : ""}`} type="button"> | ||
<i className="ion-heart" /> | ||
Favorite Post <span className="counter">({article.favoritesCount})</span> | ||
</button> | ||
{/* <button className="btn btn-sm btn-outline-secondary" type="button"> | ||
<i className="ion-edit" /> Edit Article | ||
</button> */} | ||
{/* <button className="btn btn-sm btn-outline-danger" type="button"> | ||
<i className="ion-trash-a" /> Delete Article | ||
</button> */} | ||
</div> | ||
</div> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
"use client"; | ||
|
||
export function ArticleComment(): JSX.Element { | ||
return ( | ||
<div className="row"> | ||
<div className="col-xs-12 col-md-8 offset-md-2"> | ||
<form className="card comment-form"> | ||
<div className="card-block"> | ||
<textarea className="form-control" placeholder="Write a comment..." rows={3} /> | ||
</div> | ||
<div className="card-footer"> | ||
<img alt="" className="comment-author-img" src="http://i.imgur.com/Qr71crq.jpg" /> | ||
<button className="btn btn-sm btn-primary" type="button"> | ||
Post Comment | ||
</button> | ||
</div> | ||
</form> | ||
|
||
<div className="card"> | ||
<div className="card-block"> | ||
<p className="card-text">With supporting text below as a natural lead-in to additional content.</p> | ||
</div> | ||
<div className="card-footer"> | ||
<a className="comment-author" href="/profile/author"> | ||
<img alt="" className="comment-author-img" src="http://i.imgur.com/Qr71crq.jpg" /> | ||
</a> | ||
| ||
<a className="comment-author" href="/profile/jacob-schmidt"> | ||
Jacob Schmidt | ||
</a> | ||
<span className="date-posted">Dec 29th</span> | ||
</div> | ||
</div> | ||
|
||
<div className="card"> | ||
<div className="card-block"> | ||
<p className="card-text">With supporting text below as a natural lead-in to additional content.</p> | ||
</div> | ||
<div className="card-footer"> | ||
<a className="comment-author" href="/profile/author"> | ||
<img alt="" className="comment-author-img" src="http://i.imgur.com/Qr71crq.jpg" /> | ||
</a> | ||
| ||
<a className="comment-author" href="/profile/jacob-schmidt"> | ||
Jacob Schmidt | ||
</a> | ||
<span className="date-posted">Dec 29th</span> | ||
<span className="mod-options"> | ||
<i className="ion-trash-a" /> | ||
</span> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import type { ArticleDTO } from "../_types/articles.types"; | ||
|
||
interface ArticleContentProps { | ||
article: ArticleDTO; | ||
} | ||
|
||
export function ArticleContent({ article: { article } }: ArticleContentProps): JSX.Element { | ||
return ( | ||
<> | ||
<div className="row article-content"> | ||
<div className="col-md-12"> | ||
<h2 id="introducing-ionic">Description</h2> | ||
<p>{article.description}</p> | ||
<h2 id="introducing-ionic">Main</h2> | ||
<p>{article.body.replace(/\\n/g, "\n")}</p> | ||
<ul className="tag-list"> | ||
{article.tagList.map(tag => ( | ||
<li className="tag-default tag-pill tag-outline" key={tag}> | ||
{tag} | ||
</li> | ||
))} | ||
</ul> | ||
</div> | ||
</div> | ||
<hr /> | ||
</> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
"use client"; | ||
|
||
import { useGetArticle } from "../_hooks/use-get-article"; | ||
import { ArticleComment } from "./article-comment"; | ||
import ArticleBanner from "./article-banner"; | ||
import { ArticleAction } from "./article-action"; | ||
import { ArticleContent } from "./article-content"; | ||
|
||
interface ArticleDetailProps { | ||
slug: string; | ||
} | ||
|
||
export function ArticleDetail({ slug }: ArticleDetailProps): JSX.Element { | ||
const { data } = useGetArticle(slug); | ||
|
||
return ( | ||
<div className="article-page"> | ||
{data ? ( | ||
<> | ||
<ArticleBanner article={data} /> | ||
<div className="container page"> | ||
<ArticleContent article={data} /> | ||
<ArticleAction article={data} /> | ||
<ArticleComment /> | ||
</div> | ||
</> | ||
) : null} | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
import type { ArticlesQueryParamsType } from "../_types/articles.types"; | ||
|
||
export const queryKeys = { | ||
articles: (params: ArticlesQueryParamsType) => ["articles", params] as const, | ||
}; | ||
articles: (params: ArticlesQueryParamsType) => ["articles", params], | ||
article: (slug: string) => ["articles", slug], | ||
} as const; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import type { UseQueryResult } from "@tanstack/react-query"; | ||
import { useQuery } from "@tanstack/react-query"; | ||
import type { ArticleDTO } from "../_types/articles.types"; | ||
import { queryKeys } from "../_constants/querykeys"; | ||
import { getArticle } from "../_api/get-article"; | ||
|
||
export function useGetArticle(slug: string): UseQueryResult<ArticleDTO | undefined, Error> { | ||
return useQuery(queryKeys.article(slug), () => getArticle(slug), { | ||
suspense: true, | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import { dehydrate } from "@tanstack/query-core"; | ||
import { Suspense } from "react"; | ||
import getQueryClient from "../../../../lib/get-query-clinet"; | ||
import { queryKeys } from "../../_constants/querykeys"; | ||
import { getArticle } from "../../_api/get-article"; | ||
import Hydrate from "../../../../lib/query-hydrate"; | ||
import { ArticleDetail } from "../../_components/article-detail"; | ||
|
||
interface ArticlePageProps { | ||
params: { | ||
slug: string; | ||
}; | ||
} | ||
|
||
export default async function ArticlePage({ params }: ArticlePageProps): Promise<JSX.Element> { | ||
const { slug } = params; | ||
|
||
if (!slug) { | ||
throw new Error("ERROR : article path is not found"); | ||
} | ||
|
||
const queryClient = getQueryClient(); | ||
await queryClient.prefetchQuery(queryKeys.article(slug), () => getArticle(slug)); | ||
const dehydrateState = dehydrate(queryClient); | ||
|
||
return ( | ||
<Hydrate state={dehydrateState}> | ||
<Suspense fallback={<div>loading...</div>}> | ||
<ArticleDetail slug={slug} /> | ||
</Suspense> | ||
</Hydrate> | ||
); | ||
} |
Oops, something went wrong.