Add AI chat feature for verse explanations and fix login redirect handling
Bible Reader Enhancements: - Add chat icon to each verse for AI explanations - Implement handleVerseChat function with pre-filled contextual messages - Chat opens with message: "Explain in depth this verse [text] from [version], [book] [chapter:verse] and its meaning" - Visible to all users, redirects to login for unauthenticated users - Fix copy message translation from 'bible.copied' to 'copied' Login System Improvements: - Fix redirect parameter handling in login pages - Users are now properly redirected to /bible page after successful login - Preserve redirect URL parameters through login flow - Add Suspense boundaries for useSearchParams compliance - Ensure verse/chapter context is maintained after login Technical Changes: - Add Chat icon import from Material-UI - Implement floating chat event dispatch system - Fix Next.js 15 build warnings with proper Suspense wrapping - Maintain existing UX patterns (visible to all, functional for authenticated users) 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { useState } from 'react'
|
import { useState, Suspense } from 'react'
|
||||||
import { useRouter } from 'next/navigation'
|
import { useRouter, useSearchParams } from 'next/navigation'
|
||||||
import { useTranslations, useLocale } from 'next-intl'
|
import { useTranslations, useLocale } from 'next-intl'
|
||||||
import {
|
import {
|
||||||
Container,
|
Container,
|
||||||
@@ -13,7 +13,8 @@ import {
|
|||||||
Link,
|
Link,
|
||||||
Card,
|
Card,
|
||||||
CardContent,
|
CardContent,
|
||||||
Divider
|
Divider,
|
||||||
|
CircularProgress
|
||||||
} from '@mui/material'
|
} from '@mui/material'
|
||||||
import {
|
import {
|
||||||
MenuBook,
|
MenuBook,
|
||||||
@@ -23,14 +24,20 @@ import {
|
|||||||
import { LoginForm } from '@/components/auth/login-form'
|
import { LoginForm } from '@/components/auth/login-form'
|
||||||
import { RegisterForm } from '@/components/auth/register-form'
|
import { RegisterForm } from '@/components/auth/register-form'
|
||||||
|
|
||||||
export default function AuthPage() {
|
function AuthContent() {
|
||||||
const [activeTab, setActiveTab] = useState(0)
|
const [activeTab, setActiveTab] = useState(0)
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const locale = useLocale()
|
const locale = useLocale()
|
||||||
|
const searchParams = useSearchParams()
|
||||||
const t = useTranslations('auth')
|
const t = useTranslations('auth')
|
||||||
|
|
||||||
const handleAuthSuccess = () => {
|
const handleAuthSuccess = () => {
|
||||||
router.push(`/${locale}`)
|
const redirect = searchParams.get('redirect')
|
||||||
|
if (redirect) {
|
||||||
|
router.push(redirect)
|
||||||
|
} else {
|
||||||
|
router.push(`/${locale}`)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleTabChange = (event: React.SyntheticEvent, newValue: number) => {
|
const handleTabChange = (event: React.SyntheticEvent, newValue: number) => {
|
||||||
@@ -130,4 +137,18 @@ export default function AuthPage() {
|
|||||||
</Card>
|
</Card>
|
||||||
</Container>
|
</Container>
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function AuthPage() {
|
||||||
|
return (
|
||||||
|
<Suspense fallback={
|
||||||
|
<Container maxWidth="sm" sx={{ mt: 8 }}>
|
||||||
|
<Box display="flex" justifyContent="center" alignItems="center" minHeight="400px">
|
||||||
|
<CircularProgress />
|
||||||
|
</Box>
|
||||||
|
</Container>
|
||||||
|
}>
|
||||||
|
<AuthContent />
|
||||||
|
</Suspense>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
@@ -68,7 +68,8 @@ import {
|
|||||||
ExpandMore,
|
ExpandMore,
|
||||||
MenuBook,
|
MenuBook,
|
||||||
Visibility,
|
Visibility,
|
||||||
Speed
|
Speed,
|
||||||
|
Chat
|
||||||
} from '@mui/icons-material'
|
} from '@mui/icons-material'
|
||||||
|
|
||||||
interface BibleVerse {
|
interface BibleVerse {
|
||||||
@@ -664,11 +665,32 @@ export default function BibleReaderNew() {
|
|||||||
navigator.clipboard.writeText(text).then(() => {
|
navigator.clipboard.writeText(text).then(() => {
|
||||||
setCopyFeedback({
|
setCopyFeedback({
|
||||||
open: true,
|
open: true,
|
||||||
message: t('bible.copied')
|
message: t('copied')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleVerseChat = (verse: BibleVerse) => {
|
||||||
|
// If user is not authenticated, redirect to login
|
||||||
|
if (!user) {
|
||||||
|
router.push(`/${locale}/login?redirect=${encodeURIComponent(`/${locale}/bible?version=${selectedVersion}&book=${selectedBook}&chapter=${selectedChapter}&verse=${verse.verseNum}`)}`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const versionName = versions.find(v => v.id === selectedVersion)?.name || selectedVersion
|
||||||
|
const bookName = currentBook?.name || 'Unknown Book'
|
||||||
|
|
||||||
|
const initialMessage = `Explain in depth this verse "${verse.text}" from ${versionName}, ${bookName} ${selectedChapter}:${verse.verseNum} and its meaning`
|
||||||
|
|
||||||
|
// Dispatch event to open floating chat with the pre-filled message
|
||||||
|
window.dispatchEvent(new CustomEvent('floating-chat:open', {
|
||||||
|
detail: {
|
||||||
|
initialMessage: initialMessage,
|
||||||
|
fullscreen: false
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
const getThemeStyles = () => {
|
const getThemeStyles = () => {
|
||||||
switch (preferences.theme) {
|
switch (preferences.theme) {
|
||||||
case 'dark':
|
case 'dark':
|
||||||
@@ -765,6 +787,13 @@ export default function BibleReaderNew() {
|
|||||||
>
|
>
|
||||||
<ContentCopy fontSize="small" />
|
<ContentCopy fontSize="small" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
|
<IconButton
|
||||||
|
size="small"
|
||||||
|
onClick={() => handleVerseChat(verse)}
|
||||||
|
sx={{ color: 'action.active' }}
|
||||||
|
>
|
||||||
|
<Chat fontSize="small" />
|
||||||
|
</IconButton>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -1,18 +1,21 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { useEffect } from 'react'
|
import { useEffect, Suspense } from 'react'
|
||||||
import { useRouter } from 'next/navigation'
|
import { useRouter, useSearchParams } from 'next/navigation'
|
||||||
import { useLocale } from 'next-intl'
|
import { useLocale } from 'next-intl'
|
||||||
import { Box, CircularProgress, Typography } from '@mui/material'
|
import { Box, CircularProgress, Typography } from '@mui/material'
|
||||||
|
|
||||||
export default function LoginRedirectPage() {
|
function LoginRedirectContent() {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const locale = useLocale()
|
const locale = useLocale()
|
||||||
|
const searchParams = useSearchParams()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Redirect to the actual login page
|
// Preserve the redirect parameter when redirecting to the actual login page
|
||||||
router.replace(`/${locale}/auth/login`)
|
const redirect = searchParams.get('redirect')
|
||||||
}, [router, locale])
|
const redirectParam = redirect ? `?redirect=${encodeURIComponent(redirect)}` : ''
|
||||||
|
router.replace(`/${locale}/auth/login${redirectParam}`)
|
||||||
|
}, [router, locale, searchParams])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
@@ -29,4 +32,26 @@ export default function LoginRedirectPage() {
|
|||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function LoginRedirectPage() {
|
||||||
|
return (
|
||||||
|
<Suspense fallback={
|
||||||
|
<Box
|
||||||
|
display="flex"
|
||||||
|
flexDirection="column"
|
||||||
|
alignItems="center"
|
||||||
|
justifyContent="center"
|
||||||
|
minHeight="100vh"
|
||||||
|
gap={2}
|
||||||
|
>
|
||||||
|
<CircularProgress />
|
||||||
|
<Typography variant="body2" color="text.secondary">
|
||||||
|
Loading...
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
}>
|
||||||
|
<LoginRedirectContent />
|
||||||
|
</Suspense>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user