diff --git a/src/App.tsx b/src/App.tsx
index 5e77f70d..fec46f19 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,14 +1,11 @@
import { AppDataProvider } from "./providers/app-data-provider";
-import { ThemeProvider } from "@/components/layout/theme-provider";
import Layout from "./pages/_layout";
function App() {
return (
-
-
-
-
-
+
+
+
);
}
export default App;
diff --git a/src/components/base/NoticeManager.tsx b/src/components/base/NoticeManager.tsx
deleted file mode 100644
index 88ec7281..00000000
--- a/src/components/base/NoticeManager.tsx
+++ /dev/null
@@ -1,30 +0,0 @@
-"use client";
-
-import { Toaster, toast } from "sonner";
-import { useEffect, useSyncExternalStore } from "react";
-import {
- getSnapshotNotices,
- hideNotice,
- subscribeNotices,
-} from "@/services/noticeService";
-
-export const NoticeManager = () => {
- const currentNotices = useSyncExternalStore(
- subscribeNotices,
- getSnapshotNotices,
- );
-
- useEffect(() => {
- for (const notice of currentNotices) {
- const toastId = toast(notice.message, {
- id: notice.id,
- duration: notice.duration,
- onDismiss: (t) => {
- hideNotice(t.id as number);
- },
- });
- }
- }, [currentNotices]);
-
- return ;
-};
diff --git a/src/components/base/index.ts b/src/components/base/index.ts
index e0e1afb2..82686671 100644
--- a/src/components/base/index.ts
+++ b/src/components/base/index.ts
@@ -5,4 +5,3 @@ export { BaseLoading } from "./base-loading";
export { BaseErrorBoundary } from "./base-error-boundary";
export { Switch } from "./base-switch";
export { BaseLoadingOverlay } from "./base-loading-overlay";
-export { NoticeManager } from "./NoticeManager";
diff --git a/src/components/layout/use-custom-theme.ts b/src/components/layout/use-custom-theme.ts
index 9513022f..50a909e3 100644
--- a/src/components/layout/use-custom-theme.ts
+++ b/src/components/layout/use-custom-theme.ts
@@ -1,47 +1,52 @@
-import { useEffect, useMemo } from "react";
+import { useEffect, useMemo, useState } from "react";
import { useSetThemeMode, useThemeMode } from "@/services/states";
import { useVerge } from "@/hooks/use-verge";
-import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow";
+import { getCurrentWebviewWindow, WebviewWindow } from "@tauri-apps/api/webviewWindow";
import { Theme } from "@tauri-apps/api/window";
export const useCustomTheme = () => {
- const appWindow = useMemo(() => getCurrentWebviewWindow(), []);
+ const appWindow: WebviewWindow = useMemo(() => getCurrentWebviewWindow(), []);
const { verge } = useVerge();
const { theme_mode } = verge ?? {};
const mode = useThemeMode();
const setMode = useSetThemeMode();
+ const [systemTheme, setSystemTheme] = useState(
+ () => window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"
+ );
+
useEffect(() => {
setMode(
- theme_mode === "light" || theme_mode === "dark" ? theme_mode : "system",
+ theme_mode === "light" || theme_mode === "dark" ? theme_mode : "system",
);
}, [theme_mode, setMode]);
useEffect(() => {
- const root = document.documentElement;
+ if (mode !== 'system') return;
- const activeTheme =
- mode === "system"
- ? window.matchMedia("(prefers-color-scheme: dark)").matches
- ? "dark"
- : "light"
- : mode;
+ const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
+ const handleChange = (e: MediaQueryListEvent) => {
+ setSystemTheme(e.matches ? "dark" : "light");
+ };
- root.classList.remove("light", "dark");
- root.classList.add(activeTheme);
- appWindow.setTheme(activeTheme as Theme).catch(console.error);
- }, [mode, appWindow]);
+ mediaQuery.addEventListener('change', handleChange);
+ return () => mediaQuery.removeEventListener('change', handleChange);
+ }, [mode]);
useEffect(() => {
- if (theme_mode !== "system") return;
- const unlistenPromise = appWindow.onThemeChanged(({ payload }) => {
- setMode(payload);
- });
- return () => {
- unlistenPromise.then((f) => f());
- };
- }, [theme_mode, appWindow, setMode]);
+ const root = document.documentElement;
+ const activeTheme = mode === "system" ? systemTheme : mode;
+ root.classList.remove("light", "dark");
+ root.classList.add(activeTheme);
+
+ if (theme_mode === "system") {
+ appWindow.setTheme(null).catch(console.error);
+ } else {
+ appWindow.setTheme(activeTheme as Theme).catch(console.error);
+ }
+
+ }, [mode, systemTheme, appWindow, theme_mode]);
return {};
-};
+};
\ No newline at end of file
diff --git a/src/index.css b/src/index.css
index b265e9e6..875c37e9 100644
--- a/src/index.css
+++ b/src/index.css
@@ -1,7 +1,9 @@
@import "tailwindcss";
@import "tw-animate-css";
-@variant dark .dark &;
+@theme {
+ --tailwind-darkMode: 'class';
+}
@theme inline {
--radius-sm: calc(var(--radius) - 4px);
diff --git a/src/pages/_layout.tsx b/src/pages/_layout.tsx
index b9bc197a..8a4fa5a1 100644
--- a/src/pages/_layout.tsx
+++ b/src/pages/_layout.tsx
@@ -21,7 +21,7 @@ import { useClashInfo } from "@/hooks/use-clash";
import { initGlobalLogService } from "@/services/global-log-service";
import { invoke } from "@tauri-apps/api/core";
import { showNotice } from "@/services/noticeService";
-import { NoticeManager } from "@/components/base/NoticeManager";
+import { Toaster } from "@/components/ui/sonner";
import { SidebarProvider, useSidebar } from "@/components/ui/sidebar";
import { AppSidebar } from "@/components/layout/sidebar";
import { useZoomControls } from "@/hooks/useZoomControls";
@@ -472,9 +472,9 @@ const Layout = () => {
return (
-
+
);
diff --git a/src/services/noticeService.ts b/src/services/noticeService.ts
index f5ef04ca..2e19d7bb 100644
--- a/src/services/noticeService.ts
+++ b/src/services/noticeService.ts
@@ -1,81 +1,25 @@
-import { ReactNode } from "react";
+import { toast } from "sonner";
-export interface NoticeItem {
- id: number;
- type: "success" | "error" | "info";
- message: ReactNode;
- duration: number;
- timerId?: ReturnType;
-}
+type NoticeType = 'success' | 'error' | 'info' | 'warning';
-type Listener = (notices: NoticeItem[]) => void;
+export const showNotice = (type: NoticeType, message: string, duration?: number) => {
+ const options = duration ? { duration } : {};
-let nextId = 0;
-let notices: NoticeItem[] = [];
-const listeners: Set = new Set();
-
-function notifyListeners() {
- listeners.forEach((listener) => listener([...notices])); // Pass a copy
-}
-
-// Shows a notification.
-
-export function showNotice(
- type: "success" | "error" | "info",
- message: ReactNode,
- duration?: number,
-): number {
- const id = nextId++;
- const effectiveDuration =
- duration ?? (type === "error" ? 8000 : type === "info" ? 5000 : 3000); // Longer defaults
-
- const newNotice: NoticeItem = {
- id,
- type,
- message,
- duration: effectiveDuration,
- };
-
- // Auto-hide timer (only if duration is not null/0)
- if (effectiveDuration > 0) {
- newNotice.timerId = setTimeout(() => {
- hideNotice(id);
- }, effectiveDuration);
+ switch (type) {
+ case 'success':
+ toast.success(message, options);
+ break;
+ case 'error':
+ toast.error(message, options);
+ break;
+ case 'info':
+ toast.info(message, options);
+ break;
+ case 'warning':
+ toast.warning(message, options);
+ break;
+ default:
+ toast(message, options);
+ break;
}
-
- notices = [...notices, newNotice];
- notifyListeners();
- return id;
-}
-
-// Hides a specific notification by its ID.
-
-export function hideNotice(id: number) {
- const notice = notices.find((n) => n.id === id);
- if (notice?.timerId) {
- clearTimeout(notice.timerId); // Clear timeout if manually closed
- }
- notices = notices.filter((n) => n.id !== id);
- notifyListeners();
-}
-
-// Subscribes a listener function to notice state changes.
-
-export function subscribeNotices(listener: () => void) {
- listeners.add(listener);
- return () => {
- listeners.delete(listener);
- };
-}
-export function getSnapshotNotices() {
- return notices;
-}
-
-// Function to clear all notices at once
-export function clearAllNotices() {
- notices.forEach((n) => {
- if (n.timerId) clearTimeout(n.timerId);
- });
- notices = [];
- notifyListeners();
-}
+};
\ No newline at end of file