155 lines
6.1 KiB
JavaScript
155 lines
6.1 KiB
JavaScript
import React from "react";
|
|
import { FlatList, Pressable, View } from "react-native";
|
|
import { ActivityIndicator, Text } from "react-native-paper";
|
|
import { SafeAreaView } from "react-native-safe-area-context";
|
|
import { useNavigation } from "@react-navigation/native";
|
|
import { fetchBibleChapter, parseBibleReference } from "../utils/bibleReferences.js";
|
|
import GlobalState from "../contexts/GlobalState.js";
|
|
import i18n from "../i18nMessages.js";
|
|
|
|
const BibleChapterView = ({ route }) => {
|
|
const navigation = useNavigation();
|
|
const reference = route?.params?.reference || "";
|
|
const selectable = route?.params?.selectable === true;
|
|
const { chapterReference, verse: selectedVerse } = parseBibleReference(reference);
|
|
const [loading, setLoading] = React.useState(true);
|
|
const [error, setError] = React.useState("");
|
|
const [chapterData, setChapterData] = React.useState(null);
|
|
const listRef = React.useRef(null);
|
|
const autoScrolledRef = React.useRef(false);
|
|
|
|
React.useEffect(() => {
|
|
let mounted = true;
|
|
const loadChapter = async () => {
|
|
setLoading(true);
|
|
setError("");
|
|
try {
|
|
const payload = await fetchBibleChapter(chapterReference, i18n.locale);
|
|
if (!mounted) return;
|
|
setChapterData(payload);
|
|
} catch (_error) {
|
|
if (!mounted) return;
|
|
setError(i18n.t("message.unableLoadChapter"));
|
|
setChapterData(null);
|
|
} finally {
|
|
if (mounted) setLoading(false);
|
|
}
|
|
};
|
|
loadChapter();
|
|
return () => {
|
|
mounted = false;
|
|
};
|
|
}, [chapterReference]);
|
|
|
|
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, 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 (
|
|
<SafeAreaView style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
|
|
<ActivityIndicator />
|
|
</SafeAreaView>
|
|
);
|
|
}
|
|
|
|
if (error) {
|
|
return (
|
|
<SafeAreaView style={{ flex: 1, justifyContent: "center", alignItems: "center", padding: 16 }}>
|
|
<Text style={{ color: "#b91c1c" }}>{error}</Text>
|
|
</SafeAreaView>
|
|
);
|
|
}
|
|
|
|
const handleVersePress = (verseNumber) => {
|
|
if (!selectable) return;
|
|
GlobalState.bibleChapterSelection = {
|
|
...parseBibleReference(`${chapterReference}:${verseNumber}`),
|
|
ts: Date.now(),
|
|
};
|
|
navigation.goBack();
|
|
};
|
|
|
|
return (
|
|
<SafeAreaView style={{ flex: 1 }}>
|
|
<View style={{ flex: 1, padding: 12 }}>
|
|
<Text style={{ fontSize: 22, fontWeight: "700", marginBottom: 4 }}>
|
|
{chapterData?.reference || chapterReference}
|
|
</Text>
|
|
<Text style={{ color: "#6b7280", marginBottom: 10 }}>
|
|
{chapterData?.translation_name || chapterData?.translation_id || "KJV"}
|
|
</Text>
|
|
{selectable ? (
|
|
<Text style={{ color: "#6b7280", marginBottom: 8 }}>{i18n.t("message.tapVerseToSelect")}</Text>
|
|
) : null}
|
|
<FlatList
|
|
ref={listRef}
|
|
data={verses}
|
|
keyExtractor={(item, idx) => `${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);
|
|
}}
|
|
renderItem={({ item }) => {
|
|
const verseNumber = Number(item?.verse || 0);
|
|
const isSelected = verseNumber === selectedVerseNumber;
|
|
return (
|
|
<Pressable
|
|
onPress={() => handleVersePress(verseNumber)}
|
|
style={{
|
|
paddingVertical: 8,
|
|
paddingHorizontal: 10,
|
|
borderRadius: 8,
|
|
marginBottom: 6,
|
|
backgroundColor: isSelected ? "#fff3cd" : "transparent",
|
|
borderWidth: isSelected ? 1 : 0,
|
|
borderColor: isSelected ? "#f59e0b" : "transparent",
|
|
}}
|
|
>
|
|
<Text style={{ fontWeight: "700", color: isSelected ? "#92400e" : "#374151" }}>
|
|
{verseNumber}
|
|
</Text>
|
|
<Text style={{ color: "#111827", lineHeight: 21 }}>{item?.text || ""}</Text>
|
|
</Pressable>
|
|
);
|
|
}}
|
|
/>
|
|
</View>
|
|
</SafeAreaView>
|
|
);
|
|
};
|
|
|
|
export default BibleChapterView;
|