From 39016c93aa0c3fa5237c061204ab0f3bf2de31e2 Mon Sep 17 00:00:00 2001 From: Sriram Hariharan Date: Fri, 3 Mar 2023 23:13:31 -0600 Subject: [PATCH] infinite scroll support --- src/views/components/CourseCatalogMain.tsx | 33 ++++++++++++++++--- src/views/hooks/useInfiniteScroll.ts | 38 ++++++++++++++++++++++ 2 files changed, 67 insertions(+), 4 deletions(-) create mode 100644 src/views/hooks/useInfiniteScroll.ts diff --git a/src/views/components/CourseCatalogMain.tsx b/src/views/components/CourseCatalogMain.tsx index c80ee0b9..9bd5f6f3 100644 --- a/src/views/components/CourseCatalogMain.tsx +++ b/src/views/components/CourseCatalogMain.tsx @@ -1,7 +1,7 @@ import React, { useEffect, useMemo } from 'react'; import ReactDOM from 'react-dom'; -import { bMessenger } from 'src/shared/messages'; import { Course } from 'src/shared/types/Course'; +import useInfiniteScroll from '../hooks/useInfiniteScroll'; import { populateSearchInputs } from '../lib/courseCatalog/populateSearchInputs'; import { SiteSupport } from '../lib/getSiteSupport'; import { Button } from './common/Button/Button'; @@ -17,6 +17,13 @@ export default function CourseCatalogMain({ support }: Props) { const [rows, setRows] = React.useState([]); const [selectedCourse, setSelectedCourse] = React.useState(null); + const [shouldHighlight, setShouldHighlight] = React.useState(false); + + const isInfiniteScrollLoading = useInfiniteScroll(async () => { + console.log('infinite scroll'); + return false; + }); + useEffect(() => { populateSearchInputs(); }, []); @@ -29,24 +36,42 @@ export default function CourseCatalogMain({ support }: Props) { return (
+ {rows.map(row => ( - + ))} + {isInfiniteScrollLoading &&
Scrolling...
}
); } -const TableRow: (props: { row: HTMLTableRowElement }) => JSX.Element | null = ({ row }) => { +const TableRow: (props: { row: HTMLTableRowElement; shouldHighlight: boolean }) => JSX.Element | null = ({ + row, + shouldHighlight, +}) => { const [portalContainer, setPortalContainer] = React.useState(null); useEffect(() => { const portalContainer = document.createElement('td'); - portalContainer.setAttribute('id', 'ut-registration-plus-table-row-portal'); const lastTableCell = row.querySelector('td:last-child'); lastTableCell!.after(portalContainer); setPortalContainer(portalContainer); }, []); + useEffect(() => { + console.log('shouldHighlight', shouldHighlight); + // make the color of the row change when the button is clicked + if (shouldHighlight) { + row.querySelectorAll('td').forEach(td => { + td.style.color = 'red'; + }); + } else { + row.querySelectorAll('td').forEach(td => { + td.style.color = ''; + }); + } + }, [shouldHighlight, row]); + if (!portalContainer) { return null; } diff --git a/src/views/hooks/useInfiniteScroll.ts b/src/views/hooks/useInfiniteScroll.ts new file mode 100644 index 00000000..333591ef --- /dev/null +++ b/src/views/hooks/useInfiniteScroll.ts @@ -0,0 +1,38 @@ +import { useState, useEffect } from 'react'; +import { sleep } from 'src/shared/util/time'; + +/** + * Hook to execute a callback when the user scrolls to the bottom of the page + * @param callback the function to be called when the user scrolls to the bottom of the page + * @returns isLoading boolean to indicate if the callback is currently being executed + */ + +export default function useInfiniteScroll(callback: () => Promise): boolean { + const [isLoading, setIsLoading] = useState(false); + + useEffect(() => { + window.addEventListener('scroll', isScrolling); + return () => window.removeEventListener('scroll', isScrolling); + }, []); + + useEffect(() => { + if (!isLoading) return; + callback().then(isFinished => { + if (!isFinished) { + sleep(1000).then(() => { + setIsLoading(false); + }); + } + }); + }, [isLoading]); + + function isScrolling() { + if ( + window.innerHeight + document.documentElement.scrollTop !== document.documentElement.offsetHeight || + isLoading + ) + return; + setIsLoading(true); + } + return isLoading; +}