aboutsummaryrefslogtreecommitdiff
path: root/apps/web/components/new/document-cards/tweet-preview.tsx
blob: c9a30ecd5c2b58d4fe46dcdf803b1184b03c0bf8 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
"use client"

import { Suspense } from "react"
import type { Tweet } from "react-tweet/api"
import { TweetBody, enrichTweet, TweetSkeleton } from "react-tweet"
import { cn } from "@lib/utils"
import { dmSansClassName } from "@/lib/fonts"
import { PlayCircle } from "lucide-react"

function VerifiedBadge({ className }: { className?: string }) {
	return (
		<svg
			viewBox="0 0 22 22"
			aria-label="Verified account"
			className={cn("size-3", className)}
		>
			<title>Verified</title>
			<g>
				<path
					fill="#1D9BF0"
					d="M20.396 11c-.018-.646-.215-1.275-.57-1.816-.354-.54-.852-.972-1.438-1.246.223-.607.27-1.264.14-1.897-.131-.634-.437-1.218-.882-1.687-.47-.445-1.053-.75-1.687-.882-.633-.13-1.29-.083-1.897.14-.273-.587-.704-1.086-1.245-1.44S11.647 1.62 11 1.604c-.646.017-1.273.213-1.813.568s-.969.854-1.24 1.44c-.608-.223-1.267-.272-1.902-.14-.635.13-1.22.436-1.69.882-.445.47-.749 1.055-.878 1.688-.13.633-.08 1.29.144 1.896-.587.274-1.087.705-1.443 1.245-.356.54-.555 1.17-.574 1.817.02.647.218 1.276.574 1.817.356.54.856.972 1.443 1.245-.224.606-.274 1.263-.144 1.896.13.634.433 1.218.877 1.688.47.443 1.054.747 1.687.878.633.132 1.29.084 1.897-.136.274.586.705 1.084 1.246 1.439.54.354 1.17.551 1.816.569.647-.016 1.276-.213 1.817-.567s.972-.854 1.245-1.44c.604.239 1.266.296 1.903.164.636-.132 1.22-.447 1.68-.907.46-.46.776-1.044.908-1.681s.075-1.299-.165-1.903c.586-.274 1.084-.705 1.439-1.246.354-.54.551-1.17.569-1.816zM9.662 14.85l-3.429-3.428 1.293-1.302 2.072 2.072 4.4-4.794 1.347 1.246z"
				/>
			</g>
		</svg>
	)
}

function XLogo({ className }: { className?: string }) {
	return (
		<svg
			viewBox="0 0 24 24"
			aria-hidden="true"
			className={cn("size-3 fill-white", className)}
		>
			<title>X</title>
			<g>
				<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z" />
			</g>
		</svg>
	)
}

function CustomTweetHeader({
	tweet,
}: {
	tweet: ReturnType<typeof enrichTweet>
}) {
	const user = tweet.user
	const isVerified = user.verified || user.is_blue_verified

	return (
		<div className="flex items-start justify-between pr-0.5 w-full">
			<div className="flex gap-2 items-center">
				<div className="bg-white overflow-hidden rounded-full shrink-0 size-[30px]">
					<img
						src={user.profile_image_url_https}
						alt={user.name}
						className="size-full object-cover"
					/>
				</div>
				<div className="flex flex-col items-start">
					<div className="flex gap-0.5 items-center">
						<p
							className={cn(
								"font-semibold leading-tight overflow-hidden text-[#fafafa] text-[12px] truncate tracking-[-0.12px]",
								dmSansClassName(),
							)}
						>
							{user.name}
						</p>
						{isVerified && <VerifiedBadge />}
					</div>
					<p
						className={cn(
							"font-medium leading-tight overflow-hidden text-[#737373] text-[12px] truncate tracking-[-0.12px]",
							dmSansClassName(),
						)}
					>
						@{user.screen_name}
					</p>
				</div>
			</div>
			<div className="flex gap-1.5 items-center">
				<XLogo />
			</div>
		</div>
	)
}

function CustomTweetMedia({
	tweet,
}: {
	tweet: ReturnType<typeof enrichTweet>
}) {
	const media = tweet.mediaDetails?.[0]
	if (!media) return null

	const isVideo = media.type === "video" || media.type === "animated_gif"
	const previewUrl = media.media_url_https

	return (
		<div className="relative w-full overflow-hidden rounded-[6px] border border-[rgba(47,50,54,0.2)]">
			<div className="relative w-full aspect-video">
				<img
					src={previewUrl}
					alt="Tweet media"
					className="w-full h-full object-cover"
				/>
				{isVideo && (
					<div className="absolute inset-0 bg-[rgba(4,5,5,0.8)] flex items-center justify-center">
						<PlayCircle className="size-8 text-white" strokeWidth={1.5} />
					</div>
				)}
			</div>
		</div>
	)
}

export function TweetPreview({
	data,
	noBgColor,
}: {
	data: Tweet
	noBgColor?: boolean
}) {
	const parsedTweet = typeof data === "string" ? JSON.parse(data) : data
	const tweet = enrichTweet(parsedTweet)

	return (
		<div
			className={cn(
				"w-full min-w-0",
				noBgColor ? "bg-transparent" : "bg-black rounded-[18px] p-3",
				dmSansClassName(),
			)}
		>
			<Suspense fallback={<TweetSkeleton />}>
				<div className="flex flex-col gap-3 w-full">
					<CustomTweetHeader tweet={tweet} />
					<div className="sm-tweet-theme w-full min-w-0">
						<TweetBody tweet={tweet} />
					</div>
					<CustomTweetMedia tweet={tweet} />
				</div>
			</Suspense>
		</div>
	)
}