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

@@ -120,7 +120,16 @@ export default async function BibleChapterPage({ params }: PageProps) {
// Pass the parameters as props instead of URLSearchParams
return (
<Suspense fallback={<div>Loading...</div>}>
<Suspense fallback={
<div style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
minHeight: '200px'
}}>
Loading Bible reader...
</div>
}>
<BibleReader
initialVersion={resources.versionId}
initialBook={resources.bookId}

View File

@@ -50,7 +50,16 @@ export default async function BiblePage({ searchParams, params }: PageProps) {
}
return (
<Suspense fallback={<div>Loading...</div>}>
<Suspense fallback={
<div style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
minHeight: '200px'
}}>
Loading Bible reader...
</div>
}>
<BibleReader />
</Suspense>
)

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,6 +1474,31 @@ 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' }}>
{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"
@@ -1493,11 +1512,63 @@ export default function BibleReaderNew({ initialVersion, initialBook, initialCha
<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 */}