Repository: gitify-app/gitify
Pull request #1036 (2024-04-17 18:18:02)

refactor(reason): separate into own file and use Record for mappings

setchy: Coveralls playing fun and games again haha

Patch:
diff --git a/src/components/NotificationRow.tsx b/src/components/NotificationRow.tsx
index 79b2449b..ed5b3f54 100644
--- a/src/components/NotificationRow.tsx
+++ b/src/components/NotificationRow.tsx
@@ -17,11 +17,11 @@ import { AppContext } from '../context/App';
 import type { Notification } from '../typesGithub';
 import { openExternalLink } from '../utils/comms';
 import {
-  formatReason,
   getNotificationTypeIcon,
   getNotificationTypeIconColor,
 } from '../utils/github-api';
 import { formatForDisplay, openInBrowser } from '../utils/helpers';
+import { formatReason } from '../utils/reason';
 
 interface IProps {
   hostname: string;
@@ -36,10 +36,11 @@ export const NotificationRow: FC<IProps> = ({ notification, hostname }) => {
     markNotificationRead,
     markNotificationDone,
     unsubscribeNotification,
+    notifications,
   } = useContext(AppContext);
 
-  const pressTitle = useCallback(() => {
-    openBrowser();
+  const openNotification = useCallback(() => {
+    openInBrowser(notification, accounts);
 
     if (settings.markAsDoneOnOpen) {
       markNotificationDone(notification.id, hostname);
@@ -47,12 +48,7 @@ export const NotificationRow: FC<IProps> = ({ notification, hostname }) => {
       // no need to mark as read, github does it by default when opening it
       removeNotificationFromState(notification.id, hostname);
     }
-  }, [settings]);
-
-  const openBrowser = useCallback(
-    () => openInBrowser(notification, accounts),
-    [notification],
-  );
+  }, [notifications, notification, accounts, settings]); // notifications required here to prevent weird state issues
 
   const unsubscribe = (event: MouseEvent<HTMLElement>) => {
     // Don't trigger onClick of parent element.
@@ -96,8 +92,8 @@ export const NotificationRow: FC<IProps> = ({ notification, hostname }) => {
 
       <div
         className="flex-1 overflow-hidden"
-        onClick={() => pressTitle()}
-        onKeyDown={() => pressTitle()}
+        onClick={() => openNotification()}
+        onKeyDown={() => openNotification()}
       >
         <div
           className="mb-1 text-sm whitespace-nowrap overflow-ellipsis overflow-hidden cursor-pointer"
@@ -132,7 +128,7 @@ export const NotificationRow: FC<IProps> = ({ notification, hostname }) => {
                 </span>
               )}
               <span className="ml-1" title={reason.description}>
-                {reason.type}
+                {reason.title}
               </span>
               <span className="ml-1">{updatedAt}</span>
             </span>
diff --git a/src/routes/Settings.tsx b/src/routes/Settings.tsx
index 0e0de767..77c0e00e 100644
--- a/src/routes/Settings.tsx
+++ b/src/routes/Settings.tsx
@@ -36,6 +36,7 @@ export const SettingsRoute: FC = () => {
   const navigate = useNavigate();
 
   const [isLinux, setIsLinux] = useState<boolean>(false);
+  const [isMacOS, setIsMacOS] = useState<boolean>(false);
   const [appVersion, setAppVersion] = useState<string | null>(null);
   const [repoScope, setRepoScope] = useState<boolean>(false);
 
@@ -57,6 +58,7 @@ export const SettingsRoute: FC = () => {
   useEffect(() => {
     ipcRenderer.invoke('get-platform').then((result: string) => {
       setIsLinux(result === 'linux');
+      setIsMacOS(result === 'darwin');
     });
 
     ipcRenderer.invoke('get-app-version').then((result: string) => {
@@ -240,14 +242,19 @@ export const SettingsRoute: FC = () => {
           <legend id="system" className="font-semibold mt-2 mb-1">
             System
           </legend>
-          <Checkbox
-            name="showNotificationsCountInTray"
-            label="Show notifications count in tray"
-            checked={settings.showNotificationsCountInTray}
-            onChange={(evt) =>
-              updateSetting('showNotificationsCountInTray', evt.target.checked)
-            }
-          />
+          {isMacOS && (
+            <Checkbox
+              name="showNotificationsCountInTray"
+              label="Show notifications count in tray"
+              checked={settings.showNotificationsCountInTray}
+              onChange={(evt) =>
+                updateSetting(
+                  'showNotificationsCountInTray',
+                  evt.target.checked,
+                )
+              }
+            />
+          )}
           <Checkbox
             name="showNotifications"
             label="Show system notifications"
diff --git a/src/types.ts b/src/types.ts
index 77c9e77c..1b318fb8 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -88,3 +88,8 @@ export type ErrorType =
   | 'NETWORK'
   | 'RATE_LIMITED'
   | 'UNKNOWN';
+
+export interface FormattedReason {
+  title: string;
+  description: string;
+}
diff --git a/src/utils/github-api.ts b/src/utils/github-api.ts
index 9898080e..f392fdfe 100644
--- a/src/utils/github-api.ts
+++ b/src/utils/github-api.ts
@@ -24,99 +24,7 @@ import {
   XIcon,
 } from '@primer/octicons-react';
 import type { FC } from 'react';
-import type { Reason, Subject } from '../typesGithub';
-
-const DESCRIPTIONS = {
-  APPROVAL_REQUESTED: 'You were requested to review and approve a deployment.',
-  ASSIGN: 'You were assigned to the issue.',
-  AUTHOR: 'You created the thread.',
-  CI_ACTIVITY:
-    'A GitHub Actions workflow run was triggered for your repository',
-  COMMENT: 'You commented on the thread.',
-  INVITATION: 'You accepted an invitation to contribute to the repository.',
-  MANUAL: 'You subscribed to the thread (via an issue or pull request).',
-  MEMBER_FEATURE_REQUESTED:
-    'Organization members have requested to enable a feature such as Draft Pull Requests or CoPilot.',
-  MENTION: 'You were specifically @mentioned in the content.',
-  REVIEW_REQUESTED:
-    "You, or a team you're a member of, were requested to review a pull request.",
-  SECURITY_ADVISORY_CREDIT:
-    'You were credited for contributing to a security advisory.',
-  SECURITY_ALERT:
-    'GitHub discovered a security vulnerability in your repository.',
-  STATE_CHANGE:
-    'You changed the thread state (for example, closing an issue or merging a pull request).',
-  SUBSCRIBED: "You're watching the repository.",
-  TEAM_MENTION: 'You were on a team that was mentioned.',
-  UNKNOWN: 'The reason for this notification is not supported by the app.',
-};
-
-export function formatReason(reason: Reason): {
-  type: string;
-  description: string;
-} {
-  switch (reason) {
-    case 'approval_requested':
-      return {
-        type: 'Approval Requested',
-        description: DESCRIPTIONS.APPROVAL_REQUESTED,
-      };
-    case 'assign':
-      return { type: 'Assigned', description: DESCRIPTIONS.ASSIGN };
-    case 'author':
-      return { type: 'Authored', description: DESCRIPTIONS.AUTHOR };
-    case 'ci_activity':
-      return {
-        type: 'Workflow Run Completed',
-        description: DESCRIPTIONS.CI_ACTIVITY,
-      };
-    case 'comment':
-      return { type: 'Commented', description: DESCRIPTIONS.COMMENT };
-    case 'invitation':
-      return {
-        type: 'Invitation Received',
-        description: DESCRIPTIONS.INVITATION,
-      };
-    case 'manual':
-      return { type: 'Updated', description: DESCRIPTIONS.MANUAL };
-    case 'member_feature_requested':
-      return {
-        type: 'Member Feature Requested',
-        description: DESCRIPTIONS.MEMBER_FEATURE_REQUESTED,
-      };
-    case 'mention':
-      return { type: 'Mentioned', description: DESCRIPTIONS.MENTION };
-    case 'review_requested':
-      return {
-        type: 'Review Requested',
-        description: DESCRIPTIONS.REVIEW_REQUESTED,
-      };
-    case 'security_advisory_credit':
-      return {
-        type: 'Security Advisory Credit Recevied',
-        description: DESCRIPTIONS.SECURITY_ADVISORY_CREDIT,
-      };
-    case 'security_alert':
-      return {
-        type: 'Security Alert Received',
-        description: DESCRIPTIONS.SECURITY_ALERT,
-      };
-    case 'state_change':
-      return {
-        type: 'State Changed',
-        description: DESCRIPTIONS.STATE_CHANGE,
-      };
-    case 'subscribed':
-      return { type: 'Updated', description: DESCRIPTIONS.SUBSCRIBED };
-    case 'team_mention':
-      return {
-        type: 'Team Mentioned',
-        description: DESCRIPTIONS.TEAM_MENTION,
-      };
-    default:
-      return { type: 'Unknown', description: DESCRIPTIONS.UNKNOWN };
-  }
-}
+import type { Subject } from '../typesGithub';
 
 export function getNotificationTypeIcon(subject: Subject): FC<OcticonProps> {
   switch (subject.type) {
diff --git a/src/utils/reason.ts b/src/utils/reason.ts
new file mode 100644
index 00000000..1eab5491
--- /dev/null
+++ b/src/utils/reason.ts
@@ -0,0 +1,79 @@
+import type { FormattedReason } from '../types';
+import type { Reason } from '../typesGithub';
+
+const FORMATTED_REASONS: Record<Reason, FormattedReason> = {
+  approval_requested: {
+    title: 'Approval Requested',
+    description: 'You were requested to review and approve a deployment.',
+  },
+  assign: {
+    title: 'Assigned',
+    description: 'You were assigned to the issue.',
+  },
+  author: {
+    title: 'Authored',
+    description: 'You created the thread.',
+  },
+  ci_activity: {
+    title: 'Workflow Run Completed',
+    description:
+      'A GitHub Actions workflow run was triggered for your repository.',
+  },
+  comment: {
+    title: 'Commented',
+    description: 'You commented on the thread.',
+  },
+  invitation: {
+    title: 'Invitation Received',
+    description: 'You accepted an invitation to contribute to the repository.',
+  },
+  manual: {
+    title: 'Updated',
+    description: 'You subscribed to the thread (via an issue or pull request).',
+  },
+  member_feature_requested: {
+    title: 'Member Feature Requested',
+    description:
+      'Organization members have requested to enable a feature such as Draft Pull Requests or Copilot.',
+  },
+  mention: {
+    title: 'Mentioned',
+    description: 'You were specifically @mentioned in the content.',
+  },
+  review_requested: {
+    title: 'Review Requested',
+    description:
+      "You, or a team you're a member of, were requested to review a pull request.",
+  },
+  security_advisory_credit: {
+    title: 'Security Advisory Credit Received',
+    description: 'You were credited for contributing to a security advisory.',
+  },
+  security_alert: {
+    title: 'Security Alert Received',
+    description:
+      'GitHub discovered a security vulnerability in your repository.',
+  },
+  state_change: {
+    title: 'State Changed',
+    description:
+      'You changed the thread state (for example, closing an issue or merging a pull request).',
+  },
+  subscribed: {
+    title: 'Updated',
+    description: "You're watching the repository.",
+  },
+  team_mention: {
+    title: 'Team Mentioned',
+    description: 'You were on a team that was mentioned.',
+  },
+};
+
+const UNKNOWN_REASON: FormattedReason = {
+  title: 'Unknown',
+  description: 'The reason for this notification is not supported by the app.',
+};
+
+export function formatReason(reason: Reason): FormattedReason {
+  return FORMATTED_REASONS[reason] || UNKNOWN_REASON;
+}


Test patch:
diff --git a/src/utils/__snapshots__/github-api.test.ts.snap b/src/utils/__snapshots__/github-api.test.ts.snap
index 146e409f..2c5335a0 100644
--- a/src/utils/__snapshots__/github-api.test.ts.snap
+++ b/src/utils/__snapshots__/github-api.test.ts.snap
@@ -1,117 +1,5 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`utils/github-api.ts formatReason - should format the notification reason 1`] = `
-{
-  "description": "You were requested to review and approve a deployment.",
-  "type": "Approval Requested",
-}
-`;
-
-exports[`utils/github-api.ts formatReason - should format the notification reason 2`] = `
-{
-  "description": "You were assigned to the issue.",
-  "type": "Assigned",
-}
-`;
-
-exports[`utils/github-api.ts formatReason - should format the notification reason 3`] = `
-{
-  "description": "You created the thread.",
-  "type": "Authored",
-}
-`;
-
-exports[`utils/github-api.ts formatReason - should format the notification reason 4`] = `
-{
-  "description": "A GitHub Actions workflow run was triggered for your repository",
-  "type": "Workflow Run Completed",
-}
-`;
-
-exports[`utils/github-api.ts formatReason - should format the notification reason 5`] = `
-{
-  "description": "You commented on the thread.",
-  "type": "Commented",
-}
-`;
-
-exports[`utils/github-api.ts formatReason - should format the notification reason 6`] = `
-{
-  "description": "You accepted an invitation to contribute to the repository.",
-  "type": "Invitation Received",
-}
-`;
-
-exports[`utils/github-api.ts formatReason - should format the notification reason 7`] = `
-{
-  "description": "You subscribed to the thread (via an issue or pull request).",
-  "type": "Updated",
-}
-`;
-
-exports[`utils/github-api.ts formatReason - should format the notification reason 8`] = `
-{
-  "description": "Organization members have requested to enable a feature such as Draft Pull Requests or CoPilot.",
-  "type": "Member Feature Requested",
-}
-`;
-
-exports[`utils/github-api.ts formatReason - should format the notification reason 9`] = `
-{
-  "description": "You were specifically @mentioned in the content.",
-  "type": "Mentioned",
-}
-`;
-
-exports[`utils/github-api.ts formatReason - should format the notification reason 10`] = `
-{
-  "description": "You, or a team you're a member of, were requested to review a pull request.",
-  "type": "Review Requested",
-}
-`;
-
-exports[`utils/github-api.ts formatReason - should format the notification reason 11`] = `
-{
-  "description": "You were credited for contributing to a security advisory.",
-  "type": "Security Advisory Credit Recevied",
-}
-`;
-
-exports[`utils/github-api.ts formatReason - should format the notification reason 12`] = `
-{
-  "description": "GitHub discovered a security vulnerability in your repository.",
-  "type": "Security Alert Received",
-}
-`;
-
-exports[`utils/github-api.ts formatReason - should format the notification reason 13`] = `
-{
-  "description": "You changed the thread state (for example, closing an issue or merging a pull request).",
-  "type": "State Changed",
-}
-`;
-
-exports[`utils/github-api.ts formatReason - should format the notification reason 14`] = `
-{
-  "description": "You're watching the repository.",
-  "type": "Updated",
-}
-`;
-
-exports[`utils/github-api.ts formatReason - should format the notification reason 15`] = `
-{
-  "description": "You were on a team that was mentioned.",
-  "type": "Team Mentioned",
-}
-`;
-
-exports[`utils/github-api.ts formatReason - should format the notification reason 16`] = `
-{
-  "description": "The reason for this notification is not supported by the app.",
-  "type": "Unknown",
-}
-`;
-
 exports[`utils/github-api.ts getNotificationTypeIconColor should format the notification color for check suite 1`] = `"text-gray-500 dark:text-gray-300"`;
 
 exports[`utils/github-api.ts getNotificationTypeIconColor should format the notification color for check suite 2`] = `"text-red-500"`;
diff --git a/src/utils/__snapshots__/reason.test.ts.snap b/src/utils/__snapshots__/reason.test.ts.snap
new file mode 100644
index 00000000..2bda4473
--- /dev/null
+++ b/src/utils/__snapshots__/reason.test.ts.snap
@@ -0,0 +1,113 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`utils/reason.ts formatReason - should format the notification reason 1`] = `
+{
+  "description": "You were requested to review and approve a deployment.",
+  "title": "Approval Requested",
+}
+`;
+
+exports[`utils/reason.ts formatReason - should format the notification reason 2`] = `
+{
+  "description": "You were assigned to the issue.",
+  "title": "Assigned",
+}
+`;
+
+exports[`utils/reason.ts formatReason - should format the notification reason 3`] = `
+{
+  "description": "You created the thread.",
+  "title": "Authored",
+}
+`;
+
+exports[`utils/reason.ts formatReason - should format the notification reason 4`] = `
+{
+  "description": "A GitHub Actions workflow run was triggered for your repository.",
+  "title": "Workflow Run Completed",
+}
+`;
+
+exports[`utils/reason.ts formatReason - should format the notification reason 5`] = `
+{
+  "description": "You commented on the thread.",
+  "title": "Commented",
+}
+`;
+
+exports[`utils/reason.ts formatReason - should format the notification reason 6`] = `
+{
+  "description": "You accepted an invitation to contribute to the repository.",
+  "title": "Invitation Received",
+}
+`;
+
+exports[`utils/reason.ts formatReason - should format the notification reason 7`] = `
+{
+  "description": "You subscribed to the thread (via an issue or pull request).",
+  "title": "Updated",
+}
+`;
+
+exports[`utils/reason.ts formatReason - should format the notification reason 8`] = `
+{
+  "description": "Organization members have requested to enable a feature such as Draft Pull Requests or Copilot.",
+  "title": "Member Feature Requested",
+}
+`;
+
+exports[`utils/reason.ts formatReason - should format the notification reason 9`] = `
+{
+  "description": "You were specifically @mentioned in the content.",
+  "title": "Mentioned",
+}
+`;
+
+exports[`utils/reason.ts formatReason - should format the notification reason 10`] = `
+{
+  "description": "You, or a team you're a member of, were requested to review a pull request.",
+  "title": "Review Requested",
+}
+`;
+
+exports[`utils/reason.ts formatReason - should format the notification reason 11`] = `
+{
+  "description": "You were credited for contributing to a security advisory.",
+  "title": "Security Advisory Credit Received",
+}
+`;
+
+exports[`utils/reason.ts formatReason - should format the notification reason 12`] = `
+{
+  "description": "GitHub discovered a security vulnerability in your repository.",
+  "title": "Security Alert Received",
+}
+`;
+
+exports[`utils/reason.ts formatReason - should format the notification reason 13`] = `
+{
+  "description": "You changed the thread state (for example, closing an issue or merging a pull request).",
+  "title": "State Changed",
+}
+`;
+
+exports[`utils/reason.ts formatReason - should format the notification reason 14`] = `
+{
+  "description": "You're watching the repository.",
+  "title": "Updated",
+}
+`;
+
+exports[`utils/reason.ts formatReason - should format the notification reason 15`] = `
+{
+  "description": "You were on a team that was mentioned.",
+  "title": "Team Mentioned",
+}
+`;
+
+exports[`utils/reason.ts formatReason - should format the notification reason 16`] = `
+{
+  "description": "The reason for this notification is not supported by the app.",
+  "title": "Unknown",
+}
+`;
diff --git a/src/utils/github-api.test.ts b/src/utils/github-api.test.ts
index 588e36c5..70ebd6b3 100644
--- a/src/utils/github-api.test.ts
+++ b/src/utils/github-api.test.ts
@@ -1,30 +1,10 @@
-import type { Reason, StateType, Subject, SubjectType } from '../typesGithub';
+import type { StateType, Subject, SubjectType } from '../typesGithub';
 import {
-  formatReason,
   getNotificationTypeIcon,
   getNotificationTypeIconColor,
 } from './github-api';
 
 describe('utils/github-api.ts', () => {
-  it('formatReason - should format the notification reason', () => {
-    expect(formatReason('approval_requested')).toMatchSnapshot();
-    expect(formatReason('assign')).toMatchSnapshot();
-    expect(formatReason('author')).toMatchSnapshot();
-    expect(formatReason('ci_activity')).toMatchSnapshot();
-    expect(formatReason('comment')).toMatchSnapshot();
-    expect(formatReason('invitation')).toMatchSnapshot();
-    expect(formatReason('manual')).toMatchSnapshot();
-    expect(formatReason('member_feature_requested')).toMatchSnapshot();
-    expect(formatReason('mention')).toMatchSnapshot();
-    expect(formatReason('review_requested')).toMatchSnapshot();
-    expect(formatReason('security_advisory_credit')).toMatchSnapshot();
-    expect(formatReason('security_alert')).toMatchSnapshot();
-    expect(formatReason('state_change')).toMatchSnapshot();
-    expect(formatReason('subscribed')).toMatchSnapshot();
-    expect(formatReason('team_mention')).toMatchSnapshot();
-    expect(formatReason('something_else_unknown' as Reason)).toMatchSnapshot();
-  });
-
   describe('getNotificationTypeIcon - should get the notification type icon', () => {
     expect(
       getNotificationTypeIcon(
diff --git a/src/utils/reason.test.ts b/src/utils/reason.test.ts
new file mode 100644
index 00000000..10ed1c50
--- /dev/null
+++ b/src/utils/reason.test.ts
@@ -0,0 +1,23 @@
+import type { Reason } from '../typesGithub';
+import { formatReason } from './reason';
+
+describe('utils/reason.ts', () => {
+  it('formatReason - should format the notification reason', () => {
+    expect(formatReason('approval_requested')).toMatchSnapshot();
+    expect(formatReason('assign')).toMatchSnapshot();
+    expect(formatReason('author')).toMatchSnapshot();
+    expect(formatReason('ci_activity')).toMatchSnapshot();
+    expect(formatReason('comment')).toMatchSnapshot();
+    expect(formatReason('invitation')).toMatchSnapshot();
+    expect(formatReason('manual')).toMatchSnapshot();
+    expect(formatReason('member_feature_requested')).toMatchSnapshot();
+    expect(formatReason('mention')).toMatchSnapshot();
+    expect(formatReason('review_requested')).toMatchSnapshot();
+    expect(formatReason('security_advisory_credit')).toMatchSnapshot();
+    expect(formatReason('security_alert')).toMatchSnapshot();
+    expect(formatReason('state_change')).toMatchSnapshot();
+    expect(formatReason('subscribed')).toMatchSnapshot();
+    expect(formatReason('team_mention')).toMatchSnapshot();
+    expect(formatReason('something_else_unknown' as Reason)).toMatchSnapshot();
+  });
+});