index.tsx raw
1 import { useFetchWebMetadata } from '@/hooks/useFetchWebMetadata'
2 import { cn } from '@/lib/utils'
3 import { useContentPolicy } from '@/providers/ContentPolicyProvider'
4 import { useScreenSize } from '@/providers/ScreenSizeProvider'
5 import { useMemo } from 'react'
6 import Image from '../Image'
7 import ExternalLink from '../ExternalLink'
8
9 export default function WebPreview({
10 url,
11 className,
12 mustLoad
13 }: {
14 url: string
15 className?: string
16 mustLoad?: boolean
17 }) {
18 const { autoLoadMedia } = useContentPolicy()
19 const { isSmallScreen } = useScreenSize()
20 const { title, description, image } = useFetchWebMetadata(url)
21
22 const hostname = useMemo(() => {
23 try {
24 return new URL(url).hostname
25 } catch {
26 return ''
27 }
28 }, [url])
29
30 if (!autoLoadMedia && !mustLoad) {
31 return null
32 }
33
34 if (!title) {
35 if (mustLoad) {
36 return <ExternalLink url={url} justOpenLink />
37 } else {
38 return null
39 }
40 }
41
42 if (isSmallScreen && image) {
43 return (
44 <div
45 className="rounded-xl border mt-2 overflow-hidden"
46 onClick={(e) => {
47 e.stopPropagation()
48 window.open(url, '_blank')
49 }}
50 >
51 <Image image={{ url: image }} className="w-full h-44 rounded-none" hideIfError />
52 <div className="bg-muted p-2 w-full">
53 <div className="text-xs text-muted-foreground">{hostname}</div>
54 <div className="font-semibold line-clamp-1">{title}</div>
55 </div>
56 </div>
57 )
58 }
59
60 return (
61 <div
62 className={cn('p-0 clickable flex w-full border rounded-xl overflow-hidden', className)}
63 onClick={(e) => {
64 e.stopPropagation()
65 window.open(url, '_blank')
66 }}
67 >
68 {image && (
69 <Image
70 image={{ url: image }}
71 className="aspect-[4/3] xl:aspect-video bg-foreground h-44"
72 classNames={{
73 wrapper: 'rounded-none border-r'
74 }}
75 hideIfError
76 />
77 )}
78 <div className="flex-1 w-0 p-2">
79 <div className="text-xs text-muted-foreground">{hostname}</div>
80 <div className="font-semibold line-clamp-2">{title}</div>
81 <div className="text-xs text-muted-foreground line-clamp-5">{description}</div>
82 </div>
83 </div>
84 )
85 }
86