From 1839cf79cc8f4f96a0c8fbc7f6d87754de2d7ad1 Mon Sep 17 00:00:00 2001 From: Greg Rickaby Date: Tue, 13 Feb 2024 15:23:08 -0600 Subject: [PATCH] add or level-up inline comments --- app/r/[slug]/loading.tsx | 2 +- components/BackToTop.tsx | 2 + components/BossButton.tsx | 4 +- components/Search.tsx | 103 ++++++++++++++++++++++++++------------ lib/functions.ts | 42 ++++++++++------ 5 files changed, 101 insertions(+), 52 deletions(-) diff --git a/app/r/[slug]/loading.tsx b/app/r/[slug]/loading.tsx index 3d0068b6..45246793 100644 --- a/app/r/[slug]/loading.tsx +++ b/app/r/[slug]/loading.tsx @@ -24,7 +24,7 @@ export default function Loading() { d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" > - Loading... + Loading subreddit... ) } diff --git a/components/BackToTop.tsx b/components/BackToTop.tsx index 4031a2c0..8cbe290e 100644 --- a/components/BackToTop.tsx +++ b/components/BackToTop.tsx @@ -13,6 +13,7 @@ export default function BackToTop() { useEffect(() => { // Handle scroll event. const scrollHandler = () => { + // If the user has scrolled down 200px, show the button. if (window.scrollY > 200) { setShowButton(true) } else { @@ -37,6 +38,7 @@ export default function BackToTop() { }) } + // No button? Bail. if (!showButton) { return null } diff --git a/components/BossButton.tsx b/components/BossButton.tsx index 0695f839..9e9f876b 100644 --- a/components/BossButton.tsx +++ b/components/BossButton.tsx @@ -35,11 +35,11 @@ export default function BossButton() { } } - // Add event listener. + // Add event listeners. window.addEventListener('keydown', keydownHandler) window.addEventListener('resize', resizeHandler) - // Cleanup the event listener. + // Cleanup the event listeners. return () => { window.removeEventListener('keydown', keydownHandler) window.removeEventListener('resize', resizeHandler) diff --git a/components/Search.tsx b/components/Search.tsx index 8cf934ad..2cc92129 100644 --- a/components/Search.tsx +++ b/components/Search.tsx @@ -2,9 +2,8 @@ import {fetchSearchResults} from '@/lib/actions' import {RedditSearchResponse} from '@/lib/types' -import {IconX} from '@tabler/icons-react' import Link from 'next/link' -import {usePathname, useRouter} from 'next/navigation' +import {usePathname, useRouter, useSearchParams} from 'next/navigation' import {useCallback, useEffect, useRef, useState} from 'react' import config from '@/lib/config' @@ -25,75 +24,122 @@ function useDebounce(callback: () => void, delay: number, dependencies: any[]) { * The search component. */ export default function Search() { + // Setup the router, path, and search params. const router = useRouter() const pathname = usePathname() - const initialSubreddit = pathname.split('/r/')[1] + const searchParams = useSearchParams() + + // Setup the initial subreddit, sort, and input ref. + const initialSubreddit = pathname.split('/r/')[1] || '' + const initialSort = searchParams.get('sort') || config.redditApi.sort const inputRef = useRef(null) - const [query, setQuery] = useState(initialSubreddit || '') - const [searchFilter, setSearchFilter] = useState(config.redditApi.sort) + // Setup component state. + const [query, setQuery] = useState(initialSubreddit) + const [sort, setSort] = useState(initialSort) const [results, setResults] = useState({}) const [isDrawerOpen, setIsDrawerOpen] = useState(false) const [selectedIndex, setSelectedIndex] = useState(0) + // Search input field handler. const searchInputHandler = (e: React.ChangeEvent) => { - const inputQuery = e.target.value.trim() - setQuery(inputQuery) + // Get the input value. + let inputValue = e.target.value + + // Trim the input value. + inputValue = inputValue.trim() + + // Set component state. + setQuery(inputValue) setSelectedIndex(0) - setIsDrawerOpen(!!inputQuery) + setIsDrawerOpen(!!inputValue) } - const handleFilterChange = (e: React.ChangeEvent) => { - setSearchFilter(e.target.value) - if (query.length > 0) { - router.push(`${pathname}?sort=${e.target.value}`) - } + // Sort select field handler. + const sortSelectHandler = (e: React.ChangeEvent) => { + // Set the sort value. + const sortValue = e.target.value + + // Set component state. + setSort(sortValue) + + // If there's no query, return. + if (query.length < 2) return + + // If there is a query, push the route with the sort value. + router.push(`${pathname}?sort=${e.target.value}`) } - const performSearch = useCallback(async () => { + // Setup the search query. + const searchQuery = useCallback(async () => { + // If there's no query, return. if (query.length < 2) return + + // Fetch the search results. const results = await fetchSearchResults(query) + + // Set component state. setResults(results) }, [query]) - useDebounce(performSearch, 500, [query]) + // Debounce the search query. + useDebounce(searchQuery, 500, [query]) + // Reset all component state. const resetSearch = () => { setQuery('') setResults({}) setIsDrawerOpen(false) setSelectedIndex(0) - setSearchFilter(config.redditApi.sort) + setSort(config.redditApi.sort) } + // Keyboard event handlers. useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { + // If the drawer is not open, return. if (!isDrawerOpen) return + + // Setup the item count. const itemCount = results?.data?.children?.length || 0 + // Handle the down arrow key event. if (e.key === 'ArrowDown') { + // If the selected index is the last item, set the selected index to 0. setSelectedIndex((prevIndex) => (prevIndex + 1) % itemCount) e.preventDefault() + + // Handle the up arrow key event. } else if (e.key === 'ArrowUp') { + // If the selected index is the first item, set the selected index to the last item. setSelectedIndex((prevIndex) => (prevIndex - 1 + itemCount) % itemCount) e.preventDefault() + + // Handle the enter key event. } else if (e.key === 'Enter' && itemCount > 0) { + // Get the selected result. const selectedResult = results?.data?.children[selectedIndex] + + // If the selected result exists, push the route and reset the search. if (selectedResult) { - router.push(`${selectedResult.data.url}?sort=${searchFilter}`) + router.push(`${selectedResult.data.url}?sort=${sort}`) resetSearch() } e.preventDefault() } } + // Add the event listener. window.addEventListener('keydown', handleKeyDown) + + // Cleanup the event listener. return () => window.removeEventListener('keydown', handleKeyDown) - }, [isDrawerOpen, results, selectedIndex, router, searchFilter]) + }, [isDrawerOpen, results, selectedIndex, router, sort]) + // If we're on the homepage, reset the search query. useEffect(() => { if (pathname === '/' || !initialSubreddit) { - setQuery('') + resetSearch() } else { setQuery(initialSubreddit) } @@ -117,22 +163,11 @@ export default function Search() { value={query} /> - {query.length > 0 && ( - - )} -