Add Bible picker flow and chapter navigation UI
This commit is contained in:
118
components/BibleEmbeddedView.js
Normal file
118
components/BibleEmbeddedView.js
Normal file
@@ -0,0 +1,118 @@
|
||||
import React, { useMemo, useState } from "react";
|
||||
import { StyleSheet, Text, View } from "react-native";
|
||||
import { ActivityIndicator, Chip } from "react-native-paper";
|
||||
import { useNavigation } from "@react-navigation/native";
|
||||
import { extractBibleReferences, fetchBiblePassage } from "../utils/bibleReferences.js";
|
||||
|
||||
const BibleEmbeddedView = ({ content = "", compact = false, openChapterOnPress = false }) => {
|
||||
const navigation = useNavigation();
|
||||
const references = useMemo(() => extractBibleReferences(content), [content]);
|
||||
const [selectedRef, setSelectedRef] = useState("");
|
||||
const [byReference, setByReference] = useState({});
|
||||
|
||||
if (!references.length) return null;
|
||||
|
||||
const handleSelectReference = async (reference) => {
|
||||
if (openChapterOnPress) {
|
||||
navigation.navigate("BibleChapter", { reference });
|
||||
return;
|
||||
}
|
||||
setSelectedRef(reference);
|
||||
const current = byReference[reference];
|
||||
if (current?.loading || current?.text || current?.error) return;
|
||||
|
||||
setByReference((prev) => ({ ...prev, [reference]: { loading: true } }));
|
||||
try {
|
||||
const data = await fetchBiblePassage(reference);
|
||||
setByReference((prev) => ({ ...prev, [reference]: { loading: false, ...data } }));
|
||||
} catch (_error) {
|
||||
setByReference((prev) => ({ ...prev, [reference]: { loading: false, error: true } }));
|
||||
}
|
||||
};
|
||||
|
||||
const selectedData = selectedRef ? byReference[selectedRef] : null;
|
||||
|
||||
return (
|
||||
<View style={[styles.container, compact ? styles.compactContainer : null]}>
|
||||
<Text style={styles.label}>Bible</Text>
|
||||
<View style={styles.chipsWrap}>
|
||||
{references.map((reference) => (
|
||||
<Chip
|
||||
key={reference}
|
||||
mode={selectedRef === reference ? "flat" : "outlined"}
|
||||
selected={selectedRef === reference}
|
||||
compact
|
||||
style={styles.chip}
|
||||
onPress={() => handleSelectReference(reference)}
|
||||
>
|
||||
{reference}
|
||||
</Chip>
|
||||
))}
|
||||
</View>
|
||||
{selectedData?.loading ? <ActivityIndicator size="small" style={styles.loader} /> : null}
|
||||
{selectedData?.text ? (
|
||||
<View style={styles.previewBox}>
|
||||
<Text style={styles.previewText}>{selectedData.text.slice(0, compact ? 160 : 280)}</Text>
|
||||
<Text style={styles.previewMeta}>
|
||||
{selectedData.reference} ({selectedData.translation})
|
||||
</Text>
|
||||
</View>
|
||||
) : null}
|
||||
{selectedData?.error ? <Text style={styles.errorText}>Unable to load this passage.</Text> : null}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default BibleEmbeddedView;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
paddingTop: 6,
|
||||
paddingHorizontal: 8,
|
||||
},
|
||||
compactContainer: {
|
||||
paddingTop: 4,
|
||||
paddingHorizontal: 0,
|
||||
},
|
||||
label: {
|
||||
fontSize: 12,
|
||||
color: "#4b5563",
|
||||
fontWeight: "700",
|
||||
marginBottom: 4,
|
||||
},
|
||||
chipsWrap: {
|
||||
flexDirection: "row",
|
||||
flexWrap: "wrap",
|
||||
},
|
||||
chip: {
|
||||
marginRight: 6,
|
||||
marginBottom: 6,
|
||||
backgroundColor: "#f8fafc",
|
||||
},
|
||||
loader: {
|
||||
alignSelf: "flex-start",
|
||||
},
|
||||
previewBox: {
|
||||
marginTop: 2,
|
||||
backgroundColor: "#f8fafc",
|
||||
borderWidth: 1,
|
||||
borderColor: "#e5e7eb",
|
||||
borderRadius: 10,
|
||||
padding: 8,
|
||||
},
|
||||
previewText: {
|
||||
fontSize: 13,
|
||||
color: "#111827",
|
||||
},
|
||||
previewMeta: {
|
||||
marginTop: 4,
|
||||
fontSize: 11,
|
||||
color: "#6b7280",
|
||||
fontWeight: "600",
|
||||
},
|
||||
errorText: {
|
||||
marginTop: 2,
|
||||
fontSize: 12,
|
||||
color: "#b91c1c",
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user