Thread viewer for Bluesky
at 2.0 110 lines 2.7 kB view raw
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">&bull;</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">&bull;</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>