diff --git a/islands/Container.tsx b/islands/Container.tsx index 0ea5b6b..1e6fe22 100644 --- a/islands/Container.tsx +++ b/islands/Container.tsx @@ -11,13 +11,25 @@ import t, { i18n_map, I18NMap } from "../server/i18n.ts"; import TaskManager from "./TaskManager.tsx"; import { initState, set_state } from "../server/state.ts"; import NewTask from "../components/NewTask.tsx"; -import { parse_bool } from "../server/parse.ts"; -import Switch from "preact-material-components/Switch"; +import { parse_int } from "../server/parse.ts"; +import { detect_darkmode } from "../server/dark.ts"; export type ContainerProps = { i18n: I18NMap; }; +enum DarkMode { + Auto, + Light, + Dark, +} + +function darkmode_next(d: DarkMode) { + if (d === DarkMode.Auto) return DarkMode.Light; + if (d === DarkMode.Dark) return DarkMode.Auto; + return DarkMode.Dark; +} + export default class Container extends Component { static contextType = GlobalCtx; declare context: ContextType; @@ -25,12 +37,18 @@ export default class Container extends Component { i18n_map.value = this.props.i18n; const [display, set_display] = useState(false); const [state, set_state1] = useState("#/"); - const [darkmode, set_darkmode1] = useState(false); - const set_darkmode: StateUpdater = (u) => { + const [darkmode, set_darkmode1] = useState(DarkMode.Auto); + const set_darkmode: StateUpdater = (u) => { const v = typeof u === "function" ? u(darkmode) : u; set_darkmode1(v); localStorage.setItem("darkmode", JSON.stringify(v)); - if (v) { + if (v === DarkMode.Auto) { + if (detect_darkmode()) { + document.body.classList.add("dark-scheme"); + } else { + document.body.classList.remove("dark-scheme"); + } + } else if (v === DarkMode.Dark) { document.body.classList.add("dark-scheme"); } else { document.body.classList.remove("dark-scheme"); @@ -38,9 +56,16 @@ export default class Container extends Component { }; useEffect(() => { initState(set_state1); - const dm = parse_bool(localStorage.getItem("darkmode"), false); + const dm = parse_int( + localStorage.getItem("darkmode"), + DarkMode.Auto, + ); set_darkmode1(dm); - if (dm) { + if (dm === DarkMode.Auto) { + if (detect_darkmode()) { + document.body.classList.add("dark-scheme"); + } + } else if (dm === DarkMode.Dark) { document.body.classList.add("dark-scheme"); } }, []); @@ -63,14 +88,18 @@ export default class Container extends Component { - { - set_darkmode(!darkmode); + set_darkmode(darkmode_next(darkmode)); }} - /> - {darkmode ? "dark_mode" : "light_mode"} + navigation + > + {darkmode === DarkMode.Auto + ? "brightness_auto" + : darkmode === DarkMode.Dark + ? "dark_mode" + : "light_mode"} + more_vert diff --git a/server/dark.ts b/server/dark.ts new file mode 100644 index 0000000..963f685 --- /dev/null +++ b/server/dark.ts @@ -0,0 +1,4 @@ +export function detect_darkmode() { + return window.matchMedia && + window.matchMedia("(prefers-color-scheme: dark)").matches; +} diff --git a/server/parse.ts b/server/parse.ts index a1da6e0..85aa9d7 100644 --- a/server/parse.ts +++ b/server/parse.ts @@ -11,3 +11,13 @@ export function parse_bool( return n !== 0; } } + +export function parse_int( + value: string | null, + def: T, +): number | T { + if (value === null) return def; + const n = parseInt(value); + if (isNaN(n)) return def; + return n; +}