- Verified all exports in highlight-manager.ts are correct - Installed @clerk/nextjs dependency for API routes - Fixed TypeScript errors in API routes (NextRequest type) - Fixed MUI Grid component usage in highlights-tab.tsx (replaced with Box flexbox) - Fixed HighlightColor type assertion in reading-view.tsx - Build completed successfully with no TypeScript errors 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
200 lines
5.6 KiB
TypeScript
200 lines
5.6 KiB
TypeScript
'use client'
|
|
import { useState, useEffect, CSSProperties } from 'react'
|
|
import { Box, Typography, IconButton, Paper, useMediaQuery, useTheme } from '@mui/material'
|
|
import { NavigateBefore, NavigateNext, Settings as SettingsIcon } from '@mui/icons-material'
|
|
import { BibleChapter, HighlightColor } from '@/types'
|
|
import { getCSSVariables, loadPreferences } from '@/lib/reading-preferences'
|
|
|
|
const COLOR_MAP: Record<HighlightColor, string> = {
|
|
yellow: 'rgba(255, 193, 7, 0.3)',
|
|
orange: 'rgba(255, 152, 0, 0.3)',
|
|
pink: 'rgba(233, 30, 99, 0.3)',
|
|
blue: 'rgba(33, 150, 243, 0.3)'
|
|
}
|
|
|
|
interface ReadingViewProps {
|
|
chapter: BibleChapter
|
|
loading: boolean
|
|
onPrevChapter: () => void
|
|
onNextChapter: () => void
|
|
onVerseClick: (verseId: string) => void
|
|
onSettingsOpen: () => void
|
|
hasPrevChapter: boolean
|
|
hasNextChapter: boolean
|
|
}
|
|
|
|
export function ReadingView({
|
|
chapter,
|
|
loading,
|
|
onPrevChapter,
|
|
onNextChapter,
|
|
onVerseClick,
|
|
onSettingsOpen,
|
|
hasPrevChapter,
|
|
hasNextChapter,
|
|
}: ReadingViewProps) {
|
|
const theme = useTheme()
|
|
const isMobile = useMediaQuery(theme.breakpoints.down('sm'))
|
|
const [preferences, setPreferences] = useState(loadPreferences())
|
|
const [showControls, setShowControls] = useState(!isMobile)
|
|
const [hoveredVerseNum, setHoveredVerseNum] = useState<number | null>(null)
|
|
|
|
useEffect(() => {
|
|
const handleStorageChange = () => {
|
|
setPreferences(loadPreferences())
|
|
}
|
|
|
|
setPreferences(loadPreferences())
|
|
window.addEventListener('storage', handleStorageChange)
|
|
|
|
return () => window.removeEventListener('storage', handleStorageChange)
|
|
}, [])
|
|
|
|
const cssVars = getCSSVariables(preferences)
|
|
|
|
if (loading) {
|
|
return (
|
|
<Box sx={{ display: 'flex', justifyContent: 'center', py: 8 }}>
|
|
<Typography>Loading chapter...</Typography>
|
|
</Box>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<Box
|
|
sx={{
|
|
...cssVars,
|
|
backgroundColor: 'var(--bg-color)',
|
|
color: 'var(--text-color)',
|
|
minHeight: '100vh',
|
|
transition: 'background-color 0.2s, color 0.2s',
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
position: 'relative'
|
|
} as CSSProperties}
|
|
onClick={(e) => {
|
|
if (isMobile) {
|
|
const rect = e.currentTarget.getBoundingClientRect()
|
|
const y = e.clientY - rect.top
|
|
if (y < rect.height * 0.3) {
|
|
setShowControls(true)
|
|
} else if (y > rect.height * 0.7) {
|
|
setShowControls(!showControls)
|
|
} else {
|
|
setShowControls(false)
|
|
}
|
|
}
|
|
}}
|
|
>
|
|
{/* Header */}
|
|
{(showControls || !isMobile) && (
|
|
<Paper
|
|
elevation={0}
|
|
sx={{
|
|
p: 2,
|
|
backgroundColor: 'inherit',
|
|
borderBottom: `1px solid var(--text-color)`,
|
|
opacity: 0.7
|
|
}}
|
|
>
|
|
<Typography variant="h5" fontWeight={600}>
|
|
{chapter.bookName} {chapter.chapter}
|
|
</Typography>
|
|
</Paper>
|
|
)}
|
|
|
|
{/* Main Text Area */}
|
|
<Box
|
|
sx={{
|
|
flex: 1,
|
|
py: 3,
|
|
maxWidth: 700,
|
|
mx: 'auto',
|
|
width: '100%',
|
|
px: 'var(--margin-width)',
|
|
lineHeight: 'var(--line-height)',
|
|
fontSize: 'var(--font-size)',
|
|
fontFamily: 'var(--font-family)',
|
|
textAlign: 'var(--text-align)' as any,
|
|
} as CSSProperties}
|
|
>
|
|
{chapter.verses.map((verse) => (
|
|
<span
|
|
key={verse.id}
|
|
role="button"
|
|
tabIndex={0}
|
|
aria-label={`Verse ${verse.verseNum}: ${verse.text}`}
|
|
onClick={(e) => {
|
|
e.stopPropagation()
|
|
onVerseClick(verse.id)
|
|
}}
|
|
onKeyDown={(e) => {
|
|
if (e.key === 'Enter' || e.key === ' ') {
|
|
e.preventDefault()
|
|
onVerseClick(verse.id)
|
|
}
|
|
}}
|
|
onMouseEnter={() => setHoveredVerseNum(verse.verseNum)}
|
|
onMouseLeave={() => setHoveredVerseNum(null)}
|
|
style={{
|
|
backgroundColor: (verse as any).highlight ? COLOR_MAP[(verse as any).highlight.color as HighlightColor] : 'transparent',
|
|
padding: '0.25rem 0.5rem',
|
|
borderRadius: '4px',
|
|
cursor: 'pointer',
|
|
transition: 'all 0.2s ease'
|
|
}}
|
|
>
|
|
<sup style={{ fontSize: '0.8em', marginRight: '0.25em', fontWeight: 600, opacity: 0.6 }}>
|
|
{verse.verseNum}
|
|
</sup>
|
|
{verse.text}{' '}
|
|
</span>
|
|
))}
|
|
</Box>
|
|
|
|
{/* Navigation Footer */}
|
|
{(showControls || !isMobile) && (
|
|
<Paper
|
|
elevation={0}
|
|
sx={{
|
|
p: 2,
|
|
backgroundColor: 'inherit',
|
|
borderTop: `1px solid var(--text-color)`,
|
|
opacity: 0.7,
|
|
display: 'flex',
|
|
justifyContent: 'space-between',
|
|
alignItems: 'center',
|
|
}}
|
|
>
|
|
<IconButton
|
|
onClick={onPrevChapter}
|
|
disabled={!hasPrevChapter}
|
|
size={isMobile ? 'small' : 'medium'}
|
|
>
|
|
<NavigateBefore />
|
|
</IconButton>
|
|
|
|
<Typography variant="body2">
|
|
Chapter {chapter.chapter}
|
|
</Typography>
|
|
|
|
<IconButton
|
|
onClick={onSettingsOpen}
|
|
size={isMobile ? 'small' : 'medium'}
|
|
>
|
|
<SettingsIcon />
|
|
</IconButton>
|
|
|
|
<IconButton
|
|
onClick={onNextChapter}
|
|
disabled={!hasNextChapter}
|
|
size={isMobile ? 'small' : 'medium'}
|
|
>
|
|
<NavigateNext />
|
|
</IconButton>
|
|
</Paper>
|
|
)}
|
|
</Box>
|
|
)
|
|
}
|