Skip to content

Commit

Permalink
Merge pull request #138 from pagers-org/carpe/article-comments
Browse files Browse the repository at this point in the history
feat(article): 아티클 상세화면에 진입 시 실제 아티클 댓글 데이터를 받아 렌더링하도록 합니다.
  • Loading branch information
innocarpe authored Sep 21, 2023
2 parents 811d6b8 + 862dcee commit 57db0d6
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 50 deletions.
20 changes: 19 additions & 1 deletion apps/react-world/src/apis/article/ArticleService.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { isAxiosError } from 'axios';
import { api } from '../apiInstances';
import { api } from '@apis/apiInstances';
import type {
ArticleDetailResponse,
ArticleDetailErrorResponse,
ArticleCommentsResponse,
ArticleCommentsErrorResponse,
} from './ArticleService.types';

class ArticleService {
Expand All @@ -21,6 +23,22 @@ class ArticleService {
throw error;
}
}

static async fetchArticleComments(
slug: string,
): Promise<ArticleCommentsResponse> {
try {
const response = await api.get(`/articles/${slug}/comments`);
return response.data;
} catch (error) {
if (isAxiosError(error) && error.response) {
console.error('Axios error occurred:', error.response.data);
throw error.response.data as ArticleCommentsErrorResponse;
}
console.error('An unexpected error occurred:', error);
throw error;
}
}
}

export default ArticleService;
20 changes: 20 additions & 0 deletions apps/react-world/src/apis/article/ArticleService.types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { ArticleAuthor } from './Article.types';

// Article Detail
export interface ArticleDetailData {
slug: string;
title: string;
Expand All @@ -22,3 +23,22 @@ export interface ArticleDetailErrorResponse {
body: string[];
};
}

// Article Comment
export interface ArticleCommentData {
id: number;
createdAt: string;
updatedAt: string;
body: string;
author: ArticleAuthor;
}

export interface ArticleCommentsResponse {
comments: ArticleCommentData[];
}

export interface ArticleCommentsErrorResponse {
errors: {
body: string[];
};
}
88 changes: 42 additions & 46 deletions apps/react-world/src/components/article/ArticleComments.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
const ArticleComments = () => {
import type { ArticleCommentData } from '@apis/article/ArticleService.types';

interface ArticleCommentsProps {
comments: ArticleCommentData[];
}

const ArticleComments = (props: ArticleCommentsProps) => {
const { comments } = props;

return (
<div className="row">
<div className="col-xs-12 col-md-8 offset-md-2">
Expand All @@ -10,6 +18,7 @@ const ArticleComments = () => {
></textarea>
</div>
<div className="card-footer">
{/* This will likely be the logged-in user's image */}
<img
src="http://i.imgur.com/Qr71crq.jpg"
className="comment-author-img"
Expand All @@ -18,52 +27,39 @@ const ArticleComments = () => {
</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 href="/profile/author" className="comment-author">
<img
src="http://i.imgur.com/Qr71crq.jpg"
className="comment-author-img"
/>
</a>
&nbsp;
<a href="/profile/jacob-schmidt" className="comment-author">
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 href="/profile/author" className="comment-author">
<img
src="http://i.imgur.com/Qr71crq.jpg"
className="comment-author-img"
/>
</a>
&nbsp;
<a href="/profile/jacob-schmidt" className="comment-author">
Jacob Schmidt
</a>
<span className="date-posted">Dec 29th</span>
<span className="mod-options">
<i className="ion-trash-a"></i>
</span>
{comments.map(comment => (
<div key={comment.id} className="card">
<div className="card-block">
<p className="card-text">{comment.body}</p>
</div>
<div className="card-footer">
<a
href={`/profile/${comment.author.username}`}
className="comment-author"
>
<img
src={comment.author.image}
className="comment-author-img"
/>
</a>
&nbsp;
<a
href={`/profile/${comment.author.username}`}
className="comment-author"
>
{comment.author.username}
</a>
<span className="date-posted">
{new Date(comment.createdAt).toDateString()}
</span>
{/* If the logged-in user has the right to delete this comment, display the delete button */}
{/* For now, I'm leaving it static but you'll likely need a condition */}
<span className="mod-options">
<i className="ion-trash-a"></i>
</span>
</div>
</div>
</div>
))}
</div>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import ArticleComments from './ArticleComments';
import ArticleContents from './ArticleContents';
import ArticleHeader from './ArticleHeader';
import useArticleDetailQuery from '@quries/useArticleDetailQuery';
import useArticleCommentsQuery from '@quries/useArticleCommentsQuery';

interface ArticlePageContainerProps {
articleSlug: string;
Expand All @@ -12,6 +13,7 @@ interface ArticlePageContainerProps {
const ArticlePageContainer = (props: ArticlePageContainerProps) => {
const { articleSlug } = props;
const { articleDetail } = useArticleDetailQuery(articleSlug);
const { articleComments } = useArticleCommentsQuery(articleSlug);

if (!articleDetail) {
return null;
Expand All @@ -38,7 +40,7 @@ const ArticlePageContainer = (props: ArticlePageContainerProps) => {
favorited={articleDetail.article.favorited}
favoritesCount={articleDetail.article.favoritesCount}
/>
<ArticleComments />
<ArticleComments comments={articleComments?.comments || []} />
</Container>
</div>
);
Expand Down
19 changes: 19 additions & 0 deletions apps/react-world/src/quries/useArticleCommentsQuery.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { useQuery } from '@tanstack/react-query';
import type { ArticleCommentsResponse } from '@apis/article/ArticleService.types';
import ArticleService from '@apis/article/ArticleService';

export const ARTICLE_COMMENTS_CACHE_KEY = '@article/comments';

const useArticleCommentsQuery = (slug: string) => {
const queryResult = useQuery<ArticleCommentsResponse, Error>(
[ARTICLE_COMMENTS_CACHE_KEY, slug], // 슬러그를 조합해 QueryKey 지정
() => ArticleService.fetchArticleComments(slug),
);

return {
articleComments: queryResult.data,
isArticleCommentsLoading: queryResult.isLoading,
};
};

export default useArticleCommentsQuery;
2 changes: 0 additions & 2 deletions apps/react-world/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { resolve } from 'path';
import path from 'path';

// https://vitejs.dev/config/
export default defineConfig({
Expand Down

0 comments on commit 57db0d6

Please sign in to comment.