refactor: reformat PostCard component for improved readability and consistent styling.
This commit is contained in:
+231
-225
@@ -15,14 +15,14 @@ import { formatFullHandle, NODE_DOMAIN } from '@/lib/utils/handle';
|
|||||||
// Component for link preview image that hides on error
|
// Component for link preview image that hides on error
|
||||||
function LinkPreviewImage({ src, alt }: { src: string; alt: string }) {
|
function LinkPreviewImage({ src, alt }: { src: string; alt: string }) {
|
||||||
const [hasError, setHasError] = useState(false);
|
const [hasError, setHasError] = useState(false);
|
||||||
|
|
||||||
if (hasError) return null;
|
if (hasError) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="link-preview-image">
|
<div className="link-preview-image">
|
||||||
<img
|
<img
|
||||||
src={src}
|
src={src}
|
||||||
alt={alt}
|
alt={alt}
|
||||||
onError={() => setHasError(true)}
|
onError={() => setHasError(true)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -60,19 +60,19 @@ export function PostCard({ post, onLike, onRepost, onComment, onDelete, onHide,
|
|||||||
|
|
||||||
const formatTime = (dateStr: string | Date) => {
|
const formatTime = (dateStr: string | Date) => {
|
||||||
const date = new Date(dateStr);
|
const date = new Date(dateStr);
|
||||||
|
|
||||||
if (isNaN(date.getTime())) {
|
if (isNaN(date.getTime())) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const diff = now.getTime() - date.getTime();
|
const diff = now.getTime() - date.getTime();
|
||||||
|
|
||||||
// If post is in the future (minor clock skew), show "now"
|
// If post is in the future (minor clock skew), show "now"
|
||||||
if (diff < 0) {
|
if (diff < 0) {
|
||||||
return 'now';
|
return 'now';
|
||||||
}
|
}
|
||||||
|
|
||||||
const seconds = Math.floor(diff / 1000);
|
const seconds = Math.floor(diff / 1000);
|
||||||
const minutes = Math.floor(seconds / 60);
|
const minutes = Math.floor(seconds / 60);
|
||||||
const hours = Math.floor(minutes / 60);
|
const hours = Math.floor(minutes / 60);
|
||||||
@@ -163,7 +163,7 @@ export function PostCard({ post, onLike, onRepost, onComment, onDelete, onHide,
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
setShowMenu(false);
|
setShowMenu(false);
|
||||||
|
|
||||||
if (!currentUser) {
|
if (!currentUser) {
|
||||||
showToast('Please log in to block users', 'error');
|
showToast('Please log in to block users', 'error');
|
||||||
return;
|
return;
|
||||||
@@ -188,7 +188,7 @@ export function PostCard({ post, onLike, onRepost, onComment, onDelete, onHide,
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
setShowMenu(false);
|
setShowMenu(false);
|
||||||
|
|
||||||
if (!currentUser) {
|
if (!currentUser) {
|
||||||
showToast('Please log in to mute users', 'error');
|
showToast('Please log in to mute users', 'error');
|
||||||
return;
|
return;
|
||||||
@@ -222,8 +222,8 @@ export function PostCard({ post, onLike, onRepost, onComment, onDelete, onHide,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Extract node domain from the post
|
// Extract node domain from the post
|
||||||
const nodeDomain = post.nodeDomain || (post.author.handle.includes('@')
|
const nodeDomain = post.nodeDomain || (post.author.handle.includes('@')
|
||||||
? post.author.handle.split('@')[1]
|
? post.author.handle.split('@')[1]
|
||||||
: null);
|
: null);
|
||||||
|
|
||||||
if (!nodeDomain) {
|
if (!nodeDomain) {
|
||||||
@@ -370,11 +370,11 @@ export function PostCard({ post, onLike, onRepost, onComment, onDelete, onHide,
|
|||||||
likesCount: 0,
|
likesCount: 0,
|
||||||
repostsCount: 0,
|
repostsCount: 0,
|
||||||
repliesCount: 0,
|
repliesCount: 0,
|
||||||
author: typeof post.swarmReplyToAuthor === 'string'
|
author: typeof post.swarmReplyToAuthor === 'string'
|
||||||
? JSON.parse(post.swarmReplyToAuthor)
|
? JSON.parse(post.swarmReplyToAuthor)
|
||||||
: post.swarmReplyToAuthor,
|
: post.swarmReplyToAuthor,
|
||||||
isSwarm: true,
|
isSwarm: true,
|
||||||
nodeDomain: (typeof post.swarmReplyToAuthor === 'string'
|
nodeDomain: (typeof post.swarmReplyToAuthor === 'string'
|
||||||
? JSON.parse(post.swarmReplyToAuthor)
|
? JSON.parse(post.swarmReplyToAuthor)
|
||||||
: post.swarmReplyToAuthor)?.nodeDomain,
|
: post.swarmReplyToAuthor)?.nodeDomain,
|
||||||
} as Post : null);
|
} as Post : null);
|
||||||
@@ -426,137 +426,98 @@ export function PostCard({ post, onLike, onRepost, onComment, onDelete, onHide,
|
|||||||
<article className={`post ${isDetail ? 'detail' : ''}`}>
|
<article className={`post ${isDetail ? 'detail' : ''}`}>
|
||||||
{!isDetail && <Link href={postUrl} className="post-link-overlay" aria-label="View post" />}
|
{!isDetail && <Link href={postUrl} className="post-link-overlay" aria-label="View post" />}
|
||||||
|
|
||||||
<div className="post-header">
|
<div className="post-header">
|
||||||
<Link href={`/${profileHandle}`} className="avatar-link" onClick={(e) => e.stopPropagation()}>
|
<Link href={`/${profileHandle}`} className="avatar-link" onClick={(e) => e.stopPropagation()}>
|
||||||
<div className="avatar">
|
<div className="avatar">
|
||||||
{post.author.avatarUrl ? (
|
{post.author.avatarUrl ? (
|
||||||
<img src={post.author.avatarUrl} alt={post.author.displayName} />
|
<img src={post.author.avatarUrl} alt={post.author.displayName} />
|
||||||
) : (
|
) : (
|
||||||
post.author.displayName?.charAt(0).toUpperCase() || post.author.handle.charAt(0).toUpperCase()
|
post.author.displayName?.charAt(0).toUpperCase() || post.author.handle.charAt(0).toUpperCase()
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
<div className="post-author">
|
<div className="post-author">
|
||||||
<div style={{ display: 'flex', alignItems: 'center', gap: '6px' }}>
|
<div style={{ display: 'flex', alignItems: 'center', gap: '6px' }}>
|
||||||
<Link href={`/${profileHandle}`} className="post-handle" onClick={(e) => e.stopPropagation()}>
|
<Link href={`/${profileHandle}`} className="post-handle" onClick={(e) => e.stopPropagation()}>
|
||||||
{post.author.displayName || post.author.handle}
|
{post.author.displayName || post.author.handle}
|
||||||
</Link>
|
</Link>
|
||||||
{post.bot && (
|
{post.bot && (
|
||||||
<span
|
<span
|
||||||
style={{
|
|
||||||
display: 'inline-flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
gap: '3px',
|
|
||||||
fontSize: '10px',
|
|
||||||
padding: '2px 6px',
|
|
||||||
borderRadius: '4px',
|
|
||||||
background: 'var(--accent-muted)',
|
|
||||||
color: 'var(--accent)',
|
|
||||||
fontWeight: 500,
|
|
||||||
}}
|
|
||||||
title={`AI Account: ${post.bot.name}`}
|
|
||||||
>
|
|
||||||
<Bot size={12} />
|
|
||||||
AI Account
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<span className="post-time">{formatFullHandle(post.author.handle, post.nodeDomain)} · {formatTime(post.createdAt)}</span>
|
|
||||||
</div>
|
|
||||||
{currentUser && currentUser.id !== post.author.id && (
|
|
||||||
<div style={{ position: 'relative', marginLeft: 'auto' }}>
|
|
||||||
<button
|
|
||||||
className="post-menu-btn"
|
|
||||||
onClick={(e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
setShowMenu(!showMenu);
|
|
||||||
}}
|
|
||||||
style={{
|
|
||||||
background: 'none',
|
|
||||||
border: 'none',
|
|
||||||
padding: '4px',
|
|
||||||
cursor: 'pointer',
|
|
||||||
color: 'var(--foreground-tertiary)',
|
|
||||||
borderRadius: '50%',
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<MoreHorizontal size={18} />
|
|
||||||
</button>
|
|
||||||
{showMenu && (
|
|
||||||
<>
|
|
||||||
<div
|
|
||||||
style={{
|
style={{
|
||||||
position: 'fixed',
|
display: 'inline-flex',
|
||||||
inset: 0,
|
alignItems: 'center',
|
||||||
zIndex: 99,
|
gap: '3px',
|
||||||
}}
|
fontSize: '10px',
|
||||||
onClick={(e) => {
|
padding: '2px 6px',
|
||||||
e.preventDefault();
|
borderRadius: '4px',
|
||||||
e.stopPropagation();
|
background: 'var(--accent-muted)',
|
||||||
setShowMenu(false);
|
color: 'var(--accent)',
|
||||||
}}
|
fontWeight: 500,
|
||||||
/>
|
|
||||||
<div
|
|
||||||
className="post-menu-dropdown"
|
|
||||||
style={{
|
|
||||||
position: 'absolute',
|
|
||||||
right: 0,
|
|
||||||
top: '100%',
|
|
||||||
marginTop: '4px',
|
|
||||||
background: 'var(--background-secondary)',
|
|
||||||
border: '1px solid var(--border)',
|
|
||||||
borderRadius: 'var(--radius-md)',
|
|
||||||
minWidth: '180px',
|
|
||||||
zIndex: 100,
|
|
||||||
overflow: 'hidden',
|
|
||||||
boxShadow: '0 4px 12px rgba(0,0,0,0.15)',
|
|
||||||
}}
|
}}
|
||||||
|
title={`AI Account: ${post.bot.name}`}
|
||||||
>
|
>
|
||||||
<button
|
<Bot size={12} />
|
||||||
onClick={handleMuteUser}
|
AI Account
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<span className="post-time">{formatFullHandle(post.author.handle, post.nodeDomain)} · {formatTime(post.createdAt)}</span>
|
||||||
|
</div>
|
||||||
|
{currentUser && currentUser.id !== post.author.id && (
|
||||||
|
<div style={{ position: 'relative', marginLeft: 'auto' }}>
|
||||||
|
<button
|
||||||
|
className="post-menu-btn"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
setShowMenu(!showMenu);
|
||||||
|
}}
|
||||||
|
style={{
|
||||||
|
background: 'none',
|
||||||
|
border: 'none',
|
||||||
|
padding: '4px',
|
||||||
|
cursor: 'pointer',
|
||||||
|
color: 'var(--foreground-tertiary)',
|
||||||
|
borderRadius: '50%',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<MoreHorizontal size={18} />
|
||||||
|
</button>
|
||||||
|
{showMenu && (
|
||||||
|
<>
|
||||||
|
<div
|
||||||
style={{
|
style={{
|
||||||
width: '100%',
|
position: 'fixed',
|
||||||
padding: '10px 14px',
|
inset: 0,
|
||||||
background: 'none',
|
zIndex: 99,
|
||||||
border: 'none',
|
}}
|
||||||
textAlign: 'left',
|
onClick={(e) => {
|
||||||
cursor: 'pointer',
|
e.preventDefault();
|
||||||
color: 'var(--foreground)',
|
e.stopPropagation();
|
||||||
fontSize: '14px',
|
setShowMenu(false);
|
||||||
display: 'flex',
|
}}
|
||||||
alignItems: 'center',
|
/>
|
||||||
gap: '10px',
|
<div
|
||||||
|
className="post-menu-dropdown"
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
right: 0,
|
||||||
|
top: '100%',
|
||||||
|
marginTop: '4px',
|
||||||
|
background: 'var(--background-secondary)',
|
||||||
|
border: '1px solid var(--border)',
|
||||||
|
borderRadius: 'var(--radius-md)',
|
||||||
|
minWidth: '180px',
|
||||||
|
zIndex: 100,
|
||||||
|
overflow: 'hidden',
|
||||||
|
boxShadow: '0 4px 12px rgba(0,0,0,0.15)',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<VolumeX size={16} />
|
|
||||||
Mute
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={handleBlockUser}
|
|
||||||
style={{
|
|
||||||
width: '100%',
|
|
||||||
padding: '10px 14px',
|
|
||||||
background: 'none',
|
|
||||||
border: 'none',
|
|
||||||
textAlign: 'left',
|
|
||||||
cursor: 'pointer',
|
|
||||||
color: 'var(--foreground)',
|
|
||||||
fontSize: '14px',
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
gap: '10px',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<UserX size={16} />
|
|
||||||
Block
|
|
||||||
</button>
|
|
||||||
{(post.nodeDomain || post.author.handle.includes('@')) && (
|
|
||||||
<button
|
<button
|
||||||
onClick={handleMuteNode}
|
onClick={handleMuteUser}
|
||||||
style={{
|
style={{
|
||||||
width: '100%',
|
width: '100%',
|
||||||
padding: '10px 14px',
|
padding: '10px 14px',
|
||||||
@@ -569,104 +530,149 @@ export function PostCard({ post, onLike, onRepost, onComment, onDelete, onHide,
|
|||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
gap: '10px',
|
gap: '10px',
|
||||||
borderTop: '1px solid var(--border)',
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Globe size={16} />
|
<VolumeX size={16} />
|
||||||
Mute node
|
Mute
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={handleBlockUser}
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
padding: '10px 14px',
|
||||||
|
background: 'none',
|
||||||
|
border: 'none',
|
||||||
|
textAlign: 'left',
|
||||||
|
cursor: 'pointer',
|
||||||
|
color: 'var(--foreground)',
|
||||||
|
fontSize: '14px',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: '10px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<UserX size={16} />
|
||||||
|
Block
|
||||||
|
</button>
|
||||||
|
{(post.nodeDomain || post.author.handle.includes('@')) && (
|
||||||
|
<button
|
||||||
|
onClick={handleMuteNode}
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
padding: '10px 14px',
|
||||||
|
background: 'none',
|
||||||
|
border: 'none',
|
||||||
|
textAlign: 'left',
|
||||||
|
cursor: 'pointer',
|
||||||
|
color: 'var(--foreground)',
|
||||||
|
fontSize: '14px',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: '10px',
|
||||||
|
borderTop: '1px solid var(--border)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Globe size={16} />
|
||||||
|
Mute node
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{effectiveReplyTo && !showThread && (
|
||||||
|
<div className="post-reply-to">
|
||||||
|
Replying to <Link href={`/${effectiveReplyTo.author.handle}`} onClick={(e) => e.stopPropagation()}>{formatFullHandle(effectiveReplyTo.author.handle)}</Link>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="post-content">{renderContent(post.content, post.linkPreviewUrl ?? undefined)}</div>
|
||||||
|
|
||||||
|
{post.media && post.media.length > 0 && (
|
||||||
|
<div className="post-media-grid">
|
||||||
|
{post.media.map((item) => {
|
||||||
|
const isVideo = item.mimeType?.startsWith('video/');
|
||||||
|
return (
|
||||||
|
<div className="post-media-item" key={item.id}>
|
||||||
|
{isVideo ? (
|
||||||
|
<BlurredVideo
|
||||||
|
src={item.url}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
const video = e.currentTarget;
|
||||||
|
video.muted = !video.muted;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<img src={item.url} alt={item.altText || 'Post media'} loading="lazy" />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</>
|
);
|
||||||
)}
|
})}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
|
||||||
|
|
||||||
{effectiveReplyTo && !showThread && (
|
{post.linkPreviewUrl && (
|
||||||
<div className="post-reply-to">
|
<VideoEmbed url={post.linkPreviewUrl} />
|
||||||
Replying to <Link href={`/${effectiveReplyTo.author.handle}`} onClick={(e) => e.stopPropagation()}>{formatFullHandle(effectiveReplyTo.author.handle)}</Link>
|
)}
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="post-content">{renderContent(post.content, post.linkPreviewUrl ?? undefined)}</div>
|
{post.linkPreviewUrl && !post.linkPreviewUrl.match(/(youtube\.com|youtu\.be|vimeo\.com)/) && (
|
||||||
|
<a
|
||||||
{post.media && post.media.length > 0 && (
|
href={post.linkPreviewUrl}
|
||||||
<div className="post-media-grid">
|
target="_blank"
|
||||||
{post.media.map((item) => {
|
rel="noopener noreferrer"
|
||||||
const isVideo = item.mimeType?.startsWith('video/');
|
className="link-preview-card"
|
||||||
return (
|
onClick={(e) => e.stopPropagation()}
|
||||||
<div className="post-media-item" key={item.id}>
|
>
|
||||||
{isVideo ? (
|
{post.linkPreviewImage && (
|
||||||
<BlurredVideo
|
<LinkPreviewImage src={post.linkPreviewImage} alt={post.linkPreviewTitle || ''} />
|
||||||
src={item.url}
|
)}
|
||||||
onClick={(e) => {
|
<div className="link-preview-info">
|
||||||
e.stopPropagation();
|
<div className="link-preview-title">{post.linkPreviewTitle ? decodeHtmlEntities(post.linkPreviewTitle) : ''}</div>
|
||||||
const video = e.currentTarget;
|
{post.linkPreviewDescription && (
|
||||||
video.muted = !video.muted;
|
<div className="link-preview-description">{decodeHtmlEntities(post.linkPreviewDescription)}</div>
|
||||||
}}
|
)}
|
||||||
/>
|
<div className="link-preview-url">
|
||||||
) : (
|
{new URL(post.linkPreviewUrl.startsWith('http') ? post.linkPreviewUrl : `https://${post.linkPreviewUrl}`).hostname}
|
||||||
<img src={item.url} alt={item.altText || 'Post media'} loading="lazy" />
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{post.linkPreviewUrl && (
|
|
||||||
<VideoEmbed url={post.linkPreviewUrl} />
|
|
||||||
)}
|
|
||||||
|
|
||||||
{post.linkPreviewUrl && !post.linkPreviewUrl.match(/(youtube\.com|youtu\.be|vimeo\.com)/) && (
|
|
||||||
<a
|
|
||||||
href={post.linkPreviewUrl}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
className="link-preview-card"
|
|
||||||
onClick={(e) => e.stopPropagation()}
|
|
||||||
>
|
|
||||||
{post.linkPreviewImage && (
|
|
||||||
<LinkPreviewImage src={post.linkPreviewImage} alt={post.linkPreviewTitle || ''} />
|
|
||||||
)}
|
|
||||||
<div className="link-preview-info">
|
|
||||||
<div className="link-preview-title">{post.linkPreviewTitle ? decodeHtmlEntities(post.linkPreviewTitle) : ''}</div>
|
|
||||||
{post.linkPreviewDescription && (
|
|
||||||
<div className="link-preview-description">{decodeHtmlEntities(post.linkPreviewDescription)}</div>
|
|
||||||
)}
|
|
||||||
<div className="link-preview-url">
|
|
||||||
{new URL(post.linkPreviewUrl.startsWith('http') ? post.linkPreviewUrl : `https://${post.linkPreviewUrl}`).hostname}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</a>
|
||||||
</a>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="post-actions">
|
|
||||||
<button className="post-action" onClick={handleComment}>
|
|
||||||
<MessageIcon />
|
|
||||||
<span>{post.repliesCount || ''}</span>
|
|
||||||
</button>
|
|
||||||
<button className={`post-action ${reposted ? 'reposted' : ''}`} onClick={handleRepost}>
|
|
||||||
<RepeatIcon />
|
|
||||||
<span>{(post.repostsCount - (post.isReposted ? 1 : 0)) + (reposted ? 1 : 0) || ''}</span>
|
|
||||||
</button>
|
|
||||||
<button className={`post-action ${liked ? 'liked' : ''}`} onClick={handleLike}>
|
|
||||||
<HeartIcon filled={liked} />
|
|
||||||
<span>{(post.likesCount - (post.isLiked ? 1 : 0)) + (liked ? 1 : 0) || ''}</span>
|
|
||||||
</button>
|
|
||||||
<button className="post-action" onClick={handleReport} disabled={reporting}>
|
|
||||||
<FlagIcon />
|
|
||||||
<span>{reporting ? '...' : ''}</span>
|
|
||||||
</button>
|
|
||||||
{(currentUser?.id === post.author.id || (post.bot && currentUser?.id === post.bot.ownerId) || (parentPostAuthorId && currentUser?.id === parentPostAuthorId)) && (
|
|
||||||
<button className="post-action delete-action" onClick={handleDelete} disabled={deleting} title="Delete post">
|
|
||||||
<TrashIcon />
|
|
||||||
<span>{deleting ? '...' : ''}</span>
|
|
||||||
</button>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
|
||||||
</article>
|
<div className="post-actions">
|
||||||
|
<button className="post-action" onClick={handleComment}>
|
||||||
|
<MessageIcon />
|
||||||
|
<span>{post.repliesCount || ''}</span>
|
||||||
|
</button>
|
||||||
|
<button className={`post-action ${reposted ? 'reposted' : ''}`} onClick={handleRepost}>
|
||||||
|
<RepeatIcon />
|
||||||
|
<span>{(post.repostsCount - (post.isReposted ? 1 : 0)) + (reposted ? 1 : 0) || ''}</span>
|
||||||
|
</button>
|
||||||
|
<button className={`post-action ${liked ? 'liked' : ''}`} onClick={handleLike}>
|
||||||
|
<HeartIcon filled={liked} />
|
||||||
|
<span>{(post.likesCount - (post.isLiked ? 1 : 0)) + (liked ? 1 : 0) || ''}</span>
|
||||||
|
</button>
|
||||||
|
<button className="post-action" onClick={handleReport} disabled={reporting}>
|
||||||
|
<FlagIcon />
|
||||||
|
<span>{reporting ? '...' : ''}</span>
|
||||||
|
</button>
|
||||||
|
{(currentUser && (
|
||||||
|
currentUser.id === post.author.id ||
|
||||||
|
(post.bot && currentUser.id === post.bot.ownerId) ||
|
||||||
|
(parentPostAuthorId && currentUser.id === parentPostAuthorId) ||
|
||||||
|
// Allow deleting own remote posts (where ID format differs but handle matches)
|
||||||
|
(post.author.id.startsWith('swarm:') && post.author.handle === currentUser.handle)
|
||||||
|
)) && (
|
||||||
|
<button className="post-action delete-action" onClick={handleDelete} disabled={deleting} title="Delete post">
|
||||||
|
<TrashIcon />
|
||||||
|
<span>{deleting ? '...' : ''}</span>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user