'use client'; import { useState, useEffect, useRef, useCallback } from 'react'; import Link from 'next/link'; import { useRouter } from 'next/navigation'; import { useAuth } from '@/lib/contexts/AuthContext'; import { PostCard } from '@/components/PostCard'; import { Compose } from '@/components/Compose'; import { Post } from '@/lib/types'; import { EyeOff } from 'lucide-react'; interface FeedMeta { score: number; reasons: string[]; engagement: { likes: number; reposts: number; replies: number; }; } export default function Home() { const router = useRouter(); const { user } = useAuth(); const [posts, setPosts] = useState([]); const [loading, setLoading] = useState(true); const [loadingMore, setLoadingMore] = useState(false); const [nextCursor, setNextCursor] = useState(null); const [replyingTo, setReplyingTo] = useState(null); const [feedType, setFeedType] = useState<'following' | 'curated'>('following'); const [feedMeta, setFeedMeta] = useState<{ algorithm: string; windowHours: number; seedLimit: number; weights: { engagement: number; recency: number; followBoost: number; selfBoost: number; }; } | null>(null); const loadMoreRef = useRef(null); // Redirect unauthenticated users to explore page useEffect(() => { if (user === null) { router.push('/explore'); } }, [user, router]); const loadFeed = async (type: 'following' | 'curated', cursor?: string | null) => { if (cursor) { setLoadingMore(true); } else { setLoading(true); } try { const endpoint = type === 'curated' ? `/api/posts?type=curated${cursor ? `&cursor=${cursor}` : ''}` : `/api/posts?type=home${cursor ? `&cursor=${cursor}` : ''}`; const res = await fetch(endpoint); const data = await res.json(); if (cursor) { setPosts(prev => [...prev, ...(data.posts || [])]); } else { setPosts(data.posts || []); } setFeedMeta(data.meta || null); setNextCursor(data.nextCursor || null); } catch { if (!cursor) { setPosts([]); } setFeedMeta(null); setNextCursor(null); } finally { setLoading(false); setLoadingMore(false); } }; useEffect(() => { setPosts([]); setNextCursor(null); loadFeed(feedType); }, [feedType]); // Infinite scroll observer useEffect(() => { if (!loadMoreRef.current || !nextCursor || loadingMore) return; const observer = new IntersectionObserver( (entries) => { if (entries[0].isIntersecting && nextCursor && !loadingMore) { loadFeed(feedType, nextCursor); } }, { threshold: 0.1 } ); observer.observe(loadMoreRef.current); return () => observer.disconnect(); }, [nextCursor, loadingMore, feedType]); const handlePost = async (content: string, mediaIds: string[], linkPreview?: any, replyToId?: string, isNsfw?: boolean) => { // Check if we're replying to a swarm post let swarmReplyTo: { postId: string; nodeDomain: string } | undefined; let localReplyToId: string | undefined = replyToId; if (replyingTo?.isSwarm && replyingTo.nodeDomain && replyingTo.originalPostId) { // This is a reply to a swarm post - send to the origin node swarmReplyTo = { postId: replyingTo.originalPostId, nodeDomain: replyingTo.nodeDomain, }; localReplyToId = undefined; // Don't set local replyToId for swarm posts } const res = await fetch('/api/posts', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ content, mediaIds, linkPreview, replyToId: localReplyToId, swarmReplyTo, isNsfw }), }); if (res.ok) { const data = await res.json(); if (feedType === 'curated') { setPosts([]); setNextCursor(null); loadFeed('curated'); } else { setPosts([{ ...data.post, author: user }, ...posts]); } setReplyingTo(null); } }; const handleLike = async (postId: string, currentLiked: boolean) => { const method = currentLiked ? 'DELETE' : 'POST'; await fetch(`/api/posts/${postId}/like`, { method }); }; const handleRepost = async (postId: string, currentReposted: boolean) => { const method = currentReposted ? 'DELETE' : 'POST'; await fetch(`/api/posts/${postId}/repost`, { method }); }; const handleDelete = (postId: string) => { setPosts(prev => prev.filter(p => p.id !== postId)); }; // Show loading while checking auth if (user === null) { return (
Loading...
); } return ( <>

Home

setReplyingTo(null)} /> {feedType === 'following' && (
Following feed
This feed shows posts from accounts you follow in chronological order, with the most recent posts appearing first.
)} {feedType === 'curated' && feedMeta && (
Curated feed
This feed highlights fresh posts and active discussions, with a boost for people you follow. It is designed to surface what matters without hiding your own activity.
)} {loading ? (
Loading...
) : posts.length === 0 ? (
{feedType === 'curated' ? ( <>

No posts from the swarm yet

The curated feed shows posts from other nodes in the Synapsis network. Check back later as nodes are discovered, or switch to Following to see posts from people you follow.

) : ( <>

No posts yet

Be the first to post something!

)}
) : ( <> {posts.map(post => ( { setReplyingTo(p); window.scrollTo({ top: 0, behavior: 'smooth' }); }} /> ))} {/* Infinite scroll trigger */}
{loadingMore && ( Loading more... )}
)} ); }