refactor: notification system

This commit is contained in:
wonfen
2025-05-04 22:17:08 +08:00
parent e2ad2d23f8
commit 8296675574
43 changed files with 384 additions and 355 deletions

View File

@@ -1,5 +1,5 @@
import { invoke } from "@tauri-apps/api/core";
import { Notice } from "@/components/base";
import { showNotice } from "@/services/noticeService";
export async function copyClashEnv() {
return invoke<void>("copy_clash_env");
@@ -145,25 +145,29 @@ export async function getAppDir() {
export async function openAppDir() {
return invoke<void>("open_app_dir").catch((err) =>
Notice.error(err?.message || err.toString(), 1500),
showNotice('error', err?.message || err.toString()),
);
}
export async function openCoreDir() {
return invoke<void>("open_core_dir").catch((err) =>
Notice.error(err?.message || err.toString(), 1500),
showNotice('error', err?.message || err.toString()),
);
}
export async function openLogsDir() {
return invoke<void>("open_logs_dir").catch((err) =>
Notice.error(err?.message || err.toString(), 1500),
showNotice('error', err?.message || err.toString()),
);
}
export async function openWebUrl(url: string) {
return invoke<void>("open_web_url", { url });
}
export const openWebUrl = async (url: string) => {
try {
await invoke("open_web_url", { url });
} catch (err: any) {
showNotice('error', err.toString());
}
};
export async function cmdGetProxyDelay(
name: string,
@@ -214,7 +218,7 @@ export async function cmdTestDelay(url: string) {
export async function invoke_uwp_tool() {
return invoke<void>("invoke_uwp_tool").catch((err) =>
Notice.error(err?.message || err.toString(), 1500),
showNotice('error', err?.message || err.toString(), 1500),
);
}

View File

@@ -0,0 +1,80 @@
import { ReactNode } from 'react';
export interface NoticeItem {
id: number;
type: 'success' | 'error' | 'info';
message: ReactNode;
duration: number;
timerId?: ReturnType<typeof setTimeout>;
}
type Listener = (notices: NoticeItem[]) => void;
let nextId = 0;
let notices: NoticeItem[] = [];
const listeners: Set<Listener> = 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);
}
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: Listener): () => void {
listeners.add(listener);
listener([...notices]);
return () => {
listeners.delete(listener);
};
}
// Function to clear all notices at once
export function clearAllNotices() {
notices.forEach(n => {
if (n.timerId) clearTimeout(n.timerId);
});
notices = [];
notifyListeners();
}