From 72bfcad03469440c1e9cf5ff88b5ff97a544c45e Mon Sep 17 00:00:00 2001 From: Patrick Loser Date: Sun, 26 Jul 2026 18:37:53 +0200 Subject: [PATCH] fix: Resolve Type and Lint Issues --- eslint.config.js | 67 ++++++++++--------- .../src/NativeVolumeButtonListenerModule.ts | 6 ++ .../src/NativeZipArchiveModule.ts | 6 +- src/components/Button/Button.tsx | 6 +- src/components/ErrorView/ErrorView.tsx | 2 +- src/components/__tests__/Button.test.tsx | 1 - src/components/__tests__/EmptyView.test.tsx | 1 - .../__tests__/LoadingMoreIndicator.test.tsx | 1 - src/database/__tests__/db.test.ts | 2 +- .../queries/__tests__/NovelQueries.test.ts | 12 ++-- .../queries/__tests__/StatsQueries.test.ts | 14 ++-- src/database/queries/__tests__/setup.ts | 2 +- src/hooks/common/useGithubUpdateChecker.ts | 1 + src/hooks/persisted/useTrackedNovel.ts | 1 + src/i18n/translations.ts | 12 ++-- src/navigators/ReaderStack.tsx | 8 +-- src/navigators/types/index.ts | 1 + .../BrowseSourceScreen/useBrowseSource.ts | 2 + .../GlobalSearchScreen/GlobalSearchScreen.tsx | 2 +- .../hooks/useGlobalSearch.ts | 2 +- src/screens/WebviewScreen/WebviewScreen.tsx | 4 +- .../WebviewScreen/components/Appbar.tsx | 2 +- src/screens/WebviewScreen/components/Menu.tsx | 2 +- src/screens/browse/SourceNovels.tsx | 1 - .../browse/discover/AniListTopNovels.tsx | 3 +- src/screens/browse/discover/MalTopNovels.tsx | 3 +- .../browse/migration/MigrationSourceItem.tsx | 1 - .../library/components/LibraryNovelItem.tsx | 4 +- src/screens/library/hooks/useLibrary.ts | 8 +-- src/screens/more/About.tsx | 22 +++--- src/screens/more/MoreScreen.tsx | 2 +- src/screens/more/TaskQueueScreen.tsx | 3 +- src/screens/more/components/MoreHeader.tsx | 1 - src/screens/reader/ChapterContext.tsx | 2 +- src/screens/reader/ReaderScreen.tsx | 3 + .../reader/components/ChapterDrawer/index.tsx | 8 +-- .../reader/components/WebViewReader.tsx | 39 +++++------ src/screens/reader/hooks/useTimeTracking.ts | 20 ++++-- src/screens/reader/hooks/useTtsSession.ts | 2 +- .../Components/GoogleDriveModal.tsx | 1 + .../settings/SettingsBackupScreen/index.tsx | 1 - .../modals/DownloadCooldownModal.tsx | 1 + .../SettingsReaderScreen.tsx | 8 +-- .../components/RepositoryCard.tsx | 2 +- .../settings/SettingsTrackerScreen.tsx | 13 ++-- src/services/Trackers/aniList.ts | 4 +- src/services/Trackers/myAnimeList.ts | 4 +- 47 files changed, 167 insertions(+), 146 deletions(-) diff --git a/eslint.config.js b/eslint.config.js index 2739e524..0f106f20 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,61 +1,62 @@ const { defineConfig, globalIgnores } = require('eslint/config'); -const expoConfig = require("eslint-config-expo/flat"); -const { FlatCompat } = require("@eslint/eslintrc"); +const expoConfig = require('eslint-config-expo/flat'); +const { FlatCompat } = require('@eslint/eslintrc'); const compat = new FlatCompat({ baseDirectory: __dirname, }); const testFiles = [ - "**/__tests__/**/*.[jt]s?(x)", - "**/?(*.)+(spec|test).[jt]s?(x)", + '**/__tests__/**/*.[jt]s?(x)', + '**/?(*.)+(spec|test).[jt]s?(x)', ]; module.exports = defineConfig([ globalIgnores([ - "android/**", - "ios/**", - "node_modules/**", - ".expo/**", - "dist/**", - "coverage/**", + 'android/**', + 'ios/**', + 'node_modules/**', + '.expo/**', + 'dist/**', + 'coverage/**', ]), expoConfig, ...compat - .extends("plugin:testing-library/react", "plugin:jest/recommended") - .map((config) => ({ + .extends('plugin:testing-library/react', 'plugin:jest/recommended') + .map(config => ({ ...config, files: testFiles, })), { - files: ["**/*.{js,jsx,ts,tsx}"], + files: ['**/*.{js,jsx,ts,tsx}'], rules: { - "no-shadow": "off", - "no-undef": "off", - "no-console": "error", + 'no-shadow': 'off', + 'no-undef': 'off', + 'no-console': 'error', - "@typescript-eslint/no-shadow": "warn", + '@typescript-eslint/no-shadow': 'warn', - "react-hooks/exhaustive-deps": "warn", - "react-hooks/static-components": "warn", - "react-hooks/purity": "warn", - "react-hooks/set-state-in-effect": "warn", - "react-hooks/refs": "warn", - "react-hooks/immutability": "warn", - "react/no-children-prop": "warn", + 'react-hooks/exhaustive-deps': 'warn', + 'react-hooks/static-components': 'warn', + 'react-hooks/purity': 'warn', + 'react-hooks/set-state-in-effect': 'warn', + 'react-hooks/refs': 'warn', + 'react-hooks/immutability': 'warn', + 'react/no-children-prop': 'warn', - "react/display-name": "off", + 'react/display-name': 'off', - curly: ["error", "multi-line", "consistent"], - "no-useless-return": "error", - "block-scoped-var": "error", - "no-var": "error", - "prefer-const": "error", - "no-dupe-else-if": "error", - "no-duplicate-imports": "error", + curly: ['error', 'multi-line', 'consistent'], + 'no-useless-return': 'error', + 'block-scoped-var': 'error', + 'no-var': 'error', + 'prefer-const': 'error', + 'no-dupe-else-if': 'error', + 'no-duplicate-imports': 'error', - "@react-native/no-deep-imports": "off", + '@react-native/no-deep-imports': 'off', + '@typescript-eslint/no-require-imports': 'off', }, }, ]); diff --git a/modules/native-volume-button-listener/src/NativeVolumeButtonListenerModule.ts b/modules/native-volume-button-listener/src/NativeVolumeButtonListenerModule.ts index 5d972585..78999b1f 100644 --- a/modules/native-volume-button-listener/src/NativeVolumeButtonListenerModule.ts +++ b/modules/native-volume-button-listener/src/NativeVolumeButtonListenerModule.ts @@ -9,6 +9,12 @@ declare class NativeVolumeButtonListenerModule extends NativeModule( + 'NativeVolumeButtonListener', +); + setActive(active: boolean): void; +} + export default requireNativeModule( 'NativeVolumeButtonListener', ); diff --git a/modules/native-zip-archive/src/NativeZipArchiveModule.ts b/modules/native-zip-archive/src/NativeZipArchiveModule.ts index e94f04ac..cd62ad6f 100644 --- a/modules/native-zip-archive/src/NativeZipArchiveModule.ts +++ b/modules/native-zip-archive/src/NativeZipArchiveModule.ts @@ -7,12 +7,12 @@ type NativeZipArchiveModule = { distDirPath: string, urlString: string, headers: Record, - ): Promise; + ): Promise; remoteZip( sourceDirPath: string, urlString: string, headers: Record, - ): Promise; + ): Promise; }; -export default requireNativeModule('NativeZipArchive'); \ No newline at end of file +export default requireNativeModule('NativeZipArchive'); diff --git a/src/components/Button/Button.tsx b/src/components/Button/Button.tsx index c531c1cb..3bc26054 100644 --- a/src/components/Button/Button.tsx +++ b/src/components/Button/Button.tsx @@ -19,7 +19,11 @@ const Button: React.FC = props => { [props.children, props.title], ); - return ; + return ( + + {Children} + + ); }; export default React.memo(Button); diff --git a/src/components/ErrorView/ErrorView.tsx b/src/components/ErrorView/ErrorView.tsx index 84bfacfe..85921dd7 100644 --- a/src/components/ErrorView/ErrorView.tsx +++ b/src/components/ErrorView/ErrorView.tsx @@ -1,5 +1,5 @@ import { ThemeColors } from '@theme/types'; -import React from 'react'; + import { Pressable, StyleSheet, Text, View } from 'react-native'; import { IconButton } from 'react-native-paper'; diff --git a/src/components/__tests__/Button.test.tsx b/src/components/__tests__/Button.test.tsx index 04ebea65..154a0987 100644 --- a/src/components/__tests__/Button.test.tsx +++ b/src/components/__tests__/Button.test.tsx @@ -1,5 +1,4 @@ import './mocks'; -import React from 'react'; import { render, screen, fireEvent } from '@testing-library/react-native'; import { Text } from 'react-native'; import Button from '../Button/Button'; diff --git a/src/components/__tests__/EmptyView.test.tsx b/src/components/__tests__/EmptyView.test.tsx index bde84450..e12c8b96 100644 --- a/src/components/__tests__/EmptyView.test.tsx +++ b/src/components/__tests__/EmptyView.test.tsx @@ -1,5 +1,4 @@ import './mocks'; -import React from 'react'; import { render, screen } from '@testing-library/react-native'; import { Text } from 'react-native'; import EmptyView from '../EmptyView'; diff --git a/src/components/__tests__/LoadingMoreIndicator.test.tsx b/src/components/__tests__/LoadingMoreIndicator.test.tsx index 6d6d9f66..856c43ae 100644 --- a/src/components/__tests__/LoadingMoreIndicator.test.tsx +++ b/src/components/__tests__/LoadingMoreIndicator.test.tsx @@ -1,4 +1,3 @@ -import React from 'react'; import { render } from '@testing-library/react-native'; import LoadingMoreIndicator from '../LoadingMoreIndicator/LoadingMoreIndicator'; import { ThemeColors } from '../../theme/types'; diff --git a/src/database/__tests__/db.test.ts b/src/database/__tests__/db.test.ts index f8313cd1..bd9491cb 100644 --- a/src/database/__tests__/db.test.ts +++ b/src/database/__tests__/db.test.ts @@ -240,7 +240,7 @@ describe('production migrations', () => { const appliedMigrations = sqlite.executeSync( 'SELECT name FROM __drizzle_migrations ORDER BY created_at', - ).rows as Array<{ name: string }>; + ).rows as { name: string }[]; expect(appliedMigrations.map(row => row.name)).toEqual([ '20251222152612_past_mandrill', '20260612232322_normal_saracen', diff --git a/src/database/queries/__tests__/NovelQueries.test.ts b/src/database/queries/__tests__/NovelQueries.test.ts index 485c5422..3aaba46f 100644 --- a/src/database/queries/__tests__/NovelQueries.test.ts +++ b/src/database/queries/__tests__/NovelQueries.test.ts @@ -15,12 +15,6 @@ import { import { categorySchema, novelCategorySchema } from '@database/schema'; import { eq, sql } from 'drizzle-orm'; -const mockGetLibraryDefaultCategoryId = jest.fn(); - -jest.mock('@hooks/persisted/useSettings', () => ({ - getLibraryDefaultCategoryId: () => mockGetLibraryDefaultCategoryId(), -})); - import { getAllNovels, getNovelById, @@ -37,6 +31,12 @@ import { updateNovelCategories, } from '../NovelQueries'; +const mockGetLibraryDefaultCategoryId = jest.fn(); + +jest.mock('@hooks/persisted/useSettings', () => ({ + getLibraryDefaultCategoryId: () => mockGetLibraryDefaultCategoryId(), +})); + describe('NovelQueries', () => { beforeEach(() => { const testDb = setupTestDatabase(); diff --git a/src/database/queries/__tests__/StatsQueries.test.ts b/src/database/queries/__tests__/StatsQueries.test.ts index 0d607880..51949680 100644 --- a/src/database/queries/__tests__/StatsQueries.test.ts +++ b/src/database/queries/__tests__/StatsQueries.test.ts @@ -346,12 +346,12 @@ describe('StatsQueries', () => { const result = await getTopNovelsByTimeSpentFromDb(); expect(result.topNovelsByTimeSpent).toHaveLength(2); - expect(result.topNovelsByTimeSpent!![0]).toMatchObject({ + expect(result.topNovelsByTimeSpent![0]).toMatchObject({ id: novelId2, name: 'Novel B', timeSpent: 500, }); - expect(result.topNovelsByTimeSpent!![1]).toMatchObject({ + expect(result.topNovelsByTimeSpent![1]).toMatchObject({ id: novelId1, name: 'Novel A', timeSpent: 300, @@ -370,7 +370,7 @@ describe('StatsQueries', () => { const result = await getTopNovelsByTimeSpentFromDb(); expect(result.topNovelsByTimeSpent).toHaveLength(1); - expect(result.topNovelsByTimeSpent!![0].id).toBe(libraryNovelId); + expect(result.topNovelsByTimeSpent![0].id).toBe(libraryNovelId); }); it('should exclude novels with 0 or null time spent', async () => { @@ -401,7 +401,7 @@ describe('StatsQueries', () => { const result = await getTopNovelsByTimeSpentFromDb(); expect(result.topNovelsByTimeSpent).toHaveLength(10); - expect(result.topNovelsByTimeSpent!![0].name).toBe('Novel 12'); + expect(result.topNovelsByTimeSpent![0].name).toBe('Novel 12'); }); }); @@ -426,12 +426,12 @@ describe('StatsQueries', () => { const result = await getTopCategoriesByTimeSpentFromDb(); expect(result.topCategoriesByTimeSpent).toHaveLength(2); - expect(result.topCategoriesByTimeSpent!![0]).toMatchObject({ + expect(result.topCategoriesByTimeSpent![0]).toMatchObject({ id: scifiCat, name: 'Sci-Fi', timeSpent: 300, }); - expect(result.topCategoriesByTimeSpent!![1]).toMatchObject({ + expect(result.topCategoriesByTimeSpent![1]).toMatchObject({ id: fantasyCat, name: 'Fantasy', timeSpent: 100, @@ -455,7 +455,7 @@ describe('StatsQueries', () => { const result = await getTopCategoriesByTimeSpentFromDb(); expect(result.topCategoriesByTimeSpent).toHaveLength(1); - expect(result.topCategoriesByTimeSpent!![0].timeSpent).toBe(350); + expect(result.topCategoriesByTimeSpent![0].timeSpent).toBe(350); }); it('should return an empty array when no category chapters have spent time', async () => { diff --git a/src/database/queries/__tests__/setup.ts b/src/database/queries/__tests__/setup.ts index bb6d2b4c..59328645 100644 --- a/src/database/queries/__tests__/setup.ts +++ b/src/database/queries/__tests__/setup.ts @@ -7,7 +7,7 @@ // @ts-ignore import type { TestDb } from './testDb'; - +// @ts-ignore global.__DEV__ ??= false; const getTestDbModule = () => require('./testDb') as typeof import('./testDb'); diff --git a/src/hooks/common/useGithubUpdateChecker.ts b/src/hooks/common/useGithubUpdateChecker.ts index 23aa1f9a..ac8aa54f 100644 --- a/src/hooks/common/useGithubUpdateChecker.ts +++ b/src/hooks/common/useGithubUpdateChecker.ts @@ -115,6 +115,7 @@ export const useGithubUpdateChecker = (): GithubUpdate => { }; useEffect(() => { + // eslint-disable-next-line react-hooks/set-state-in-effect checkForRelease(); }, [checkForRelease]); diff --git a/src/hooks/persisted/useTrackedNovel.ts b/src/hooks/persisted/useTrackedNovel.ts index 5aec5da8..caa2924b 100644 --- a/src/hooks/persisted/useTrackedNovel.ts +++ b/src/hooks/persisted/useTrackedNovel.ts @@ -67,6 +67,7 @@ export const useTrackedNovel = (novelId: number | 'NO_ID') => { } }); + // eslint-disable-next-line react-hooks/set-state-in-effect setTrackedNovels(loadedNovels); }, [novelId, migrated, setMigrated]); diff --git a/src/i18n/translations.ts b/src/i18n/translations.ts index 20673719..318af511 100644 --- a/src/i18n/translations.ts +++ b/src/i18n/translations.ts @@ -9,12 +9,6 @@ import localizedFormat from 'dayjs/plugin/localizedFormat'; import relativeTime from 'dayjs/plugin/relativeTime'; import calendar from 'dayjs/plugin/calendar'; import duration from 'dayjs/plugin/duration'; -dayjs.extend(customParseFormat); -dayjs.extend(localeData); -dayjs.extend(localizedFormat); -dayjs.extend(relativeTime); -dayjs.extend(calendar); -dayjs.extend(duration); import 'dayjs/locale/ar'; import 'dayjs/locale/ca'; @@ -85,6 +79,12 @@ import zhTw from './languages/zh_TW/strings.json'; import { StringMap } from './types'; import { showToast } from '@utils/showToast'; +dayjs.extend(customParseFormat); +dayjs.extend(localeData); +dayjs.extend(localizedFormat); +dayjs.extend(relativeTime); +dayjs.extend(calendar); +dayjs.extend(duration); const i18n = new I18n({ af, diff --git a/src/navigators/ReaderStack.tsx b/src/navigators/ReaderStack.tsx index 67be4e32..5bb8e04a 100644 --- a/src/navigators/ReaderStack.tsx +++ b/src/navigators/ReaderStack.tsx @@ -1,4 +1,4 @@ -import React, { useRef } from 'react'; +import { useRef } from 'react'; import { createNativeStackNavigator } from '@react-navigation/native-stack'; @@ -20,13 +20,13 @@ const Stack = createNativeStackNavigator(); const ReaderStack = ({ route }) => { const params = useRef(route?.params); const theme = useTheme(); + // eslint-disable-next-line react-hooks/refs + const routeParams = route?.params ?? params.current; return ( { + // eslint-disable-next-line react-hooks/set-state-in-effect fetchNovels(currentPage, selectedFilters); }, [fetchNovels, currentPage, selectedFilters]); @@ -159,6 +160,7 @@ export const useSearchSource = (pluginId: string) => { useEffect(() => { if (searchText) { + // eslint-disable-next-line react-hooks/set-state-in-effect fetchNovels(searchText, currentPage); } }, [currentPage, fetchNovels, searchText]); diff --git a/src/screens/GlobalSearchScreen/GlobalSearchScreen.tsx b/src/screens/GlobalSearchScreen/GlobalSearchScreen.tsx index b73e987b..eb0524ad 100644 --- a/src/screens/GlobalSearchScreen/GlobalSearchScreen.tsx +++ b/src/screens/GlobalSearchScreen/GlobalSearchScreen.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import { useState } from 'react'; import { StyleSheet, View } from 'react-native'; import { ProgressBar } from 'react-native-paper'; diff --git a/src/screens/GlobalSearchScreen/hooks/useGlobalSearch.ts b/src/screens/GlobalSearchScreen/hooks/useGlobalSearch.ts index 368bbc07..82b78d61 100644 --- a/src/screens/GlobalSearchScreen/hooks/useGlobalSearch.ts +++ b/src/screens/GlobalSearchScreen/hooks/useGlobalSearch.ts @@ -156,7 +156,7 @@ export const useGlobalSearch = ({ ); const debouncedGlobalSearch = useMemo( - () => debounce(globalSearch, 300), + () => debounce(globalSearch, 300), // eslint-disable-line react-hooks/refs [globalSearch], ); diff --git a/src/screens/WebviewScreen/WebviewScreen.tsx b/src/screens/WebviewScreen/WebviewScreen.tsx index 92f7c108..73540e2d 100644 --- a/src/screens/WebviewScreen/WebviewScreen.tsx +++ b/src/screens/WebviewScreen/WebviewScreen.tsx @@ -32,7 +32,7 @@ const WebviewScreen = ({ route, navigation }: WebviewScreenProps) => { const userAgent = useMemo(() => getUserAgent(), []); const theme = useTheme(); - const webViewRef = useRef(null); + const webViewRef = useRef | null>(null); const [progress, setProgress] = useState(0); const [title, setTitle] = useState(name || ''); @@ -115,7 +115,7 @@ const WebviewScreen = ({ route, navigation }: WebviewScreenProps) => { progress={Math.round(1000 * progress) / 1000} visible={progress !== 1} /> - userAgent={userAgent} ref={webViewRef} source={source} diff --git a/src/screens/WebviewScreen/components/Appbar.tsx b/src/screens/WebviewScreen/components/Appbar.tsx index cdffe554..5a30a45f 100644 --- a/src/screens/WebviewScreen/components/Appbar.tsx +++ b/src/screens/WebviewScreen/components/Appbar.tsx @@ -12,7 +12,7 @@ interface AppbarProps { theme: ThemeColors; canGoBack: boolean; canGoForward: boolean; - webView: RefObject; + webView: RefObject | null>; setMenuVisible: (value: boolean) => void; goBack: () => void; } diff --git a/src/screens/WebviewScreen/components/Menu.tsx b/src/screens/WebviewScreen/components/Menu.tsx index 4f1c1aaa..07ec9327 100644 --- a/src/screens/WebviewScreen/components/Menu.tsx +++ b/src/screens/WebviewScreen/components/Menu.tsx @@ -11,7 +11,7 @@ import { showToast } from '@utils/showToast'; interface MenuProps { theme: ThemeColors; currentUrl: string; - webView: RefObject; + webView: RefObject | null>; setMenuVisible: (value: boolean) => void; } diff --git a/src/screens/browse/SourceNovels.tsx b/src/screens/browse/SourceNovels.tsx index 1c81a981..3022c320 100644 --- a/src/screens/browse/SourceNovels.tsx +++ b/src/screens/browse/SourceNovels.tsx @@ -1,4 +1,3 @@ -import React from 'react'; import { StyleSheet, View, FlatList, Text, FlatListProps } from 'react-native'; import { useTheme } from '@hooks/persisted'; diff --git a/src/screens/browse/discover/AniListTopNovels.tsx b/src/screens/browse/discover/AniListTopNovels.tsx index c2ce2c1c..a0ef790e 100644 --- a/src/screens/browse/discover/AniListTopNovels.tsx +++ b/src/screens/browse/discover/AniListTopNovels.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect, useState } from 'react'; +import { useCallback, useEffect, useState } from 'react'; import { StyleSheet, View, @@ -147,6 +147,7 @@ const BrowseALScreen = ({ navigation }: BrowseALScreenProps) => { }; useEffect(() => { + // eslint-disable-next-line react-hooks/set-state-in-effect searchAniList(true); }, [searchAniList]); diff --git a/src/screens/browse/discover/MalTopNovels.tsx b/src/screens/browse/discover/MalTopNovels.tsx index 1b85ff49..2e76b9e3 100644 --- a/src/screens/browse/discover/MalTopNovels.tsx +++ b/src/screens/browse/discover/MalTopNovels.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect, useRef, useState } from 'react'; +import { useCallback, useEffect, useRef, useState } from 'react'; import { StyleSheet, View, @@ -70,6 +70,7 @@ const BrowseMalScreen = ({ navigation }: BrowseMalScreenProps) => { }; useEffect(() => { + // eslint-disable-next-line react-hooks/set-state-in-effect getNovels(); }, [getNovels]); diff --git a/src/screens/browse/migration/MigrationSourceItem.tsx b/src/screens/browse/migration/MigrationSourceItem.tsx index 44e64cc6..7d5608a8 100644 --- a/src/screens/browse/migration/MigrationSourceItem.tsx +++ b/src/screens/browse/migration/MigrationSourceItem.tsx @@ -1,6 +1,5 @@ import { PluginItem } from '@plugins/types'; import { ThemeColors } from '@theme/types'; -import React from 'react'; import { View, Text, Image, StyleSheet } from 'react-native'; import { TouchableRipple } from 'react-native-paper'; diff --git a/src/screens/library/components/LibraryNovelItem.tsx b/src/screens/library/components/LibraryNovelItem.tsx index bb508c99..b8d5c987 100644 --- a/src/screens/library/components/LibraryNovelItem.tsx +++ b/src/screens/library/components/LibraryNovelItem.tsx @@ -1,4 +1,4 @@ -import React, { memo, useCallback } from 'react'; +import { memo, useCallback } from 'react'; import NovelCover from '@components/NovelCover'; import { NovelInfo } from '@database/types'; import { ThemeColors } from '@theme/types'; @@ -14,7 +14,7 @@ interface LibraryNovelItemProps { imageRequestInit: ImageRequestInit | undefined; } -const LibraryNovelItem = memo(function LibraryNovelItem({ +const LibraryNovelItem = memo(function LibraryNovelItem_({ item, theme, isSelected, diff --git a/src/screens/library/hooks/useLibrary.ts b/src/screens/library/hooks/useLibrary.ts index f43d5479..ae87a764 100644 --- a/src/screens/library/hooks/useLibrary.ts +++ b/src/screens/library/hooks/useLibrary.ts @@ -71,7 +71,7 @@ export const useLibrary = (): UseLibraryReturnType => { setIsLoading(true); } - const [_, novels] = await Promise.all([ + const [, novels] = await Promise.all([ refreshCategories(), getLibraryNovelsFromDb(sortOrder, filter, searchText, downloadedOnlyMode), ]); @@ -117,9 +117,9 @@ export const useLibrary = (): UseLibraryReturnType => { getLibrary(); }); - const [taskQueue] = useMMKVObject< - (BackgroundTask | QueuedBackgroundTask)[] - >(BACKGROUND_TASKS_STORE_KEY); + const [taskQueue] = useMMKVObject<(BackgroundTask | QueuedBackgroundTask)[]>( + BACKGROUND_TASKS_STORE_KEY, + ); const restoreTasksCount = useMemo( () => taskQueue?.filter(t => { diff --git a/src/screens/more/About.tsx b/src/screens/more/About.tsx index d9de7162..72b3a1f7 100644 --- a/src/screens/more/About.tsx +++ b/src/screens/more/About.tsx @@ -1,20 +1,21 @@ -import React from 'react'; import { ScrollView, StyleSheet } from 'react-native'; import * as Linking from 'expo-linking'; - -import { getString } from '@i18n/translations'; -import { MoreHeader } from './components/MoreHeader'; -import { useTheme } from '@hooks/persisted'; -import { List, SafeAreaView } from '@components'; -import { AboutScreenProps } from '@navigators/types'; -import Config from '@env'; -import * as Clipboard from 'expo-clipboard'; import { version } from '../../../package.json'; +import { getString } from '@i18n/translations'; +import { setStringAsync } from 'expo-clipboard'; +import { useTheme } from '@hooks/persisted'; +import { List, SafeAreaView } from '@components'; +import { MoreHeader } from './components/MoreHeader'; +import { AboutScreenProps } from '@navigators/types'; +import Config from '@env'; + +/* eslint-disable import/no-named-as-default-member */ const GIT_HASH = Config.GIT_HASH; const RELEASE_DATE = Config.RELEASE_DATE; const BUILD_TYPE = Config.BUILD_TYPE; +/* eslint-enable import/no-named-as-default-member */ const AboutScreen = ({ navigation }: AboutScreenProps) => { const theme = useTheme(); @@ -32,7 +33,6 @@ const AboutScreen = ({ navigation }: AboutScreenProps) => { return `${BUILD_TYPE} ${version} (${localDateTime}) Commit: ${GIT_HASH}`; } } - return ( { description={getBuildName()} theme={theme} onPress={() => { - Clipboard.setStringAsync(getBuildName()); + setStringAsync(getBuildName()); }} /> { const closeMenu = () => setVisible(false); useEffect(() => { if (taskQueue?.length === 0) { + // eslint-disable-next-line react-hooks/set-state-in-effect setIsRunning(false); } }, [taskQueue]); diff --git a/src/screens/more/components/MoreHeader.tsx b/src/screens/more/components/MoreHeader.tsx index 03d80db6..1e8e5be5 100644 --- a/src/screens/more/components/MoreHeader.tsx +++ b/src/screens/more/components/MoreHeader.tsx @@ -1,4 +1,3 @@ -import React from 'react'; import { Image, StyleSheet, View } from 'react-native'; import { Appbar, List } from '@components'; diff --git a/src/screens/reader/ChapterContext.tsx b/src/screens/reader/ChapterContext.tsx index 0e84fd0d..fc53bf5c 100644 --- a/src/screens/reader/ChapterContext.tsx +++ b/src/screens/reader/ChapterContext.tsx @@ -5,7 +5,7 @@ import useChapter from './hooks/useChapter'; type ChapterContextType = ReturnType['chapterContext'] & { novel: NovelInfo; - webViewRef: React.RefObject | null>; + webViewRef: React.RefObject | null>; }; const defaultValue = {} as ChapterContextType; diff --git a/src/screens/reader/ReaderScreen.tsx b/src/screens/reader/ReaderScreen.tsx index ce8917a1..1a4f894e 100644 --- a/src/screens/reader/ReaderScreen.tsx +++ b/src/screens/reader/ReaderScreen.tsx @@ -157,16 +157,19 @@ export const ChapterContent = ({ ); useEffect(() => { + // eslint-disable-next-line react-hooks/set-state-in-effect setBookmarked(chapter.bookmark ?? false); }, [chapter]); useEffect(() => { + // eslint-disable-next-line react-hooks/set-state-in-effect setSearchVisible(false); resetSearch(); }, [chapter.id, resetSearch]); useEffect(() => { if (hidden) { + // eslint-disable-next-line react-hooks/set-state-in-effect setSearchVisible(false); } }, [hidden]); diff --git a/src/screens/reader/components/ChapterDrawer/index.tsx b/src/screens/reader/components/ChapterDrawer/index.tsx index 0bfb33e3..d964954c 100644 --- a/src/screens/reader/components/ChapterDrawer/index.tsx +++ b/src/screens/reader/components/ChapterDrawer/index.tsx @@ -1,10 +1,4 @@ -import React, { - useCallback, - useEffect, - useMemo, - useRef, - useState, -} from 'react'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { StyleSheet, View } from 'react-native'; import { Text } from 'react-native-paper'; import { useAppSettings, useTheme } from '@hooks/persisted'; diff --git a/src/screens/reader/components/WebViewReader.tsx b/src/screens/reader/components/WebViewReader.tsx index 78f121ec..a8076a59 100644 --- a/src/screens/reader/components/WebViewReader.tsx +++ b/src/screens/reader/components/WebViewReader.tsx @@ -282,8 +282,10 @@ const WebViewReader: React.FC = ({ * page, so nothing that changes while a chapter is on screen may be part of * it – those updates go through `injectJavaScript` instead. */ - const source = useMemo( - () => ({ + const source = useMemo(() => { + // eslint-disable-next-line react-hooks/refs + const isNextChapterScreenVisible = nextChapterScreenVisible.current; + return { baseUrl: !chapter.isDownloaded ? plugin?.site : undefined, headers: plugin?.imageRequestInit?.headers, method: plugin?.imageRequestInit?.method, @@ -345,7 +347,7 @@ const WebViewReader: React.FC = ({ chapterGeneralSettings.pageReader ? 'page-reader' : '' }">
{ {/* Large Preview Area */} - ref={webViewRef} originWhitelist={['*']} allowFileAccess={true} @@ -359,7 +359,7 @@ const SettingsReaderScreen = () => { + }">
${dummyHTML}
diff --git a/src/screens/settings/SettingsRepositoryScreen/components/RepositoryCard.tsx b/src/screens/settings/SettingsRepositoryScreen/components/RepositoryCard.tsx index 68566104..7df0fd7a 100644 --- a/src/screens/settings/SettingsRepositoryScreen/components/RepositoryCard.tsx +++ b/src/screens/settings/SettingsRepositoryScreen/components/RepositoryCard.tsx @@ -1,4 +1,4 @@ -import React, { FC } from 'react'; +import { FC } from 'react'; import { StyleSheet, Text, View } from 'react-native'; import * as Clipboard from 'expo-clipboard'; import * as Linking from 'expo-linking'; diff --git a/src/screens/settings/SettingsTrackerScreen.tsx b/src/screens/settings/SettingsTrackerScreen.tsx index 9d0a1e4e..d8a57bcb 100644 --- a/src/screens/settings/SettingsTrackerScreen.tsx +++ b/src/screens/settings/SettingsTrackerScreen.tsx @@ -165,6 +165,8 @@ const TrackerScreen = ({ navigation }: TrackerSettingsScreenProps) => { [theme, isTrackerAuthenticated], ); + // eslint-disable-next-line react-hooks/purity + const now = Date.now(); return ( @@ -256,12 +258,10 @@ const TrackerScreen = ({ navigation }: TrackerSettingsScreenProps) => { /> {(isTrackerAuthenticated('MyAnimeList') && getTrackerAuth('MyAnimeList')?.auth?.expiresAt && - getTrackerAuth('MyAnimeList')!.auth.expiresAt < - new Date(Date.now())) || + getTrackerAuth('MyAnimeList')!.auth.expiresAt < new Date(now)) || (isTrackerAuthenticated('Kitsu') && getTrackerAuth('Kitsu')?.auth?.expiresAt && - getTrackerAuth('Kitsu')!.auth.expiresAt < - new Date(Date.now())) ? ( + getTrackerAuth('Kitsu')!.auth.expiresAt < new Date(now)) ? ( <> {getString('common.settings')} @@ -269,7 +269,7 @@ const TrackerScreen = ({ navigation }: TrackerSettingsScreenProps) => { {isTrackerAuthenticated('MyAnimeList') && getTrackerAuth('MyAnimeList')?.auth?.expiresAt && getTrackerAuth('MyAnimeList')!.auth.expiresAt < - new Date(Date.now()) && ( + new Date(now) && ( { )} {isTrackerAuthenticated('Kitsu') && getTrackerAuth('Kitsu')?.auth?.expiresAt && - getTrackerAuth('Kitsu')!.auth.expiresAt < - new Date(Date.now()) && ( + getTrackerAuth('Kitsu')!.auth.expiresAt < new Date(now) && ( { diff --git a/src/services/Trackers/aniList.ts b/src/services/Trackers/aniList.ts index 30d392a4..d0e5eb99 100644 --- a/src/services/Trackers/aniList.ts +++ b/src/services/Trackers/aniList.ts @@ -1,10 +1,10 @@ import * as Linking from 'expo-linking'; import * as WebBrowser from 'expo-web-browser'; -import Config from '@env'; +import { ANILIST_CLIENT_ID } from '@env'; import { AuthenticationResult, Tracker } from './index'; const apiEndpoint = 'https://graphql.anilist.co'; -const clientId = Config.ANILIST_CLIENT_ID; +const clientId = ANILIST_CLIENT_ID; const redirectUri = Linking.createURL('tracker/AL'); diff --git a/src/services/Trackers/myAnimeList.ts b/src/services/Trackers/myAnimeList.ts index 6746e613..5af71a9a 100644 --- a/src/services/Trackers/myAnimeList.ts +++ b/src/services/Trackers/myAnimeList.ts @@ -1,6 +1,6 @@ import * as Linking from 'expo-linking'; import * as WebBrowser from 'expo-web-browser'; -import Config from '@env'; +import { MYANIMELIST_CLIENT_ID } from '@env'; import { AuthenticationResult, Tracker, @@ -8,7 +8,7 @@ import { UserListStatus, } from './index'; -const clientId = Config.MYANIMELIST_CLIENT_ID; +const clientId = MYANIMELIST_CLIENT_ID; const baseOAuthUrl = 'https://myanimelist.net/v1/oauth2/authorize'; const tokenUrl = 'https://myanimelist.net/v1/oauth2/token'; const baseApiUrl = 'https://api.myanimelist.net/v2';