From c4651cb31586c44875f3f0db274ab61cc161f425 Mon Sep 17 00:00:00 2001 From: Jake Jarvis Date: Wed, 27 Apr 2022 10:58:25 -0400 Subject: [PATCH] clean up `ThemeContext` --- contexts/ThemeContext.tsx | 49 +++++++++++++++++--------------------- lib/helpers/parse-notes.ts | 1 + 2 files changed, 23 insertions(+), 27 deletions(-) diff --git a/contexts/ThemeContext.tsx b/contexts/ThemeContext.tsx index f0622114..dfb5b32f 100644 --- a/contexts/ThemeContext.tsx +++ b/contexts/ThemeContext.tsx @@ -1,4 +1,4 @@ -import { createContext, useCallback, useEffect, useState, useRef } from "react"; +import { createContext, useCallback, useEffect, useState } from "react"; import { useLocalStorage } from "react-use"; import { darkModeQuery, themeStorageKey } from "../lib/config/themes"; import type { Context, PropsWithChildren } from "react"; @@ -26,69 +26,64 @@ export const ThemeProvider = ({ classNames: { [themeName: string]: string; }; - /** Optionally set `color-scheme` CSS property to change browser appearance. */ - enableColorScheme?: boolean; }>) => { // keep track of if/when the user has set their theme *here*: const [preferredTheme, setPreferredTheme] = useLocalStorage(themeStorageKey, null, { raw: true }); // save the end result no matter how we got there (by preference or by system): const [resolvedTheme, setResolvedTheme] = useState(""); - // TODO: remove this and do related stuff more gracefully - const validThemes = Object.keys(classNames); + // get the theme names (light, dark) via passed-in classnames' keys + const themeNames = Object.keys(classNames); // updates the DOM and optionally saves the new theme to local storage const changeTheme = useCallback( - (theme: string, updateStorage: boolean) => { + (theme: string, updateStorage?: boolean) => { if (updateStorage) { setPreferredTheme(theme); } // remove all theme classes first to start fresh const all = Object.values(classNames); - const d = document.documentElement; - d.classList.remove(...all); - d.classList.add(classNames[theme]); + document.documentElement.classList.remove(...all); + document.documentElement.classList.add(classNames[theme]); }, [classNames, setPreferredTheme] ); // memoize browser media query handler const handleMediaQuery = useCallback( - (e?: MediaQueryList) => { + (e: MediaQueryListEvent | MediaQueryList) => { // get the user's preferred theme via their OS/browser settings - const media = e || window.matchMedia(darkModeQuery); - const systemTheme = media.matches ? "dark" : "light"; + const systemTheme = e.matches ? "dark" : "light"; + + // keep track of the resolved theme whether or not we change it below setResolvedTheme(systemTheme); + // only actually change the theme if preference is unset (and *don't* save new theme to storage) - if (!preferredTheme || !validThemes.includes(preferredTheme)) changeTheme(systemTheme, false); + if (!preferredTheme || !themeNames.includes(preferredTheme)) { + changeTheme(systemTheme, false); + } }, - [changeTheme, preferredTheme, validThemes] + [changeTheme, preferredTheme, themeNames] ); - // ref hack to avoid adding handleMediaQuery as a dependency - const mediaListener = useRef(handleMediaQuery); - mediaListener.current = handleMediaQuery; // listen for changes in OS preference useEffect(() => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const handler = (...args: any) => mediaListener.current(...args); const media = window.matchMedia(darkModeQuery); - - media.addEventListener("change", handler); - handler(media); + media.addEventListener("change", handleMediaQuery); + handleMediaQuery(media); // clean up the event listener return () => { - media.removeEventListener("change", handler); + media.removeEventListener("change", handleMediaQuery); }; - }, []); + }, [handleMediaQuery]); // color-scheme handling (tells browser how to render built-in elements like forms, scrollbars, etc.) useEffect(() => { // only "light" and "dark" are valid here const colorScheme = ["light", "dark"].includes(preferredTheme) ? preferredTheme : resolvedTheme; - document.documentElement.style.setProperty("color-scheme", colorScheme); + document.documentElement.style?.setProperty("color-scheme", colorScheme); }, [preferredTheme, resolvedTheme]); return ( @@ -101,8 +96,8 @@ export const ThemeProvider = ({ }, [changeTheme] ), - preferredTheme: validThemes.includes(preferredTheme) ? preferredTheme : undefined, - resolvedTheme: validThemes.includes(preferredTheme) ? preferredTheme : resolvedTheme, + preferredTheme: themeNames.includes(preferredTheme) ? preferredTheme : undefined, + resolvedTheme: themeNames.includes(preferredTheme) ? preferredTheme : resolvedTheme, }} > {children} diff --git a/lib/helpers/parse-notes.ts b/lib/helpers/parse-notes.ts index 7806705d..75f1e226 100644 --- a/lib/helpers/parse-notes.ts +++ b/lib/helpers/parse-notes.ts @@ -24,6 +24,7 @@ import type { NoteType } from "../../types"; export const getNoteSlugs = async () => { // get all files in NOTES_DIR const files = await fs.readdir(path.join(process.cwd(), NOTES_DIR)); + // narrow to only the .mdx files and strip the .mdx extension return files.filter((file) => /\.mdx$/.test(file)).map((noteFile) => noteFile.replace(/\.mdx$/, "")); };