diff --git a/apps/readest-app/eslint.config.mjs b/apps/readest-app/eslint.config.mjs index 8cc926be..05784df3 100644 --- a/apps/readest-app/eslint.config.mjs +++ b/apps/readest-app/eslint.config.mjs @@ -1,25 +1,21 @@ -import path from 'node:path'; -import js from '@eslint/js'; +import { defineConfig, globalIgnores } from 'eslint/config'; +import nextVitals from 'eslint-config-next/core-web-vitals'; import jsxA11y from 'eslint-plugin-jsx-a11y'; -import { fileURLToPath } from 'node:url'; -import { FlatCompat } from '@eslint/eslintrc'; -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); -const compat = new FlatCompat({ - baseDirectory: __dirname, - recommendedConfig: js.configs.recommended, - allConfig: js.configs.all, -}); - -export default [ - ...compat.extends('next/core-web-vitals', 'next/typescript'), +const eslintConfig = defineConfig([ + ...nextVitals, { - plugins: { - 'jsx-a11y': jsxA11y, - }, - rules: { - ...jsxA11y.configs.recommended.rules, - }, + rules: jsxA11y.configs.recommended.rules, }, -]; + globalIgnores([ + 'node_modules/**', + '.next/**', + '.open-next/**', + 'out/**', + 'build/**', + 'public/**', + 'next-env.d.ts', + ]), +]); + +export default eslintConfig; diff --git a/apps/readest-app/package.json b/apps/readest-app/package.json index fec301bb..9dd97896 100644 --- a/apps/readest-app/package.json +++ b/apps/readest-app/package.json @@ -10,7 +10,7 @@ "build-web": "dotenv -e .env.web -- next build", "start-web": "dotenv -e .env.web -- next start", "i18n:extract": "i18next-scanner", - "lint": "next lint", + "lint": "eslint .", "test": "dotenv -e .env -e .env.test.local vitest", "tauri": "tauri", "prepare-public-vendor": "mkdirp ./public/vendor/pdfjs", @@ -126,7 +126,7 @@ "daisyui": "^4.12.24", "dotenv-cli": "^7.4.4", "eslint": "^9.16.0", - "eslint-config-next": "15.0.3", + "eslint-config-next": "16.0.0", "eslint-plugin-jsx-a11y": "^6.10.2", "i18next-scanner": "^4.6.0", "jsdom": "^26.1.0", diff --git a/apps/readest-app/src/__tests__/helpers/supabase-mock.ts b/apps/readest-app/src/__tests__/helpers/supabase-mock.ts index efeb93da..098b5243 100644 --- a/apps/readest-app/src/__tests__/helpers/supabase-mock.ts +++ b/apps/readest-app/src/__tests__/helpers/supabase-mock.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ import { vi } from 'vitest'; export const setupSupabaseMocks = async ( diff --git a/apps/readest-app/src/app/error.tsx b/apps/readest-app/src/app/error.tsx index 85c85f47..422cafc2 100644 --- a/apps/readest-app/src/app/error.tsx +++ b/apps/readest-app/src/app/error.tsx @@ -19,6 +19,9 @@ export default function Error({ error, reset }: ErrorPageProps) { useEffect(() => { setBrowserInfo(parseWebViewInfo(appService)); + }, [appService]); + + useEffect(() => { posthog.captureException(error); handleGlobalError(error); }, [appService, error]); diff --git a/apps/readest-app/src/app/library/components/SettingsMenu.tsx b/apps/readest-app/src/app/library/components/SettingsMenu.tsx index 03048aa0..3c507d99 100644 --- a/apps/readest-app/src/app/library/components/SettingsMenu.tsx +++ b/apps/readest-app/src/app/library/components/SettingsMenu.tsx @@ -20,6 +20,7 @@ import { tauriHandleSetAlwaysOnTop, tauriHandleToggleFullScreen } from '@/utils/ import { optInTelemetry, optOutTelemetry } from '@/utils/telemetry'; import { setAboutDialogVisible } from '@/components/AboutWindow'; import { setMigrateDataDirDialogVisible } from '@/app/library/components/MigrateDataWindow'; +import { saveSysSettings } from '@/helpers/settings'; import UserAvatar from '@/components/UserAvatar'; import MenuItem from '@/components/MenuItem'; import Quota from '@/components/Quota'; @@ -40,8 +41,7 @@ const SettingsMenu: React.FC = ({ setIsDropdownOpen }) => { const { user } = useAuth(); const { userPlan, quotas } = useQuotaStats(true); const { themeMode, setThemeMode } = useThemeStore(); - const { settings, setSettings, saveSettings } = useSettingsStore(); - const { setFontLayoutSettingsDialogOpen } = useSettingsStore(); + const { settings, setSettingsDialogOpen } = useSettingsStore(); const [isAutoUpload, setIsAutoUpload] = useState(settings.autoUpload); const [isAutoCheckUpdates, setIsAutoCheckUpdates] = useState(settings.autoCheckUpdates); const [isAlwaysOnTop, setIsAlwaysOnTop] = useState(settings.alwaysOnTop); @@ -91,77 +91,67 @@ const SettingsMenu: React.FC = ({ setIsDropdownOpen }) => { }; const toggleOpenInNewWindow = () => { - settings.openBookInNewWindow = !settings.openBookInNewWindow; - setSettings(settings); - saveSettings(envConfig, settings); + saveSysSettings(envConfig, 'openBookInNewWindow', !settings.openBookInNewWindow); setIsDropdownOpen?.(false); }; const toggleAlwaysOnTop = () => { - settings.alwaysOnTop = !settings.alwaysOnTop; - setSettings(settings); - saveSettings(envConfig, settings); - setIsAlwaysOnTop(settings.alwaysOnTop); - tauriHandleSetAlwaysOnTop(settings.alwaysOnTop); + const newValue = !settings.alwaysOnTop; + saveSysSettings(envConfig, 'alwaysOnTop', newValue); + setIsAlwaysOnTop(newValue); + tauriHandleSetAlwaysOnTop(newValue); setIsDropdownOpen?.(false); }; const toggleAlwaysShowStatusBar = () => { - settings.alwaysShowStatusBar = !settings.alwaysShowStatusBar; - setSettings(settings); - saveSettings(envConfig, settings); - setIsAlwaysShowStatusBar(settings.alwaysShowStatusBar); + const newValue = !settings.alwaysShowStatusBar; + saveSysSettings(envConfig, 'alwaysShowStatusBar', newValue); + setIsAlwaysShowStatusBar(newValue); }; const toggleAutoUploadBooks = () => { - settings.autoUpload = !settings.autoUpload; - setSettings(settings); - saveSettings(envConfig, settings); - setIsAutoUpload(settings.autoUpload); + const newValue = !settings.autoUpload; + saveSysSettings(envConfig, 'autoUpload', newValue); + setIsAutoUpload(newValue); - if (settings.autoUpload && !user) { + if (newValue && !user) { navigateToLogin(router); } }; const toggleAutoImportBooksOnOpen = () => { - settings.autoImportBooksOnOpen = !settings.autoImportBooksOnOpen; - setSettings(settings); - saveSettings(envConfig, settings); - setIsAutoImportBooksOnOpen(settings.autoImportBooksOnOpen); + const newValue = !settings.autoImportBooksOnOpen; + saveSysSettings(envConfig, 'autoImportBooksOnOpen', newValue); + setIsAutoImportBooksOnOpen(newValue); }; const toggleAutoCheckUpdates = () => { - settings.autoCheckUpdates = !settings.autoCheckUpdates; - setSettings(settings); - saveSettings(envConfig, settings); - setIsAutoCheckUpdates(settings.autoCheckUpdates); + const newValue = !settings.autoCheckUpdates; + saveSysSettings(envConfig, 'autoCheckUpdates', newValue); + setIsAutoCheckUpdates(newValue); }; const toggleScreenWakeLock = () => { - settings.screenWakeLock = !settings.screenWakeLock; - setSettings(settings); - saveSettings(envConfig, settings); - setIsScreenWakeLock(settings.screenWakeLock); + const newValue = !settings.screenWakeLock; + saveSysSettings(envConfig, 'screenWakeLock', newValue); + setIsScreenWakeLock(newValue); }; const toggleOpenLastBooks = () => { - settings.openLastBooks = !settings.openLastBooks; - setSettings(settings); - saveSettings(envConfig, settings); - setIsOpenLastBooks(settings.openLastBooks); + const newValue = !settings.openLastBooks; + saveSysSettings(envConfig, 'openLastBooks', newValue); + setIsOpenLastBooks(newValue); }; const toggleTelemetry = () => { - settings.telemetryEnabled = !settings.telemetryEnabled; - if (settings.telemetryEnabled) { + const newValue = !settings.telemetryEnabled; + saveSysSettings(envConfig, 'telemetryEnabled', newValue); + setIsTelemetryEnabled(newValue); + if (newValue) { optInTelemetry(); } else { optOutTelemetry(); } - setSettings(settings); - saveSettings(envConfig, settings); - setIsTelemetryEnabled(settings.telemetryEnabled); }; const handleUpgrade = () => { @@ -176,7 +166,7 @@ const SettingsMenu: React.FC = ({ setIsDropdownOpen }) => { const openSettingsDialog = () => { setIsDropdownOpen?.(false); - setFontLayoutSettingsDialogOpen(true); + setSettingsDialogOpen(true); }; const toggleAlwaysInForeground = async () => { @@ -192,10 +182,8 @@ const SettingsMenu: React.FC = ({ setIsDropdownOpen }) => { if (permission.postNotification !== 'granted') return; } - settings.alwaysInForeground = requestAlwaysInForeground; - setSettings(settings); - saveSettings(envConfig, settings); - setAlwaysInForeground(settings.alwaysInForeground); + saveSysSettings(envConfig, 'alwaysInForeground', requestAlwaysInForeground); + setAlwaysInForeground(requestAlwaysInForeground); }; const avatarUrl = user?.user_metadata?.['picture'] || user?.user_metadata?.['avatar_url']; diff --git a/apps/readest-app/src/app/library/components/ViewMenu.tsx b/apps/readest-app/src/app/library/components/ViewMenu.tsx index a12937e5..96e65b15 100644 --- a/apps/readest-app/src/app/library/components/ViewMenu.tsx +++ b/apps/readest-app/src/app/library/components/ViewMenu.tsx @@ -4,6 +4,7 @@ import { useEnv } from '@/context/EnvContext'; import { useSettingsStore } from '@/store/settingsStore'; import { useTranslation } from '@/hooks/useTranslation'; import { LibraryCoverFitType, LibrarySortByType, LibraryViewModeType } from '@/types/settings'; +import { saveSysSettings } from '@/helpers/settings'; import { navigateToLibrary } from '@/utils/nav'; import MenuItem from '@/components/MenuItem'; import Menu from '@/components/Menu'; @@ -17,7 +18,7 @@ const ViewMenu: React.FC = ({ setIsDropdownOpen }) => { const router = useRouter(); const searchParams = useSearchParams(); const { envConfig } = useEnv(); - const { settings, setSettings, saveSettings } = useSettingsStore(); + const { settings } = useSettingsStore(); const viewMode = settings.libraryViewMode; const sortBy = settings.librarySortBy; @@ -48,9 +49,7 @@ const ViewMenu: React.FC = ({ setIsDropdownOpen }) => { ]; const handleSetViewMode = (value: LibraryViewModeType) => { - settings.libraryViewMode = value; - setSettings(settings); - saveSettings(envConfig, settings); + saveSysSettings(envConfig, 'libraryViewMode', value); setIsDropdownOpen?.(false); const params = new URLSearchParams(searchParams?.toString()); @@ -59,9 +58,7 @@ const ViewMenu: React.FC = ({ setIsDropdownOpen }) => { }; const handleToggleCropCovers = (value: LibraryCoverFitType) => { - settings.libraryCoverFit = value; - setSettings(settings); - saveSettings(envConfig, settings); + saveSysSettings(envConfig, 'libraryCoverFit', value); setIsDropdownOpen?.(false); const params = new URLSearchParams(searchParams?.toString()); @@ -70,9 +67,7 @@ const ViewMenu: React.FC = ({ setIsDropdownOpen }) => { }; const handleSetSortBy = (value: LibrarySortByType) => { - settings.librarySortBy = value; - setSettings(settings); - saveSettings(envConfig, settings); + saveSysSettings(envConfig, 'librarySortBy', value); setIsDropdownOpen?.(false); const params = new URLSearchParams(searchParams?.toString()); @@ -81,9 +76,7 @@ const ViewMenu: React.FC = ({ setIsDropdownOpen }) => { }; const handleSetSortAscending = (value: boolean) => { - settings.librarySortAscending = value; - setSettings(settings); - saveSettings(envConfig, settings); + saveSysSettings(envConfig, 'librarySortAscending', value); setIsDropdownOpen?.(false); const params = new URLSearchParams(searchParams?.toString()); diff --git a/apps/readest-app/src/app/library/page.tsx b/apps/readest-app/src/app/library/page.tsx index 171fa9b7..6bf5486f 100644 --- a/apps/readest-app/src/app/library/page.tsx +++ b/apps/readest-app/src/app/library/page.tsx @@ -79,10 +79,8 @@ const LibraryPageContent = ({ searchParams }: { searchParams: ReadonlyURLSearchP const { selectFiles } = useFileSelector(appService, _); const { safeAreaInsets: insets, isRoundedWindow } = useThemeStore(); const { settings, setSettings, saveSettings } = useSettingsStore(); - const { isFontLayoutSettingsDialogOpen } = useSettingsStore(); - const { setFontLayoutSettingsDialogOpen } = useSettingsStore(); + const { isSettingsDialogOpen, setSettingsDialogOpen } = useSettingsStore(); const [loading, setLoading] = useState(false); - const isInitiating = useRef(false); const [libraryLoaded, setLibraryLoaded] = useState(false); const [isSelectMode, setIsSelectMode] = useState(false); const [isSelectAll, setIsSelectAll] = useState(false); @@ -93,6 +91,7 @@ const LibraryPageContent = ({ searchParams }: { searchParams: ReadonlyURLSearchP }>({}); const [pendingNavigationBookIds, setPendingNavigationBookIds] = useState(null); const [isDragging, setIsDragging] = useState(false); + const isInitiating = useRef(false); const demoBooks = useDemoBooks(); const osRef = useRef(null); @@ -129,7 +128,7 @@ const LibraryPageContent = ({ searchParams }: { searchParams: ReadonlyURLSearchP } }, onOpenFontLayoutSettings: () => { - setFontLayoutSettingsDialogOpen(true); + setSettingsDialogOpen(true); }, onOpenBooks: () => { handleImportBooks(); @@ -772,7 +771,7 @@ const LibraryPageContent = ({ searchParams }: { searchParams: ReadonlyURLSearchP - {isFontLayoutSettingsDialogOpen && } + {isSettingsDialogOpen && } ); diff --git a/apps/readest-app/src/app/reader/components/BooksGrid.tsx b/apps/readest-app/src/app/reader/components/BooksGrid.tsx index a46526f6..1c410135 100644 --- a/apps/readest-app/src/app/reader/components/BooksGrid.tsx +++ b/apps/readest-app/src/app/reader/components/BooksGrid.tsx @@ -34,7 +34,7 @@ const BooksGrid: React.FC = ({ bookKeys, onCloseBook }) => { const { getProgress, getViewState, getViewSettings } = useReaderStore(); const { setGridInsets, hoveredBookKey } = useReaderStore(); const { sideBarBookKey } = useSidebarStore(); - const { isFontLayoutSettingsDialogOpen } = useSettingsStore(); + const { isSettingsDialogOpen } = useSettingsStore(); const { safeAreaInsets: screenInsets } = useThemeStore(); const aspectRatio = window.innerWidth / window.innerHeight; @@ -202,7 +202,7 @@ const BooksGrid: React.FC = ({ bookKeys, onCloseBook }) => { isHoveredAnim={false} gridInsets={gridInsets} /> - {isFontLayoutSettingsDialogOpen && } + {isSettingsDialogOpen && } ); })} diff --git a/apps/readest-app/src/app/reader/components/SettingsToggler.tsx b/apps/readest-app/src/app/reader/components/SettingsToggler.tsx index b745262c..37e4f239 100644 --- a/apps/readest-app/src/app/reader/components/SettingsToggler.tsx +++ b/apps/readest-app/src/app/reader/components/SettingsToggler.tsx @@ -9,10 +9,10 @@ import Button from '@/components/Button'; const SettingsToggler = () => { const _ = useTranslation(); const { setHoveredBookKey } = useReaderStore(); - const { isFontLayoutSettingsDialogOpen, setFontLayoutSettingsDialogOpen } = useSettingsStore(); + const { isSettingsDialogOpen, setSettingsDialogOpen } = useSettingsStore(); const handleToggleSettings = () => { setHoveredBookKey(''); - setFontLayoutSettingsDialogOpen(!isFontLayoutSettingsDialogOpen); + setSettingsDialogOpen(!isSettingsDialogOpen); }; return (