'use client'; import { useState, useEffect } from 'react'; import Link from 'next/link'; import { useRouter } from 'next/navigation'; import { HeartIcon, RepeatIcon, MessageIcon, FlagIcon, TrashIcon } from '@/components/Icons'; import { Bot, MoreHorizontal, UserX, VolumeX, Globe } from 'lucide-react'; import { Post } from '@/lib/types'; import { useAuth } from '@/lib/contexts/AuthContext'; import { useToast } from '@/lib/contexts/ToastContext'; import { VideoEmbed } from '@/components/VideoEmbed'; import BlurredVideo from '@/components/BlurredVideo'; import { useFormattedHandle } from '@/lib/utils/handle'; import { useDomain } from '@/lib/contexts/ConfigContext'; import { signedAPI } from '@/lib/api/signed-fetch'; // Component for link preview image that hides on error function LinkPreviewImage({ src, alt }: { src: string; alt: string }) { const [hasError, setHasError] = useState(false); if (hasError) return null; return (
, /gi, '\n\n');
decoded = decoded.replace(/<[^>]+>/g, '');
return decoded.trim();
};
const renderContent = (content: string, hidePreviewUrl?: string) => {
const decoded = decodeHtmlEntities(content);
const parts = decoded.split(/(https?:\/\/[^\s]+)/g);
return parts.map((part, index) => {
if (part.match(/^https?:\/\/[^\s]+$/)) {
// If this URL matches the link preview URL, hide it entirely
if (hidePreviewUrl && part.includes(hidePreviewUrl.replace(/^https?:\/\/(www\.)?/, '').split('/')[0])) {
return null;
}
// Extract just the domain (TLD)
try {
const url = new URL(part);
const domain = url.hostname.replace(/^www\./, '');
return (
e.stopPropagation()}
title={part}
>
{domain}
);
} catch {
// Fallback if URL parsing fails
return (
e.stopPropagation()}
>
{part}
);
}
}
// Handle newlines
if (part.includes('\n')) {
return part.split('\n').map((line, lineIndex, arr) => (
{line}
{lineIndex < arr.length - 1 &&
, etc.)
decoded = decoded.replace(/
/gi, '\n');
decoded = decoded.replace(/<\/p>\s*
}
));
}
return {part};
});
};
// Build a synthetic replyTo for swarm replies
const effectiveReplyTo = post.replyTo || (post.swarmReplyToId && post.swarmReplyToAuthor ? {
id: post.swarmReplyToId,
content: post.swarmReplyToContent || '',
createdAt: post.createdAt, // Use same time as approximation
likesCount: 0,
repostsCount: 0,
repliesCount: 0,
author: typeof post.swarmReplyToAuthor === 'string'
? JSON.parse(post.swarmReplyToAuthor)
: post.swarmReplyToAuthor,
isSwarm: true,
nodeDomain: (typeof post.swarmReplyToAuthor === 'string'
? JSON.parse(post.swarmReplyToAuthor)
: post.swarmReplyToAuthor)?.nodeDomain,
} as Post : null);
const replyToHandle = effectiveReplyTo?.author?.handle ? useFormattedHandle(effectiveReplyTo.author.handle, effectiveReplyTo.nodeDomain) : '';
// If this is a thread parent being rendered, just render the article
if (isThreadParent) {
return (
) : (
post.author.displayName?.charAt(0).toUpperCase() || post.author.handle.charAt(0).toUpperCase()
)}
) : (
post.author.displayName?.charAt(0).toUpperCase() || post.author.handle.charAt(0).toUpperCase()
)}
)}