Repository: gitify-app/gitify
Pull request #1507 (2024-09-02 13:19:34)

fix: default avatar icon size
fix inconsistent sizes between avatar img and default avatar icon


Patch:
diff --git a/package.json b/package.json
index 2e9e0867..8af75a9e 100644
--- a/package.json
+++ b/package.json
@@ -139,7 +139,7 @@
     "jest": "29.7.0",
     "jest-environment-jsdom": "29.7.0",
     "nock": "13.5.5",
-    "postcss": "8.4.43",
+    "postcss": "8.4.44",
     "postcss-loader": "8.1.1",
     "resize-observer-polyfill": "1.5.1",
     "rimraf": "6.0.1",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 107da8e6..66afcaf6 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -95,7 +95,7 @@ importers:
         version: 5.3.3
       autoprefixer:
         specifier: 10.4.20
-        version: 10.4.20(postcss@8.4.43)
+        version: 10.4.20(postcss@8.4.44)
       css-loader:
         specifier: 7.1.2
         version: 7.1.2(webpack@5.94.0(webpack-cli@5.1.4))
@@ -121,11 +121,11 @@ importers:
         specifier: 13.5.5
         version: 13.5.5
       postcss:
-        specifier: 8.4.43
-        version: 8.4.43
+        specifier: 8.4.44
+        version: 8.4.44
       postcss-loader:
         specifier: 8.1.1
-        version: 8.1.1(postcss@8.4.43)(typescript@5.5.4)(webpack@5.94.0(webpack-cli@5.1.4))
+        version: 8.1.1(postcss@8.4.44)(typescript@5.5.4)(webpack@5.94.0(webpack-cli@5.1.4))
       resize-observer-polyfill:
         specifier: 1.5.1
         version: 1.5.1
@@ -2521,8 +2521,8 @@ packages:
   postcss-value-parser@4.2.0:
     resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
 
-  postcss@8.4.43:
-    resolution: {integrity: sha512-gJAQVYbh5R3gYm33FijzCZj7CHyQ3hWMgJMprLUlIYqCwTeZhBQ19wp0e9mA25BUbEvY5+EXuuaAjqQsrBxQBQ==}
+  postcss@8.4.44:
+    resolution: {integrity: sha512-Aweb9unOEpQ3ezu4Q00DPvvM2ZTUitJdNKeP/+uQgr1IBIqu574IaZoURId7BKtWMREwzKa9OgzPzezWGPWFQw==}
     engines: {node: ^10 || ^12 || >=14}
 
   pretty-format@27.5.1:
@@ -4255,14 +4255,14 @@ snapshots:
 
   at-least-node@1.0.0: {}
 
-  autoprefixer@10.4.20(postcss@8.4.43):
+  autoprefixer@10.4.20(postcss@8.4.44):
     dependencies:
       browserslist: 4.23.3
       caniuse-lite: 1.0.30001647
       fraction.js: 4.3.7
       normalize-range: 0.1.2
       picocolors: 1.0.1
-      postcss: 8.4.43
+      postcss: 8.4.44
       postcss-value-parser: 4.2.0
 
   axios@1.7.7:
@@ -4606,12 +4606,12 @@ snapshots:
 
   css-loader@7.1.2(webpack@5.94.0(webpack-cli@5.1.4)):
     dependencies:
-      icss-utils: 5.1.0(postcss@8.4.43)
-      postcss: 8.4.43
-      postcss-modules-extract-imports: 3.1.0(postcss@8.4.43)
-      postcss-modules-local-by-default: 4.0.5(postcss@8.4.43)
-      postcss-modules-scope: 3.2.0(postcss@8.4.43)
-      postcss-modules-values: 4.0.0(postcss@8.4.43)
+      icss-utils: 5.1.0(postcss@8.4.44)
+      postcss: 8.4.44
+      postcss-modules-extract-imports: 3.1.0(postcss@8.4.44)
+      postcss-modules-local-by-default: 4.0.5(postcss@8.4.44)
+      postcss-modules-scope: 3.2.0(postcss@8.4.44)
+      postcss-modules-values: 4.0.0(postcss@8.4.44)
       postcss-value-parser: 4.2.0
       semver: 7.6.0
     optionalDependencies:
@@ -5180,9 +5180,9 @@ snapshots:
     dependencies:
       safer-buffer: 2.1.2
 
-  icss-utils@5.1.0(postcss@8.4.43):
+  icss-utils@5.1.0(postcss@8.4.44):
     dependencies:
-      postcss: 8.4.43
+      postcss: 8.4.44
 
   ieee754@1.2.1: {}
 
@@ -5988,61 +5988,61 @@ snapshots:
       base64-js: 1.5.1
       xmlbuilder: 15.1.1
 
-  postcss-import@15.1.0(postcss@8.4.43):
+  postcss-import@15.1.0(postcss@8.4.44):
     dependencies:
-      postcss: 8.4.43
+      postcss: 8.4.44
       postcss-value-parser: 4.2.0
       read-cache: 1.0.0
       resolve: 1.22.8
 
-  postcss-js@4.0.1(postcss@8.4.43):
+  postcss-js@4.0.1(postcss@8.4.44):
     dependencies:
       camelcase-css: 2.0.1
-      postcss: 8.4.43
+      postcss: 8.4.44
 
-  postcss-load-config@4.0.2(postcss@8.4.43)(ts-node@10.9.2(@types/node@20.16.3)(typescript@5.5.4)):
+  postcss-load-config@4.0.2(postcss@8.4.44)(ts-node@10.9.2(@types/node@20.16.3)(typescript@5.5.4)):
     dependencies:
       lilconfig: 3.1.1
       yaml: 2.4.1
     optionalDependencies:
-      postcss: 8.4.43
+      postcss: 8.4.44
       ts-node: 10.9.2(@types/node@20.16.3)(typescript@5.5.4)
 
-  postcss-loader@8.1.1(postcss@8.4.43)(typescript@5.5.4)(webpack@5.94.0(webpack-cli@5.1.4)):
+  postcss-loader@8.1.1(postcss@8.4.44)(typescript@5.5.4)(webpack@5.94.0(webpack-cli@5.1.4)):
     dependencies:
       cosmiconfig: 9.0.0(typescript@5.5.4)
       jiti: 1.21.0
-      postcss: 8.4.43
+      postcss: 8.4.44
       semver: 7.6.0
     optionalDependencies:
       webpack: 5.94.0(webpack-cli@5.1.4)
     transitivePeerDependencies:
       - typescript
 
-  postcss-modules-extract-imports@3.1.0(postcss@8.4.43):
+  postcss-modules-extract-imports@3.1.0(postcss@8.4.44):
     dependencies:
-      postcss: 8.4.43
+      postcss: 8.4.44
 
-  postcss-modules-local-by-default@4.0.5(postcss@8.4.43):
+  postcss-modules-local-by-default@4.0.5(postcss@8.4.44):
     dependencies:
-      icss-utils: 5.1.0(postcss@8.4.43)
-      postcss: 8.4.43
+      icss-utils: 5.1.0(postcss@8.4.44)
+      postcss: 8.4.44
       postcss-selector-parser: 6.0.16
       postcss-value-parser: 4.2.0
 
-  postcss-modules-scope@3.2.0(postcss@8.4.43):
+  postcss-modules-scope@3.2.0(postcss@8.4.44):
     dependencies:
-      postcss: 8.4.43
+      postcss: 8.4.44
       postcss-selector-parser: 6.0.16
 
-  postcss-modules-values@4.0.0(postcss@8.4.43):
+  postcss-modules-values@4.0.0(postcss@8.4.44):
     dependencies:
-      icss-utils: 5.1.0(postcss@8.4.43)
-      postcss: 8.4.43
+      icss-utils: 5.1.0(postcss@8.4.44)
+      postcss: 8.4.44
 
-  postcss-nested@6.0.1(postcss@8.4.43):
+  postcss-nested@6.0.1(postcss@8.4.44):
     dependencies:
-      postcss: 8.4.43
+      postcss: 8.4.44
       postcss-selector-parser: 6.0.16
 
   postcss-selector-parser@6.0.16:
@@ -6052,7 +6052,7 @@ snapshots:
 
   postcss-value-parser@4.2.0: {}
 
-  postcss@8.4.43:
+  postcss@8.4.44:
     dependencies:
       nanoid: 3.3.7
       picocolors: 1.0.1
@@ -6433,11 +6433,11 @@ snapshots:
       normalize-path: 3.0.0
       object-hash: 3.0.0
       picocolors: 1.0.1
-      postcss: 8.4.43
-      postcss-import: 15.1.0(postcss@8.4.43)
-      postcss-js: 4.0.1(postcss@8.4.43)
-      postcss-load-config: 4.0.2(postcss@8.4.43)(ts-node@10.9.2(@types/node@20.16.3)(typescript@5.5.4))
-      postcss-nested: 6.0.1(postcss@8.4.43)
+      postcss: 8.4.44
+      postcss-import: 15.1.0(postcss@8.4.44)
+      postcss-js: 4.0.1(postcss@8.4.44)
+      postcss-load-config: 4.0.2(postcss@8.4.44)(ts-node@10.9.2(@types/node@20.16.3)(typescript@5.5.4))
+      postcss-nested: 6.0.1(postcss@8.4.44)
       postcss-selector-parser: 6.0.16
       resolve: 1.22.8
       sucrase: 3.35.0
diff --git a/src/__mocks__/partial-mocks.ts b/src/__mocks__/partial-mocks.ts
index 2d6023a8..5db97d31 100644
--- a/src/__mocks__/partial-mocks.ts
+++ b/src/__mocks__/partial-mocks.ts
@@ -1,6 +1,6 @@
 import type { Hostname, Link } from '../types';
 import type { Notification, Subject, User } from '../typesGitHub';
-import Constants from '../utils/constants';
+import { Constants } from '../utils/constants';
 import { mockGitifyUser, mockToken } from './state-mocks';
 
 export function partialMockNotification(
diff --git a/src/__mocks__/state-mocks.ts b/src/__mocks__/state-mocks.ts
index 12b3f638..07237b59 100644
--- a/src/__mocks__/state-mocks.ts
+++ b/src/__mocks__/state-mocks.ts
@@ -12,7 +12,7 @@ import {
   type Token,
 } from '../types';
 import type { EnterpriseAccount } from '../utils/auth/types';
-import Constants from '../utils/constants';
+import { Constants } from '../utils/constants';
 
 export const mockEnterpriseAccounts: EnterpriseAccount[] = [
   {
@@ -87,6 +87,7 @@ const mockNotificationSettings = {
   groupBy: GroupBy.REPOSITORY,
   participating: false,
   markAsDoneOnOpen: false,
+  markAsDoneOnUnsubscribe: false,
   delayNotificationState: false,
 };
 
diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx
index 6391d205..61f2f584 100644
--- a/src/components/Sidebar.tsx
+++ b/src/components/Sidebar.tsx
@@ -12,7 +12,7 @@ import { useLocation, useNavigate } from 'react-router-dom';
 import { AppContext } from '../context/App';
 import { Size } from '../types';
 import { quitApp } from '../utils/comms';
-import Constants from '../utils/constants';
+import { Constants } from '../utils/constants';
 import { getFilterCount } from '../utils/helpers';
 import {
   openGitHubIssues,
diff --git a/src/components/icons/AvatarIcon.tsx b/src/components/icons/AvatarIcon.tsx
index 98c03610..7db4f485 100644
--- a/src/components/icons/AvatarIcon.tsx
+++ b/src/components/icons/AvatarIcon.tsx
@@ -26,9 +26,12 @@ export const AvatarIcon: FC<IAvatarIcon> = (props: IAvatarIcon) => {
     );
   }
 
+  const defaultIconSize =
+    props.size === Size.XSMALL ? 16 : props.size === Size.SMALL ? 20 : 24;
+
   return (
     <props.defaultIcon
-      size={props.size}
+      size={defaultIconSize}
       className="text-gray-500 dark:text-gray-300"
     />
   );
diff --git a/src/components/settings/NotificationSettings.tsx b/src/components/settings/NotificationSettings.tsx
index 1a709628..329ea999 100644
--- a/src/components/settings/NotificationSettings.tsx
+++ b/src/components/settings/NotificationSettings.tsx
@@ -63,6 +63,20 @@ export const NotificationSettings: FC = () => {
           </div>
         }
       />
+      <Checkbox
+        name="markAsDoneOnUnsubscribe"
+        label="Mark as done on unsubscribe"
+        checked={settings.markAsDoneOnUnsubscribe}
+        onChange={(evt) =>
+          updateSetting('markAsDoneOnUnsubscribe', evt.target.checked)
+        }
+        tooltip={
+          <div>
+            <strong>Mark as Done</strong> feature is supported in GitHub Cloud
+            and GitHub Enterprise Server 3.13 or later.
+          </div>
+        }
+      />
       <Checkbox
         name="delayNotificationState"
         label="Delay notification state"
diff --git a/src/components/settings/SystemSettings.tsx b/src/components/settings/SystemSettings.tsx
index 1d87ad3f..11eff641 100644
--- a/src/components/settings/SystemSettings.tsx
+++ b/src/components/settings/SystemSettings.tsx
@@ -2,7 +2,7 @@ import { DeviceDesktopIcon } from '@primer/octicons-react';
 import { type FC, useContext } from 'react';
 import { AppContext } from '../../context/App';
 import type { OpenPreference } from '../../types';
-import Constants from '../../utils/constants';
+import { Constants } from '../../utils/constants';
 import { isLinux, isMacOS } from '../../utils/platform';
 import { Checkbox } from '../fields/Checkbox';
 import { RadioGroup } from '../fields/RadioGroup';
diff --git a/src/context/App.tsx b/src/context/App.tsx
index eb93a8e9..c006a129 100644
--- a/src/context/App.tsx
+++ b/src/context/App.tsx
@@ -41,7 +41,7 @@ import {
   setKeyboardShortcut,
   updateTrayTitle,
 } from '../utils/comms';
-import Constants from '../utils/constants';
+import { Constants } from '../utils/constants';
 import { getNotificationCount } from '../utils/notifications';
 import { clearState, loadState, saveState } from '../utils/storage';
 import { setTheme } from '../utils/theme';
@@ -67,6 +67,7 @@ const defaultNotificationSettings = {
   groupBy: GroupBy.REPOSITORY,
   participating: false,
   markAsDoneOnOpen: false,
+  markAsDoneOnUnsubscribe: false,
   delayNotificationState: false,
 };
 
diff --git a/src/hooks/useNotifications.ts b/src/hooks/useNotifications.ts
index c5dd9b11..e732aa0b 100644
--- a/src/hooks/useNotifications.ts
+++ b/src/hooks/useNotifications.ts
@@ -176,7 +176,12 @@ export const useNotifications = (): NotificationsState => {
           notification.account.hostname,
           notification.account.token,
         );
-        await markNotificationRead(state, notification);
+
+        if (state.settings.markAsDoneOnUnsubscribe) {
+          await markNotificationDone(state, notification);
+        } else {
+          await markNotificationRead(state, notification);
+        }
       } catch (err) {
         log.error(
           'Error occurred while unsubscribing from notification thread',
diff --git a/src/routes/LoginWithOAuthApp.tsx b/src/routes/LoginWithOAuthApp.tsx
index 3d560b69..1e0cbbaf 100644
--- a/src/routes/LoginWithOAuthApp.tsx
+++ b/src/routes/LoginWithOAuthApp.tsx
@@ -21,7 +21,7 @@ import {
   isValidHostname,
   isValidToken,
 } from '../utils/auth/utils';
-import Constants from '../utils/constants';
+import { Constants } from '../utils/constants';
 
 interface IValues {
   hostname?: Hostname;
diff --git a/src/routes/Notifications.tsx b/src/routes/Notifications.tsx
index c92430ba..ab129c70 100644
--- a/src/routes/Notifications.tsx
+++ b/src/routes/Notifications.tsx
@@ -4,7 +4,7 @@ import { AllRead } from '../components/AllRead';
 import { Oops } from '../components/Oops';
 import { AppContext } from '../context/App';
 import { getAccountUUID } from '../utils/auth/utils';
-import { Errors } from '../utils/constants';
+import { Errors } from '../utils/errors';
 import { getNotificationCount } from '../utils/notifications';
 
 export const NotificationsRoute: FC = () => {
diff --git a/src/types.ts b/src/types.ts
index d21ffb95..f7c1bea0 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -78,6 +78,7 @@ interface NotificationSettingsState {
   participating: boolean;
   showNotifications: boolean;
   markAsDoneOnOpen: boolean;
+  markAsDoneOnUnsubscribe: boolean;
   delayNotificationState: boolean;
   groupBy: GroupBy;
 }
diff --git a/src/utils/api/errors.ts b/src/utils/api/errors.ts
index 7202d567..4f697614 100644
--- a/src/utils/api/errors.ts
+++ b/src/utils/api/errors.ts
@@ -1,7 +1,7 @@
 import { AxiosError } from 'axios';
 import type { GitifyError } from '../../types';
 import type { GitHubRESTError } from '../../typesGitHub';
-import { Errors } from '../constants';
+import { Errors } from '../errors';
 
 export function determineFailureType(
   err: AxiosError<GitHubRESTError>,
diff --git a/src/utils/api/utils.ts b/src/utils/api/utils.ts
index 644d9f33..9ad0e966 100644
--- a/src/utils/api/utils.ts
+++ b/src/utils/api/utils.ts
@@ -1,5 +1,5 @@
 import type { Hostname } from '../../types';
-import Constants from '../constants';
+import { Constants } from '../constants';
 import { isEnterpriseServerHost } from '../helpers';
 
 export function getGitHubAPIBaseUrl(hostname: Hostname): URL {
diff --git a/src/utils/auth/migration.ts b/src/utils/auth/migration.ts
index 643edcb3..87b32e67 100644
--- a/src/utils/auth/migration.ts
+++ b/src/utils/auth/migration.ts
@@ -1,6 +1,6 @@
 import log from 'electron-log';
 import type { Account, AuthState } from '../../types';
-import Constants from '../constants';
+import { Constants } from '../constants';
 import { loadState, saveState } from '../storage';
 import { getUserData } from './utils';
 
diff --git a/src/utils/comms.ts b/src/utils/comms.ts
index 0a11fa0c..03266e60 100644
--- a/src/utils/comms.ts
+++ b/src/utils/comms.ts
@@ -1,7 +1,7 @@
 import { ipcRenderer, shell } from 'electron';
 import { defaultSettings } from '../context/App';
 import { type Link, OpenPreference } from '../types';
-import Constants from './constants';
+import { Constants } from './constants';
 import { loadState } from './storage';
 
 export function openExternalLink(url: Link): void {
diff --git a/src/utils/constants.ts b/src/utils/constants.ts
index eabacbae..1d95d155 100644
--- a/src/utils/constants.ts
+++ b/src/utils/constants.ts
@@ -1,4 +1,4 @@
-import type { ErrorType, GitifyError, Link } from '../types';
+import type { Link } from '../types';
 import type { ClientID, ClientSecret, Hostname } from '../types';
 
 export const Constants = {
@@ -36,36 +36,3 @@ export const Constants = {
       'https://docs.github.com/en/account-and-profile/managing-subscriptions-and-notifications-on-github/setting-up-notifications/configuring-notifications#about-participating-and-watching-notifications' as Link,
   },
 };
-
-export const Errors: Record<ErrorType, GitifyError> = {
-  BAD_CREDENTIALS: {
-    title: 'Bad Credentials',
-    descriptions: ['Your credentials are either invalid or expired.'],
-    emojis: ['🔓'],
-  },
-  MISSING_SCOPES: {
-    title: 'Missing Scopes',
-    descriptions: ['Your credentials are missing a required API scope.'],
-    emojis: ['🔭'],
-  },
-  NETWORK: {
-    title: 'Network Error',
-    descriptions: [
-      'Unable to connect to one or more of your GitHub environments.',
-      'Please check your network connection, including whether you require a VPN, and try again.',
-    ],
-    emojis: ['🛜'],
-  },
-  RATE_LIMITED: {
-    title: 'Rate Limited',
-    descriptions: ['Please wait a while before trying again.'],
-    emojis: ['😮‍💨'],
-  },
-  UNKNOWN: {
-    title: 'Oops! Something went wrong',
-    descriptions: ['Please try again later.'],
-    emojis: ['🤔', '🥲', '😳', '🫠', '🙃', '🙈'],
-  },
-};
-
-export default Constants;
diff --git a/src/utils/errors.ts b/src/utils/errors.ts
new file mode 100644
index 00000000..e83ee5c7
--- /dev/null
+++ b/src/utils/errors.ts
@@ -0,0 +1,32 @@
+import type { ErrorType, GitifyError } from '../types';
+
+export const Errors: Record<ErrorType, GitifyError> = {
+  BAD_CREDENTIALS: {
+    title: 'Bad Credentials',
+    descriptions: ['Your credentials are either invalid or expired.'],
+    emojis: ['🔓'],
+  },
+  MISSING_SCOPES: {
+    title: 'Missing Scopes',
+    descriptions: ['Your credentials are missing a required API scope.'],
+    emojis: ['🔭'],
+  },
+  NETWORK: {
+    title: 'Network Error',
+    descriptions: [
+      'Unable to connect to one or more of your GitHub environments.',
+      'Please check your network connection, including whether you require a VPN, and try again.',
+    ],
+    emojis: ['🛜'],
+  },
+  RATE_LIMITED: {
+    title: 'Rate Limited',
+    descriptions: ['Please wait a while before trying again.'],
+    emojis: ['😮‍💨'],
+  },
+  UNKNOWN: {
+    title: 'Oops! Something went wrong',
+    descriptions: ['Please try again later.'],
+    emojis: ['🤔', '🥲', '😳', '🫠', '🙃', '🙈'],
+  },
+};
diff --git a/src/utils/links.ts b/src/utils/links.ts
index e19aaadd..28b84d03 100644
--- a/src/utils/links.ts
+++ b/src/utils/links.ts
@@ -2,7 +2,7 @@ import type { Account, Hostname, Link } from '../types';
 import type { Notification, Repository, SubjectUser } from '../typesGitHub';
 import { getDeveloperSettingsURL } from './auth/utils';
 import { openExternalLink } from './comms';
-import Constants from './constants';
+import { Constants } from './constants';
 import { generateGitHubWebUrl } from './helpers';
 
 export function openGitifyRepository() {


Test patch:
diff --git a/src/components/__snapshots__/RepositoryNotifications.test.tsx.snap b/src/components/__snapshots__/RepositoryNotifications.test.tsx.snap
index 31eacbd2..716b1ccc 100644
--- a/src/components/__snapshots__/RepositoryNotifications.test.tsx.snap
+++ b/src/components/__snapshots__/RepositoryNotifications.test.tsx.snap
@@ -261,10 +261,10 @@ exports[`components/Repository.tsx should toggle account notifications visibilit
             class="text-gray-500 dark:text-gray-300"
             fill="currentColor"
             focusable="false"
-            height="12"
+            height="16"
             style="display: inline-block; user-select: none; vertical-align: text-bottom; overflow: visible;"
             viewBox="0 0 16 16"
-            width="12"
+            width="16"
           >
             <path
               d="M8 0c4.42 0 8 3.58 8 8a8.013 8.013 0 0 1-5.45 7.59c-.4.08-.55-.17-.55-.38 0-.27.01-1.13.01-2.2 0-.75-.25-1.23-.54-1.48 1.78-.2 3.65-.88 3.65-3.95 0-.88-.31-1.59-.82-2.15.08-.2.36-1.02-.08-2.12 0 0-.67-.22-2.2.82-.64-.18-1.32-.27-2-.27-.68 0-1.36.09-2 .27-1.53-1.03-2.2-.82-2.2-.82-.44 1.1-.16 1.92-.08 2.12-.51.56-.82 1.28-.82 2.15 0 3.06 1.86 3.75 3.64 3.95-.23.2-.44.55-.51 1.07-.46.21-1.61.55-2.33-.66-.15-.24-.6-.83-1.23-.82-.67.01-.27.38.01.53.34.19.73.9.82 1.13.16.45.68 1.31 2.69.94 0 .67.01 1.3.01 1.49 0 .21-.15.45-.55.38A7.995 7.995 0 0 1 0 8c0-4.42 3.58-8 8-8Z"
@@ -357,10 +357,10 @@ exports[`components/Repository.tsx should toggle account notifications visibilit
             class="text-gray-500 dark:text-gray-300"
             fill="currentColor"
             focusable="false"
-            height="12"
+            height="16"
             style="display: inline-block; user-select: none; vertical-align: text-bottom; overflow: visible;"
             viewBox="0 0 16 16"
-            width="12"
+            width="16"
           >
             <path
               d="M8 0c4.42 0 8 3.58 8 8a8.013 8.013 0 0 1-5.45 7.59c-.4.08-.55-.17-.55-.38 0-.27.01-1.13.01-2.2 0-.75-.25-1.23-.54-1.48 1.78-.2 3.65-.88 3.65-3.95 0-.88-.31-1.59-.82-2.15.08-.2.36-1.02-.08-2.12 0 0-.67-.22-2.2.82-.64-.18-1.32-.27-2-.27-.68 0-1.36.09-2 .27-1.53-1.03-2.2-.82-2.2-.82-.44 1.1-.16 1.92-.08 2.12-.51.56-.82 1.28-.82 2.15 0 3.06 1.86 3.75 3.64 3.95-.23.2-.44.55-.51 1.07-.46.21-1.61.55-2.33-.66-.15-.24-.6-.83-1.23-.82-.67.01-.27.38.01.53.34.19.73.9.82 1.13.16.45.68 1.31 2.69.94 0 .67.01 1.3.01 1.49 0 .21-.15.45-.55.38A7.995 7.995 0 0 1 0 8c0-4.42 3.58-8 8-8Z"
@@ -460,10 +460,10 @@ exports[`components/Repository.tsx should toggle account notifications visibilit
           class="text-gray-500 dark:text-gray-300"
           fill="currentColor"
           focusable="false"
-          height="12"
+          height="16"
           style="display: inline-block; user-select: none; vertical-align: text-bottom; overflow: visible;"
           viewBox="0 0 16 16"
-          width="12"
+          width="16"
         >
           <path
             d="M8 0c4.42 0 8 3.58 8 8a8.013 8.013 0 0 1-5.45 7.59c-.4.08-.55-.17-.55-.38 0-.27.01-1.13.01-2.2 0-.75-.25-1.23-.54-1.48 1.78-.2 3.65-.88 3.65-3.95 0-.88-.31-1.59-.82-2.15.08-.2.36-1.02-.08-2.12 0 0-.67-.22-2.2.82-.64-.18-1.32-.27-2-.27-.68 0-1.36.09-2 .27-1.53-1.03-2.2-.82-2.2-.82-.44 1.1-.16 1.92-.08 2.12-.51.56-.82 1.28-.82 2.15 0 3.06 1.86 3.75 3.64 3.95-.23.2-.44.55-.51 1.07-.46.21-1.61.55-2.33-.66-.15-.24-.6-.83-1.23-.82-.67.01-.27.38.01.53.34.19.73.9.82 1.13.16.45.68 1.31 2.69.94 0 .67.01 1.3.01 1.49 0 .21-.15.45-.55.38A7.995 7.995 0 0 1 0 8c0-4.42 3.58-8 8-8Z"
@@ -620,10 +620,10 @@ exports[`components/Repository.tsx should use default repository icon when avata
             class="text-gray-500 dark:text-gray-300"
             fill="currentColor"
             focusable="false"
-            height="12"
+            height="16"
             style="display: inline-block; user-select: none; vertical-align: text-bottom; overflow: visible;"
             viewBox="0 0 16 16"
-            width="12"
+            width="16"
           >
             <path
               d="M8 0c4.42 0 8 3.58 8 8a8.013 8.013 0 0 1-5.45 7.59c-.4.08-.55-.17-.55-.38 0-.27.01-1.13.01-2.2 0-.75-.25-1.23-.54-1.48 1.78-.2 3.65-.88 3.65-3.95 0-.88-.31-1.59-.82-2.15.08-.2.36-1.02-.08-2.12 0 0-.67-.22-2.2.82-.64-.18-1.32-.27-2-.27-.68 0-1.36.09-2 .27-1.53-1.03-2.2-.82-2.2-.82-.44 1.1-.16 1.92-.08 2.12-.51.56-.82 1.28-.82 2.15 0 3.06 1.86 3.75 3.64 3.95-.23.2-.44.55-.51 1.07-.46.21-1.61.55-2.33-.66-.15-.24-.6-.83-1.23-.82-.67.01-.27.38.01.53.34.19.73.9.82 1.13.16.45.68 1.31 2.69.94 0 .67.01 1.3.01 1.49 0 .21-.15.45-.55.38A7.995 7.995 0 0 1 0 8c0-4.42 3.58-8 8-8Z"
@@ -723,10 +723,10 @@ exports[`components/Repository.tsx should use default repository icon when avata
           class="text-gray-500 dark:text-gray-300"
           fill="currentColor"
           focusable="false"
-          height="12"
+          height="16"
           style="display: inline-block; user-select: none; vertical-align: text-bottom; overflow: visible;"
           viewBox="0 0 16 16"
-          width="12"
+          width="16"
         >
           <path
             d="M8 0c4.42 0 8 3.58 8 8a8.013 8.013 0 0 1-5.45 7.59c-.4.08-.55-.17-.55-.38 0-.27.01-1.13.01-2.2 0-.75-.25-1.23-.54-1.48 1.78-.2 3.65-.88 3.65-3.95 0-.88-.31-1.59-.82-2.15.08-.2.36-1.02-.08-2.12 0 0-.67-.22-2.2.82-.64-.18-1.32-.27-2-.27-.68 0-1.36.09-2 .27-1.53-1.03-2.2-.82-2.2-.82-.44 1.1-.16 1.92-.08 2.12-.51.56-.82 1.28-.82 2.15 0 3.06 1.86 3.75 3.64 3.95-.23.2-.44.55-.51 1.07-.46.21-1.61.55-2.33-.66-.15-.24-.6-.83-1.23-.82-.67.01-.27.38.01.53.34.19.73.9.82 1.13.16.45.68 1.31 2.69.94 0 .67.01 1.3.01 1.49 0 .21-.15.45-.55.38A7.995 7.995 0 0 1 0 8c0-4.42 3.58-8 8-8Z"
diff --git a/src/components/icons/AvatarIcon.test.tsx b/src/components/icons/AvatarIcon.test.tsx
index d1e98604..6dffd386 100644
--- a/src/components/icons/AvatarIcon.test.tsx
+++ b/src/components/icons/AvatarIcon.test.tsx
@@ -37,7 +37,7 @@ describe('components/icons/AvatarIcon.tsx', () => {
     expect(tree).toMatchSnapshot();
   });
 
-  it('should render default icon when no url', () => {
+  it('should render default extra small icon when no url', () => {
     const props: IAvatarIcon = {
       defaultIcon: MarkGithubIcon,
       title: 'test',
@@ -47,4 +47,26 @@ describe('components/icons/AvatarIcon.tsx', () => {
     const tree = render(<AvatarIcon {...props} />);
     expect(tree).toMatchSnapshot();
   });
+
+  it('should render default small icon when no url', () => {
+    const props: IAvatarIcon = {
+      defaultIcon: MarkGithubIcon,
+      title: 'test',
+      url: null,
+      size: Size.SMALL,
+    };
+    const tree = render(<AvatarIcon {...props} />);
+    expect(tree).toMatchSnapshot();
+  });
+
+  it('should render default medium icon when no url', () => {
+    const props: IAvatarIcon = {
+      defaultIcon: MarkGithubIcon,
+      title: 'test',
+      url: null,
+      size: Size.MEDIUM,
+    };
+    const tree = render(<AvatarIcon {...props} />);
+    expect(tree).toMatchSnapshot();
+  });
 });
diff --git a/src/components/icons/__snapshots__/AvatarIcon.test.tsx.snap b/src/components/icons/__snapshots__/AvatarIcon.test.tsx.snap
index b659b6fe..e114d0ee 100644
--- a/src/components/icons/__snapshots__/AvatarIcon.test.tsx.snap
+++ b/src/components/icons/__snapshots__/AvatarIcon.test.tsx.snap
@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`components/icons/AvatarIcon.tsx should render default icon when no url 1`] = `
+exports[`components/icons/AvatarIcon.tsx should render default extra small icon when no url 1`] = `
 {
   "asFragment": [Function],
   "baseElement": <body>
@@ -10,10 +10,10 @@ exports[`components/icons/AvatarIcon.tsx should render default icon when no url
         class="text-gray-500 dark:text-gray-300"
         fill="currentColor"
         focusable="false"
-        height="12"
+        height="16"
         style="display: inline-block; user-select: none; vertical-align: text-bottom; overflow: visible;"
         viewBox="0 0 16 16"
-        width="12"
+        width="16"
       >
         <path
           d="M8 0c4.42 0 8 3.58 8 8a8.013 8.013 0 0 1-5.45 7.59c-.4.08-.55-.17-.55-.38 0-.27.01-1.13.01-2.2 0-.75-.25-1.23-.54-1.48 1.78-.2 3.65-.88 3.65-3.95 0-.88-.31-1.59-.82-2.15.08-.2.36-1.02-.08-2.12 0 0-.67-.22-2.2.82-.64-.18-1.32-.27-2-.27-.68 0-1.36.09-2 .27-1.53-1.03-2.2-.82-2.2-.82-.44 1.1-.16 1.92-.08 2.12-.51.56-.82 1.28-.82 2.15 0 3.06 1.86 3.75 3.64 3.95-.23.2-.44.55-.51 1.07-.46.21-1.61.55-2.33-.66-.15-.24-.6-.83-1.23-.82-.67.01-.27.38.01.53.34.19.73.9.82 1.13.16.45.68 1.31 2.69.94 0 .67.01 1.3.01 1.49 0 .21-.15.45-.55.38A7.995 7.995 0 0 1 0 8c0-4.42 3.58-8 8-8Z"
@@ -27,10 +27,192 @@ exports[`components/icons/AvatarIcon.tsx should render default icon when no url
       class="text-gray-500 dark:text-gray-300"
       fill="currentColor"
       focusable="false"
-      height="12"
+      height="16"
       style="display: inline-block; user-select: none; vertical-align: text-bottom; overflow: visible;"
       viewBox="0 0 16 16"
-      width="12"
+      width="16"
+    >
+      <path
+        d="M8 0c4.42 0 8 3.58 8 8a8.013 8.013 0 0 1-5.45 7.59c-.4.08-.55-.17-.55-.38 0-.27.01-1.13.01-2.2 0-.75-.25-1.23-.54-1.48 1.78-.2 3.65-.88 3.65-3.95 0-.88-.31-1.59-.82-2.15.08-.2.36-1.02-.08-2.12 0 0-.67-.22-2.2.82-.64-.18-1.32-.27-2-.27-.68 0-1.36.09-2 .27-1.53-1.03-2.2-.82-2.2-.82-.44 1.1-.16 1.92-.08 2.12-.51.56-.82 1.28-.82 2.15 0 3.06 1.86 3.75 3.64 3.95-.23.2-.44.55-.51 1.07-.46.21-1.61.55-2.33-.66-.15-.24-.6-.83-1.23-.82-.67.01-.27.38.01.53.34.19.73.9.82 1.13.16.45.68 1.31 2.69.94 0 .67.01 1.3.01 1.49 0 .21-.15.45-.55.38A7.995 7.995 0 0 1 0 8c0-4.42 3.58-8 8-8Z"
+      />
+    </svg>
+  </div>,
+  "debug": [Function],
+  "findAllByAltText": [Function],
+  "findAllByDisplayValue": [Function],
+  "findAllByLabelText": [Function],
+  "findAllByPlaceholderText": [Function],
+  "findAllByRole": [Function],
+  "findAllByTestId": [Function],
+  "findAllByText": [Function],
+  "findAllByTitle": [Function],
+  "findByAltText": [Function],
+  "findByDisplayValue": [Function],
+  "findByLabelText": [Function],
+  "findByPlaceholderText": [Function],
+  "findByRole": [Function],
+  "findByTestId": [Function],
+  "findByText": [Function],
+  "findByTitle": [Function],
+  "getAllByAltText": [Function],
+  "getAllByDisplayValue": [Function],
+  "getAllByLabelText": [Function],
+  "getAllByPlaceholderText": [Function],
+  "getAllByRole": [Function],
+  "getAllByTestId": [Function],
+  "getAllByText": [Function],
+  "getAllByTitle": [Function],
+  "getByAltText": [Function],
+  "getByDisplayValue": [Function],
+  "getByLabelText": [Function],
+  "getByPlaceholderText": [Function],
+  "getByRole": [Function],
+  "getByTestId": [Function],
+  "getByText": [Function],
+  "getByTitle": [Function],
+  "queryAllByAltText": [Function],
+  "queryAllByDisplayValue": [Function],
+  "queryAllByLabelText": [Function],
+  "queryAllByPlaceholderText": [Function],
+  "queryAllByRole": [Function],
+  "queryAllByTestId": [Function],
+  "queryAllByText": [Function],
+  "queryAllByTitle": [Function],
+  "queryByAltText": [Function],
+  "queryByDisplayValue": [Function],
+  "queryByLabelText": [Function],
+  "queryByPlaceholderText": [Function],
+  "queryByRole": [Function],
+  "queryByTestId": [Function],
+  "queryByText": [Function],
+  "queryByTitle": [Function],
+  "rerender": [Function],
+  "unmount": [Function],
+}
+`;
+
+exports[`components/icons/AvatarIcon.tsx should render default medium icon when no url 1`] = `
+{
+  "asFragment": [Function],
+  "baseElement": <body>
+    <div>
+      <svg
+        aria-hidden="true"
+        class="text-gray-500 dark:text-gray-300"
+        fill="currentColor"
+        focusable="false"
+        height="24"
+        style="display: inline-block; user-select: none; vertical-align: text-bottom; overflow: visible;"
+        viewBox="0 0 24 24"
+        width="24"
+      >
+        <path
+          d="M12.5.75C6.146.75 1 5.896 1 12.25c0 5.089 3.292 9.387 7.863 10.91.575.101.79-.244.79-.546 0-.273-.014-1.178-.014-2.142-2.889.532-3.636-.704-3.866-1.35-.13-.331-.69-1.352-1.18-1.625-.402-.216-.977-.748-.014-.762.906-.014 1.553.834 1.769 1.179 1.035 1.74 2.688 1.25 3.349.948.1-.747.402-1.25.733-1.538-2.559-.287-5.232-1.279-5.232-5.678 0-1.25.445-2.285 1.178-3.09-.115-.288-.517-1.467.115-3.048 0 0 .963-.302 3.163 1.179.92-.259 1.897-.388 2.875-.388.977 0 1.955.13 2.875.388 2.2-1.495 3.162-1.179 3.162-1.179.633 1.581.23 2.76.115 3.048.733.805 1.179 1.825 1.179 3.09 0 4.413-2.688 5.39-5.247 5.678.417.36.776 1.05.776 2.128 0 1.538-.014 2.774-.014 3.162 0 .302.216.662.79.547C20.709 21.637 24 17.324 24 12.25 24 5.896 18.854.75 12.5.75Z"
+        />
+      </svg>
+    </div>
+  </body>,
+  "container": <div>
+    <svg
+      aria-hidden="true"
+      class="text-gray-500 dark:text-gray-300"
+      fill="currentColor"
+      focusable="false"
+      height="24"
+      style="display: inline-block; user-select: none; vertical-align: text-bottom; overflow: visible;"
+      viewBox="0 0 24 24"
+      width="24"
+    >
+      <path
+        d="M12.5.75C6.146.75 1 5.896 1 12.25c0 5.089 3.292 9.387 7.863 10.91.575.101.79-.244.79-.546 0-.273-.014-1.178-.014-2.142-2.889.532-3.636-.704-3.866-1.35-.13-.331-.69-1.352-1.18-1.625-.402-.216-.977-.748-.014-.762.906-.014 1.553.834 1.769 1.179 1.035 1.74 2.688 1.25 3.349.948.1-.747.402-1.25.733-1.538-2.559-.287-5.232-1.279-5.232-5.678 0-1.25.445-2.285 1.178-3.09-.115-.288-.517-1.467.115-3.048 0 0 .963-.302 3.163 1.179.92-.259 1.897-.388 2.875-.388.977 0 1.955.13 2.875.388 2.2-1.495 3.162-1.179 3.162-1.179.633 1.581.23 2.76.115 3.048.733.805 1.179 1.825 1.179 3.09 0 4.413-2.688 5.39-5.247 5.678.417.36.776 1.05.776 2.128 0 1.538-.014 2.774-.014 3.162 0 .302.216.662.79.547C20.709 21.637 24 17.324 24 12.25 24 5.896 18.854.75 12.5.75Z"
+      />
+    </svg>
+  </div>,
+  "debug": [Function],
+  "findAllByAltText": [Function],
+  "findAllByDisplayValue": [Function],
+  "findAllByLabelText": [Function],
+  "findAllByPlaceholderText": [Function],
+  "findAllByRole": [Function],
+  "findAllByTestId": [Function],
+  "findAllByText": [Function],
+  "findAllByTitle": [Function],
+  "findByAltText": [Function],
+  "findByDisplayValue": [Function],
+  "findByLabelText": [Function],
+  "findByPlaceholderText": [Function],
+  "findByRole": [Function],
+  "findByTestId": [Function],
+  "findByText": [Function],
+  "findByTitle": [Function],
+  "getAllByAltText": [Function],
+  "getAllByDisplayValue": [Function],
+  "getAllByLabelText": [Function],
+  "getAllByPlaceholderText": [Function],
+  "getAllByRole": [Function],
+  "getAllByTestId": [Function],
+  "getAllByText": [Function],
+  "getAllByTitle": [Function],
+  "getByAltText": [Function],
+  "getByDisplayValue": [Function],
+  "getByLabelText": [Function],
+  "getByPlaceholderText": [Function],
+  "getByRole": [Function],
+  "getByTestId": [Function],
+  "getByText": [Function],
+  "getByTitle": [Function],
+  "queryAllByAltText": [Function],
+  "queryAllByDisplayValue": [Function],
+  "queryAllByLabelText": [Function],
+  "queryAllByPlaceholderText": [Function],
+  "queryAllByRole": [Function],
+  "queryAllByTestId": [Function],
+  "queryAllByText": [Function],
+  "queryAllByTitle": [Function],
+  "queryByAltText": [Function],
+  "queryByDisplayValue": [Function],
+  "queryByLabelText": [Function],
+  "queryByPlaceholderText": [Function],
+  "queryByRole": [Function],
+  "queryByTestId": [Function],
+  "queryByText": [Function],
+  "queryByTitle": [Function],
+  "rerender": [Function],
+  "unmount": [Function],
+}
+`;
+
+exports[`components/icons/AvatarIcon.tsx should render default small icon when no url 1`] = `
+{
+  "asFragment": [Function],
+  "baseElement": <body>
+    <div>
+      <svg
+        aria-hidden="true"
+        class="text-gray-500 dark:text-gray-300"
+        fill="currentColor"
+        focusable="false"
+        height="20"
+        style="display: inline-block; user-select: none; vertical-align: text-bottom; overflow: visible;"
+        viewBox="0 0 16 16"
+        width="20"
+      >
+        <path
+          d="M8 0c4.42 0 8 3.58 8 8a8.013 8.013 0 0 1-5.45 7.59c-.4.08-.55-.17-.55-.38 0-.27.01-1.13.01-2.2 0-.75-.25-1.23-.54-1.48 1.78-.2 3.65-.88 3.65-3.95 0-.88-.31-1.59-.82-2.15.08-.2.36-1.02-.08-2.12 0 0-.67-.22-2.2.82-.64-.18-1.32-.27-2-.27-.68 0-1.36.09-2 .27-1.53-1.03-2.2-.82-2.2-.82-.44 1.1-.16 1.92-.08 2.12-.51.56-.82 1.28-.82 2.15 0 3.06 1.86 3.75 3.64 3.95-.23.2-.44.55-.51 1.07-.46.21-1.61.55-2.33-.66-.15-.24-.6-.83-1.23-.82-.67.01-.27.38.01.53.34.19.73.9.82 1.13.16.45.68 1.31 2.69.94 0 .67.01 1.3.01 1.49 0 .21-.15.45-.55.38A7.995 7.995 0 0 1 0 8c0-4.42 3.58-8 8-8Z"
+        />
+      </svg>
+    </div>
+  </body>,
+  "container": <div>
+    <svg
+      aria-hidden="true"
+      class="text-gray-500 dark:text-gray-300"
+      fill="currentColor"
+      focusable="false"
+      height="20"
+      style="display: inline-block; user-select: none; vertical-align: text-bottom; overflow: visible;"
+      viewBox="0 0 16 16"
+      width="20"
     >
       <path
         d="M8 0c4.42 0 8 3.58 8 8a8.013 8.013 0 0 1-5.45 7.59c-.4.08-.55-.17-.55-.38 0-.27.01-1.13.01-2.2 0-.75-.25-1.23-.54-1.48 1.78-.2 3.65-.88 3.65-3.95 0-.88-.31-1.59-.82-2.15.08-.2.36-1.02-.08-2.12 0 0-.67-.22-2.2.82-.64-.18-1.32-.27-2-.27-.68 0-1.36.09-2 .27-1.53-1.03-2.2-.82-2.2-.82-.44 1.1-.16 1.92-.08 2.12-.51.56-.82 1.28-.82 2.15 0 3.06 1.86 3.75 3.64 3.95-.23.2-.44.55-.51 1.07-.46.21-1.61.55-2.33-.66-.15-.24-.6-.83-1.23-.82-.67.01-.27.38.01.53.34.19.73.9.82 1.13.16.45.68 1.31 2.69.94 0 .67.01 1.3.01 1.49 0 .21-.15.45-.55.38A7.995 7.995 0 0 1 0 8c0-4.42 3.58-8 8-8Z"
diff --git a/src/components/settings/NotificationSettings.test.tsx b/src/components/settings/NotificationSettings.test.tsx
index 6cfbd1f8..7b740125 100644
--- a/src/components/settings/NotificationSettings.test.tsx
+++ b/src/components/settings/NotificationSettings.test.tsx
@@ -123,6 +123,34 @@ describe('routes/components/settings/NotificationSettings.tsx', () => {
     expect(updateSetting).toHaveBeenCalledWith('markAsDoneOnOpen', false);
   });
 
+  it('should toggle the markAsDoneOnUnsubscribe checkbox', async () => {
+    await act(async () => {
+      render(
+        <AppContext.Provider
+          value={{
+            auth: mockAuth,
+            settings: mockSettings,
+            updateSetting,
+          }}
+        >
+          <MemoryRouter>
+            <NotificationSettings />
+          </MemoryRouter>
+        </AppContext.Provider>,
+      );
+    });
+
+    fireEvent.click(screen.getByLabelText('Mark as done on unsubscribe'), {
+      target: { checked: true },
+    });
+
+    expect(updateSetting).toHaveBeenCalledTimes(1);
+    expect(updateSetting).toHaveBeenCalledWith(
+      'markAsDoneOnUnsubscribe',
+      false,
+    );
+  });
+
   it('should toggle the delayNotificationState checkbox', async () => {
     await act(async () => {
       render(
diff --git a/src/context/App.test.tsx b/src/context/App.test.tsx
index 3c7fe43b..52b982e7 100644
--- a/src/context/App.test.tsx
+++ b/src/context/App.test.tsx
@@ -6,7 +6,7 @@ import type { AuthState, Hostname, SettingsState, Token } from '../types';
 import { mockSingleNotification } from '../utils/api/__mocks__/response-mocks';
 import * as apiRequests from '../utils/api/request';
 import * as comms from '../utils/comms';
-import Constants from '../utils/constants';
+import { Constants } from '../utils/constants';
 import * as notifications from '../utils/notifications';
 import * as storage from '../utils/storage';
 import { AppContext, AppProvider, defaultSettings } from './App';
@@ -354,6 +354,7 @@ describe('context/App.tsx', () => {
           theme: 'SYSTEM',
           detailedNotifications: true,
           markAsDoneOnOpen: false,
+          markAsDoneOnUnsubscribe: false,
           showAccountHeader: false,
           delayNotificationState: false,
           showPills: true,
@@ -411,6 +412,7 @@ describe('context/App.tsx', () => {
           theme: 'SYSTEM',
           detailedNotifications: true,
           markAsDoneOnOpen: false,
+          markAsDoneOnUnsubscribe: false,
           showAccountHeader: false,
           delayNotificationState: false,
           showPills: true,
diff --git a/src/hooks/useNotifications.test.ts b/src/hooks/useNotifications.test.ts
index 1b014a12..d825cebe 100644
--- a/src/hooks/useNotifications.test.ts
+++ b/src/hooks/useNotifications.test.ts
@@ -13,7 +13,7 @@ import {
   mockNotificationUser,
   mockSingleNotification,
 } from '../utils/api/__mocks__/response-mocks';
-import { Errors } from '../utils/constants';
+import { Errors } from '../utils/errors';
 import { useNotifications } from './useNotifications';
 
 describe('hooks/useNotifications.ts', () => {
@@ -426,7 +426,7 @@ describe('hooks/useNotifications.ts', () => {
   describe('unsubscribeNotification', () => {
     const id = 'notification-123';
 
-    it('should unsubscribe from a notification with success', async () => {
+    it('should unsubscribe from a notification with success - markAsDoneOnUnsubscribe = false', async () => {
       // The unsubscribe endpoint call.
       nock('https://api.github.com/')
         .put(`/notifications/threads/${id}/subscription`)
@@ -453,6 +453,39 @@ describe('hooks/useNotifications.ts', () => {
       expect(result.current.notifications.length).toBe(0);
     });
 
+    it('should unsubscribe from a notification with success - markAsDoneOnUnsubscribe = true', async () => {
+      // The unsubscribe endpoint call.
+      nock('https://api.github.com/')
+        .put(`/notifications/threads/${id}/subscription`)
+        .reply(200);
+
+      // The mark done endpoint call.
+      nock('https://api.github.com/')
+        .delete(`/notifications/threads/${id}`)
+        .reply(200);
+
+      const { result } = renderHook(() => useNotifications());
+
+      act(() => {
+        result.current.unsubscribeNotification(
+          {
+            ...mockState,
+            settings: {
+              ...mockState.settings,
+              markAsDoneOnUnsubscribe: true,
+            },
+          },
+          mockSingleNotification,
+        );
+      });
+
+      await waitFor(() => {
+        expect(result.current.status).toBe('success');
+      });
+
+      expect(result.current.notifications.length).toBe(0);
+    });
+
     it('should unsubscribe from a notification with failure', async () => {
       // The unsubscribe endpoint call.
       nock('https://api.github.com/')
diff --git a/src/routes/Notifications.test.tsx b/src/routes/Notifications.test.tsx
index f883bcc4..f61e78f2 100644
--- a/src/routes/Notifications.test.tsx
+++ b/src/routes/Notifications.test.tsx
@@ -2,7 +2,7 @@ import { render } from '@testing-library/react';
 import { mockAccountNotifications } from '../__mocks__/notifications-mocks';
 import { mockSettings } from '../__mocks__/state-mocks';
 import { AppContext } from '../context/App';
-import { Errors } from '../utils/constants';
+import { Errors } from '../utils/errors';
 import { NotificationsRoute } from './Notifications';
 
 jest.mock('../components/AccountNotifications', () => ({
diff --git a/src/routes/__snapshots__/Settings.test.tsx.snap b/src/routes/__snapshots__/Settings.test.tsx.snap
index 74818bec..90effae0 100644
--- a/src/routes/__snapshots__/Settings.test.tsx.snap
+++ b/src/routes/__snapshots__/Settings.test.tsx.snap
@@ -513,6 +513,49 @@ exports[`routes/Settings.tsx should render itself & its children 1`] = `
           </div>
         </div>
       </div>
+      <div
+        class="mt-3 mb-2 text-sm"
+      >
+        <div
+          class="flex items-center"
+        >
+          <input
+            class="size-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500 cursor-pointer"
+            id="markAsDoneOnUnsubscribe"
+            type="checkbox"
+          />
+          <div
+            class="ml-3"
+          >
+            <label
+              class="font-medium text-gray-700 dark:text-gray-200 cursor-pointer"
+              for="markAsDoneOnUnsubscribe"
+            >
+              Mark as done on unsubscribe
+              <span
+                aria-label="tooltip-markAsDoneOnUnsubscribe"
+                class="relative"
+                id="tooltip-markAsDoneOnUnsubscribe"
+              >
+                <svg
+                  aria-hidden="true"
+                  class="ml-1 text-blue-500"
+                  fill="currentColor"
+                  focusable="false"
+                  height="16"
+                  style="display: inline-block; user-select: none; vertical-align: text-bottom; overflow: visible;"
+                  viewBox="0 0 16 16"
+                  width="16"
+                >
+                  <path
+                    d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.92 6.085h.001a.749.749 0 1 1-1.342-.67c.169-.339.436-.701.849-.977C6.845 4.16 7.369 4 8 4a2.756 2.756 0 0 1 1.637.525c.503.377.863.965.863 1.725 0 .448-.115.83-.329 1.15-.205.307-.47.513-.692.662-.109.072-.22.138-.313.195l-.006.004a6.24 6.24 0 0 0-.26.16.952.952 0 0 0-.276.245.75.75 0 0 1-1.248-.832c.184-.264.42-.489.692-.661.103-.067.207-.132.313-.195l.007-.004c.1-.061.182-.11.258-.161a.969.969 0 0 0 .277-.245C8.96 6.514 9 6.427 9 6.25a.612.612 0 0 0-.262-.525A1.27 1.27 0 0 0 8 5.5c-.369 0-.595.09-.74.187a1.01 1.01 0 0 0-.34.398ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"
+                  />
+                </svg>
+              </span>
+            </label>
+          </div>
+        </div>
+      </div>
       <div
         class="mt-3 mb-2 text-sm"
       >
diff --git a/src/utils/api/errors.test.ts b/src/utils/api/errors.test.ts
index 792d31b1..8764a460 100644
--- a/src/utils/api/errors.test.ts
+++ b/src/utils/api/errors.test.ts
@@ -1,7 +1,7 @@
 import { AxiosError, type AxiosResponse } from 'axios';
 import type { Link } from '../../types';
 import type { GitHubRESTError } from '../../typesGitHub';
-import { Errors } from '../constants';
+import { Errors } from '../errors';
 import { determineFailureType } from './errors';
 
 describe('utils/api/errors.ts', () => {
diff --git a/src/utils/auth/migration.test.ts b/src/utils/auth/migration.test.ts
index b6669d37..2ab67b75 100644
--- a/src/utils/auth/migration.test.ts
+++ b/src/utils/auth/migration.test.ts
@@ -3,7 +3,7 @@ import log from 'electron-log';
 import nock from 'nock';
 import { mockGitifyUser, mockToken } from '../../__mocks__/state-mocks';
 import type { AuthState, Hostname } from '../../types';
-import Constants from '../constants';
+import { Constants } from '../constants';
 import {
   convertAccounts,
   hasAccountsToMigrate,
diff --git a/src/utils/links.test.ts b/src/utils/links.test.ts
index d4c4f656..1ea209ea 100644
--- a/src/utils/links.test.ts
+++ b/src/utils/links.test.ts
@@ -5,7 +5,7 @@ import type { Repository } from '../typesGitHub';
 import { mockSingleNotification } from './api/__mocks__/response-mocks';
 import * as authUtils from './auth/utils';
 import * as comms from './comms';
-import Constants from './constants';
+import { Constants } from './constants';
 import * as helpers from './helpers';
 import {
   openAccountProfile,
diff --git a/src/utils/storage.test.ts b/src/utils/storage.test.ts
index 41794ce8..46d59c6c 100644
--- a/src/utils/storage.test.ts
+++ b/src/utils/storage.test.ts
@@ -1,6 +1,6 @@
 import { mockSettings } from '../__mocks__/state-mocks';
 import type { Token } from '../types';
-import Constants from './constants';
+import { Constants } from './constants';
 import { clearState, loadState, saveState } from './storage';
 
 describe('utils/storage.ts', () => {