import { useState } from "react"; import { View, Text, TouchableOpacity, StyleSheet, Vibration, Pressable, Keyboard, } from "react-native"; import { Comment as CommentType, useCommentVotes, useCommentSection, useUser, getUserName, handleError, } from "@replyke/core"; import { useSocialStyleConfig } from "@replyke/comments-social-core"; import { UserAvatar, FromNow, parseContentWithMentions, getImageComponent, } from "@replyke/ui-core-react-native"; import { Replies } from "./Replies"; import HeartButton from "./HeartButton"; import useSheetManager from "../../../hooks/useSheetManager"; const Comment = ({ comment: commentFromSection, extraLeftPadding = 0, }: { comment: CommentType; extraLeftPadding?: number; }) => { // Dynamically get the correct Image component and whether it is expo-image. const { ImageComponent, isExpo } = getImageComponent(); const { user } = useUser(); const { handleShallowReply, handleDeepReply, callbacks, highlightedComment } = useCommentSection(); const { styleConfig } = useSocialStyleConfig(); const { openCommentOptionsSheet } = useSheetManager(); const { horizontalItemsGap, verticalItemsGap, authorAvatarSize, authorFontSize, authorFontWeight, authorFontColor, fromNowFontSize, fromNowFontColor, commentBodyFontSize, commentBodyFontColor, actionsItemGap, replyButtonFontSize, replyButtonFontWeight, replyButtonFontColor, heartIconSize, heartIconEmptyColor, heartIconFullColor, heartIconPaddingBottom, likesCountFontSize, likesCountFontWeight, likesCountFontColor, } = styleConfig!.commentProps; const [comment, setComment] = useState(commentFromSection); const { upvoteComment, removeCommentUpvote } = useCommentVotes({ comment, setComment, }); const handleUpvoteComment = () => { if (!user) { ( callbacks?.loginRequiredCallback || (() => alert("Please login first.")) )(); return; } if (!user.username && callbacks?.usernameRequiredCallback) { callbacks.usernameRequiredCallback(); return; } try { upvoteComment(); } catch (err) { handleError(err, "Failed to upvote comment"); } }; const handleRemoveCommentUpvote = () => { if (!user) { ( callbacks?.loginRequiredCallback || (() => alert("Please login first.")) )(); return; } try { removeCommentUpvote(); } catch (err) { handleError(err, "Failed to upvote comment"); } }; const userUpvotedComment = !!(user && comment.upvotes.includes(user.id)); const imageStyle = { width: (comment.gif?.aspectRatio || 1) < 1 ? 200 : 200 * (comment.gif?.aspectRatio || 1), height: (comment.gif?.aspectRatio || 1) > 1 ? 200 : 200 * (comment.gif?.aspectRatio || 1), borderRadius: 4, overflow: "hidden", }; const imageProps = isExpo ? { source: comment.gif?.gifUrl, // expo-image accepts a string contentFit: "cover", transition: 1000, placeholder: comment.gif?.gifPreviewUrl, } : { source: { uri: comment.gif?.gifUrl }, // React Native's Image requires { uri: ... } }; return ( { Vibration.vibrate(50); openCommentOptionsSheet!(comment); Keyboard.dismiss(); }} style={{ ...styles.container, paddingRight: 16, paddingLeft: 16 + extraLeftPadding, }} > { if (comment.user.id === user?.id) { callbacks?.currentUserClickCallback?.(); } else { callbacks?.otherUserClickCallback?.( comment.user.id, comment.user.foreignId ); } }} > { if (comment.user.id === user?.id) { callbacks?.currentUserClickCallback?.(); } else { callbacks?.otherUserClickCallback?.( comment.user.id, comment.user.foreignId ); } }} > {getUserName(comment.user, "username")} {comment.content && ( {parseContentWithMentions( comment.content, comment.mentions, user?.id, callbacks?.currentUserClickCallback, callbacks?.otherUserClickCallback )} )} {comment.gif?.gifUrl && ( )} comment.parentId ? handleShallowReply!(comment) : handleDeepReply!(comment) } style={styles.replyButton} > Reply {comment.upvotes.length} {!comment.parentId && } ); }; const styles = StyleSheet.create({ container: { width: "100%", }, commentHeader: { display: "flex", flexDirection: "row", alignItems: "flex-start", }, commentBody: { flex: 1, display: "flex", flexDirection: "column", }, commentMeta: { display: "flex", flexDirection: "row", alignItems: "baseline", gap: 4, marginTop: 2, }, authorText: { // Custom styles if needed }, commentText: { // Custom styles if needed }, actionsContainer: { display: "flex", flexDirection: "row", alignItems: "center", }, replyButton: { // Custom styles if needed }, menuButton: { display: "flex", alignItems: "center", justifyContent: "center", }, likesContainer: { display: "flex", flexDirection: "column", alignItems: "center", }, }); export default Comment;