fix: improve Bible reader loading UX with skeleton states

- Remove full-screen loading backdrop that hides entire UI
- Add skeleton loading components for chapter headers and verses
- Implement smooth content transitions without UI disappearance
- Change initial loading state to prevent immediate UI hide
- Enhance Suspense fallbacks with better loading messages
- Keep Bible reader interface visible during all loading states

Fixes issue where:
- Entire reader disappeared during chapter changes
- Users saw only header/footer during loading
- Poor perceived performance with jarring transitions

Now provides professional skeleton loading within the reader
interface for a smooth, responsive user experience.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-09-29 20:14:51 +00:00
parent b337b82fde
commit 44831a096f
3 changed files with 114 additions and 25 deletions

View File

@@ -175,7 +175,7 @@ export default function BibleReaderNew({ initialVersion, initialBook, initialCha
const [selectedBook, setSelectedBook] = useState<string>('')
const [selectedChapter, setSelectedChapter] = useState<number>(1)
const [verses, setVerses] = useState<BibleVerse[]>([])
const [loading, setLoading] = useState(true)
const [loading, setLoading] = useState(false)
const [versionsLoading, setVersionsLoading] = useState(true)
const [showAllVersions, setShowAllVersions] = useState(false)
@@ -1382,13 +1382,7 @@ export default function BibleReaderNew({ initialVersion, initialBook, initialCha
</Dialog>
)
if (loading && books.length === 0) {
return (
<Backdrop open>
<CircularProgress color="inherit" />
</Backdrop>
)
}
// Always render the UI - loading will be handled within components
return (
<Box
@@ -1480,24 +1474,101 @@ export default function BibleReaderNew({ initialVersion, initialBook, initialCha
<Box sx={{ opacity: loading ? 0.3 : 1, transition: 'opacity 0.3s ease' }}>
{/* Chapter Header */}
<Box sx={{ mb: 4, textAlign: 'center' }}>
<Typography
variant="h3"
component="h1"
sx={{
mb: 2,
fontFamily: preferences.fontFamily === 'serif' ? 'Georgia, serif' : 'Arial, sans-serif'
}}
>
{currentBook?.name} {selectedChapter}
</Typography>
<Typography variant="body2" color="text.secondary">
{(loading && previousVerses.length > 0 ? previousVerses : verses).length} {t('verses')}
</Typography>
{loading && !currentBook ? (
// Skeleton loading for chapter header
<>
<Box
sx={{
height: 40,
backgroundColor: 'action.hover',
borderRadius: 1,
mb: 2,
margin: '0 auto',
width: '200px'
}}
/>
<Box
sx={{
height: 16,
backgroundColor: 'action.hover',
borderRadius: 1,
margin: '0 auto',
width: '80px'
}}
/>
</>
) : (
<>
<Typography
variant="h3"
component="h1"
sx={{
mb: 2,
fontFamily: preferences.fontFamily === 'serif' ? 'Georgia, serif' : 'Arial, sans-serif'
}}
>
{currentBook?.name} {selectedChapter}
</Typography>
<Typography variant="body2" color="text.secondary">
{(loading && previousVerses.length > 0 ? previousVerses : verses).length} {t('verses')}
</Typography>
</>
)}
</Box>
{/* Verses */}
<Box sx={{ mb: 4 }}>
{(loading && previousVerses.length > 0 ? previousVerses : verses).map(renderVerse)}
{loading && verses.length === 0 && previousVerses.length === 0 ? (
// Skeleton loading for verses
<>
{Array.from({ length: 8 }).map((_, index) => (
<Box key={`skeleton-${index}`} sx={{ mb: 2, display: 'flex', alignItems: 'flex-start' }}>
<Box
sx={{
width: 32,
height: 20,
backgroundColor: 'action.hover',
borderRadius: 1,
mr: 2,
flexShrink: 0
}}
/>
<Box sx={{ width: '100%' }}>
<Box
sx={{
height: 16,
backgroundColor: 'action.hover',
borderRadius: 1,
mb: 1,
width: `${Math.random() * 40 + 60}%`
}}
/>
<Box
sx={{
height: 16,
backgroundColor: 'action.hover',
borderRadius: 1,
mb: 1,
width: `${Math.random() * 50 + 40}%`
}}
/>
{Math.random() > 0.5 && (
<Box
sx={{
height: 16,
backgroundColor: 'action.hover',
borderRadius: 1,
width: `${Math.random() * 30 + 20}%`
}}
/>
)}
</Box>
</Box>
))}
</>
) : (
(loading && previousVerses.length > 0 ? previousVerses : verses).map(renderVerse)
)}
</Box>
{/* Chapter Navigation */}