diff --git a/src/pages/extensions.tsx b/src/pages/extensions.tsx index 77d41f8..1d574d4 100644 --- a/src/pages/extensions.tsx +++ b/src/pages/extensions.tsx @@ -4,7 +4,6 @@ import Layout from "../components/Layout"; import getAppProps, { AppProps } from "../components/WithAppProps"; import Link from "next/link"; import { AwesomeArcadeExtensionsList } from "@/components/AwesomeArcadeList"; -import { debounce } from "@/scripts/Utils/Timers"; import { AnalyticEvents } from "@/components/Analytics"; import { useSession } from "next-auth/react"; import Tippy from "@tippyjs/react"; @@ -27,7 +26,9 @@ export function Extensions({ }: ExtensionsProps): React.ReactNode { const { data: session } = useSession(); - const [search, setSearch] = React.useState(""); + const [disableSearch, setDisableSearch] = React.useState(false); + const [searchParamsChanged, setSearchParamsChanged] = React.useState(false); + const [searchQuery, setSearchQuery] = React.useState(""); const [showJSOnlyExts, setShowJSOnlyExts] = React.useState(false); const [filteredList, setFilteredList] = React.useState(list); const [resultCount, setResultCount] = React.useState( @@ -40,7 +41,7 @@ export function Extensions({ React.useEffect(() => { const q = new URLSearchParams(window.location.search).get(searchParam); if (q !== null) { - setSearch(q); + setSearchQuery(q); } const showJSOnly = new URLSearchParams(window.location.search).get( showJSOnlyParam, @@ -48,76 +49,75 @@ export function Extensions({ if (showJSOnly !== null) { setShowJSOnlyExts(stringToBool(showJSOnly)); } + runSearch(q, showJSOnly != null ? stringToBool(showJSOnly) : null); if (window.location.hash.length > 0) { smoothScrollToID(window.location.hash.replace("#", "")); } + // eslint-disable-next-line }, []); - React.useEffect(() => { - const url = new URL(window.location.toString()); - if (search === "") { - url.searchParams.delete(searchParam); - } else { - url.searchParams.set(searchParam, search); - } - if (showJSOnlyExts) { - url.searchParams.set(showJSOnlyParam, "true"); - } else { - url.searchParams.delete(showJSOnlyParam); - } - window.history.replaceState({}, "", url.toString()); - }, [search, showJSOnlyExts]); - - // React.useEffect(() => { - // document.addEventListener("keydown", (e) => { - // if (e.keyCode === 114 || ((e.ctrlKey || e.metaKey) && e.keyCode === 70)) { - // e.preventDefault(); - // } - // if ((e.ctrlKey || e.metaKey) && e.code == "KeyF") { - // setTimeout(() => { - // console.log("FOCUS FIND"); - // const searchBar = getElement("searchBar") as HTMLInputElement; - // searchBar.focus(); - // }, 1000); - // } - // }); - // }, []); - - React.useEffect(() => { - if (search.length > 0 || showJSOnlyExts) { - const filtered = structuredClone(list); - let extCount = 0; - const group = filtered; - const normalizeString = (s: string): string => { - return s.trim().toLowerCase(); - }; - const normalizedSearch = normalizeString(search); - for (let i = group.length - 1; i >= 0; i--) { - const ext = group[i]; - if ( - !( - normalizeString(ext.repo).includes(normalizedSearch) || - normalizeString(ext.url).includes(normalizedSearch) || - // normalizeString(ext.description).includes(normalizedSearch) || - normalizeString(ext.author).includes(normalizedSearch) - ) || - (!showJSOnlyExts && ext.javascriptOnly) - ) { - group.splice(i, 1); + const runSearch = ( + query: string | null = null, + showJSOnly: boolean | null = null, + ) => { + setDisableSearch(true); + setTimeout(() => { + const q = query ?? searchQuery; + const showJS = showJSOnly ?? showJSOnlyExts; + setSearchQuery(q); + setShowJSOnlyExts(showJS); + if (q.length > 0 || showJS) { + const filtered = structuredClone(list); + let extCount = 0; + const group = filtered; + const normalizeString = (s: string): string => { + return s.trim().toLowerCase(); + }; + const normalizedSearch = normalizeString(q); + for (let i = group.length - 1; i >= 0; i--) { + const ext = group[i]; + if ( + !( + normalizeString(ext.repo).includes(normalizedSearch) || + normalizeString(ext.url).includes(normalizedSearch) || + normalizeString( + JSON.stringify(ext["description"]["children"]), + ).includes(normalizedSearch) || + normalizeString(ext.author).includes(normalizedSearch) + ) || + (!showJS && ext.javascriptOnly) + ) { + group.splice(i, 1); + } } + extCount += group.length; + setFilteredList(filtered); + setResultCount(extCount); + } else { + setFilteredList( + list.filter((ext) => { + return !ext.javascriptOnly; + }), + ); + setResultCount(undefined); } - extCount += group.length; - setFilteredList(filtered); - setResultCount(extCount); - } else { - setFilteredList( - list.filter((ext) => { - return !ext.javascriptOnly; - }), - ); - setResultCount(undefined); - } - }, [search, showJSOnlyExts, list]); + const url = new URL(window.location.toString()); + if (q === "") { + url.searchParams.delete(searchParam); + } else { + url.searchParams.set(searchParam, q); + } + if (showJS) { + url.searchParams.set(showJSOnlyParam, "true"); + } else { + url.searchParams.delete(showJSOnlyParam); + } + window.history.replaceState({}, "", url.toString()); + AnalyticEvents.sendSearch(q); + setDisableSearch(false); + setSearchParamsChanged(false); + }); + }; return ( - + { - const v = event.target.value; - setSearch(v); - debounce( - "extensionSearchChange", - () => { - AnalyticEvents.sendSearch(v); - }, - 1000, - ); + placeholder="Search extensions by author, name, description, or URL!" + // disabled={disableSearch} + value={searchQuery} + onChange={(e) => { + setSearchQuery(e.target.value); + setSearchParamsChanged(true); + }} + onKeyDown={(e) => { + if (e.key === "Enter") { + runSearch(); + e.currentTarget.focus(); + } }} aria-label="Search query" /> +
+ +
{ setShowJSOnlyExts(e.target.checked); + setSearchParamsChanged(true); }} id="showJSOnlyExtsCheckInput" /> diff --git a/src/pages/tools.tsx b/src/pages/tools.tsx index ad16a65..126e02c 100644 --- a/src/pages/tools.tsx +++ b/src/pages/tools.tsx @@ -4,7 +4,6 @@ import getAppProps, { AppProps } from "../components/WithAppProps"; import Link from "next/link"; import { promises as fs } from "fs"; import { AwesomeArcadeToolsList } from "@/components/AwesomeArcadeList"; -import { debounce } from "@/scripts/Utils/Timers"; import { AnalyticEvents } from "@/components/Analytics"; import Tippy from "@tippyjs/react"; import { useSession } from "next-auth/react"; @@ -24,7 +23,9 @@ type ToolsProps = { export function Tools({ appProps, list }: ToolsProps): React.ReactNode { const { data: session } = useSession(); - const [search, setSearch] = React.useState(""); + const [disableSearch, setDisableSearch] = React.useState(false); + const [searchParamsChanged, setSearchParamsChanged] = React.useState(false); + const [searchQuery, setSearchQuery] = React.useState(""); const [showNotWebsiteTools, setShowNotWebsiteTools] = React.useState(false); const [filteredList, setFilteredList] = React.useState(list); const [resultCount, setResultCount] = React.useState( @@ -37,7 +38,7 @@ export function Tools({ appProps, list }: ToolsProps): React.ReactNode { React.useEffect(() => { const q = new URLSearchParams(window.location.search).get(searchParam); if (q !== null) { - setSearch(q); + setSearchQuery(q); } const showNotWebsite = new URLSearchParams(window.location.search).get( showNotWebsiteParam, @@ -45,61 +46,73 @@ export function Tools({ appProps, list }: ToolsProps): React.ReactNode { if (showNotWebsite !== null) { setShowNotWebsiteTools(stringToBool(showNotWebsite)); } + runSearch(q, showNotWebsite != null ? stringToBool(showNotWebsite) : null); if (window.location.hash.length > 0) { smoothScrollToID(window.location.hash.replace("#", "")); } + // eslint-disable-next-line }, []); - React.useEffect(() => { - const url = new URL(window.location.toString()); - if (search === "") { - url.searchParams.delete(searchParam); - } else { - url.searchParams.set(searchParam, search); - } - if (showNotWebsiteTools) { - url.searchParams.set(showNotWebsiteParam, "true"); - } else { - url.searchParams.delete(showNotWebsiteParam); - } - window.history.replaceState({}, "", url.toString()); - }, [search, showNotWebsiteTools]); - - React.useEffect(() => { - if (search.length > 0 || showNotWebsiteTools) { - const filtered = structuredClone(list); - let toolCount = 0; - const group = filtered; - const normalizeString = (s: string): string => { - return s.trim().toLowerCase(); - }; - const normalizedSearch = normalizeString(search); - for (let i = group.length - 1; i >= 0; i--) { - const tool = group[i]; - if ( - !( - normalizeString(tool.repo).includes(normalizedSearch) || - normalizeString(tool.url).includes(normalizedSearch) || - // normalizeString(tool.description).includes(normalizedSearch) || - normalizeString(tool.author).includes(normalizedSearch) - ) || - (!showNotWebsiteTools && tool.notAWebsite) - ) { - group.splice(i, 1); + const runSearch = ( + query: string | null = null, + showNotWebsites: boolean | null = null, + ) => { + setDisableSearch(true); + setTimeout(() => { + const q = query ?? searchQuery; + const showNon = showNotWebsites ?? showNotWebsiteTools; + if (q.length > 0 || showNon) { + const filtered = structuredClone(list); + let toolCount = 0; + const group = filtered; + const normalizeString = (s: string): string => { + return s.trim().toLowerCase(); + }; + const normalizedSearch = normalizeString(q); + for (let i = group.length - 1; i >= 0; i--) { + const tool = group[i]; + if ( + !( + normalizeString(tool.repo).includes(normalizedSearch) || + normalizeString(tool.url).includes(normalizedSearch) || + normalizeString( + JSON.stringify(tool["description"]["children"]), + ).includes(normalizedSearch) || + normalizeString(tool.author).includes(normalizedSearch) + ) || + (!showNon && tool.notAWebsite) + ) { + group.splice(i, 1); + } } + toolCount += group.length; + setFilteredList(filtered); + setResultCount(toolCount); + } else { + setFilteredList( + list.filter((tool) => { + return !tool.notAWebsite; + }), + ); + setResultCount(undefined); } - toolCount += group.length; - setFilteredList(filtered); - setResultCount(toolCount); - } else { - setFilteredList( - list.filter((tool) => { - return !tool.notAWebsite; - }), - ); - setResultCount(undefined); - } - }, [search, showNotWebsiteTools, list]); + const url = new URL(window.location.toString()); + if (q === "") { + url.searchParams.delete(searchParam); + } else { + url.searchParams.set(searchParam, q); + } + if (showNon) { + url.searchParams.set(showNotWebsiteParam, "true"); + } else { + url.searchParams.delete(showNotWebsiteParam); + } + window.history.replaceState({}, "", url.toString()); + AnalyticEvents.sendSearch(q); + setDisableSearch(false); + setSearchParamsChanged(false); + }); + }; return ( - + { - const v = event.target.value; - setSearch(v); - debounce( - "toolSearchChange", - () => { - AnalyticEvents.sendSearch(v); - }, - 1000, - ); + setSearchQuery(event.target.value); + setSearchParamsChanged(true); + }} + onKeyDown={(e) => { + if (e.key === "Enter") { + runSearch(); + e.currentTarget.focus(); + } }} aria-label="Search query" />
+
+ +
{ setShowNotWebsiteTools(e.target.checked); + setSearchParamsChanged(true); }} id="showNotWebsiteToolsCheckInput" />