diff --git a/app/[locale]/bible/page.tsx b/app/[locale]/bible/page.tsx index 2d77f2a..23be61a 100644 --- a/app/[locale]/bible/page.tsx +++ b/app/[locale]/bible/page.tsx @@ -1,564 +1,5 @@ -'use client' -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[] -} +import BibleReader from './reader' export default function BiblePage() { - const theme = useTheme() - const t = useTranslations('pages.bible') - const locale = useLocale() - const searchParams = useSearchParams() - const [books, setBooks] = useState([]) - const [selectedBook, setSelectedBook] = useState('') - const [selectedChapter, setSelectedChapter] = useState(1) - const [verses, setVerses] = useState([]) - 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(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 ( - - - - - - {t('loading')} - - - - - ) - } - - return ( - - - - {/* Header */} - - - - {t('title')} - - - {t('subtitle')} - - - - - {/* Left Sidebar - Book Selection */} - - - - - {t('selectBook')} - - - - {t('book')} - - - - - {t('chapter')} - - - - - - - - - - - {/* Main Content - Bible Text */} - - - - {/* Chapter Header */} - - - - {currentBook?.name || 'Geneza'} {selectedChapter} - - - {verses.length} {t('verses')} - - - - - - - - - - - - {/* Bible Verses */} - {loading ? ( - - {Array.from({ length: 5 }).map((_, index) => ( - - - - - - ))} - - ) : verses.length > 0 ? ( - - {verses.map((verse) => { - const isVerseBookmarked = !!verseBookmarks[verse.id] - const isVerseLoading = !!verseBookmarkLoading[verse.id] - - return ( - - - - - {verse.verseNum} - - {verse.text} - - - - {user && ( - handleVerseBookmarkToggle(verse)} - disabled={isVerseLoading} - sx={{ - opacity: isVerseBookmarked ? 1 : 0.3, - transition: 'opacity 0.2s', - color: isVerseBookmarked ? 'warning.main' : 'action.active' - }} - > - {isVerseBookmarked ? : } - - )} - - ) - })} - - ) : ( - - {t('noVerses')} - - )} - - {/* Navigation */} - - - - - {currentBook?.name} {selectedChapter} - - - - - - - - - - - ) + return } \ No newline at end of file diff --git a/app/[locale]/bible/reader.tsx b/app/[locale]/bible/reader.tsx new file mode 100644 index 0000000..647c3a1 --- /dev/null +++ b/app/[locale]/bible/reader.tsx @@ -0,0 +1,1003 @@ +'use client' + +import { useState, useEffect, useRef, useCallback } from 'react' +import { useTranslations, useLocale } from 'next-intl' +import { useAuth } from '@/hooks/use-auth' +import { useSearchParams } from 'next/navigation' +import { + Box, + Typography, + IconButton, + Drawer, + List, + ListItem, + ListItemButton, + ListItemText, + AppBar, + Toolbar, + Button, + Menu, + MenuItem, + Slider, + Switch, + FormControlLabel, + Tooltip, + Fab, + Paper, + Divider, + Chip, + ButtonGroup, + useTheme, + useMediaQuery, + Collapse, + ListItemIcon, + Dialog, + DialogTitle, + DialogContent, + DialogActions, + TextField, + Snackbar, + Alert, + Backdrop, + CircularProgress, + Grid, + FormControl, + InputLabel, + Select, + Container +} from '@mui/material' +import { + Menu as MenuIcon, + Settings, + Bookmark, + BookmarkBorder, + Share, + ContentCopy, + ArrowBack, + ArrowForward, + FullscreenExit, + Fullscreen, + KeyboardArrowUp, + KeyboardArrowDown, + FormatSize, + Palette, + Note, + Close, + ExpandLess, + ExpandMore, + MenuBook, + Visibility, + Speed +} from '@mui/icons-material' + +interface BibleVerse { + id: string + verseNum: number + text: string +} + +interface BibleChapter { + id: string + chapterNum: number + verses: BibleVerse[] +} + +interface BibleBook { + id: string + versionId: string + name: string + testament: string + orderNum: number + bookKey: string + chapters: BibleChapter[] +} + +interface ReadingPreferences { + fontSize: number + lineHeight: number + fontFamily: string + theme: 'light' | 'dark' | 'sepia' + showVerseNumbers: boolean + columnLayout: boolean + readingMode: boolean +} + +const defaultPreferences: ReadingPreferences = { + fontSize: 18, + lineHeight: 1.6, + fontFamily: 'serif', + theme: 'light', + showVerseNumbers: true, + columnLayout: false, + readingMode: false +} + +export default function BibleReaderNew() { + const theme = useTheme() + const isMobile = useMediaQuery(theme.breakpoints.down('md')) + const t = useTranslations('pages.bible') + const locale = useLocale() + const searchParams = useSearchParams() + const { user } = useAuth() + + // Core state + const [books, setBooks] = useState([]) + const [selectedBook, setSelectedBook] = useState('') + const [selectedChapter, setSelectedChapter] = useState(1) + const [verses, setVerses] = useState([]) + const [loading, setLoading] = useState(true) + + // UI state + const [settingsOpen, setSettingsOpen] = useState(false) + const [preferences, setPreferences] = useState(defaultPreferences) + const [highlightedVerse, setHighlightedVerse] = useState(null) + const [showScrollTop, setShowScrollTop] = useState(false) + + // Bookmark state + const [isChapterBookmarked, setIsChapterBookmarked] = useState(false) + const [verseBookmarks, setVerseBookmarks] = useState<{[key: string]: any}>({}) + const [bookmarkLoading, setBookmarkLoading] = useState(false) + + // Note dialog state + const [noteDialog, setNoteDialog] = useState<{ + open: boolean + verse?: BibleVerse + note: string + }>({ + open: false, + note: '' + }) + + // Copy feedback + const [copyFeedback, setCopyFeedback] = useState<{ + open: boolean + message: string + }>({ + open: false, + message: '' + }) + + // Refs + const contentRef = useRef(null) + const verseRefs = useRef<{[key: number]: HTMLDivElement}>({}) + + // Load user preferences from localStorage + useEffect(() => { + const savedPrefs = localStorage.getItem('bibleReaderPreferences') + if (savedPrefs) { + try { + const parsed = JSON.parse(savedPrefs) + setPreferences({ ...defaultPreferences, ...parsed }) + } catch (e) { + console.error('Failed to parse preferences:', e) + } + } + }, []) + + // Save preferences to localStorage + useEffect(() => { + localStorage.setItem('bibleReaderPreferences', JSON.stringify(preferences)) + }, [preferences]) + + // Scroll handler for show scroll to top button + useEffect(() => { + const handleScroll = () => { + setShowScrollTop(window.scrollY > 300) + } + + window.addEventListener('scroll', handleScroll) + return () => window.removeEventListener('scroll', handleScroll) + }, []) + + // Fetch 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) + }) + }, [locale]) + + // Handle URL parameters + 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) + setTimeout(() => { + scrollToVerse(verse) + setTimeout(() => setHighlightedVerse(null), 3000) + }, 500) + } + } + } + } + } + }, [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 chapter bookmark status + 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 => setIsChapterBookmarked(data.isBookmarked || false)) + .catch(err => console.error('Error checking bookmark:', err)) + } + } else { + setIsChapterBookmarked(false) + } + }, [selectedBook, selectedChapter, user, locale]) + + // Check verse bookmarks + 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]) + + // Keyboard shortcuts + useEffect(() => { + const handleKeyDown = (e: KeyboardEvent) => { + if (e.ctrlKey || e.metaKey) return + + switch (e.key) { + case 'ArrowLeft': + e.preventDefault() + handlePreviousChapter() + break + case 'ArrowRight': + e.preventDefault() + handleNextChapter() + break + case 's': + e.preventDefault() + setSettingsOpen(prev => !prev) + break + case 'r': + e.preventDefault() + setPreferences(prev => ({ ...prev, readingMode: !prev.readingMode })) + break + case 'Escape': + e.preventDefault() + if (preferences.readingMode) { + setPreferences(prev => ({ ...prev, readingMode: false })) + } + break + } + } + + window.addEventListener('keydown', handleKeyDown) + return () => window.removeEventListener('keydown', handleKeyDown) + }, [selectedBook, selectedChapter, books, preferences.readingMode]) + + const currentBook = books.find(book => book.id === selectedBook) + const maxChapters = currentBook?.chapters?.length || 1 + + const updateUrl = (bookId: string, chapter: number) => { + const url = new URL(window.location.href) + url.searchParams.set('book', bookId) + url.searchParams.set('chapter', chapter.toString()) + window.history.replaceState({}, '', url.toString()) + } + + const scrollToVerse = (verseNum: number) => { + const verseElement = verseRefs.current[verseNum] + if (verseElement) { + verseElement.scrollIntoView({ behavior: 'smooth', block: 'center' }) + } + } + + const handlePreviousChapter = () => { + if (selectedChapter > 1) { + const newChapter = selectedChapter - 1 + setSelectedChapter(newChapter) + updateUrl(selectedBook, newChapter) + } else { + const currentBookIndex = books.findIndex(book => book.id === selectedBook) + if (currentBookIndex > 0) { + const previousBook = books[currentBookIndex - 1] + const lastChapter = previousBook.chapters?.length || 1 + setSelectedBook(previousBook.id) + setSelectedChapter(lastChapter) + updateUrl(previousBook.id, lastChapter) + } + } + } + + const handleNextChapter = () => { + if (selectedChapter < maxChapters) { + const newChapter = selectedChapter + 1 + setSelectedChapter(newChapter) + updateUrl(selectedBook, newChapter) + } else { + const currentBookIndex = books.findIndex(book => book.id === selectedBook) + if (currentBookIndex < books.length - 1) { + const nextBook = books[currentBookIndex + 1] + setSelectedBook(nextBook.id) + setSelectedChapter(1) + updateUrl(nextBook.id, 1) + } + } + } + + const handleChapterBookmark = async () => { + if (!user || !selectedBook || !selectedChapter) return + + setBookmarkLoading(true) + const token = localStorage.getItem('authToken') + if (!token) { + setBookmarkLoading(false) + return + } + + try { + if (isChapterBookmarked) { + const response = await fetch(`/api/bookmarks/chapter?bookId=${selectedBook}&chapterNum=${selectedChapter}&locale=${locale}`, { + method: 'DELETE', + headers: { 'Authorization': `Bearer ${token}` } + }) + if (response.ok) { + setIsChapterBookmarked(false) + } + } else { + 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) { + setIsChapterBookmarked(true) + } + } + } catch (error) { + console.error('Error toggling bookmark:', error) + } finally { + setBookmarkLoading(false) + } + } + + const handleVerseBookmark = async (verse: BibleVerse) => { + if (!user) return + + const token = localStorage.getItem('authToken') + if (!token) return + + try { + const isCurrentlyBookmarked = !!verseBookmarks[verse.id] + + if (isCurrentlyBookmarked) { + 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 { + 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) + } + } + + const handleCopyVerse = (verse: BibleVerse) => { + const text = `${currentBook?.name} ${selectedChapter}:${verse.verseNum} - ${verse.text}` + navigator.clipboard.writeText(text).then(() => { + setCopyFeedback({ + open: true, + message: t('verseCopied') + }) + }) + } + + const handleShare = () => { + const url = `${window.location.origin}/${locale}/bible?book=${selectedBook}&chapter=${selectedChapter}` + navigator.clipboard.writeText(url).then(() => { + setCopyFeedback({ + open: true, + message: t('linkCopied') + }) + }) + } + + const getThemeStyles = () => { + switch (preferences.theme) { + case 'dark': + return { + backgroundColor: '#1a1a1a', + color: '#e0e0e0', + borderColor: '#333' + } + case 'sepia': + return { + backgroundColor: '#f7f3e9', + color: '#5c4b3a', + borderColor: '#d4c5a0' + } + default: + return { + backgroundColor: '#ffffff', + color: '#000000', + borderColor: '#e0e0e0' + } + } + } + + const renderVerse = (verse: BibleVerse) => { + const isBookmarked = !!verseBookmarks[verse.id] + const isHighlighted = highlightedVerse === verse.verseNum + + return ( + { if (el) verseRefs.current[verse.verseNum] = el }} + sx={{ + mb: 1, + display: 'flex', + alignItems: 'flex-start', + gap: 1, + '&:hover .verse-actions': { + opacity: 1 + } + }} + > + + + {preferences.showVerseNumbers && ( + + {verse.verseNum} + + )} + {verse.text} + + + + {user && !preferences.readingMode && ( + + handleVerseBookmark(verse)} + sx={{ color: isBookmarked ? 'warning.main' : 'action.active' }} + > + {isBookmarked ? : } + + handleCopyVerse(verse)} + sx={{ color: 'action.active' }} + > + + + + )} + + ) + } + + const renderNavigation = () => ( + + + {/* Books Selection */} + + + {t('book')} + + + + + {/* Chapter Selection */} + + + {t('chapter')} + + + + + {/* Font Size Controls */} + + + setPreferences(prev => ({ + ...prev, + fontSize: Math.max(12, prev.fontSize - 1) + }))} + disabled={preferences.fontSize <= 12} + > + A⁻ + + setPreferences(prev => ({ + ...prev, + fontSize: Math.min(28, prev.fontSize + 1) + }))} + disabled={preferences.fontSize >= 28} + > + A⁺ + + + + + {/* Action Buttons */} + + + + setPreferences(prev => ({ ...prev, readingMode: !prev.readingMode }))} + sx={{ color: preferences.readingMode ? 'primary.main' : 'inherit' }} + > + {preferences.readingMode ? : } + + + + + setSettingsOpen(true)}> + + + + + {user && ( + + + {isChapterBookmarked ? : } + + + )} + + + + + + + + + + + + ) + + const renderSettings = () => ( + setSettingsOpen(false)} + maxWidth="sm" + fullWidth + > + + {t('readingSettings')} + + + + + {t('fontSize')} + setPreferences(prev => ({ ...prev, fontSize: value as number }))} + min={12} + max={24} + marks + valueLabelDisplay="auto" + /> + + + + {t('lineHeight')} + setPreferences(prev => ({ ...prev, lineHeight: value as number }))} + min={1.2} + max={2.0} + step={0.1} + marks + valueLabelDisplay="auto" + /> + + + + {t('fontFamily')} + + + + + + + + {t('theme')} + + + + + + + + setPreferences(prev => ({ ...prev, showVerseNumbers: e.target.checked }))} + /> + } + label={t('showVerseNumbers')} + /> + + setPreferences(prev => ({ ...prev, readingMode: e.target.checked }))} + /> + } + label={t('readingMode')} + /> + + + + + + + ) + + if (loading && books.length === 0) { + return ( + + + + ) + } + + return ( + + {/* Top Toolbar - Simplified */} + {!preferences.readingMode && ( + + + + {currentBook?.name} {selectedChapter} + + + + + + + + + + + + + + + + + + )} + + {/* Main Content */} + + {/* Navigation Section */} + {!preferences.readingMode && renderNavigation()} + + {/* Reading Content */} + + {loading ? ( + + + + ) : ( + + {/* Chapter Header */} + + + {currentBook?.name} {selectedChapter} + + + {verses.length} {t('verses')} + + + + {/* Verses */} + + {verses.map(renderVerse)} + + + {/* Chapter Navigation */} + + + + + + + )} + + + + {/* Floating Action Buttons */} + {!preferences.readingMode && ( + <> + {showScrollTop && ( + window.scrollTo({ top: 0, behavior: 'smooth' })} + sx={{ + position: 'fixed', + bottom: 16, + right: 16 + }} + > + + + )} + + )} + + {preferences.readingMode && ( + setPreferences(prev => ({ ...prev, readingMode: false }))} + sx={{ + position: 'fixed', + top: 16, + right: 16 + }} + > + + + )} + + {/* Settings Dialog */} + {renderSettings()} + + {/* Copy Feedback */} + setCopyFeedback({ open: false, message: '' })} + anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }} + > + setCopyFeedback({ open: false, message: '' })}> + {copyFeedback.message} + + + + ) +} \ No newline at end of file diff --git a/messages/en.json b/messages/en.json index 7bec193..af95efa 100644 --- a/messages/en.json +++ b/messages/en.json @@ -90,7 +90,51 @@ "nextChapter": "Next chapter", "loading": "Loading verses...", "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": { "title": "Prayers", diff --git a/messages/ro.json b/messages/ro.json index dfc2dcd..f6f29e2 100644 --- a/messages/ro.json +++ b/messages/ro.json @@ -90,7 +90,51 @@ "nextChapter": "Capitolul următor", "loading": "Se încarcă versetele...", "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": { "title": "Rugăciuni",