potential fix for not jumping to reading position sometimes
This commit is contained in:
@@ -238,17 +238,20 @@ export default function StoryReadingPage() {
|
||||
// Auto-scroll to saved reading position or URL hash when story content is loaded
|
||||
useEffect(() => {
|
||||
if (story && sanitizedContent && !hasScrolledToPosition) {
|
||||
// Use a small delay to ensure content is rendered
|
||||
const timeout = setTimeout(() => {
|
||||
let cancelled = false;
|
||||
|
||||
const performScroll = () => {
|
||||
if (cancelled) return;
|
||||
|
||||
debug.log('Initializing reading position tracking, saved position:', story.readingPosition);
|
||||
|
||||
|
||||
// Check if there's a hash in the URL (for TOC navigation)
|
||||
const hash = window.location.hash.substring(1);
|
||||
if (hash && hash.startsWith('heading-')) {
|
||||
debug.log('Auto-scrolling to heading from URL hash:', hash);
|
||||
const element = document.getElementById(hash);
|
||||
if (element) {
|
||||
element.scrollIntoView({
|
||||
element.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'start'
|
||||
});
|
||||
@@ -256,7 +259,7 @@ export default function StoryReadingPage() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Otherwise, use saved reading position
|
||||
if (story.readingPosition && story.readingPosition > 0) {
|
||||
debug.log('Auto-scrolling to saved position:', story.readingPosition);
|
||||
@@ -269,9 +272,57 @@ export default function StoryReadingPage() {
|
||||
setReadingPercentage(0);
|
||||
setHasScrolledToPosition(true);
|
||||
}
|
||||
}, 500);
|
||||
|
||||
return () => clearTimeout(timeout);
|
||||
};
|
||||
|
||||
// Wait for images to load before scrolling so scrollHeight is accurate
|
||||
const waitForImagesAndScroll = () => {
|
||||
if (!contentRef.current) {
|
||||
// Content not in DOM yet, use a small delay
|
||||
setTimeout(performScroll, 500);
|
||||
return;
|
||||
}
|
||||
|
||||
const images = contentRef.current.querySelectorAll('img');
|
||||
const unloadedImages = Array.from(images).filter(img => !img.complete);
|
||||
|
||||
if (unloadedImages.length === 0) {
|
||||
// No pending images, scroll after a brief delay for layout
|
||||
debug.log('No pending images, scrolling immediately');
|
||||
setTimeout(performScroll, 100);
|
||||
return;
|
||||
}
|
||||
|
||||
debug.log(`Waiting for ${unloadedImages.length} images to load before scrolling`);
|
||||
let loadedCount = 0;
|
||||
|
||||
// Fallback timeout in case images take too long
|
||||
const fallbackTimeout = setTimeout(() => {
|
||||
debug.log('Image load timeout reached, scrolling anyway');
|
||||
performScroll();
|
||||
}, 5000);
|
||||
|
||||
const onImageReady = () => {
|
||||
loadedCount++;
|
||||
if (loadedCount >= unloadedImages.length) {
|
||||
clearTimeout(fallbackTimeout);
|
||||
// Small delay after last image loads for layout to settle
|
||||
setTimeout(performScroll, 50);
|
||||
}
|
||||
};
|
||||
|
||||
unloadedImages.forEach(img => {
|
||||
img.addEventListener('load', onImageReady, { once: true });
|
||||
img.addEventListener('error', onImageReady, { once: true });
|
||||
});
|
||||
};
|
||||
|
||||
// Small delay to ensure content is in the DOM
|
||||
const timeout = setTimeout(waitForImagesAndScroll, 100);
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
clearTimeout(timeout);
|
||||
};
|
||||
}
|
||||
}, [story, sanitizedContent, scrollToCharacterPosition, calculateReadingPercentage, hasScrolledToPosition]);
|
||||
|
||||
|
||||
@@ -39,8 +39,8 @@ export default function CollectionReadingView({
|
||||
(scrolled - contentTop + windowHeight * 0.3) / contentHeight
|
||||
));
|
||||
|
||||
// Convert to character position in the plain text content
|
||||
const textLength = story.contentPlain?.length || story.contentHtml?.length || 0;
|
||||
// Convert to character position (ALWAYS use contentHtml for consistency with backend)
|
||||
const textLength = story.contentHtml?.length || 0;
|
||||
return Math.floor(scrollRatio * textLength);
|
||||
}, [story]);
|
||||
|
||||
@@ -48,7 +48,7 @@ export default function CollectionReadingView({
|
||||
const calculateReadingPercentage = useCallback((currentPosition: number): number => {
|
||||
if (!story) return 0;
|
||||
|
||||
const totalLength = story.contentPlain?.length || story.contentHtml?.length || 0;
|
||||
const totalLength = story.contentHtml?.length || 0;
|
||||
if (totalLength === 0) return 0;
|
||||
|
||||
return Math.round((currentPosition / totalLength) * 100);
|
||||
@@ -58,7 +58,7 @@ export default function CollectionReadingView({
|
||||
const scrollToCharacterPosition = useCallback((position: number) => {
|
||||
if (!contentRef.current || !story || hasScrolledToPosition) return;
|
||||
|
||||
const textLength = story.contentPlain?.length || story.contentHtml?.length || 0;
|
||||
const textLength = story.contentHtml?.length || 0;
|
||||
if (textLength === 0 || position === 0) return;
|
||||
|
||||
const ratio = position / textLength;
|
||||
@@ -109,7 +109,11 @@ export default function CollectionReadingView({
|
||||
// Auto-scroll to saved reading position when story content is loaded
|
||||
useEffect(() => {
|
||||
if (story && !hasScrolledToPosition) {
|
||||
const timeout = setTimeout(() => {
|
||||
let cancelled = false;
|
||||
|
||||
const performScroll = () => {
|
||||
if (cancelled) return;
|
||||
|
||||
console.log('Collection view - initializing reading position tracking, saved position:', story.readingPosition);
|
||||
if (story.readingPosition && story.readingPosition > 0) {
|
||||
console.log('Collection view - auto-scrolling to saved position:', story.readingPosition);
|
||||
@@ -121,9 +125,51 @@ export default function CollectionReadingView({
|
||||
setReadingPercentage(0);
|
||||
setHasScrolledToPosition(true);
|
||||
}
|
||||
}, 500);
|
||||
|
||||
return () => clearTimeout(timeout);
|
||||
};
|
||||
|
||||
// Wait for images to load before scrolling so scrollHeight is accurate
|
||||
const waitForImagesAndScroll = () => {
|
||||
if (!contentRef.current) {
|
||||
setTimeout(performScroll, 500);
|
||||
return;
|
||||
}
|
||||
|
||||
const images = contentRef.current.querySelectorAll('img');
|
||||
const unloadedImages = Array.from(images).filter(img => !img.complete);
|
||||
|
||||
if (unloadedImages.length === 0) {
|
||||
setTimeout(performScroll, 100);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Collection view - waiting for ${unloadedImages.length} images to load before scrolling`);
|
||||
let loadedCount = 0;
|
||||
|
||||
const fallbackTimeout = setTimeout(() => {
|
||||
console.log('Collection view - image load timeout reached, scrolling anyway');
|
||||
performScroll();
|
||||
}, 5000);
|
||||
|
||||
const onImageReady = () => {
|
||||
loadedCount++;
|
||||
if (loadedCount >= unloadedImages.length) {
|
||||
clearTimeout(fallbackTimeout);
|
||||
setTimeout(performScroll, 50);
|
||||
}
|
||||
};
|
||||
|
||||
unloadedImages.forEach(img => {
|
||||
img.addEventListener('load', onImageReady, { once: true });
|
||||
img.addEventListener('error', onImageReady, { once: true });
|
||||
});
|
||||
};
|
||||
|
||||
const timeout = setTimeout(waitForImagesAndScroll, 100);
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
clearTimeout(timeout);
|
||||
};
|
||||
}
|
||||
}, [story, scrollToCharacterPosition, calculateReadingPercentage, hasScrolledToPosition]);
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user