From be7565de4f89d70fe640c2fd740d82e95d8d52fd Mon Sep 17 00:00:00 2001 From: Cyrus Yiu Date: Mon, 21 Aug 2023 16:33:27 -0400 Subject: [PATCH] Move home to old page --- .../Navbar/ThemePicker/themePicker.tsx | 22 ++ src/pages/index.tsx | 253 +---------------- src/pages/old.tsx | 261 ++++++++++++++++++ 3 files changed, 291 insertions(+), 245 deletions(-) create mode 100644 src/pages/old.tsx diff --git a/src/components/Navbar/ThemePicker/themePicker.tsx b/src/components/Navbar/ThemePicker/themePicker.tsx index ce07891..0583ec3 100644 --- a/src/components/Navbar/ThemePicker/themePicker.tsx +++ b/src/components/Navbar/ThemePicker/themePicker.tsx @@ -183,6 +183,28 @@ export function ThemeProxy(): JSX.Element { window.localStorage.setItem("themeUsed", actualTheme); }, [actualTheme, loadedPreferredTheme]); + const [_, setTheme] = React.useState<"dark" | "light">("light"); + + function onThemeChange(event: CustomEvent<"Dark" | "Light">) { + setTheme(event.detail.toLowerCase() as "dark" | "light"); + } + + React.useEffect(() => { + let theme = window.localStorage.getItem("themeUsed"); + if (theme === null) { + theme = "light"; + } else { + theme = theme.toLowerCase(); + } + + window.document.documentElement.addEventListener( + "themeused", + onThemeChange + ); + + setTheme(theme as "dark" | "light"); + }, []); + return <>; } diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 1fadecf..ff31361 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -1,238 +1,22 @@ import React from "react"; import { promises as fs } from "fs"; -import path from "path"; import Layout from "../components/Layout"; import getAppProps, { AppProps } from "../components/WithAppProps"; import generateSiteWebmanifest from "../scripts/Utils/SiteWebmanifest/manifest"; -import AwesomeArcadeExtensionList from "../components/AwesomeArcadeExtensionList"; -import parseExtensionXML, { - Extension, - ExtensionList, - Tool, -} from "@/scripts/Utils/ParseExtensionsXML"; -import { smoothScrollToID } from "@/components/AwesomeArcadeExtensionList/linkableHeader"; -import { debounce } from "@/scripts/Utils/Timers"; -import { AnalyticEvents } from "@/components/Analytics"; -import { ClickCountContext } from "@/components/contexts"; -import Tippy from "@tippyjs/react"; +import Link from "next/link"; const pageName = "Home"; -type HomeProps = { appProps: AppProps; list: ExtensionList }; - -export type ClickCountListing = { [repo: string]: number }; - -declare global { - interface HTMLElementEventMap { - clickrepo: CustomEvent; - repoclickcountchange: CustomEvent<{ - repo: string; - count: string | undefined; - }>; - } -} - -export function Home({ appProps, list }: HomeProps): JSX.Element { - const [_, setTheme] = React.useState<"dark" | "light">("light"); - - function onThemeChange(event: CustomEvent<"Dark" | "Light">) { - setTheme(event.detail.toLowerCase() as "dark" | "light"); - } - - React.useEffect(() => { - let theme = window.localStorage.getItem("themeUsed"); - if (theme === null) { - theme = "light"; - } else { - theme = theme.toLowerCase(); - } - - window.document.documentElement.addEventListener( - "themeused", - onThemeChange - ); - - setTheme(theme as "dark" | "light"); - }, []); - - const [search, setSearch] = React.useState(""); - const [filteredList, setFilteredList] = React.useState(list); - const [resultCount, setResultCount] = React.useState< - { extensions: number; tools: number } | undefined - >(undefined); - - const searchParam = "q"; - - React.useEffect(() => { - const q = new URLSearchParams(window.location.search).get(searchParam); - if (q !== null) { - setSearch(q); - } - if (window.location.hash.length > 0) { - smoothScrollToID(window.location.hash.replace("#", "")); - } - }, []); - - React.useEffect(() => { - const url = new URL(window.location.toString()); - if (search === "") { - url.searchParams.delete(searchParam); - } else { - url.searchParams.set(searchParam, search); - } - window.history.replaceState({}, "", url.toString()); - }, [search]); - - React.useEffect(() => { - if (search.length > 0) { - const filtered = structuredClone(list); - let extCount = 0; - let toolCount = 0; - for (const group of Object.values(filtered)) { - for (let i = group.length - 1; i >= 0; i--) { - if ( - !group[i].repo - .trim() - .toLowerCase() - .includes(search.trim().toLowerCase()) - ) { - group.splice(i, 1); - } - } - if (group.length > 0) { - switch (group[0].type) { - case "Extension": { - extCount += group.length; - break; - } - case "Tool": { - toolCount += group.length; - break; - } - } - } - } - setFilteredList(filtered); - setResultCount({ - extensions: extCount, - tools: toolCount, - }); - } else { - setFilteredList(list); - setResultCount(undefined); - } - }, [search, list]); - - const [clickCounts, setClickCounts] = React.useState< - ClickCountListing | undefined - >(undefined); - - const refreshAllClickCounts = () => { - console.log("Refreshing click counts"); - fetch(`${window.location.origin}/api/all/`) - .then((response) => { - return response.json(); - }) - .then((json) => { - setClickCounts(json); - console.log( - `Successfully refreshed ${Object.keys(json).length} click counts` - ); - }) - .catch((error) => { - setClickCounts(undefined); - console.error(`Error fetching click counts: ${error}`); - }); - }; - - const refreshCountsRef = React.useRef(undefined); - const REFRESH_CLICK_COUNT_PERIOD = 1000 * 60 * 2; - - React.useEffect(() => { - refreshAllClickCounts(); - - refreshCountsRef.current = window.setInterval( - refreshAllClickCounts, - REFRESH_CLICK_COUNT_PERIOD - ); - return () => { - window.clearInterval(refreshCountsRef.current); - }; - }, []); // eslint-disable-line - - // I hate closures - const clickCountRef = React.useRef(undefined); - - React.useEffect(() => { - clickCountRef.current = clickCounts; - }, [clickCounts]); - - const logRepoClickFromEvent = (event: CustomEvent) => { - const repo = event.detail; - fetch(`${window.location.origin}/api/click?repo=${repo}`) - .then((response) => { - return response.json(); - }) - .then((json) => { - const repo = Object.keys(json)[0]; - if (clickCountRef.current != undefined) { - const counts = structuredClone(clickCountRef.current); - counts[repo] = json[repo]; - setClickCounts(counts); - } else { - throw new Error("Click count cache is undefined"); - } - }) - .catch((error) => { - setClickCounts(undefined); - console.error( - `Error refreshing individual click count for ${repo}: ${error}` - ); - }); - }; - - React.useEffect(() => { - window.document.documentElement.addEventListener( - "clickrepo", - logRepoClickFromEvent - ); - return () => { - window.document.documentElement.removeEventListener( - "clickrepo", - logRepoClickFromEvent - ); - }; - }, []); // eslint-disable-line +type HomeProps = { appProps: AppProps }; +export function Home({ appProps }: HomeProps): JSX.Element { return ( - { - const v = event.target.value; - setSearch(v); - debounce( - "searchChange", - () => { - AnalyticEvents.sendSearch(v); - }, - 1000 - ); - }} - aria-label="Search query" - /> - - } + description="" + keywords="" >

Welcome to Awesome Arcade Extensions

@@ -243,18 +27,9 @@ export function Home({ appProps, list }: HomeProps): JSX.Element { Please note that this website is not developed, affiliated, or endorsed by Microsoft, the owner of MakeCode Arcade.

-
- {resultCount != undefined ? ( -

- Found {resultCount.extensions} extension - {resultCount.extensions !== 1 ? "s" : ""} and {resultCount.tools}{" "} - tool{resultCount.tools !== 1 ? "s" : ""}. -

- ) : undefined} - - - -
+

+ You can find the old home page here. +

); } @@ -267,21 +42,9 @@ export async function getStaticProps(): Promise<{ await generateSiteWebmanifest() ); - const list = await parseExtensionXML( - ( - await fs.readFile(path.resolve(process.cwd(), "src", "extensions.xml")) - ).toString() - ); - - await fs.writeFile( - "./public/extensions.json", - JSON.stringify(list, undefined, 2) - ); - return { props: { appProps: await getAppProps(), - list, }, }; } diff --git a/src/pages/old.tsx b/src/pages/old.tsx new file mode 100644 index 0000000..46ad20e --- /dev/null +++ b/src/pages/old.tsx @@ -0,0 +1,261 @@ +import React from "react"; +import Layout from "../components/Layout"; +import getAppProps, { AppProps } from "@/components/WithAppProps"; +import AwesomeArcadeExtensionList from "../components/AwesomeArcadeExtensionList"; +import parseExtensionXML, { + Extension, + ExtensionList, + Tool, +} from "@/scripts/Utils/ParseExtensionsXML"; +import { smoothScrollToID } from "@/components/AwesomeArcadeExtensionList/linkableHeader"; +import { debounce } from "@/scripts/Utils/Timers"; +import { AnalyticEvents } from "@/components/Analytics"; +import { ClickCountContext } from "@/components/contexts"; +import Tippy from "@tippyjs/react"; +import { promises as fs } from "fs"; +import path from "path"; + +const pageName = "Old home"; + +type OldHomeProps = { appProps: AppProps; list: ExtensionList }; + +export type ClickCountListing = { [repo: string]: number }; + +declare global { + interface HTMLElementEventMap { + clickrepo: CustomEvent; + repoclickcountchange: CustomEvent<{ + repo: string; + count: string | undefined; + }>; + } +} + +export function OldHome({ appProps, list }: OldHomeProps): JSX.Element { + const [search, setSearch] = React.useState(""); + const [filteredList, setFilteredList] = React.useState(list); + const [resultCount, setResultCount] = React.useState< + { extensions: number; tools: number } | undefined + >(undefined); + + const searchParam = "q"; + + React.useEffect(() => { + const q = new URLSearchParams(window.location.search).get(searchParam); + if (q !== null) { + setSearch(q); + } + if (window.location.hash.length > 0) { + smoothScrollToID(window.location.hash.replace("#", "")); + } + }, []); + + React.useEffect(() => { + const url = new URL(window.location.toString()); + if (search === "") { + url.searchParams.delete(searchParam); + } else { + url.searchParams.set(searchParam, search); + } + window.history.replaceState({}, "", url.toString()); + }, [search]); + + React.useEffect(() => { + if (search.length > 0) { + const filtered = structuredClone(list); + let extCount = 0; + let toolCount = 0; + for (const group of Object.values(filtered)) { + for (let i = group.length - 1; i >= 0; i--) { + if ( + !group[i].repo + .trim() + .toLowerCase() + .includes(search.trim().toLowerCase()) + ) { + group.splice(i, 1); + } + } + if (group.length > 0) { + switch (group[0].type) { + case "Extension": { + extCount += group.length; + break; + } + case "Tool": { + toolCount += group.length; + break; + } + } + } + } + setFilteredList(filtered); + setResultCount({ + extensions: extCount, + tools: toolCount, + }); + } else { + setFilteredList(list); + setResultCount(undefined); + } + }, [search, list]); + + const [clickCounts, setClickCounts] = React.useState< + ClickCountListing | undefined + >(undefined); + + const refreshAllClickCounts = () => { + console.log("Refreshing click counts"); + fetch(`${window.location.origin}/api/all/`) + .then((response) => { + return response.json(); + }) + .then((json) => { + setClickCounts(json); + console.log( + `Successfully refreshed ${Object.keys(json).length} click counts` + ); + }) + .catch((error) => { + setClickCounts(undefined); + console.error(`Error fetching click counts: ${error}`); + }); + }; + + const refreshCountsRef = React.useRef(undefined); + const REFRESH_CLICK_COUNT_PERIOD = 1000 * 60 * 2; + + React.useEffect(() => { + refreshAllClickCounts(); + + refreshCountsRef.current = window.setInterval( + refreshAllClickCounts, + REFRESH_CLICK_COUNT_PERIOD + ); + return () => { + window.clearInterval(refreshCountsRef.current); + }; + }, []); // eslint-disable-line + + // I hate closures + const clickCountRef = React.useRef(undefined); + + React.useEffect(() => { + clickCountRef.current = clickCounts; + }, [clickCounts]); + + const logRepoClickFromEvent = (event: CustomEvent) => { + const repo = event.detail; + fetch(`${window.location.origin}/api/click?repo=${repo}`) + .then((response) => { + return response.json(); + }) + .then((json) => { + const repo = Object.keys(json)[0]; + if (clickCountRef.current != undefined) { + const counts = structuredClone(clickCountRef.current); + counts[repo] = json[repo]; + setClickCounts(counts); + } else { + throw new Error("Click count cache is undefined"); + } + }) + .catch((error) => { + setClickCounts(undefined); + console.error( + `Error refreshing individual click count for ${repo}: ${error}` + ); + }); + }; + + React.useEffect(() => { + window.document.documentElement.addEventListener( + "clickrepo", + logRepoClickFromEvent + ); + return () => { + window.document.documentElement.removeEventListener( + "clickrepo", + logRepoClickFromEvent + ); + }; + }, []); // eslint-disable-line + + return ( + + { + const v = event.target.value; + setSearch(v); + debounce( + "searchChange", + () => { + AnalyticEvents.sendSearch(v); + }, + 1000 + ); + }} + aria-label="Search query" + /> + + } + > +

Welcome to Awesome Arcade Extensions

+

+ This is a list of MakeCode Arcade extensions that I find super useful + (or just plain cool) in my projects. +

+

+ Please note that this website is not developed, affiliated, or endorsed + by Microsoft, the owner of MakeCode Arcade. +

+
+ {resultCount != undefined ? ( +

+ Found {resultCount.extensions} extension + {resultCount.extensions !== 1 ? "s" : ""} and {resultCount.tools}{" "} + tool{resultCount.tools !== 1 ? "s" : ""}. +

+ ) : undefined} + + + +
+
+ ); +} + +export async function getStaticProps(): Promise<{ + props: OldHomeProps; +}> { + const list = await parseExtensionXML( + ( + await fs.readFile(path.resolve(process.cwd(), "src", "extensions.xml")) + ).toString() + ); + + await fs.writeFile( + "./public/extensions.json", + JSON.stringify(list, undefined, 2) + ); + + return { + props: { + appProps: await getAppProps(), + list, + }, + }; +} + +export default OldHome;