diff --git a/app/[locale]/bible/reader.tsx b/app/[locale]/bible/reader.tsx index 8a4c833..a837bf5 100644 --- a/app/[locale]/bible/reader.tsx +++ b/app/[locale]/bible/reader.tsx @@ -4,6 +4,9 @@ import { useState, useEffect, useRef, useCallback } from 'react' import { useTranslations, useLocale } from 'next-intl' import { useAuth } from '@/hooks/use-auth' import { useSearchParams, useRouter } from 'next/navigation' +import { OfflineDownloadManager } from '@/components/bible/offline-download-manager' +import { OfflineBibleReader } from '@/components/bible/offline-bible-reader' +import { InstallPrompt, useInstallPrompt } from '@/components/pwa/install-prompt' import { Box, Typography, @@ -69,7 +72,10 @@ import { MenuBook, Visibility, Speed, - Chat + Chat, + CloudDownload, + WifiOff, + Storage } from '@mui/icons-material' interface BibleVerse { @@ -149,6 +155,11 @@ export default function BibleReaderNew() { const [showScrollTop, setShowScrollTop] = useState(false) const [previousVerses, setPreviousVerses] = useState([]) // Keep previous content during loading + // Offline/PWA state + const [isOnline, setIsOnline] = useState(true) + const [isOfflineMode, setIsOfflineMode] = useState(false) + const [offlineDialogOpen, setOfflineDialogOpen] = useState(false) + // Bookmark state const [isChapterBookmarked, setIsChapterBookmarked] = useState(false) const [verseBookmarks, setVerseBookmarks] = useState<{[key: string]: any}>({}) @@ -177,6 +188,9 @@ export default function BibleReaderNew() { const contentRef = useRef(null) const verseRefs = useRef<{[key: number]: HTMLDivElement}>({}) + // PWA install prompt + const { canInstall, isInstalled, showInstallPrompt } = useInstallPrompt() + // Load user preferences from localStorage useEffect(() => { const savedPrefs = localStorage.getItem('bibleReaderPreferences') @@ -235,6 +249,39 @@ export default function BibleReaderNew() { return () => window.removeEventListener('scroll', handleScroll) }, []) + // Online/offline detection + useEffect(() => { + const handleOnline = () => { + setIsOnline(true) + if (isOfflineMode) { + // Show notification that connection is restored + console.log('Connection restored, you can now access all features') + } + } + + const handleOffline = () => { + setIsOnline(false) + console.log('You are now offline. Only downloaded content is available.') + } + + // Set initial state + setIsOnline(navigator.onLine) + + // Check for offline mode preference + const offlineParam = new URLSearchParams(window.location.search).get('offline') + if (offlineParam === 'true') { + setIsOfflineMode(true) + } + + window.addEventListener('online', handleOnline) + window.addEventListener('offline', handleOffline) + + return () => { + window.removeEventListener('online', handleOnline) + window.removeEventListener('offline', handleOffline) + } + }, [isOfflineMode]) + // Fetch versions based on showAllVersions state and locale useEffect(() => { setVersionsLoading(true) @@ -1093,6 +1140,24 @@ export default function BibleReaderNew() { + + + setOfflineDialogOpen(true)} + sx={{ color: !isOnline ? 'warning.main' : 'inherit' }} + > + {isOnline ? : } + + + + {canInstall && !isInstalled && ( + + + + + + )} ) @@ -1386,6 +1451,38 @@ export default function BibleReaderNew() { {/* Settings Dialog */} {renderSettings()} + {/* Offline Downloads Dialog */} + setOfflineDialogOpen(false)} + maxWidth="md" + fullWidth + fullScreen={isMobile} + > + + + + Offline Bible Downloads + + + + { + console.log(`Version ${versionId} downloaded successfully`) + }} + /> + + + + + + + {/* PWA Install Prompt */} + + {/* Copy Feedback */} }): Promise { @@ -53,6 +54,7 @@ export async function generateMetadata({ params }: { params: Promise<{ locale: s description: t('twitterDescription'), images: [ogImageUrl], }, + manifest: '/manifest.json', other: { 'application/ld+json': JSON.stringify({ "@context": "https://schema.org", @@ -119,12 +121,14 @@ export default async function LocaleLayout({ - - - {children} -