From 61421868f2a0a0c5b900930d76099eb9ccc0270c Mon Sep 17 00:00:00 2001 From: Adam Setch Date: Mon, 20 Jan 2025 09:09:39 -0500 Subject: [PATCH] feat: error tray icon Signed-off-by: Adam Setch --- assets/images/tray-error.png | Bin 0 -> 1923 bytes assets/images/tray-error@2x.png | Bin 0 -> 3290 bytes src/main/icons.test.ts | 8 +++-- src/main/icons.ts | 7 ++-- src/main/main.ts | 12 +++++-- src/renderer/components/Oops.tsx | 2 +- .../__snapshots__/AllRead.test.tsx.snap | 8 ++--- .../__snapshots__/Oops.test.tsx.snap | 8 ++--- .../AccountNotifications.test.tsx.snap | 12 +++---- .../components/primitives/Centered.tsx | 2 +- .../__snapshots__/Centered.test.tsx.snap | 4 +-- src/renderer/hooks/useNotifications.ts | 30 ++++++++++-------- .../routes/__snapshots__/Login.test.tsx.snap | 4 +-- src/renderer/utils/comms.test.ts | 9 ++++++ src/renderer/utils/comms.ts | 10 ++++-- 15 files changed, 71 insertions(+), 45 deletions(-) create mode 100644 assets/images/tray-error.png create mode 100644 assets/images/tray-error@2x.png diff --git a/assets/images/tray-error.png b/assets/images/tray-error.png new file mode 100644 index 0000000000000000000000000000000000000000..5cd7e549be53e4b5d23e1f0aa1f1df7c1934b0fe GIT binary patch literal 1923 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjEa{HEjtmSN`?>!lvI6-E$sR$z z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBDAAG{;hE;^%b*2hb1<+n z3NbJPS&Tr)z$nE4G7ZRL@M4sPvx68lplX;H7}_%#SfFa6fHVk90Ai3H2+h2J5n8?tx|+G%>LOTY(~q za3DknLPKa?W{I5R>Jd!=gqUbvOi!xJzK?!jg)DmPdB=sQwSp{TPq~=7p zWag&k6@z?ZYytEQRvC1i&iOg{MZpD$$*FdRP%TJ8=&Hf?MB=muNd~IZMjsU4NbwIz zTwqaP>aydq(TAs0JFXoP9p@Mrm?wI=IEHxe-VHgM9TF~L|NZgV*+*vZ8!N|ha`Cz_ zbLDC_cNnXEe9S+i;mW0VCCW-E(X*Bw-Su+O7yTE9G@ZglP0u)pzg^_CAvPsg@XiY# zyFY94@9nA1uYaBv_epK$l$^J%Gt1xa{Ql>h<^Io(U-s#5oX#LKaY0sS(CVw7_c1>8 zT0HR$>*Dv)Y-LY3y{q2op4jgmDZ71x!qG!bsW)$(IpdLZ##3U>q+>jjio-YrBbNTN ziCBOC+^gLC-%Sg4M}-z|omF_QGi~ReshhvOs$0s{c%$mvH0QOMtr`0&{{LHRrS#VQ zdf$hKOGS9%w0Ie6Hm(TCJS$O^?ZeU>bg1;35StZyi-bzHw#7ZI%^S|&%4^tat9$d6DSqazWcjiH~YQA?m}mSmrrw6 zRJ(OeZDEkqf%&xo>;KQt`ucm597CDQMo&Q@ZOe^oZ{B=t)TNe`ZS9fsq3y+Tkv*oi zwhw!L`bikSYVi=LJD2(Qd;GOJy$k$*dvc^S%4U0Px!uk-b5IXcA<2rt>ppbPb>DkT*uO~yn4#&6d^_BCx_~z-wy>#&ins;HNTSvaY zLD`uq?mzdw2X1^PEX-+dRkFPkM&)y(Ct%Mg W{gTLEzTZxRsuoXIKbLh*2~7auyxhP5 literal 0 HcmV?d00001 diff --git a/assets/images/tray-error@2x.png b/assets/images/tray-error@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..add3eb993635aa3792c7bf07f6ea3efed5918ba8 GIT binary patch literal 3290 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 HI)(lh4#JS= literal 0 HcmV?d00001 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 {