appview-less bluesky client
24
fork

Configure Feed

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

at main 138 lines 3.8 kB view raw
1<script lang="ts"> 2 import MutedAccountItem from './MutedAccountItem.svelte'; 3 import VirtualList from '@tutorlatin/svelte-tiny-virtual-list'; 4 import type { ActorIdentifier, Did } from '@atcute/lexicons'; 5 import { allBacklinks, createBlock, deleteBlock, clients } from '$lib/state.svelte'; 6 import { blockSource } from '$lib'; 7 import { isActorIdentifier } from '@atcute/lexicons/syntax'; 8 import { resolveHandle } from '$lib/at/client.svelte'; 9 10 interface Props { 11 mutes: Did[]; 12 onAddMute: (did: Did) => void; 13 onRemoveMute: (did: Did) => void; 14 selectedAccount: Did | null; 15 } 16 17 let { mutes, onAddMute, onRemoveMute, selectedAccount }: Props = $props(); 18 19 let newMuteInput = $state(''); 20 let newBlockInput = $state(''); 21 22 const handleAddMute = async () => { 23 if (!newMuteInput.trim()) return; 24 const did = await resolveHandle(newMuteInput.trim() as ActorIdentifier); 25 if (!did.ok) return; 26 onAddMute(did.value); 27 newMuteInput = ''; 28 }; 29 30 const blocks = $derived.by(() => { 31 if (!selectedAccount) return []; 32 const blockMap = allBacklinks.get(blockSource); 33 if (!blockMap) return []; 34 const blockedDids: Did[] = []; 35 for (const [subjectUri, didMap] of blockMap) { 36 if (didMap.has(selectedAccount)) { 37 const did = subjectUri.replace('at://', '') as Did; 38 blockedDids.push(did); 39 } 40 } 41 return blockedDids; 42 }); 43 44 const handleAddBlock = async () => { 45 if (!newBlockInput.trim() || !selectedAccount) return; 46 const client = clients.get(selectedAccount); 47 if (!client) return; 48 const did = await resolveHandle(newBlockInput.trim() as ActorIdentifier); 49 if (!did.ok) return; 50 await createBlock(client, did.value); 51 newBlockInput = ''; 52 }; 53 54 const handleRemoveBlock = async (did: Did) => { 55 if (!selectedAccount) return; 56 const client = clients.get(selectedAccount); 57 if (!client) return; 58 await deleteBlock(client, did); 59 }; 60</script> 61 62<div class="space-y-4 p-4"> 63 <div> 64 <h3 class="settings-header">muted accounts</h3> 65 <div class="settings-box space-y-2"> 66 <div class="flex gap-2"> 67 <input 68 type="text" 69 bind:value={newMuteInput} 70 placeholder="enter identifier" 71 class="single-line-input flex-1" 72 /> 73 <button 74 disabled={!isActorIdentifier(newMuteInput)} 75 onclick={handleAddMute} 76 class="action-button">add</button 77 > 78 </div> 79 {#if mutes.length > 0} 80 <div class="h-fit"> 81 <VirtualList 82 height={Math.min(mutes.length, 6) * 44} 83 itemCount={mutes.length} 84 itemSize={44} 85 > 86 {#snippet item({ index, style }: { index: number; style: string })} 87 <MutedAccountItem 88 {style} 89 did={mutes[index]} 90 onRemove={() => onRemoveMute(mutes[index])} 91 /> 92 {/snippet} 93 </VirtualList> 94 </div> 95 {:else} 96 <p class="py-2 text-center text-sm opacity-50">no muted accounts</p> 97 {/if} 98 </div> 99 </div> 100 101 <div> 102 <h3 class="settings-header">blocked accounts</h3> 103 <div class="settings-box space-y-2"> 104 <div class="flex gap-2"> 105 <input 106 type="text" 107 bind:value={newBlockInput} 108 placeholder="enter identifier" 109 class="single-line-input flex-1" 110 /> 111 <button 112 disabled={!isActorIdentifier(newBlockInput)} 113 onclick={handleAddBlock} 114 class="action-button">add</button 115 > 116 </div> 117 {#if blocks.length > 0} 118 <div class="h-fit"> 119 <VirtualList 120 height={Math.min(blocks.length, 6) * 44} 121 itemCount={blocks.length} 122 itemSize={44} 123 > 124 {#snippet item({ index, style }: { index: number; style: string })} 125 <MutedAccountItem 126 {style} 127 did={blocks[index]} 128 onRemove={() => handleRemoveBlock(blocks[index])} 129 /> 130 {/snippet} 131 </VirtualList> 132 </div> 133 {:else} 134 <p class="py-2 text-center text-sm opacity-50">no blocked accounts</p> 135 {/if} 136 </div> 137 </div> 138</div>