diff --git a/assets/images/tray-error.png b/assets/images/tray-error.png new file mode 100644 index 000000000..5cd7e549b Binary files /dev/null and b/assets/images/tray-error.png differ diff --git a/assets/images/tray-error@2x.png b/assets/images/tray-error@2x.png new file mode 100644 index 000000000..add3eb993 Binary files /dev/null and b/assets/images/tray-error@2x.png differ diff --git a/src/main/icons.test.ts b/src/main/icons.test.ts index 58222cb2a..183fa6227 100644 --- a/src/main/icons.test.ts +++ b/src/main/icons.test.ts @@ -4,13 +4,13 @@ describe('main/icons.ts', () => { it('should return icon images', () => { expect(TrayIcons.active).toContain('assets/images/tray-active.png'); - expect(TrayIcons.activeUpdateIcon).toContain( + expect(TrayIcons.activeWithUpdate).toContain( 'assets/images/tray-active-update.png', ); expect(TrayIcons.idle).toContain('assets/images/tray-idleTemplate.png'); - expect(TrayIcons.idleUpdateIcon).toContain( + expect(TrayIcons.idleWithUpdate).toContain( 'assets/images/tray-idle-update.png', ); @@ -18,8 +18,10 @@ describe('main/icons.ts', () => { 'assets/images/tray-idle-white.png', ); - expect(TrayIcons.idleAlternateUpdateIcon).toContain( + expect(TrayIcons.idleAlternateWithUpdate).toContain( 'assets/images/tray-idle-white-update.png', ); + + expect(TrayIcons.error).toContain('assets/images/tray-error.png'); }); }); diff --git a/src/main/icons.ts b/src/main/icons.ts index fd86d8b7e..28a5139a7 100644 --- a/src/main/icons.ts +++ b/src/main/icons.ts @@ -2,11 +2,12 @@ import path from 'node:path'; export const TrayIcons = { active: getIconPath('tray-active.png'), - activeUpdateIcon: getIconPath('tray-active-update.png'), + activeWithUpdate: getIconPath('tray-active-update.png'), idle: getIconPath('tray-idleTemplate.png'), - idleUpdateIcon: getIconPath('tray-idle-update.png'), + idleWithUpdate: getIconPath('tray-idle-update.png'), idleAlternate: getIconPath('tray-idle-white.png'), - idleAlternateUpdateIcon: getIconPath('tray-idle-white-update.png'), + idleAlternateWithUpdate: getIconPath('tray-idle-white-update.png'), + error: getIconPath('tray-error.png'), }; function getIconPath(iconName: string) { diff --git a/src/main/main.ts b/src/main/main.ts index ce7ca1f53..822d0a766 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -118,11 +118,17 @@ app.whenReady().then(async () => { }, ); + ipc.on(namespacedEvent('icon-error'), () => { + if (!mb.tray.isDestroyed()) { + mb.tray.setImage(TrayIcons.error); + } + }); + ipc.on(namespacedEvent('icon-active'), () => { if (!mb.tray.isDestroyed()) { mb.tray.setImage( menuBuilder.isUpdateAvailable() - ? TrayIcons.activeUpdateIcon + ? TrayIcons.activeWithUpdate : TrayIcons.active, ); } @@ -133,13 +139,13 @@ app.whenReady().then(async () => { if (shouldUseAlternateIdleIcon) { mb.tray.setImage( menuBuilder.isUpdateAvailable() - ? TrayIcons.idleAlternateUpdateIcon + ? TrayIcons.idleAlternateWithUpdate : TrayIcons.idleAlternate, ); } else { mb.tray.setImage( menuBuilder.isUpdateAvailable() - ? TrayIcons.idleUpdateIcon + ? TrayIcons.idleWithUpdate : TrayIcons.idle, ); } diff --git a/src/renderer/components/Oops.tsx b/src/renderer/components/Oops.tsx index ad543cecd..65f4cac66 100644 --- a/src/renderer/components/Oops.tsx +++ b/src/renderer/components/Oops.tsx @@ -18,7 +18,7 @@ export const Oops: FC = ({ error }: IOops) => { return ( -
+
diff --git a/src/renderer/components/__snapshots__/AllRead.test.tsx.snap b/src/renderer/components/__snapshots__/AllRead.test.tsx.snap index b28381556..fd033d405 100644 --- a/src/renderer/components/__snapshots__/AllRead.test.tsx.snap +++ b/src/renderer/components/__snapshots__/AllRead.test.tsx.snap @@ -6,7 +6,7 @@ exports[`renderer/components/AllRead.tsx should render itself & its children - n "baseElement":
, "container":
, "container":
, "container":
= (props: ICentered) => { return ( -
+
{props.children}
); diff --git a/src/renderer/components/primitives/__snapshots__/Centered.test.tsx.snap b/src/renderer/components/primitives/__snapshots__/Centered.test.tsx.snap index 584b4f019..3657c201e 100644 --- a/src/renderer/components/primitives/__snapshots__/Centered.test.tsx.snap +++ b/src/renderer/components/primitives/__snapshots__/Centered.test.tsx.snap @@ -6,7 +6,7 @@ exports[`renderer/components/primitives/Centered.tsx should render itself & its "baseElement":
Test
@@ -14,7 +14,7 @@ exports[`renderer/components/primitives/Centered.tsx should render itself & its , "container":
Test
diff --git a/src/renderer/hooks/useNotifications.ts b/src/renderer/hooks/useNotifications.ts index e4ecb443a..b5b8cdf6b 100644 --- a/src/renderer/hooks/useNotifications.ts +++ b/src/renderer/hooks/useNotifications.ts @@ -14,6 +14,7 @@ import { markNotificationThreadAsDone, markNotificationThreadAsRead, } from '../utils/api/client'; +import { updateTrayIcon } from '../utils/comms'; import { isMarkAsDoneFeatureSupported } from '../utils/features'; import { triggerNativeNotifications } from '../utils/notifications/native'; import { @@ -73,26 +74,27 @@ export const useNotifications = (): NotificationsState => { const fetchedNotifications = await getAllNotifications(state); // Set Global Error if all accounts have the same error - if (fetchNotifications.length > 0) { - const allAccountsHaveErrors = fetchedNotifications.every((account) => { + const allAccountsHaveErrors = + fetchedNotifications.length > 0 && + fetchedNotifications.every((account) => { return account.error !== null; }); - let accountErrorsAreAllSame = true; - const accountError = fetchedNotifications[0]?.error; + let accountErrorsAreAllSame = true; + const accountError = fetchedNotifications[0]?.error; - for (const fetchedNotification of fetchedNotifications) { - if (accountError !== fetchedNotification.error) { - accountErrorsAreAllSame = false; - break; - } + for (const fetchedNotification of fetchedNotifications) { + if (accountError !== fetchedNotification.error) { + accountErrorsAreAllSame = false; + break; } + } - if (allAccountsHaveErrors) { - setStatus('error'); - setGlobalError(accountErrorsAreAllSame ? accountError : null); - return; - } + if (allAccountsHaveErrors) { + setStatus('error'); + setGlobalError(accountErrorsAreAllSame ? accountError : null); + updateTrayIcon(-1); + return; } setNotifications(fetchedNotifications); diff --git a/src/renderer/routes/__snapshots__/Login.test.tsx.snap b/src/renderer/routes/__snapshots__/Login.test.tsx.snap index 8fa7fdfcf..4c43b0a1a 100644 --- a/src/renderer/routes/__snapshots__/Login.test.tsx.snap +++ b/src/renderer/routes/__snapshots__/Login.test.tsx.snap @@ -6,7 +6,7 @@ exports[`renderer/routes/Login.tsx should render itself & its children 1`] = ` "baseElement":
, "container":
{ expect(ipcRenderer.send).toHaveBeenCalledTimes(1); expect(ipcRenderer.send).toHaveBeenCalledWith(namespacedEvent('icon-idle')); }); + + it('should send mark the icons as error', () => { + const notificationsLength = -1; + updateTrayIcon(notificationsLength); + expect(ipcRenderer.send).toHaveBeenCalledTimes(1); + expect(ipcRenderer.send).toHaveBeenCalledWith( + namespacedEvent('icon-error'), + ); + }); }); diff --git a/src/renderer/utils/comms.ts b/src/renderer/utils/comms.ts index c33d878ae..8bd8d16fb 100644 --- a/src/renderer/utils/comms.ts +++ b/src/renderer/utils/comms.ts @@ -55,11 +55,17 @@ export function setKeyboardShortcut(keyboardShortcut: boolean): void { } export function updateTrayIcon(notificationsLength = 0): void { + if (notificationsLength < 0) { + ipcRenderer.send(namespacedEvent('icon-error')); + return; + } + if (notificationsLength > 0) { ipcRenderer.send(namespacedEvent('icon-active')); - } else { - ipcRenderer.send(namespacedEvent('icon-idle')); + return; } + + ipcRenderer.send(namespacedEvent('icon-idle')); } export function updateTrayTitle(title = ''): void {