From 050277ac8b557f4eb548e26121f5f25df4555713 Mon Sep 17 00:00:00 2001 From: jude Date: Sat, 16 May 2026 18:20:00 +0100 Subject: [PATCH] Add dark mode support --- migrations/20260516170852_drop_columns.sql | 17 ++ reminder-dashboard/package.json | 3 +- .../components/App/ColorSchemeProvider.tsx | 43 +++ .../src/components/App/index.tsx | 113 ++++---- .../src/components/Guild/index.scss | 2 +- .../src/components/Modal/index.tsx | 6 +- .../src/components/Reminder/styles.scss | 2 +- .../src/components/Sidebar/styles.scss | 2 +- .../src/components/UserPreferences/index.tsx | 40 +++ reminder-dashboard/src/styles.scss | 258 ++++++++++++++++-- src/commands/look.rs | 5 +- src/component_models/mod.rs | 4 +- src/models/channel_data.rs | 13 +- src/web/ip_blocking.rs | 2 +- src/web/mod.rs | 10 +- src/web/routes/dashboard/api/guild/mod.rs | 7 +- .../routes/dashboard/api/guild/reminders.rs | 5 +- .../dashboard/export/reminder_templates.rs | 10 +- src/web/routes/dashboard/export/reminders.rs | 6 +- src/web/routes/dashboard/export/todos.rs | 8 +- 20 files changed, 431 insertions(+), 125 deletions(-) create mode 100644 migrations/20260516170852_drop_columns.sql create mode 100644 reminder-dashboard/src/components/App/ColorSchemeProvider.tsx diff --git a/migrations/20260516170852_drop_columns.sql b/migrations/20260516170852_drop_columns.sql new file mode 100644 index 0000000..7c7fbee --- /dev/null +++ b/migrations/20260516170852_drop_columns.sql @@ -0,0 +1,17 @@ +SET foreign_key_checks = 0; + +-- Drop all old tables +DROP TABLE IF EXISTS users_old; +DROP TABLE IF EXISTS messages; +DROP TABLE IF EXISTS embeds; +DROP TABLE IF EXISTS embed_fields; +DROP TABLE IF EXISTS command_aliases; +DROP TABLE IF EXISTS macro; +DROP TABLE IF EXISTS roles; +DROP TABLE IF EXISTS command_restrictions; + +-- Drop columns from channels that are no longer used +ALTER TABLE channels DROP COLUMN `name`; +ALTER TABLE channels DROP COLUMN `blacklisted`; + +SET foreign_key_checks = 1; diff --git a/reminder-dashboard/package.json b/reminder-dashboard/package.json index 9ff1aea..875efee 100644 --- a/reminder-dashboard/package.json +++ b/reminder-dashboard/package.json @@ -5,7 +5,8 @@ "scripts": { "dev": "vite build --watch --mode development", "build": "vite build", - "preview": "vite preview" + "preview": "vite preview", + "prettier": "prettier -w src/" }, "dependencies": { "axios": "^1.5.1", diff --git a/reminder-dashboard/src/components/App/ColorSchemeProvider.tsx b/reminder-dashboard/src/components/App/ColorSchemeProvider.tsx new file mode 100644 index 0000000..a5deb0a --- /dev/null +++ b/reminder-dashboard/src/components/App/ColorSchemeProvider.tsx @@ -0,0 +1,43 @@ +import { createContext } from "preact"; +import { useContext } from "preact/compat"; +import { useEffect, useState } from "preact/hooks"; +import { fetchUserInfo } from "../../api"; +import { useQuery } from "react-query"; + +type TColorScheme = "light" | "dark"; + +type TColorSchemeContext = { + colorScheme: TColorScheme; +}; + +const ColorSchemeContext = createContext({ colorScheme: "light" } as TColorSchemeContext); + +export const ColorSchemeProvider = ({ children }) => { + const { data } = useQuery({ ...fetchUserInfo() }); + const [activeScheme, setActiveScheme] = useState("light"); + + useEffect(() => { + const preference = data?.preferences?.dashboard_color_scheme || "system"; + + if (preference === "system") { + const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)"); + const handleChange = () => { + setActiveScheme(mediaQuery.matches ? "dark" : "light"); + }; + + handleChange(); + mediaQuery.addEventListener("change", handleChange); + return () => mediaQuery.removeEventListener("change", handleChange); + } else { + setActiveScheme(preference as TColorScheme); + } + }, [data]); + + return ( + + {children} + + ); +}; + +export const useColorScheme = () => useContext(ColorSchemeContext); diff --git a/reminder-dashboard/src/components/App/index.tsx b/reminder-dashboard/src/components/App/index.tsx index 240c06f..78544cd 100644 --- a/reminder-dashboard/src/components/App/index.tsx +++ b/reminder-dashboard/src/components/App/index.tsx @@ -1,59 +1,72 @@ -import { Sidebar } from "../Sidebar"; -import { QueryClient, QueryClientProvider } from "react-query"; -import { Route, Router, Switch } from "wouter"; -import { Welcome } from "../Welcome"; -import { Guild } from "../Guild"; -import { FlashProvider } from "./FlashProvider"; -import { TimezoneProvider } from "./TimezoneProvider"; -import { User } from "../User"; -import { GuildReminders } from "../Guild/GuildReminders"; -import { GuildTodos } from "../Guild/GuildTodos"; +import {Sidebar} from "../Sidebar"; +import {QueryClient, QueryClientProvider} from "react-query"; +import {Route, Router, Switch} from "wouter"; +import {Welcome} from "../Welcome"; +import {Guild} from "../Guild"; +import {FlashProvider} from "./FlashProvider"; +import {TimezoneProvider} from "./TimezoneProvider"; +import {User} from "../User"; +import {GuildReminders} from "../Guild/GuildReminders"; +import {GuildTodos} from "../Guild/GuildTodos"; +import {ColorSchemeProvider, useColorScheme} from "./ColorSchemeProvider"; +import {useEffect} from "preact/hooks"; + +const InnerApp = () => { + const {colorScheme} = useColorScheme(); + + useEffect(() => { + const body = document.querySelector("body"); + body.className = body.className.replace(/scheme-\w+/g, ""); + body.classList.add(`scheme-${colorScheme}`); + }, [colorScheme]); + + return ( + +
+ +
+
+ + + ( + + + + )} + > + ( + + + + )} + > + + + + +
+
+
+
+ ); +}; export function App() { const queryClient = new QueryClient(); - let scheme = "light"; - // if (window.matchMedia("(prefers-color-scheme: dark)").matches) { - // scheme = "dark"; - // } - return ( - - - -
- -
-
- - - ( - - - - )} - > - ( - - - - )} - > - - - - -
-
-
-
-
-
+ + + + + + +
); } diff --git a/reminder-dashboard/src/components/Guild/index.scss b/reminder-dashboard/src/components/Guild/index.scss index f608b31..6210f56 100644 --- a/reminder-dashboard/src/components/Guild/index.scss +++ b/reminder-dashboard/src/components/Guild/index.scss @@ -2,4 +2,4 @@ > * { margin: 2px; } -} \ No newline at end of file +} diff --git a/reminder-dashboard/src/components/Modal/index.tsx b/reminder-dashboard/src/components/Modal/index.tsx index 4aba010..beb58a4 100644 --- a/reminder-dashboard/src/components/Modal/index.tsx +++ b/reminder-dashboard/src/components/Modal/index.tsx @@ -1,5 +1,5 @@ -import {JSX} from "preact"; -import {createPortal} from "preact/compat"; +import { JSX } from "preact"; +import { createPortal } from "preact/compat"; type Props = { setModalOpen: (open: boolean) => never; @@ -9,7 +9,7 @@ type Props = { children: string | JSX.Element | JSX.Element[] | (() => JSX.Element); }; -export const Modal = ({setModalOpen, title, onSubmit, onSubmitText, children}: Props) => { +export const Modal = ({ setModalOpen, title, onSubmit, onSubmitText, children }: Props) => { const body = document.querySelector("body"); return createPortal( diff --git a/reminder-dashboard/src/components/Reminder/styles.scss b/reminder-dashboard/src/components/Reminder/styles.scss index 68c2ebf..6edff79 100644 --- a/reminder-dashboard/src/components/Reminder/styles.scss +++ b/reminder-dashboard/src/components/Reminder/styles.scss @@ -11,7 +11,7 @@ border-radius: 8px; margin: 4px; padding: 4px; - box-shadow: 0 0 5px 0 rgba(0,0,0,0.75); + box-shadow: 0 0 5px 0 rgba(0, 0, 0, 0.75); .highlight { background-color: #35373c; diff --git a/reminder-dashboard/src/components/Sidebar/styles.scss b/reminder-dashboard/src/components/Sidebar/styles.scss index c55f55e..a8d88b3 100644 --- a/reminder-dashboard/src/components/Sidebar/styles.scss +++ b/reminder-dashboard/src/components/Sidebar/styles.scss @@ -66,7 +66,7 @@ aside.menu { padding-left: 4px; } -@media screen and (max-width: 1023px),print { +@media screen and (max-width: 1023px), print { .columns:not(.is-desktop) { flex-direction: column; } diff --git a/reminder-dashboard/src/components/UserPreferences/index.tsx b/reminder-dashboard/src/components/UserPreferences/index.tsx index 7b61ec0..f5616b8 100644 --- a/reminder-dashboard/src/components/UserPreferences/index.tsx +++ b/reminder-dashboard/src/components/UserPreferences/index.tsx @@ -5,6 +5,12 @@ import { useRef, useState } from "preact/hooks"; import { ICON_FLASH_TIME } from "../../consts"; import { useFlash } from "../App/FlashContext"; +enum ColorScheme { + System = "system", + Dark = "dark", + Light = "light", +} + export const UserPreferences = () => { const [modalOpen, setModalOpen] = useState(false); @@ -96,6 +102,40 @@ const PreferencesModal = ({ setModalOpen }) => {

+
+ +
+