Enhance FeedItemCard with inline comment box and list
cgen-2e44bf49a27e4e9882d5a5e69d803c6e
This commit is contained in:
parent
c37b8f48ad
commit
add0e3aee0
1 changed files with 92 additions and 2 deletions
|
|
@ -1,4 +1,4 @@
|
|||
import { useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
|
|
@ -10,7 +10,11 @@ import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
|||
import { Button } from "@/components/ui/button";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { useAuth } from "@/contexts/AuthContext";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
import { communityService } from "@/lib/supabase-service";
|
||||
import { Heart, MessageCircle, Share2, Volume2, VolumeX } from "lucide-react";
|
||||
import type { FeedItem } from "@/pages/Feed";
|
||||
|
||||
|
|
@ -32,8 +36,54 @@ export function FeedItemCard({
|
|||
onComment,
|
||||
}: FeedItemCardProps) {
|
||||
const [muted, setMuted] = useState(true);
|
||||
const [showComments, setShowComments] = useState(false);
|
||||
const [comments, setComments] = useState<any[]>([]);
|
||||
const [loadingComments, setLoadingComments] = useState(false);
|
||||
const [commentText, setCommentText] = useState("");
|
||||
const [submittingComment, setSubmittingComment] = useState(false);
|
||||
const { user } = useAuth();
|
||||
const { toast } = useToast();
|
||||
const hasMedia = item.mediaType !== "none" && Boolean(item.mediaUrl);
|
||||
|
||||
useEffect(() => {
|
||||
if (!showComments) return;
|
||||
let cancelled = false;
|
||||
(async () => {
|
||||
setLoadingComments(true);
|
||||
try {
|
||||
const data = await communityService.listComments(item.id);
|
||||
if (!cancelled) setComments(Array.isArray(data) ? data : []);
|
||||
} finally {
|
||||
if (!cancelled) setLoadingComments(false);
|
||||
}
|
||||
})();
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [showComments, item.id]);
|
||||
|
||||
const submitComment = async () => {
|
||||
if (!user?.id) {
|
||||
toast({ description: "Please sign in to comment." });
|
||||
return;
|
||||
}
|
||||
const content = commentText.trim();
|
||||
if (!content) return;
|
||||
setSubmittingComment(true);
|
||||
try {
|
||||
const created = await communityService.addComment(item.id, user.id, content);
|
||||
if (created) {
|
||||
setComments((prev) => [...prev, created]);
|
||||
setCommentText("");
|
||||
onComment?.(item.id);
|
||||
}
|
||||
} catch (e) {
|
||||
toast({ variant: "destructive", description: "Failed to add comment" });
|
||||
} finally {
|
||||
setSubmittingComment(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Card className="overflow-hidden border-border/40 bg-background/70 shadow-2xl backdrop-blur-lg">
|
||||
<CardHeader className="pb-0">
|
||||
|
|
@ -131,7 +181,7 @@ export function FeedItemCard({
|
|||
variant="ghost"
|
||||
size="sm"
|
||||
className="gap-2 pl-2 pr-3"
|
||||
onClick={() => onComment(item.id)}
|
||||
onClick={() => setShowComments((s) => !s)}
|
||||
>
|
||||
<MessageCircle className="h-4 w-4 text-aethex-400" />
|
||||
<span className="font-medium text-foreground">
|
||||
|
|
@ -164,6 +214,46 @@ export function FeedItemCard({
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{showComments && (
|
||||
<div className="rounded-2xl border border-border/40 bg-background/80 p-4 space-y-3">
|
||||
<div className="space-y-2 max-h-60 overflow-auto pr-1">
|
||||
{loadingComments ? (
|
||||
<p className="text-sm text-muted-foreground">Loading comments…</p>
|
||||
) : comments.length === 0 ? (
|
||||
<p className="text-sm text-muted-foreground">Be the first to comment.</p>
|
||||
) : (
|
||||
comments.map((c) => (
|
||||
<div key={c.id} className="flex items-start gap-3">
|
||||
<Avatar className="h-8 w-8">
|
||||
<AvatarImage src={c.user_profiles?.avatar_url || undefined} />
|
||||
<AvatarFallback>
|
||||
{(c.user_profiles?.full_name || c.user_profiles?.username || "U")[0]?.toUpperCase() || "U"}
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
<div>
|
||||
<div className="text-sm font-medium">
|
||||
{c.user_profiles?.full_name || c.user_profiles?.username || "Member"}
|
||||
</div>
|
||||
<div className="text-sm text-foreground/90 whitespace-pre-wrap">{c.content}</div>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-start gap-3">
|
||||
<Textarea
|
||||
placeholder="Write a comment…"
|
||||
value={commentText}
|
||||
onChange={(e) => setCommentText(e.target.value)}
|
||||
className="min-h-[44px]"
|
||||
/>
|
||||
<Button onClick={submitComment} disabled={submittingComment || !commentText.trim()}>
|
||||
{submittingComment ? "Posting…" : "Post"}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
|
|
|
|||
Loading…
Reference in a new issue