diff --git a/src/components/settings/__snapshots__/SettingsFooter.test.tsx.snap b/src/components/settings/__snapshots__/SettingsFooter.test.tsx.snap
index c06b29078..eaef3dec9 100644
--- a/src/components/settings/__snapshots__/SettingsFooter.test.tsx.snap
+++ b/src/components/settings/__snapshots__/SettingsFooter.test.tsx.snap
@@ -17,45 +17,3 @@ exports[`routes/components/settings/SettingsFooter.tsx app version should show p
v0.0.1
`;
-
-exports[`routes/components/settings/SettingsFooter.tsx update available visual indicator new version available 1`] = `
-
-
-
-`;
-
-exports[`routes/components/settings/SettingsFooter.tsx update available visual indicator using latest version 1`] = `
-
-
-
-`;
diff --git a/src/electron/main.js b/src/electron/main.js
index b8d6f853f..dd36b70c9 100644
--- a/src/electron/main.js
+++ b/src/electron/main.js
@@ -5,27 +5,35 @@ const {
globalShortcut,
Menu,
dialog,
+ MenuItem,
} = require('electron/main');
const { menubar } = require('menubar');
-const { autoUpdater } = require('electron-updater');
const { onFirstRunMaybe } = require('./first-run');
const path = require('node:path');
const log = require('electron-log');
const fs = require('node:fs');
const os = require('node:os');
+const { autoUpdater } = require('electron-updater');
+const { updateElectronApp } = require('update-electron-app');
log.initialize();
-autoUpdater.logger = log;
// TODO: Remove @electron/remote use - see #650
require('@electron/remote/main').initialize();
+// Tray Icons
const idleIcon = path.resolve(
`${__dirname}/../../assets/images/tray-idleTemplate.png`,
);
+const idleUpdateAvailableIcon = path.resolve(
+ `${__dirname}/../../assets/images/tray-idle-update.png`,
+);
const activeIcon = path.resolve(
`${__dirname}/../../assets/images/tray-active.png`,
);
+const activeUpdateAvailableIcon = path.resolve(
+ `${__dirname}/../../assets/images/tray-active-update.png`,
+);
const browserWindowOpts = {
width: 500,
@@ -40,29 +48,32 @@ const browserWindowOpts = {
},
};
-let isUpdateAvailable = false;
-let isUpdateDownloaded = false;
-
-const contextMenu = Menu.buildFromTemplate([
- {
- label: 'Check for updates',
- visible: !isUpdateAvailable,
- click: () => {
- checkForUpdates();
- },
- },
- {
- label: 'An update is available',
- enabled: false,
- visible: isUpdateAvailable,
+const checkForUpdatesMenuItem = new MenuItem({
+ label: 'Check for updates',
+ enabled: true,
+ click: () => {
+ autoUpdater.checkForUpdatesAndNotify();
},
- {
- label: 'Restart to update',
- visible: isUpdateDownloaded,
- click: () => {
- autoUpdater.quitAndInstall();
- },
+});
+
+const updateAvailableMenuItem = new MenuItem({
+ label: 'An update is available',
+ enabled: false,
+ visible: false,
+});
+
+const updateReadyForInstallMenuItem = new MenuItem({
+ label: 'Restart to update',
+ visible: false,
+ click: () => {
+ autoUpdater.quitAndInstall();
},
+});
+
+const contextMenu = Menu.buildFromTemplate([
+ checkForUpdatesMenuItem,
+ updateAvailableMenuItem,
+ updateReadyForInstallMenuItem,
{ type: 'separator' },
{
label: 'Developer',
@@ -142,27 +153,6 @@ app.whenReady().then(async () => {
mb.positioner.move('trayCenter', trayBounds);
mb.window.resizable = false;
});
-
- // Auto Updater
- checkForUpdates();
- setInterval(checkForUpdates, 24 * 60 * 60 * 1000); // 24 hours
-
- autoUpdater.on('update-available', () => {
- log.info('Auto Updater: New update available');
- isUpdateAvailable = true;
- mb.window.webContents.send('gitify:auto-updater', isUpdateAvailable);
- });
-
- autoUpdater.on('update-not-available', () => {
- log.info('Auto Updater: Already on the latest version');
- isUpdateAvailable = false;
- mb.window.webContents.send('gitify:auto-updater', isUpdateAvailable);
- });
-
- autoUpdater.on('update-downloaded', () => {
- log.info('Auto Updater: Update downloaded');
- isUpdateDownloaded = true;
- });
});
nativeTheme.on('updated', () => {
@@ -186,19 +176,25 @@ app.whenReady().then(async () => {
ipc.on('gitify:icon-active', () => {
if (!mb.tray.isDestroyed()) {
- mb.tray.setImage(activeIcon);
+ mb.tray.setImage(
+ updateAvailableMenuItem.visible
+ ? activeUpdateAvailableIcon
+ : activeIcon,
+ );
}
});
ipc.on('gitify:icon-idle', () => {
if (!mb.tray.isDestroyed()) {
- mb.tray.setImage(idleIcon);
+ mb.tray.setImage(
+ updateAvailableMenuItem.visible ? idleUpdateAvailableIcon : idleIcon,
+ );
}
});
ipc.on('gitify:update-title', (_, title) => {
if (!mb.tray.isDestroyed()) {
- mb.tray.setTitle(`${isUpdateAvailable ? '⤓' : ''}${title}`);
+ mb.tray.setTitle(title);
}
});
@@ -223,12 +219,40 @@ app.whenReady().then(async () => {
ipc.on('gitify:update-auto-launch', (_, settings) => {
app.setLoginItemSettings(settings);
});
-});
-function checkForUpdates() {
- log.info('Auto Updater: Checking for updates...');
- autoUpdater.checkForUpdatesAndNotify();
-}
+ // Auto Updater
+ updateElectronApp({
+ updateInterval: '24 hours',
+ logger: log,
+ });
+
+ autoUpdater.on('checking-for-update', () => {
+ log.info('Auto Updater: Checking for update');
+ checkForUpdatesMenuItem.enabled = false;
+ });
+
+ autoUpdater.on('error', (error) => {
+ log.error('Auto Updater: error checking for update', error);
+ checkForUpdatesMenuItem.enabled = true;
+ });
+
+ autoUpdater.on('update-available', () => {
+ log.info('Auto Updater: New update available');
+ updateAvailableMenuItem.visible = true;
+ mb.tray.setToolTip('Gitify\nA new update is available');
+ });
+
+ autoUpdater.on('update-downloaded', () => {
+ log.info('Auto Updater: Update downloaded');
+ updateReadyForInstallMenuItem.visible = true;
+ mb.tray.setToolTip('Gitify\nA new update is ready to install');
+ });
+
+ autoUpdater.on('update-not-available', () => {
+ log.info('Auto Updater: update not available');
+ checkForUpdatesMenuItem.enabled = true;
+ });
+});
function takeScreenshot() {
const date = new Date();
diff --git a/src/routes/Settings.tsx b/src/routes/Settings.tsx
index 3d07f80ef..2ee168061 100644
--- a/src/routes/Settings.tsx
+++ b/src/routes/Settings.tsx
@@ -1,6 +1,5 @@
import { GearIcon } from '@primer/octicons-react';
-import { ipcRenderer } from 'electron';
-import { type FC, useContext, useEffect, useState } from 'react';
+import { type FC, useContext } from 'react';
import { Header } from '../components/Header';
import { AppearanceSettings } from '../components/settings/AppearanceSettings';
import { NotificationSettings } from '../components/settings/NotificationSettings';
@@ -10,13 +9,6 @@ import { AppContext } from '../context/App';
export const SettingsRoute: FC = () => {
const { resetSettings } = useContext(AppContext);
- const [isUpdateAvailable, setIsUpdateAvailable] = useState(false);
-
- useEffect(() => {
- ipcRenderer.on('gitify:auto-updater', (_, isUpdateAvailable: boolean) => {
- setIsUpdateAvailable(isUpdateAvailable);
- });
- }, []);
return (
@@ -40,7 +32,7 @@ export const SettingsRoute: FC = () => {
-
+
);
};
diff --git a/src/routes/__snapshots__/Settings.test.tsx.snap b/src/routes/__snapshots__/Settings.test.tsx.snap
index 54e98764e..0f0b2a506 100644
--- a/src/routes/__snapshots__/Settings.test.tsx.snap
+++ b/src/routes/__snapshots__/Settings.test.tsx.snap
@@ -797,28 +797,6 @@ exports[`routes/Settings.tsx should render itself & its children 1`] = `
Gitify
v0.0.1
-