added link preview card
This commit is contained in:
parent
06875d47a4
commit
5bd643e672
77
components/LinkCard/index.tsx
Normal file
77
components/LinkCard/index.tsx
Normal file
@ -0,0 +1,77 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect } from "react";
|
||||
import Image from "next/image";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { fetchMetadata } from "@/lib/fetchers/metadata";
|
||||
import { HiOutlineCheckBadge } from "react-icons/hi2";
|
||||
|
||||
type LinkCardProps = {
|
||||
url: string;
|
||||
metadata?: {
|
||||
title: string;
|
||||
image?: string;
|
||||
description?: string;
|
||||
creator?: string;
|
||||
type?: string;
|
||||
"theme-color"?: string;
|
||||
};
|
||||
className?: string;
|
||||
};
|
||||
export default function LinkCard({
|
||||
url,
|
||||
metadata: _metadata,
|
||||
className,
|
||||
}: LinkCardProps) {
|
||||
const [metadata, setMetadata] = useState(_metadata);
|
||||
useEffect(() => {
|
||||
if (!metadata) {
|
||||
fetchMetadata(url)
|
||||
?.then((r) => {
|
||||
if (r) {
|
||||
setMetadata(r.data);
|
||||
}
|
||||
})
|
||||
.catch((e) => console.log("fetch error"));
|
||||
}
|
||||
}, [url]);
|
||||
if (metadata) {
|
||||
return (
|
||||
<a href={url} target="_blank" rel="nonreferrer">
|
||||
<Card className="group">
|
||||
{metadata.image && (
|
||||
<div className="h-[150px] overflow-hidden rounded-t-md">
|
||||
<Image
|
||||
width={250}
|
||||
height={150}
|
||||
src={metadata.image}
|
||||
alt={metadata.title}
|
||||
unoptimized
|
||||
className={cn(
|
||||
"w-auto object-cover object-center transition-all group-hover:scale-105",
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className="">
|
||||
<CardHeader className="">
|
||||
<CardTitle className="line-clamp-2">{metadata.title}</CardTitle>
|
||||
<CardDescription className="line-clamp-3">
|
||||
{metadata.description}
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
</div>
|
||||
</Card>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
31
lib/fetchers/metadata.ts
Normal file
31
lib/fetchers/metadata.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import { z } from "zod";
|
||||
import { validateUrl } from "@/lib/utils";
|
||||
import { createZodFetcher } from "zod-fetch";
|
||||
|
||||
const fetchWithZod = createZodFetcher();
|
||||
|
||||
const metadataSchema = z.object({
|
||||
title: z.string(),
|
||||
description: z.string().optional(),
|
||||
image: z.string().optional(),
|
||||
creator: z.string().optional(),
|
||||
type: z.string().optional(),
|
||||
"theme-color": z.string().optional(),
|
||||
});
|
||||
const metadataSchemaResponse = z.object({
|
||||
data: metadataSchema,
|
||||
});
|
||||
|
||||
export function fetchMetadata(url: string) {
|
||||
if (!validateUrl(url)) return;
|
||||
return fetchWithZod(
|
||||
// The schema you want to validate with
|
||||
metadataSchemaResponse,
|
||||
// Any parameters you would usually pass to fetch
|
||||
"/api/metadata",
|
||||
{
|
||||
method: "POST",
|
||||
body: JSON.stringify({ url }),
|
||||
},
|
||||
);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user