Repository: gitify-app/gitify (ts)

Issue:
Stop re-ordering notifications when I mark one as read
I find it disorienting that notifications rearrange when I am marking some of them as 'read'. Since they group by repo, if I mark one the newest notification from repo B as read, this may cause repo A to jump ahead of repo B in the list. 

This can cause mis-clicks and certainly makes me feel like the rug has been yanked out from underneath me.

I would suggest only re-ordering when new results come from the server and/or when the window is closed or blurred and subsequently opened/focused.

Pull request description:
feat: delay notification removal
Add new **Notification Settings** which allows users to "delay" notifications from being removed from Gitify upon user interactions (marked as done, marked as read, open).

When enabled, notification rows will be set temporarily as opaque until they're removal upon next refresh (scheduled or user initiated).

Defaulted to disabled.

**Demo**
https://github.com/gitify-app/gitify/assets/386277/812a2f1c-0a15-406d-91c1-00210b432387

Closes #319

Patch:
diff --git a/main.js b/main.js
index a9e12ffa..1cb8e2cf 100644
--- a/main.js
+++ b/main.js
@@ -60,12 +60,8 @@ menubarApp.on('ready', () => {
     }
   });
 
-  ipcMain.handle('get-platform', async () => {
-    return process.platform;
-  });
-  ipcMain.handle('get-app-version', async () => {
-    return app.getVersion();
-  });
+  ipcMain.handle('get-platform', () => process.platform);
+  ipcMain.handle('get-app-version', () => app.getVersion());
 
   ipcMain.on('reopen-window', () => menubarApp.showWindow());
   ipcMain.on('hide-window', () => menubarApp.hideWindow());
diff --git a/src/__mocks__/mock-state.ts b/src/__mocks__/mock-state.ts
index 01cb113a..6594be1d 100644
--- a/src/__mocks__/mock-state.ts
+++ b/src/__mocks__/mock-state.ts
@@ -18,4 +18,5 @@ export const mockSettings: SettingsState = {
   detailedNotifications: false,
   markAsDoneOnOpen: false,
   showAccountHostname: false,
+  delayNotificationState: false,
 };
diff --git a/src/__mocks__/mockedData.ts b/src/__mocks__/mockedData.ts
index 9bf11ee3..f035be23 100644
--- a/src/__mocks__/mockedData.ts
+++ b/src/__mocks__/mockedData.ts
@@ -404,44 +404,6 @@ const mockDiscussionReplier: DiscussionAuthor = {
 
 export const mockDiscussionComments: DiscussionComments = {
   nodes: [
-    {
-      databaseId: 2215656,
-      createdAt: '2022-02-20T18:33:39Z',
-      author: mockDiscussionAuthor,
-      replies: {
-        nodes: [],
-      },
-    },
-    {
-      databaseId: 2217789,
-      createdAt: '2022-02-21T03:30:42Z',
-      author: mockDiscussionAuthor,
-      replies: {
-        nodes: [],
-      },
-    },
-    {
-      databaseId: 2223243,
-      createdAt: '2022-02-21T18:26:27Z',
-      author: mockDiscussionAuthor,
-      replies: {
-        nodes: [
-          {
-            databaseId: 2232922,
-            createdAt: '2022-02-23T00:57:58Z',
-            author: mockDiscussionReplier,
-          },
-        ],
-      },
-    },
-    {
-      databaseId: 2232921,
-      createdAt: '2022-02-23T00:57:49Z',
-      author: mockDiscussionAuthor,
-      replies: {
-        nodes: [],
-      },
-    },
     {
       databaseId: 2258799,
       createdAt: '2022-02-27T01:22:20Z',
@@ -456,35 +418,6 @@ export const mockDiscussionComments: DiscussionComments = {
         ],
       },
     },
-    {
-      databaseId: 2297637,
-      createdAt: '2022-03-04T20:39:44Z',
-      author: mockDiscussionAuthor,
-
-      replies: {
-        nodes: [
-          {
-            databaseId: 2300893,
-            createdAt: '2022-03-05T17:41:04Z',
-            author: mockDiscussionReplier,
-          },
-        ],
-      },
-    },
-    {
-      databaseId: 2299763,
-      createdAt: '2022-03-05T11:05:42Z',
-      author: mockDiscussionAuthor,
-      replies: {
-        nodes: [
-          {
-            databaseId: 2300895,
-            createdAt: '2022-03-05T17:41:44Z',
-            author: mockDiscussionReplier,
-          },
-        ],
-      },
-    },
   ],
 };
 
diff --git a/src/components/NotificationRow.tsx b/src/components/NotificationRow.tsx
index 75eaa399..bae5af4b 100644
--- a/src/components/NotificationRow.tsx
+++ b/src/components/NotificationRow.tsx
@@ -47,7 +47,7 @@ export const NotificationRow: FC<IProps> = ({ notification, hostname }) => {
       markNotificationDone(notification.id, hostname);
     } else {
       // no need to mark as read, github does it by default when opening it
-      removeNotificationFromState(notification.id, hostname);
+      removeNotificationFromState(settings, notification.id, hostname);
     }
   }, [notifications, notification, accounts, settings]); // notifications required here to prevent weird state issues
 
@@ -84,7 +84,10 @@ export const NotificationRow: FC<IProps> = ({ notification, hostname }) => {
   ]);
 
   return (
-    <div className="flex space-x-3 py-2 px-3 bg-white dark:bg-gray-dark dark:text-white hover:bg-gray-100 dark:hover:bg-gray-darker border-b border-gray-100 dark:border-gray-darker group">
+    <div
+      id={notification.id}
+      className="flex space-x-3 py-2 px-3 bg-white dark:bg-gray-dark dark:text-white hover:bg-gray-100 dark:hover:bg-gray-darker border-b border-gray-100 dark:border-gray-darker group"
+    >
       <div
         className={`flex justify-center items-center w-5 ${iconColor}`}
         title={notificationTitle}
diff --git a/src/context/App.tsx b/src/context/App.tsx
index c240cc85..45eacda1 100644
--- a/src/context/App.tsx
+++ b/src/context/App.tsx
@@ -44,6 +44,7 @@ export const defaultSettings: SettingsState = {
   detailedNotifications: false,
   markAsDoneOnOpen: false,
   showAccountHostname: false,
+  delayNotificationState: false,
 };
 
 interface AppContextState {
@@ -57,7 +58,11 @@ interface AppContextState {
   notifications: AccountNotifications[];
   status: Status;
   errorDetails: GitifyError;
-  removeNotificationFromState: (id: string, hostname: string) => void;
+  removeNotificationFromState: (
+    settings: SettingsState,
+    id: string,
+    hostname: string,
+  ) => void;
   fetchNotifications: () => Promise<void>;
   markNotificationRead: (id: string, hostname: string) => Promise<void>;
   markNotificationDone: (id: string, hostname: string) => Promise<void>;
@@ -199,31 +204,31 @@ export const AppProvider = ({ children }: { children: ReactNode }) => {
 
   const markNotificationReadWithAccounts = useCallback(
     async (id: string, hostname: string) =>
-      await markNotificationRead(accounts, id, hostname),
+      await markNotificationRead(accounts, settings, id, hostname),
     [accounts, notifications],
   );
 
   const markNotificationDoneWithAccounts = useCallback(
     async (id: string, hostname: string) =>
-      await markNotificationDone(accounts, id, hostname),
+      await markNotificationDone(accounts, settings, id, hostname),
     [accounts, notifications],
   );
 
   const unsubscribeNotificationWithAccounts = useCallback(
     async (id: string, hostname: string) =>
-      await unsubscribeNotification(accounts, id, hostname),
+      await unsubscribeNotification(accounts, settings, id, hostname),
     [accounts, notifications],
   );
 
   const markRepoNotificationsWithAccounts = useCallback(
     async (repoSlug: string, hostname: string) =>
-      await markRepoNotifications(accounts, repoSlug, hostname),
+      await markRepoNotifications(accounts, settings, repoSlug, hostname),
     [accounts, notifications],
   );
 
   const markRepoNotificationsDoneWithAccounts = useCallback(
     async (repoSlug: string, hostname: string) =>
-      await markRepoNotificationsDone(accounts, repoSlug, hostname),
+      await markRepoNotificationsDone(accounts, settings, repoSlug, hostname),
     [accounts, notifications],
   );
 
diff --git a/src/hooks/useNotifications.ts b/src/hooks/useNotifications.ts
index 9558e444..13824685 100644
--- a/src/hooks/useNotifications.ts
+++ b/src/hooks/useNotifications.ts
@@ -25,33 +25,42 @@ import { removeNotifications } from '../utils/remove-notifications';
 
 interface NotificationsState {
   notifications: AccountNotifications[];
-  removeNotificationFromState: (id: string, hostname: string) => void;
+  removeNotificationFromState: (
+    settings: SettingsState,
+    id: string,
+    hostname: string,
+  ) => void;
   fetchNotifications: (
     accounts: AuthState,
     settings: SettingsState,
   ) => Promise<void>;
   markNotificationRead: (
     accounts: AuthState,
+    settings: SettingsState,
     id: string,
     hostname: string,
   ) => Promise<void>;
   markNotificationDone: (
     accounts: AuthState,
+    settings: SettingsState,
     id: string,
     hostname: string,
   ) => Promise<void>;
   unsubscribeNotification: (
     accounts: AuthState,
+    settings: SettingsState,
     id: string,
     hostname: string,
   ) => Promise<void>;
   markRepoNotifications: (
     accounts: AuthState,
+    settings: SettingsState,
     repoSlug: string,
     hostname: string,
   ) => Promise<void>;
   markRepoNotificationsDone: (
     accounts: AuthState,
+    settings: SettingsState,
     repoSlug: string,
     hostname: string,
   ) => Promise<void>;
@@ -94,7 +103,12 @@ export const useNotifications = (): NotificationsState => {
   );
 
   const markNotificationRead = useCallback(
-    async (accounts: AuthState, id: string, hostname: string) => {
+    async (
+      accounts: AuthState,
+      settings: SettingsState,
+      id: string,
+      hostname: string,
+    ) => {
       setStatus('loading');
 
       const token = getTokenForHost(hostname, accounts);
@@ -103,6 +117,7 @@ export const useNotifications = (): NotificationsState => {
         await markNotificationThreadAsRead(id, hostname, token);
 
         const updatedNotifications = removeNotification(
+          settings,
           id,
           notifications,
           hostname,
@@ -119,7 +134,12 @@ export const useNotifications = (): NotificationsState => {
   );
 
   const markNotificationDone = useCallback(
-    async (accounts: AuthState, id: string, hostname: string) => {
+    async (
+      accounts: AuthState,
+      settings: SettingsState,
+      id: string,
+      hostname: string,
+    ) => {
       setStatus('loading');
 
       const token = getTokenForHost(hostname, accounts);
@@ -128,6 +148,7 @@ export const useNotifications = (): NotificationsState => {
         await markNotificationThreadAsDone(id, hostname, token);
 
         const updatedNotifications = removeNotification(
+          settings,
           id,
           notifications,
           hostname,
@@ -144,14 +165,19 @@ export const useNotifications = (): NotificationsState => {
   );
 
   const unsubscribeNotification = useCallback(
-    async (accounts: AuthState, id: string, hostname: string) => {
+    async (
+      accounts: AuthState,
+      settings: SettingsState,
+      id: string,
+      hostname: string,
+    ) => {
       setStatus('loading');
 
       const token = getTokenForHost(hostname, accounts);
 
       try {
         await ignoreNotificationThreadSubscription(id, hostname, token);
-        await markNotificationRead(accounts, id, hostname);
+        await markNotificationRead(accounts, settings, id, hostname);
         setStatus('success');
       } catch (err) {
         setStatus('success');
@@ -161,7 +187,12 @@ export const useNotifications = (): NotificationsState => {
   );
 
   const markRepoNotifications = useCallback(
-    async (accounts: AuthState, repoSlug: string, hostname: string) => {
+    async (
+      accounts: AuthState,
+      settings: SettingsState,
+      repoSlug: string,
+      hostname: string,
+    ) => {
       setStatus('loading');
 
       const token = getTokenForHost(hostname, accounts);
@@ -185,7 +216,12 @@ export const useNotifications = (): NotificationsState => {
   );
 
   const markRepoNotificationsDone = useCallback(
-    async (accounts: AuthState, repoSlug: string, hostname: string) => {
+    async (
+      accounts: AuthState,
+      settings: SettingsState,
+      repoSlug: string,
+      hostname: string,
+    ) => {
       setStatus('loading');
 
       try {
@@ -204,6 +240,7 @@ export const useNotifications = (): NotificationsState => {
             notificationsToRemove.map((notification) =>
               markNotificationDone(
                 accounts,
+                settings,
                 notification.id,
                 notifications[accountIndex].hostname,
               ),
@@ -228,8 +265,9 @@ export const useNotifications = (): NotificationsState => {
   );
 
   const removeNotificationFromState = useCallback(
-    (id: string, hostname: string) => {
+    (settings: SettingsState, id: string, hostname: string) => {
       const updatedNotifications = removeNotification(
+        settings,
         id,
         notifications,
         hostname,
diff --git a/src/routes/Settings.tsx b/src/routes/Settings.tsx
index 1145bc9a..9709b136 100644
--- a/src/routes/Settings.tsx
+++ b/src/routes/Settings.tsx
@@ -236,6 +236,23 @@ export const SettingsRoute: FC = () => {
               updateSetting('markAsDoneOnOpen', evt.target.checked)
             }
           />
+          <Checkbox
+            name="delayNotificationState"
+            label="Delay notification state"
+            checked={settings.delayNotificationState}
+            onChange={(evt) =>
+              updateSetting('delayNotificationState', evt.target.checked)
+            }
+            tooltip={
+              <div>
+                <div className="pb-3">
+                  Keep the notification within Gitify window upon interaction
+                  (click, mark as read, mark as done, etc) until the next
+                  refresh window (scheduled or user initiated)
+                </div>
+              </div>
+            }
+          />
         </fieldset>
 
         <fieldset className="mb-3">
diff --git a/src/types.ts b/src/types.ts
index f361b473..b4f663cd 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -25,6 +25,7 @@ interface NotificationSettingsState {
   showNotifications: boolean;
   showBots: boolean;
   markAsDoneOnOpen: boolean;
+  delayNotificationState: boolean;
 }
 
 interface SystemSettingsState {
diff --git a/src/utils/api/client.ts b/src/utils/api/client.ts
index da356f36..3401851d 100644
--- a/src/utils/api/client.ts
+++ b/src/utils/api/client.ts
@@ -15,13 +15,12 @@ import type {
   RootHypermediaLinks,
   UserDetails,
 } from '../../typesGitHub';
-import { getGitHubAPIBaseUrl } from '../helpers';
 import { apiRequestAuth } from './request';
 
 import { print } from 'graphql/language/printer';
 import Constants from '../constants';
 import { QUERY_SEARCH_DISCUSSIONS } from './graphql/discussions';
-import { formatSearchQueryString } from './utils';
+import { formatSearchQueryString, getGitHubAPIBaseUrl } from './utils';
 
 /**
  * Get Hypermedia links to resources accessible in GitHub's REST API
@@ -32,8 +31,7 @@ export function getRootHypermediaLinks(
   hostname: string,
   token: string,
 ): AxiosPromise<RootHypermediaLinks> {
-  const baseUrl = getGitHubAPIBaseUrl(hostname);
-  const url = new URL(baseUrl);
+  const url = getGitHubAPIBaseUrl(hostname);
   return apiRequestAuth(url.toString(), 'GET', token);
 }
 
@@ -46,8 +44,9 @@ export function getAuthenticatedUser(
   hostname: string,
   token: string,
 ): AxiosPromise<UserDetails> {
-  const baseUrl = getGitHubAPIBaseUrl(hostname);
-  const url = new URL(`${baseUrl}/user`);
+  const url = getGitHubAPIBaseUrl(hostname);
+  url.pathname += 'user';
+
   return apiRequestAuth(url.toString(), 'GET', token);
 }
 
@@ -56,8 +55,9 @@ export function headNotifications(
   hostname: string,
   token: string,
 ): AxiosPromise<void> {
-  const baseUrl = getGitHubAPIBaseUrl(hostname);
-  const url = new URL(`${baseUrl}/notifications`);
+  const url = getGitHubAPIBaseUrl(hostname);
+  url.pathname += 'notifications';
+
   return apiRequestAuth(url.toString(), 'HEAD', token);
 }
 
@@ -71,8 +71,8 @@ export function listNotificationsForAuthenticatedUser(
   token: string,
   settings: SettingsState,
 ): AxiosPromise<Notification[]> {
-  const baseUrl = getGitHubAPIBaseUrl(hostname);
-  const url = new URL(`${baseUrl}/notifications`);
+  const url = getGitHubAPIBaseUrl(hostname);
+  url.pathname += 'notifications';
   url.searchParams.append('participating', String(settings.participating));
 
   return apiRequestAuth(url.toString(), 'GET', token);
@@ -89,8 +89,9 @@ export function markNotificationThreadAsRead(
   hostname: string,
   token: string,
 ): AxiosPromise<void> {
-  const baseUrl = getGitHubAPIBaseUrl(hostname);
-  const url = new URL(`${baseUrl}/notifications/threads/${threadId}`);
+  const url = getGitHubAPIBaseUrl(hostname);
+  url.pathname += `notifications/threads/${threadId}`;
+
   return apiRequestAuth(url.toString(), 'PATCH', token, {});
 }
 
@@ -105,8 +106,9 @@ export function markNotificationThreadAsDone(
   hostname: string,
   token: string,
 ): AxiosPromise<void> {
-  const baseUrl = getGitHubAPIBaseUrl(hostname);
-  const url = new URL(`${baseUrl}/notifications/threads/${threadId}`);
+  const url = getGitHubAPIBaseUrl(hostname);
+  url.pathname += `notifications/threads/${threadId}`;
+
   return apiRequestAuth(url.toString(), 'DELETE', token, {});
 }
 
@@ -120,10 +122,9 @@ export function ignoreNotificationThreadSubscription(
   hostname: string,
   token: string,
 ): AxiosPromise<NotificationThreadSubscription> {
-  const baseUrl = getGitHubAPIBaseUrl(hostname);
-  const url = new URL(
-    `${baseUrl}/notifications/threads/${threadId}/subscription`,
-  );
+  const url = getGitHubAPIBaseUrl(hostname);
+  url.pathname += `notifications/threads/${threadId}/subscription`;
+
   return apiRequestAuth(url.toString(), 'PUT', token, { ignored: true });
 }
 
@@ -140,8 +141,9 @@ export function markRepositoryNotificationsAsRead(
   hostname: string,
   token: string,
 ): AxiosPromise<void> {
-  const baseUrl = getGitHubAPIBaseUrl(hostname);
-  const url = new URL(`${baseUrl}/repos/${repoSlug}/notifications`);
+  const url = getGitHubAPIBaseUrl(hostname);
+  url.pathname += `repos/${repoSlug}/notifications`;
+
   return apiRequestAuth(url.toString(), 'PUT', token, {});
 }
 
@@ -253,7 +255,7 @@ export async function searchDiscussions(
         notification.updated_at,
       ),
       firstDiscussions: 10,
-      lastComments: 100,
+      lastComments: 1,
       lastReplies: 1,
     },
   });
diff --git a/src/utils/api/utils.ts b/src/utils/api/utils.ts
index 6a833c9f..954704b0 100644
--- a/src/utils/api/utils.ts
+++ b/src/utils/api/utils.ts
@@ -1,3 +1,16 @@
+import Constants from '../constants';
+import { isEnterpriseHost } from '../helpers';
+
+export function getGitHubAPIBaseUrl(hostname: string): URL {
+  const url = new URL(Constants.GITHUB_API_BASE_URL);
+
+  if (isEnterpriseHost(hostname)) {
+    url.hostname = hostname;
+    url.pathname = '/api/v3/';
+  }
+  return url;
+}
+
 export function formatSearchQueryString(
   repo: string,
   title: string,
diff --git a/src/utils/constants.ts b/src/utils/constants.ts
index e41c0775..b8aea32f 100644
--- a/src/utils/constants.ts
+++ b/src/utils/constants.ts
@@ -21,6 +21,8 @@ export const Constants = {
   ALLREAD_EMOJIS: ['😉', '🎉', '🐯', '🙈', '🎈', '🎊', '👏', '🎪', '🍝'],
 
   FETCH_INTERVAL: 60000,
+
+  READ_CLASS_NAME: 'opacity-50 dark:opacity-50',
 };
 
 export const Errors: Record<ErrorType, GitifyError> = {
diff --git a/src/utils/helpers.ts b/src/utils/helpers.ts
index 575e6645..b6501bd6 100644
--- a/src/utils/helpers.ts
+++ b/src/utils/helpers.ts
@@ -28,28 +28,6 @@ export function isEnterpriseHost(hostname: string): boolean {
   return !hostname.endsWith(Constants.DEFAULT_AUTH_OPTIONS.hostname);
 }
 
-export function getGitHubAPIBaseUrl(hostname) {
-  const isEnterprise = isEnterpriseHost(hostname);
-  return isEnterprise
-    ? `https://${hostname}/api/v3`
-    : Constants.GITHUB_API_BASE_URL;
-}
-
-export function addNotificationReferrerIdToUrl(
-  url: string,
-  notificationId: string,
-  userId: number,
-): string {
-  const parsedUrl = new URL(url);
-
-  parsedUrl.searchParams.set(
-    'notification_referrer_id',
-    generateNotificationReferrerId(notificationId, userId),
-  );
-
-  return parsedUrl.href;
-}
-
 export function generateNotificationReferrerId(
   notificationId: string,
   userId: number,
@@ -60,8 +38,7 @@ export function generateNotificationReferrerId(
   return buffer.toString('base64');
 }
 
-export function getCheckSuiteUrl(notification: Notification) {
-  let url = `${notification.repository.html_url}/actions`;
+export function getCheckSuiteUrl(notification: Notification): string {
   const filters = [];
 
   const checkSuiteAttributes = getCheckSuiteAttributes(notification);
@@ -80,15 +57,10 @@ export function getCheckSuiteUrl(notification: Notification) {
     filters.push(`branch:${checkSuiteAttributes.branchName}`);
   }
 
-  if (filters.length > 0) {
-    url += `?query=${filters.join('+')}`;
-  }
-
-  return url;
+  return actionsURL(notification.repository.html_url, filters);
 }
 
-export function getWorkflowRunUrl(notification: Notification) {
-  let url = `${notification.repository.html_url}/actions`;
+export function getWorkflowRunUrl(notification: Notification): string {
   const filters = [];
 
   const workflowRunAttributes = getWorkflowRunAttributes(notification);
@@ -97,34 +69,46 @@ export function getWorkflowRunUrl(notification: Notification) {
     filters.push(`is:${workflowRunAttributes.status}`);
   }
 
+  return actionsURL(notification.repository.html_url, filters);
+}
+
+/**
+ * Construct a GitHub Actions URL for a repository with optional filters.
+ */
+export function actionsURL(repositoryURL: string, filters: string[]): string {
+  const url = new URL(repositoryURL);
+  url.pathname += '/actions';
+
   if (filters.length > 0) {
-    url += `?query=${filters.join('+')}`;
+    url.searchParams.append('query', filters.join('+'));
   }
 
-  return url;
+  // Note: the GitHub Actions UI cannot handle encoded '+' characters.
+  return url.toString().replace(/%2B/g, '+');
 }
 
 async function getDiscussionUrl(
   notification: Notification,
   token: string,
 ): Promise<string> {
-  let url = `${notification.repository.html_url}/discussions`;
+  const url = new URL(notification.repository.html_url);
+  url.pathname += '/discussions';
 
   const discussion = await fetchDiscussion(notification, token);
 
   if (discussion) {
-    url = discussion.url;
+    url.href = discussion.url;
 
     const comments = discussion.comments.nodes;
 
-    const latestCommentId = getLatestDiscussionComment(comments)?.databaseId;
+    const latestComment = getLatestDiscussionComment(comments);
 
-    if (latestCommentId) {
-      url += `#discussioncomment-${latestCommentId}`;
+    if (latestComment) {
+      url.hash = `#discussioncomment-${latestComment.databaseId}`;
     }
   }
 
-  return url;
+  return url.toString();
 }
 
 export async function fetchDiscussion(
@@ -139,10 +123,11 @@ export async function fetchDiscussion(
         (discussion) => discussion.title === notification.subject.title,
       ) || [];
 
-    if (discussions.length > 1)
+    if (discussions.length > 1) {
       discussions = discussions.filter(
         (discussion) => discussion.viewerSubscription === 'SUBSCRIBED',
       );
+    }
 
     return discussions[0];
   } catch (err) {}
@@ -155,49 +140,55 @@ export function getLatestDiscussionComment(
     return null;
   }
 
-  return comments
-    .flatMap((comment) => comment.replies.nodes)
-    .concat([comments[comments.length - 1]])
-    .reduce((a, b) => (a.createdAt > b.createdAt ? a : b));
+  // Return latest reply if available
+  if (comments[0].replies.nodes.length === 1) {
+    return comments[0].replies.nodes[0];
+  }
+
+  // Return latest comment if no replies
+  return comments[0];
 }
 
 export async function generateGitHubWebUrl(
   notification: Notification,
   accounts: AuthState,
 ): Promise<string> {
-  let url = notification.repository.html_url;
+  const url = new URL(notification.repository.html_url);
   const token = getTokenForHost(notification.hostname, accounts);
 
   if (notification.subject.latest_comment_url) {
-    url = await getHtmlUrl(notification.subject.latest_comment_url, token);
+    url.href = await getHtmlUrl(notification.subject.latest_comment_url, token);
   } else if (notification.subject.url) {
-    url = await getHtmlUrl(notification.subject.url, token);
+    url.href = await getHtmlUrl(notification.subject.url, token);
   } else {
     // Perform any specific notification type handling (only required for a few special notification scenarios)
     switch (notification.subject.type) {
       case 'CheckSuite':
-        url = getCheckSuiteUrl(notification);
+        url.href = getCheckSuiteUrl(notification);
         break;
       case 'Discussion':
-        url = await getDiscussionUrl(notification, token);
+        url.href = await getDiscussionUrl(notification, token);
         break;
       case 'RepositoryInvitation':
-        url = `${notification.repository.html_url}/invitations`;
+        url.pathname += '/invitations';
         break;
       case 'WorkflowRun':
-        url = getWorkflowRunUrl(notification);
+        url.href = getWorkflowRunUrl(notification);
         break;
       default:
         break;
     }
   }
 
-  url = addNotificationReferrerIdToUrl(url, notification.id, accounts.user?.id);
+  url.searchParams.set(
+    'notification_referrer_id',
+    generateNotificationReferrerId(notification.id, accounts.user?.id),
+  );
 
-  return url;
+  return url.toString();
 }
 
-export function formatForDisplay(text: string[]) {
+export function formatForDisplay(text: string[]): string {
   if (!text) {
     return '';
   }
diff --git a/src/utils/remove-notification.ts b/src/utils/remove-notification.ts
index c7cd6989..4e93cbe5 100644
--- a/src/utils/remove-notification.ts
+++ b/src/utils/remove-notification.ts
@@ -1,10 +1,18 @@
-import type { AccountNotifications } from '../types';
+import type { AccountNotifications, SettingsState } from '../types';
+import Constants from './constants';
 
 export const removeNotification = (
+  settings: SettingsState,
   id: string,
   notifications: AccountNotifications[],
   hostname: string,
 ): AccountNotifications[] => {
+  if (settings.delayNotificationState) {
+    const notificationRow = document.getElementById(id);
+    notificationRow.className += ` ${Constants.READ_CLASS_NAME}`;
+    return notifications;
+  }
+
   const accountIndex = notifications.findIndex(
     (accountNotifications) => accountNotifications.hostname === hostname,
   );


Test patch:
diff --git a/src/components/__snapshots__/NotificationRow.test.tsx.snap b/src/components/__snapshots__/NotificationRow.test.tsx.snap
index e9830cdf..2dd1b9f7 100644
--- a/src/components/__snapshots__/NotificationRow.test.tsx.snap
+++ b/src/components/__snapshots__/NotificationRow.test.tsx.snap
@@ -3,6 +3,7 @@
 exports[`components/NotificationRow.tsx should render itself & its children 1`] = `
 <div
   className="flex space-x-3 py-2 px-3 bg-white dark:bg-gray-dark dark:text-white hover:bg-gray-100 dark:hover:bg-gray-darker border-b border-gray-100 dark:border-gray-darker group"
+  id="138661096"
 >
   <div
     className="flex justify-center items-center w-5 text-green-500"
@@ -234,6 +235,7 @@ exports[`components/NotificationRow.tsx should render itself & its children 1`]
 exports[`components/NotificationRow.tsx should render itself & its children without avatar 1`] = `
 <div
   className="flex space-x-3 py-2 px-3 bg-white dark:bg-gray-dark dark:text-white hover:bg-gray-100 dark:hover:bg-gray-darker border-b border-gray-100 dark:border-gray-darker group"
+  id="138661096"
 >
   <div
     className="flex justify-center items-center w-5 text-green-500"
diff --git a/src/context/App.test.tsx b/src/context/App.test.tsx
index cfdbc0c1..f687aeed 100644
--- a/src/context/App.test.tsx
+++ b/src/context/App.test.tsx
@@ -131,7 +131,12 @@ describe('context/App.tsx', () => {
 
       expect(markNotificationReadMock).toHaveBeenCalledTimes(1);
       expect(markNotificationReadMock).toHaveBeenCalledWith(
-        { enterpriseAccounts: [], token: null, user: null },
+        {
+          enterpriseAccounts: [],
+          token: null,
+          user: null,
+        },
+        mockSettings,
         '123-456',
         'github.com',
       );
@@ -160,6 +165,7 @@ describe('context/App.tsx', () => {
       expect(markNotificationDoneMock).toHaveBeenCalledTimes(1);
       expect(markNotificationDoneMock).toHaveBeenCalledWith(
         { enterpriseAccounts: [], token: null, user: null },
+        mockSettings,
         '123-456',
         'github.com',
       );
@@ -188,6 +194,7 @@ describe('context/App.tsx', () => {
       expect(unsubscribeNotificationMock).toHaveBeenCalledTimes(1);
       expect(unsubscribeNotificationMock).toHaveBeenCalledWith(
         { enterpriseAccounts: [], token: null, user: null },
+        mockSettings,
         '123-456',
         'github.com',
       );
@@ -221,6 +228,7 @@ describe('context/App.tsx', () => {
       expect(markRepoNotificationsMock).toHaveBeenCalledTimes(1);
       expect(markRepoNotificationsMock).toHaveBeenCalledWith(
         { enterpriseAccounts: [], token: null, user: null },
+        mockSettings,
         'gitify-app/notifications-test',
         'github.com',
       );
@@ -325,6 +333,7 @@ describe('context/App.tsx', () => {
         detailedNotifications: false,
         markAsDoneOnOpen: false,
         showAccountHostname: false,
+        delayNotificationState: false,
       },
     );
   });
@@ -369,6 +378,7 @@ describe('context/App.tsx', () => {
         detailedNotifications: false,
         markAsDoneOnOpen: false,
         showAccountHostname: false,
+        delayNotificationState: false,
       },
     );
   });
diff --git a/src/hooks/useNotifications.test.ts b/src/hooks/useNotifications.test.ts
index 146fa85d..053cf0cd 100644
--- a/src/hooks/useNotifications.test.ts
+++ b/src/hooks/useNotifications.test.ts
@@ -427,6 +427,7 @@ describe('hooks/useNotifications.ts', () => {
 
       act(() => {
         result.current.removeNotificationFromState(
+          mockSettings,
           result.current.notifications[0].notifications[0].id,
           result.current.notifications[0].hostname,
         );
@@ -451,7 +452,12 @@ describe('hooks/useNotifications.ts', () => {
         const { result } = renderHook(() => useNotifications());
 
         act(() => {
-          result.current.markNotificationRead(accounts, id, hostname);
+          result.current.markNotificationRead(
+            accounts,
+            mockSettings,
+            id,
+            hostname,
+          );
         });
 
         await waitFor(() => {
@@ -469,7 +475,12 @@ describe('hooks/useNotifications.ts', () => {
         const { result } = renderHook(() => useNotifications());
 
         act(() => {
-          result.current.markNotificationRead(accounts, id, hostname);
+          result.current.markNotificationRead(
+            accounts,
+            mockSettings,
+            id,
+            hostname,
+          );
         });
 
         await waitFor(() => {
@@ -492,7 +503,12 @@ describe('hooks/useNotifications.ts', () => {
         const { result } = renderHook(() => useNotifications());
 
         act(() => {
-          result.current.markNotificationRead(accounts, id, hostname);
+          result.current.markNotificationRead(
+            accounts,
+            mockSettings,
+            id,
+            hostname,
+          );
         });
 
         await waitFor(() => {
@@ -510,7 +526,12 @@ describe('hooks/useNotifications.ts', () => {
         const { result } = renderHook(() => useNotifications());
 
         act(() => {
-          result.current.markNotificationRead(accounts, id, hostname);
+          result.current.markNotificationRead(
+            accounts,
+            mockSettings,
+            id,
+            hostname,
+          );
         });
 
         await waitFor(() => {
@@ -537,7 +558,12 @@ describe('hooks/useNotifications.ts', () => {
         const { result } = renderHook(() => useNotifications());
 
         act(() => {
-          result.current.markNotificationDone(accounts, id, hostname);
+          result.current.markNotificationDone(
+            accounts,
+            mockSettings,
+            id,
+            hostname,
+          );
         });
 
         await waitFor(() => {
@@ -555,7 +581,12 @@ describe('hooks/useNotifications.ts', () => {
         const { result } = renderHook(() => useNotifications());
 
         act(() => {
-          result.current.markNotificationDone(accounts, id, hostname);
+          result.current.markNotificationDone(
+            accounts,
+            mockSettings,
+            id,
+            hostname,
+          );
         });
 
         await waitFor(() => {
@@ -578,7 +609,12 @@ describe('hooks/useNotifications.ts', () => {
         const { result } = renderHook(() => useNotifications());
 
         act(() => {
-          result.current.markNotificationDone(accounts, id, hostname);
+          result.current.markNotificationDone(
+            accounts,
+            mockSettings,
+            id,
+            hostname,
+          );
         });
 
         await waitFor(() => {
@@ -596,7 +632,12 @@ describe('hooks/useNotifications.ts', () => {
         const { result } = renderHook(() => useNotifications());
 
         act(() => {
-          result.current.markNotificationDone(accounts, id, hostname);
+          result.current.markNotificationDone(
+            accounts,
+            mockSettings,
+            id,
+            hostname,
+          );
         });
 
         await waitFor(() => {
@@ -629,7 +670,12 @@ describe('hooks/useNotifications.ts', () => {
         const { result } = renderHook(() => useNotifications());
 
         act(() => {
-          result.current.unsubscribeNotification(accounts, id, hostname);
+          result.current.unsubscribeNotification(
+            accounts,
+            mockSettings,
+            id,
+            hostname,
+          );
         });
 
         await waitFor(() => {
@@ -653,7 +699,12 @@ describe('hooks/useNotifications.ts', () => {
         const { result } = renderHook(() => useNotifications());
 
         act(() => {
-          result.current.unsubscribeNotification(accounts, id, hostname);
+          result.current.unsubscribeNotification(
+            accounts,
+            mockSettings,
+            id,
+            hostname,
+          );
         });
 
         await waitFor(() => {
@@ -682,7 +733,12 @@ describe('hooks/useNotifications.ts', () => {
         const { result } = renderHook(() => useNotifications());
 
         act(() => {
-          result.current.unsubscribeNotification(accounts, id, hostname);
+          result.current.unsubscribeNotification(
+            accounts,
+            mockSettings,
+            id,
+            hostname,
+          );
         });
 
         await waitFor(() => {
@@ -706,7 +762,12 @@ describe('hooks/useNotifications.ts', () => {
         const { result } = renderHook(() => useNotifications());
 
         act(() => {
-          result.current.unsubscribeNotification(accounts, id, hostname);
+          result.current.unsubscribeNotification(
+            accounts,
+            mockSettings,
+            id,
+            hostname,
+          );
         });
 
         await waitFor(() => {
@@ -733,7 +794,12 @@ describe('hooks/useNotifications.ts', () => {
         const { result } = renderHook(() => useNotifications());
 
         act(() => {
-          result.current.markRepoNotifications(accounts, repoSlug, hostname);
+          result.current.markRepoNotifications(
+            accounts,
+            mockSettings,
+            repoSlug,
+            hostname,
+          );
         });
 
         await waitFor(() => {
@@ -751,7 +817,12 @@ describe('hooks/useNotifications.ts', () => {
         const { result } = renderHook(() => useNotifications());
 
         act(() => {
-          result.current.markRepoNotifications(accounts, repoSlug, hostname);
+          result.current.markRepoNotifications(
+            accounts,
+            mockSettings,
+            repoSlug,
+            hostname,
+          );
         });
 
         await waitFor(() => {
@@ -774,7 +845,12 @@ describe('hooks/useNotifications.ts', () => {
         const { result } = renderHook(() => useNotifications());
 
         act(() => {
-          result.current.markRepoNotifications(accounts, repoSlug, hostname);
+          result.current.markRepoNotifications(
+            accounts,
+            mockSettings,
+            repoSlug,
+            hostname,
+          );
         });
 
         await waitFor(() => {
@@ -792,7 +868,12 @@ describe('hooks/useNotifications.ts', () => {
         const { result } = renderHook(() => useNotifications());
 
         act(() => {
-          result.current.markRepoNotifications(accounts, repoSlug, hostname);
+          result.current.markRepoNotifications(
+            accounts,
+            mockSettings,
+            repoSlug,
+            hostname,
+          );
         });
 
         await waitFor(() => {
@@ -822,6 +903,7 @@ describe('hooks/useNotifications.ts', () => {
         act(() => {
           result.current.markRepoNotificationsDone(
             accounts,
+            mockSettings,
             repoSlug,
             hostname,
           );
@@ -844,6 +926,7 @@ describe('hooks/useNotifications.ts', () => {
         act(() => {
           result.current.markRepoNotificationsDone(
             accounts,
+            mockSettings,
             repoSlug,
             hostname,
           );
@@ -871,6 +954,7 @@ describe('hooks/useNotifications.ts', () => {
         act(() => {
           result.current.markRepoNotificationsDone(
             accounts,
+            mockSettings,
             repoSlug,
             hostname,
           );
@@ -893,6 +977,7 @@ describe('hooks/useNotifications.ts', () => {
         act(() => {
           result.current.markRepoNotificationsDone(
             accounts,
+            mockSettings,
             repoSlug,
             hostname,
           );
diff --git a/src/routes/Settings.test.tsx b/src/routes/Settings.test.tsx
index 94b8e5e2..22d88f83 100644
--- a/src/routes/Settings.test.tsx
+++ b/src/routes/Settings.test.tsx
@@ -336,6 +336,34 @@ describe('routes/Settings.tsx', () => {
       expect(updateSetting).toHaveBeenCalledTimes(1);
       expect(updateSetting).toHaveBeenCalledWith('markAsDoneOnOpen', false);
     });
+
+    it('should toggle the delayNotificationState checkbox', async () => {
+      await act(async () => {
+        render(
+          <AppContext.Provider
+            value={{
+              settings: mockSettings,
+              accounts: mockAccounts,
+              updateSetting,
+            }}
+          >
+            <MemoryRouter>
+              <SettingsRoute />
+            </MemoryRouter>
+          </AppContext.Provider>,
+        );
+      });
+
+      fireEvent.click(screen.getByLabelText('Delay notification state'), {
+        target: { checked: true },
+      });
+
+      expect(updateSetting).toHaveBeenCalledTimes(1);
+      expect(updateSetting).toHaveBeenCalledWith(
+        'delayNotificationState',
+        false,
+      );
+    });
   });
 
   describe('System section', () => {
diff --git a/src/routes/__snapshots__/Settings.test.tsx.snap b/src/routes/__snapshots__/Settings.test.tsx.snap
index fd658a36..f8a185eb 100644
--- a/src/routes/__snapshots__/Settings.test.tsx.snap
+++ b/src/routes/__snapshots__/Settings.test.tsx.snap
@@ -380,6 +380,53 @@ exports[`routes/Settings.tsx General should render itself & its children 1`] = `
           </div>
         </div>
       </div>
+      <div
+        class="mt-1 mb-3 text-sm"
+      >
+        <div
+          class="flex items-start"
+        >
+          <div
+            class="flex items-center h-5"
+          >
+            <input
+              class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded"
+              id="delayNotificationState"
+              type="checkbox"
+            />
+          </div>
+          <div
+            class="ml-3 "
+          >
+            <label
+              class="font-medium text-gray-700 dark:text-gray-200"
+              for="delayNotificationState"
+            >
+              Delay notification state
+              <span
+                aria-label="tooltip-delayNotificationState"
+                class="relative"
+                id="tooltip-delayNotificationState"
+              >
+                <svg
+                  aria-hidden="true"
+                  class="text-blue-500 ml-1"
+                  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>
     </fieldset>
     <fieldset
       class="mb-3"
diff --git a/src/utils/api/client.test.ts b/src/utils/api/client.test.ts
index e0d864e9..58fb9277 100644
--- a/src/utils/api/client.test.ts
+++ b/src/utils/api/client.test.ts
@@ -43,7 +43,7 @@ describe('utils/api/client.ts', () => {
       await getRootHypermediaLinks(mockEnterpriseHostname, mockToken);
 
       expect(axios).toHaveBeenCalledWith({
-        url: 'https://example.com/api/v3',
+        url: 'https://example.com/api/v3/',
         method: 'GET',
         data: {},
       });
diff --git a/src/utils/api/utils.test.ts b/src/utils/api/utils.test.ts
index 001b3f3c..54af2e1e 100644
--- a/src/utils/api/utils.test.ts
+++ b/src/utils/api/utils.test.ts
@@ -1,15 +1,19 @@
-import { addHours, formatSearchQueryString } from './utils';
+import {
+  addHours,
+  formatSearchQueryString,
+  getGitHubAPIBaseUrl,
+} from './utils';
 
 describe('utils/api/utils.ts', () => {
-  describe('addHours', () => {
-    test('adds hours correctly for positive values', () => {
-      const result = addHours('2024-02-20T12:00:00.000Z', 3);
-      expect(result).toBe('2024-02-20T15:00:00.000Z');
+  describe('generateGitHubAPIUrl', () => {
+    it('should generate a GitHub API url - non enterprise', () => {
+      const result = getGitHubAPIBaseUrl('github.com');
+      expect(result.toString()).toBe('https://api.github.com/');
     });
 
-    test('adds hours correctly for negative values', () => {
-      const result = addHours('2024-02-20T12:00:00.000Z', -2);
-      expect(result).toBe('2024-02-20T10:00:00.000Z');
+    it('should generate a GitHub API url - enterprise', () => {
+      const result = getGitHubAPIBaseUrl('github.manos.im');
+      expect(result.toString()).toBe('https://github.manos.im/api/v3/');
     });
   });
 
@@ -26,4 +30,16 @@ describe('utils/api/utils.ts', () => {
       );
     });
   });
+
+  describe('addHours', () => {
+    test('adds hours correctly for positive values', () => {
+      const result = addHours('2024-02-20T12:00:00.000Z', 3);
+      expect(result).toBe('2024-02-20T15:00:00.000Z');
+    });
+
+    test('adds hours correctly for negative values', () => {
+      const result = addHours('2024-02-20T12:00:00.000Z', -2);
+      expect(result).toBe('2024-02-20T10:00:00.000Z');
+    });
+  });
 });
diff --git a/src/utils/helpers.test.ts b/src/utils/helpers.test.ts
index d80ab645..1adbdfcb 100644
--- a/src/utils/helpers.test.ts
+++ b/src/utils/helpers.test.ts
@@ -8,11 +8,9 @@ import {
 import type { SubjectType } from '../typesGitHub';
 import * as apiRequests from './api/request';
 import {
-  addNotificationReferrerIdToUrl,
   formatForDisplay,
   generateGitHubWebUrl,
   generateNotificationReferrerId,
-  getGitHubAPIBaseUrl,
   isEnterpriseHost,
   isGitHubLoggedIn,
 } from './helpers';
@@ -29,8 +27,8 @@ describe('utils/helpers.ts', () => {
   });
   describe('isEnterpriseHost', () => {
     it('should return true for enterprise host', () => {
-      expect(isEnterpriseHost('github.manos.im')).toBe(true);
-      expect(isEnterpriseHost('api.github.manos.im')).toBe(true);
+      expect(isEnterpriseHost('github.gitify.app')).toBe(true);
+      expect(isEnterpriseHost('api.github.gitify.app')).toBe(true);
     });
 
     it('should return false for non-enterprise host', () => {
@@ -39,43 +37,6 @@ describe('utils/helpers.ts', () => {
     });
   });
 
-  describe('addNotificationReferrerIdToUrl', () => {
-    it('should add notification_referrer_id to the URL', () => {
-      // Mock data
-      const url = 'https://github.com/gitify-app/notifications-test';
-      const notificationId = '123';
-      const userId = 456;
-
-      const result = addNotificationReferrerIdToUrl(
-        url,
-        notificationId,
-        userId,
-      );
-
-      expect(result).toEqual(
-        'https://github.com/gitify-app/notifications-test?notification_referrer_id=MDE4Ok5vdGlmaWNhdGlvblRocmVhZDEyMzo0NTY%3D',
-      );
-    });
-
-    it('should add notification_referrer_id to the URL, preserving anchor tags', () => {
-      // Mock data
-      const url =
-        'https://github.com/gitify-app/notifications-test/pull/123#issuecomment-1951055051';
-      const notificationId = '123';
-      const userId = 456;
-
-      const result = addNotificationReferrerIdToUrl(
-        url,
-        notificationId,
-        userId,
-      );
-
-      expect(result).toEqual(
-        'https://github.com/gitify-app/notifications-test/pull/123?notification_referrer_id=MDE4Ok5vdGlmaWNhdGlvblRocmVhZDEyMzo0NTY%3D#issuecomment-1951055051',
-      );
-    });
-  });
-
   describe('generateNotificationReferrerId', () => {
     it('should generate the notification_referrer_id', () => {
       const referrerId = generateNotificationReferrerId(
@@ -88,18 +49,6 @@ describe('utils/helpers.ts', () => {
     });
   });
 
-  describe('generateGitHubAPIUrl', () => {
-    it('should generate a GitHub API url - non enterprise', () => {
-      const result = getGitHubAPIBaseUrl('github.com');
-      expect(result).toBe('https://api.github.com');
-    });
-
-    it('should generate a GitHub API url - enterprise', () => {
-      const result = getGitHubAPIBaseUrl('github.manos.im');
-      expect(result).toBe('https://github.manos.im/api/v3');
-    });
-  });
-
   describe('generateGitHubWebUrl', () => {
     const mockedHtmlUrl =
       'https://github.com/gitify-app/notifications-test/issues/785';
diff --git a/src/utils/remove-notification.test.ts b/src/utils/remove-notification.test.ts
index dfbf8a8a..625d00d4 100644
--- a/src/utils/remove-notification.test.ts
+++ b/src/utils/remove-notification.test.ts
@@ -1,7 +1,9 @@
+import { mockSettings } from '../__mocks__/mock-state';
 import {
   mockedSingleAccountNotifications,
   mockedSingleNotification,
 } from '../__mocks__/mockedData';
+import Constants from './constants';
 import { removeNotification } from './remove-notification';
 
 describe('utils/remove-notification.ts', () => {
@@ -12,6 +14,7 @@ describe('utils/remove-notification.ts', () => {
     expect(mockedSingleAccountNotifications[0].notifications.length).toBe(1);
 
     const result = removeNotification(
+      { ...mockSettings, delayNotificationState: false },
       notificationId,
       mockedSingleAccountNotifications,
       hostname,
@@ -19,4 +22,25 @@ describe('utils/remove-notification.ts', () => {
 
     expect(result[0].notifications.length).toBe(0);
   });
+
+  it('should set notification as opaque if delayNotificationState enabled', () => {
+    const mockElement = document.createElement('div');
+    mockElement.id = mockedSingleAccountNotifications[0].notifications[0].id;
+    jest.spyOn(document, 'getElementById').mockReturnValue(mockElement);
+
+    expect(mockedSingleAccountNotifications[0].notifications.length).toBe(1);
+
+    const result = removeNotification(
+      { ...mockSettings, delayNotificationState: true },
+      notificationId,
+      mockedSingleAccountNotifications,
+      hostname,
+    );
+
+    expect(result[0].notifications.length).toBe(1);
+    expect(document.getElementById).toHaveBeenCalledWith(
+      mockedSingleAccountNotifications[0].notifications[0].id,
+    );
+    expect(mockElement.className).toContain(Constants.READ_CLASS_NAME);
+  });
 });