mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-07-18 19:32:47 +08:00
🐛 fix(updater): 修复关于页正式版信息兜底
- 为关于页补充固定项目元数据兜底 - 过滤 Unknown/未知 等无效版本与作者信息 - 增加正式版 AppInfo 缺失时的回归测试
This commit is contained in:
@@ -245,6 +245,42 @@ describe('useAppUpdateManager', () => {
|
||||
expect(hook?.lastUpdateInfo?.releaseNotesUrl).toBe('https://github.com/Syngnat/GoNavi/releases/tag/dev-latest');
|
||||
});
|
||||
|
||||
it('keeps official about metadata usable when backend app info is incomplete', async () => {
|
||||
backendApp.GetAppInfo.mockResolvedValue({
|
||||
success: true,
|
||||
data: {
|
||||
version: '',
|
||||
author: 'Unknown',
|
||||
},
|
||||
});
|
||||
backendApp.CheckForUpdates.mockResolvedValue({
|
||||
success: true,
|
||||
data: {
|
||||
hasUpdate: false,
|
||||
currentVersion: '0.8.5',
|
||||
latestVersion: '0.8.5',
|
||||
},
|
||||
});
|
||||
|
||||
renderHook();
|
||||
|
||||
await act(async () => {
|
||||
await hook?.checkForUpdates(false);
|
||||
});
|
||||
await act(async () => {
|
||||
hook?.setIsAboutOpen(true);
|
||||
await Promise.resolve();
|
||||
await Promise.resolve();
|
||||
});
|
||||
|
||||
expect(hook?.aboutDisplayVersion).toBe('0.8.5');
|
||||
expect(hook?.aboutInfo?.author).toBe('Syngnat');
|
||||
expect(hook?.aboutInfo?.repoUrl).toBe('https://github.com/Syngnat/GoNavi');
|
||||
expect(hook?.aboutInfo?.issueUrl).toBe('https://github.com/Syngnat/GoNavi/issues');
|
||||
expect(hook?.aboutInfo?.releaseUrl).toBe('https://github.com/Syngnat/GoNavi/releases');
|
||||
expect(messageApi.error).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('opens the downloaded update directory when a package is already downloaded', async () => {
|
||||
backendApp.CheckForUpdates.mockResolvedValue({
|
||||
success: true,
|
||||
|
||||
@@ -45,6 +45,25 @@ type UseAppUpdateManagerOptions = {
|
||||
t: Translator;
|
||||
};
|
||||
|
||||
type AboutInfo = {
|
||||
version: string;
|
||||
author: string;
|
||||
buildTime?: string;
|
||||
repoUrl?: string;
|
||||
issueUrl?: string;
|
||||
releaseUrl?: string;
|
||||
communityUrl?: string;
|
||||
};
|
||||
|
||||
const DEFAULT_ABOUT_INFO: AboutInfo = {
|
||||
version: '',
|
||||
author: 'Syngnat',
|
||||
repoUrl: 'https://github.com/Syngnat/GoNavi',
|
||||
issueUrl: 'https://github.com/Syngnat/GoNavi/issues',
|
||||
releaseUrl: 'https://github.com/Syngnat/GoNavi/releases',
|
||||
communityUrl: 'https://aibook.ren',
|
||||
};
|
||||
|
||||
const createEmptyDownloadProgress = () => ({
|
||||
open: false,
|
||||
version: '',
|
||||
@@ -64,6 +83,44 @@ const buildUpdateKey = (info: Pick<UpdateInfo, 'channel' | 'latestVersion'> | nu
|
||||
? `${normalizeUpdateChannel(info.channel)}:${String(info.latestVersion || '').trim()}`
|
||||
: '';
|
||||
|
||||
const isUnknownAboutValue = (value: string): boolean => {
|
||||
const normalized = value.trim().toLowerCase();
|
||||
return normalized === 'unknown' || normalized === '未知' || normalized === 'common.unknown';
|
||||
};
|
||||
|
||||
const normalizeAboutText = (value: unknown): string =>
|
||||
String(value || '').trim();
|
||||
|
||||
const normalizeAboutVersion = (value: unknown): string => {
|
||||
const text = normalizeAboutText(value);
|
||||
if (!text || text === '0.0.0' || isUnknownAboutValue(text)) {
|
||||
return '';
|
||||
}
|
||||
return text;
|
||||
};
|
||||
|
||||
const normalizeAboutInfo = (value: unknown): AboutInfo => {
|
||||
const source = (value && typeof value === 'object' ? value : {}) as Record<string, unknown>;
|
||||
const version = normalizeAboutVersion(source.version);
|
||||
const author = normalizeAboutText(source.author);
|
||||
const buildTime = normalizeAboutText(source.buildTime);
|
||||
const repoUrl = normalizeAboutText(source.repoUrl);
|
||||
const issueUrl = normalizeAboutText(source.issueUrl);
|
||||
const releaseUrl = normalizeAboutText(source.releaseUrl);
|
||||
const communityUrl = normalizeAboutText(source.communityUrl);
|
||||
|
||||
return {
|
||||
...DEFAULT_ABOUT_INFO,
|
||||
version,
|
||||
author: author && !isUnknownAboutValue(author) ? author : DEFAULT_ABOUT_INFO.author,
|
||||
buildTime: buildTime || undefined,
|
||||
repoUrl: repoUrl || DEFAULT_ABOUT_INFO.repoUrl,
|
||||
issueUrl: issueUrl || DEFAULT_ABOUT_INFO.issueUrl,
|
||||
releaseUrl: releaseUrl || DEFAULT_ABOUT_INFO.releaseUrl,
|
||||
communityUrl: communityUrl || DEFAULT_ABOUT_INFO.communityUrl,
|
||||
};
|
||||
};
|
||||
|
||||
const shouldAutoInstallDownloadedUpdate = (resultData: UpdateDownloadResultData | null | undefined): boolean => {
|
||||
const platform = String(resultData?.platform || '').trim().toLowerCase();
|
||||
return platform === 'darwin' && resultData?.autoRelaunch !== false;
|
||||
@@ -87,19 +144,14 @@ export const useAppUpdateManager = ({
|
||||
const [updateChannel, setUpdateChannelState] = useState<UpdateChannel>('latest');
|
||||
const [isUpdateChannelLoading, setIsUpdateChannelLoading] = useState(false);
|
||||
const [isUpdateChannelSaving, setIsUpdateChannelSaving] = useState(false);
|
||||
const [aboutInfo, setAboutInfo] = useState<{
|
||||
version: string;
|
||||
author: string;
|
||||
buildTime?: string;
|
||||
repoUrl?: string;
|
||||
issueUrl?: string;
|
||||
releaseUrl?: string;
|
||||
communityUrl?: string;
|
||||
} | null>(null);
|
||||
const aboutDisplayVersion = resolveAboutDisplayVersion(runtimeBuildType, aboutInfo?.version);
|
||||
const [aboutInfo, setAboutInfo] = useState<AboutInfo>(() => DEFAULT_ABOUT_INFO);
|
||||
const [aboutUpdateStatus, setAboutUpdateStatus] = useState<string>('');
|
||||
const [lastUpdateInfo, setLastUpdateInfo] = useState<UpdateInfo | null>(null);
|
||||
const [updateDownloadProgress, setUpdateDownloadProgress] = useState(createEmptyDownloadProgress);
|
||||
const aboutDisplayVersion = resolveAboutDisplayVersion(
|
||||
runtimeBuildType,
|
||||
normalizeAboutVersion(aboutInfo.version) || normalizeAboutVersion(lastUpdateInfo?.currentVersion),
|
||||
);
|
||||
const lastUpdateKey = buildUpdateKey(lastUpdateInfo);
|
||||
|
||||
const formatAboutUpdateStatus = useCallback((info: UpdateInfo | null): string => {
|
||||
@@ -404,13 +456,26 @@ export const useAppUpdateManager = ({
|
||||
|
||||
const loadAboutInfo = useCallback(async () => {
|
||||
setAboutLoading(true);
|
||||
const res = await (window as any).go.app.App.GetAppInfo();
|
||||
if (res?.success) {
|
||||
setAboutInfo(res.data);
|
||||
} else {
|
||||
void message.error(t('app.about.message.load_failed', { error: res?.message || t('common.unknown') }));
|
||||
try {
|
||||
const backendApp = (window as any).go?.app?.App;
|
||||
if (typeof backendApp?.GetAppInfo !== 'function') {
|
||||
setAboutInfo(DEFAULT_ABOUT_INFO);
|
||||
return;
|
||||
}
|
||||
const res = await backendApp.GetAppInfo();
|
||||
if (res?.success) {
|
||||
setAboutInfo(normalizeAboutInfo(res.data));
|
||||
} else {
|
||||
setAboutInfo(DEFAULT_ABOUT_INFO);
|
||||
void message.error(t('app.about.message.load_failed', { error: res?.message || t('common.unknown') }));
|
||||
}
|
||||
} catch (e: any) {
|
||||
setAboutInfo(DEFAULT_ABOUT_INFO);
|
||||
const error = e?.message || t('common.unknown');
|
||||
void message.error(t('app.about.message.load_failed', { error }));
|
||||
} finally {
|
||||
setAboutLoading(false);
|
||||
}
|
||||
setAboutLoading(false);
|
||||
}, [t]);
|
||||
|
||||
const loadUpdateChannel = useCallback(async () => {
|
||||
|
||||
Reference in New Issue
Block a user