Repository: gitify-app/gitify
Pull request #1439 (2024-08-06 05:57:59)

feat(settings): disable `show accounts` when multiple accounts are authenticated
Toggling this setting has no affect when there are multiple accounts authenticated. 


Patch:
diff --git a/package.json b/package.json
index 210f4725..c1f62f35 100644
--- a/package.json
+++ b/package.json
@@ -122,7 +122,7 @@
   },
   "devDependencies": {
     "@biomejs/biome": "1.8.3",
-    "@electron/notarize": "2.3.2",
+    "@electron/notarize": "2.4.0",
     "@testing-library/react": "16.0.0",
     "@types/jest": "29.5.12",
     "@types/node": "20.14.15",
@@ -131,7 +131,7 @@
     "@types/react-router-dom": "5.3.3",
     "autoprefixer": "10.4.20",
     "css-loader": "7.1.2",
-    "electron": "31.3.1",
+    "electron": "31.4.0",
     "electron-builder": "24.13.3",
     "graphql-tag": "2.12.6",
     "husky": "9.1.4",
@@ -143,14 +143,14 @@
     "resize-observer-polyfill": "1.5.1",
     "rimraf": "6.0.1",
     "style-loader": "4.0.0",
-    "tailwindcss": "3.4.9",
+    "tailwindcss": "3.4.10",
     "ts-jest": "29.2.4",
     "ts-node": "10.9.2",
     "webpack": "5.93.0",
     "webpack-cli": "5.1.4",
     "webpack-merge": "6.0.1"
   },
-  "packageManager": "pnpm@9.7.0",
+  "packageManager": "pnpm@9.7.1",
   "lint-staged": {
     "*": "biome check --fix --no-errors-on-unmatched",
     "*.{js,ts,tsx}": "pnpm test -- --findRelatedTests -u --passWithNoTests"
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 4311b39e..b109ac37 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -10,7 +10,7 @@ importers:
     dependencies:
       '@electron/remote':
         specifier: 2.1.2
-        version: 2.1.2(electron@31.3.1)
+        version: 2.1.2(electron@31.4.0)
       '@primer/octicons-react':
         specifier: 19.11.0
         version: 19.11.0(react@18.3.1)
@@ -37,7 +37,7 @@ importers:
         version: 4.20.10
       menubar:
         specifier: 9.5.0
-        version: 9.5.0(electron@31.3.1)
+        version: 9.5.0(electron@31.4.0)
       nprogress:
         specifier: 0.2.0
         version: 0.2.0
@@ -70,8 +70,8 @@ importers:
         specifier: 1.8.3
         version: 1.8.3
       '@electron/notarize':
-        specifier: 2.3.2
-        version: 2.3.2
+        specifier: 2.4.0
+        version: 2.4.0
       '@testing-library/react':
         specifier: 16.0.0
         version: 16.0.0(@testing-library/dom@10.0.0)(@types/react-dom@18.2.22)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@@ -97,8 +97,8 @@ importers:
         specifier: 7.1.2
         version: 7.1.2(webpack@5.93.0(webpack-cli@5.1.4))
       electron:
-        specifier: 31.3.1
-        version: 31.3.1
+        specifier: 31.4.0
+        version: 31.4.0
       electron-builder:
         specifier: 24.13.3
         version: 24.13.3(electron-builder-squirrel-windows@24.13.3(dmg-builder@24.13.3))
@@ -133,8 +133,8 @@ importers:
         specifier: 4.0.0
         version: 4.0.0(webpack@5.93.0(webpack-cli@5.1.4))
       tailwindcss:
-        specifier: 3.4.9
-        version: 3.4.9(ts-node@10.9.2(@types/node@20.14.15)(typescript@5.5.4))
+        specifier: 3.4.10
+        version: 3.4.10(ts-node@10.9.2(@types/node@20.14.15)(typescript@5.5.4))
       ts-jest:
         specifier: 29.2.4
         version: 29.2.4(@babel/core@7.24.3)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.3))(jest@29.7.0(@types/node@20.14.15)(ts-node@10.9.2(@types/node@20.14.15)(typescript@5.5.4)))(typescript@5.5.4)
@@ -413,8 +413,8 @@ packages:
     resolution: {integrity: sha512-aL+bFMIkpR0cmmj5Zgy0LMKEpgy43/hw5zadEArgmAMWWlKc5buwFvFT9G/o/YJkvXAJm5q3iuTuLaiaXW39sg==}
     engines: {node: '>= 10.0.0'}
 
-  '@electron/notarize@2.3.2':
-    resolution: {integrity: sha512-zfayxCe19euNwRycCty1C7lF7snk9YwfRpB5M8GLr1a4ICH63znxaPNAubrMvj0yDvVozqfgsdYpXVUnpWBDpg==}
+  '@electron/notarize@2.4.0':
+    resolution: {integrity: sha512-ArHnRPIJJGrmV+uWNQSINAht+cM4gAo3uA3WFI54bYF93mzmD15gzhPQ0Dd+v/fkMhnRiiIO8NNkGdn87Vsy0g==}
     engines: {node: '>= 10.0.0'}
 
   '@electron/osx-sign@1.0.5':
@@ -1406,8 +1406,8 @@ packages:
   electron-updater@6.3.3:
     resolution: {integrity: sha512-Kj1u6kfyxUyatnspvKa6qhGn82rMZfUD03WOvCGJ12PyRss/AC8kkYsN9IrJihKTlN8nRwTjZ1JM2UUXoD0KsA==}
 
-  electron@31.3.1:
-    resolution: {integrity: sha512-9fiuWlRhBfygtcT+auRd/WdBK/f8LZZcrpx0RjpXhH2DPTP/PfnkC4JB1PW55qCbGbh4wAgkYbf4ExIag8oGCA==}
+  electron@31.4.0:
+    resolution: {integrity: sha512-YTwKoAA+nrJMlI1TTHnIXLYWoQLKnhbkz0qxZcI7Hadcy0UaFMFs9xzwvH2MnrRpVJy7RKo49kVGuvSdRl8zMA==}
     engines: {node: '>= 12.20.55'}
     hasBin: true
 
@@ -2897,8 +2897,8 @@ packages:
   tailwind-merge@2.5.2:
     resolution: {integrity: sha512-kjEBm+pvD+6eAwzJL2Bi+02/9LFLal1Gs61+QB7HvTfQQ0aXwC5LGT8PEt1gS0CWKktKe6ysPTAy3cBC5MeiIg==}
 
-  tailwindcss@3.4.9:
-    resolution: {integrity: sha512-1SEOvRr6sSdV5IDf9iC+NU4dhwdqzF4zKKq3sAbasUWHEM6lsMhX+eNN5gkPx1BvLFEnZQEUFbXnGj8Qlp83Pg==}
+  tailwindcss@3.4.10:
+    resolution: {integrity: sha512-KWZkVPm7yJRhdu4SRSl9d4AK2wM3a50UsvgHZO7xY77NQr2V+fIrEuoDGQcbvswWvFGbS2f6e+jC/6WJm1Dl0w==}
     engines: {node: '>=14.0.0'}
     hasBin: true
 
@@ -3541,7 +3541,7 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  '@electron/notarize@2.3.2':
+  '@electron/notarize@2.4.0':
     dependencies:
       debug: 4.3.4
       fs-extra: 9.1.0
@@ -3560,9 +3560,9 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  '@electron/remote@2.1.2(electron@31.3.1)':
+  '@electron/remote@2.1.2(electron@31.4.0)':
     dependencies:
-      electron: 31.3.1
+      electron: 31.4.0
 
   '@electron/universal@1.5.1':
     dependencies:
@@ -4814,7 +4814,7 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  electron@31.3.1:
+  electron@31.4.0:
     dependencies:
       '@electron/get': 2.0.3
       '@types/node': 20.14.15
@@ -5813,9 +5813,9 @@ snapshots:
       escape-string-regexp: 4.0.0
     optional: true
 
-  menubar@9.5.0(electron@31.3.1):
+  menubar@9.5.0(electron@31.4.0):
     dependencies:
-      electron: 31.3.1
+      electron: 31.4.0
       electron-positioner: 4.1.0
 
   merge-stream@2.0.0: {}
@@ -6432,7 +6432,7 @@ snapshots:
 
   tailwind-merge@2.5.2: {}
 
-  tailwindcss@3.4.9(ts-node@10.9.2(@types/node@20.14.15)(typescript@5.5.4)):
+  tailwindcss@3.4.10(ts-node@10.9.2(@types/node@20.14.15)(typescript@5.5.4)):
     dependencies:
       '@alloc/quick-lru': 5.2.0
       arg: 5.0.2
diff --git a/src/components/settings/AppearanceSettings.tsx b/src/components/settings/AppearanceSettings.tsx
index 6d5066be..1014776b 100644
--- a/src/components/settings/AppearanceSettings.tsx
+++ b/src/components/settings/AppearanceSettings.tsx
@@ -11,6 +11,7 @@ import { ipcRenderer, webFrame } from 'electron';
 import { type FC, useContext, useEffect, useState } from 'react';
 import { AppContext } from '../../context/App';
 import { Size, Theme } from '../../types';
+import { hasMultipleAccounts } from '../../utils/auth/utils';
 import { setTheme } from '../../utils/theme';
 import { zoomLevelToPercentage, zoomPercentageToLevel } from '../../utils/zoom';
 import { Button } from '../buttons/Button';
@@ -22,7 +23,7 @@ let timeout: NodeJS.Timeout;
 const DELAY = 200;
 
 export const AppearanceSettings: FC = () => {
-  const { settings, updateSetting } = useContext(AppContext);
+  const { auth, settings, updateSetting } = useContext(AppContext);
   const [zoomPercentage, setZoomPercentage] = useState(
     zoomLevelToPercentage(webFrame.getZoomLevel()),
   );
@@ -198,7 +199,8 @@ export const AppearanceSettings: FC = () => {
       <Checkbox
         name="showAccountHeader"
         label="Show account header"
-        checked={settings.showAccountHeader}
+        checked={settings.showAccountHeader || hasMultipleAccounts(auth)}
+        disabled={hasMultipleAccounts(auth)}
         onChange={(evt) =>
           updateSetting('showAccountHeader', evt.target.checked)
         }
diff --git a/src/context/App.tsx b/src/context/App.tsx
index b0291548..eb93a8e9 100644
--- a/src/context/App.tsx
+++ b/src/context/App.tsx
@@ -32,6 +32,7 @@ import {
   addAccount,
   authGitHub,
   getToken,
+  hasAccounts,
   refreshAccount,
   removeAccount,
 } from '../utils/auth/utils';
@@ -203,7 +204,7 @@ export const AppProvider = ({ children }: { children: ReactNode }) => {
   );
 
   const isLoggedIn = useMemo(() => {
-    return auth.accounts.length > 0;
+    return hasAccounts(auth);
   }, [auth]);
 
   const loginWithGitHubApp = useCallback(async () => {
diff --git a/src/utils/auth/utils.ts b/src/utils/auth/utils.ts
index 94a1965b..d79fcbce 100644
--- a/src/utils/auth/utils.ts
+++ b/src/utils/auth/utils.ts
@@ -241,3 +241,11 @@ export function isValidToken(token: Token) {
 export function getAccountUUID(account: Account): string {
   return btoa(`${account.hostname}-${account.user.id}-${account.method}`);
 }
+
+export function hasAccounts(auth: AuthState) {
+  return auth.accounts.length > 0;
+}
+
+export function hasMultipleAccounts(auth: AuthState) {
+  return auth.accounts.length > 1;
+}


Test patch:
diff --git a/src/components/settings/AppearanceSettings.test.tsx b/src/components/settings/AppearanceSettings.test.tsx
index 2cbba6fe..ce8afe30 100644
--- a/src/components/settings/AppearanceSettings.test.tsx
+++ b/src/components/settings/AppearanceSettings.test.tsx
@@ -1,7 +1,11 @@
 import { act, fireEvent, render, screen } from '@testing-library/react';
 import { webFrame } from 'electron';
 import { MemoryRouter } from 'react-router-dom';
-import { mockAuth, mockSettings } from '../../__mocks__/state-mocks';
+import {
+  mockAuth,
+  mockGitHubAppAccount,
+  mockSettings,
+} from '../../__mocks__/state-mocks';
 import { AppContext } from '../../context/App';
 import { AppearanceSettings } from './AppearanceSettings';
 
@@ -200,7 +204,9 @@ describe('routes/components/settings/AppearanceSettings.tsx', () => {
       render(
         <AppContext.Provider
           value={{
-            auth: mockAuth,
+            auth: {
+              accounts: [mockGitHubAppAccount],
+            },
             settings: mockSettings,
             updateSetting,
           }}
diff --git a/src/routes/__snapshots__/Settings.test.tsx.snap b/src/routes/__snapshots__/Settings.test.tsx.snap
index c3270ea9..74818bec 100644
--- a/src/routes/__snapshots__/Settings.test.tsx.snap
+++ b/src/routes/__snapshots__/Settings.test.tsx.snap
@@ -327,7 +327,9 @@ exports[`routes/Settings.tsx should render itself & its children 1`] = `
           class="flex items-center"
         >
           <input
+            checked=""
             class="size-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500 cursor-pointer"
+            disabled=""
             id="showAccountHeader"
             type="checkbox"
           />
@@ -337,6 +339,7 @@ exports[`routes/Settings.tsx should render itself & its children 1`] = `
             <label
               class="font-medium text-gray-700 dark:text-gray-200 cursor-pointer"
               for="showAccountHeader"
+              style="text-decoration: line-through;"
             >
               Show account header
             </label>
diff --git a/src/utils/auth/utils.test.ts b/src/utils/auth/utils.test.ts
index 460b0496..18965400 100644
--- a/src/utils/auth/utils.test.ts
+++ b/src/utils/auth/utils.test.ts
@@ -368,4 +368,37 @@ describe('utils/auth/utils.ts', () => {
       expect(auth.isValidToken('1234567890asdfg' as Token)).toBeFalsy();
     });
   });
+
+  describe('hasAccounts', () => {
+    it('should return true', () => {
+      expect(auth.hasAccounts(mockAuth)).toBeTruthy();
+    });
+
+    it('should validate false', () => {
+      expect(
+        auth.hasAccounts({
+          accounts: [],
+        }),
+      ).toBeFalsy();
+    });
+  });
+
+  describe('hasMultipleAccounts', () => {
+    it('should return true', () => {
+      expect(auth.hasMultipleAccounts(mockAuth)).toBeTruthy();
+    });
+
+    it('should validate false', () => {
+      expect(
+        auth.hasMultipleAccounts({
+          accounts: [],
+        }),
+      ).toBeFalsy();
+      expect(
+        auth.hasMultipleAccounts({
+          accounts: [mockGitHubCloudAccount],
+        }),
+      ).toBeFalsy();
+    });
+  });
 });