Thread viewer for Bluesky
1<script lang="ts">
2 import { api } from '../api.js';
3 import { Post } from '../models/posts.js';
4 import * as paginator from '../utils/paginator.js';
5 import MainLoader from '../components/MainLoader.svelte';
6 import PostComponent from '../components/posts/PostComponent.svelte';
7
8 let { hashtag }: { hashtag: string } = $props();
9 hashtag = hashtag.replace(/^\#/, '');
10
11 let posts: Post[] = $state([]);
12 let firstPageLoaded = $state(false);
13 let loadingFailed = $state(false);
14
15 let isLoading = false;
16 let finished = false;
17 let cursor: string | undefined;
18
19 paginator.loadInPages(async () => {
20 if (isLoading || finished) { return }
21 isLoading = true;
22
23 try {
24 let data = await api.getHashtagFeed(hashtag, cursor);
25 let batch = data.posts.map((j: json) => new Post(j)) as Post[];
26 firstPageLoaded = true;
27
28 posts.push(...batch);
29
30 isLoading = false;
31 cursor = data.cursor;
32
33 if (!cursor || posts.length == 0) {
34 finished = true;
35 }
36 } catch(error) {
37 console.log(error);
38 isLoading = false;
39 loadingFailed = true;
40 }
41 });
42</script>
43
44<svelte:head>
45 <title>#{hashtag} - Skythread</title>
46</svelte:head>
47
48{#if firstPageLoaded}
49 <main class="hashtag">
50 <header>
51 <h2>
52 {#if posts.length > 0}
53 Posts tagged: #{hashtag}
54 {:else}
55 No posts tagged #{hashtag}.
56 {/if}
57 </h2>
58 </header>
59
60 {#each posts as post (post.uri)}
61 <PostComponent {post} placement="feed" />
62 {/each}
63 </main>
64{:else if !loadingFailed}
65 <MainLoader />
66{/if}
67
68<style>
69 .hashtag > :global(.post) {
70 padding-bottom: 10px;
71 border-bottom: 1px solid #ddd;
72 }
73</style>