import React from "react"; import { FlatList, Pressable, View } from "react-native"; import { ActivityIndicator, IconButton, Menu, Text, Button } from "react-native-paper"; import { SafeAreaView } from "react-native-safe-area-context"; import { useNavigation } from "@react-navigation/native"; import { fetchBibleChapter, parseBibleReference, AVAILABLE_TRANSLATIONS, BIBLE_BOOKS, getBookChapterCount } from "../utils/bibleReferences.js"; import GlobalState from "../contexts/GlobalState.js"; import { useSnapshot } from "valtio"; import API from "../API.js"; import i18n from "../i18nMessages.js"; const BibleChapterView = ({ route }) => { const navigation = useNavigation(); const gState = useSnapshot(GlobalState); const reference = route?.params?.reference || ""; const selectable = route?.params?.selectable === true; const { chapterReference, verse: selectedVerse, book, chapter } = parseBibleReference(reference); const [loading, setLoading] = React.useState(true); const [error, setError] = React.useState(""); const [chapterData, setChapterData] = React.useState(null); const initialTranslation = gState.me?.data?.bibleTranslation || ""; const [selectedTranslation, setSelectedTranslation] = React.useState(initialTranslation); const [translationMenuVisible, setTranslationMenuVisible] = React.useState(false); const listRef = React.useRef(null); const autoScrolledRef = React.useRef(false); const bookIndex = BIBLE_BOOKS.indexOf(book); const chapterCount = getBookChapterCount(book); let prevReference = null; if (chapter > 1) { prevReference = `${book} ${chapter - 1}`; } else if (bookIndex > 0) { const prevBook = BIBLE_BOOKS[bookIndex - 1]; const prevBookChapters = getBookChapterCount(prevBook); prevReference = `${prevBook} ${prevBookChapters}`; } let nextReference = null; if (chapter < chapterCount) { nextReference = `${book} ${chapter + 1}`; } else if (bookIndex >= 0 && bookIndex < BIBLE_BOOKS.length - 1) { const nextBook = BIBLE_BOOKS[bookIndex + 1]; nextReference = `${nextBook} 1`; } React.useEffect(() => { let mounted = true; const loadChapter = async () => { setLoading(true); setError(""); try { const payload = await fetchBibleChapter(chapterReference, i18n.locale, selectedTranslation); if (!mounted) return; setChapterData(payload); } catch (_error) { if (!mounted) return; setError(i18n.t("message.unableLoadChapter") || "Unable to load chapter"); setChapterData(null); } finally { if (mounted) setLoading(false); } }; loadChapter(); return () => { mounted = false; }; }, [chapterReference, selectedTranslation]); const verses = Array.isArray(chapterData?.verses) ? chapterData.verses : []; const selectedVerseNumber = Number(selectedVerse || 1); const selectedIndex = verses.findIndex((item) => Number(item?.verse || 0) === selectedVerseNumber); React.useEffect(() => { autoScrolledRef.current = false; }, [chapterReference, selectedTranslation, selectedVerseNumber]); const scrollToSelectedVerse = React.useCallback((animated = false) => { if (autoScrolledRef.current) return; if (!listRef.current || selectedIndex < 0 || !verses.length) return; try { listRef.current.scrollToIndex({ index: selectedIndex, animated, viewPosition: 0.35, }); autoScrolledRef.current = true; } catch (_error) { // FlatList can throw before enough measurements are available. } }, [selectedIndex, verses.length]); if (loading) { return ( ); } if (error) { return ( {error} ); } const handleVersePress = (verseNumber) => { if (!selectable) return; GlobalState.bibleChapterSelection = { ...parseBibleReference(`${chapterReference}:${verseNumber}`), ts: Date.now(), }; navigation.goBack(); }; return ( {chapterData?.reference || chapterReference} setTranslationMenuVisible(false)} anchor={ setTranslationMenuVisible(true)}> {chapterData?.translation_name || chapterData?.translation_id || "KJV"} } > {AVAILABLE_TRANSLATIONS.map((trans) => ( { setSelectedTranslation(trans.id); setTranslationMenuVisible(false); if (GlobalState.me) { if (!GlobalState.me.data) GlobalState.me.data = {}; GlobalState.me.data.bibleTranslation = trans.id; } API.setDataValue("bibleTranslation", trans.id); }} title={trans.name} /> ))} {!selectable ? ( navigation.navigate("Bible")} style={{ margin: 0, marginTop: -4 }} /> ) : null} {selectable ? ( {i18n.t("message.tapVerseToSelect")} ) : null} `${item?.verse || idx}`} initialNumToRender={24} onLayout={() => { setTimeout(() => scrollToSelectedVerse(false), 40); }} onContentSizeChange={() => { setTimeout(() => scrollToSelectedVerse(false), 40); }} onScrollToIndexFailed={({ index, averageItemLength }) => { if (!listRef.current) return; listRef.current.scrollToOffset({ offset: Math.max(0, (averageItemLength || 36) * index), animated: false, }); setTimeout(() => { scrollToSelectedVerse(false); }, 120); }} ListFooterComponent={() => ( )} renderItem={({ item }) => { const verseNumber = Number(item?.verse || 0); const isSelected = verseNumber === selectedVerseNumber; return ( handleVersePress(verseNumber)} style={{ paddingVertical: 4, paddingHorizontal: 8, borderRadius: 8, marginBottom: 2, backgroundColor: isSelected ? "#fff3cd" : "transparent", borderWidth: isSelected ? 1 : 0, borderColor: isSelected ? "#f59e0b" : "transparent", flexDirection: "row", }} > {verseNumber} {(item?.text || "").replace(/\n/g, " ").trim()} ); }} /> ); }; export default BibleChapterView;