diff --git a/checkers-app/src/components/common/BackButton.tsx b/checkers-app/src/components/common/BackButton.tsx index cac81b07..faea16f5 100644 --- a/checkers-app/src/components/common/BackButton.tsx +++ b/checkers-app/src/components/common/BackButton.tsx @@ -7,7 +7,7 @@ export function BackButton() { const regex = /^\/messages\/[^/]+\/voteRequests\/[^/]+\/?$/; function onClick() { if (regex.test(location.pathname)) { - navigate("/votes"); + navigate("/votes", { state: location.state }); //for resumption of old position } else { navigate(-1); } diff --git a/checkers-app/src/components/common/Layout.tsx b/checkers-app/src/components/common/Layout.tsx index b272df60..f425d6ff 100644 --- a/checkers-app/src/components/common/Layout.tsx +++ b/checkers-app/src/components/common/Layout.tsx @@ -14,7 +14,7 @@ export default function Layout({ showMenu = false, }: LayoutProps) { return ( -
+
{/*
*/}
+ {/* {pageHeader}
*/} -
{children}
+
{children}
); diff --git a/checkers-app/src/components/myvotes/MessageCard.tsx b/checkers-app/src/components/myvotes/MessageCard.tsx index 67be1bbb..f706e5d9 100644 --- a/checkers-app/src/components/myvotes/MessageCard.tsx +++ b/checkers-app/src/components/myvotes/MessageCard.tsx @@ -6,6 +6,7 @@ import "./MessageCard.css"; interface MessageCardProps { voteSummary: VoteSummary; status: string; + scrollPosition: number; } type ColourMap = { @@ -71,13 +72,14 @@ export default function MessageCard(props: MessageCardProps) { firestorePath, } = props.voteSummary; const status = props.status; + const scrollPosition = props.scrollPosition; // const colour: string = colours[category]; const navigate = useNavigate(); const dateString = dateToDateString(new Date(createdTimestamp)); // If the message is PENDING, clicking the button should go to the voting page - const viewVote = (firestorePath: string) => { - navigate(`/${firestorePath}`); + const viewVote = (firestorePath: string, status: string, scrollPosition: number) => { + navigate(`/${firestorePath}`, {state: {status: status, scrollPosition: scrollPosition}}); }; const textStyle = "font-normal"; //add bold in future @@ -128,7 +130,7 @@ export default function MessageCard(props: MessageCardProps) { return (
viewVote(firestorePath)} + onClick={() => viewVote(firestorePath, status, scrollPosition)} > {/* Coloured dot if needs review*/} diff --git a/checkers-app/src/components/myvotes/MessagesDisplay.tsx b/checkers-app/src/components/myvotes/MessagesDisplay.tsx index 93d28145..1be8f91d 100644 --- a/checkers-app/src/components/myvotes/MessagesDisplay.tsx +++ b/checkers-app/src/components/myvotes/MessagesDisplay.tsx @@ -16,100 +16,148 @@ Idea: - Have a nested component for each button */ -import { useState, useEffect, FC } from "react"; +interface MessagesDisplayProps { + status: "pending" | "voted"; + scrollPosition: number; +} + +import { useState, useEffect, FC, useCallback, useRef } from "react"; import { useUser } from "../../providers/UserContext"; -import Loading from "../common/Loading"; import MessageCard from "./MessageCard"; import { Typography } from "@material-tailwind/react"; import { getCheckerVotes } from "../../services/api"; import { VoteSummary, VoteSummaryApiResponse } from "../../types"; -import Pagination from "./Pagination"; // Make sure to create this component -const MessagesDisplay: FC = () => { +const MessagesDisplay: FC = ({ + status, + scrollPosition, +}) => { const { checkerDetails } = useUser(); const [isLoading, setIsLoading] = useState(false); const [votes, setVotes] = useState([]); const [lastPath, setLastPath] = useState(null); - const [activeTab, setActiveTab] = useState<"pending" | "voted">("pending"); - const [currentPage, setCurrentPage] = useState(1); + const [activeTab, setActiveTab] = useState<"pending" | "voted">(status); const [totalPages, setTotalPages] = useState(1); const [error, setError] = useState(""); + const [page, setPage] = useState(1); + const [scrollY, setScrollY] = useState(0); + + const scrollRef = useRef(null); + + // Scroll Functions + const handleScroll = () => { + const scrollY = window.scrollY; + setScrollY(scrollY); + }; useEffect(() => { - const fetchMessages = async () => { - setIsLoading(true); - try { - if (!checkerDetails.checkerId) { - throw new Error("Checker Id missing."); - } - const response: VoteSummaryApiResponse = await getCheckerVotes( - checkerDetails.checkerId, - activeTab.toLowerCase(), - 5, - lastPath - ); - // Assuming your API correctly maps to the ApiResponse interface - if (response.votes) { - setVotes(response.votes); - } - setTotalPages(response.totalPages); - setLastPath(response.lastPath); - setIsLoading(false); - } catch (err) { - setError("Failed to fetch messages"); - setIsLoading(false); - } + window.addEventListener("scroll", handleScroll); + return () => { + window.removeEventListener("scroll", handleScroll); }; + }, []); + + useEffect(() => { + if (scrollY >= scrollPosition) { + return; + } else if (scrollPosition !== 0) { + setTimeout(() => { + window.scrollTo({ + top: scrollPosition, + behavior: "smooth", + }); + }, 200); + } + }, [votes]); + + const fetchMessages = async () => { + setIsLoading(true); + try { + if (!checkerDetails.checkerId) { + throw new Error("Checker Id missing."); + } + const response: VoteSummaryApiResponse = await getCheckerVotes( + checkerDetails.checkerId, + activeTab.toLowerCase(), + 10, + lastPath + ); + if (response.votes) { + setVotes((prevVotes) => [...prevVotes, ...response.votes]); + } + setTotalPages(response.totalPages); + setLastPath(response.lastPath); + setIsLoading(false); + } catch (err) { + setError("Failed to fetch messages"); + setIsLoading(false); + } + }; + + useEffect(() => { if (checkerDetails.checkerId) { fetchMessages(); } - }, [checkerDetails.checkerId, activeTab, currentPage]); + }, [checkerDetails.checkerId, activeTab, page]); const handleTabChange = (tab: "pending" | "voted") => { + setVotes([]); setActiveTab(tab); handlePageChange(1); // Reset to the first page whenever the tab changes }; + const observer = useRef(null); + // Function to use the Intersection Observer API + const lastMessageElementRef = useCallback( + (node: any) => { + if (isLoading) return; + if (observer.current) observer.current.disconnect(); + + observer.current = new IntersectionObserver((entries) => { + if (entries[0].isIntersecting && page !== totalPages) { + setPage((prevPage) => prevPage + 1); + } + }); + if (node) observer.current.observe(node); + }, + [isLoading] + ); + // Function to handle page change from the Pagination component const handlePageChange = (page: number) => { - setCurrentPage(page); + setPage(page); if (page === 1) { setLastPath(null); } }; - if (isLoading) { - return ; - } - return (
-
- - +
+
+ + +
-
+
{error &&
{error}
} {!error && votes.length === 0 && (
@@ -118,18 +166,18 @@ const MessagesDisplay: FC = () => { )} {!error && votes.map((voteSummary, index) => ( -
- +
+
))}
- {!error && votes.length > 0 && ( - - )}
); }; diff --git a/checkers-app/src/components/myvotes/Pagination.tsx b/checkers-app/src/components/myvotes/Pagination.tsx deleted file mode 100644 index 0a128be0..00000000 --- a/checkers-app/src/components/myvotes/Pagination.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import { FC } from "react"; - -interface PaginationProps { - currentPage: number; - totalPages: number; - onPageChange: (page: number) => void; -} - -const Pagination: FC = ({ - currentPage, - totalPages, - onPageChange, -}) => { - const pageNumbers: number[] = []; - - // Determine page numbers to display - for (let i = 1; i <= totalPages; i++) { - pageNumbers.push(i); - } - - // Handle page change - const handlePageChange = (pageNumber: number) => { - onPageChange(pageNumber); - }; - - if (totalPages <= 1) return null; // Don't display pagination for single page - - return ( -
- - {/* - {pageNumbers.map((number) => ( - - ))} */} - - {/* */} -
- ); -}; - -export default Pagination; diff --git a/checkers-app/src/components/myvotes/index.tsx b/checkers-app/src/components/myvotes/index.tsx index 1f11e0a0..f8ea06b0 100644 --- a/checkers-app/src/components/myvotes/index.tsx +++ b/checkers-app/src/components/myvotes/index.tsx @@ -1,10 +1,26 @@ import MessagesDisplay from "./MessagesDisplay"; +// import MessagesDisplayTest from "./MessagesDisplayTest"; +import { useState, useEffect } from "react"; +import { useLocation } from "react-router-dom"; export default function MyVotes() { + const [activeTab, setActiveTab] = useState<"pending" | "voted">("pending"); + const location = useLocation(); + + useEffect(() => { + console.log(location) + if (location.state) { + if (location.state.status) { + setActiveTab(location.state.status) + } + } + }, [location, activeTab]) + return (
- + + {/* */}
); diff --git a/functions/src/definitions/api/handlers/getCheckerVotes.ts b/functions/src/definitions/api/handlers/getCheckerVotes.ts index 29eb1ab3..055f04d6 100644 --- a/functions/src/definitions/api/handlers/getCheckerVotes.ts +++ b/functions/src/definitions/api/handlers/getCheckerVotes.ts @@ -151,6 +151,7 @@ const getCheckerVotesHandler = async (req: Request, res: Response) => { lastPath: lastVotePath, totalPages, } + return res.status(200).send(response) } catch (error) { logger.error("Error fetching documents: ", error)