diff --git a/biome.json b/biome.json index b417c560c..8b73637ee 100644 --- a/biome.json +++ b/biome.json @@ -7,6 +7,10 @@ "enabled": true, "rules": { "recommended": true, + "nursery": { + "noUnusedFunctionParameters": "error", + "useDefaultSwitchClause": "error" + }, "correctness": { "useExhaustiveDependencies": { "level": "warn", diff --git a/src/__mocks__/electron.js b/src/__mocks__/electron.js index 89c50b418..7d4f4f34e 100644 --- a/src/__mocks__/electron.js +++ b/src/__mocks__/electron.js @@ -32,7 +32,7 @@ module.exports = { send: jest.fn(), on: jest.fn(), sendSync: jest.fn(), - invoke: jest.fn((channel, ...args) => { + invoke: jest.fn((channel, ..._args) => { switch (channel) { case 'get-platform': return Promise.resolve('darwin'); diff --git a/src/components/AccountNotifications.tsx b/src/components/AccountNotifications.tsx index 34179e056..b11d05e5b 100644 --- a/src/components/AccountNotifications.tsx +++ b/src/components/AccountNotifications.tsx @@ -84,7 +84,6 @@ export const AccountNotifications = (props: IProps) => { return ( diff --git a/src/components/NotificationRow.tsx b/src/components/NotificationRow.tsx index febe971c9..975d09d94 100644 --- a/src/components/NotificationRow.tsx +++ b/src/components/NotificationRow.tsx @@ -16,7 +16,7 @@ import { useState, } from 'react'; import { AppContext } from '../context/App'; -import { type Account, IconColor } from '../types'; +import { IconColor } from '../types'; import type { Notification } from '../typesGitHub'; import { cn } from '../utils/cn'; import { @@ -33,19 +33,16 @@ import { formatReason } from '../utils/reason'; import { PillButton } from './buttons/PillButton'; interface IProps { - account: Account; notification: Notification; } -export const NotificationRow: FC = ({ notification, account }) => { +export const NotificationRow: FC = ({ notification }) => { const { - auth, settings, removeNotificationFromState, markNotificationRead, markNotificationDone, unsubscribeNotification, - notifications, } = useContext(AppContext); const [animateExit, setAnimateExit] = useState(false); @@ -59,8 +56,12 @@ export const NotificationRow: FC = ({ notification, account }) => { // no need to mark as read, github does it by default when opening it removeNotificationFromState(settings, notification); } - }, [notifications, notification, auth, settings]); // notifications required here to prevent weird state issues - + }, [ + notification, + markNotificationDone, + removeNotificationFromState, + settings, + ]); const unsubscribeFromThread = (event: MouseEvent) => { // Don't trigger onClick of parent element. event.stopPropagation(); diff --git a/src/components/Repository.tsx b/src/components/Repository.tsx index 187f10cd2..ace1d48a4 100644 --- a/src/components/Repository.tsx +++ b/src/components/Repository.tsx @@ -1,13 +1,11 @@ import { CheckIcon, MarkGithubIcon, ReadIcon } from '@primer/octicons-react'; import { type FC, useCallback, useContext } from 'react'; import { AppContext } from '../context/App'; -import type { Account } from '../types'; import type { Notification } from '../typesGitHub'; import { openRepository } from '../utils/links'; import { NotificationRow } from './NotificationRow'; interface IProps { - account: Account; repoNotifications: Notification[]; repoName: string; } @@ -15,18 +13,17 @@ interface IProps { export const RepositoryNotifications: FC = ({ repoName, repoNotifications, - account, }) => { const { markRepoNotificationsRead, markRepoNotificationsDone } = useContext(AppContext); const markRepoAsRead = useCallback(() => { markRepoNotificationsRead(repoNotifications[0]); - }, [repoNotifications, account]); + }, [repoNotifications, markRepoNotificationsRead]); const markRepoAsDone = useCallback(() => { markRepoNotificationsDone(repoNotifications[0]); - }, [repoNotifications, account]); + }, [repoNotifications, markRepoNotificationsDone]); const avatarUrl = repoNotifications[0].repository.owner.avatar_url; const repoSlug = repoNotifications[0].repository.full_name; @@ -77,7 +74,7 @@ export const RepositoryNotifications: FC = ({ {repoNotifications.map((obj) => ( - + ))} ); diff --git a/src/context/App.tsx b/src/context/App.tsx index 04dadd821..c9fa4e19f 100644 --- a/src/context/App.tsx +++ b/src/context/App.tsx @@ -129,7 +129,6 @@ export const AppProvider = ({ children }: { children: ReactNode }) => { fetchNotifications({ auth, settings }); }, Constants.FETCH_INTERVAL); - // biome-ignore lint/correctness/useExhaustiveDependencies: We need to update tray title when settings or notifications changes. useEffect(() => { const count = getNotificationCount(notifications); @@ -227,37 +226,37 @@ export const AppProvider = ({ children }: { children: ReactNode }) => { const fetchNotificationsWithAccounts = useCallback( async () => await fetchNotifications({ auth, settings }), - [auth, settings, notifications], + [auth, settings, fetchNotifications], ); const markNotificationReadWithAccounts = useCallback( async (notification: Notification) => await markNotificationRead({ auth, settings }, notification), - [auth, notifications], + [auth, settings, markNotificationRead], ); const markNotificationDoneWithAccounts = useCallback( async (notification: Notification) => await markNotificationDone({ auth, settings }, notification), - [auth, notifications], + [auth, settings, markNotificationDone], ); const unsubscribeNotificationWithAccounts = useCallback( async (notification: Notification) => await unsubscribeNotification({ auth, settings }, notification), - [auth, notifications], + [auth, settings, unsubscribeNotification], ); const markRepoNotificationsReadWithAccounts = useCallback( async (notification: Notification) => await markRepoNotificationsRead({ auth, settings }, notification), - [auth, notifications], + [auth, settings, markRepoNotificationsRead], ); const markRepoNotificationsDoneWithAccounts = useCallback( async (notification: Notification) => await markRepoNotificationsDone({ auth, settings }, notification), - [auth, notifications], + [auth, settings, markRepoNotificationsDone], ); return ( diff --git a/src/hooks/useNotifications.ts b/src/hooks/useNotifications.ts index d2c54b78b..2b4b604c4 100644 --- a/src/hooks/useNotifications.ts +++ b/src/hooks/useNotifications.ts @@ -150,11 +150,11 @@ export const useNotifications = (): NotificationsState => { setStatus('success'); } }, - [notifications], + [markNotificationRead], ); const markRepoNotificationsRead = useCallback( - async (state: GitifyState, notification: Notification) => { + async (_state: GitifyState, notification: Notification) => { setStatus('loading'); const repoSlug = notification.repository.full_name; @@ -220,7 +220,7 @@ export const useNotifications = (): NotificationsState => { setStatus('success'); } }, - [notifications], + [notifications, markNotificationDone], ); const removeNotificationFromState = useCallback( diff --git a/src/routes/Accounts.tsx b/src/routes/Accounts.tsx index 616f152fd..9f23377a6 100644 --- a/src/routes/Accounts.tsx +++ b/src/routes/Accounts.tsx @@ -27,12 +27,15 @@ export const AccountsRoute: FC = () => { const { auth, logoutFromAccount } = useContext(AppContext); const navigate = useNavigate(); - const logoutAccount = useCallback((account: Account) => { - logoutFromAccount(account); - navigate(-1); - updateTrayIcon(); - updateTrayTitle(); - }, []); + const logoutAccount = useCallback( + (account: Account) => { + logoutFromAccount(account); + navigate(-1); + updateTrayIcon(); + updateTrayTitle(); + }, + [logoutFromAccount], + ); const loginWithPersonalAccessToken = useCallback(() => { return navigate('/login-personal-access-token', { replace: true }); diff --git a/src/routes/LoginWithOAuthApp.tsx b/src/routes/LoginWithOAuthApp.tsx index 07f0c9d7a..53fd3d5f6 100644 --- a/src/routes/LoginWithOAuthApp.tsx +++ b/src/routes/LoginWithOAuthApp.tsx @@ -116,13 +116,16 @@ export const LoginWithOAuthApp: FC = () => { ); }; - const login = useCallback(async (data: IValues) => { - try { - await loginWithOAuthApp(data as LoginOAuthAppOptions); - } catch (err) { - // Skip - } - }, []); + const login = useCallback( + async (data: IValues) => { + try { + await loginWithOAuthApp(data as LoginOAuthAppOptions); + } catch (err) { + // Skip + } + }, + [loginWithOAuthApp], + ); return (
diff --git a/src/routes/LoginWithPersonalAccessToken.tsx b/src/routes/LoginWithPersonalAccessToken.tsx index 46b85f927..938a8f8d0 100644 --- a/src/routes/LoginWithPersonalAccessToken.tsx +++ b/src/routes/LoginWithPersonalAccessToken.tsx @@ -120,17 +120,20 @@ export const LoginWithPersonalAccessToken: FC = () => { ); }; - const login = useCallback(async (data: IValues) => { - setIsValidToken(true); - try { - await loginWithPersonalAccessToken( - data as LoginPersonalAccessTokenOptions, - ); - navigate(-1); - } catch (err) { - setIsValidToken(false); - } - }, []); + const login = useCallback( + async (data: IValues) => { + setIsValidToken(true); + try { + await loginWithPersonalAccessToken( + data as LoginPersonalAccessTokenOptions, + ); + navigate(-1); + } catch (err) { + setIsValidToken(false); + } + }, + [loginWithPersonalAccessToken], + ); return (
diff --git a/src/utils/auth/utils.test.ts b/src/utils/auth/utils.test.ts index f120fd49a..5648a4599 100644 --- a/src/utils/auth/utils.test.ts +++ b/src/utils/auth/utils.test.ts @@ -3,6 +3,7 @@ import type { AxiosPromise, AxiosResponse } from 'axios'; import { mockAuth, mockGitHubCloudAccount } from '../../__mocks__/state-mocks'; import type { Account, AuthState } from '../../types'; import * as apiRequests from '../api/request'; +import type { AuthMethod } from './types'; import * as auth from './utils'; import { getNewOAuthAppURL, getNewTokenURL } from './utils'; @@ -222,18 +223,27 @@ describe('utils/auth/utils.ts', () => { method: 'GitHub App', } as Account), ).toBe('https://github.com/settings/apps'); + expect( auth.getDeveloperSettingsURL({ hostname: 'github.com', method: 'OAuth App', } as Account), ).toBe('https://github.com/settings/developers'); + expect( auth.getDeveloperSettingsURL({ hostname: 'github.com', method: 'Personal Access Token', } as Account), ).toBe('https://github.com/settings/tokens'); + + expect( + auth.getDeveloperSettingsURL({ + hostname: 'github.com', + method: 'unknown' as AuthMethod, + } as Account), + ).toBe('https://github.com/settings'); }); describe('getNewTokenURL', () => { diff --git a/src/utils/auth/utils.ts b/src/utils/auth/utils.ts index a6c3f2122..151b29c5f 100644 --- a/src/utils/auth/utils.ts +++ b/src/utils/auth/utils.ts @@ -53,7 +53,7 @@ export const authGitHub = ( authWindow.webContents.on( 'did-fail-load', - (event, errorCode, errorDescription, validatedURL) => { + (_event, _errorCode, _errorDescription, validatedURL) => { if (validatedURL.includes(authOptions.hostname)) { authWindow.destroy(); reject( @@ -151,6 +151,9 @@ export function getDeveloperSettingsURL(account: Account): string { case 'Personal Access Token': settingsURL.pathname = '/settings/tokens'; break; + default: + settingsURL.pathname = '/settings'; + break; } return settingsURL.toString(); } diff --git a/src/utils/theme.test.ts b/src/utils/theme.test.ts index 0c54200c4..9018ac869 100644 --- a/src/utils/theme.test.ts +++ b/src/utils/theme.test.ts @@ -21,7 +21,7 @@ describe('utils/theme.ts', () => { it("should use the system's mode - light", () => { Object.defineProperty(window, 'matchMedia', { writable: true, - value: jest.fn().mockImplementation((query) => ({ + value: jest.fn().mockImplementation((_query) => ({ matches: false, })), }); @@ -32,7 +32,7 @@ describe('utils/theme.ts', () => { it("should use the system's mode - dark", () => { Object.defineProperty(window, 'matchMedia', { writable: true, - value: jest.fn().mockImplementation((query) => ({ + value: jest.fn().mockImplementation((_query) => ({ matches: true, })), });