import { useState, useEffect, useMemo } from "react"; import { useSearchParams } from "react-router-dom"; import Layout from "@/components/Layout"; import SEO from "@/components/SEO"; import ArmPostCard, { ArmType } from "@/components/feed/ArmPostCard"; import CommentsModal from "@/components/feed/CommentsModal"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Checkbox } from "@/components/ui/checkbox"; import { Label } from "@/components/ui/label"; import { Badge } from "@/components/ui/badge"; import { useAuth } from "@/contexts/AuthContext"; import { aethexToast } from "@/lib/aethex-toast"; import LoadingScreen from "@/components/LoadingScreen"; import { Zap, Gamepad2, Briefcase, BookOpen, Network, Sparkles, Shield, Plus, Filter, X, } from "lucide-react"; const API_BASE = import.meta.env.VITE_API_BASE || ""; const ARMS: { id: ArmType; label: string; icon: any; color: string }[] = [ { id: "labs", label: "Labs", icon: Zap, color: "text-yellow-400" }, { id: "gameforge", label: "GameForge", icon: Gamepad2, color: "text-green-400" }, { id: "corp", label: "Corp", icon: Briefcase, color: "text-blue-400" }, { id: "foundation", label: "Foundation", icon: BookOpen, color: "text-red-400" }, { id: "devlink", label: "Dev-Link", icon: Network, color: "text-cyan-400" }, { id: "nexus", label: "Nexus", icon: Sparkles, color: "text-purple-400" }, { id: "staff", label: "Staff", icon: Shield, color: "text-purple-400" }, ]; interface Post { id: string; title: string; content: string; arm_affiliation: ArmType; author_id: string; created_at: string; likes_count?: number; comments_count?: number; tags?: string[]; category?: string; user_profiles?: { id: string; username?: string; full_name?: string; avatar_url?: string; }; } export default function Feed() { const { user } = useAuth(); const [searchParams, setSearchParams] = useSearchParams(); const [posts, setPosts] = useState([]); const [isLoading, setIsLoading] = useState(true); const [selectedArms, setSelectedArms] = useState([]); const [userFollowedArms, setUserFollowedArms] = useState([]); const [showFilters, setShowFilters] = useState(false); const [activeTab, setActiveTab] = useState<"all" | ArmType>("all"); const [selectedPostForComments, setSelectedPostForComments] = useState(null); const [userLikedPosts, setUserLikedPosts] = useState>(new Set()); // Load user's followed arms useEffect(() => { const loadFollowedArms = async () => { if (!user?.id) return; try { const response = await fetch( `${API_BASE}/api/user/followed-arms?user_id=${user.id}` ); if (response.ok) { const data = await response.json(); setUserFollowedArms(data.followedArms || []); // Initialize selected arms with followed arms if (data.followedArms.length > 0) { setSelectedArms(data.followedArms); } else { // If no followed arms, default to all setSelectedArms(ARMS.map((a) => a.id)); } } } catch (error) { console.error("Failed to load followed arms:", error); } }; loadFollowedArms(); }, [user?.id]); // Load user's liked posts const loadUserLikes = async (postIds: string[]) => { if (!user?.id || postIds.length === 0) return; try { const likedPosts = new Set(); await Promise.allSettled( postIds.map(async (postId) => { const response = await fetch( `${API_BASE}/api/community/post-likes?post_id=${postId}&user_id=${user.id}` ); if (response.ok) { const data = await response.json(); if (data.userLiked) { likedPosts.add(postId); } } }) ); setUserLikedPosts(likedPosts); } catch (error) { console.error("Failed to load user likes:", error); } }; // Load feed posts useEffect(() => { const loadPosts = async () => { setIsLoading(true); try { const armFilter = activeTab === "all" && selectedArms.length > 0 ? selectedArms : activeTab !== "all" ? [activeTab] : []; const params = new URLSearchParams({ limit: "50", offset: "0", }); if (armFilter.length > 0) { armFilter.forEach((arm) => { params.append("arm_filter", arm); }); } if (user?.id) { params.append("user_id", user.id); } const response = await fetch(`${API_BASE}/api/feed?${params.toString()}`); if (response.ok) { const data = await response.json(); setPosts(data.posts || []); // Load user's likes for these posts const postIds = data.posts?.map((p: Post) => p.id) || []; if (postIds.length > 0) { loadUserLikes(postIds); } } else { console.error("Failed to load feed"); aethexToast.error({ title: "Failed to load feed", description: "Please try again later", }); } } catch (error) { console.error("Error loading feed:", error); aethexToast.error({ title: "Error loading feed", description: error instanceof Error ? error.message : "Unknown error", }); } finally { setIsLoading(false); } }; loadPosts(); }, [activeTab, selectedArms, user?.id]); const handleArmToggle = (armId: ArmType) => { setSelectedArms((prev) => prev.includes(armId) ? prev.filter((a) => a !== armId) : [...prev, armId] ); }; const handleFollowArm = async (armId: ArmType) => { if (!user?.id) { aethexToast.error({ title: "Not signed in", description: "Please sign in to follow arms", }); return; } try { const isFollowing = userFollowedArms.includes(armId); const response = await fetch(`${API_BASE}/api/user/followed-arms`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ user_id: user.id, arm_id: armId, action: isFollowing ? "unfollow" : "follow", }), }); if (response.ok) { if (isFollowing) { setUserFollowedArms((prev) => prev.filter((a) => a !== armId)); aethexToast.info({ title: "Unfollowed", description: `You unfollowed ${armId}`, }); } else { setUserFollowedArms((prev) => [...prev, armId]); aethexToast.success({ title: "Following", description: `You're now following ${armId}`, }); } } } catch (error) { console.error("Error toggling arm follow:", error); aethexToast.error({ title: "Error", description: "Failed to update follow status", }); } }; const handleLike = async (postId: string) => { if (!user?.id) { aethexToast.error({ title: "Not signed in", description: "Please sign in to like posts", }); return; } try { const response = await fetch(`${API_BASE}/api/community/post-likes`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ post_id: postId, user_id: user.id, }), }); if (response.ok) { const data = await response.json(); if (data.liked) { setUserLikedPosts((prev) => new Set([...prev, postId])); } else { setUserLikedPosts((prev) => { const newSet = new Set(prev); newSet.delete(postId); return newSet; }); } // Update post likes count setPosts((prev) => prev.map((p) => p.id === postId ? { ...p, likes_count: data.likes_count } : p ) ); } } catch (error) { console.error("Error toggling like:", error); aethexToast.error({ title: "Error", description: "Failed to like post", }); } }; const handleCommentClick = (postId: string) => { setSelectedPostForComments(postId); }; const handleCommentAdded = () => { // Reload posts to get updated comment count const loadPosts = async () => { try { const armFilter = activeTab === "all" && selectedArms.length > 0 ? selectedArms : activeTab !== "all" ? [activeTab] : []; const params = new URLSearchParams({ limit: "50", offset: "0", }); if (armFilter.length > 0) { armFilter.forEach((arm) => { params.append("arm_filter", arm); }); } if (user?.id) { params.append("user_id", user.id); } const response = await fetch(`${API_BASE}/api/feed?${params.toString()}`); if (response.ok) { const data = await response.json(); setPosts(data.posts || []); } } catch (error) { console.error("Error loading feed:", error); } }; loadPosts(); }; const filteredPosts = useMemo(() => { if (activeTab === "all") { return posts.filter((post) => selectedArms.includes(post.arm_affiliation)); } return posts.filter((post) => post.arm_affiliation === activeTab); }, [posts, activeTab, selectedArms]); if (isLoading) { return ; } return (
{/* Header */}

Community Feed

The AeThex Town Square • See what all arms are building

{/* Sidebar - Filters & Followed Arms */}
{/* Filter Toggle (Mobile) */} {/* Filters Card */} {(showFilters || true) && ( Filter by Arm {ARMS.map((arm) => (
handleArmToggle(arm.id)} className="transition-all" /> {posts.filter((p) => p.arm_affiliation === arm.id).length}
))}
)} {/* Followed Arms Card */} My Arms {userFollowedArms.length === 0 ? (

No arms followed yet. Start following to personalize your feed.

) : ( userFollowedArms.map((armId) => { const arm = ARMS.find((a) => a.id === armId); if (!arm) return null; return (
{arm.label}
); }) )}
{/* Main Feed Content */}
{/* Tabs */}
{ARMS.map((arm) => ( ))}
{/* Posts */}
{filteredPosts.length === 0 ? (

No posts found in{" "} {activeTab === "all" ? "your feed" : activeTab}

) : ( filteredPosts.map((post, index) => (
handleLike(post.id)} onComment={() => handleCommentClick(post.id)} isLiked={userLikedPosts.has(post.id)} />
)) )}
{/* Comments Modal */} {selectedPostForComments && ( { if (!open) { setSelectedPostForComments(null); } }} postId={selectedPostForComments} currentUserId={user?.id} onCommentAdded={handleCommentAdded} /> )}
); }