Improve comment composer and add Bible reference pills

This commit is contained in:
Adolfo Reyna
2026-02-24 16:26:58 -05:00
parent a2846423b9
commit 53df0699a7
2 changed files with 138 additions and 42 deletions

View File

@@ -4,6 +4,7 @@ import { FAB, Button, Card, Title, IconButton } from 'react-native-paper';
import API from './../API.js';
import UserName from './UserName.js';
import Media from './Media.js';
import BibleEmbeddedView from './BibleEmbeddedView.js';
import { useSnapshot } from 'valtio';
import GlobalState from '../contexts/GlobalState.js';
import Moment from 'moment';
@@ -16,7 +17,7 @@ let Comment = ({ comment, postid }) => {
const gState = useSnapshot(GlobalState);
const viewer = gState.me;
let [likes, changeLikes] = useState(Object.keys(comment.reactions).length);
let cleanContent = comment.content.replace(/@[A-z]+:.+\w/g, '');
let cleanContent = String(comment.content || '');
const newCommentReaction = () => {
if (!comment.reactions[viewer._id]) {
comment.reactions[viewer._id] = { type: "like" };
@@ -54,6 +55,7 @@ let Comment = ({ comment, postid }) => {
</View>
</View>
<Text style={{ fontSize: 14 }}>{cleanContent}</Text>
<BibleEmbeddedView content={comment.content} compact openChapterOnPress />
<Media content={comment.content} postId={postid} skiptVideo={true} />
</Card.Content>
</Card>

View File

@@ -1,49 +1,108 @@
import React, { useState } from 'react';
import { View, StyleSheet, Icon } from 'react-native';
import { TextInput, Button } from 'react-native-paper';
import React, { useEffect, useState } from 'react';
import { View, StyleSheet, Pressable } from 'react-native';
import { TextInput, Chip } from 'react-native-paper';
import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
import API from './../API.js';
import { useNavigation } from '@react-navigation/native';
import AwesomeIcon from 'react-native-vector-icons/FontAwesome';
import i18n from "../i18nMessages.js";
import { useNavigation } from '@react-navigation/native';
import { useSnapshot } from 'valtio';
import GlobalState from '../contexts/GlobalState.js';
import { createBibleToken } from '../utils/bibleReferences.js';
const CircleIconAction = ({ icon, onPress, color = "#6b7280", disabled = false }) => (
<Pressable
onPress={onPress}
disabled={disabled}
style={({ pressed }) => ({
width: 36,
height: 36,
borderRadius: 18,
alignItems: "center",
justifyContent: "center",
backgroundColor: disabled ? "#f3f4f6" : (pressed ? "#e5e7eb" : "#f9fafb"),
marginHorizontal: 2,
transform: [{ scale: pressed ? 0.96 : 1 }],
})}
>
<MaterialIcons name={icon} size={20} color={disabled ? "#9ca3af" : color} />
</Pressable>
);
let NewComment = ({ postid, newComentAdded }) => {
let [commentContent, setCommentContent] = useState('');
const gState = useSnapshot(GlobalState);
const [commentContent, setCommentContent] = useState('');
const [bibleReferences, setBibleReferences] = useState([]);
const navigation = useNavigation();
const biblePickerSelectionTs = gState?.biblePickerSelection?.ts;
const hasContent = commentContent.trim().length > 0;
useEffect(() => {
const selection = gState?.biblePickerSelection;
if (!selection || selection.target !== "comment" || !selection.reference) return;
setBibleReferences((prev) => {
if (prev.includes(selection.reference)) return prev;
return prev.concat(selection.reference);
});
GlobalState.biblePickerSelection = null;
}, [biblePickerSelectionTs, gState?.biblePickerSelection]);
const handleSubmit = () => {
if (!hasContent && bibleReferences.length === 0) return;
const trimmedComment = commentContent.trim();
const bibleTokens = bibleReferences.map((reference) => createBibleToken(reference)).filter(Boolean);
setCommentContent('');
setBibleReferences([]);
API.newPostComment(postid, [trimmedComment, bibleTokens.join(" ")].join(" ").trim()).then((newPost) => {
if (newComentAdded) newComentAdded(newPost);
});
};
return (
<View style={styles.NewComment}>
<TextInput
label={i18n.t("message.newComment")}
value={commentContent}
onChangeText={setCommentContent}
mode="outlined"
multiline={true}
dense={true}
style={{
flex: 8,
fontSize: 12,
backgroundColor: "white",
}}
/>
<View style={{
flex: 2,
fontSize: 12,
flexDirection: "column",
alignItems: "center", // ignore this - we'll come back to it
justifyContent: "center",
}}>
<Button mode="outlined" onPress={() => {
if (commentContent.trim() === "") return 0;
setCommentContent('');
API.newPostComment(postid, commentContent).then((newPost) => {
setCommentContent('');
if(newComentAdded) newComentAdded(newPost);
});
}}>
<AwesomeIcon name="send" />
</Button>
<View style={styles.newComment}>
<View style={styles.composerShell}>
<CircleIconAction
icon="menu-book"
onPress={() => navigation.navigate("BiblePicker", { target: "comment" })}
color="#6b7280"
/>
<View style={styles.inputWrap}>
<TextInput
mode="flat"
placeholder={i18n.t("message.newComment")}
value={commentContent}
onChangeText={setCommentContent}
multiline
maxLength={500}
dense
underlineColor="transparent"
activeUnderlineColor="transparent"
style={styles.input}
contentStyle={styles.inputContent}
/>
</View>
<CircleIconAction
icon="send"
onPress={handleSubmit}
disabled={!hasContent && bibleReferences.length === 0}
color="#0d6efd"
/>
</View>
{bibleReferences.length ? (
<View style={styles.referencesWrap}>
{bibleReferences.map((reference) => (
<Chip
key={reference}
style={styles.referenceChip}
onPress={() => navigation.navigate("BibleChapter", { reference })}
onClose={() => {
setBibleReferences((prev) => prev.filter((item) => item !== reference));
}}
>
{reference}
</Chip>
))}
</View>
) : null}
</View>
);
}
@@ -51,9 +110,44 @@ let NewComment = ({ postid, newComentAdded }) => {
export default NewComment;
const styles = StyleSheet.create({
NewComment: {
margin: 10,
newComment: {
marginHorizontal: 10,
marginTop: 10,
marginBottom: 6,
},
composerShell: {
borderWidth: 1,
borderColor: "#e5e7eb",
borderRadius: 28,
backgroundColor: "#ffffff",
paddingHorizontal: 4,
paddingVertical: 2,
flexDirection: "row",
flex: 6
}
alignItems: "flex-end",
shadowColor: "#111827",
shadowOpacity: 0.08,
shadowRadius: 10,
shadowOffset: { width: 0, height: 3 },
elevation: 2,
},
inputWrap: {
flex: 1,
paddingRight: 2,
},
input: {
backgroundColor: "transparent",
maxHeight: 120,
},
inputContent: {
paddingVertical: 10,
},
referencesWrap: {
flexDirection: "row",
flexWrap: "wrap",
marginTop: 8,
},
referenceChip: {
marginRight: 6,
marginBottom: 6,
},
});