Repository: gitify-app/gitify
Pull request #1405 (2024-07-23 23:54:20)

feat(developer): reset application
Partially closes https://github.com/gitify-app/gitify/issues/980

Builds upon the former `logout` logic, now unused. 


Patch:
diff --git a/.nvmrc b/.nvmrc
index 119f15a0..593cb75b 100644
--- a/.nvmrc
+++ b/.nvmrc
@@ -1,1 +1,1 @@
-20.15.1
\ No newline at end of file
+20.16.0
\ No newline at end of file
diff --git a/package.json b/package.json
index f60561b1..a557c0e3 100644
--- a/package.json
+++ b/package.json
@@ -106,8 +106,8 @@
     "class-variance-authority": "0.7.0",
     "clsx": "2.1.1",
     "date-fns": "3.6.0",
-    "electron-log": "5.1.6",
-    "electron-updater": "6.3.0",
+    "electron-log": "5.1.7",
+    "electron-updater": "6.3.1",
     "final-form": "4.20.10",
     "menubar": "9.5.0",
     "nprogress": "0.2.0",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index e34ffb31..ce634c32 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -27,11 +27,11 @@ importers:
         specifier: 3.6.0
         version: 3.6.0
       electron-log:
-        specifier: 5.1.6
-        version: 5.1.6
+        specifier: 5.1.7
+        version: 5.1.7
       electron-updater:
-        specifier: 6.3.0
-        version: 6.3.0
+        specifier: 6.3.1
+        version: 6.3.1
       final-form:
         specifier: 4.20.10
         version: 4.20.10
@@ -1376,8 +1376,8 @@ packages:
     engines: {node: '>=14.0.0'}
     hasBin: true
 
-  electron-log@5.1.6:
-    resolution: {integrity: sha512-o7+gNnlth67wexW7ym4niGAghXE5GZ7J1bhdNBSnqGeiMQwJ0Mhr7/HtDw2m/2thCngaOsvfjU5FDZs81TCcXw==}
+  electron-log@5.1.7:
+    resolution: {integrity: sha512-/PjrS9zGkrZCDTHt6IgNE3FeciBbi4wd7U76NG9jAoNXF99E9IJdvBkqvaUJ1NjLojYDKs0kTvn9YhKy1/Zi+Q==}
     engines: {node: '>= 14'}
 
   electron-positioner@4.1.0:
@@ -1389,8 +1389,8 @@ packages:
   electron-to-chromium@1.4.719:
     resolution: {integrity: sha512-FbWy2Q2YgdFzkFUW/W5jBjE9dj+804+98E4Pup78JBPnbdb3pv6IneY2JCPKdeKLh3AOKHQeYf+KwLr7mxGh6Q==}
 
-  electron-updater@6.3.0:
-    resolution: {integrity: sha512-3Xlezhk+dKaSQrOnkQNqCGiuGSSUPO9BV9TQZ4Iig6AyTJ4FzJONE5gFFc382sY53Sh9dwJfzKsA3DxRHt2btw==}
+  electron-updater@6.3.1:
+    resolution: {integrity: sha512-Et9t9LcH42q/A73yu398YGHR7cbdTnHbIyFN/p0FNZIJi10XxGshZBVVE6FsClMuoggw7FVhn1+kMUVMCqS7MA==}
 
   electron@31.2.1:
     resolution: {integrity: sha512-g3CLKjl4yuXt6VWm/KpgEjYYhFiCl19RgUn8lOC8zV/56ZXAS3+mqV4wWzicE/7vSYXs6GRO7vkYRwrwhX3Gaw==}
@@ -4743,7 +4743,7 @@ snapshots:
       - electron-builder-squirrel-windows
       - supports-color
 
-  electron-log@5.1.6: {}
+  electron-log@5.1.7: {}
 
   electron-positioner@4.1.0: {}
 
@@ -4761,7 +4761,7 @@ snapshots:
 
   electron-to-chromium@1.4.719: {}
 
-  electron-updater@6.3.0:
+  electron-updater@6.3.1:
     dependencies:
       builder-util-runtime: 9.2.5
       fs-extra: 10.1.0
diff --git a/renovate.json b/renovate.json
index 75c5bee5..6e1f62fe 100644
--- a/renovate.json
+++ b/renovate.json
@@ -4,6 +4,7 @@
     "config:recommended",
     ":separateMultipleMajorReleases",
     ":enableVulnerabilityAlerts",
+    "schedule:weekly",
     "regexManagers:biomeVersions"
   ],
   "labels": ["dependency"],
diff --git a/src/context/App.tsx b/src/context/App.tsx
index 5c4bb409..4b57a36c 100644
--- a/src/context/App.tsx
+++ b/src/context/App.tsx
@@ -1,4 +1,4 @@
-import { webFrame } from 'electron';
+import { ipcRenderer, webFrame } from 'electron';
 import {
   type ReactNode,
   createContext,
@@ -97,7 +97,6 @@ interface AppContextState {
   loginWithOAuthApp: (data: LoginOAuthAppOptions) => void;
   loginWithPersonalAccessToken: (data: LoginPersonalAccessTokenOptions) => void;
   logoutFromAccount: (account: Account) => void;
-  logout: () => void;
 
   notifications: AccountNotifications[];
   status: Status;
@@ -163,6 +162,13 @@ export const AppProvider = ({ children }: { children: ReactNode }) => {
     setKeyboardShortcut(settings.keyboardShortcut);
   }, [settings.keyboardShortcut]);
 
+  useEffect(() => {
+    ipcRenderer.on('gitify:reset-app', () => {
+      setAuth(defaultAuth);
+      clearState();
+    });
+  }, []);
+
   const clearFilters = useCallback(() => {
     const newSettings = { ...settings, ...defaultFilters };
     setSettings(newSettings);
@@ -239,11 +245,6 @@ export const AppProvider = ({ children }: { children: ReactNode }) => {
     [auth, settings],
   );
 
-  const logout = useCallback(() => {
-    setAuth(defaultAuth);
-    clearState();
-  }, []);
-
   const restoreSettings = useCallback(async () => {
     await migrateAuthenticatedAccounts();
 
@@ -307,7 +308,6 @@ export const AppProvider = ({ children }: { children: ReactNode }) => {
         loginWithOAuthApp,
         loginWithPersonalAccessToken,
         logoutFromAccount,
-        logout,
 
         notifications,
         status,
diff --git a/src/electron/first-run.js b/src/electron/first-run.js
index d93826db..d5381d7d 100644
--- a/src/electron/first-run.js
+++ b/src/electron/first-run.js
@@ -49,7 +49,7 @@ function isFirstRun() {
 
     fs.writeFileSync(configPath, '');
   } catch (error) {
-    log.warn('First run: Unable to write firstRun file', error);
+    log.error('First run: Unable to write firstRun file', error);
   }
 
   return true;
diff --git a/src/electron/main.js b/src/electron/main.js
index 58994bcb..334112cd 100644
--- a/src/electron/main.js
+++ b/src/electron/main.js
@@ -4,6 +4,7 @@ const {
   nativeTheme,
   globalShortcut,
   Menu,
+  dialog,
 } = require('electron/main');
 const { menubar } = require('menubar');
 const { autoUpdater } = require('electron-updater');
@@ -50,6 +51,12 @@ const contextMenu = Menu.buildFromTemplate([
         accelerator:
           process.platform === 'darwin' ? 'Alt+Cmd+I' : 'Ctrl+Shift+I',
       },
+      {
+        label: 'Reset App',
+        click: () => {
+          resetApp();
+        },
+      },
     ],
   },
   { type: 'separator' },
@@ -62,17 +69,17 @@ const contextMenu = Menu.buildFromTemplate([
   },
 ]);
 
+const mb = menubar({
+  icon: idleIcon,
+  index: `file://${__dirname}/index.html`,
+  browserWindow: browserWindowOpts,
+  preloadWindow: true,
+  showDockIcon: false,
+});
+
 app.whenReady().then(async () => {
   await onFirstRunMaybe();
 
-  const mb = menubar({
-    icon: idleIcon,
-    index: `file://${__dirname}/index.html`,
-    browserWindow: browserWindowOpts,
-    preloadWindow: true,
-    showDockIcon: false,
-  });
-
   mb.on('ready', () => {
     autoUpdater.checkForUpdatesAndNotify();
 
@@ -168,3 +175,24 @@ app.whenReady().then(async () => {
     app.setLoginItemSettings(settings);
   });
 });
+
+function resetApp() {
+  const cancelButtonId = 0;
+
+  const response = dialog.showMessageBoxSync(mb.window, {
+    type: 'warning',
+    title: 'Reset Gitify',
+    message:
+      'Are you sure you want to reset Gitify? You will be logged out of all accounts',
+    buttons: ['Cancel', 'Reset'],
+    defaultId: cancelButtonId,
+    cancelId: cancelButtonId,
+  });
+
+  if (response === cancelButtonId) {
+    return;
+  }
+
+  mb.window.webContents.send('gitify:reset-app');
+  mb.app.quit();
+}
diff --git a/src/utils/api/utils.ts b/src/utils/api/utils.ts
index 52fdf25f..644d9f33 100644
--- a/src/utils/api/utils.ts
+++ b/src/utils/api/utils.ts
@@ -1,11 +1,11 @@
 import type { Hostname } from '../../types';
 import Constants from '../constants';
-import { isEnterpriseHost } from '../helpers';
+import { isEnterpriseServerHost } from '../helpers';
 
 export function getGitHubAPIBaseUrl(hostname: Hostname): URL {
   const url = new URL(Constants.GITHUB_API_BASE_URL);
 
-  if (isEnterpriseHost(hostname)) {
+  if (isEnterpriseServerHost(hostname)) {
     url.hostname = hostname;
     url.pathname = '/api/v3/';
   }
@@ -15,7 +15,7 @@ export function getGitHubAPIBaseUrl(hostname: Hostname): URL {
 export function getGitHubGraphQLUrl(hostname: Hostname): URL {
   const url = new URL(Constants.GITHUB_API_GRAPHQL_URL);
 
-  if (isEnterpriseHost(hostname)) {
+  if (isEnterpriseServerHost(hostname)) {
     url.hostname = hostname;
     url.pathname = '/api/graphql';
   }
diff --git a/src/utils/helpers.ts b/src/utils/helpers.ts
index 31f047f0..a0724bfe 100644
--- a/src/utils/helpers.ts
+++ b/src/utils/helpers.ts
@@ -17,7 +17,7 @@ export function getPlatformFromHostname(hostname: string): PlatformType {
     : 'GitHub Enterprise Server';
 }
 
-export function isEnterpriseHost(hostname: Hostname): boolean {
+export function isEnterpriseServerHost(hostname: Hostname): boolean {
   return !hostname.endsWith(Constants.DEFAULT_AUTH_OPTIONS.hostname);
 }
 


Test patch:
diff --git a/src/context/App.test.tsx b/src/context/App.test.tsx
index 32bbc23a..4e49f7fe 100644
--- a/src/context/App.test.tsx
+++ b/src/context/App.test.tsx
@@ -304,28 +304,6 @@ describe('context/App.tsx', () => {
     });
   });
 
-  it('should call logout', async () => {
-    const clearStateMock = jest.spyOn(storage, 'clearState');
-
-    const TestComponent = () => {
-      const { logout } = useContext(AppContext);
-
-      return (
-        <button type="button" onClick={logout}>
-          Test Case
-        </button>
-      );
-    };
-
-    const { getByText } = customRender(<TestComponent />);
-
-    act(() => {
-      fireEvent.click(getByText('Test Case'));
-    });
-
-    expect(clearStateMock).toHaveBeenCalledTimes(1);
-  });
-
   describe('settings methods', () => {
     const fetchNotificationsMock = jest.fn();
 
diff --git a/src/utils/helpers.test.ts b/src/utils/helpers.test.ts
index 49c83bfe..805c740b 100644
--- a/src/utils/helpers.test.ts
+++ b/src/utils/helpers.test.ts
@@ -16,7 +16,7 @@ import {
   generateNotificationReferrerId,
   getFilterCount,
   getPlatformFromHostname,
-  isEnterpriseHost,
+  isEnterpriseServerHost,
 } from './helpers';
 
 describe('utils/helpers.ts', () => {
@@ -40,15 +40,19 @@ describe('utils/helpers.ts', () => {
     });
   });
 
-  describe('isEnterpriseHost', () => {
+  describe('isEnterpriseServerHost', () => {
     it('should return true for enterprise host', () => {
-      expect(isEnterpriseHost('github.gitify.app' as Hostname)).toBe(true);
-      expect(isEnterpriseHost('api.github.gitify.app' as Hostname)).toBe(true);
+      expect(isEnterpriseServerHost('github.gitify.app' as Hostname)).toBe(
+        true,
+      );
+      expect(isEnterpriseServerHost('api.github.gitify.app' as Hostname)).toBe(
+        true,
+      );
     });
 
     it('should return false for non-enterprise host', () => {
-      expect(isEnterpriseHost('github.com' as Hostname)).toBe(false);
-      expect(isEnterpriseHost('api.github.com' as Hostname)).toBe(false);
+      expect(isEnterpriseServerHost('github.com' as Hostname)).toBe(false);
+      expect(isEnterpriseServerHost('api.github.com' as Hostname)).toBe(false);
     });
   });