Merge branch 'fix-migrate-tauri2-errors'
* fix-migrate-tauri2-errors: (288 commits) # Conflicts: # .github/ISSUE_TEMPLATE/bug_report.yml
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import useSWR, { mutate } from "swr";
|
||||
import { useLockFn } from "ahooks";
|
||||
import { getAxios, getVersion, updateConfigs } from "@/services/api";
|
||||
import { getAxios, getVersion } from "@/services/api";
|
||||
import {
|
||||
getClashInfo,
|
||||
patchClashConfig,
|
||||
@@ -10,16 +10,15 @@ import {
|
||||
export const useClash = () => {
|
||||
const { data: clash, mutate: mutateClash } = useSWR(
|
||||
"getRuntimeConfig",
|
||||
getRuntimeConfig
|
||||
getRuntimeConfig,
|
||||
);
|
||||
|
||||
const { data: versionData, mutate: mutateVersion } = useSWR(
|
||||
"getVersion",
|
||||
getVersion
|
||||
getVersion,
|
||||
);
|
||||
|
||||
const patchClash = useLockFn(async (patch: Partial<IConfigData>) => {
|
||||
await updateConfigs(patch);
|
||||
await patchClashConfig(patch);
|
||||
mutateClash();
|
||||
});
|
||||
@@ -27,8 +26,8 @@ export const useClash = () => {
|
||||
const version = versionData?.premium
|
||||
? `${versionData.version} Premium`
|
||||
: versionData?.meta
|
||||
? `${versionData.version} Mihomo`
|
||||
: versionData?.version || "-";
|
||||
? `${versionData.version} Mihomo`
|
||||
: versionData?.version || "-";
|
||||
|
||||
return {
|
||||
clash,
|
||||
@@ -42,7 +41,7 @@ export const useClash = () => {
|
||||
export const useClashInfo = () => {
|
||||
const { data: clashInfo, mutate: mutateInfo } = useSWR(
|
||||
"getClashInfo",
|
||||
getClashInfo
|
||||
getClashInfo,
|
||||
);
|
||||
|
||||
const patchInfo = async (
|
||||
@@ -57,7 +56,7 @@ export const useClashInfo = () => {
|
||||
| "external-controller"
|
||||
| "secret"
|
||||
>
|
||||
>
|
||||
>,
|
||||
) => {
|
||||
const hasInfo =
|
||||
patch["redir-port"] != null ||
|
||||
|
||||
31
src/hooks/use-listen.ts
Normal file
31
src/hooks/use-listen.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { listen, UnlistenFn, EventCallback } from "@tauri-apps/api/event";
|
||||
import { event } from "@tauri-apps/api";
|
||||
import { useRef } from "react";
|
||||
|
||||
export const useListen = () => {
|
||||
const unlistenFns = useRef<UnlistenFn[]>([]);
|
||||
|
||||
const addListener = async <T>(
|
||||
eventName: string,
|
||||
handler: EventCallback<T>,
|
||||
) => {
|
||||
const unlisten = await listen(eventName, handler);
|
||||
unlistenFns.current.push(unlisten);
|
||||
return unlisten;
|
||||
};
|
||||
const removeAllListeners = () => {
|
||||
unlistenFns.current.forEach((unlisten) => unlisten());
|
||||
unlistenFns.current = [];
|
||||
};
|
||||
|
||||
const setupCloseListener = async function () {
|
||||
await event.once("tauri://close-requested", async () => {
|
||||
removeAllListeners();
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
addListener,
|
||||
setupCloseListener,
|
||||
};
|
||||
};
|
||||
@@ -1,57 +1,105 @@
|
||||
import useSWRSubscription from "swr/subscription";
|
||||
import { useEffect } from "react";
|
||||
import { useEnableLog } from "../services/states";
|
||||
import { createSockette } from "../utils/websocket";
|
||||
import { useClashInfo } from "./use-clash";
|
||||
import dayjs from "dayjs";
|
||||
import { getClashLogs } from "../services/cmds";
|
||||
import { create } from "zustand";
|
||||
|
||||
const MAX_LOG_NUM = 1000;
|
||||
|
||||
export const useLogData = () => {
|
||||
const { clashInfo } = useClashInfo();
|
||||
export type LogLevel = "warning" | "info" | "debug" | "error" | "all";
|
||||
|
||||
const [enableLog] = useEnableLog();
|
||||
!enableLog || !clashInfo;
|
||||
interface ILogItem {
|
||||
time?: string;
|
||||
type: string;
|
||||
payload: string;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
return useSWRSubscription<ILogItem[], any, "getClashLog" | null>(
|
||||
enableLog && clashInfo ? "getClashLog" : null,
|
||||
(_key, { next }) => {
|
||||
const { server = "", secret = "" } = clashInfo!;
|
||||
const buildWSUrl = (server: string, secret: string, logLevel: LogLevel) => {
|
||||
const baseUrl = `ws://${server}/logs`;
|
||||
const params = new URLSearchParams();
|
||||
|
||||
// populate the initial logs
|
||||
getClashLogs().then(
|
||||
(logs) => next(null, logs),
|
||||
(err) => next(err)
|
||||
);
|
||||
|
||||
const s = createSockette(
|
||||
`ws://${server}/logs?token=${encodeURIComponent(secret)}`,
|
||||
{
|
||||
onmessage(event) {
|
||||
const data = JSON.parse(event.data) as ILogItem;
|
||||
|
||||
// append new log item on socket message
|
||||
next(null, (l = []) => {
|
||||
const time = dayjs().format("MM-DD HH:mm:ss");
|
||||
|
||||
if (l.length >= MAX_LOG_NUM) l.shift();
|
||||
return [...l, { ...data, time }];
|
||||
});
|
||||
},
|
||||
onerror(event) {
|
||||
this.close();
|
||||
next(event);
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
return () => {
|
||||
s.close();
|
||||
};
|
||||
},
|
||||
{
|
||||
fallbackData: [],
|
||||
keepPreviousData: true,
|
||||
}
|
||||
);
|
||||
if (secret) {
|
||||
params.append("token", encodeURIComponent(secret));
|
||||
}
|
||||
if (logLevel === "all") {
|
||||
params.append("level", "debug");
|
||||
} else {
|
||||
params.append("level", logLevel);
|
||||
}
|
||||
const queryString = params.toString();
|
||||
return queryString ? `${baseUrl}?${queryString}` : baseUrl;
|
||||
};
|
||||
|
||||
interface LogStore {
|
||||
logs: Record<LogLevel, ILogItem[]>;
|
||||
clearLogs: (level?: LogLevel) => void;
|
||||
appendLog: (level: LogLevel, log: ILogItem) => void;
|
||||
}
|
||||
|
||||
const useLogStore = create<LogStore>(
|
||||
(set: (fn: (state: LogStore) => Partial<LogStore>) => void) => ({
|
||||
logs: {
|
||||
warning: [],
|
||||
info: [],
|
||||
debug: [],
|
||||
error: [],
|
||||
all: [],
|
||||
},
|
||||
clearLogs: (level?: LogLevel) =>
|
||||
set((state: LogStore) => ({
|
||||
logs: level
|
||||
? { ...state.logs, [level]: [] }
|
||||
: { warning: [], info: [], debug: [], error: [], all: [] },
|
||||
})),
|
||||
appendLog: (level: LogLevel, log: ILogItem) =>
|
||||
set((state: LogStore) => {
|
||||
const currentLogs = state.logs[level];
|
||||
const newLogs =
|
||||
currentLogs.length >= MAX_LOG_NUM
|
||||
? [...currentLogs.slice(1), log]
|
||||
: [...currentLogs, log];
|
||||
return { logs: { ...state.logs, [level]: newLogs } };
|
||||
}),
|
||||
}),
|
||||
);
|
||||
|
||||
export const useLogData = (logLevel: LogLevel) => {
|
||||
const { clashInfo } = useClashInfo();
|
||||
const [enableLog] = useEnableLog();
|
||||
const { logs, appendLog } = useLogStore();
|
||||
|
||||
useEffect(() => {
|
||||
if (!enableLog || !clashInfo) return;
|
||||
|
||||
const { server = "", secret = "" } = clashInfo;
|
||||
const wsUrl = buildWSUrl(server, secret, logLevel);
|
||||
|
||||
let isActive = true;
|
||||
const socket = createSockette(wsUrl, {
|
||||
onmessage(event) {
|
||||
if (!isActive) return;
|
||||
const data = JSON.parse(event.data) as ILogItem;
|
||||
const time = dayjs().format("MM-DD HH:mm:ss");
|
||||
appendLog(logLevel, { ...data, time });
|
||||
},
|
||||
onerror() {
|
||||
if (!isActive) return;
|
||||
socket.close();
|
||||
},
|
||||
});
|
||||
|
||||
return () => {
|
||||
isActive = false;
|
||||
socket.close();
|
||||
};
|
||||
}, [clashInfo, enableLog, logLevel]);
|
||||
|
||||
return logs[logLevel];
|
||||
};
|
||||
|
||||
// 导出清空日志的方法
|
||||
export const clearLogs = (level?: LogLevel) => {
|
||||
useLogStore.getState().clearLogs(level);
|
||||
};
|
||||
|
||||
@@ -4,7 +4,10 @@ import { getVergeConfig, patchVergeConfig } from "@/services/cmds";
|
||||
export const useVerge = () => {
|
||||
const { data: verge, mutate: mutateVerge } = useSWR(
|
||||
"getVergeConfig",
|
||||
getVergeConfig
|
||||
async () => {
|
||||
const config = await getVergeConfig();
|
||||
return config;
|
||||
},
|
||||
);
|
||||
|
||||
const patchVerge = async (value: Partial<IVergeConfig>) => {
|
||||
|
||||
Reference in New Issue
Block a user