bugfixes, and logging cleanup
This commit is contained in:
@@ -139,6 +139,15 @@
|
||||
@apply max-w-full h-auto mx-auto my-6 rounded-lg shadow-sm;
|
||||
max-height: 80vh; /* Prevent images from being too tall */
|
||||
display: block;
|
||||
/* Optimize for performance and prevent reloading */
|
||||
will-change: auto;
|
||||
transform: translateZ(0); /* Force hardware acceleration */
|
||||
backface-visibility: hidden;
|
||||
image-rendering: optimizeQuality;
|
||||
/* Prevent layout shifts that might trigger reloads */
|
||||
box-sizing: border-box;
|
||||
/* Ensure stable dimensions */
|
||||
min-height: 1px;
|
||||
}
|
||||
|
||||
.reading-content img[align="left"] {
|
||||
|
||||
@@ -11,6 +11,7 @@ import StoryRating from '../../../components/stories/StoryRating';
|
||||
import TagDisplay from '../../../components/tags/TagDisplay';
|
||||
import TableOfContents from '../../../components/stories/TableOfContents';
|
||||
import { sanitizeHtml, preloadSanitizationConfig } from '../../../lib/sanitization';
|
||||
import { debug } from '../../../lib/debug';
|
||||
|
||||
// Memoized content component that only re-renders when content changes
|
||||
const StoryContent = memo(({
|
||||
@@ -20,13 +21,50 @@ const StoryContent = memo(({
|
||||
content: string;
|
||||
contentRef: React.RefObject<HTMLDivElement>;
|
||||
}) => {
|
||||
console.log('🔄 StoryContent component rendering with content length:', content.length);
|
||||
const renderTime = Date.now();
|
||||
debug.log('🔄 StoryContent component rendering at', renderTime, 'with content length:', content.length, 'hash:', content.slice(0, 50) + '...');
|
||||
|
||||
// Add observer to track image loading events
|
||||
useEffect(() => {
|
||||
if (!contentRef.current) return;
|
||||
|
||||
const images = contentRef.current.querySelectorAll('img');
|
||||
debug.log('📸 Found', images.length, 'images in content');
|
||||
|
||||
const handleImageLoad = (e: Event) => {
|
||||
const img = e.target as HTMLImageElement;
|
||||
debug.log('🖼️ Image loaded:', img.src);
|
||||
};
|
||||
|
||||
const handleImageError = (e: Event) => {
|
||||
const img = e.target as HTMLImageElement;
|
||||
debug.log('❌ Image error:', img.src);
|
||||
};
|
||||
|
||||
images.forEach(img => {
|
||||
img.addEventListener('load', handleImageLoad);
|
||||
img.addEventListener('error', handleImageError);
|
||||
debug.log('👀 Monitoring image:', img.src);
|
||||
});
|
||||
|
||||
return () => {
|
||||
images.forEach(img => {
|
||||
img.removeEventListener('load', handleImageLoad);
|
||||
img.removeEventListener('error', handleImageError);
|
||||
});
|
||||
};
|
||||
}, [content]);
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={contentRef}
|
||||
className="reading-content"
|
||||
dangerouslySetInnerHTML={{ __html: content }}
|
||||
style={{
|
||||
// Prevent layout shifts that might cause image reloads
|
||||
minHeight: '100vh',
|
||||
contain: 'layout style'
|
||||
}}
|
||||
/>
|
||||
);
|
||||
});
|
||||
@@ -112,14 +150,14 @@ export default function StoryReadingPage() {
|
||||
// Debounced function to save reading position
|
||||
const saveReadingPosition = useCallback(async (position: number) => {
|
||||
if (!story || position === story.readingPosition) {
|
||||
console.log('Skipping save - no story or position unchanged:', { story: !!story, position, current: story?.readingPosition });
|
||||
debug.log('Skipping save - no story or position unchanged:', { story: !!story, position, current: story?.readingPosition });
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('Saving reading position:', position, 'for story:', story.id);
|
||||
debug.log('Saving reading position:', position, 'for story:', story.id);
|
||||
try {
|
||||
const updatedStory = await storyApi.updateReadingProgress(story.id, position);
|
||||
console.log('Reading position saved successfully, updated story:', updatedStory.readingPosition);
|
||||
debug.log('Reading position saved successfully, updated story:', updatedStory.readingPosition);
|
||||
setStory(prev => prev ? { ...prev, readingPosition: position, lastReadAt: updatedStory.lastReadAt } : null);
|
||||
} catch (error) {
|
||||
console.error('Failed to save reading position:', error);
|
||||
@@ -200,12 +238,12 @@ export default function StoryReadingPage() {
|
||||
if (story && sanitizedContent && !hasScrolledToPosition) {
|
||||
// Use a small delay to ensure content is rendered
|
||||
const timeout = setTimeout(() => {
|
||||
console.log('Initializing reading position tracking, saved position:', story.readingPosition);
|
||||
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-')) {
|
||||
console.log('Auto-scrolling to heading from URL hash:', hash);
|
||||
debug.log('Auto-scrolling to heading from URL hash:', hash);
|
||||
const element = document.getElementById(hash);
|
||||
if (element) {
|
||||
element.scrollIntoView({
|
||||
@@ -219,13 +257,13 @@ export default function StoryReadingPage() {
|
||||
|
||||
// Otherwise, use saved reading position
|
||||
if (story.readingPosition && story.readingPosition > 0) {
|
||||
console.log('Auto-scrolling to saved position:', story.readingPosition);
|
||||
debug.log('Auto-scrolling to saved position:', story.readingPosition);
|
||||
const initialPercentage = calculateReadingPercentage(story.readingPosition);
|
||||
setReadingPercentage(initialPercentage);
|
||||
scrollToCharacterPosition(story.readingPosition);
|
||||
} else {
|
||||
// Even if there's no saved position, mark as ready for tracking
|
||||
console.log('No saved position, starting fresh tracking');
|
||||
debug.log('No saved position, starting fresh tracking');
|
||||
setReadingPercentage(0);
|
||||
setHasScrolledToPosition(true);
|
||||
}
|
||||
@@ -238,8 +276,14 @@ export default function StoryReadingPage() {
|
||||
// Track reading progress and save position
|
||||
useEffect(() => {
|
||||
let ticking = false;
|
||||
let scrollEventCount = 0;
|
||||
|
||||
const handleScroll = () => {
|
||||
scrollEventCount++;
|
||||
if (scrollEventCount % 10 === 0) {
|
||||
debug.log('📜 Scroll event #', scrollEventCount, 'at', Date.now());
|
||||
}
|
||||
|
||||
if (!ticking) {
|
||||
requestAnimationFrame(() => {
|
||||
const article = document.querySelector('[data-reading-content]') as HTMLElement;
|
||||
@@ -278,7 +322,7 @@ export default function StoryReadingPage() {
|
||||
|
||||
// Trigger end detection if user is near bottom AND (has high progress OR story content is fully visible)
|
||||
if (nearBottom && (highProgress || storyContentFullyVisible) && !hasReachedEnd && hasScrolledToPosition) {
|
||||
console.log('End of story detected:', { nearBottom, highProgress, storyContentFullyVisible, distanceFromBottom, progress });
|
||||
debug.log('End of story detected:', { nearBottom, highProgress, storyContentFullyVisible, distanceFromBottom, progress });
|
||||
setHasReachedEnd(true);
|
||||
setShowEndOfStoryPopup(true);
|
||||
}
|
||||
@@ -287,11 +331,11 @@ export default function StoryReadingPage() {
|
||||
if (hasScrolledToPosition) { // Only save after initial auto-scroll
|
||||
const characterPosition = getCharacterPositionFromScroll();
|
||||
const percentage = calculateReadingPercentage(characterPosition);
|
||||
console.log('Scroll detected, character position:', characterPosition, 'percentage:', percentage);
|
||||
debug.log('Scroll detected, character position:', characterPosition, 'percentage:', percentage);
|
||||
setReadingPercentage(percentage);
|
||||
debouncedSavePosition(characterPosition);
|
||||
} else {
|
||||
console.log('Scroll detected but not ready for tracking yet');
|
||||
debug.log('Scroll detected but not ready for tracking yet');
|
||||
}
|
||||
}
|
||||
ticking = false;
|
||||
|
||||
Reference in New Issue
Block a user