forked from akai/readest
fix(reader): open books without a View Transition to avoid timeout (#4949)
useAppRouter wraps every navigation in a View Transition. Opening a book is a heavy render (the reader mounts and loads the book) that can overrun the transition's ~4s DOM-update budget and abort with a TimeoutError (Sentry READEST-9). Navigate to the reader with the plain router instead, matching every other into-reader path in the app; the transition router stays for lighter navigation. Applies to the tap-to-open flow (useOpenBook) and the post-import queued open (library/page). Fixes READEST-9 Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -3,8 +3,8 @@ import { Book } from '@/types/book';
|
||||
import { useEnv } from '@/context/EnvContext';
|
||||
import { useLibraryStore } from '@/store/libraryStore';
|
||||
import { useSettingsStore } from '@/store/settingsStore';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useTranslation } from '@/hooks/useTranslation';
|
||||
import { useAppRouter } from '@/hooks/useAppRouter';
|
||||
import { eventDispatcher } from '@/utils/event';
|
||||
import { navigateToReader, showReaderWindow } from '@/utils/nav';
|
||||
|
||||
@@ -25,7 +25,11 @@ interface UseOpenBookOptions {
|
||||
*/
|
||||
export const useOpenBook = ({ setLoading, handleBookDownload }: UseOpenBookOptions) => {
|
||||
const _ = useTranslation();
|
||||
const router = useAppRouter();
|
||||
// Open the reader with the plain router, NOT the View Transition router:
|
||||
// the reader's initial render is heavy enough to blow the transition's ~4s
|
||||
// DOM-update budget, which aborts with a TimeoutError (Sentry READEST-9).
|
||||
// This matches how every other into-reader path already navigates.
|
||||
const router = useRouter();
|
||||
const { envConfig, appService } = useEnv();
|
||||
const { settings } = useSettingsStore();
|
||||
const { updateBook } = useLibraryStore();
|
||||
|
||||
@@ -4,7 +4,7 @@ import clsx from 'clsx';
|
||||
import * as React from 'react';
|
||||
import { MdChevronRight } from 'react-icons/md';
|
||||
import { useState, useRef, useEffect, Suspense, useCallback } from 'react';
|
||||
import { ReadonlyURLSearchParams, useSearchParams } from 'next/navigation';
|
||||
import { ReadonlyURLSearchParams, useRouter, useSearchParams } from 'next/navigation';
|
||||
|
||||
import { Book } from '@/types/book';
|
||||
import { AppService, DeleteAction } from '@/types/system';
|
||||
@@ -155,6 +155,10 @@ const LibraryPageWithSearchParams = () => {
|
||||
|
||||
const LibraryPageContent = ({ searchParams }: { searchParams: ReadonlyURLSearchParams | null }) => {
|
||||
const router = useAppRouter();
|
||||
// Opening the reader is a heavy render that overruns the View Transition
|
||||
// DOM-update budget (TimeoutError, Sentry READEST-9), so navigate to it with
|
||||
// the plain router; `router` above keeps transitions for lighter navigation.
|
||||
const readerRouter = useRouter();
|
||||
const { envConfig, appService } = useEnv();
|
||||
const { token, user } = useAuth();
|
||||
const {
|
||||
@@ -568,10 +572,10 @@ const LibraryPageContent = ({ searchParams }: { searchParams: ReadonlyURLSearchP
|
||||
const bookIds = pendingNavigationBookIds;
|
||||
setPendingNavigationBookIds(null);
|
||||
if (bookIds.length > 0) {
|
||||
navigateToReader(router, bookIds);
|
||||
navigateToReader(readerRouter, bookIds);
|
||||
}
|
||||
}
|
||||
}, [pendingNavigationBookIds, appService, router]);
|
||||
}, [pendingNavigationBookIds, appService, readerRouter]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isInitiating.current) return;
|
||||
|
||||
Reference in New Issue
Block a user