Enhance routing and settings functionality (#29)

This commit is contained in:
samanhappy
2025-04-21 17:51:21 +08:00
committed by GitHub
parent 6bf22025e1
commit afd1ee7a50
21 changed files with 541 additions and 131 deletions

View File

@@ -2,16 +2,16 @@ import { useState, useEffect, useRef, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { Server, ApiResponse } from '@/types';
// 配置选项
// Configuration options
const CONFIG = {
// 初始化启动阶段的配置
// Initialization phase configuration
startup: {
maxAttempts: 60, // 初始化阶段最大尝试次数
pollingInterval: 3000 // 初始阶段轮询间隔 (3秒)
maxAttempts: 60, // Maximum number of attempts during initialization
pollingInterval: 3000 // Polling interval during initialization (3 seconds)
},
// 正常运行阶段的配置
// Normal operation phase configuration
normal: {
pollingInterval: 10000 // 正常运行时的轮询间隔 (10秒)
pollingInterval: 10000 // Polling interval during normal operation (10 seconds)
}
};
@@ -23,12 +23,12 @@ export const useServerData = () => {
const [isInitialLoading, setIsInitialLoading] = useState(true);
const [fetchAttempts, setFetchAttempts] = useState(0);
// 轮询定时器引用
// Timer reference for polling
const intervalRef = useRef<NodeJS.Timeout | null>(null);
// 保存当前尝试次数,避免依赖循环
// Track current attempt count to avoid dependency cycles
const attemptsRef = useRef<number>(0);
// 清理定时器
// Clear the timer
const clearTimer = () => {
if (intervalRef.current) {
clearInterval(intervalRef.current);
@@ -36,9 +36,9 @@ export const useServerData = () => {
}
};
// 开始正常轮询
// Start normal polling
const startNormalPolling = useCallback(() => {
// 确保没有其他定时器在运行
// Ensure no other timers are running
clearTimer();
const fetchServers = async () => {
@@ -60,12 +60,12 @@ export const useServerData = () => {
setServers([]);
}
// 重置错误状态
// Reset error state
setError(null);
} catch (err) {
console.error('Error fetching servers during normal polling:', err);
// 使用友好的错误消息
// Use friendly error message
if (!navigator.onLine) {
setError(t('errors.network'));
} else if (err instanceof TypeError && (
@@ -79,21 +79,21 @@ export const useServerData = () => {
}
};
// 立即执行一次
// Execute immediately
fetchServers();
// 设置定期轮询
// Set up regular polling
intervalRef.current = setInterval(fetchServers, CONFIG.normal.pollingInterval);
}, [t]);
useEffect(() => {
// 重置尝试计数
// Reset attempt count
if (refreshKey > 0) {
attemptsRef.current = 0;
setFetchAttempts(0);
}
// 初始化加载阶段的请求函数
// Initialization phase request function
const fetchInitialData = async () => {
try {
const token = localStorage.getItem('mcphub_token');
@@ -104,51 +104,51 @@ export const useServerData = () => {
});
const data = await response.json();
// 处理API响应中的包装对象提取data字段
// Handle API response wrapper object, extract data field
if (data && data.success && Array.isArray(data.data)) {
setServers(data.data);
setIsInitialLoading(false);
// 初始化成功,开始正常轮询
// Initialization successful, start normal polling
startNormalPolling();
return true;
} else if (data && Array.isArray(data)) {
// 兼容性处理如果API直接返回数组
// Compatibility handling, if API directly returns array
setServers(data);
setIsInitialLoading(false);
// 初始化成功,开始正常轮询
// Initialization successful, start normal polling
startNormalPolling();
return true;
} else {
// 如果数据格式不符合预期,设置为空数组
// If data format is not as expected, set to empty array
console.error('Invalid server data format:', data);
setServers([]);
setIsInitialLoading(false);
// 初始化成功但数据为空,开始正常轮询
// Initialization successful but data is empty, start normal polling
startNormalPolling();
return true;
}
} catch (err) {
// 增加尝试次数计数,使用 ref 避免触发 effect 重新运行
// Increment attempt count, use ref to avoid triggering effect rerun
attemptsRef.current += 1;
console.error(`Initial loading attempt ${attemptsRef.current} failed:`, err);
// 更新状态用于显示
// Update state for display
setFetchAttempts(attemptsRef.current);
// 设置适当的错误消息
// Set appropriate error message
if (!navigator.onLine) {
setError(t('errors.network'));
} else {
setError(t('errors.initialStartup'));
}
// 如果已超过最大尝试次数,放弃初始化并切换到正常轮询
// If maximum attempt count is exceeded, give up initialization and switch to normal polling
if (attemptsRef.current >= CONFIG.startup.maxAttempts) {
console.log('Maximum startup attempts reached, switching to normal polling');
setIsInitialLoading(false);
// 清除初始化的轮询
// Clear initialization polling
clearTimer();
// 切换到正常轮询模式
// Switch to normal polling mode
startNormalPolling();
}
@@ -156,45 +156,45 @@ export const useServerData = () => {
}
};
// 组件挂载时,根据当前状态设置适当的轮询
// On component mount, set appropriate polling based on current state
if (isInitialLoading) {
// 确保没有其他定时器在运行
// Ensure no other timers are running
clearTimer();
// 立即执行一次初始请求
// Execute initial request immediately
fetchInitialData();
// 设置初始阶段的轮询间隔
// Set polling interval for initialization phase
intervalRef.current = setInterval(fetchInitialData, CONFIG.startup.pollingInterval);
console.log(`Started initial polling with interval: ${CONFIG.startup.pollingInterval}ms`);
} else {
// 已经初始化完成,开始正常轮询
// Initialization completed, start normal polling
startNormalPolling();
}
// 清理函数
// Cleanup function
return () => {
clearTimer();
};
}, [refreshKey, t, isInitialLoading, startNormalPolling]);
// 手动触发刷新
// Manually trigger refresh
const triggerRefresh = () => {
// 清除当前的定时器
// Clear current timer
clearTimer();
// 如果在初始化阶段,重置初始化状态
// If in initialization phase, reset initialization state
if (isInitialLoading) {
setIsInitialLoading(true);
attemptsRef.current = 0;
setFetchAttempts(0);
}
// refreshKey 的改变会触发 useEffect 再次运行
// Change in refreshKey will trigger useEffect to run again
setRefreshKey(prevKey => prevKey + 1);
};
// 服务器相关操作
// Server related operations
const handleServerAdd = () => {
setRefreshKey(prevKey => prevKey + 1);
};