diff --git a/components/BeerById/BeerPostCommentsSection.tsx b/components/BeerById/BeerPostCommentsSection.tsx index bfeb7c9..8d67709 100644 --- a/components/BeerById/BeerPostCommentsSection.tsx +++ b/components/BeerById/BeerPostCommentsSection.tsx @@ -8,36 +8,23 @@ import { z } from 'zod'; import useBeerPostComments from '@/hooks/useBeerPostComments'; import { useRouter } from 'next/router'; import { useInView } from 'react-intersection-observer'; +import { FaArrowUp } from 'react-icons/fa'; import BeerCommentForm from './BeerCommentForm'; import CommentCardBody from './CommentCardBody'; import NoCommentsCard from './NoCommentsCard'; -import CommentLoadingCardBody from './CommentLoadingCardBody'; -import Spinner from '../ui/Spinner'; +import LoadingComponent from './LoadingComponent'; interface BeerPostCommentsSectionProps { beerPost: z.infer; } -const LoadingComponent: FC<{ length: number }> = ({ length }) => { - return ( - <> - {Array.from({ length }).map((_, i) => ( - - ))} -
- -
- - ); -}; - const BeerPostCommentsSection: FC = ({ beerPost }) => { const { user } = useContext(UserContext); const router = useRouter(); const { id } = beerPost; const pageNum = parseInt(router.query.comments_page as string, 10) || 1; - const PAGE_SIZE = 6; + const PAGE_SIZE = 4; const { comments, isLoading, mutate, setSize, size, isLoadingMore, isAtEnd } = useBeerPostComments({ @@ -46,8 +33,11 @@ const BeerPostCommentsSection: FC = ({ beerPost }) pageSize: PAGE_SIZE, }); - const { ref } = useInView({ - delay: 3000, + const { ref: lastCommentRef } = useInView({ + /** + * When the last comment comes into view, call setSize from useBeerPostComments to + * load more comments. + */ onChange: (visible) => { if (!visible || isAtEnd) return; setSize(size + 1); @@ -58,7 +48,7 @@ const BeerPostCommentsSection: FC = ({ beerPost }) return (
-
+
{user ? ( ) : ( @@ -69,47 +59,80 @@ const BeerPostCommentsSection: FC = ({ beerPost })
- {comments && !!comments.length && !isLoading && ( -
- {comments.map((comment, index) => { - const isLastComment = index === comments.length - 1; + { + /** + * If the comments are loading, show a loading component. Otherwise, show the + * comments. + */ + isLoading ? ( +
+ +
+ ) : ( + <> + {!!comments.length && ( +
+ {comments.map((comment, index) => { + const isLastComment = index === comments.length - 1; - return ( -
- + /** + * Attach a ref to the last comment in the list. When it comes into + * view, the component will call setSize to load more comments. + */ + return ( +
+ +
+ ); + })} + + { + /** + * If there are more comments to load, show a loading component with a + * skeleton loader and a loading spinner. + */ + !!isLoadingMore && ( + + ) + } + + { + /** + * If the user has scrolled to the end of the comments, show a button + * that will scroll them back to the top of the comments section. + */ + !!isAtEnd && ( +
+
+ +
+
+ ) + }
- ); - })} + )} - {!!isLoadingMore && ( -
- -
- )} - - {isAtEnd && ( -
- -
- )} -
- )} - - {!comments?.length && !isLoading && } - - {isLoading && ( -
- -
- )} + {!comments.length && } + + ) + }
); }; diff --git a/components/BeerById/LoadingComponent.tsx b/components/BeerById/LoadingComponent.tsx new file mode 100644 index 0000000..5e09a28 --- /dev/null +++ b/components/BeerById/LoadingComponent.tsx @@ -0,0 +1,22 @@ +import { FC } from 'react'; +import Spinner from '../ui/Spinner'; +import CommentLoadingCardBody from './CommentLoadingCardBody'; + +interface LoadingComponentProps { + length: number; +} + +const LoadingComponent: FC = ({ length }) => { + return ( + <> + {Array.from({ length }).map((_, i) => ( + + ))} +
+ +
+ + ); +}; + +export default LoadingComponent; diff --git a/components/ui/Spinner.tsx b/components/ui/Spinner.tsx index df69177..d42f745 100644 --- a/components/ui/Spinner.tsx +++ b/components/ui/Spinner.tsx @@ -7,17 +7,22 @@ interface SpinnerProps { const Spinner: FC = ({ size = 'md' }) => { const spinnerWidths: Record, `w-[${number}px]`> = { xs: 'w-[45px]', - sm: 'w-[60px]', - md: 'w-[100px]', - lg: 'w-[150px]', - xl: 'w-[200px]', + sm: 'w-[90px]', + md: 'w-[135px]', + lg: 'w-[180px]', + xl: 'w-[225px]', }; return ( -
+
- {beerPost.beerImages[0] && ( - {beerPost.beerImages[0].alt} - )} + + {beerPost.beerImages.map((image, index) => ( +
+ {image.alt} +
+ ))} +
-
+
diff --git a/pages/beers/index.tsx b/pages/beers/index.tsx index 6be6a80..2471037 100644 --- a/pages/beers/index.tsx +++ b/pages/beers/index.tsx @@ -9,6 +9,9 @@ import BeerCard from '@/components/BeerIndex/BeerCard'; import beerPostQueryResult from '@/services/BeerPost/schema/BeerPostQueryResult'; import Head from 'next/head'; import { z } from 'zod'; +import Link from 'next/link'; +import UserContext from '@/contexts/userContext'; +import { useContext } from 'react'; interface BeerPageProps { initialBeerPosts: z.infer[]; @@ -19,6 +22,8 @@ const BeerPage: NextPage = ({ initialBeerPosts, pageCount }) => { const router = useRouter(); const { query } = router; + const { user } = useContext(UserContext); + const pageNum = parseInt(query.page_num as string, 10) || 1; return ( @@ -28,13 +33,20 @@ const BeerPage: NextPage = ({ initialBeerPosts, pageCount }) => {
-
+

The Biergarten Index

Page {pageNum} of {pageCount}

+ {!!user && ( +
+ + Create a new beer post + +
+ )}
{initialBeerPosts.map((post) => {