your personal website on atproto - mirror
blento.app
1<script lang="ts">
2 import Embed from './embeds/Embed.svelte';
3 import { cn, Prose } from '@foxui/core';
4 import type { WithChildren, WithElementRef } from 'bits-ui';
5 import type { HTMLAttributes } from 'svelte/elements';
6 import type { PostData } from '.';
7 import PostAction from './PostAction.svelte';
8 import type { Snippet } from 'svelte';
9 import { numberToHumanReadable } from '..';
10 import { RelativeTime } from '@foxui/time';
11
12 let {
13 ref = $bindable(),
14 data,
15 class: className,
16 bookmarked = $bindable(false),
17 liked = $bindable(false),
18
19 showReply = $bindable(true),
20 showRepost = $bindable(true),
21 showLike = $bindable(true),
22 showBookmark = $bindable(true),
23
24 onReplyClick,
25 onRepostClick,
26 onLikeClick,
27 onBookmarkClick,
28
29 replyHref,
30 repostHref,
31 likeHref,
32
33 customActions,
34
35 children,
36
37 logo
38 }: WithElementRef<WithChildren<HTMLAttributes<HTMLDivElement>>> & {
39 data: PostData;
40 class?: string;
41
42 bookmarked?: boolean;
43 liked?: boolean;
44
45 showReply?: boolean;
46 showRepost?: boolean;
47 showLike?: boolean;
48 showBookmark?: boolean;
49
50 onReplyClick?: () => void;
51 onRepostClick?: () => void;
52 onLikeClick?: () => void;
53 onBookmarkClick?: () => void;
54
55 replyHref?: string;
56 repostHref?: string;
57 likeHref?: string;
58
59 customActions?: Snippet;
60
61 logo?: Snippet;
62 } = $props();
63</script>
64
65<div
66 bind:this={ref}
67 class={cn('text-base-950 dark:text-base-50 transition-colors duration-200', className)}
68>
69 {#if data.reposted}
70 <div class="mb-3 inline-flex items-center gap-2 text-xs">
71 <svg
72 xmlns="http://www.w3.org/2000/svg"
73 viewBox="0 0 24 24"
74 fill="currentColor"
75 class="size-3"
76 >
77 <path
78 fill-rule="evenodd"
79 d="M4.755 10.059a7.5 7.5 0 0 1 12.548-3.364l1.903 1.903h-3.183a.75.75 0 1 0 0 1.5h4.992a.75.75 0 0 0 .75-.75V4.356a.75.75 0 0 0-1.5 0v3.18l-1.9-1.9A9 9 0 0 0 3.306 9.67a.75.75 0 1 0 1.45.388Zm15.408 3.352a.75.75 0 0 0-.919.53 7.5 7.5 0 0 1-12.548 3.364l-1.902-1.903h3.183a.75.75 0 0 0 0-1.5H2.984a.75.75 0 0 0-.75.75v4.992a.75.75 0 0 0 1.5 0v-3.18l1.9 1.9a9 9 0 0 0 15.059-4.035.75.75 0 0 0-.53-.918Z"
80 clip-rule="evenodd"
81 />
82 </svg>
83
84 <div class="inline-flex gap-1">
85 reposted by
86 <a
87 href={data.reposted.href}
88 class="hover:text-accent-600 dark:hover:text-accent-400 font-bold"
89 >
90 @{data.reposted.handle}
91 </a>
92 </div>
93 </div>
94 {/if}
95 {#if data.replyTo}
96 <div class="mb-3 inline-flex items-center gap-2 text-xs">
97 <svg
98 xmlns="http://www.w3.org/2000/svg"
99 viewBox="0 0 24 24"
100 fill="currentColor"
101 class="size-3"
102 >
103 <path
104 fill-rule="evenodd"
105 d="M14.47 2.47a.75.75 0 0 1 1.06 0l6 6a.75.75 0 0 1 0 1.06l-6 6a.75.75 0 1 1-1.06-1.06l4.72-4.72H9a5.25 5.25 0 1 0 0 10.5h3a.75.75 0 0 1 0 1.5H9a6.75 6.75 0 0 1 0-13.5h10.19l-4.72-4.72a.75.75 0 0 1 0-1.06Z"
106 clip-rule="evenodd"
107 />
108 </svg>
109
110 <div class="inline-flex gap-1">
111 replying to
112 <a
113 href={data.replyTo.href}
114 class="hover:text-accent-600 dark:hover:text-accent-400 font-bold"
115 >
116 @{data.replyTo.handle}
117 </a>
118 </div>
119 </div>
120 {/if}
121 <div class="flex gap-4">
122 <div class="w-full">
123 <div class="mb-1 flex items-start justify-between gap-2">
124 <div class="flex items-start gap-4">
125 {#if data.author.href}
126 <a
127 class="hover:bg-accent-900/5 accent:hover:bg-accent-100/10 group/post-author -mx-2 -my-0.5 flex flex-col items-baseline gap-x-2 gap-y-0.5 rounded-xl px-2 py-0.5 sm:flex-row"
128 href={data.author.href}
129 >
130 {#if data.author.displayName}
131 <div
132 class="text-base-900 group-hover/post-author:text-accent-600 dark:text-base-50 dark:group-hover/post-author:text-accent-300 accent:group-hover/post-author:text-accent-950 line-clamp-1 text-sm leading-tight font-semibold"
133 >
134 {data.author.displayName}
135 </div>
136 {/if}
137 <div
138 class={cn(
139 'group-hover/post-author:text-accent-600 dark:group-hover/post-author:text-accent-400 accent:text-accent-950 accent:group-hover/post-author:text-accent-900 text-sm',
140 !data.author.displayName
141 ? 'text-base-900 dark:text-base-50 font-semibold'
142 : 'text-base-600 dark:text-base-400'
143 )}
144 >
145 @{data.author.handle}
146 </div>
147 </a>
148 {:else}
149 <div
150 class="-mx-2 -my-0.5 flex flex-col items-baseline gap-x-2 gap-y-0.5 rounded-xl px-2 py-0.5 sm:flex-row"
151 >
152 <div class="text-base-900 dark:text-base-50 text-sm leading-tight font-semibold">
153 {data.author.displayName}
154 </div>
155 <div class="text-base-600 dark:text-base-400 accent:text-accent-950 text-sm">
156 @{data.author.handle}
157 </div>
158 </div>
159 {/if}
160
161 <div
162 class="text-base-600 dark:text-base-400 accent:text-accent-950 block text-sm no-underline"
163 >
164 <RelativeTime date={new Date(data.createdAt)} locale="en" />
165 </div>
166 </div>
167
168 {#if logo}
169 {@render logo?.()}
170 {/if}
171 </div>
172
173 <Prose
174 size="md"
175 class="accent:prose-a:text-accent-950 accent:text-base-900 accent:prose-p:text-base-900 accent:prose-a:underline"
176 >
177 {#if data.htmlContent}
178 {@html data.htmlContent}
179 {:else}
180 {@render children?.()}
181 {/if}
182 </Prose>
183
184 {#if data.embed}
185 <Embed embed={data.embed} />
186 {/if}
187
188 {#if showReply || showRepost || showLike || showBookmark || customActions}
189 <div
190 class="text-base-500 dark:text-base-400 accent:text-base-900 mt-4 flex justify-between gap-2"
191 >
192 {#if showReply}
193 <PostAction onclick={onReplyClick} href={replyHref}>
194 <svg
195 xmlns="http://www.w3.org/2000/svg"
196 fill="none"
197 viewBox="0 0 24 24"
198 stroke-width="1.5"
199 stroke="currentColor"
200 class="group-hover/post-action:bg-accent-500/10 group-hover/post-action:text-accent-700 dark:group-hover/post-action:text-accent-400 -m-1.5 size-7 rounded-full p-1.5 transition-all duration-100"
201 >
202 <path
203 stroke-linecap="round"
204 stroke-linejoin="round"
205 d="M12 20.25c4.97 0 9-3.694 9-8.25s-4.03-8.25-9-8.25S3 7.444 3 12c0 2.104.859 4.023 2.273 5.48.432.447.74 1.04.586 1.641a4.483 4.483 0 0 1-.923 1.785A5.969 5.969 0 0 0 6 21c1.282 0 2.47-.402 3.445-1.087.81.22 1.668.337 2.555.337Z"
206 />
207 </svg>
208 {#if data.replyCount}
209 {numberToHumanReadable(data.replyCount)}
210 {/if}
211 </PostAction>
212 {/if}
213
214 {#if showRepost}
215 <PostAction onclick={onRepostClick} href={repostHref}>
216 <svg
217 xmlns="http://www.w3.org/2000/svg"
218 fill="none"
219 viewBox="0 0 24 24"
220 stroke-width="1.5"
221 stroke="currentColor"
222 class="group-hover/post-action:bg-accent-500/10 group-hover/post-action:text-accent-700 dark:group-hover/post-action:text-accent-400 -m-1.5 size-7 rounded-full p-1.5 transition-all duration-100"
223 >
224 <path
225 stroke-linecap="round"
226 stroke-linejoin="round"
227 d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0 3.181 3.183a8.25 8.25 0 0 0 13.803-3.7M4.031 9.865a8.25 8.25 0 0 1 13.803-3.7l3.181 3.182m0-4.991v4.99"
228 />
229 </svg>
230 {#if data.repostCount}
231 {numberToHumanReadable(data.repostCount)}
232 {/if}
233 </PostAction>
234 {/if}
235 {#if showLike}
236 <PostAction
237 class={liked ? 'text-accent-700 dark:text-accent-500 font-semibold' : ''}
238 onclick={onLikeClick}
239 href={likeHref}
240 >
241 {#if liked}
242 <svg
243 xmlns="http://www.w3.org/2000/svg"
244 viewBox="0 0 24 24"
245 fill="currentColor"
246 class="group-hover/post-action:bg-accent-500/10 text-accent-700 dark:text-accent-500 -m-1.5 size-7 rounded-full p-1.5 transition-all duration-100"
247 >
248 <path
249 d="m11.645 20.91-.007-.003-.022-.012a15.247 15.247 0 0 1-.383-.218 25.18 25.18 0 0 1-4.244-3.17C4.688 15.36 2.25 12.174 2.25 8.25 2.25 5.322 4.714 3 7.688 3A5.5 5.5 0 0 1 12 5.052 5.5 5.5 0 0 1 16.313 3c2.973 0 5.437 2.322 5.437 5.25 0 3.925-2.438 7.111-4.739 9.256a25.175 25.175 0 0 1-4.244 3.17 15.247 15.247 0 0 1-.383.219l-.022.012-.007.004-.003.001a.752.752 0 0 1-.704 0l-.003-.001Z"
250 />
251 </svg>
252 {:else}
253 <svg
254 xmlns="http://www.w3.org/2000/svg"
255 fill="none"
256 viewBox="0 0 24 24"
257 stroke-width="1.5"
258 stroke="currentColor"
259 class="group-hover/post-action:bg-accent-500/10 group-hover/post-action:text-accent-700 dark:group-hover/post-action:text-accent-400 -m-1.5 size-7 rounded-full p-1.5 transition-all duration-100"
260 >
261 <path
262 stroke-linecap="round"
263 stroke-linejoin="round"
264 d="M21 8.25c0-2.485-2.099-4.5-4.688-4.5-1.935 0-3.597 1.126-4.312 2.733-.715-1.607-2.377-2.733-4.313-2.733C5.1 3.75 3 5.765 3 8.25c0 7.22 9 12 9 12s9-4.78 9-12Z"
265 />
266 </svg>
267 {/if}
268 {#if data.likeCount}
269 {numberToHumanReadable(data.likeCount)}
270 {/if}
271 </PostAction>
272 {/if}
273
274 {#if showBookmark}
275 <PostAction onclick={onBookmarkClick}>
276 <span class="sr-only">Bookmark</span>
277
278 {#if bookmarked}
279 <svg
280 xmlns="http://www.w3.org/2000/svg"
281 viewBox="0 0 24 24"
282 fill="currentColor"
283 class="group-hover/post-action:bg-accent-500/10 text-accent-700 dark:text-accent-400 -m-1.5 size-7 rounded-full p-1.5 transition-all duration-100"
284 >
285 <path
286 fill-rule="evenodd"
287 d="M6.32 2.577a49.255 49.255 0 0 1 11.36 0c1.497.174 2.57 1.46 2.57 2.93V21a.75.75 0 0 1-1.085.67L12 18.089l-7.165 3.583A.75.75 0 0 1 3.75 21V5.507c0-1.47 1.073-2.756 2.57-2.93Z"
288 clip-rule="evenodd"
289 />
290 </svg>
291 {:else}
292 <svg
293 xmlns="http://www.w3.org/2000/svg"
294 fill="none"
295 viewBox="0 0 24 24"
296 stroke-width="1.5"
297 stroke="currentColor"
298 class="group-hover/post-action:bg-accent-500/10 group-hover/post-action:text-accent-700 dark:group-hover/post-action:text-accent-400 -m-1.5 size-7 rounded-full p-1.5 transition-all duration-100"
299 >
300 <path
301 stroke-linecap="round"
302 stroke-linejoin="round"
303 d="M17.593 3.322c1.1.128 1.907 1.077 1.907 2.185V21L12 17.25 4.5 21V5.507c0-1.108.806-2.057 1.907-2.185a48.507 48.507 0 0 1 11.186 0Z"
304 />
305 </svg>
306 {/if}
307 </PostAction>
308 {/if}
309
310 {@render customActions?.()}
311 </div>
312 {/if}
313 </div>
314 </div>
315</div>