Thread viewer for Bluesky
at 2.0 158 lines 3.6 kB view raw
1<script lang="ts"> 2 import { accountAPI } from '../../api.js'; 3 import { getPostContext } from './PostComponent.svelte'; 4 import { linkToPostThread, linkToQuotesPage } from '../../router.js'; 5 import { account } from '../../models/account.svelte.js'; 6 import { showLoginDialog } from '../Dialogs.svelte'; 7 import { showError, pluralize } from '../../utils.js'; 8 9 let { post, placement } = getPostContext(); 10 let { quoteCount }: { quoteCount: number | undefined } = $props(); 11 12 let isLiked = $state(post.liked); 13 let likeCount = $state(post.likeCount); 14 let isUnavailableForLiking = $state(false); 15 16 async function onHeartClick() { 17 try { 18 if (post.hasViewerInfo) { 19 await likePost(); 20 } else if (account.loggedIn) { 21 await checkIfCanBeLiked(); 22 } else { 23 showLoginDialog({ showClose: true }); 24 } 25 } catch (error) { 26 showError(error); 27 } 28 } 29 30 async function checkIfCanBeLiked() { 31 let data = await accountAPI.loadPostViewerInfo(post); 32 33 if (data) { 34 if (post.liked) { 35 isLiked = true; 36 } else { 37 await likePost(); 38 } 39 } else { 40 isUnavailableForLiking = true; 41 } 42 } 43 44 async function likePost() { 45 if (!isLiked) { 46 let like = await accountAPI.likePost(post); 47 post.viewerLike = like.uri; 48 49 isLiked = true; 50 likeCount += 1; 51 } else { 52 await accountAPI.removeLike(post.viewerLike); 53 post.viewerLike = undefined; 54 55 isLiked = false; 56 likeCount -= 1; 57 } 58 } 59</script> 60 61<p class="stats"> 62 <span> 63 <i class="fa-solid fa-heart {isLiked ? 'liked' : ''}" onclick={onHeartClick}></i> <output>{likeCount}</output> 64 </span> 65 66 {#if post.repostCount > 0} 67 <span><i class="fa-solid fa-retweet"></i> {post.repostCount}</span> 68 {/if} 69 70 {#if post.replyCount > 0 && (placement == 'quotes' || placement == 'feed')} 71 <span> 72 <i class="fa-regular fa-message"></i> 73 <a href="{linkToPostThread(post)}">{pluralize(post.replyCount, 'reply', 'replies')}</a> 74 </span> 75 {/if} 76 77 {#if quoteCount && placement != 'quote'} 78 {#if placement == 'quotes' || placement == 'feed' || post.isPageRoot} 79 <span> 80 <i class="fa-regular fa-comments"></i> 81 <a href={linkToQuotesPage(post.linkToPost)}>{pluralize(quoteCount, 'quote')}</a> 82 </span> 83 {:else} 84 <a href={linkToQuotesPage(post.linkToPost)}> 85 <i class="fa-regular fa-comments"></i> {quoteCount} 86 </a> 87 {/if} 88 {/if} 89 90 {#if post.isRestrictingReplies} 91 {#if placement == 'thread'} 92 <span><i class="fa-solid fa-ban"></i> Limited replies</span> 93 {:else if placement == 'quotes'} 94 <span><i class="fa-solid fa-ban" title="Limited replies"></i></span> 95 {/if} 96 {/if} 97 98 {#if isUnavailableForLiking} 99 <span class="blocked-info">🚫 Post unavailable</span> 100 {/if} 101</p> 102 103<style> 104 .stats { 105 font-size: 10pt; 106 color: #666; 107 } 108 109 a { 110 color: #666; 111 text-decoration: none; 112 } 113 114 a:hover { 115 text-decoration: underline; 116 } 117 118 i { 119 font-size: 9pt; 120 color: #888; 121 } 122 123 i.fa-heart { 124 color: #aaa; 125 } 126 127 i.fa-heart.liked { 128 color: #e03030; 129 } 130 131 i.fa-heart:hover { 132 color: #888; 133 cursor: pointer; 134 } 135 136 i.fa-heart.liked:hover { 137 color: #c02020; 138 } 139 140 span { 141 margin-right: 7px; 142 } 143 144 .blocked-info { 145 color: #a02020; 146 font-weight: bold; 147 margin-left: 5px; 148 } 149 150 @media (prefers-color-scheme: dark) { 151 .stats { color: #aaa; } 152 i { color: #888; } 153 i.fa-heart { color: #aaa; } 154 i.fa-heart.liked { color: #f04040; } 155 i.fa-heart:hover { color: #eee; } 156 i.fa-heart.liked:hover { color: #ff7070; } 157 } 158</style>