refactor: remove unused notification permission hook and related code

This commit is contained in:
Tunglies
2025-09-18 19:13:11 +08:00
parent a995a13163
commit 409571f54b
19 changed files with 17 additions and 315 deletions

View File

@@ -43,17 +43,14 @@
"@tauri-apps/plugin-clipboard-manager": "^2.3.0", "@tauri-apps/plugin-clipboard-manager": "^2.3.0",
"@tauri-apps/plugin-dialog": "^2.4.0", "@tauri-apps/plugin-dialog": "^2.4.0",
"@tauri-apps/plugin-fs": "^2.4.2", "@tauri-apps/plugin-fs": "^2.4.2",
"@tauri-apps/plugin-notification": "^2.3.1",
"@tauri-apps/plugin-process": "^2.3.0", "@tauri-apps/plugin-process": "^2.3.0",
"@tauri-apps/plugin-shell": "2.3.1", "@tauri-apps/plugin-shell": "2.3.1",
"@tauri-apps/plugin-updater": "2.9.0", "@tauri-apps/plugin-updater": "2.9.0",
"@types/json-schema": "^7.0.15", "@types/json-schema": "^7.0.15",
"ahooks": "^3.9.5", "ahooks": "^3.9.5",
"axios": "^1.12.2", "axios": "^1.12.2",
"cli-color": "^2.0.4",
"dayjs": "1.11.18", "dayjs": "1.11.18",
"foxact": "^0.2.49", "foxact": "^0.2.49",
"glob": "^11.0.3",
"i18next": "^25.5.2", "i18next": "^25.5.2",
"js-yaml": "^4.1.0", "js-yaml": "^4.1.0",
"json-schema": "^0.4.0", "json-schema": "^0.4.0",
@@ -70,13 +67,14 @@
"react-monaco-editor": "0.59.0", "react-monaco-editor": "0.59.0",
"react-router-dom": "7.9.1", "react-router-dom": "7.9.1",
"react-virtuoso": "^4.14.0", "react-virtuoso": "^4.14.0",
"sockette": "^2.0.6",
"swr": "^2.3.6", "swr": "^2.3.6",
"tar": "^7.4.3",
"types-pac": "^1.0.3", "types-pac": "^1.0.3",
"zustand": "^5.0.8" "zustand": "^5.0.8"
}, },
"devDependencies": { "devDependencies": {
"tar": "^7.4.3",
"glob": "^11.0.3",
"cli-color": "^2.0.4",
"@actions/github": "^6.0.1", "@actions/github": "^6.0.1",
"@eslint/js": "^9.35.0", "@eslint/js": "^9.35.0",
"@tauri-apps/cli": "2.8.4", "@tauri-apps/cli": "2.8.4",
@@ -96,9 +94,7 @@
"jiti": "^2.5.1", "jiti": "^2.5.1",
"meta-json-schema": "^1.19.13", "meta-json-schema": "^1.19.13",
"node-fetch": "^3.3.2", "node-fetch": "^3.3.2",
"path": "^0.12.7",
"prettier": "^3.6.2", "prettier": "^3.6.2",
"process": "^0.11.10",
"sass": "^1.92.1", "sass": "^1.92.1",
"terser": "^5.44.0", "terser": "^5.44.0",
"typescript": "^5.9.2", "typescript": "^5.9.2",

View File

@@ -1,9 +1,7 @@
import { AppDataProvider } from "./providers/app-data-provider"; import { AppDataProvider } from "./providers/app-data-provider";
import Layout from "./pages/_layout"; import Layout from "./pages/_layout";
import { useNotificationPermission } from "./hooks/useNotificationPermission";
function App() { function App() {
useNotificationPermission();
return ( return (
<AppDataProvider> <AppDataProvider>
<Layout /> <Layout />

View File

@@ -1,7 +1,7 @@
import React from "react"; import React from "react";
import { Box, CircularProgress } from "@mui/material"; import { Box, CircularProgress } from "@mui/material";
export interface BaseLoadingOverlayProps { interface BaseLoadingOverlayProps {
isLoading: boolean; isLoading: boolean;
} }
@@ -29,5 +29,3 @@ export const BaseLoadingOverlay: React.FC<BaseLoadingOverlayProps> = ({
</Box> </Box>
); );
}; };
export default BaseLoadingOverlay;

View File

@@ -44,7 +44,7 @@ interface ProxyOption {
} }
// 排序类型: 默认 | 按延迟 | 按字母 // 排序类型: 默认 | 按延迟 | 按字母
export type ProxySortType = 0 | 1 | 2; type ProxySortType = 0 | 1 | 2;
function convertDelayColor(delayValue: number) { function convertDelayColor(delayValue: number) {
const colorStr = delayManager.formatDelayColor(delayValue); const colorStr = delayManager.formatDelayColor(delayValue);

View File

@@ -17,7 +17,7 @@ import {
} from "@/hooks/use-traffic-monitor"; } from "@/hooks/use-traffic-monitor";
// 流量数据项接口 // 流量数据项接口
export interface ITrafficItem { interface ITrafficItem {
up: number; up: number;
down: number; down: number;
timestamp?: number; timestamp?: number;

View File

@@ -2,7 +2,7 @@ import { Box, Typography, alpha, useTheme } from "@mui/material";
import { ReactNode } from "react"; import { ReactNode } from "react";
// 自定义卡片组件接口 // 自定义卡片组件接口
export interface EnhancedCardProps { interface EnhancedCardProps {
title: ReactNode; title: ReactNode;
icon: ReactNode; icon: ReactNode;
action?: ReactNode; action?: ReactNode;

View File

@@ -55,7 +55,7 @@ interface ProfileExtra {
expire: number; expire: number;
} }
export interface ProfileItem { interface ProfileItem {
uid: string; uid: string;
type?: "local" | "remote" | "merge" | "script"; type?: "local" | "remote" | "merge" | "script";
name?: string; name?: string;
@@ -68,7 +68,7 @@ export interface ProfileItem {
option?: any; option?: any;
} }
export interface HomeProfileCardProps { interface HomeProfileCardProps {
current: ProfileItem | null | undefined; current: ProfileItem | null | undefined;
onProfileUpdated?: () => void; onProfileUpdated?: () => void;
} }

View File

@@ -17,7 +17,7 @@ import VisibilityOff from "@mui/icons-material/VisibilityOff";
import { saveWebdavConfig, createWebdavBackup } from "@/services/cmds"; import { saveWebdavConfig, createWebdavBackup } from "@/services/cmds";
import { showNotice } from "@/services/noticeService"; import { showNotice } from "@/services/noticeService";
export interface BackupConfigViewerProps { interface BackupConfigViewerProps {
onBackupSuccess: () => Promise<void>; onBackupSuccess: () => Promise<void>;
onSaveSuccess: () => Promise<void>; onSaveSuccess: () => Promise<void>;
onRefresh: () => Promise<void>; onRefresh: () => Promise<void>;

View File

@@ -33,7 +33,7 @@ export type BackupFile = IWebDavFile & {
export const DEFAULT_ROWS_PER_PAGE = 5; export const DEFAULT_ROWS_PER_PAGE = 5;
export interface BackupTableViewerProps { interface BackupTableViewerProps {
datasource: BackupFile[]; datasource: BackupFile[];
page: number; page: number;
onPageChange: ( onPageChange: (

View File

@@ -2,12 +2,10 @@ import {
useGlobalLogData, useGlobalLogData,
clearGlobalLogs, clearGlobalLogs,
LogLevel, LogLevel,
ILogItem,
} from "@/services/global-log-service"; } from "@/services/global-log-service";
// 为了向后兼容,导出相同的类型 // 为了向后兼容,导出相同的类型
export type { LogLevel }; export type { LogLevel };
export type { ILogItem };
export const useLogData = useGlobalLogData; export const useLogData = useGlobalLogData;

View File

@@ -28,7 +28,7 @@ const cleanupConnections = async (previousProxy: string) => {
} }
}; };
export interface ProxySelectionOptions { interface ProxySelectionOptions {
onSuccess?: () => void; onSuccess?: () => void;
onError?: (error: any) => void; onError?: (error: any) => void;
enableConnectionCleanup?: boolean; enableConnectionCleanup?: boolean;

View File

@@ -1,8 +0,0 @@
import { setupNotificationPermission } from "../utils/notification-permission";
import { useEffect } from "react";
export function useNotificationPermission() {
useEffect(() => {
setupNotificationPermission();
}, []);
}

View File

@@ -201,7 +201,7 @@ const HomeSettingsDialog = ({
); );
}; };
export const HomePage = () => { const HomePage = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const { verge } = useVerge(); const { verge } = useVerge();
const { current, mutateProfiles } = useProfiles(); const { current, mutateProfiles } = useProfiles();

View File

@@ -12,7 +12,7 @@ const MAX_LOG_NUM = 1000;
export type LogLevel = "debug" | "info" | "warning" | "error" | "all"; export type LogLevel = "debug" | "info" | "warning" | "error" | "all";
export interface ILogItem { interface ILogItem {
time?: string; time?: string;
type: string; type: string;
payload: string; payload: string;

View File

@@ -7,9 +7,9 @@ import {
} from "@/services/cmds"; } from "@/services/cmds";
import dayjs from "dayjs"; import dayjs from "dayjs";
export type LogLevel = "debug" | "info" | "warning" | "error" | "all"; type LogLevel = "debug" | "info" | "warning" | "error" | "all";
export interface ILogItem { interface ILogItem {
time?: string; time?: string;
type: string; type: string;
payload: string; payload: string;

View File

@@ -1,6 +1,6 @@
import { ReactNode } from "react"; import { ReactNode } from "react";
export interface NoticeItem { interface NoticeItem {
id: number; id: number;
type: "success" | "error" | "info"; type: "success" | "error" | "info";
message: ReactNode; message: ReactNode;

View File

@@ -1,17 +0,0 @@
import {
isPermissionGranted,
requestPermission,
} from "@tauri-apps/plugin-notification";
export async function setupNotificationPermission() {
let permission = await isPermissionGranted();
if (!permission) {
const result = await requestPermission();
permission = result === "granted";
}
if (permission) {
console.log("通知权限已授予");
} else {
console.log("通知权限被拒绝");
}
}

View File

@@ -1,262 +0,0 @@
import Sockette, { type SocketteOptions } from "sockette";
/**
* A wrapper of Sockette that will automatically reconnect up to `maxError` before emitting an error event.
*/
export const createSockette = (
url: string,
opt: SocketteOptions,
maxError = 10,
) => {
let remainRetryCount = maxError;
return new Sockette(url, {
...opt,
// Sockette has a built-in reconnect when ECONNREFUSED feature
// Use maxError if opt.maxAttempts is not specified
maxAttempts: opt.maxAttempts ?? maxError,
onmessage(this: Sockette, ev) {
remainRetryCount = maxError; // reset counter
opt.onmessage?.call(this, ev);
},
onerror(this: Sockette, ev) {
remainRetryCount -= 1;
if (remainRetryCount >= 0) {
if (this instanceof Sockette) {
this.close();
this.reconnect();
}
} else {
opt.onerror?.call(this, ev);
}
},
onmaximum(this: Sockette, ev) {
opt.onmaximum?.call(this, ev);
// onmaximum will be fired when Sockette reaches built-in reconnect limit,
// We will also set remainRetryCount to 0 to prevent further reconnect.
remainRetryCount = 0;
},
});
};
/**
* 创建一个支持认证的WebSocket连接
* 使用标准的URL参数方式添加token
*
* 注意mihomo服务器对WebSocket的认证支持不佳使用URL参数方式传递token
*/
export const createAuthSockette = (
baseUrl: string,
secret: string,
opt: SocketteOptions,
maxError = 10,
) => {
// 确保baseUrl格式正确
let url = baseUrl;
if (!url.startsWith("ws://") && !url.startsWith("wss://")) {
url = `ws://${url}`;
}
// 重试控制
let reconnectAttempts = 0;
const MAX_RECONNECT = maxError;
let reconnectTimeout: any = null;
let ws: WebSocket | null = null;
// 使用URL API解析和构建URL
try {
const urlObj = new URL(url);
// 添加token参数如果有secret
if (secret) {
urlObj.searchParams.delete("token");
urlObj.searchParams.append("token", secret);
}
url = urlObj.toString();
console.log(`[WebSocket] 创建连接: ${url.replace(secret || "", "***")}`);
} catch (e) {
console.error(`[WebSocket] URL格式错误: ${url}`, e);
if (opt.onerror) {
// 使用任意类型避免类型错误
const anyOpt = opt as any;
anyOpt.onerror(
new ErrorEvent("error", { message: `URL格式错误: ${e}` } as any),
);
}
return createDummySocket();
}
function connect() {
try {
ws = new WebSocket(url);
ws.onopen = function (event) {
console.log(
`[WebSocket] 连接成功: ${url.replace(secret || "", "***")}`,
);
reconnectAttempts = 0; // 重置重连计数
if (opt.onopen) {
// 使用任意类型避免类型错误
const anyOpt = opt as any;
anyOpt.onopen(event);
}
};
ws.onmessage = function (event) {
if (opt.onmessage) {
// 使用任意类型避免类型错误
const anyOpt = opt as any;
anyOpt.onmessage(event);
}
};
ws.onerror = function (event) {
console.error(
`[WebSocket] 连接错误: ${url.replace(secret || "", "***")}`,
);
// 错误处理
if (reconnectAttempts < MAX_RECONNECT) {
scheduleReconnect();
} else if (opt.onerror) {
// 使用任意类型避免类型错误
const anyOpt = opt as any;
anyOpt.onerror(event);
}
};
ws.onclose = function (event) {
console.log(
`[WebSocket] 连接关闭: ${url.replace(secret || "", "***")}, 代码: ${event.code}`,
);
// 如果不是正常关闭(1000, 1001),尝试重连
if (
event.code !== 1000 &&
event.code !== 1001 &&
reconnectAttempts < MAX_RECONNECT
) {
scheduleReconnect();
} else {
if (opt.onclose) {
// 使用任意类型避免类型错误
const anyOpt = opt as any;
anyOpt.onclose(event);
}
// 如果已达到最大重试次数
if (reconnectAttempts >= MAX_RECONNECT && opt.onmaximum) {
console.error(
`[WebSocket] 达到最大重试次数: ${url.replace(secret || "", "***")}`,
);
const anyOpt = opt as any;
anyOpt.onmaximum(event);
}
}
};
} catch (error) {
console.error(`[WebSocket] 创建连接失败:`, error);
if (opt.onerror) {
// 使用任意类型避免类型错误
const anyOpt = opt as any;
anyOpt.onerror(
new ErrorEvent("error", { message: `创建连接失败: ${error}` } as any),
);
}
}
}
function scheduleReconnect() {
if (reconnectTimeout) {
clearTimeout(reconnectTimeout);
}
reconnectAttempts++;
const delay = Math.min(1000 * Math.pow(1.5, reconnectAttempts), 10000); // 指数退避最大10秒
console.log(
`[WebSocket] 计划重连 (${reconnectAttempts}/${MAX_RECONNECT}) 延迟: ${delay}ms`,
);
reconnectTimeout = setTimeout(() => {
console.log(
`[WebSocket] 尝试重连 (${reconnectAttempts}/${MAX_RECONNECT})`,
);
cleanup();
connect();
}, delay);
}
function cleanup() {
if (ws) {
// 移除所有事件监听器
ws.onopen = null;
ws.onmessage = null;
ws.onerror = null;
ws.onclose = null;
// 如果连接仍然打开,关闭它
if (
ws.readyState === WebSocket.OPEN ||
ws.readyState === WebSocket.CONNECTING
) {
try {
ws.close();
} catch (e) {
console.error("[WebSocket] 关闭连接时出错:", e);
}
}
ws = null;
}
// 清除重连计时器
if (reconnectTimeout) {
clearTimeout(reconnectTimeout);
reconnectTimeout = null;
}
}
// 创建一个类似Sockette的接口对象
const socketLike = {
ws,
close: () => {
console.log(
`[WebSocket] 手动关闭连接: ${url.replace(secret || "", "***")}`,
);
cleanup();
},
reconnect: () => {
cleanup();
connect();
},
json: (data: any) => {
if (ws && ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify(data));
}
},
send: (data: string) => {
if (ws && ws.readyState === WebSocket.OPEN) {
ws.send(data);
}
},
open: connect,
};
// 立即连接
connect();
return socketLike;
};
// 创建一个空的WebSocket对象
function createDummySocket() {
return {
close: () => {},
reconnect: () => {},
json: () => {},
send: () => {},
open: () => {},
};
}

View File

@@ -112,7 +112,6 @@ export default defineConfig({
id.includes("@tauri-apps/plugin-dialog") || id.includes("@tauri-apps/plugin-dialog") ||
id.includes("@tauri-apps/plugin-fs") || id.includes("@tauri-apps/plugin-fs") ||
id.includes("@tauri-apps/plugin-global-shortcut") || id.includes("@tauri-apps/plugin-global-shortcut") ||
id.includes("@tauri-apps/plugin-notification") ||
id.includes("@tauri-apps/plugin-process") || id.includes("@tauri-apps/plugin-process") ||
id.includes("@tauri-apps/plugin-shell") || id.includes("@tauri-apps/plugin-shell") ||
id.includes("@tauri-apps/plugin-updater") id.includes("@tauri-apps/plugin-updater")