your personal website on atproto - mirror blento.app
25
fork

Configure Feed

Select the types of activity you want to include in your feed.

at pages 126 lines 3.1 kB view raw
1<script lang="ts"> 2 import { onMount } from 'svelte'; 3 import { getAdditionalUserData, getDidContext, getHandleContext } from '$lib/website/context'; 4 import { CardDefinitionsByType } from '../..'; 5 import type { ContentComponentProps } from '../../types'; 6 import { Button } from '@foxui/core'; 7 import { BlueskyPost } from '$lib/components/bluesky-post'; 8 import type { PostView } from '@atcute/bluesky/types/app/feed/defs'; 9 10 let { item }: ContentComponentProps = $props(); 11 12 const data = getAdditionalUserData(); 13 const did = getDidContext(); 14 const handle = getHandleContext(); 15 16 type Reply = { 17 $type: string; 18 post: PostView; 19 }; 20 21 let isLoaded = $state(false); 22 23 let cardUri = $derived(item.cardData.uri as string); 24 25 // svelte-ignore state_referenced_locally 26 let replies = $state<Reply[]>( 27 ((data['guestbook'] as Record<string, Reply[]>)?.[item.cardData.uri as string] ?? []) as Reply[] 28 ); 29 30 onMount(async () => { 31 if (!cardUri) { 32 isLoaded = true; 33 return; 34 } 35 36 try { 37 const loaded = await CardDefinitionsByType[item.cardType]?.loadData?.([item], { 38 did, 39 handle 40 }); 41 const result = loaded as Record<string, Reply[]> | undefined; 42 const freshReplies = result?.[cardUri] ?? []; 43 44 if (freshReplies.length > 0) { 45 replies = freshReplies; 46 } 47 48 if (!data['guestbook']) { 49 data['guestbook'] = {}; 50 } 51 (data['guestbook'] as Record<string, Reply[]>)[cardUri] = replies; 52 } catch (e) { 53 console.error('Failed to load guestbook replies', e); 54 } 55 56 isLoaded = true; 57 }); 58</script> 59 60<div class="flex h-full flex-col overflow-hidden p-4"> 61 {#if item.cardData.href} 62 <div class="mb-2 flex justify-end"> 63 <a href={item.cardData.href} target="_blank" rel="noopener noreferrer"> 64 <Button size="sm">Add a comment on Bluesky</Button> 65 </a> 66 </div> 67 {/if} 68 69 <div class="flex-1 overflow-y-auto"> 70 {#if replies.length > 0} 71 <div class="replies"> 72 {#each replies as reply (reply.post.uri)} 73 <div class="reply"> 74 <BlueskyPost feedViewPost={reply.post} showAvatar compact showLogo={false} /> 75 </div> 76 {/each} 77 </div> 78 {:else if isLoaded} 79 <div 80 class="text-base-500 dark:text-base-400 accent:text-white/60 flex h-full items-center justify-center text-center text-sm" 81 > 82 No comments yet — share your Bluesky post to get started! 83 </div> 84 {:else} 85 <div 86 class="text-base-500 dark:text-base-400 accent:text-white/60 flex h-full items-center justify-center text-center text-sm" 87 > 88 Loading comments... 89 </div> 90 {/if} 91 </div> 92</div> 93 94<style> 95 .reply { 96 padding-bottom: 1rem; 97 margin-bottom: 1rem; 98 border-bottom: 1px solid oklch(0.5 0 0 / 0.1); 99 } 100 101 .reply:last-child { 102 border-bottom: none; 103 margin-bottom: 0; 104 padding-bottom: 0; 105 } 106 107 .reply :global(img:not([class*='rounded-full'])) { 108 max-height: 10rem; 109 } 110 111 .reply :global(article) { 112 max-height: 10rem; 113 } 114 115 @container card (width >= 30rem) { 116 .replies { 117 columns: 2; 118 column-gap: 1.5rem; 119 column-rule: 1px solid oklch(0.5 0 0 / 0.15); 120 } 121 122 .reply { 123 break-inside: avoid; 124 } 125 } 126</style>