Tweak search relay presentation and use

This commit is contained in:
Jon Staab 2026-09-07 15:05:57 -07:00
parent 7b145c525d
commit f81453c612
3 changed files with 71 additions and 48 deletions

View file

@ -14,7 +14,7 @@
import RelayName from "@app/components/RelayName.svelte"
import SearchBody from "@app/components/SearchBody.svelte"
import {CONTENT_KINDS} from "@app/content"
import {app, relays, searchRelayLists, user} from "@app/core"
import {app, relays, searchRelayLists, user, userSearchRelayUrls} from "@app/core"
import {pushModal} from "@app/modal"
import {userSpaceUrls} from "@app/rooms"
@ -38,12 +38,23 @@
<ModalHeader>
<ModalTitle>Search</ModalTitle>
<ModalSubtitle>across all your spaces</ModalSubtitle>
<Button class="button button-link button-sm self-center" onclick={showRelays}>
<Icon size={4} icon={Server} />
Search relays
</Button>
</ModalHeader>
<SearchBody placeholder="Search your spaces..." relays={$userSpaceUrls} {filter}>
{#snippet empty()}
{@const spaces = $userSpaceUrls.length}
{@const extras = $userSearchRelayUrls.length}
<div class="flex flex-col items-center gap-2 py-12 text-center">
<p class="text-content-muted text-sm">
Searching {spaces}
{spaces === 1 ? "space" : "spaces"}{#if extras > 0}, plus {extras}
extra {extras === 1 ? "relay" : "relays"} for people{/if}.
</p>
<Button class="button button-link button-sm" onclick={showRelays}>
<Icon size={4} icon={Server} />
Manage search relays
</Button>
</div>
{/snippet}
{#snippet badges(event)}
{@const url = getSpaceUrl(event)}
{#if url}

View file

@ -27,9 +27,10 @@
url?: string
members?: string[]
badges?: Snippet<[TrustedEvent]>
empty?: Snippet
}
const {placeholder, relays, filter, url, members, badges}: Props = $props()
const {placeholder, relays, filter, url, members, badges, empty}: Props = $props()
const profileSearch = $profiles.profileSearch
@ -126,42 +127,46 @@
{placeholder}
oninput={onInput} />
</label>
{#if people.length > 0}
<p class="text-xs uppercase tracking-wide opacity-60">People</p>
{#each people as pubkey (pubkey)}
<PeopleItem {pubkey} {url} />
{/each}
{/if}
{#if loading}
<Spinner {loading}>Searching...</Spinner>
{:else if term && results.length === 0 && people.length === 0}
<Spinner {loading}>No results found.</Spinner>
{:else}
{#each eventsByAge as [key, events] (key)}
<p class="text-xs uppercase tracking-wide opacity-60">
{#if key === "day"}
Last 24 Hours
{:else if key === "week"}
Last 7 Days
{:else}
Older
{/if}
</p>
{#each events as event (event.id)}
<Button
class="card card-sm card-interactive flex flex-col gap-2"
onclick={() => onResultClick(event)}>
<NoteCard minimal {event} {url}>
<NoteContentMinimal {event} />
</NoteCard>
<div class="flex gap-2">
<Badge variant="neutral">
{formatTimestampRelative(event.created_at)}
</Badge>
{@render badges?.(event)}
</div>
</Button>
{#if term}
{#if people.length > 0}
<p class="text-xs uppercase tracking-wide opacity-60">People</p>
{#each people as pubkey (pubkey)}
<PeopleItem {pubkey} {url} />
{/each}
{/each}
{/if}
{#if loading}
<Spinner {loading}>Searching...</Spinner>
{:else if results.length === 0 && people.length === 0}
<Spinner {loading}>No results found.</Spinner>
{:else}
{#each eventsByAge as [key, events] (key)}
<p class="text-xs uppercase tracking-wide opacity-60">
{#if key === "day"}
Last 24 Hours
{:else if key === "week"}
Last 7 Days
{:else}
Older
{/if}
</p>
{#each events as event (event.id)}
<Button
class="card card-sm card-interactive flex flex-col gap-2"
onclick={() => onResultClick(event)}>
<NoteCard minimal {event} {url}>
<NoteContentMinimal {event} />
</NoteCard>
<div class="flex gap-2">
<Badge variant="neutral">
{formatTimestampRelative(event.created_at)}
</Badge>
{@render badges?.(event)}
</div>
</Button>
{/each}
{/each}
{/if}
{:else}
{@render empty?.()}
{/if}
</ModalBody>

View file

@ -1,4 +1,4 @@
import {derived, writable} from "svelte/store"
import {derived, readable, writable} from "svelte/store"
import type {Readable} from "svelte/store"
import {always} from "@welshman/lib"
import type {Maybe} from "@welshman/lib"
@ -59,11 +59,7 @@ const makeApp = (user?: User) => {
dufflepudUrl: DUFFLEPUD_URL,
getDefaultRelays: always(DEFAULT_RELAYS),
getIndexerRelays: always(INDEXER_RELAYS),
getSearchRelays: () => {
const urls = user ? instance.use(SearchRelayLists).urls(user.pubkey).get() : []
return urls.length > 0 ? urls : DEFAULT_SEARCH_RELAYS
},
getSearchRelays: () => userSearchRelayUrls.get(),
},
policies: appPolicies,
})
@ -164,6 +160,17 @@ export const thunks = usePlugin(Thunks)
export const wot = usePlugin(Wot)
export const wraps = usePlugin(Wraps)
// The relays profile search runs against, with a fallback so search still works before the user
// has chosen any of their own.
export const userSearchRelayUrls = withGetter(
derived(
fromApp($app =>
$app.user ? $app.use(SearchRelayLists).urls($app.user.pubkey).$ : readable<string[]>([]),
),
urls => (urls.length > 0 ? urls : DEFAULT_SEARCH_RELAYS),
),
)
// The domain entry points, since almost every read or write goes through one of them.
export const reader = <R extends BaseEventReader, W extends EventWriter<R>>(
factory: KindFactory<R, W>,