Coves frontend - a photon fork
1<script lang="ts">
2 import { coves } from '$lib/api/client.svelte'
3 import type { CommunityView } from '$lib/api/coves/types'
4 import { MenuButton, Search, toast } from 'mono-svelte'
5 import { Icon, XCircle } from 'svelte-hero-icons/dist'
6 import { fly } from 'svelte/transition'
7 import Avatar from '../generic/Avatar.svelte'
8
9 interface Props {
10 q?: string
11 showWhenEmpty?: boolean
12 placeholder?: string
13 onselect?: (item: CommunityView | undefined) => void
14 label?: string
15 required?: boolean
16 }
17
18 let {
19 q = $bindable(''),
20 showWhenEmpty = false,
21 required,
22 onselect,
23 ...rest
24 }: Props = $props()
25
26 let searchError = $state(false)
27</script>
28
29<Search
30 search={async (q) => {
31 searchError = false
32 try {
33 const api = coves()
34
35 if (q.trim()) {
36 const results = await api.searchCommunities({
37 query: q,
38 limit: 20,
39 })
40 return results.communities
41 } else {
42 const results = await api.listCommunities({
43 limit: 20,
44 })
45 return results.communities
46 }
47 } catch (err) {
48 console.error('[ObjectAutocomplete] search failed:', err)
49 searchError = true
50 toast({
51 content: 'Failed to search communities',
52 type: 'error',
53 })
54 return []
55 }
56 }}
57 extractName={(c) => c.name}
58 bind:query={q}
59 {required}
60 {...rest}
61>
62 {#snippet noresults()}
63 <div class="w-full h-full">
64 {#if q == '' && showWhenEmpty}
65 <MenuButton onclick={() => onselect?.(undefined)}>
66 <Icon src={XCircle} size="16" mini />
67 <div class="flex flex-col text-left">
68 <span>None</span>
69 </div>
70 </MenuButton>
71 {:else if searchError}
72 <span class="mx-auto my-auto text-red-500 dark:text-red-400">
73 Search failed.
74 </span>
75 {:else}
76 <span class="mx-auto my-auto">No results.</span>
77 {/if}
78 </div>
79 {/snippet}
80 {#snippet children({ item, select })}
81 <div in:fly|global={{ y: -4, opacity: 0 }}>
82 <MenuButton
83 onclick={() => {
84 select(item)
85 onselect?.(item)
86 }}
87 >
88 <Avatar url={item.avatar} alt={item.name} width={24} />
89 <div class="flex flex-col text-left">
90 <span>{item.displayName ?? item.name}</span>
91 <span class="text-xs opacity-80">
92 {item.handle ?? item.did}
93 </span>
94 </div>
95 </MenuButton>
96 </div>
97 {/snippet}
98</Search>