mirror of
https://github.com/logos-messaging/OpChan.git
synced 2026-01-03 21:33:09 +00:00
clean up the share button(s)
This commit is contained in:
parent
244119bb05
commit
df2efac6c3
@ -27,6 +27,7 @@ import { ModerationToggle } from './ui/moderation-toggle';
|
|||||||
import { sortCells, SortOption } from '@/lib/utils/sorting';
|
import { sortCells, SortOption } from '@/lib/utils/sorting';
|
||||||
import { Cell } from '@/types/forum';
|
import { Cell } from '@/types/forum';
|
||||||
import { usePending } from '@/hooks/usePending';
|
import { usePending } from '@/hooks/usePending';
|
||||||
|
import { ShareButton } from './ui/ShareButton';
|
||||||
|
|
||||||
// Empty State Component
|
// Empty State Component
|
||||||
const EmptyState: React.FC<{ canCreateCell: boolean }> = ({
|
const EmptyState: React.FC<{ canCreateCell: boolean }> = ({
|
||||||
@ -125,6 +126,11 @@ const CellItem: React.FC<{ cell: Cell }> = ({ cell }) => {
|
|||||||
{cell.activeMemberCount || 0} members
|
{cell.activeMemberCount || 0} members
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
<ShareButton
|
||||||
|
size='sm'
|
||||||
|
url={`${window.location.origin}/cell/${cell.id}`}
|
||||||
|
title={cell.name}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -18,6 +18,7 @@ import {
|
|||||||
TooltipContent,
|
TooltipContent,
|
||||||
TooltipTrigger,
|
TooltipTrigger,
|
||||||
} from '@/components/ui/tooltip';
|
} from '@/components/ui/tooltip';
|
||||||
|
import { ShareButton } from '@/components/ui/ShareButton';
|
||||||
|
|
||||||
interface CommentCardProps {
|
interface CommentCardProps {
|
||||||
comment: Comment;
|
comment: Comment;
|
||||||
@ -126,13 +127,20 @@ const CommentCard: React.FC<CommentCardProps> = ({
|
|||||||
</span>
|
</span>
|
||||||
<PendingBadge id={comment.id} />
|
<PendingBadge id={comment.id} />
|
||||||
</div>
|
</div>
|
||||||
<BookmarkButton
|
<div className="flex items-center gap-2">
|
||||||
isBookmarked={isBookmarked}
|
<ShareButton
|
||||||
loading={bookmarkLoading}
|
size='sm'
|
||||||
onClick={handleBookmark}
|
url={`${window.location.origin}/post/${postId}#comment-${comment.id}`}
|
||||||
size="sm"
|
title={comment.content.substring(0, 50) + (comment.content.length > 50 ? '...' : '')}
|
||||||
variant="ghost"
|
/>
|
||||||
/>
|
<BookmarkButton
|
||||||
|
isBookmarked={isBookmarked}
|
||||||
|
loading={bookmarkLoading}
|
||||||
|
onClick={handleBookmark}
|
||||||
|
size="sm"
|
||||||
|
variant="ghost"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p className="text-sm break-words mb-2">
|
<p className="text-sm break-words mb-2">
|
||||||
|
|||||||
@ -15,7 +15,7 @@ import { AuthorDisplay } from '@/components/ui/author-display';
|
|||||||
import { BookmarkButton } from '@/components/ui/bookmark-button';
|
import { BookmarkButton } from '@/components/ui/bookmark-button';
|
||||||
import { LinkRenderer } from '@/components/ui/link-renderer';
|
import { LinkRenderer } from '@/components/ui/link-renderer';
|
||||||
import { usePending, usePendingVote } from '@/hooks/usePending';
|
import { usePending, usePendingVote } from '@/hooks/usePending';
|
||||||
import { useToast } from '@/components/ui/use-toast';
|
import { ShareButton } from '@/components/ui/ShareButton';
|
||||||
|
|
||||||
interface PostCardProps {
|
interface PostCardProps {
|
||||||
post: Post;
|
post: Post;
|
||||||
@ -32,7 +32,6 @@ const PostCard: React.FC<PostCardProps> = ({ post, commentCount = 0 }) => {
|
|||||||
loading: bookmarkLoading,
|
loading: bookmarkLoading,
|
||||||
toggleBookmark,
|
toggleBookmark,
|
||||||
} = usePostBookmark(post, post.cellId);
|
} = usePostBookmark(post, post.cellId);
|
||||||
const { toast } = useToast();
|
|
||||||
|
|
||||||
// ✅ Get pre-computed cell data
|
// ✅ Get pre-computed cell data
|
||||||
const cell = cellsWithStats.find(c => c.id === post.cellId);
|
const cell = cellsWithStats.find(c => c.id === post.cellId);
|
||||||
@ -71,33 +70,6 @@ const PostCard: React.FC<PostCardProps> = ({ post, commentCount = 0 }) => {
|
|||||||
await toggleBookmark();
|
await toggleBookmark();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleShare = async (e: React.MouseEvent) => {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
|
|
||||||
const postUrl = `${window.location.origin}/post/${post.id}`;
|
|
||||||
|
|
||||||
try {
|
|
||||||
await navigator.clipboard.writeText(postUrl);
|
|
||||||
toast({
|
|
||||||
title: 'Link copied!',
|
|
||||||
description: 'Post link has been copied to your clipboard.',
|
|
||||||
});
|
|
||||||
} catch {
|
|
||||||
// Fallback for older browsers
|
|
||||||
const textArea = document.createElement('textarea');
|
|
||||||
textArea.value = postUrl;
|
|
||||||
document.body.appendChild(textArea);
|
|
||||||
textArea.select();
|
|
||||||
document.execCommand('copy');
|
|
||||||
document.body.removeChild(textArea);
|
|
||||||
|
|
||||||
toast({
|
|
||||||
title: 'Link copied!',
|
|
||||||
description: 'Post link has been copied to your clipboard.',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="thread-card mb-2">
|
<div className="thread-card mb-2">
|
||||||
@ -207,14 +179,11 @@ const PostCard: React.FC<PostCardProps> = ({ post, commentCount = 0 }) => {
|
|||||||
syncing…
|
syncing…
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
<button
|
<ShareButton
|
||||||
onClick={handleShare}
|
size='sm'
|
||||||
className="hover:text-cyber-accent transition-colors flex items-center gap-1"
|
url={`${window.location.origin}/post/${post.id}`}
|
||||||
title="Copy link"
|
title={post.title}
|
||||||
>
|
/>
|
||||||
<Clipboard size={14} />
|
|
||||||
Share
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
<BookmarkButton
|
<BookmarkButton
|
||||||
isBookmarked={isBookmarked}
|
isBookmarked={isBookmarked}
|
||||||
|
|||||||
@ -27,6 +27,7 @@ import { BookmarkButton } from './ui/bookmark-button';
|
|||||||
import { LinkRenderer } from './ui/link-renderer';
|
import { LinkRenderer } from './ui/link-renderer';
|
||||||
import CommentCard from './CommentCard';
|
import CommentCard from './CommentCard';
|
||||||
import { usePending, usePendingVote } from '@/hooks/usePending';
|
import { usePending, usePendingVote } from '@/hooks/usePending';
|
||||||
|
import { ShareButton } from './ui/ShareButton';
|
||||||
|
|
||||||
const PostDetail = () => {
|
const PostDetail = () => {
|
||||||
const { postId } = useParams<{ postId: string }>();
|
const { postId } = useParams<{ postId: string }>();
|
||||||
@ -243,6 +244,11 @@ const PostDetail = () => {
|
|||||||
variant="ghost"
|
variant="ghost"
|
||||||
showText={true}
|
showText={true}
|
||||||
/>
|
/>
|
||||||
|
<ShareButton
|
||||||
|
size='lg'
|
||||||
|
url={`${window.location.origin}/post/${post.id}`}
|
||||||
|
title={post.title}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm whitespace-pre-wrap break-words">
|
<p className="text-sm whitespace-pre-wrap break-words">
|
||||||
<LinkRenderer text={post.content} />
|
<LinkRenderer text={post.content} />
|
||||||
|
|||||||
@ -14,6 +14,7 @@ import { Input } from '@/components/ui/input';
|
|||||||
import { Textarea } from '@/components/ui/textarea';
|
import { Textarea } from '@/components/ui/textarea';
|
||||||
import { Skeleton } from '@/components/ui/skeleton';
|
import { Skeleton } from '@/components/ui/skeleton';
|
||||||
import { LinkRenderer } from '@/components/ui/link-renderer';
|
import { LinkRenderer } from '@/components/ui/link-renderer';
|
||||||
|
import { ShareButton } from '@/components/ui/ShareButton';
|
||||||
import {
|
import {
|
||||||
ArrowLeft,
|
ArrowLeft,
|
||||||
MessageSquare,
|
MessageSquare,
|
||||||
@ -324,6 +325,15 @@ const PostList = () => {
|
|||||||
className="text-xs"
|
className="text-xs"
|
||||||
showBadge={false}
|
showBadge={false}
|
||||||
/>
|
/>
|
||||||
|
<ShareButton
|
||||||
|
url={`${window.location.origin}/post/${post.id}`}
|
||||||
|
title={post.title}
|
||||||
|
description={post.content}
|
||||||
|
size="sm"
|
||||||
|
variant="ghost"
|
||||||
|
className="text-cyber-neutral"
|
||||||
|
showText={false}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
{canModerate(cell.id) && !post.moderated && (
|
{canModerate(cell.id) && !post.moderated && (
|
||||||
|
|||||||
81
src/components/ui/ShareButton.tsx
Normal file
81
src/components/ui/ShareButton.tsx
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { Share2 } from 'lucide-react';
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
import { useToast } from '../ui/use-toast';
|
||||||
|
|
||||||
|
interface ShareButtonProps {
|
||||||
|
url: string;
|
||||||
|
title: string;
|
||||||
|
description?: string;
|
||||||
|
size?: 'sm' | 'lg';
|
||||||
|
variant?: 'default' | 'ghost' | 'outline';
|
||||||
|
className?: string;
|
||||||
|
showText?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ShareButton({
|
||||||
|
url,
|
||||||
|
title,
|
||||||
|
description = 'Check out this post',
|
||||||
|
size = 'sm',
|
||||||
|
variant = 'ghost',
|
||||||
|
className,
|
||||||
|
showText = false,
|
||||||
|
}: ShareButtonProps) {
|
||||||
|
const { toast } = useToast();
|
||||||
|
|
||||||
|
const sizeClasses = {
|
||||||
|
sm: 'h-8 w-10',
|
||||||
|
lg: 'h-10 whitespace-nowrap px-4',
|
||||||
|
};
|
||||||
|
|
||||||
|
const iconSize = {
|
||||||
|
sm: 14,
|
||||||
|
lg: 18,
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleShare = async (e: React.MouseEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.writeText(url);
|
||||||
|
toast({
|
||||||
|
title: 'Link copied!',
|
||||||
|
description: 'Link has been copied to your clipboard.',
|
||||||
|
});
|
||||||
|
} catch {
|
||||||
|
// Fallback for older browsers
|
||||||
|
const textArea = document.createElement('textarea');
|
||||||
|
textArea.value = url;
|
||||||
|
document.body.appendChild(textArea);
|
||||||
|
textArea.select();
|
||||||
|
document.execCommand('copy');
|
||||||
|
document.body.removeChild(textArea);
|
||||||
|
|
||||||
|
toast({
|
||||||
|
title: 'Link copied!',
|
||||||
|
description: 'Link has been copied to your clipboard.',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
variant={variant}
|
||||||
|
size={size}
|
||||||
|
onClick={handleShare}
|
||||||
|
className={cn(
|
||||||
|
sizeClasses[size],
|
||||||
|
'transition-colors duration-200 text-cyber-neutral hover:text-cyber-light',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
title="Copy link"
|
||||||
|
>
|
||||||
|
<Share2 size={iconSize[size]} />
|
||||||
|
{showText && (
|
||||||
|
<span className="ml-2 text-xs">Share</span>
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user