mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-08-21 16:34:21 +08:00
Merge remote-tracking branch 'origin/dev' into dev
This commit is contained in:
@@ -2456,6 +2456,8 @@ function App() {
|
||||
close: () => void;
|
||||
isOpen: () => boolean;
|
||||
} | null>(null);
|
||||
// 手动「检查更新」发现新版本时,由 useAppUpdateManager 触发打开更新日志弹窗
|
||||
const openReleaseNotesOnManualCheckRef = useRef<(() => void) | null>(null);
|
||||
const {
|
||||
aboutDisplayVersion,
|
||||
aboutInfo,
|
||||
@@ -2489,6 +2491,7 @@ function App() {
|
||||
runtimeBuildType,
|
||||
t,
|
||||
updateCenterBridgeRef,
|
||||
onManualCheckHasUpdateRef: openReleaseNotesOnManualCheckRef,
|
||||
});
|
||||
const [aboutLastCheckedAt, setAboutLastCheckedAt] = useState('');
|
||||
const [releaseNotesModalOpen, setReleaseNotesModalOpen] = useState(false);
|
||||
@@ -3637,6 +3640,14 @@ function App() {
|
||||
updateCenterBridgeRef.current = null;
|
||||
};
|
||||
}, [handleOpenSettingsCenterPane]);
|
||||
useEffect(() => {
|
||||
openReleaseNotesOnManualCheckRef.current = () => {
|
||||
setReleaseNotesModalOpen(true);
|
||||
};
|
||||
return () => {
|
||||
openReleaseNotesOnManualCheckRef.current = null;
|
||||
};
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
if (!isSettingsAboutPaneOpen) {
|
||||
return;
|
||||
@@ -5409,7 +5420,7 @@ function App() {
|
||||
key="check"
|
||||
icon={<CloudDownloadOutlined />}
|
||||
loading={isCheckingForUpdates}
|
||||
onClick={() => checkForUpdates(false)}
|
||||
onClick={() => checkForUpdates(false, true)}
|
||||
>
|
||||
{t('app.about.action.check_updates')}
|
||||
</Button>,
|
||||
|
||||
@@ -669,6 +669,46 @@ describe('useAppUpdateManager', () => {
|
||||
expect(hook?.lastUpdateInfo?.channel).toBe('dev');
|
||||
});
|
||||
|
||||
it('does not invoke the manual-check bridge when a channel change re-check finds an update', async () => {
|
||||
const openReleaseNotes = vi.fn();
|
||||
const openReleaseNotesRef = { current: openReleaseNotes };
|
||||
|
||||
backendApp.SetUpdateChannel.mockResolvedValue({ success: true, data: { channel: 'dev' } });
|
||||
backendApp.CheckForUpdates.mockResolvedValue({
|
||||
success: true,
|
||||
data: {
|
||||
hasUpdate: true,
|
||||
channel: 'dev',
|
||||
currentVersion: '0.8.1',
|
||||
latestVersion: 'dev-a1b2c3d',
|
||||
},
|
||||
});
|
||||
|
||||
const Harness = () => {
|
||||
hook = useAppUpdateManager({
|
||||
runtimeBuildType: 'release',
|
||||
t,
|
||||
onManualCheckHasUpdateRef: openReleaseNotesRef,
|
||||
});
|
||||
return null;
|
||||
};
|
||||
|
||||
act(() => {
|
||||
renderer = create(<Harness />);
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
await hook?.changeUpdateChannel('dev');
|
||||
});
|
||||
|
||||
expect(backendApp.SetUpdateChannel).toHaveBeenCalledWith('dev');
|
||||
expect(backendApp.CheckForUpdates).toHaveBeenCalledTimes(1);
|
||||
// 通道切换后的自动复查即便发现更新,也不应打开更新日志弹窗(#818 触发边界修正)
|
||||
expect(openReleaseNotes).not.toHaveBeenCalled();
|
||||
expect(hook?.lastUpdateInfo?.hasUpdate).toBe(true);
|
||||
expect(hook?.lastUpdateInfo?.latestVersion).toBe('dev-a1b2c3d');
|
||||
});
|
||||
|
||||
it('keeps release metadata from the backend update response', async () => {
|
||||
backendApp.CheckForUpdates.mockResolvedValue({
|
||||
success: true,
|
||||
@@ -770,6 +810,114 @@ describe('useAppUpdateManager', () => {
|
||||
expect(hook?.lastUpdateInfo?.latestVersion).toBe('0.8.2');
|
||||
});
|
||||
|
||||
it('invokes the manual-check bridge when a manual check finds an update', async () => {
|
||||
const openReleaseNotes = vi.fn();
|
||||
const openReleaseNotesRef = { current: openReleaseNotes };
|
||||
|
||||
backendApp.CheckForUpdates.mockResolvedValue({
|
||||
success: true,
|
||||
data: {
|
||||
hasUpdate: true,
|
||||
channel: 'latest',
|
||||
currentVersion: '0.8.1',
|
||||
latestVersion: '0.8.2',
|
||||
releaseNotesUrl: 'https://github.com/Syngnat/GoNavi/releases/tag/v0.8.2',
|
||||
},
|
||||
});
|
||||
|
||||
const Harness = () => {
|
||||
hook = useAppUpdateManager({
|
||||
runtimeBuildType: 'release',
|
||||
t,
|
||||
onManualCheckHasUpdateRef: openReleaseNotesRef,
|
||||
});
|
||||
return null;
|
||||
};
|
||||
|
||||
act(() => {
|
||||
renderer = create(<Harness />);
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
await hook?.checkForUpdates(false, true);
|
||||
});
|
||||
|
||||
expect(openReleaseNotes).toHaveBeenCalledTimes(1);
|
||||
expect(hook?.lastUpdateInfo?.hasUpdate).toBe(true);
|
||||
expect(hook?.lastUpdateInfo?.latestVersion).toBe('0.8.2');
|
||||
});
|
||||
|
||||
it('does not invoke the manual-check bridge when a manual check finds no update', async () => {
|
||||
const openReleaseNotes = vi.fn();
|
||||
const openReleaseNotesRef = { current: openReleaseNotes };
|
||||
|
||||
backendApp.CheckForUpdates.mockResolvedValue({
|
||||
success: true,
|
||||
data: {
|
||||
hasUpdate: false,
|
||||
channel: 'latest',
|
||||
currentVersion: '0.8.1',
|
||||
latestVersion: '0.8.1',
|
||||
},
|
||||
});
|
||||
|
||||
const Harness = () => {
|
||||
hook = useAppUpdateManager({
|
||||
runtimeBuildType: 'release',
|
||||
t,
|
||||
onManualCheckHasUpdateRef: openReleaseNotesRef,
|
||||
});
|
||||
return null;
|
||||
};
|
||||
|
||||
act(() => {
|
||||
renderer = create(<Harness />);
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
await hook?.checkForUpdates(false, true);
|
||||
});
|
||||
|
||||
expect(openReleaseNotes).not.toHaveBeenCalled();
|
||||
expect(hook?.lastUpdateInfo?.hasUpdate).toBe(false);
|
||||
expect(messageApi.success).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does not invoke the manual-check bridge on silent update discovery', async () => {
|
||||
const openReleaseNotes = vi.fn();
|
||||
const openReleaseNotesRef = { current: openReleaseNotes };
|
||||
|
||||
backendApp.CheckForUpdatesSilently.mockResolvedValue({
|
||||
success: true,
|
||||
data: {
|
||||
hasUpdate: true,
|
||||
channel: 'latest',
|
||||
currentVersion: '0.8.1',
|
||||
latestVersion: '0.8.2',
|
||||
},
|
||||
});
|
||||
|
||||
const Harness = () => {
|
||||
hook = useAppUpdateManager({
|
||||
runtimeBuildType: 'release',
|
||||
t,
|
||||
onManualCheckHasUpdateRef: openReleaseNotesRef,
|
||||
});
|
||||
return null;
|
||||
};
|
||||
|
||||
act(() => {
|
||||
renderer = create(<Harness />);
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
await hook?.checkForUpdates(true);
|
||||
});
|
||||
|
||||
expect(openReleaseNotes).not.toHaveBeenCalled();
|
||||
expect(hook?.lastUpdateInfo?.hasUpdate).toBe(true);
|
||||
});
|
||||
|
||||
it('opens the downloaded update directory when a package is already downloaded', async () => {
|
||||
backendApp.CheckForUpdates.mockResolvedValue({
|
||||
success: true,
|
||||
|
||||
@@ -63,6 +63,8 @@ type UseAppUpdateManagerOptions = {
|
||||
runtimeBuildType: string;
|
||||
t: Translator;
|
||||
updateCenterBridgeRef?: MutableRefObject<UpdateCenterBridge | null>;
|
||||
/** 手动「检查更新」发现新版本时,触发打开更新日志弹窗的桥接回调 */
|
||||
onManualCheckHasUpdateRef?: MutableRefObject<(() => void) | null>;
|
||||
};
|
||||
|
||||
type AboutInfo = {
|
||||
@@ -183,6 +185,7 @@ export const useAppUpdateManager = ({
|
||||
runtimeBuildType,
|
||||
t,
|
||||
updateCenterBridgeRef,
|
||||
onManualCheckHasUpdateRef,
|
||||
}: UseAppUpdateManagerOptions) => {
|
||||
const autoCheckForUpdates = useStore((state) => state.autoCheckForUpdates);
|
||||
const autoCheckForUpdatesIntervalMinutes = useStore(
|
||||
@@ -470,7 +473,7 @@ export const useAppUpdateManager = ({
|
||||
void message.success(res?.message || t('app.about.message.install_directory_opened_manual_replace'));
|
||||
}, [t]);
|
||||
|
||||
const checkForUpdates = useCallback(async (silent: boolean) => {
|
||||
const checkForUpdates = useCallback(async (silent: boolean, openReleaseNotes = false) => {
|
||||
if (updateCheckInFlightRef.current) return;
|
||||
updateCheckInFlightRef.current = true;
|
||||
setIsCheckingForUpdates(true);
|
||||
@@ -565,6 +568,11 @@ export const useAppUpdateManager = ({
|
||||
if (!silent) {
|
||||
void message.info(t('app.about.message.new_version_found', { version: info.latestVersion }));
|
||||
setAboutUpdateStatus(statusText);
|
||||
// 仅当显式请求打开更新日志时(如用户点击「检查更新」按钮),才触发弹窗;
|
||||
// 通道切换后的自动复查等场景不传 openReleaseNotes,避免越界打开弹窗(#818)
|
||||
if (openReleaseNotes) {
|
||||
onManualCheckHasUpdateRef?.current?.();
|
||||
}
|
||||
}
|
||||
if (silent && aboutOpen) {
|
||||
setAboutUpdateStatus(statusText);
|
||||
@@ -598,7 +606,7 @@ export const useAppUpdateManager = ({
|
||||
} else {
|
||||
setLastUpdateInfo(info);
|
||||
}
|
||||
}, [formatAboutUpdateStatus, isUpdateCenterOpen, openUpdateCenter, t]);
|
||||
}, [formatAboutUpdateStatus, isUpdateCenterOpen, onManualCheckHasUpdateRef, openUpdateCenter, t]);
|
||||
|
||||
const loadAboutInfo = useCallback(async () => {
|
||||
setAboutLoading(true);
|
||||
|
||||
Reference in New Issue
Block a user