import { createSignal, createEffect, For, Show, onMount, onCleanup } from "solid-js"; import { useLocation } from "@solidjs/router"; import type { CommentBlockProps, CommentReaction, UserPublicData } from "~/types/comment"; import { createWindowWidth } from "~/lib/resize-utils"; import UserDefaultImage from "~/components/icons/UserDefaultImage"; import ReplyIcon from "~/components/icons/ReplyIcon"; import TrashIcon from "~/components/icons/TrashIcon"; import EditIcon from "~/components/icons/EditIcon"; import ThumbsUpEmoji from "~/components/icons/emojis/ThumbsUp"; import LoadingSpinner from "~/components/LoadingSpinner"; import CommentInputBlock from "./CommentInputBlock"; import ReactionBar from "./ReactionBar"; export default function CommentBlock(props: CommentBlockProps) { const location = useLocation(); // State signals const [commentCollapsed, setCommentCollapsed] = createSignal(false); const [showingReactionOptions, setShowingReactionOptions] = createSignal(false); const [replyBoxShowing, setReplyBoxShowing] = createSignal(false); const [toggleHeight, setToggleHeight] = createSignal(0); const [reactions, setReactions] = createSignal([]); const windowWidth = createWindowWidth(200); const [deletionLoading, setDeletionLoading] = createSignal(false); const [userData, setUserData] = createSignal(null); // Refs let containerRef: HTMLDivElement | undefined; let commentInputRef: HTMLDivElement | undefined; // Auto-collapse at level 4+ createEffect(() => { setCommentCollapsed(props.level >= 4); }); // Find user data from comment map createEffect(() => { if (props.userCommentMap) { props.userCommentMap.forEach((commentIds, user) => { if (commentIds.includes(props.comment.id)) { setUserData(user); } }); } }); // Update toggle height based on container size createEffect(() => { if (containerRef) { const correction = showingReactionOptions() ? 80 : 48; setToggleHeight(containerRef.clientHeight + correction); } // Trigger on these dependencies windowWidth(); showingReactionOptions(); }); // Update reactions from map createEffect(() => { setReactions(props.reactionMap.get(props.comment.id) || []); }); // Event handlers const collapseCommentToggle = (e: MouseEvent) => { e.stopPropagation(); e.preventDefault(); setCommentCollapsed(!commentCollapsed()); }; const showingReactionOptionsToggle = (e: MouseEvent) => { e.stopPropagation(); e.preventDefault(); setShowingReactionOptions(!showingReactionOptions()); }; const toggleCommentReplyBox = (e: MouseEvent) => { e.stopPropagation(); e.preventDefault(); setReplyBoxShowing(!replyBoxShowing()); }; const deleteCommentTrigger = async (e: MouseEvent) => { e.stopPropagation(); setDeletionLoading(true); const user = userData(); props.toggleModification( props.comment.id, props.comment.commenter_id, props.comment.body, "delete", user?.image, user?.email, user?.display_name ); setDeletionLoading(false); }; const editCommentTrigger = (e: MouseEvent) => { e.stopPropagation(); const user = userData(); props.toggleModification( props.comment.id, props.comment.commenter_id, props.comment.body, "edit", user?.image, user?.email, user?.display_name ); }; // Computed values const upvoteCount = () => reactions().filter((r) => r.type === "upVote").length; const downvoteCount = () => reactions().filter((r) => r.type === "downVote").length; const hasUpvoted = () => reactions().some( (r) => r.type === "upVote" && r.user_id === props.currentUserID ); const hasDownvoted = () => reactions().some( (r) => r.type === "downVote" && r.user_id === props.currentUserID ); const canDelete = () => props.currentUserID === props.comment.commenter_id || props.privilegeLevel === "admin"; const canEdit = () => props.currentUserID === props.comment.commenter_id; const isAnonymous = () => props.privilegeLevel === "anonymous"; const replyIconColor = () => "var(--color-peach)"; return ( <> {/* Collapsed state */} {/* Expanded state */}
{/* Vote buttons column */}
{/* Upvote */} {/* Vote count */}
{upvoteCount() - downvoteCount()}
{/* Downvote */}
{/* Collapse toggle line */} {/* Comment content */}
{props.comment.body}
Edited
{/* User info */}
} > user-image
{userData()?.display_name || userData()?.email || "[removed]"}
{/* Delete button */}
{/* Edit and Reply buttons */}
{/* Reaction bar */}
0 ? "" : "opacity-0" } ml-16`} >
{/* Reply box */}
{/* Recursive child comments */}
{(childComment) => ( comment.parent_comment_id === childComment.id )} privilegeLevel={props.privilegeLevel} currentUserID={props.currentUserID} reactionMap={props.reactionMap} level={props.level + 1} socket={props.socket} userCommentMap={props.userCommentMap} toggleModification={props.toggleModification} newComment={props.newComment} commentSubmitLoading={props.commentSubmitLoading} commentReaction={props.commentReaction} /> )}
); }