Create enhanced Bible reader with modern UX and compact navigation
- Replace old Bible page with comprehensive reader component - Add customizable reading preferences (font size, themes, line height) - Implement in-page navigation instead of sidebar layout - Add quick font size controls (A- / A+) in navigation bar - Create fullscreen reading mode with distraction-free experience - Include verse-level bookmarking and copy functionality - Add keyboard shortcuts for navigation and settings - Support URL parameters for bookmark navigation - Responsive design optimized for mobile and desktop - Complete internationalization for Romanian and English - Center navigation controls for better visual balance 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,564 +1,5 @@
|
|||||||
'use client'
|
import BibleReader from './reader'
|
||||||
import {
|
|
||||||
Container,
|
|
||||||
Grid,
|
|
||||||
Card,
|
|
||||||
CardContent,
|
|
||||||
Typography,
|
|
||||||
Box,
|
|
||||||
Select,
|
|
||||||
MenuItem,
|
|
||||||
FormControl,
|
|
||||||
InputLabel,
|
|
||||||
Paper,
|
|
||||||
List,
|
|
||||||
ListItem,
|
|
||||||
ListItemButton,
|
|
||||||
ListItemText,
|
|
||||||
Divider,
|
|
||||||
Button,
|
|
||||||
Chip,
|
|
||||||
useTheme,
|
|
||||||
CircularProgress,
|
|
||||||
Skeleton,
|
|
||||||
IconButton,
|
|
||||||
} from '@mui/material'
|
|
||||||
import {
|
|
||||||
MenuBook,
|
|
||||||
NavigateBefore,
|
|
||||||
NavigateNext,
|
|
||||||
Bookmark,
|
|
||||||
BookmarkBorder,
|
|
||||||
Share,
|
|
||||||
} from '@mui/icons-material'
|
|
||||||
import { useState, useEffect } from 'react'
|
|
||||||
import { useTranslations, useLocale } from 'next-intl'
|
|
||||||
import { useAuth } from '@/hooks/use-auth'
|
|
||||||
import { useSearchParams } from 'next/navigation'
|
|
||||||
|
|
||||||
interface BibleVerse {
|
|
||||||
id: string
|
|
||||||
verseNum: number
|
|
||||||
text: string
|
|
||||||
}
|
|
||||||
|
|
||||||
interface BibleChapter {
|
|
||||||
id: string
|
|
||||||
chapterNum: number
|
|
||||||
verses: BibleVerse[]
|
|
||||||
}
|
|
||||||
|
|
||||||
interface BibleBook {
|
|
||||||
id: string
|
|
||||||
name: string
|
|
||||||
testament: string
|
|
||||||
orderNum: number
|
|
||||||
bookKey: string
|
|
||||||
chapters: BibleChapter[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function BiblePage() {
|
export default function BiblePage() {
|
||||||
const theme = useTheme()
|
return <BibleReader />
|
||||||
const t = useTranslations('pages.bible')
|
|
||||||
const locale = useLocale()
|
|
||||||
const searchParams = useSearchParams()
|
|
||||||
const [books, setBooks] = useState<BibleBook[]>([])
|
|
||||||
const [selectedBook, setSelectedBook] = useState<string>('')
|
|
||||||
const [selectedChapter, setSelectedChapter] = useState<number>(1)
|
|
||||||
const [verses, setVerses] = useState<BibleVerse[]>([])
|
|
||||||
const [loading, setLoading] = useState(true)
|
|
||||||
const [isBookmarked, setIsBookmarked] = useState(false)
|
|
||||||
const [bookmarkLoading, setBookmarkLoading] = useState(false)
|
|
||||||
const [verseBookmarks, setVerseBookmarks] = useState<{[key: string]: any}>({})
|
|
||||||
const [verseBookmarkLoading, setVerseBookmarkLoading] = useState<{[key: string]: boolean}>({})
|
|
||||||
const [highlightedVerse, setHighlightedVerse] = useState<number | null>(null)
|
|
||||||
const { user } = useAuth()
|
|
||||||
|
|
||||||
// Fetch available books
|
|
||||||
useEffect(() => {
|
|
||||||
fetch(`/api/bible/books?locale=${locale}`)
|
|
||||||
.then(res => res.json())
|
|
||||||
.then(data => {
|
|
||||||
setBooks(data.books || [])
|
|
||||||
if (data.books && data.books.length > 0) {
|
|
||||||
setSelectedBook(data.books[0].id)
|
|
||||||
}
|
|
||||||
setLoading(false)
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
console.error('Error fetching books:', err)
|
|
||||||
setLoading(false)
|
|
||||||
})
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
// Handle URL parameters for navigation from bookmarks
|
|
||||||
useEffect(() => {
|
|
||||||
if (books.length > 0) {
|
|
||||||
const bookParam = searchParams.get('book')
|
|
||||||
const chapterParam = searchParams.get('chapter')
|
|
||||||
const verseParam = searchParams.get('verse')
|
|
||||||
|
|
||||||
if (bookParam) {
|
|
||||||
const book = books.find(b => b.id === bookParam)
|
|
||||||
if (book) {
|
|
||||||
setSelectedBook(bookParam)
|
|
||||||
|
|
||||||
if (chapterParam) {
|
|
||||||
const chapter = parseInt(chapterParam)
|
|
||||||
if (chapter > 0) {
|
|
||||||
setSelectedChapter(chapter)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (verseParam) {
|
|
||||||
const verse = parseInt(verseParam)
|
|
||||||
if (verse > 0) {
|
|
||||||
setHighlightedVerse(verse)
|
|
||||||
// Clear highlight after 3 seconds
|
|
||||||
setTimeout(() => setHighlightedVerse(null), 3000)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [books, searchParams])
|
|
||||||
|
|
||||||
// Fetch verses when book/chapter changes
|
|
||||||
useEffect(() => {
|
|
||||||
if (selectedBook && selectedChapter) {
|
|
||||||
setLoading(true)
|
|
||||||
fetch(`/api/bible/verses?bookId=${selectedBook}&chapter=${selectedChapter}`)
|
|
||||||
.then(res => res.json())
|
|
||||||
.then(data => {
|
|
||||||
setVerses(data.verses || [])
|
|
||||||
setLoading(false)
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
console.error('Error fetching verses:', err)
|
|
||||||
setLoading(false)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}, [selectedBook, selectedChapter])
|
|
||||||
|
|
||||||
// Check if chapter is bookmarked
|
|
||||||
useEffect(() => {
|
|
||||||
if (selectedBook && selectedChapter && user) {
|
|
||||||
const token = localStorage.getItem('authToken')
|
|
||||||
if (token) {
|
|
||||||
fetch(`/api/bookmarks/chapter/check?bookId=${selectedBook}&chapterNum=${selectedChapter}&locale=${locale}`, {
|
|
||||||
headers: {
|
|
||||||
'Authorization': `Bearer ${token}`
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.then(res => res.json())
|
|
||||||
.then(data => {
|
|
||||||
setIsBookmarked(data.isBookmarked || false)
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
console.error('Error checking bookmark:', err)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
setIsBookmarked(false)
|
|
||||||
}
|
|
||||||
}, [selectedBook, selectedChapter, user, locale])
|
|
||||||
|
|
||||||
// Check verse bookmarks when verses change
|
|
||||||
useEffect(() => {
|
|
||||||
if (verses.length > 0 && user) {
|
|
||||||
const token = localStorage.getItem('authToken')
|
|
||||||
if (token) {
|
|
||||||
const verseIds = verses.map(verse => verse.id)
|
|
||||||
fetch(`/api/bookmarks/verse/bulk-check?locale=${locale}`, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'Authorization': `Bearer ${token}`
|
|
||||||
},
|
|
||||||
body: JSON.stringify({ verseIds })
|
|
||||||
})
|
|
||||||
.then(res => res.json())
|
|
||||||
.then(data => {
|
|
||||||
setVerseBookmarks(data.bookmarks || {})
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
console.error('Error checking verse bookmarks:', err)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
setVerseBookmarks({})
|
|
||||||
}
|
|
||||||
}, [verses, user, locale])
|
|
||||||
|
|
||||||
const currentBook = books.find(book => book.id === selectedBook)
|
|
||||||
const maxChapters = currentBook?.chapters?.length || 50 // Default fallback
|
|
||||||
|
|
||||||
const handlePreviousChapter = () => {
|
|
||||||
if (selectedChapter > 1) {
|
|
||||||
setSelectedChapter(selectedChapter - 1)
|
|
||||||
} else if (selectedBook > 1) {
|
|
||||||
setSelectedBook(selectedBook - 1)
|
|
||||||
setSelectedChapter(50) // Will be adjusted by actual chapter count
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleNextChapter = () => {
|
|
||||||
if (selectedChapter < maxChapters) {
|
|
||||||
setSelectedChapter(selectedChapter + 1)
|
|
||||||
} else {
|
|
||||||
const nextBook = books.find(book => book.id === selectedBook + 1)
|
|
||||||
if (nextBook) {
|
|
||||||
setSelectedBook(selectedBook + 1)
|
|
||||||
setSelectedChapter(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleBookmarkToggle = async () => {
|
|
||||||
if (!user || !selectedBook || !selectedChapter) return
|
|
||||||
|
|
||||||
setBookmarkLoading(true)
|
|
||||||
const token = localStorage.getItem('authToken')
|
|
||||||
|
|
||||||
if (!token) {
|
|
||||||
setBookmarkLoading(false)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (isBookmarked) {
|
|
||||||
// Remove bookmark
|
|
||||||
const response = await fetch(`/api/bookmarks/chapter?bookId=${selectedBook}&chapterNum=${selectedChapter}&locale=${locale}`, {
|
|
||||||
method: 'DELETE',
|
|
||||||
headers: {
|
|
||||||
'Authorization': `Bearer ${token}`
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
if (response.ok) {
|
|
||||||
setIsBookmarked(false)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Add bookmark
|
|
||||||
const response = await fetch(`/api/bookmarks/chapter?locale=${locale}`, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'Authorization': `Bearer ${token}`
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
bookId: selectedBook,
|
|
||||||
chapterNum: selectedChapter
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
if (response.ok) {
|
|
||||||
setIsBookmarked(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error toggling bookmark:', error)
|
|
||||||
} finally {
|
|
||||||
setBookmarkLoading(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleVerseBookmarkToggle = async (verse: BibleVerse) => {
|
|
||||||
if (!user) return
|
|
||||||
|
|
||||||
setVerseBookmarkLoading(prev => ({ ...prev, [verse.id]: true }))
|
|
||||||
const token = localStorage.getItem('authToken')
|
|
||||||
|
|
||||||
if (!token) {
|
|
||||||
setVerseBookmarkLoading(prev => ({ ...prev, [verse.id]: false }))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const isCurrentlyBookmarked = !!verseBookmarks[verse.id]
|
|
||||||
|
|
||||||
if (isCurrentlyBookmarked) {
|
|
||||||
// Remove verse bookmark
|
|
||||||
const response = await fetch(`/api/bookmarks/verse?verseId=${verse.id}&locale=${locale}`, {
|
|
||||||
method: 'DELETE',
|
|
||||||
headers: {
|
|
||||||
'Authorization': `Bearer ${token}`
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
if (response.ok) {
|
|
||||||
setVerseBookmarks(prev => {
|
|
||||||
const newBookmarks = { ...prev }
|
|
||||||
delete newBookmarks[verse.id]
|
|
||||||
return newBookmarks
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Add verse bookmark
|
|
||||||
const response = await fetch(`/api/bookmarks/verse?locale=${locale}`, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'Authorization': `Bearer ${token}`
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
verseId: verse.id
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
if (response.ok) {
|
|
||||||
const data = await response.json()
|
|
||||||
setVerseBookmarks(prev => ({
|
|
||||||
...prev,
|
|
||||||
[verse.id]: data.bookmark
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error toggling verse bookmark:', error)
|
|
||||||
} finally {
|
|
||||||
setVerseBookmarkLoading(prev => ({ ...prev, [verse.id]: false }))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (loading && books.length === 0) {
|
|
||||||
return (
|
|
||||||
<Box>
|
|
||||||
<Container maxWidth="lg" sx={{ py: 4 }}>
|
|
||||||
<Box sx={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 2 }}>
|
|
||||||
<CircularProgress size={48} />
|
|
||||||
<Typography variant="h6" color="text.secondary">
|
|
||||||
{t('loading')}
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
</Container>
|
|
||||||
</Box>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Box>
|
|
||||||
|
|
||||||
<Container maxWidth="lg" sx={{ py: 4 }}>
|
|
||||||
{/* Header */}
|
|
||||||
<Box sx={{ mb: 4, textAlign: 'center' }}>
|
|
||||||
<Typography variant="h3" component="h1" gutterBottom>
|
|
||||||
<MenuBook sx={{ fontSize: 40, mr: 2, verticalAlign: 'middle' }} />
|
|
||||||
{t('title')}
|
|
||||||
</Typography>
|
|
||||||
<Typography variant="body1" color="text.secondary">
|
|
||||||
{t('subtitle')}
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<Grid container spacing={4}>
|
|
||||||
{/* Left Sidebar - Book Selection */}
|
|
||||||
<Grid item xs={12} md={3}>
|
|
||||||
<Card>
|
|
||||||
<CardContent>
|
|
||||||
<Typography variant="h6" gutterBottom>
|
|
||||||
{t('selectBook')}
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<FormControl fullWidth sx={{ mb: 2 }}>
|
|
||||||
<InputLabel>{t('book')}</InputLabel>
|
|
||||||
<Select
|
|
||||||
value={selectedBook}
|
|
||||||
label={t('book')}
|
|
||||||
onChange={(e) => {
|
|
||||||
setSelectedBook(e.target.value)
|
|
||||||
setSelectedChapter(1)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{books.map((book) => (
|
|
||||||
<MenuItem key={book.id} value={book.id}>
|
|
||||||
{book.name}
|
|
||||||
</MenuItem>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<FormControl fullWidth>
|
|
||||||
<InputLabel>{t('chapter')}</InputLabel>
|
|
||||||
<Select
|
|
||||||
value={selectedChapter}
|
|
||||||
label={t('chapter')}
|
|
||||||
onChange={(e) => setSelectedChapter(Number(e.target.value))}
|
|
||||||
>
|
|
||||||
{Array.from({ length: maxChapters }, (_, i) => (
|
|
||||||
<MenuItem key={i + 1} value={i + 1}>
|
|
||||||
{t('chapter')} {i + 1}
|
|
||||||
</MenuItem>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<Box sx={{ mt: 2, display: 'flex', gap: 1 }}>
|
|
||||||
<Chip
|
|
||||||
label={currentBook?.testament || 'Vechiul Testament'}
|
|
||||||
size="small"
|
|
||||||
color="primary"
|
|
||||||
variant="outlined"
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
{/* Main Content - Bible Text */}
|
|
||||||
<Grid item xs={12} md={9}>
|
|
||||||
<Card>
|
|
||||||
<CardContent>
|
|
||||||
{/* Chapter Header */}
|
|
||||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 3 }}>
|
|
||||||
<Box>
|
|
||||||
<Typography variant="h4" component="h2">
|
|
||||||
{currentBook?.name || 'Geneza'} {selectedChapter}
|
|
||||||
</Typography>
|
|
||||||
<Typography variant="body2" color="text.secondary">
|
|
||||||
{verses.length} {t('verses')}
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<Box sx={{ display: 'flex', gap: 1 }}>
|
|
||||||
<Button
|
|
||||||
startIcon={<Bookmark />}
|
|
||||||
variant={isBookmarked ? "contained" : "outlined"}
|
|
||||||
size="small"
|
|
||||||
onClick={handleBookmarkToggle}
|
|
||||||
disabled={!user || bookmarkLoading}
|
|
||||||
color={isBookmarked ? "primary" : "inherit"}
|
|
||||||
>
|
|
||||||
{bookmarkLoading ? t('saving') : (isBookmarked ? t('saved') : t('save'))}
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
startIcon={<Share />}
|
|
||||||
variant="outlined"
|
|
||||||
size="small"
|
|
||||||
>
|
|
||||||
{t('share')}
|
|
||||||
</Button>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<Divider sx={{ mb: 3 }} />
|
|
||||||
|
|
||||||
{/* Bible Verses */}
|
|
||||||
{loading ? (
|
|
||||||
<Box>
|
|
||||||
{Array.from({ length: 5 }).map((_, index) => (
|
|
||||||
<Box key={index} sx={{ mb: 2 }}>
|
|
||||||
<Skeleton variant="text" width="100%" height={40} />
|
|
||||||
<Skeleton variant="text" width="90%" height={30} />
|
|
||||||
<Skeleton variant="text" width="95%" height={30} />
|
|
||||||
</Box>
|
|
||||||
))}
|
|
||||||
</Box>
|
|
||||||
) : verses.length > 0 ? (
|
|
||||||
<Box>
|
|
||||||
{verses.map((verse) => {
|
|
||||||
const isVerseBookmarked = !!verseBookmarks[verse.id]
|
|
||||||
const isVerseLoading = !!verseBookmarkLoading[verse.id]
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Box
|
|
||||||
key={verse.id}
|
|
||||||
sx={{
|
|
||||||
mb: 2,
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'flex-start',
|
|
||||||
gap: 1,
|
|
||||||
'&:hover .verse-bookmark': {
|
|
||||||
opacity: 1
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Box sx={{ flex: 1 }}>
|
|
||||||
<Typography
|
|
||||||
variant="body1"
|
|
||||||
component="p"
|
|
||||||
sx={{
|
|
||||||
lineHeight: 1.8,
|
|
||||||
fontSize: '1.1rem',
|
|
||||||
bgcolor: highlightedVerse === verse.verseNum
|
|
||||||
? 'primary.light'
|
|
||||||
: isVerseBookmarked
|
|
||||||
? 'warning.light'
|
|
||||||
: 'transparent',
|
|
||||||
borderRadius: (isVerseBookmarked || highlightedVerse === verse.verseNum) ? 1 : 0,
|
|
||||||
p: (isVerseBookmarked || highlightedVerse === verse.verseNum) ? 1 : 0,
|
|
||||||
transition: 'all 0.3s ease',
|
|
||||||
border: highlightedVerse === verse.verseNum ? '2px solid' : 'none',
|
|
||||||
borderColor: 'primary.main',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Typography
|
|
||||||
component="span"
|
|
||||||
sx={{
|
|
||||||
fontWeight: 'bold',
|
|
||||||
color: 'primary.main',
|
|
||||||
mr: 1,
|
|
||||||
fontSize: '0.9rem',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{verse.verseNum}
|
|
||||||
</Typography>
|
|
||||||
{verse.text}
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
{user && (
|
|
||||||
<IconButton
|
|
||||||
className="verse-bookmark"
|
|
||||||
size="small"
|
|
||||||
onClick={() => handleVerseBookmarkToggle(verse)}
|
|
||||||
disabled={isVerseLoading}
|
|
||||||
sx={{
|
|
||||||
opacity: isVerseBookmarked ? 1 : 0.3,
|
|
||||||
transition: 'opacity 0.2s',
|
|
||||||
color: isVerseBookmarked ? 'warning.main' : 'action.active'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{isVerseBookmarked ? <Bookmark fontSize="small" /> : <BookmarkBorder fontSize="small" />}
|
|
||||||
</IconButton>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</Box>
|
|
||||||
) : (
|
|
||||||
<Typography textAlign="center" color="text.secondary">
|
|
||||||
{t('noVerses')}
|
|
||||||
</Typography>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Navigation */}
|
|
||||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', mt: 4, pt: 3, borderTop: 1, borderColor: 'divider' }}>
|
|
||||||
<Button
|
|
||||||
startIcon={<NavigateBefore />}
|
|
||||||
onClick={handlePreviousChapter}
|
|
||||||
disabled={selectedBook === 1 && selectedChapter === 1}
|
|
||||||
>
|
|
||||||
{t('previousChapter')}
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Typography variant="body2" color="text.secondary" sx={{ alignSelf: 'center' }}>
|
|
||||||
{currentBook?.name} {selectedChapter}
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
endIcon={<NavigateNext />}
|
|
||||||
onClick={handleNextChapter}
|
|
||||||
disabled={selectedBook === books.length && selectedChapter === maxChapters}
|
|
||||||
>
|
|
||||||
{t('nextChapter')}
|
|
||||||
</Button>
|
|
||||||
</Box>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</Container>
|
|
||||||
</Box>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
1003
app/[locale]/bible/reader.tsx
Normal file
1003
app/[locale]/bible/reader.tsx
Normal file
File diff suppressed because it is too large
Load Diff
@@ -90,7 +90,51 @@
|
|||||||
"nextChapter": "Next chapter",
|
"nextChapter": "Next chapter",
|
||||||
"loading": "Loading verses...",
|
"loading": "Loading verses...",
|
||||||
"noVerses": "No verses found for this selection.",
|
"noVerses": "No verses found for this selection.",
|
||||||
"startReading": "Start exploring Scripture"
|
"startReading": "Start exploring Scripture",
|
||||||
|
"navigation": "Navigation",
|
||||||
|
"settings": "Reading Settings",
|
||||||
|
"readingSettings": "Reading Settings",
|
||||||
|
"preferences": "Preferences",
|
||||||
|
"fontSize": "Font Size",
|
||||||
|
"lineHeight": "Line Height",
|
||||||
|
"fontFamily": "Font Family",
|
||||||
|
"theme": "Theme",
|
||||||
|
"showVerseNumbers": "Show Verse Numbers",
|
||||||
|
"columnLayout": "Column Layout",
|
||||||
|
"readingMode": "Reading Mode",
|
||||||
|
"readingModeDesc": "Hide UI elements for distraction-free reading",
|
||||||
|
"resetPreferences": "Reset Preferences",
|
||||||
|
"keyboardShortcuts": "Keyboard Shortcuts",
|
||||||
|
"shortcuts": {
|
||||||
|
"navigation": "← → : Navigate chapters",
|
||||||
|
"sidebar": "B : Toggle sidebar",
|
||||||
|
"settings": "S : Open settings",
|
||||||
|
"readingMode": "R : Toggle reading mode",
|
||||||
|
"copy": "Ctrl+C : Copy current verse"
|
||||||
|
},
|
||||||
|
"themes": {
|
||||||
|
"light": "Light",
|
||||||
|
"dark": "Dark",
|
||||||
|
"sepia": "Sepia"
|
||||||
|
},
|
||||||
|
"fontFamilies": {
|
||||||
|
"system": "System Font",
|
||||||
|
"serif": "Serif",
|
||||||
|
"sans": "Sans-serif"
|
||||||
|
},
|
||||||
|
"copy": "Copy",
|
||||||
|
"copied": "Copied!",
|
||||||
|
"copyVerse": "Copy verse",
|
||||||
|
"scrollToTop": "Scroll to top",
|
||||||
|
"toggleSidebar": "Toggle sidebar",
|
||||||
|
"toggleSettings": "Toggle settings",
|
||||||
|
"toggleReadingMode": "Toggle reading mode",
|
||||||
|
"chapters": "chapters",
|
||||||
|
"addBookmark": "Add bookmark",
|
||||||
|
"removeBookmark": "Remove bookmark",
|
||||||
|
"bookmarkVerse": "Bookmark verse",
|
||||||
|
"removeVerseBookmark": "Remove verse bookmark",
|
||||||
|
"toggleFullscreen": "Toggle fullscreen"
|
||||||
},
|
},
|
||||||
"prayers": {
|
"prayers": {
|
||||||
"title": "Prayers",
|
"title": "Prayers",
|
||||||
|
|||||||
@@ -90,7 +90,51 @@
|
|||||||
"nextChapter": "Capitolul următor",
|
"nextChapter": "Capitolul următor",
|
||||||
"loading": "Se încarcă versetele...",
|
"loading": "Se încarcă versetele...",
|
||||||
"noVerses": "Nu s-au găsit versete pentru această selecție.",
|
"noVerses": "Nu s-au găsit versete pentru această selecție.",
|
||||||
"startReading": "Începe să explorezi Scriptura"
|
"startReading": "Începe să explorezi Scriptura",
|
||||||
|
"navigation": "Navigare",
|
||||||
|
"settings": "Setări de citire",
|
||||||
|
"readingSettings": "Setări de citire",
|
||||||
|
"preferences": "Preferințe",
|
||||||
|
"fontSize": "Mărimea fontului",
|
||||||
|
"lineHeight": "Înălțimea liniei",
|
||||||
|
"fontFamily": "Familie font",
|
||||||
|
"theme": "Temă",
|
||||||
|
"showVerseNumbers": "Afișează numerele versetelor",
|
||||||
|
"columnLayout": "Aspect cu coloane",
|
||||||
|
"readingMode": "Mod de citire",
|
||||||
|
"readingModeDesc": "Ascunde elementele UI pentru citire fără distrageri",
|
||||||
|
"resetPreferences": "Resetează preferințele",
|
||||||
|
"keyboardShortcuts": "Comenzi rapide tastatură",
|
||||||
|
"shortcuts": {
|
||||||
|
"navigation": "← → : Navigare capitole",
|
||||||
|
"sidebar": "B : Comută bara laterală",
|
||||||
|
"settings": "S : Deschide setări",
|
||||||
|
"readingMode": "R : Comută modul de citire",
|
||||||
|
"copy": "Ctrl+C : Copiază versetul curent"
|
||||||
|
},
|
||||||
|
"themes": {
|
||||||
|
"light": "Luminos",
|
||||||
|
"dark": "Întunecat",
|
||||||
|
"sepia": "Sepia"
|
||||||
|
},
|
||||||
|
"fontFamilies": {
|
||||||
|
"system": "Font sistem",
|
||||||
|
"serif": "Serif",
|
||||||
|
"sans": "Sans-serif"
|
||||||
|
},
|
||||||
|
"copy": "Copiază",
|
||||||
|
"copied": "Copiat!",
|
||||||
|
"copyVerse": "Copiază versetul",
|
||||||
|
"scrollToTop": "Înapoi la început",
|
||||||
|
"toggleSidebar": "Comută bara laterală",
|
||||||
|
"toggleSettings": "Comută setările",
|
||||||
|
"toggleReadingMode": "Comută modul de citire",
|
||||||
|
"chapters": "capitole",
|
||||||
|
"addBookmark": "Adaugă bookmark",
|
||||||
|
"removeBookmark": "Elimină bookmark",
|
||||||
|
"bookmarkVerse": "Bookmark verset",
|
||||||
|
"removeVerseBookmark": "Elimină bookmark verset",
|
||||||
|
"toggleFullscreen": "Comută ecran complet"
|
||||||
},
|
},
|
||||||
"prayers": {
|
"prayers": {
|
||||||
"title": "Rugăciuni",
|
"title": "Rugăciuni",
|
||||||
|
|||||||
Reference in New Issue
Block a user