appview-less bluesky client
24
fork

Configure Feed

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

at main 70 lines 2.2 kB view raw
1<script lang="ts"> 2 import FeedItem from './FeedItem.svelte'; 3 import VirtualList from '@tutorlatin/svelte-tiny-virtual-list'; 4 import type { FeedGenerator } from '$lib/at/feeds'; 5 import { fetchFeedGenerator, parseFeedUri } from '$lib/at/feeds'; 6 import type { SavedFeed } from '$lib/settings'; 7 import { viewClient } from '$lib/state.svelte'; 8 9 interface Props { 10 feeds: SavedFeed[]; 11 onAddFeed: (feed: FeedGenerator) => void; 12 onRemoveFeed: (uri: string) => void; 13 onTogglePin: (uri: string) => void; 14 } 15 16 let { feeds, onAddFeed, onRemoveFeed, onTogglePin }: Props = $props(); 17 18 let newFeedInput = $state(''); 19 20 const handleAddFeed = async () => { 21 const uri = newFeedInput.trim(); 22 if (!uri) return; 23 if (!parseFeedUri(uri)) return; 24 if (feeds.some((f) => f.feed.uri === uri)) return; 25 const feed = await fetchFeedGenerator(viewClient, uri); 26 if (!feed) return; 27 onAddFeed(feed); 28 newFeedInput = ''; 29 }; 30 31 const sortedFeeds = $derived([...feeds].sort((a, b) => (b.pinned ? 1 : 0) - (a.pinned ? 1 : 0))); 32 const isValidUri = $derived(parseFeedUri(newFeedInput.trim()) !== null); 33</script> 34 35<div class="space-y-4 p-4"> 36 <div> 37 <h3 class="settings-header">saved feeds</h3> 38 <div class="settings-box space-y-2"> 39 <div class="flex gap-2"> 40 <input 41 type="text" 42 bind:value={newFeedInput} 43 placeholder="https://bsky.app/profile/bsky.app/feed/whats-hot" 44 class="single-line-input flex-1" 45 /> 46 <button disabled={!isValidUri} onclick={handleAddFeed} class="action-button">add</button> 47 </div> 48 {#if sortedFeeds.length > 0} 49 <div class="h-fit"> 50 <VirtualList 51 height={Math.min(sortedFeeds.length, 7) * 44} 52 itemCount={sortedFeeds.length} 53 itemSize={44} 54 > 55 {#snippet item({ index, style }: { index: number; style: string })} 56 <FeedItem 57 {style} 58 data={sortedFeeds[index]} 59 onRemove={() => onRemoveFeed(sortedFeeds[index].feed.uri)} 60 onTogglePin={() => onTogglePin(sortedFeeds[index].feed.uri)} 61 /> 62 {/snippet} 63 </VirtualList> 64 </div> 65 {:else} 66 <p class="py-2 text-center text-sm opacity-50">no saved feeds</p> 67 {/if} 68 </div> 69 </div> 70</div>