Fix 15 pre-existing ESLint issues in studyPages.tsx; v2.1.1
Publish Container / build (push) Successful in 16m13s
Publish Container / build (push) Successful in 16m13s
Fixes two real bugs: useEffect and usePageMeta were called after an early "study not found" return, violating React's rules of hooks (could corrupt hook state if a study slug resolves inconsistently across renders). Also removes dead code (unused NOTE_ANCHORS array, an unused state pair, a stale eslint-disable comment), fixes three return-in-finally statements that could mask exceptions, adds the missing study?.slug effect dependency, and cleans up an unnecessary regex escape and an empty catch block. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
841e754e16
commit
c92bb2189a
2 files changed
+37
-46
No files matched your search
+1
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "siteforge",
|
||||
"private": true,
|
||||
"version": "2.1.0",
|
||||
"version": "2.1.1",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
+36
-45
@@ -211,13 +211,7 @@ function getNoteId(studySlug: string, sectionId: string) {
|
||||
return `${studySlug}--${sectionId}`
|
||||
}
|
||||
|
||||
const NOTE_ANCHORS = [
|
||||
{ id: 'scripture', label: 'Scripture Text' },
|
||||
{ id: 'commentary', label: 'Commentary' },
|
||||
{ id: 'questions', label: 'Reflection Questions' },
|
||||
] as const
|
||||
|
||||
type NoteAnchorId = typeof NOTE_ANCHORS[number]['id'] | 'general'
|
||||
type NoteAnchorId = 'scripture' | 'commentary' | 'questions' | 'general'
|
||||
|
||||
const ANCHOR_LABELS: Record<string, string> = {
|
||||
scripture: 'Scripture Text',
|
||||
@@ -233,7 +227,9 @@ function parseNoteMap(raw: string): Record<string, string> {
|
||||
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
||||
return parsed as Record<string, string>
|
||||
}
|
||||
} catch {}
|
||||
} catch {
|
||||
// Not valid JSON — treat the raw string as legacy plain-text notes.
|
||||
}
|
||||
return { general: raw }
|
||||
}
|
||||
|
||||
@@ -671,8 +667,7 @@ export function StudyLandingPage({ content }: Props) {
|
||||
if (cancelled) return
|
||||
setError(err instanceof Error ? err.message : 'Unable to load your study dashboard.')
|
||||
} finally {
|
||||
if (cancelled) return
|
||||
setLoading(false)
|
||||
if (!cancelled) setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1045,7 +1040,7 @@ export function StudyLandingPage({ content }: Props) {
|
||||
)
|
||||
}
|
||||
|
||||
const EMAIL_REGEX = /^[a-zA-Z0-9._%+\-]{1,64}@[a-zA-Z0-9.\-]{1,253}\.[a-zA-Z]{2,}$/
|
||||
const EMAIL_REGEX = /^[a-zA-Z0-9._%+-]{1,64}@[a-zA-Z0-9.-]{1,253}\.[a-zA-Z]{2,}$/
|
||||
|
||||
export function StudySignupPage() {
|
||||
const navigate = useNavigate()
|
||||
@@ -1107,7 +1102,7 @@ export function StudySignupPage() {
|
||||
} finally {
|
||||
setBusy(false)
|
||||
}
|
||||
}, [mode, email, password, subscribeNewsletter])
|
||||
}, [mode, email, password, subscribeNewsletter, navigate])
|
||||
|
||||
const submitTotp = useCallback(async () => {
|
||||
if (!totpCode.trim()) { setMessage('Please enter your 6-digit code.'); return }
|
||||
@@ -1127,7 +1122,7 @@ export function StudySignupPage() {
|
||||
} finally {
|
||||
setBusy(false)
|
||||
}
|
||||
}, [totpPendingToken, totpCode, email])
|
||||
}, [totpPendingToken, totpCode, email, navigate])
|
||||
|
||||
const submitForgot = useCallback(async () => {
|
||||
if (!email.trim() || !EMAIL_REGEX.test(email.trim())) {
|
||||
@@ -1497,6 +1492,7 @@ export function ColossiansStudyIndexPage({ content }: Props) {
|
||||
const [enrollMessage, setEnrollMessage] = useState('')
|
||||
const [studyProgress, setStudyProgress] = useState<StudyProgress>({ completedSectionIds: [], bookmarkedSectionIds: [] })
|
||||
const [progressLoading, setProgressLoading] = useState(false)
|
||||
const enrolled = isEnrolledInStudy(auth, study?.slug)
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false
|
||||
@@ -1519,26 +1515,6 @@ export function ColossiansStudyIndexPage({ content }: Props) {
|
||||
}
|
||||
}, [])
|
||||
|
||||
if (!study) {
|
||||
return (
|
||||
<main className="thanks-page" aria-label="Study not found">
|
||||
<div className="thanks-card">
|
||||
<p className="eyebrow">Study Hub</p>
|
||||
<h1>Study not found</h1>
|
||||
<p>The study you requested is not available yet.</p>
|
||||
<Link to="/study" className="btn-primary">Back to Studies</Link>
|
||||
</div>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
|
||||
const sections = study.sections
|
||||
const chapterSummaries = getChapterSummaries(study)
|
||||
const releasedSections = sections.filter(isSectionReleased)
|
||||
const firstReleasedSection = releasedSections[0]
|
||||
const populatedChapterCount = chapterSummaries.filter(chapter => chapter.lessonCount > 0).length
|
||||
const enrolled = isEnrolledInStudy(auth, study.slug)
|
||||
|
||||
useEffect(() => {
|
||||
if (!auth.checked || !auth.authenticated || !study || !enrolled) return
|
||||
let cancelled = false
|
||||
@@ -1563,6 +1539,30 @@ export function ColossiansStudyIndexPage({ content }: Props) {
|
||||
}
|
||||
}, [auth.checked, auth.authenticated, study, enrolled])
|
||||
|
||||
usePageMeta(
|
||||
study ? `${study.title} | Bible Study` : 'Study Not Found',
|
||||
study?.description,
|
||||
)
|
||||
|
||||
if (!study) {
|
||||
return (
|
||||
<main className="thanks-page" aria-label="Study not found">
|
||||
<div className="thanks-card">
|
||||
<p className="eyebrow">Study Hub</p>
|
||||
<h1>Study not found</h1>
|
||||
<p>The study you requested is not available yet.</p>
|
||||
<Link to="/study" className="btn-primary">Back to Studies</Link>
|
||||
</div>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
|
||||
const sections = study.sections
|
||||
const chapterSummaries = getChapterSummaries(study)
|
||||
const releasedSections = sections.filter(isSectionReleased)
|
||||
const firstReleasedSection = releasedSections[0]
|
||||
const populatedChapterCount = chapterSummaries.filter(chapter => chapter.lessonCount > 0).length
|
||||
|
||||
async function enrollInStudy() {
|
||||
if (!study) return
|
||||
setEnrollMessage('')
|
||||
@@ -1583,11 +1583,6 @@ export function ColossiansStudyIndexPage({ content }: Props) {
|
||||
}
|
||||
}
|
||||
|
||||
usePageMeta(
|
||||
`${study.title} | Bible Study`,
|
||||
study.description,
|
||||
)
|
||||
|
||||
return (
|
||||
<main className="study-index-page" aria-label={`${study.title} study`}>
|
||||
<section className="section-study-course-hero">
|
||||
@@ -1748,7 +1743,6 @@ export function ColossiansStudySectionPage({ content }: Props) {
|
||||
const [showCelebration, setShowCelebration] = useState(false)
|
||||
const [bannerDismissed, setBannerDismissed] = useState(() => localStorage.getItem('study-banner-dismissed-v1') === '1')
|
||||
const [autoSaveMsg, setAutoSaveMsg] = useState('')
|
||||
const [_checkpointReflection, _setCheckpointReflection] = useState('')
|
||||
const [bsbPassage, setBsbPassage] = useState<string | null>(null)
|
||||
const [bsbPassageLoading, setBsbPassageLoading] = useState(false)
|
||||
|
||||
@@ -1927,7 +1921,6 @@ export function ColossiansStudySectionPage({ content }: Props) {
|
||||
}
|
||||
}, 2000)
|
||||
return () => clearTimeout(timer)
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [noteMap, noteId, auth.authenticated, noteLoading])
|
||||
|
||||
useEffect(() => {
|
||||
@@ -2596,8 +2589,7 @@ export function ColossiansStudyNotesPage({ content }: Props) {
|
||||
if (cancelled) return
|
||||
setMessage(err instanceof Error ? err.message : 'Unable to load notes.')
|
||||
} finally {
|
||||
if (cancelled) return
|
||||
setLoading(false)
|
||||
if (!cancelled) setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2605,7 +2597,7 @@ export function ColossiansStudyNotesPage({ content }: Props) {
|
||||
return () => {
|
||||
cancelled = true
|
||||
}
|
||||
}, [])
|
||||
}, [study?.slug])
|
||||
|
||||
if (!study) {
|
||||
return (
|
||||
@@ -3826,8 +3818,7 @@ export function StudyQuizPage({ content }: Props) {
|
||||
if (cancelled) return
|
||||
setError(err instanceof Error ? err.message : 'Unable to load answers.')
|
||||
} finally {
|
||||
if (cancelled) return
|
||||
setLoading(false)
|
||||
if (!cancelled) setLoading(false)
|
||||
}
|
||||
}
|
||||
void load()
|
||||
|
||||
Reference in new issue
Block a user