Thread viewer for Bluesky
1<script lang="ts">
2 import { getPostContext } from '../posts/PostComponent.svelte';
3 import { isValidURL, truncateText } from '../../utils.js';
4 import GIFPlayer from './GIFPlayer.svelte';
5 import { InlineLinkEmbed, RawLinkEmbed } from '../../models/embeds.js';
6
7 let { embed }: { embed: InlineLinkEmbed | RawLinkEmbed } = $props();
8 let { post } = getPostContext();
9
10 let showingGIF = $state(false);
11
12 let hostname = $derived(new URL(embed.url).hostname);
13 let isTenorGIF = $derived(hostname == 'media.tenor.com');
14 let onclick = $derived(isTenorGIF ? playGIF : undefined);
15
16 function playGIF(e: Event) {
17 e.preventDefault();
18 showingGIF = true;
19 }
20
21 function thumbnailURL() {
22 if (typeof embed.thumb == 'string') {
23 return embed.thumb;
24 } else {
25 return `https://cdn.bsky.app/img/avatar/feed_thumbnail/${post.author.did}/${embed.thumb.ref.$link}@jpeg`;
26 }
27 }
28</script>
29
30{#if showingGIF}
31 <GIFPlayer gifURL={embed.url} staticURL={thumbnailURL()} alt={embed.title} />
32{:else}
33 {#if isValidURL(embed.url)}
34 <a class="link-card" href={embed.url} target="_blank" {onclick}>
35 <div>
36 <p class="domain">{hostname}</p>
37 <h2>{embed.title || embed.url}</h2>
38
39 {#if embed.description}
40 <p class="description">{truncateText(embed.description, 300)}</p>
41 {/if}
42 </div>
43 </a>
44 {:else}
45 <p>
46 [Link: {embed.url}]
47 </p>
48 {/if}
49{/if}