feat: toggle next auto-update time on subscription card click and show update result feedback
This commit is contained in:
@@ -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>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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 代理更新成功",
|
||||
|
||||
@@ -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 });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user