Repository: gitify-app/gitify
Pull request #1397 (2024-07-21 21:34:58)

feat: check for updates
Based on #1395 from @bquorning 

Draft PR to consider:
- Adding a context menu for checking for updates manually
- Adding a scheduled timer to check for updates automatically (not just on app start)
- Display somewhere in the UI a visual indicator when there is a new version available 

setchy: **Latest version**
<img width="500" alt="Screenshot 2024-07-22 at 10 30 23 AM" src="https://github.com/user-attachments/assets/79142609-81ae-4925-915d-b3e3223ddb68">

**Update available**
<img width="500" alt="Screenshot 2024-07-22 at 10 31 19 AM" src="https://github.com/user-attachments/assets/370bb6b9-ba06-4bcd-94a7-98414898feff">

afonsojramos: Love the idea, but I think the implementation in the UI is a bit weird. The behaviour that I see in these menubar apps is usually an indicator in the icon of the menubar and I think it is less intrusive and more informative at the same time, what do you think?

Example: Ollama
<img width="43" alt="image" src="https://github.com/user-attachments/assets/548b91f2-5e8c-4417-8960-20af32351502">
<img width="225" alt="image" src="https://github.com/user-attachments/assets/fa87a65b-6192-4362-97b4-257c05c8c70a">
<img width="44" alt="image" src="https://github.com/user-attachments/assets/9bcc9942-25db-4471-a841-e83fa4d6ba76">

setchy: > Love the idea, but I think the implementation in the UI is a bit weird. The behaviour that I see in these menubar apps is usually an indicator in the icon of the menubar and I think it is less intrusive and more informative at the same time, what do you think?
> 
> Example: Ollama
> <img width="43" alt="image" src="https://github.com/user-attachments/assets/548b91f2-5e8c-4417-8960-20af32351502">
> <img width="225" alt="image" src="https://github.com/user-attachments/assets/fa87a65b-6192-4362-97b4-257c05c8c70a">
> <img width="44" alt="image" src="https://github.com/user-attachments/assets/9bcc9942-25db-4471-a841-e83fa4d6ba76">
> 

I like that approach. Would you be interested in helping pivot this way?
setchy: I also like the `restart to update` context menu entry. That seems better than doing it automatically 
setchy: Another thought, maybe our menu system could adopt something like this

- check for update 
-- show status if update available 
-- show restart to update if available 
- separator
- Developer (sub menu)
-- Reload
-- Toggle Developer Tools
-- separator 
- Quit
setchy: @afonsojramos - pushed updates to this branch that add the extra context menu elements.

Not sure the best way to update the tray icon.  We'd either need to create a new png, or, manipulate the tray title (used for notification counts) to display a visual indicator

Patch:
diff --git a/package.json b/package.json
index 7980ef93..66edf3ad 100644
--- a/package.json
+++ b/package.json
@@ -102,19 +102,19 @@
   "dependencies": {
     "@electron/remote": "2.1.2",
     "@primer/octicons-react": "19.11.0",
-    "axios": "1.7.2",
+    "axios": "1.7.3",
     "class-variance-authority": "0.7.0",
     "clsx": "2.1.1",
     "date-fns": "3.6.0",
     "electron-log": "5.1.7",
-    "electron-updater": "6.3.1",
+    "electron-updater": "6.3.2",
     "final-form": "4.20.10",
     "menubar": "9.5.0",
     "nprogress": "0.2.0",
     "react": "18.3.1",
     "react-dom": "18.3.1",
     "react-final-form": "6.5.9",
-    "react-router-dom": "6.25.1",
+    "react-router-dom": "6.26.0",
     "tailwind-merge": "2.4.0",
     "ts-loader": "9.5.1",
     "typescript": "5.5.4"
@@ -124,11 +124,11 @@
     "@electron/notarize": "2.3.2",
     "@testing-library/react": "16.0.0",
     "@types/jest": "29.5.12",
-    "@types/node": "20.14.13",
+    "@types/node": "20.14.14",
     "@types/nprogress": "0.2.3",
     "@types/react": "18.3.3",
     "@types/react-router-dom": "5.3.3",
-    "autoprefixer": "10.4.19",
+    "autoprefixer": "10.4.20",
     "css-loader": "7.1.2",
     "electron": "31.3.1",
     "electron-builder": "24.13.3",
@@ -143,7 +143,7 @@
     "rimraf": "6.0.1",
     "style-loader": "4.0.0",
     "tailwindcss": "3.4.7",
-    "ts-jest": "29.2.3",
+    "ts-jest": "29.2.4",
     "ts-node": "10.9.2",
     "webpack": "5.93.0",
     "webpack-cli": "5.1.4",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 6337c240..613abc2a 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -15,8 +15,8 @@ importers:
         specifier: 19.11.0
         version: 19.11.0(react@18.3.1)
       axios:
-        specifier: 1.7.2
-        version: 1.7.2
+        specifier: 1.7.3
+        version: 1.7.3
       class-variance-authority:
         specifier: 0.7.0
         version: 0.7.0
@@ -30,8 +30,8 @@ importers:
         specifier: 5.1.7
         version: 5.1.7
       electron-updater:
-        specifier: 6.3.1
-        version: 6.3.1
+        specifier: 6.3.2
+        version: 6.3.2
       final-form:
         specifier: 4.20.10
         version: 4.20.10
@@ -51,8 +51,8 @@ importers:
         specifier: 6.5.9
         version: 6.5.9(final-form@4.20.10)(react@18.3.1)
       react-router-dom:
-        specifier: 6.25.1
-        version: 6.25.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+        specifier: 6.26.0
+        version: 6.26.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       tailwind-merge:
         specifier: 2.4.0
         version: 2.4.0
@@ -76,8 +76,8 @@ importers:
         specifier: 29.5.12
         version: 29.5.12
       '@types/node':
-        specifier: 20.14.13
-        version: 20.14.13
+        specifier: 20.14.14
+        version: 20.14.14
       '@types/nprogress':
         specifier: 0.2.3
         version: 0.2.3
@@ -88,8 +88,8 @@ importers:
         specifier: 5.3.3
         version: 5.3.3
       autoprefixer:
-        specifier: 10.4.19
-        version: 10.4.19(postcss@8.4.40)
+        specifier: 10.4.20
+        version: 10.4.20(postcss@8.4.40)
       css-loader:
         specifier: 7.1.2
         version: 7.1.2(webpack@5.93.0(webpack-cli@5.1.4))
@@ -107,7 +107,7 @@ importers:
         version: 9.1.4
       jest:
         specifier: 29.7.0
-        version: 29.7.0(@types/node@20.14.13)(ts-node@10.9.2(@types/node@20.14.13)(typescript@5.5.4))
+        version: 29.7.0(@types/node@20.14.14)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))
       jest-environment-jsdom:
         specifier: 29.7.0
         version: 29.7.0
@@ -131,13 +131,13 @@ importers:
         version: 4.0.0(webpack@5.93.0(webpack-cli@5.1.4))
       tailwindcss:
         specifier: 3.4.7
-        version: 3.4.7(ts-node@10.9.2(@types/node@20.14.13)(typescript@5.5.4))
+        version: 3.4.7(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))
       ts-jest:
-        specifier: 29.2.3
-        version: 29.2.3(@babel/core@7.24.3)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.3))(jest@29.7.0(@types/node@20.14.13)(ts-node@10.9.2(@types/node@20.14.13)(typescript@5.5.4)))(typescript@5.5.4)
+        specifier: 29.2.4
+        version: 29.2.4(@babel/core@7.24.3)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.3))(jest@29.7.0(@types/node@20.14.14)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4)))(typescript@5.5.4)
       ts-node:
         specifier: 10.9.2
-        version: 10.9.2(@types/node@20.14.13)(typescript@5.5.4)
+        version: 10.9.2(@types/node@20.14.14)(typescript@5.5.4)
       webpack:
         specifier: 5.93.0
         version: 5.93.0(webpack-cli@5.1.4)
@@ -560,8 +560,8 @@ packages:
     peerDependencies:
       react: '>=16.3'
 
-  '@remix-run/router@1.18.0':
-    resolution: {integrity: sha512-L3jkqmqoSVBVKHfpGZmLrex0lxR5SucGA0sUfFzGctehw+S/ggL9L/0NnC5mw6P8HUWpFZ3nQw3cRApjjWx9Sw==}
+  '@remix-run/router@1.19.0':
+    resolution: {integrity: sha512-zDICCLKEwbVYTS6TjYaWtHXxkdoUvD/QXvyVZjGCsWz5vyH7aFeONlPffPdW+Y/t6KT0MgXb2Mfjun9YpWN1dA==}
     engines: {node: '>=14.0.0'}
 
   '@sinclair/typebox@0.27.8':
@@ -682,8 +682,8 @@ packages:
   '@types/ms@0.7.34':
     resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==}
 
-  '@types/node@20.14.13':
-    resolution: {integrity: sha512-+bHoGiZb8UiQ0+WEtmph2IWQCjIqg8MDZMAV+ppRRhUZnquF5mQkP/9vpSwJClEiSM/C7fZZExPzfU0vJTyp8w==}
+  '@types/node@20.14.14':
+    resolution: {integrity: sha512-d64f00982fS9YoOgJkAMolK7MN8Iq3TDdVjchbYHdEmjth/DHowx82GnoA+tVUAN+7vxfYUgAzi+JXbKNd2SDQ==}
 
   '@types/nprogress@0.2.3':
     resolution: {integrity: sha512-k7kRA033QNtC+gLc4VPlfnue58CM1iQLgn1IMAU8VPHGOj7oIHPp9UlhedEnD/Gl8evoCjwkZjlBORtZ3JByUA==}
@@ -934,15 +934,15 @@ packages:
     resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==}
     engines: {node: '>= 4.0.0'}
 
-  autoprefixer@10.4.19:
-    resolution: {integrity: sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==}
+  autoprefixer@10.4.20:
+    resolution: {integrity: sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==}
     engines: {node: ^10 || ^12 || >=14}
     hasBin: true
     peerDependencies:
       postcss: ^8.1.0
 
-  axios@1.7.2:
-    resolution: {integrity: sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==}
+  axios@1.7.3:
+    resolution: {integrity: sha512-Ar7ND9pU99eJ9GpoGQKhKf58GpUOgnzuaB7ueNQ5BMi0p+LZ5oaEnfF999fAArcTIBwXTCHAmGcHOZJaWPq9Nw==}
 
   babel-jest@29.7.0:
     resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==}
@@ -1006,6 +1006,11 @@ packages:
     engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
     hasBin: true
 
+  browserslist@4.23.3:
+    resolution: {integrity: sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==}
+    engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
+    hasBin: true
+
   bs-logger@0.2.6:
     resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==}
     engines: {node: '>= 6'}
@@ -1064,6 +1069,9 @@ packages:
   caniuse-lite@1.0.30001600:
     resolution: {integrity: sha512-+2S9/2JFhYmYaDpZvo0lKkfvuKIglrx68MwOBqMGHhQsNkLjB5xtc/TGoEPs+MxjSyN/72qer2g97nzR641mOQ==}
 
+  caniuse-lite@1.0.30001647:
+    resolution: {integrity: sha512-n83xdNiyeNcHpzWY+1aFbqCK7LuLfBricc4+alSQL2Xb6OR3XpnQAmlDG+pQcdTfiHRuLcQ96VOfrPSGiNJYSg==}
+
   chalk@2.4.2:
     resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
     engines: {node: '>=4'}
@@ -1389,8 +1397,11 @@ packages:
   electron-to-chromium@1.4.719:
     resolution: {integrity: sha512-FbWy2Q2YgdFzkFUW/W5jBjE9dj+804+98E4Pup78JBPnbdb3pv6IneY2JCPKdeKLh3AOKHQeYf+KwLr7mxGh6Q==}
 
-  electron-updater@6.3.1:
-    resolution: {integrity: sha512-Et9t9LcH42q/A73yu398YGHR7cbdTnHbIyFN/p0FNZIJi10XxGshZBVVE6FsClMuoggw7FVhn1+kMUVMCqS7MA==}
+  electron-to-chromium@1.5.4:
+    resolution: {integrity: sha512-orzA81VqLyIGUEA77YkVA1D+N+nNfl2isJVjjmOyrlxuooZ19ynb+dOlaDTqd/idKRS9lDCSBmtzM+kyCsMnkA==}
+
+  electron-updater@6.3.2:
+    resolution: {integrity: sha512-bEpuZ1IRnMtvZZaWeYi9ocX90Cnk+/impZ/08r6GQkfOMqECtKC2IjvxHcDk2VpWO8QZzK0+MUNaBiO81CGvQQ==}
 
   electron@31.3.1:
     resolution: {integrity: sha512-9fiuWlRhBfygtcT+auRd/WdBK/f8LZZcrpx0RjpXhH2DPTP/PfnkC4JB1PW55qCbGbh4wAgkYbf4ExIag8oGCA==}
@@ -2308,6 +2319,9 @@ packages:
   node-releases@2.0.14:
     resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==}
 
+  node-releases@2.0.18:
+    resolution: {integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==}
+
   normalize-path@3.0.0:
     resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
     engines: {node: '>=0.10.0'}
@@ -2409,9 +2423,6 @@ packages:
   pend@1.2.0:
     resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==}
 
-  picocolors@1.0.0:
-    resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
-
   picocolors@1.0.1:
     resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==}
 
@@ -2586,15 +2597,15 @@ packages:
   react-is@18.2.0:
     resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==}
 
-  react-router-dom@6.25.1:
-    resolution: {integrity: sha512-0tUDpbFvk35iv+N89dWNrJp+afLgd+y4VtorJZuOCXK0kkCWjEvb3vTJM++SYvMEpbVwXKf3FjeVveVEb6JpDQ==}
+  react-router-dom@6.26.0:
+    resolution: {integrity: sha512-RRGUIiDtLrkX3uYcFiCIxKFWMcWQGMojpYZfcstc63A1+sSnVgILGIm9gNUA6na3Fm1QuPGSBQH2EMbAZOnMsQ==}
     engines: {node: '>=14.0.0'}
     peerDependencies:
       react: '>=16.8'
       react-dom: '>=16.8'
 
-  react-router@6.25.1:
-    resolution: {integrity: sha512-u8ELFr5Z6g02nUtpPAggP73Jigj1mRePSwhS/2nkTrlPU5yEkH1vYzWNyvSnSzeeE2DNqWdH+P8OhIh9wuXhTw==}
+  react-router@6.26.0:
+    resolution: {integrity: sha512-wVQq0/iFYd3iZ9H2l3N3k4PL8EEHcb0XlU2Na8nEwmiXgIUElEH6gaJDtUQxJ+JFzmIXaQjfdpcGWaM6IoQGxg==}
     engines: {node: '>=14.0.0'}
     peerDependencies:
       react: '>=16.8'
@@ -2964,8 +2975,8 @@ packages:
   ts-interface-checker@0.1.13:
     resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
 
-  ts-jest@29.2.3:
-    resolution: {integrity: sha512-yCcfVdiBFngVz9/keHin9EnsrQtQtEu3nRykNy9RVp+FiPFFbPJ3Sg6Qg4+TkmH0vMP5qsTKgXSsk80HRwvdgQ==}
+  ts-jest@29.2.4:
+    resolution: {integrity: sha512-3d6tgDyhCI29HlpwIq87sNuI+3Q6GLTTCeYRHCs7vDz+/3GCMwEtV9jezLyl4ZtnBgx00I7hm8PCP8cTksMGrw==}
     engines: {node: ^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0}
     hasBin: true
     peerDependencies:
@@ -3050,6 +3061,12 @@ packages:
     peerDependencies:
       browserslist: '>= 4.21.0'
 
+  update-browserslist-db@1.1.0:
+    resolution: {integrity: sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==}
+    hasBin: true
+    peerDependencies:
+      browserslist: '>= 4.21.0'
+
   uri-js@4.4.1:
     resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
 
@@ -3569,27 +3586,27 @@ snapshots:
   '@jest/console@29.7.0':
     dependencies:
       '@jest/types': 29.6.3
-      '@types/node': 20.14.13
+      '@types/node': 20.14.14
       chalk: 4.1.2
       jest-message-util: 29.7.0
       jest-util: 29.7.0
       slash: 3.0.0
 
-  '@jest/core@29.7.0(ts-node@10.9.2(@types/node@20.14.13)(typescript@5.5.4))':
+  '@jest/core@29.7.0(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))':
     dependencies:
       '@jest/console': 29.7.0
       '@jest/reporters': 29.7.0
       '@jest/test-result': 29.7.0
       '@jest/transform': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.14.13
+      '@types/node': 20.14.14
       ansi-escapes: 4.3.2
       chalk: 4.1.2
       ci-info: 3.9.0
       exit: 0.1.2
       graceful-fs: 4.2.11
       jest-changed-files: 29.7.0
-      jest-config: 29.7.0(@types/node@20.14.13)(ts-node@10.9.2(@types/node@20.14.13)(typescript@5.5.4))
+      jest-config: 29.7.0(@types/node@20.14.14)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))
       jest-haste-map: 29.7.0
       jest-message-util: 29.7.0
       jest-regex-util: 29.6.3
@@ -3614,7 +3631,7 @@ snapshots:
     dependencies:
       '@jest/fake-timers': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.14.13
+      '@types/node': 20.14.14
       jest-mock: 29.7.0
 
   '@jest/expect-utils@29.7.0':
@@ -3632,7 +3649,7 @@ snapshots:
     dependencies:
       '@jest/types': 29.6.3
       '@sinonjs/fake-timers': 10.3.0
-      '@types/node': 20.14.13
+      '@types/node': 20.14.14
       jest-message-util: 29.7.0
       jest-mock: 29.7.0
       jest-util: 29.7.0
@@ -3654,7 +3671,7 @@ snapshots:
       '@jest/transform': 29.7.0
       '@jest/types': 29.6.3
       '@jridgewell/trace-mapping': 0.3.25
-      '@types/node': 20.14.13
+      '@types/node': 20.14.14
       chalk: 4.1.2
       collect-v8-coverage: 1.0.2
       exit: 0.1.2
@@ -3724,7 +3741,7 @@ snapshots:
       '@jest/schemas': 29.6.3
       '@types/istanbul-lib-coverage': 2.0.6
       '@types/istanbul-reports': 3.0.4
-      '@types/node': 20.14.13
+      '@types/node': 20.14.14
       '@types/yargs': 17.0.32
       chalk: 4.1.2
 
@@ -3787,7 +3804,7 @@ snapshots:
     dependencies:
       react: 18.3.1
 
-  '@remix-run/router@1.18.0': {}
+  '@remix-run/router@1.19.0': {}
 
   '@sinclair/typebox@0.27.8': {}
 
@@ -3863,7 +3880,7 @@ snapshots:
     dependencies:
       '@types/http-cache-semantics': 4.0.4
       '@types/keyv': 3.1.4
-      '@types/node': 20.14.13
+      '@types/node': 20.14.14
       '@types/responselike': 1.0.3
 
   '@types/debug@4.1.12':
@@ -3884,11 +3901,11 @@ snapshots:
 
   '@types/fs-extra@9.0.13':
     dependencies:
-      '@types/node': 20.14.13
+      '@types/node': 20.14.14
 
   '@types/graceful-fs@4.1.9':
     dependencies:
-      '@types/node': 20.14.13
+      '@types/node': 20.14.14
 
   '@types/history@4.7.11': {}
 
@@ -3911,7 +3928,7 @@ snapshots:
 
   '@types/jsdom@20.0.1':
     dependencies:
-      '@types/node': 20.14.13
+      '@types/node': 20.14.14
       '@types/tough-cookie': 4.0.5
       parse5: 7.1.2
 
@@ -3919,11 +3936,11 @@ snapshots:
 
   '@types/keyv@3.1.4':
     dependencies:
-      '@types/node': 20.14.13
+      '@types/node': 20.14.14
 
   '@types/ms@0.7.34': {}
 
-  '@types/node@20.14.13':
+  '@types/node@20.14.14':
     dependencies:
       undici-types: 5.26.5
 
@@ -3931,7 +3948,7 @@ snapshots:
 
   '@types/plist@3.0.5':
     dependencies:
-      '@types/node': 20.14.13
+      '@types/node': 20.14.14
       xmlbuilder: 15.1.1
     optional: true
 
@@ -3960,7 +3977,7 @@ snapshots:
 
   '@types/responselike@1.0.3':
     dependencies:
-      '@types/node': 20.14.13
+      '@types/node': 20.14.14
 
   '@types/stack-utils@2.0.3': {}
 
@@ -3977,7 +3994,7 @@ snapshots:
 
   '@types/yauzl@2.10.3':
     dependencies:
-      '@types/node': 20.14.13
+      '@types/node': 20.14.14
     optional: true
 
   '@webassemblyjs/ast@1.12.1':
@@ -4236,17 +4253,17 @@ snapshots:
 
   at-least-node@1.0.0: {}
 
-  autoprefixer@10.4.19(postcss@8.4.40):
+  autoprefixer@10.4.20(postcss@8.4.40):
     dependencies:
-      browserslist: 4.23.0
-      caniuse-lite: 1.0.30001600
+      browserslist: 4.23.3
+      caniuse-lite: 1.0.30001647
       fraction.js: 4.3.7
       normalize-range: 0.1.2
-      picocolors: 1.0.0
+      picocolors: 1.0.1
       postcss: 8.4.40
       postcss-value-parser: 4.2.0
 
-  axios@1.7.2:
+  axios@1.7.3:
     dependencies:
       follow-redirects: 1.15.6
       form-data: 4.0.0
@@ -4347,6 +4364,13 @@ snapshots:
       node-releases: 2.0.14
       update-browserslist-db: 1.0.13(browserslist@4.23.0)
 
+  browserslist@4.23.3:
+    dependencies:
+      caniuse-lite: 1.0.30001647
+      electron-to-chromium: 1.5.4
+      node-releases: 2.0.18
+      update-browserslist-db: 1.1.0(browserslist@4.23.3)
+
   bs-logger@0.2.6:
     dependencies:
       fast-json-stable-stringify: 2.1.0
@@ -4423,6 +4447,8 @@ snapshots:
 
   caniuse-lite@1.0.30001600: {}
 
+  caniuse-lite@1.0.30001647: {}
+
   chalk@2.4.2:
     dependencies:
       ansi-styles: 3.2.1
@@ -4562,13 +4588,13 @@ snapshots:
       buffer: 5.7.1
     optional: true
 
-  create-jest@29.7.0(@types/node@20.14.13)(ts-node@10.9.2(@types/node@20.14.13)(typescript@5.5.4)):
+  create-jest@29.7.0(@types/node@20.14.14)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4)):
     dependencies:
       '@jest/types': 29.6.3
       chalk: 4.1.2
       exit: 0.1.2
       graceful-fs: 4.2.11
-      jest-config: 29.7.0(@types/node@20.14.13)(ts-node@10.9.2(@types/node@20.14.13)(typescript@5.5.4))
+      jest-config: 29.7.0(@types/node@20.14.14)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))
       jest-util: 29.7.0
       prompts: 2.4.2
     transitivePeerDependencies:
@@ -4761,7 +4787,9 @@ snapshots:
 
   electron-to-chromium@1.4.719: {}
 
-  electron-updater@6.3.1:
+  electron-to-chromium@1.5.4: {}
+
+  electron-updater@6.3.2:
     dependencies:
       builder-util-runtime: 9.2.5
       fs-extra: 10.1.0
@@ -4777,7 +4805,7 @@ snapshots:
   electron@31.3.1:
     dependencies:
       '@electron/get': 2.0.3
-      '@types/node': 20.14.13
+      '@types/node': 20.14.14
       extract-zip: 2.0.1
     transitivePeerDependencies:
       - supports-color
@@ -5300,7 +5328,7 @@ snapshots:
       '@jest/expect': 29.7.0
       '@jest/test-result': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.14.13
+      '@types/node': 20.14.14
       chalk: 4.1.2
       co: 4.6.0
       dedent: 1.5.1
@@ -5320,16 +5348,16 @@ snapshots:
       - babel-plugin-macros
       - supports-color
 
-  jest-cli@29.7.0(@types/node@20.14.13)(ts-node@10.9.2(@types/node@20.14.13)(typescript@5.5.4)):
+  jest-cli@29.7.0(@types/node@20.14.14)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4)):
     dependencies:
-      '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@20.14.13)(typescript@5.5.4))
+      '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))
       '@jest/test-result': 29.7.0
       '@jest/types': 29.6.3
       chalk: 4.1.2
-      create-jest: 29.7.0(@types/node@20.14.13)(ts-node@10.9.2(@types/node@20.14.13)(typescript@5.5.4))
+      create-jest: 29.7.0(@types/node@20.14.14)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))
       exit: 0.1.2
       import-local: 3.1.0
-      jest-config: 29.7.0(@types/node@20.14.13)(ts-node@10.9.2(@types/node@20.14.13)(typescript@5.5.4))
+      jest-config: 29.7.0(@types/node@20.14.14)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))
       jest-util: 29.7.0
       jest-validate: 29.7.0
       yargs: 17.7.2
@@ -5339,7 +5367,7 @@ snapshots:
       - supports-color
       - ts-node
 
-  jest-config@29.7.0(@types/node@20.14.13)(ts-node@10.9.2(@types/node@20.14.13)(typescript@5.5.4)):
+  jest-config@29.7.0(@types/node@20.14.14)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4)):
     dependencies:
       '@babel/core': 7.24.3
       '@jest/test-sequencer': 29.7.0
@@ -5364,8 +5392,8 @@ snapshots:
       slash: 3.0.0
       strip-json-comments: 3.1.1
     optionalDependencies:
-      '@types/node': 20.14.13
-      ts-node: 10.9.2(@types/node@20.14.13)(typescript@5.5.4)
+      '@types/node': 20.14.14
+      ts-node: 10.9.2(@types/node@20.14.14)(typescript@5.5.4)
     transitivePeerDependencies:
       - babel-plugin-macros
       - supports-color
@@ -5395,7 +5423,7 @@ snapshots:
       '@jest/fake-timers': 29.7.0
       '@jest/types': 29.6.3
       '@types/jsdom': 20.0.1
-      '@types/node': 20.14.13
+      '@types/node': 20.14.14
       jest-mock: 29.7.0
       jest-util: 29.7.0
       jsdom: 20.0.3
@@ -5409,7 +5437,7 @@ snapshots:
       '@jest/environment': 29.7.0
       '@jest/fake-timers': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.14.13
+      '@types/node': 20.14.14
       jest-mock: 29.7.0
       jest-util: 29.7.0
 
@@ -5419,7 +5447,7 @@ snapshots:
     dependencies:
       '@jest/types': 29.6.3
       '@types/graceful-fs': 4.1.9
-      '@types/node': 20.14.13
+      '@types/node': 20.14.14
       anymatch: 3.1.3
       fb-watchman: 2.0.2
       graceful-fs: 4.2.11
@@ -5458,7 +5486,7 @@ snapshots:
   jest-mock@29.7.0:
     dependencies:
       '@jest/types': 29.6.3
-      '@types/node': 20.14.13
+      '@types/node': 20.14.14
       jest-util: 29.7.0
 
   jest-pnp-resolver@1.2.3(jest-resolve@29.7.0):
@@ -5493,7 +5521,7 @@ snapshots:
       '@jest/test-result': 29.7.0
       '@jest/transform': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.14.13
+      '@types/node': 20.14.14
       chalk: 4.1.2
       emittery: 0.13.1
       graceful-fs: 4.2.11
@@ -5521,7 +5549,7 @@ snapshots:
       '@jest/test-result': 29.7.0
       '@jest/transform': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.14.13
+      '@types/node': 20.14.14
       chalk: 4.1.2
       cjs-module-lexer: 1.2.3
       collect-v8-coverage: 1.0.2
@@ -5567,7 +5595,7 @@ snapshots:
   jest-util@29.7.0:
     dependencies:
       '@jest/types': 29.6.3
-      '@types/node': 20.14.13
+      '@types/node': 20.14.14
       chalk: 4.1.2
       ci-info: 3.9.0
       graceful-fs: 4.2.11
@@ -5586,7 +5614,7 @@ snapshots:
     dependencies:
       '@jest/test-result': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.14.13
+      '@types/node': 20.14.14
       ansi-escapes: 4.3.2
       chalk: 4.1.2
       emittery: 0.13.1
@@ -5595,23 +5623,23 @@ snapshots:
 
   jest-worker@27.5.1:
     dependencies:
-      '@types/node': 20.14.13
+      '@types/node': 20.14.14
       merge-stream: 2.0.0
       supports-color: 8.1.1
 
   jest-worker@29.7.0:
     dependencies:
-      '@types/node': 20.14.13
+      '@types/node': 20.14.14
       jest-util: 29.7.0
       merge-stream: 2.0.0
       supports-color: 8.1.1
 
-  jest@29.7.0(@types/node@20.14.13)(ts-node@10.9.2(@types/node@20.14.13)(typescript@5.5.4)):
+  jest@29.7.0(@types/node@20.14.14)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4)):
     dependencies:
-      '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@20.14.13)(typescript@5.5.4))
+      '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))
       '@jest/types': 29.6.3
       import-local: 3.1.0
-      jest-cli: 29.7.0(@types/node@20.14.13)(ts-node@10.9.2(@types/node@20.14.13)(typescript@5.5.4))
+      jest-cli: 29.7.0(@types/node@20.14.14)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))
     transitivePeerDependencies:
       - '@types/node'
       - babel-plugin-macros
@@ -5857,6 +5885,8 @@ snapshots:
 
   node-releases@2.0.14: {}
 
+  node-releases@2.0.18: {}
+
   normalize-path@3.0.0: {}
 
   normalize-range@0.1.2: {}
@@ -5939,8 +5969,6 @@ snapshots:
 
   pend@1.2.0: {}
 
-  picocolors@1.0.0: {}
-
   picocolors@1.0.1: {}
 
   picomatch@2.3.1: {}
@@ -5971,13 +5999,13 @@ snapshots:
       camelcase-css: 2.0.1
       postcss: 8.4.40
 
-  postcss-load-config@4.0.2(postcss@8.4.40)(ts-node@10.9.2(@types/node@20.14.13)(typescript@5.5.4)):
+  postcss-load-config@4.0.2(postcss@8.4.40)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4)):
     dependencies:
       lilconfig: 3.1.1
       yaml: 2.4.1
     optionalDependencies:
       postcss: 8.4.40
-      ts-node: 10.9.2(@types/node@20.14.13)(typescript@5.5.4)
+      ts-node: 10.9.2(@types/node@20.14.14)(typescript@5.5.4)
 
   postcss-loader@8.1.1(postcss@8.4.40)(typescript@5.5.4)(webpack@5.93.0(webpack-cli@5.1.4)):
     dependencies:
@@ -6096,16 +6124,16 @@ snapshots:
 
   react-is@18.2.0: {}
 
-  react-router-dom@6.25.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
+  react-router-dom@6.26.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
     dependencies:
-      '@remix-run/router': 1.18.0
+      '@remix-run/router': 1.19.0
       react: 18.3.1
       react-dom: 18.3.1(react@18.3.1)
-      react-router: 6.25.1(react@18.3.1)
+      react-router: 6.26.0(react@18.3.1)
 
-  react-router@6.25.1(react@18.3.1):
+  react-router@6.26.0(react@18.3.1):
     dependencies:
-      '@remix-run/router': 1.18.0
+      '@remix-run/router': 1.19.0
       react: 18.3.1
 
   react@18.3.1:
@@ -6386,7 +6414,7 @@ snapshots:
 
   tailwind-merge@2.4.0: {}
 
-  tailwindcss@3.4.7(ts-node@10.9.2(@types/node@20.14.13)(typescript@5.5.4)):
+  tailwindcss@3.4.7(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4)):
     dependencies:
       '@alloc/quick-lru': 5.2.0
       arg: 5.0.2
@@ -6405,7 +6433,7 @@ snapshots:
       postcss: 8.4.40
       postcss-import: 15.1.0(postcss@8.4.40)
       postcss-js: 4.0.1(postcss@8.4.40)
-      postcss-load-config: 4.0.2(postcss@8.4.40)(ts-node@10.9.2(@types/node@20.14.13)(typescript@5.5.4))
+      postcss-load-config: 4.0.2(postcss@8.4.40)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))
       postcss-nested: 6.0.1(postcss@8.4.40)
       postcss-selector-parser: 6.0.16
       resolve: 1.22.8
@@ -6500,12 +6528,12 @@ snapshots:
 
   ts-interface-checker@0.1.13: {}
 
-  ts-jest@29.2.3(@babel/core@7.24.3)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.3))(jest@29.7.0(@types/node@20.14.13)(ts-node@10.9.2(@types/node@20.14.13)(typescript@5.5.4)))(typescript@5.5.4):
+  ts-jest@29.2.4(@babel/core@7.24.3)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.3))(jest@29.7.0(@types/node@20.14.14)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4)))(typescript@5.5.4):
     dependencies:
       bs-logger: 0.2.6
       ejs: 3.1.10
       fast-json-stable-stringify: 2.1.0
-      jest: 29.7.0(@types/node@20.14.13)(ts-node@10.9.2(@types/node@20.14.13)(typescript@5.5.4))
+      jest: 29.7.0(@types/node@20.14.14)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))
       jest-util: 29.7.0
       json5: 2.2.3
       lodash.memoize: 4.1.2
@@ -6529,14 +6557,14 @@ snapshots:
       typescript: 5.5.4
       webpack: 5.93.0(webpack-cli@5.1.4)
 
-  ts-node@10.9.2(@types/node@20.14.13)(typescript@5.5.4):
+  ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4):
     dependencies:
       '@cspotcode/source-map-support': 0.8.1
       '@tsconfig/node10': 1.0.11
       '@tsconfig/node12': 1.0.11
       '@tsconfig/node14': 1.0.3
       '@tsconfig/node16': 1.0.4
-      '@types/node': 20.14.13
+      '@types/node': 20.14.14
       acorn: 8.11.3
       acorn-walk: 8.3.2
       arg: 4.1.3
@@ -6570,7 +6598,13 @@ snapshots:
     dependencies:
       browserslist: 4.23.0
       escalade: 3.1.2
-      picocolors: 1.0.0
+      picocolors: 1.0.1
+
+  update-browserslist-db@1.1.0(browserslist@4.23.3):
+    dependencies:
+      browserslist: 4.23.3
+      escalade: 3.1.2
+      picocolors: 1.0.1
 
   uri-js@4.4.1:
     dependencies:
diff --git a/src/components/settings/SettingsFooter.tsx b/src/components/settings/SettingsFooter.tsx
index 54d4c470..e53ca02e 100644
--- a/src/components/settings/SettingsFooter.tsx
+++ b/src/components/settings/SettingsFooter.tsx
@@ -1,12 +1,23 @@
-import { PersonIcon, XCircleIcon } from '@primer/octicons-react';
+import {
+  AlertFillIcon,
+  CheckCircleFillIcon,
+  PersonIcon,
+  XCircleIcon,
+} from '@primer/octicons-react';
 import { type FC, useEffect, useState } from 'react';
 import { useNavigate } from 'react-router-dom';
 import { BUTTON_CLASS_NAME } from '../../styles/gitify';
-import { Size } from '../../types';
+import { IconColor, Size } from '../../types';
 import { getAppVersion, quitApp } from '../../utils/comms';
 import { openGitifyReleaseNotes } from '../../utils/links';
 
-export const SettingsFooter: FC = () => {
+interface ISettingsFooter {
+  isUpdateAvailable?: boolean;
+}
+
+export const SettingsFooter: FC<ISettingsFooter> = ({
+  isUpdateAvailable = false,
+}: ISettingsFooter) => {
   const [appVersion, setAppVersion] = useState<string | null>(null);
   const navigate = useNavigate();
 
@@ -29,7 +40,26 @@ export const SettingsFooter: FC = () => {
         title="View release notes"
         onClick={() => openGitifyReleaseNotes(appVersion)}
       >
-        <span title="app-version">Gitify {appVersion}</span>
+        <div className="flex items-center gap-1">
+          <span aria-label="app-version">Gitify {appVersion}</span>
+          <span className="pb-1">
+            {isUpdateAvailable ? (
+              <span title="New version available">
+                <AlertFillIcon
+                  size={Size.XSMALL}
+                  className={IconColor.YELLOW}
+                />
+              </span>
+            ) : (
+              <span title="You are using the latest version">
+                <CheckCircleFillIcon
+                  size={Size.XSMALL}
+                  className={IconColor.GREEN}
+                />
+              </span>
+            )}
+          </span>
+        </div>
       </button>
       <div>
         <button
diff --git a/src/electron/main.js b/src/electron/main.js
index 9bb7d7ec..b8d6f853 100644
--- a/src/electron/main.js
+++ b/src/electron/main.js
@@ -40,7 +40,30 @@ 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,
+  },
+  {
+    label: 'Restart to update',
+    visible: isUpdateDownloaded,
+    click: () => {
+      autoUpdater.quitAndInstall();
+    },
+  },
+  { type: 'separator' },
   {
     label: 'Developer',
     submenu: [
@@ -88,8 +111,6 @@ app.whenReady().then(async () => {
   await onFirstRunMaybe();
 
   mb.on('ready', () => {
-    autoUpdater.checkForUpdatesAndNotify();
-
     mb.app.setAppUserModelId('com.electron.gitify');
 
     // Tray configuration
@@ -121,6 +142,27 @@ 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', () => {
@@ -156,7 +198,7 @@ app.whenReady().then(async () => {
 
   ipc.on('gitify:update-title', (_, title) => {
     if (!mb.tray.isDestroyed()) {
-      mb.tray.setTitle(title);
+      mb.tray.setTitle(`${isUpdateAvailable ? '⤓' : ''}${title}`);
     }
   });
 
@@ -183,6 +225,11 @@ app.whenReady().then(async () => {
   });
 });
 
+function checkForUpdates() {
+  log.info('Auto Updater: Checking for updates...');
+  autoUpdater.checkForUpdatesAndNotify();
+}
+
 function takeScreenshot() {
   const date = new Date();
   const dateStr = date.toISOString().replace(/:/g, '-');
diff --git a/src/routes/Settings.tsx b/src/routes/Settings.tsx
index 8a84c117..3d07f80e 100644
--- a/src/routes/Settings.tsx
+++ b/src/routes/Settings.tsx
@@ -1,5 +1,6 @@
 import { GearIcon } from '@primer/octicons-react';
-import { type FC, useContext } from 'react';
+import { ipcRenderer } from 'electron';
+import { type FC, useContext, useEffect, useState } from 'react';
 import { Header } from '../components/Header';
 import { AppearanceSettings } from '../components/settings/AppearanceSettings';
 import { NotificationSettings } from '../components/settings/NotificationSettings';
@@ -9,6 +10,14 @@ 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 (
     <div className="flex h-screen flex-col" data-testid="settings">
       <Header fetchOnBack icon={GearIcon}>
@@ -31,7 +40,7 @@ export const SettingsRoute: FC = () => {
         </button>
       </div>
 
-      <SettingsFooter />
+      <SettingsFooter isUpdateAvailable={isUpdateAvailable} />
     </div>
   );
 };


Test patch:
diff --git a/src/components/settings/SettingsFooter.test.tsx b/src/components/settings/SettingsFooter.test.tsx
index 5f1f4178..5e0b23c0 100644
--- a/src/components/settings/SettingsFooter.test.tsx
+++ b/src/components/settings/SettingsFooter.test.tsx
@@ -48,7 +48,7 @@ describe('routes/components/settings/SettingsFooter.tsx', () => {
         );
       });
 
-      expect(screen.getByTitle('app-version')).toMatchSnapshot();
+      expect(screen.getByLabelText('app-version')).toMatchSnapshot();
     });
 
     it('should show development app version', async () => {
@@ -72,7 +72,49 @@ describe('routes/components/settings/SettingsFooter.tsx', () => {
         );
       });
 
-      expect(screen.getByTitle('app-version')).toMatchSnapshot();
+      expect(screen.getByLabelText('app-version')).toMatchSnapshot();
+    });
+  });
+
+  describe('update available visual indicator', () => {
+    it('using latest version', async () => {
+      await act(async () => {
+        render(
+          <AppContext.Provider
+            value={{
+              auth: mockAuth,
+              settings: mockSettings,
+            }}
+          >
+            <MemoryRouter>
+              <SettingsFooter isUpdateAvailable={false} />
+            </MemoryRouter>
+          </AppContext.Provider>,
+        );
+      });
+
+      expect(
+        screen.getByTitle('You are using the latest version'),
+      ).toMatchSnapshot();
+    });
+
+    it('new version available', async () => {
+      await act(async () => {
+        render(
+          <AppContext.Provider
+            value={{
+              auth: mockAuth,
+              settings: mockSettings,
+            }}
+          >
+            <MemoryRouter>
+              <SettingsFooter isUpdateAvailable={true} />
+            </MemoryRouter>
+          </AppContext.Provider>,
+        );
+      });
+
+      expect(screen.getByTitle('New version available')).toMatchSnapshot();
     });
   });
 
diff --git a/src/components/settings/__snapshots__/SettingsFooter.test.tsx.snap b/src/components/settings/__snapshots__/SettingsFooter.test.tsx.snap
index ade8eef0..c06b2907 100644
--- a/src/components/settings/__snapshots__/SettingsFooter.test.tsx.snap
+++ b/src/components/settings/__snapshots__/SettingsFooter.test.tsx.snap
@@ -2,7 +2,7 @@
 
 exports[`routes/components/settings/SettingsFooter.tsx app version should show development app version 1`] = `
 <span
-  title="app-version"
+  aria-label="app-version"
 >
   Gitify 
   dev
@@ -11,9 +11,51 @@ exports[`routes/components/settings/SettingsFooter.tsx app version should show d
 
 exports[`routes/components/settings/SettingsFooter.tsx app version should show production app version 1`] = `
 <span
-  title="app-version"
+  aria-label="app-version"
 >
   Gitify 
   v0.0.1
 </span>
 `;
+
+exports[`routes/components/settings/SettingsFooter.tsx update available visual indicator new version available 1`] = `
+<span
+  title="New version available"
+>
+  <svg
+    aria-hidden="true"
+    class="text-yellow-500 dark:text-yellow-300"
+    fill="currentColor"
+    focusable="false"
+    height="12"
+    style="display: inline-block; user-select: none; vertical-align: text-bottom; overflow: visible;"
+    viewBox="0 0 12 12"
+    width="12"
+  >
+    <path
+      d="M4.855.708c.5-.896 1.79-.896 2.29 0l4.675 8.351a1.312 1.312 0 0 1-1.146 1.954H1.33A1.313 1.313 0 0 1 .183 9.058ZM7 7V3H5v4Zm-1 3a1 1 0 1 0 0-2 1 1 0 0 0 0 2Z"
+    />
+  </svg>
+</span>
+`;
+
+exports[`routes/components/settings/SettingsFooter.tsx update available visual indicator using latest version 1`] = `
+<span
+  title="You are using the latest version"
+>
+  <svg
+    aria-hidden="true"
+    class="text-green-500"
+    fill="currentColor"
+    focusable="false"
+    height="12"
+    style="display: inline-block; user-select: none; vertical-align: text-bottom; overflow: visible;"
+    viewBox="0 0 12 12"
+    width="12"
+  >
+    <path
+      d="M6 0a6 6 0 1 1 0 12A6 6 0 0 1 6 0Zm-.705 8.737L9.63 4.403 8.392 3.166 5.295 6.263l-1.7-1.702L2.356 5.8l2.938 2.938Z"
+    />
+  </svg>
+</span>
+`;
diff --git a/src/routes/__snapshots__/Settings.test.tsx.snap b/src/routes/__snapshots__/Settings.test.tsx.snap
index 58ff65ec..e5b4f986 100644
--- a/src/routes/__snapshots__/Settings.test.tsx.snap
+++ b/src/routes/__snapshots__/Settings.test.tsx.snap
@@ -768,12 +768,38 @@ exports[`routes/Settings.tsx should render itself & its children 1`] = `
       title="View release notes"
       type="button"
     >
-      <span
-        title="app-version"
+      <div
+        class="flex items-center gap-1"
       >
-        Gitify 
-        v0.0.1
-      </span>
+        <span
+          aria-label="app-version"
+        >
+          Gitify 
+          v0.0.1
+        </span>
+        <span
+          class="pb-1"
+        >
+          <span
+            title="You are using the latest version"
+          >
+            <svg
+              aria-hidden="true"
+              class="text-green-500"
+              fill="currentColor"
+              focusable="false"
+              height="12"
+              style="display: inline-block; user-select: none; vertical-align: text-bottom; overflow: visible;"
+              viewBox="0 0 12 12"
+              width="12"
+            >
+              <path
+                d="M6 0a6 6 0 1 1 0 12A6 6 0 0 1 6 0Zm-.705 8.737L9.63 4.403 8.392 3.166 5.295 6.263l-1.7-1.702L2.356 5.8l2.938 2.938Z"
+              />
+            </svg>
+          </span>
+        </span>
+      </div>
     </button>
     <div>
       <button