Skip to content

Commit

Permalink
add comments in articles with pagination
Browse files Browse the repository at this point in the history
  • Loading branch information
Mielie committed Mar 14, 2023
1 parent a4631c7 commit aeb27dc
Show file tree
Hide file tree
Showing 11 changed files with 247 additions and 39 deletions.
81 changes: 73 additions & 8 deletions src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
text-align: center;
width: 100vw;
height: 100vh;
font-family: arial;
}

* {
Expand Down Expand Up @@ -204,6 +205,76 @@ header {
height: 30px;
}

#commentsBox {
width: 100vw;
min-height: 300px;
background-color: #f4f2f1;
}

#commentLoadingSpinner {
left: 50%;
margin-left: -40px;
}

#commentsTitle {
margin-top: 20px;
text-align: center;
font-size: medium;
font-weight: bold;
color: #414d52;
font-family: arial;
}

.commentCard {
display: grid;
grid-template-columns: 40px auto 100px;
grid-template-rows: 40px auto 25px;
grid-template-areas:
"commentAvatar commentAuthor commentVotes"
"commentBody commentBody commentBody"
"commentDate commentDate commentDate";
background-color: #f9f2ec;
border: 1px solid #a2baab;
border-radius: 10px;
margin: 10px 20px 10px 20px;
box-shadow: 0px 0px 3px #414d52;
}

.commentAvatar {
grid-area: commentAvatar;
width: 30px;
height: 30px;
border-radius: 50%;
margin: auto;
}

.commentAuthor {
grid-area: commentAuthor;
line-height: 40px;
font-style: italic;
}

.commentVotes {
grid-area: commentVotes;
line-height: 40px;
text-align: right;
margin-right: 20px;
}

.commentBody {
grid-area: commentBody;
text-align: justify;
margin: 10px;
}

.commentDate {
grid-area: commentDate;
text-align: right;
margin: 0px 20px 0px 0px;
font-size: small;
color: #414d52;
}

#footerBox {
position: fixed;
left: 0;
Expand All @@ -225,22 +296,16 @@ header {
font-weight: bold;
}

#articleCount {
#footerRightText {
font-weight: bold;
grid-column: 3;
text-align: right;
margin-right: 20px;
line-height: 28px;
}

#wordCount {
font-weight: bold;
grid-column: 2;
line-height: 28px;
}

@media (max-width: 500px) {
#articleCount {
#footerRightText {
display: none;
}
}
Expand Down
21 changes: 14 additions & 7 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ import Article from "./Article";
import { useState } from "react";

function App() {
const [numArticles, setNumArticles] = useState(null);
const [numItems, setNumItems] = useState(null);
const [pageNumber, setPageNumber] = useState(1);
const [commentPageNumber, setCommentPageNumber] = useState(1);
const [articlePerPage, setArticlePerPage] = useState(10);
const [articleWordCount, setArticleWordCount] = useState(null);

Expand All @@ -19,21 +20,27 @@ function App() {
<Route
path="/"
element={
<ArticleList
setNumArticles={setNumArticles}
pageNumber={pageNumber}
/>
<ArticleList setNumItems={setNumItems} pageNumber={pageNumber} />
}
/>
<Route
path="/articles/:articleid"
element={<Article setArticleWordCount={setArticleWordCount} />}
element={
<Article
setArticleWordCount={setArticleWordCount}
setNumItems={setNumItems}
setCommentPageNumber={setCommentPageNumber}
commentPageNumber={commentPageNumber}
/>
}
/>
</Routes>
<Footer
pageNumber={pageNumber}
setPageNumber={setPageNumber}
numArticles={numArticles}
commentPageNumber={commentPageNumber}
setCommentPageNumber={setCommentPageNumber}
numItems={numItems}
articlesPerPage={articlePerPage}
articleWordCount={articleWordCount}
/>
Expand Down
19 changes: 17 additions & 2 deletions src/Article.jsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,29 @@
import { useEffect, useState } from "react";
import axios from "axios";
import { useParams } from "react-router-dom";
import { formatDate, wordCount } from "./utils";
import LoadingSpinner from "./LoadingSpinner";
import { getArticle } from "./apiFunctions";
import Comment from "./Comment";

const Article = ({ setArticleWordCount }) => {
const Article = ({
setArticleWordCount,
setNumItems,
setCommentPageNumber,
commentPageNumber,
}) => {
const { articleid } = useParams();
const [article, setArticle] = useState(null);
const [isLoading, setIsLoading] = useState(true);
const [commentCount, setCommentCount] = useState(null);

useEffect(() => {
setIsLoading(true);
setNumItems(null);
setCommentPageNumber(1);
setArticleWordCount(null);
getArticle(articleid).then((article) => {
setArticle(article);
setCommentCount(article.comment_count);
setArticleWordCount(wordCount(article.body));
setIsLoading(false);
});
Expand All @@ -36,6 +45,12 @@ const Article = ({ setArticleWordCount }) => {
<p id="articleViewAuthor">{article.author}</p>
<p id="articleViewDate">{formatDate(article.created_at)}</p>
<p id="articleViewBody">{article.body}</p>
<Comment
articleid={articleid}
commentCount={commentCount}
setNumItems={setNumItems}
commentPageNumber={commentPageNumber}
/>
</article>
);
};
Expand Down
7 changes: 3 additions & 4 deletions src/ArticleList.jsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
import { useState, useEffect } from "react";
import { getArticles } from "./apiFunctions";
import ArticleItem from "./ArticleItem";
import { formatDate } from "./utils";
import Footer from "./Footer";
import LoadingSpinner from "./LoadingSpinner";

const ArticleList = ({ setNumArticles, pageNumber }) => {
const ArticleList = ({ setNumItems, pageNumber }) => {
const [isLoading, setIsLoading] = useState(true);
const [articles, setArticles] = useState([]);

useEffect(() => {
setIsLoading(true);
setNumItems(null);
getArticles(pageNumber).then((articles) => {
setNumArticles(articles.total_count);
setNumItems(articles.total_count);
setArticles(articles.articles);
setIsLoading(false);
});
Expand Down
54 changes: 54 additions & 0 deletions src/Comment.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { useState, useEffect } from "react";
import LoadingSpinner from "./LoadingSpinner";
import { getComments, getAuthorAvatar } from "./apiFunctions";
import CommentHistory from "./CommentHistory";

const Comment = ({
articleid,
commentCount,
setNumItems,
commentPageNumber,
}) => {
const [isLoading, setIsLoading] = useState(true);
const [comments, setComments] = useState(null);
const [authorAvatars, setAuthorAvatars] = useState({});

useEffect(() => {
setIsLoading(true);
setNumItems(null);
getComments(articleid, commentPageNumber).then((comments) => {
setComments(comments);
const authorList = [
...new Set(comments.map((comment) => comment.author)),
];
const promises = [];
authorList.forEach((author) =>
promises.push(getAuthorAvatar(author))
);
Promise.all(promises).then((data) => {
setAuthorAvatars(
data.reduce((authors, item) => {
authors[item[0]] = item[1];
return authors;
}, {})
);
setNumItems(commentCount);
setIsLoading(false);
});
});
}, [commentPageNumber]);

return isLoading ? (
<div id="commentsBox">
<LoadingSpinner id="commentLoadingSpinner" />
</div>
) : (
<CommentHistory
comments={comments}
commentCount={commentCount}
authorAvatars={authorAvatars}
/>
);
};

export default Comment;
19 changes: 19 additions & 0 deletions src/CommentCard.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { formatDate } from "./utils";

const CommentCard = ({ comment, authorAvatars }) => {
return (
<div className="commentCard">
<img
src={authorAvatars[comment.author]}
alt={comment.author}
className="commentAvatar"
/>
<p className="commentAuthor">{comment.author}</p>
<p className="commentVotes">{comment.votes} likes</p>
<p className="commentBody">{comment.body}</p>
<p className="commentDate">{formatDate(comment.created_at)}</p>
</div>
);
};

export default CommentCard;
23 changes: 23 additions & 0 deletions src/CommentHistory.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import CommentCard from "./CommentCard";

const CommentHistory = ({ comments, authorAvatars, commentCount }) => {
return comments.length === 0 ? (
<div id="commentsBox">
<h3 id="commentsTitle">No comments</h3>
</div>
) : (
<div id="commentsBox">
<h3 id="commentsTitle">Recent comments ({commentCount})</h3>
{comments.map((comment) => (
<CommentCard
comment={comment}
authorAvatars={authorAvatars}
key={comment.comment_id}
/>
))}
<div id="bottomSpace"></div>
</div>
);
};

export default CommentHistory;
40 changes: 25 additions & 15 deletions src/Footer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,42 +3,50 @@ import { useLocation } from "react-router-dom";
const Footer = ({
pageNumber,
setPageNumber,
numArticles,
commentPageNumber,
setCommentPageNumber,
numItems,
articlesPerPage,
articleWordCount,
}) => {
const { pathname: path } = useLocation();

const articleView = /\/articles\/[0-9]+/i.test(path);
const totalPages = Math.ceil(numArticles / articlesPerPage);
const totalPages = Math.ceil(numItems / articlesPerPage);
const pageNumbers = createPageNumberButtons(
pageNumber,
articleView ? commentPageNumber : pageNumber,
totalPages,
setPageNumber
setPageNumber,
setCommentPageNumber,
articleView
);

return articleView ? (
<footer id="footerBox">
<div id="wordCount">
{articleWordCount ? `${articleWordCount} words` : null}
</div>
</footer>
) : numArticles ? (
return numItems ? (
<footer id="footerBox">
<div id="pageNumberText">
<span>Page: </span>
{pageNumbers}
</div>
<div id="articleCount">
{`${numArticles} article${numArticles === 1 ? "" : "s"}`}
<div id="footerRightText">
{articleView
? articleWordCount
? `${articleWordCount} words`
: null
: `${numItems} article${numItems === 1 ? "" : "s"}`}
</div>
</footer>
) : (
<footer id="footerBox"></footer>
);
};

const createPageNumberButtons = (pageNumber, totalPages, setPageNumber) => {
const createPageNumberButtons = (
pageNumber,
totalPages,
setPageNumber,
setCommentPageNumber,
articleView
) => {
const pageNumbers = [];

let start = pageNumber - 2;
Expand All @@ -58,7 +66,9 @@ const createPageNumberButtons = (pageNumber, totalPages, setPageNumber) => {
className="brandedButton pageNumberButton"
key={n}
disabled={n === pageNumber ? true : false}
onClick={() => setPageNumber(n)}
onClick={() => {
articleView ? setCommentPageNumber(n) : setPageNumber(n);
}}
>
{n}
</button>
Expand Down
Loading

0 comments on commit aeb27dc

Please sign in to comment.