From 042d61548de4296dd0446a738210485e458f10fc Mon Sep 17 00:00:00 2001 From: Oliver Schwendener Date: Sat, 14 Dec 2024 22:03:28 +0100 Subject: [PATCH] Add separate module for settings window --- .../Core/BrowserWindow/BrowserWindowModule.ts | 4 - .../SettingsWindow/SettingsWindowModule.ts | 56 +++++++ src/main/Core/SettingsWindow/index.ts | 1 + src/main/Core/index.ts | 1 + src/main/index.ts | 3 +- src/renderer/Core/Settings/Settings.tsx | 141 ++++++++++-------- src/renderer/Core/Settings/SettingsHeader.tsx | 31 ---- src/renderer/settings.tsx | 3 +- 8 files changed, 140 insertions(+), 100 deletions(-) create mode 100644 src/main/Core/SettingsWindow/SettingsWindowModule.ts create mode 100644 src/main/Core/SettingsWindow/index.ts delete mode 100644 src/renderer/Core/Settings/SettingsHeader.tsx diff --git a/src/main/Core/BrowserWindow/BrowserWindowModule.ts b/src/main/Core/BrowserWindow/BrowserWindowModule.ts index 3f7ce4881..f798e3794 100644 --- a/src/main/Core/BrowserWindow/BrowserWindowModule.ts +++ b/src/main/Core/BrowserWindow/BrowserWindowModule.ts @@ -90,10 +90,6 @@ export class BrowserWindowModule { dependencyRegistry.get("EnvironmentVariableProvider"), "search.html", ); - - ipcMain.on("openSettings", () => { - console.log("open settings window", { pathname: "/settings/general" }); - }); } private static registerBrowserWindowEventListeners( diff --git a/src/main/Core/SettingsWindow/SettingsWindowModule.ts b/src/main/Core/SettingsWindow/SettingsWindowModule.ts new file mode 100644 index 000000000..fea9e4b9e --- /dev/null +++ b/src/main/Core/SettingsWindow/SettingsWindowModule.ts @@ -0,0 +1,56 @@ +import type { Dependencies } from "@Core/Dependencies"; +import type { DependencyRegistry } from "@Core/DependencyRegistry"; +import { BrowserWindow } from "electron"; +import { join } from "path"; + +export class SettingsWindowModule { + public static async bootstrap(dependencyRegistry: DependencyRegistry) { + const ipcMain = dependencyRegistry.get("IpcMain"); + + let settingsWindow = await this.createSettingsWindow(dependencyRegistry); + + ipcMain.on("openSettings", async () => { + if (settingsWindow.isDestroyed()) { + settingsWindow = await this.createSettingsWindow(dependencyRegistry); + } + + settingsWindow.focus(); + settingsWindow.show(); + }); + } + + private static async createSettingsWindow( + dependencyRegistry: DependencyRegistry, + ): Promise { + const app = dependencyRegistry.get("App"); + const environmentVariableProvider = dependencyRegistry.get("EnvironmentVariableProvider"); + + const settingsWindow = new BrowserWindow({ + show: false, + backgroundMaterial: "mica", + autoHideMenuBar: true, + webPreferences: { + preload: join(__dirname, "..", "dist-preload", "index.js"), + spellcheck: false, + + // The dev tools should only be available in development mode. Once the app is packaged, the dev tools + // should be disabled. + devTools: !app.isPackaged, + + // The following options are needed for images with `file://` URLs to work during development + allowRunningInsecureContent: !app.isPackaged, + webSecurity: app.isPackaged, + }, + }); + + if (app.isPackaged) { + await settingsWindow.loadFile(join(__dirname, "..", "dist-renderer", "settings.html")); + } else { + await settingsWindow.loadURL( + `${environmentVariableProvider.get("VITE_DEV_SERVER_URL")}/${"settings.html"}`, + ); + } + + return settingsWindow; + } +} diff --git a/src/main/Core/SettingsWindow/index.ts b/src/main/Core/SettingsWindow/index.ts new file mode 100644 index 000000000..08a474dee --- /dev/null +++ b/src/main/Core/SettingsWindow/index.ts @@ -0,0 +1 @@ +export * from "./SettingsWindowModule"; diff --git a/src/main/Core/index.ts b/src/main/Core/index.ts index 7d10fdc79..2507bf344 100644 --- a/src/main/Core/index.ts +++ b/src/main/Core/index.ts @@ -38,6 +38,7 @@ export * from "./Settings"; export * from "./SettingsFile"; export * from "./SettingsManager"; export * from "./SettingsReader"; +export * from "./SettingsWindow"; export * from "./SettingsWriter"; export * from "./Shell"; export * from "./TaskScheduler"; diff --git a/src/main/index.ts b/src/main/index.ts index 4a8d418cc..c33e4761d 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -90,8 +90,9 @@ if (!app.requestSingleInstanceLock()) { Extensions.ExtensionLoader.bootstrap(dependencyRegistry); await Core.ExtensionManagerModule.bootstrap(dependencyRegistry); - // BrowserWindow + // Windows await Core.BrowserWindowModule.bootstrap(dependencyRegistry); + await Core.SettingsWindowModule.bootstrap(dependencyRegistry); Core.RescanOrchestratorModule.bootstrap(dependencyRegistry); })(); diff --git a/src/renderer/Core/Settings/Settings.tsx b/src/renderer/Core/Settings/Settings.tsx index 8d070e77c..ed19d69b1 100644 --- a/src/renderer/Core/Settings/Settings.tsx +++ b/src/renderer/Core/Settings/Settings.tsx @@ -1,81 +1,96 @@ -import type { KeyboardEvent } from "react"; -import { Route, Routes, useNavigate } from "react-router"; +import { useScrollBar } from "@Core/Hooks"; +import { useI18n } from "@Core/I18n"; +import { getTheme } from "@Core/Theme"; +import { ThemeContext } from "@Core/ThemeContext"; +import { useAppCssProperties } from "@Core/useAppCssProperties"; +import { FluentProvider, type Theme } from "@fluentui/react-components"; +import { useEffect, useState } from "react"; +import { Route, Routes } from "react-router"; import { ExtensionSettings } from "./ExtensionSettings"; import { Navigation } from "./Navigation"; import { settingsPages } from "./Pages"; -import { SettingsHeader } from "./SettingsHeader"; export const Settings = () => { - const navigate = useNavigate(); - const closeSettings = () => navigate({ pathname: "/" }); + const [theme, setTheme] = useState(getTheme(window.ContextBridge)); - const handleKeyDownEvent = (event: KeyboardEvent) => { - if (event.key === "Escape") { - event.preventDefault(); - closeSettings(); - } - }; + const [shouldPreferDarkColors, setShouldPreferDarkColors] = useState( + window.matchMedia("(prefers-color-scheme: dark)").matches, + ); - return ( -
-
- -
+ const { appCssProperties } = useAppCssProperties(); + + useI18n(); + useScrollBar({ document, theme }); + + useEffect(() => { + const nativeThemeChangedEventHandler = () => { + setTheme(getTheme(window.ContextBridge)); + setShouldPreferDarkColors(window.matchMedia("(prefers-color-scheme: dark)").matches); + }; -
-
+ window.ContextBridge.ipcRenderer.on("nativeThemeChanged", nativeThemeChangedEventHandler); + + return () => { + window.ContextBridge.ipcRenderer.off("nativeThemeChanged", nativeThemeChangedEventHandler); + }; + }, []); + + return ( + + +
- +
+
+ +
+
+
+ + {settingsPages.map(({ element, relativePath }) => ( + + ))} + } /> + +
-
- - {settingsPages.map(({ element, relativePath }) => ( - - ))} - } /> - -
-
-
+ + ); }; diff --git a/src/renderer/Core/Settings/SettingsHeader.tsx b/src/renderer/Core/Settings/SettingsHeader.tsx deleted file mode 100644 index 721ac817a..000000000 --- a/src/renderer/Core/Settings/SettingsHeader.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import { Button, Text, Tooltip } from "@fluentui/react-components"; -import { ArrowLeftFilled } from "@fluentui/react-icons"; -import { useTranslation } from "react-i18next"; -import { Header } from "../Header"; - -type SettingsHeaderProps = { - onCloseSettingsClicked: () => void; -}; - -export const SettingsHeader = ({ onCloseSettingsClicked }: SettingsHeaderProps) => { - const { t } = useTranslation(); - - return ( -
- - - } - > - {t("settings", { ns: "general" })} -
- ); -}; diff --git a/src/renderer/settings.tsx b/src/renderer/settings.tsx index a4e47f629..ce7b79215 100644 --- a/src/renderer/settings.tsx +++ b/src/renderer/settings.tsx @@ -1,10 +1,11 @@ +import { Settings } from "@Core/Settings"; import { createRoot } from "react-dom/client"; import { HashRouter } from "react-router-dom"; document.addEventListener("DOMContentLoaded", () => { createRoot(document.getElementById("react-app") as HTMLDivElement).render( -

Hello from settings

+
, ); });