diff --git a/src/App.css b/src/App.css
index 9d1052b..5bb6340 100644
--- a/src/App.css
+++ b/src/App.css
@@ -2,6 +2,7 @@
text-align: center;
width: 100vw;
height: 100vh;
+ font-family: arial;
}
* {
@@ -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;
@@ -225,7 +296,7 @@ header {
font-weight: bold;
}
-#articleCount {
+#footerRightText {
font-weight: bold;
grid-column: 3;
text-align: right;
@@ -233,14 +304,8 @@ header {
line-height: 28px;
}
-#wordCount {
- font-weight: bold;
- grid-column: 2;
- line-height: 28px;
-}
-
@media (max-width: 500px) {
- #articleCount {
+ #footerRightText {
display: none;
}
}
diff --git a/src/App.js b/src/App.js
index 7b85913..31fac5f 100644
--- a/src/App.js
+++ b/src/App.js
@@ -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);
@@ -19,21 +20,27 @@ function App() {
+
}
/>
}
+ element={
+
+ }
/>
diff --git a/src/Article.jsx b/src/Article.jsx
index 800c68f..33bf0d2 100644
--- a/src/Article.jsx
+++ b/src/Article.jsx
@@ -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);
});
@@ -36,6 +45,12 @@ const Article = ({ setArticleWordCount }) => {
{article.author}
{formatDate(article.created_at)}
{article.body}
+
);
};
diff --git a/src/ArticleList.jsx b/src/ArticleList.jsx
index f85a269..be6ed5f 100644
--- a/src/ArticleList.jsx
+++ b/src/ArticleList.jsx
@@ -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);
});
diff --git a/src/Comment.jsx b/src/Comment.jsx
new file mode 100644
index 0000000..e3b2f49
--- /dev/null
+++ b/src/Comment.jsx
@@ -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 ? (
+
+ ) : (
+
+ );
+};
+
+export default Comment;
diff --git a/src/CommentCard.jsx b/src/CommentCard.jsx
new file mode 100644
index 0000000..4b8ade9
--- /dev/null
+++ b/src/CommentCard.jsx
@@ -0,0 +1,19 @@
+import { formatDate } from "./utils";
+
+const CommentCard = ({ comment, authorAvatars }) => {
+ return (
+
+

+
{comment.author}
+
{comment.votes} likes
+
{comment.body}
+
{formatDate(comment.created_at)}
+
+ );
+};
+
+export default CommentCard;
diff --git a/src/CommentHistory.jsx b/src/CommentHistory.jsx
new file mode 100644
index 0000000..a35577c
--- /dev/null
+++ b/src/CommentHistory.jsx
@@ -0,0 +1,23 @@
+import CommentCard from "./CommentCard";
+
+const CommentHistory = ({ comments, authorAvatars, commentCount }) => {
+ return comments.length === 0 ? (
+
+ ) : (
+
+ );
+};
+
+export default CommentHistory;
diff --git a/src/Footer.jsx b/src/Footer.jsx
index c317910..0b85e2e 100644
--- a/src/Footer.jsx
+++ b/src/Footer.jsx
@@ -3,34 +3,36 @@ 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 ? (
-
- ) : numArticles ? (
+ return numItems ? (