@@ -5,7 +5,7 @@ import type { InterlinearProject, TextAnalysis } from 'interlinearizer';
55import { TabToolbar } from 'platform-bible-react' ;
66import type { SelectMenuItemHandler } from 'platform-bible-react' ;
77import { isPlatformError } from 'platform-bible-utils' ;
8- import { useCallback , useEffect , useMemo , useRef , useState } from 'react' ;
8+ import { useCallback , useEffect , useMemo , useState } from 'react' ;
99import useInterlinearizerBookData from '../hooks/useInterlinearizerBookData' ;
1010import useOptimisticBooleanSetting from '../hooks/useOptimisticBooleanSetting' ;
1111import type { InterlinearProjectSummary } from '../types/interlinear-project-summary' ;
@@ -221,7 +221,14 @@ function InterlinearizerLoaderInner({
221221 isHideInactiveLinkButtonsLoading ||
222222 isSimplifyPhrasesLoading ||
223223 isChapterLabelInVerseLoading ;
224- const showLoading = isLoading || isAnalysisLoading || isSettingLoading ;
224+ // True during a cross-book swap: the live `scrRef` already names the new book but the loaded `book`
225+ // is still the previous one (its USJ hasn't arrived yet). The old `Interlinearizer` is still
226+ // mounted here; showing it (even frozen on its last in-book reference) lets the previous book's
227+ // components stay visible while the new book loads, so the swap is seen before the fade hides it.
228+ // Treating this window as loading swaps the old view for the Loading… curtain immediately, so
229+ // nothing of either book shows until the new one has mounted and fades in.
230+ const isCrossBookSwap = ! ! book && scrRef . book !== book . bookRef ;
231+ const showLoading = isLoading || isAnalysisLoading || isSettingLoading || isCrossBookSwap ;
225232 const isLoaded = ! hasError && ! showLoading && ! ! book ;
226233
227234 // Abort any in-flight cross-book fade when the new book fails to load, so the error is revealed
@@ -230,22 +237,6 @@ function InterlinearizerLoaderInner({
230237 if ( hasError ) cancelFade ( ) ;
231238 } , [ hasError , cancelFade ] ) ;
232239
233- /**
234- * The scripture reference handed to {@link Interlinearizer}. While a cross-book navigation is in
235- * flight, `scrRef` already names the new book but the loaded `book` is still the previous one
236- * (its USJ hasn't arrived), so the still-mounted views would react to a verse absent from the
237- * mounted book — reseeding focus and scrolling toward it, a visible shuffle behind/before the
238- * fade. Gating on `book.bookRef` keeps the views on a reference that matches the book they
239- * actually render: the live `scrRef` only once the loaded book matches it, otherwise the last
240- * in-book reference. Once the new book's USJ arrives, `Interlinearizer` remounts on it (it is
241- * keyed by `book.bookRef`, so a book change tears down the old instance rather than updating it
242- * in place with carried-over scroll/focus state) and immediately receives the live (new-book)
243- * reference, so it seeds focus on the intended verse and reports settled.
244- */
245- const lastInBookScrRefRef = useRef ( scrRef ) ;
246- if ( book && scrRef . book === book . bookRef ) lastInBookScrRefRef . current = scrRef ;
247- const viewScrRef = book && scrRef . book === book . bookRef ? scrRef : lastInBookScrRefRef . current ;
248-
249240 const [ modal , setModal ] = useState < ModalState > ( 'none' ) ;
250241
251242 const [ phraseMode , setPhraseMode ] = useState < PhraseMode > ( { kind : 'view' } ) ;
@@ -376,7 +367,7 @@ function InterlinearizerLoaderInner({
376367 key = { `${ activeProject ?. id ?? '' } :${ book . bookRef } ` }
377368 book = { book }
378369 continuousScroll = { continuousScroll }
379- scrRef = { viewScrRef }
370+ scrRef = { scrRef }
380371 analysisLanguage = { analysisLanguage }
381372 initialAnalysis = { activeProjectAnalysis }
382373 onSaveAnalysis = { handleSaveAnalysis }
0 commit comments