Thread viewer for Bluesky
1<script lang="ts">
2 import { getPostContext } from './PostComponent.svelte';
3 import { avatarPreloader } from '../../utils.js';
4 import { PostPresenter } from '../../utils/post_presenter.js';
5 import PostSubtreeLink from './PostSubtreeLink.svelte';
6
7 let { post, placement } = getPostContext();
8 let presenter = new PostPresenter(post, placement);
9
10 let avatar: HTMLImageElement | undefined = $state();
11
12 $effect(() => {
13 if (avatar) {
14 avatarPreloader.observe(avatar);
15 }
16
17 return () => {
18 avatar && avatarPreloader.unobserve(avatar);
19 };
20 });
21</script>
22
23<h2>
24 {#if post.muted}
25 <i class="muted-avatar fa-regular fa-circle-user fa-2x"></i>
26 {:else if post.author.avatar}
27 <img class="avatar" alt="Avatar" loading="lazy" src={post.author.avatar} bind:this={avatar}>
28 {:else}
29 <i class="no-avatar fa-regular fa-face-smile fa-2x"></i>
30 {/if}
31
32 {post.authorDisplayName}
33
34 {#if post.isFediPost}
35 <a class="handle" href="{post.linkToAuthor}" target="_blank">@{post.authorFediHandle}</a>
36 <img src="icons/mastodon.svg" class="mastodon" alt="Mastodon logo">
37 {:else}
38 <a class="handle" href="{post.linkToAuthor}" target="_blank">{post.hasValidHandle ? `@${post.author.handle}` : '[invalid handle]'}</a>
39 {/if}
40
41 <span class="separator">•</span>
42
43 <a class="time" href="{post.linkToPost}" target="_blank" title="{post.createdAt.toISOString()}">{presenter.formattedTimestamp}</a>
44
45 {#if (post.replyCount > 0 && !post.isPageRoot) || ['quote', 'quotes', 'feed'].includes(placement)}
46 <span class="separator">•</span>
47
48 {#if ['quote', 'quotes', 'feed'].includes(placement)}
49 <PostSubtreeLink {post} title="Load thread" />
50 {:else}
51 <PostSubtreeLink {post} title="Load this subtree" />
52 {/if}
53 {/if}
54</h2>
55
56<style>
57 h2 {
58 font-size: 12pt;
59 margin-bottom: 0;
60 }
61
62 .avatar {
63 width: 32px;
64 height: 32px;
65 border-radius: 16px;
66 vertical-align: middle;
67 margin-bottom: 3px;
68 margin-right: 4px;
69 }
70
71 .no-avatar, .muted-avatar {
72 color: #aaa;
73 background-color: #eee;
74 border-radius: 16px;
75 vertical-align: middle;
76 margin-right: 4px;
77 }
78
79 .muted-avatar {
80 color: #bbb;
81 }
82
83 .handle {
84 color: #888;
85 font-weight: normal;
86 font-size: 11pt;
87 vertical-align: text-top;
88 }
89
90 .mastodon {
91 width: 15px;
92 position: relative;
93 top: 2px;
94 margin-left: 3px;
95 }
96
97 .time {
98 color: #666;
99 font-weight: normal;
100 font-size: 10pt;
101 vertical-align: text-top;
102 }
103
104 @media (prefers-color-scheme: dark) {
105 .handle { color: #888; }
106 .separator { color: #888; }
107 .time { color: #aaa; }
108 h2 :global(.action) { color: #888; }
109 }
110</style>