fix: optimize asynchronous handling to prevent UI blocking in various components

fix: add missing showNotice error handling and improve async UI feedback

- Add showNotice error notifications to unlock page async error branches
- Restore showNotice for YAML serialization errors in rules/groups/proxies editor
- Ensure all user-facing async errors are surfaced via showNotice
- Add fade-in animation to layout for smoother theme transition and reduce white screen
- Use requestIdleCallback/setTimeout for heavy UI state updates to avoid UI blocking
- Minor: remove window.showNotice usage, use direct import instead
This commit is contained in:
Tunglies
2025-05-30 17:34:38 +08:00
parent 756d303f6a
commit 1e3566ed7d
10 changed files with 378 additions and 275 deletions

View File

@@ -24,6 +24,7 @@ import {
RefreshRounded,
AccessTimeOutlined,
} from "@mui/icons-material";
import { showNotice } from "@/services/noticeService";
// 定义流媒体检测项类型
interface UnlockItem {
@@ -121,61 +122,67 @@ const UnlockPage = () => {
}
};
// invoke加超时防止后端卡死
const invokeWithTimeout = async <T,>(
cmd: string,
args?: any,
timeout = 15000,
): Promise<T> => {
return Promise.race([
invoke<T>(cmd, args),
new Promise<T>((_, reject) => setTimeout(() => reject(new Error("Timeout")), timeout)),
]);
};
// 执行全部项目检测
const checkAllMedia = useLockFn(async () => {
try {
setIsCheckingAll(true);
const result = await invoke<UnlockItem[]>("check_media_unlock");
const result = await invokeWithTimeout<UnlockItem[]>("check_media_unlock");
const sortedItems = sortItemsByName(result);
// 更新UI
setUnlockItems(sortedItems);
const currentTime = new Date().toLocaleString();
setLastCheckTime(currentTime);
// 保存结果到本地存储
saveResultsToStorage(sortedItems, currentTime);
setIsCheckingAll(false);
} catch (err: any) {
setIsCheckingAll(false);
showNotice('error', err?.message || err?.toString() || '检测超时或失败');
alert("检测超时或失败: " + (err?.message || err));
console.error("Failed to check media unlock:", err);
}
});
// 根据项目名称检测单个流媒体服务
// 检测单个流媒体服务
const checkSingleMedia = useLockFn(async (name: string) => {
try {
// 将该项目添加到加载状态
setLoadingItems((prev) => [...prev, name]);
const result = await invokeWithTimeout<UnlockItem[]>("check_media_unlock");
// 执行检测
const result = await invoke<UnlockItem[]>("check_media_unlock");
// 找到对应的检测结果
const targetItem = result.find((item: UnlockItem) => item.name === name);
if (targetItem) {
// 更新单个检测项结果并按名称排序
const updatedItems = sortItemsByName(
unlockItems.map((item: UnlockItem) =>
item.name === name ? targetItem : item,
),
);
// 更新UI
setUnlockItems(updatedItems);
const currentTime = new Date().toLocaleString();
setLastCheckTime(currentTime);
// 保存结果到本地存储
saveResultsToStorage(updatedItems, currentTime);
}
// 移除加载状态
setLoadingItems((prev) => prev.filter((item) => item !== name));
} catch (err: any) {
setLoadingItems((prev) => prev.filter((item) => item !== name));
showNotice('error', err?.message || err?.toString() || `检测${name}失败`);
alert("检测超时或失败: " + (err?.message || err));
console.error(`Failed to check ${name}:`, err);
}
});