feat: toggle next auto-update time on subscription card click and show update result feedback

This commit is contained in:
wonfen
2025-04-25 17:17:34 +08:00
parent bd3231bfa8
commit d6a79316a6
10 changed files with 391 additions and 43 deletions

View File

@@ -22,6 +22,7 @@ import {
readProfileFile,
updateProfile,
saveProfileFile,
getNextUpdateTime,
} from "@/services/cmds";
import { Notice } from "@/components/base";
import { GroupsEditorViewer } from "@/components/profile/groups-editor-viewer";
@@ -65,9 +66,107 @@ export const ProfileItem = (props: Props) => {
const [position, setPosition] = useState({ left: 0, top: 0 });
const loadingCache = useLoadingCache();
const setLoadingCache = useSetLoadingCache();
// 新增状态:是否显示下次更新时间
const [showNextUpdate, setShowNextUpdate] = useState(false);
const [nextUpdateTime, setNextUpdateTime] = useState("");
const { uid, name = "Profile", extra, updated = 0, option } = itemData;
// 获取下次更新时间的函数
const fetchNextUpdateTime = useLockFn(async (forceRefresh = false) => {
if (itemData.option?.update_interval && itemData.option.update_interval > 0) {
try {
console.log(`尝试获取配置 ${itemData.uid} 的下次更新时间`);
// 如果需要强制刷新先触发Timer.refresh()
if (forceRefresh) {
// 这里可以通过一个新的API来触发刷新但目前我们依赖patch_profile中的刷新
console.log(`强制刷新定时器任务`);
}
const nextUpdate = await getNextUpdateTime(itemData.uid);
console.log(`获取到下次更新时间结果:`, nextUpdate);
if (nextUpdate) {
const nextUpdateDate = dayjs(nextUpdate * 1000);
const now = dayjs();
// 如果已经过期,显示"更新失败"
if (nextUpdateDate.isBefore(now)) {
setNextUpdateTime(t("Last Update failed"));
} else {
// 否则显示剩余时间
const diffMinutes = nextUpdateDate.diff(now, 'minute');
if (diffMinutes < 60) {
if (diffMinutes <= 0) {
setNextUpdateTime(`${t("Next Up")} <1m`);
} else {
setNextUpdateTime(`${t("Next Up")} ${diffMinutes}m`);
}
} else {
const hours = Math.floor(diffMinutes / 60);
const mins = diffMinutes % 60;
setNextUpdateTime(`${t("Next Up")} ${hours}h ${mins}m`);
}
}
} else {
console.log(`返回的下次更新时间为空`);
setNextUpdateTime(t("No schedule"));
}
} catch (err) {
console.error(`获取下次更新时间出错:`, err);
setNextUpdateTime(t("Unknown"));
}
} else {
console.log(`该配置未设置更新间隔或间隔为0`);
setNextUpdateTime(t("Auto update disabled"));
}
});
// 切换显示模式的函数
const toggleUpdateTimeDisplay = (e: React.MouseEvent) => {
e.stopPropagation();
if (!showNextUpdate) {
fetchNextUpdateTime();
}
setShowNextUpdate(!showNextUpdate);
};
// 当组件加载或更新间隔变化时更新下次更新时间
useEffect(() => {
if (showNextUpdate) {
fetchNextUpdateTime();
}
}, [showNextUpdate, itemData.option?.update_interval, updated]);
// 订阅定时器更新事件
useEffect(() => {
// 处理定时器更新事件 - 这个事件专门用于通知定时器变更
const handleTimerUpdate = (event: any) => {
const updatedUid = event.payload as string;
// 只有当更新的是当前配置时才刷新显示
if (updatedUid === itemData.uid && showNextUpdate) {
console.log(`收到定时器更新事件: uid=${updatedUid}`);
setTimeout(() => {
fetchNextUpdateTime(true);
}, 1000);
}
};
// 只注册定时器更新事件监听
window.addEventListener('verge://timer-updated', handleTimerUpdate as EventListener);
return () => {
// 清理事件监听
window.removeEventListener('verge://timer-updated', handleTimerUpdate as EventListener);
};
}, [showNextUpdate, itemData.uid]);
// local file mode
// remote file mode
// remote file mode
@@ -303,6 +402,35 @@ export const ProfileItem = (props: Props) => {
justifyContent: "space-between",
};
// 监听自动更新事件
useEffect(() => {
const handleUpdateStarted = (event: CustomEvent) => {
if (event.detail.uid === itemData.uid) {
setLoadingCache((cache) => ({ ...cache, [itemData.uid]: true }));
}
};
const handleUpdateCompleted = (event: CustomEvent) => {
if (event.detail.uid === itemData.uid) {
setLoadingCache((cache) => ({ ...cache, [itemData.uid]: false }));
// 更新完成后刷新显示
if (showNextUpdate) {
fetchNextUpdateTime();
}
}
};
// 注册事件监听
window.addEventListener('profile-update-started', handleUpdateStarted as EventListener);
window.addEventListener('profile-update-completed', handleUpdateCompleted as EventListener);
return () => {
// 清理事件监听
window.removeEventListener('profile-update-started', handleUpdateStarted as EventListener);
window.removeEventListener('profile-update-completed', handleUpdateCompleted as EventListener);
};
}, [itemData.uid, showNextUpdate]);
return (
<Box
sx={{
@@ -413,15 +541,30 @@ export const ProfileItem = (props: Props) => {
)
)}
{hasUrl && (
<Typography
noWrap
flex="1 0 auto"
fontSize={14}
textAlign="right"
title={`${t("Update Time")}: ${parseExpire(updated)}`}
>
{updated > 0 ? dayjs(updated * 1000).fromNow() : ""}
</Typography>
<Box sx={{ display: "flex", justifyContent: "flex-end", ml: "auto" }}>
<Typography
noWrap
component="span"
fontSize={14}
textAlign="right"
title={showNextUpdate ? t("Click to show last update time") : `${t("Update Time")}: ${parseExpire(updated)}\n${t("Click to show next update")}`}
sx={{
cursor: "pointer",
display: "inline-block",
borderBottom: "1px dashed transparent",
transition: "all 0.2s",
"&:hover": {
borderBottomColor: "primary.main",
color: "primary.main",
}
}}
onClick={toggleUpdateTimeDisplay}
>
{showNextUpdate
? nextUpdateTime
: (updated > 0 ? dayjs(updated * 1000).fromNow() : "")}
</Typography>
</Box>
)}
</>
}

View File

@@ -563,6 +563,11 @@
"Administrator + Service Mode": "Admin + Service Mode",
"Last Check Update": "Last Check Update",
"Click to import subscription": "Click to import subscription",
"Last Update failed": "Last Update failed",
"Next Up": "Next Up",
"No schedule": "No schedule",
"Unknown": "Unknown",
"Auto update disabled": "Auto update disabled",
"Update subscription successfully": "Update subscription successfully",
"Update failed, retrying with Clash proxy...": "Update failed, retrying with Clash proxy...",
"Update with Clash proxy successfully": "Update with Clash proxy successfully",

View File

@@ -563,6 +563,11 @@
"Administrator + Service Mode": "管理员 + 服务模式",
"Last Check Update": "最后检查更新",
"Click to import subscription": "点击导入订阅",
"Last Update failed": "上次更新失败",
"Next Up": "下次更新",
"No schedule": "没有计划",
"Unknown": "未知",
"Auto update disabled": "自动更新已禁用",
"Update subscription successfully": "订阅更新成功",
"Update failed, retrying with Clash proxy...": "订阅更新失败,尝试使用 Clash 代理更新",
"Update with Clash proxy successfully": "使用 Clash 代理更新成功",

View File

@@ -359,3 +359,7 @@ export const isAdmin = async () => {
return false;
}
};
export async function getNextUpdateTime(uid: string) {
return invoke<number | null>("get_next_update_time", { uid });
}