ef9e87fb29
- 移除自定义createId函数,统一使用nanoid库生成唯一标识符 - 将useEffectiveAiConfig Hook替换为resolveEffectiveConfig工具函数 - 直接从config store获取publicSettings避免额外的Hook依赖 - 更新所有组件中的ID生成调用以使用nanoid - 简化buildApiUrl函数中的基础URL规范化逻辑
95 lines
3.2 KiB
TypeScript
95 lines
3.2 KiB
TypeScript
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
import { App } from "antd";
|
|
import { APP_VERSION } from "@/constant/env";
|
|
import { parseChangelog, type ReleaseInfo } from "@/lib/release";
|
|
|
|
const latestVersionUrl = "https://raw.githubusercontent.com/basketikun/infinite-canvas/main/VERSION";
|
|
const latestChangelogUrl = "https://raw.githubusercontent.com/basketikun/infinite-canvas/main/CHANGELOG.md";
|
|
|
|
function readLocalReleases(): ReleaseInfo[] {
|
|
try {
|
|
return JSON.parse(process.env.NEXT_PUBLIC_APP_RELEASES || "[]");
|
|
} catch {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
function toVersionParts(version: string) {
|
|
const match = version.trim().match(/^v?(\d+)\.(\d+)\.(\d+)/);
|
|
return match ? match.slice(1).map(Number) : null;
|
|
}
|
|
|
|
function isNewerVersion(latestVersion: string, currentVersion: string) {
|
|
const latest = toVersionParts(latestVersion);
|
|
const current = toVersionParts(currentVersion);
|
|
if (!latest || !current) return false;
|
|
return latest.some((value, index) => value > current[index] && latest.slice(0, index).every((part, prevIndex) => part === current[prevIndex]));
|
|
}
|
|
|
|
export function useVersionCheck() {
|
|
const currentVersion = APP_VERSION;
|
|
const { message } = App.useApp();
|
|
const localReleases = useMemo(readLocalReleases, []);
|
|
const [latestVersion, setLatestVersion] = useState(currentVersion);
|
|
const [releases, setReleases] = useState<ReleaseInfo[]>(localReleases);
|
|
const [checking, setChecking] = useState(false);
|
|
const [open, setOpen] = useState(false);
|
|
const hasNewVersion = isNewerVersion(latestVersion, currentVersion);
|
|
|
|
const checkLatestVersion = useCallback(async () => {
|
|
try {
|
|
const response = await fetch(latestVersionUrl);
|
|
if (!response.ok) return false;
|
|
const version = await response.text();
|
|
setLatestVersion(version.trim() || currentVersion);
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}, [currentVersion]);
|
|
|
|
const checkLatestRelease = useCallback(async (showMessage = false) => {
|
|
setChecking(true);
|
|
try {
|
|
const [versionResponse, changelogResponse] = await Promise.all([
|
|
fetch(latestVersionUrl),
|
|
fetch(latestChangelogUrl),
|
|
]);
|
|
if (!versionResponse.ok) throw new Error("版本读取失败");
|
|
if (!changelogResponse.ok) throw new Error("更新日志读取失败");
|
|
const [version, changelog] = await Promise.all([versionResponse.text(), changelogResponse.text()]);
|
|
setLatestVersion(version.trim() || currentVersion);
|
|
if (changelog.trim()) setReleases(parseChangelog(changelog));
|
|
if (showMessage) message.success("已获取最新版本信息");
|
|
return true;
|
|
} catch {
|
|
setLatestVersion(currentVersion);
|
|
setReleases(localReleases);
|
|
if (showMessage) message.error("获取最新版本信息失败");
|
|
return false;
|
|
} finally {
|
|
setChecking(false);
|
|
}
|
|
}, [currentVersion, localReleases, message]);
|
|
|
|
useEffect(() => {
|
|
void checkLatestVersion();
|
|
}, [checkLatestVersion]);
|
|
|
|
const openReleaseModal = useCallback(() => {
|
|
setOpen(true);
|
|
void checkLatestRelease();
|
|
}, [checkLatestRelease]);
|
|
|
|
return {
|
|
open,
|
|
setOpen,
|
|
openReleaseModal,
|
|
latestVersion,
|
|
releases,
|
|
checking,
|
|
hasNewVersion,
|
|
checkLatestRelease,
|
|
};
|
|
}
|