feat: support sending and preview links
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
import { Copy, ExternalLink, Globe } from "lucide-react";
|
||||
import type { LinkMetadata } from "@/lib/link-metadata";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
interface LinkPreviewCardProps {
|
||||
metadata: LinkMetadata;
|
||||
compact?: boolean;
|
||||
}
|
||||
|
||||
export function LinkPreviewCard({ metadata, compact }: LinkPreviewCardProps) {
|
||||
const handleOpen = (e: React.MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
window.electronLink.openExternal(metadata.url);
|
||||
};
|
||||
|
||||
const handleCopy = (e: React.MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
navigator.clipboard.writeText(metadata.url);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className="max-w-sm overflow-hidden rounded-2xl bg-white/10 backdrop-blur-md"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
{metadata.image && (
|
||||
<img
|
||||
src={metadata.image}
|
||||
alt=""
|
||||
className="h-32 w-full object-cover"
|
||||
onError={(e) => {
|
||||
(e.target as HTMLImageElement).style.display = "none";
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<div className="flex flex-col gap-1 p-3">
|
||||
<div className="flex items-center gap-1.5 text-xs text-white/50">
|
||||
{metadata.favicon ? (
|
||||
<img
|
||||
src={metadata.favicon}
|
||||
alt=""
|
||||
className="size-4 rounded-sm"
|
||||
onError={(e) => {
|
||||
(e.target as HTMLImageElement).replaceWith(
|
||||
document.createElement("span"),
|
||||
);
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<Globe className="size-4" />
|
||||
)}
|
||||
<span className="truncate">{metadata.domain}</span>
|
||||
</div>
|
||||
{metadata.title && (
|
||||
<p className="truncate text-sm font-semibold leading-snug text-white">
|
||||
{metadata.title}
|
||||
</p>
|
||||
)}
|
||||
{metadata.description && (
|
||||
<p className="line-clamp-2 text-xs leading-relaxed text-white/70">
|
||||
{metadata.description}
|
||||
</p>
|
||||
)}
|
||||
{!compact && (
|
||||
<div className="mt-1.5 flex gap-2">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="xs"
|
||||
className="text-white/70 hover:bg-white/10 hover:text-white"
|
||||
onClick={handleOpen}
|
||||
>
|
||||
<ExternalLink data-icon="inline-start" />
|
||||
Open
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="xs"
|
||||
className="text-white/70 hover:bg-white/10 hover:text-white"
|
||||
onClick={handleCopy}
|
||||
>
|
||||
<Copy data-icon="inline-start" />
|
||||
Copy
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function LinkPreviewCardSkeleton() {
|
||||
return (
|
||||
<div className="max-w-sm overflow-hidden rounded-2xl bg-white/10 backdrop-blur-md">
|
||||
<Skeleton className="h-32 w-full rounded-none bg-white/5" />
|
||||
<div className="flex flex-col gap-2 p-3">
|
||||
<Skeleton className="h-3 w-24 bg-white/10" />
|
||||
<Skeleton className="h-4 w-48 bg-white/10" />
|
||||
<Skeleton className="h-3 w-full bg-white/10" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user