Skip to content

Commit 2f4ee9a

Browse files
authored
feat: check for updates (#1397)
* feat: check for updates * feat: check for updates * feat: check for updates * feat: check for updates * feat: check for updates * feat: check for updates * feat: check for updates * feat: check for updates * feat: check for updates * feat: check for updates * feat: check for updates * feat: check for updates * feat: check for updates * feat: check for updates * hide check for updates if one is available * Merge branch 'main' into feature/check-for-updates * increase coverage * adjust alignment * Merge branch 'main' into feature/check-for-updates
1 parent ae1eb4f commit 2f4ee9a

File tree

6 files changed

+214
-18
lines changed

6 files changed

+214
-18
lines changed

src/components/settings/SettingsFooter.test.tsx

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ describe('routes/components/settings/SettingsFooter.tsx', () => {
4848
);
4949
});
5050

51-
expect(screen.getByTitle('app-version')).toMatchSnapshot();
51+
expect(screen.getByLabelText('app-version')).toMatchSnapshot();
5252
});
5353

5454
it('should show development app version', async () => {
@@ -72,7 +72,49 @@ describe('routes/components/settings/SettingsFooter.tsx', () => {
7272
);
7373
});
7474

75-
expect(screen.getByTitle('app-version')).toMatchSnapshot();
75+
expect(screen.getByLabelText('app-version')).toMatchSnapshot();
76+
});
77+
});
78+
79+
describe('update available visual indicator', () => {
80+
it('using latest version', async () => {
81+
await act(async () => {
82+
render(
83+
<AppContext.Provider
84+
value={{
85+
auth: mockAuth,
86+
settings: mockSettings,
87+
}}
88+
>
89+
<MemoryRouter>
90+
<SettingsFooter isUpdateAvailable={false} />
91+
</MemoryRouter>
92+
</AppContext.Provider>,
93+
);
94+
});
95+
96+
expect(
97+
screen.getByTitle('You are using the latest version'),
98+
).toMatchSnapshot();
99+
});
100+
101+
it('new version available', async () => {
102+
await act(async () => {
103+
render(
104+
<AppContext.Provider
105+
value={{
106+
auth: mockAuth,
107+
settings: mockSettings,
108+
}}
109+
>
110+
<MemoryRouter>
111+
<SettingsFooter isUpdateAvailable={true} />
112+
</MemoryRouter>
113+
</AppContext.Provider>,
114+
);
115+
});
116+
117+
expect(screen.getByTitle('New version available')).toMatchSnapshot();
76118
});
77119
});
78120

src/components/settings/SettingsFooter.tsx

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,23 @@
1-
import { PersonIcon, XCircleIcon } from '@primer/octicons-react';
1+
import {
2+
AlertFillIcon,
3+
CheckCircleFillIcon,
4+
PersonIcon,
5+
XCircleIcon,
6+
} from '@primer/octicons-react';
27
import { type FC, useEffect, useState } from 'react';
38
import { useNavigate } from 'react-router-dom';
49
import { BUTTON_CLASS_NAME } from '../../styles/gitify';
5-
import { Size } from '../../types';
10+
import { IconColor, Size } from '../../types';
611
import { getAppVersion, quitApp } from '../../utils/comms';
712
import { openGitifyReleaseNotes } from '../../utils/links';
813

9-
export const SettingsFooter: FC = () => {
14+
interface ISettingsFooter {
15+
isUpdateAvailable?: boolean;
16+
}
17+
18+
export const SettingsFooter: FC<ISettingsFooter> = ({
19+
isUpdateAvailable = false,
20+
}: ISettingsFooter) => {
1021
const [appVersion, setAppVersion] = useState<string | null>(null);
1122
const navigate = useNavigate();
1223

@@ -29,7 +40,26 @@ export const SettingsFooter: FC = () => {
2940
title="View release notes"
3041
onClick={() => openGitifyReleaseNotes(appVersion)}
3142
>
32-
<span title="app-version">Gitify {appVersion}</span>
43+
<div className="flex items-center gap-1">
44+
<span aria-label="app-version">Gitify {appVersion}</span>
45+
<span className="pb-1">
46+
{isUpdateAvailable ? (
47+
<span title="New version available">
48+
<AlertFillIcon
49+
size={Size.XSMALL}
50+
className={IconColor.YELLOW}
51+
/>
52+
</span>
53+
) : (
54+
<span title="You are using the latest version">
55+
<CheckCircleFillIcon
56+
size={Size.XSMALL}
57+
className={IconColor.GREEN}
58+
/>
59+
</span>
60+
)}
61+
</span>
62+
</div>
3363
</button>
3464
<div>
3565
<button

src/components/settings/__snapshots__/SettingsFooter.test.tsx.snap

Lines changed: 44 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/electron/main.js

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,30 @@ const browserWindowOpts = {
4040
},
4141
};
4242

43+
let isUpdateAvailable = false;
44+
let isUpdateDownloaded = false;
45+
4346
const contextMenu = Menu.buildFromTemplate([
47+
{
48+
label: 'Check for updates',
49+
visible: !isUpdateAvailable,
50+
click: () => {
51+
checkForUpdates();
52+
},
53+
},
54+
{
55+
label: 'An update is available',
56+
enabled: false,
57+
visible: isUpdateAvailable,
58+
},
59+
{
60+
label: 'Restart to update',
61+
visible: isUpdateDownloaded,
62+
click: () => {
63+
autoUpdater.quitAndInstall();
64+
},
65+
},
66+
{ type: 'separator' },
4467
{
4568
label: 'Developer',
4669
submenu: [
@@ -88,8 +111,6 @@ app.whenReady().then(async () => {
88111
await onFirstRunMaybe();
89112

90113
mb.on('ready', () => {
91-
autoUpdater.checkForUpdatesAndNotify();
92-
93114
mb.app.setAppUserModelId('com.electron.gitify');
94115

95116
// Tray configuration
@@ -121,6 +142,27 @@ app.whenReady().then(async () => {
121142
mb.positioner.move('trayCenter', trayBounds);
122143
mb.window.resizable = false;
123144
});
145+
146+
// Auto Updater
147+
checkForUpdates();
148+
setInterval(checkForUpdates, 24 * 60 * 60 * 1000); // 24 hours
149+
150+
autoUpdater.on('update-available', () => {
151+
log.info('Auto Updater: New update available');
152+
isUpdateAvailable = true;
153+
mb.window.webContents.send('gitify:auto-updater', isUpdateAvailable);
154+
});
155+
156+
autoUpdater.on('update-not-available', () => {
157+
log.info('Auto Updater: Already on the latest version');
158+
isUpdateAvailable = false;
159+
mb.window.webContents.send('gitify:auto-updater', isUpdateAvailable);
160+
});
161+
162+
autoUpdater.on('update-downloaded', () => {
163+
log.info('Auto Updater: Update downloaded');
164+
isUpdateDownloaded = true;
165+
});
124166
});
125167

126168
nativeTheme.on('updated', () => {
@@ -156,7 +198,7 @@ app.whenReady().then(async () => {
156198

157199
ipc.on('gitify:update-title', (_, title) => {
158200
if (!mb.tray.isDestroyed()) {
159-
mb.tray.setTitle(title);
201+
mb.tray.setTitle(`${isUpdateAvailable ? '⤓' : ''}${title}`);
160202
}
161203
});
162204

@@ -183,6 +225,11 @@ app.whenReady().then(async () => {
183225
});
184226
});
185227

228+
function checkForUpdates() {
229+
log.info('Auto Updater: Checking for updates...');
230+
autoUpdater.checkForUpdatesAndNotify();
231+
}
232+
186233
function takeScreenshot() {
187234
const date = new Date();
188235
const dateStr = date.toISOString().replace(/:/g, '-');

src/routes/Settings.tsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { GearIcon } from '@primer/octicons-react';
2-
import { type FC, useContext } from 'react';
2+
import { ipcRenderer } from 'electron';
3+
import { type FC, useContext, useEffect, useState } from 'react';
34
import { Header } from '../components/Header';
45
import { AppearanceSettings } from '../components/settings/AppearanceSettings';
56
import { NotificationSettings } from '../components/settings/NotificationSettings';
@@ -9,6 +10,14 @@ import { AppContext } from '../context/App';
910

1011
export const SettingsRoute: FC = () => {
1112
const { resetSettings } = useContext(AppContext);
13+
const [isUpdateAvailable, setIsUpdateAvailable] = useState(false);
14+
15+
useEffect(() => {
16+
ipcRenderer.on('gitify:auto-updater', (_, isUpdateAvailable: boolean) => {
17+
setIsUpdateAvailable(isUpdateAvailable);
18+
});
19+
}, []);
20+
1221
return (
1322
<div className="flex h-screen flex-col" data-testid="settings">
1423
<Header fetchOnBack icon={GearIcon}>
@@ -31,7 +40,7 @@ export const SettingsRoute: FC = () => {
3140
</button>
3241
</div>
3342

34-
<SettingsFooter />
43+
<SettingsFooter isUpdateAvailable={isUpdateAvailable} />
3544
</div>
3645
);
3746
};

src/routes/__snapshots__/Settings.test.tsx.snap

Lines changed: 31 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)