fix(settings): show latest update feedback

This commit is contained in:
Tommaso Casaburi
2026-05-13 23:55:38 +07:00
parent 29f5e67c72
commit c8640a8010
38 changed files with 144 additions and 36 deletions
@@ -92,6 +92,7 @@ describe('InterfaceSettings', () => {
useFeedViewSettingsStore.getState().setEnableInfiniteScroll(false);
useAppUpdateStore.setState({
availableUpdate: null,
appUpdateCheckStatus: 'idle',
isApplyingUpdate: false,
isCheckingForUpdate: false,
applyAppUpdate: testState.applyAppUpdateMock,
@@ -106,6 +107,8 @@ describe('InterfaceSettings', () => {
afterEach(() => {
act(() => root.unmount());
useAppUpdateStore.getState().clearAppUpdateCheckStatus();
vi.useRealTimers();
container.remove();
setItemSpy.mockRestore();
vi.unstubAllGlobals();
@@ -230,6 +233,7 @@ describe('InterfaceSettings', () => {
});
it('checks for app updates when the check button is pressed', async () => {
testState.refreshAvailableUpdateMock.mockResolvedValueOnce(null);
render(createElement(InterfaceSettings));
await settleLazyImports();
@@ -245,6 +249,33 @@ describe('InterfaceSettings', () => {
expect(testState.applyAppUpdateMock).not.toHaveBeenCalled();
});
it('shows then clears the latest-version status when no app update is available', async () => {
vi.useFakeTimers();
testState.refreshAvailableUpdateMock.mockResolvedValueOnce(null);
render(createElement(InterfaceSettings));
await settleLazyImports();
const button = findButtonByText('Check');
expect(button).toBeTruthy();
await act(async () => {
button?.dispatchEvent(new MouseEvent('click', { bubbles: true }));
await Promise.resolve();
});
expect(container.textContent).toContain('app_is_up_to_date');
act(() => {
vi.advanceTimersByTime(3999);
});
expect(container.textContent).toContain('app_is_up_to_date');
act(() => {
vi.advanceTimersByTime(1);
});
expect(container.textContent).not.toContain('app_is_up_to_date');
});
it('applies the available app update when the button is pressed', async () => {
useAppUpdateStore.setState({
availableUpdate: {
@@ -8,8 +8,10 @@ const UpdateButton = () => {
const availableUpdate = useAppUpdateStore((state) => state.availableUpdate);
const isApplyingUpdate = useAppUpdateStore((state) => state.isApplyingUpdate);
const isCheckingForUpdate = useAppUpdateStore((state) => state.isCheckingForUpdate);
const appUpdateCheckStatus = useAppUpdateStore((state) => state.appUpdateCheckStatus);
const applyAppUpdate = useAppUpdateStore((state) => state.applyAppUpdate);
const refreshAvailableUpdate = useAppUpdateStore((state) => state.refreshAvailableUpdate);
const showAppUpdateUpToDateStatus = useAppUpdateStore((state) => state.showAppUpdateUpToDateStatus);
const handleUpdateAction = async () => {
try {
@@ -18,13 +20,17 @@ const UpdateButton = () => {
return;
}
await refreshAvailableUpdate();
const update = await refreshAvailableUpdate();
if (!update) {
showAppUpdateUpToDateStatus();
}
} catch (error) {
alert(String(error));
}
};
const buttonLabel = availableUpdate ? t('download') : t('check');
const isBusy = isApplyingUpdate || isCheckingForUpdate;
const shouldShowUpToDateStatus = !isCheckingForUpdate && !availableUpdate && appUpdateCheckStatus === 'upToDate';
return (
<>
@@ -36,6 +42,11 @@ const UpdateButton = () => {
{t('checking_for_updates')}
</span>
)}
{shouldShowUpToDateStatus && (
<span className={styles.updateStatus} aria-live='polite'>
{t('app_is_up_to_date')}
</span>
)}
{!isCheckingForUpdate && availableUpdate && (
<span className={styles.updateStatus} aria-live='polite'>
{t('new_version_found')}:&nbsp;