"use client"; import { formatDistanceToNow, parseISO } from "date-fns"; import { BellRing, ExternalLink } from "lucide-react"; import Link from "next/link"; import { useEffect, useState } from "react"; import type { FeedItem, ParsedFeed } from "@/actions/feeds"; import { Badge, Button, DropdownMenu, DropdownMenuContent, DropdownMenuTrigger, Separator, } from "@/components/shadcn"; import { Tooltip, TooltipContent, TooltipTrigger, } from "@/components/shadcn/tooltip"; import { hasNewFeeds, markFeedsAsSeen } from "@/lib/feeds-storage"; import { cn } from "@/lib/utils"; interface FeedsClientProps { feedData: ParsedFeed; error?: string; } export function FeedsClient({ feedData, error }: FeedsClientProps) { const { items, totalCount } = feedData; const hasFeeds = totalCount > 0 && !error; // State to track if there are new unseen feeds const [hasUnseenFeeds, setHasUnseenFeeds] = useState(false); // Check for new feeds on mount useEffect(() => { if (hasFeeds) { const currentFeedIds = items.map((item) => item.id); const isNew = hasNewFeeds(currentFeedIds); setHasUnseenFeeds(isNew); } }, [hasFeeds, items]); // Mark feeds as seen when dropdown opens const handleOpenChange = (open: boolean) => { if (open && hasFeeds) { const currentFeedIds = items.map((item) => item.id); markFeedsAsSeen(currentFeedIds); setHasUnseenFeeds(false); } }; return ( {hasUnseenFeeds ? "New updates available" : "Latest Updates"}

Latest Updates

Recent releases and announcements

{error && (

{error}

)} {!error && items.length === 0 && (

No updates available

Check back later for new releases

)} {hasFeeds && (
{items.map((item, index) => ( ))}
)}
); } interface FeedTimelineItemProps { item: FeedItem; isLast: boolean; } function FeedTimelineItem({ item, isLast }: FeedTimelineItemProps) { const relativeTime = formatDistanceToNow(parseISO(item.pubDate), { addSuffix: true, }); // Extract version from title if it's a GitHub release const versionMatch = item.title.match(/v?(\d+\.\d+\.\d+)/); const version = versionMatch ? versionMatch[1] : null; return (
{/* Timeline dot */}
{!isLast && (
)}
{/* Content */}

{item.title}

{version && ( v{version} )}
{item.description && (

{item.description}

)}
Read more
); }