diff options
| author | Adam Soltys <[email protected]> | 2026-03-06 17:14:31 -0800 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-03-06 17:14:31 -0800 |
| commit | 07c2d1afc6d8512fda97ce5c74c44dade1ddb218 (patch) | |
| tree | f1c0d52c809896decad58960195374e64050676e /src/routes | |
| parent | f84e49bdfefa674b6b6a56ea5ba2bd467326e87e (diff) | |
| download | coinos-ui-07c2d1afc6d8512fda97ce5c74c44dade1ddb218.tar.gz coinos-ui-07c2d1afc6d8512fda97ce5c74c44dade1ddb218.zip | |
MLS user search on /messages
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Diffstat (limited to 'src/routes')
| -rw-r--r-- | src/routes/(app)/messages/+page.svelte | 57 |
1 files changed, 46 insertions, 11 deletions
diff --git a/src/routes/(app)/messages/+page.svelte b/src/routes/(app)/messages/+page.svelte index f9f59bb1..1981b5f1 100644 --- a/src/routes/(app)/messages/+page.svelte +++ b/src/routes/(app)/messages/+page.svelte @@ -3,12 +3,14 @@ import { getGroups, subscribeToMessages, fetchGroupHistory, - resolveUser, displayName, + searchMlsUsers, preloadMlsUsers, resolveUser, displayName, } from "$lib/messaging"; import type { GroupInfo } from "$lib/messaging"; import { t } from "$lib/translations"; import Avatar from "$comp/Avatar.svelte"; + const focus = (el: HTMLElement) => el.focus(); + let { data } = $props(); const user = $derived(data.user); @@ -22,6 +24,15 @@ let loaded = $state(false); let memberInfos = $state(new Map<string, any>()); + // Search state + let query = $state(""); + let searchResults: any[] = $state([]); + + const onSearchInput = async () => { + if (!query.trim()) { searchResults = []; return; } + searchResults = (await searchMlsUsers(query)).filter((u) => u.pubkey !== user.pubkey); + }; + const otherMembers = (g: GroupInfo) => g.members.filter((pk) => pk !== user.pubkey); const groupDisplayName = (g: GroupInfo): string => { @@ -50,30 +61,54 @@ } }; - const loadGroups = async () => { - await fetchGroupHistory(user); + const refreshGroups = async () => { const allGroups = await getGroups(user); groups = allGroups.filter((g) => g.members.includes(user.pubkey)); resolveMembers(groups); - loaded = true; }; let closeSub: (() => void) | undefined; onMount(() => { - loadGroups(); - subscribeToMessages(user, async () => { - const allGroups = await getGroups(user); - groups = allGroups.filter((g) => g.members.includes(user.pubkey)); - resolveMembers(groups); - }).then((close) => (closeSub = close)); + // Show cached groups immediately, then fetch history in background + refreshGroups().then(() => { loaded = true; }); + fetchGroupHistory(user).then(refreshGroups); + preloadMlsUsers(); + subscribeToMessages(user, refreshGroups).then((close) => (closeSub = close)); }); onDestroy(() => closeSub?.()); </script> <div class="container mx-auto max-w-xl px-4 space-y-1"> - {#if !loaded} + <input + use:focus + type="text" + bind:value={query} + oninput={onSearchInput} + placeholder={$t("dm.searchMls")} + class="w-full px-4 py-2 rounded-full border border-base-300 bg-base-100 text-base-content focus:outline-none focus:border-base-content/30 mb-2" + /> + + {#if query.trim()} + {#if searchResults.length === 0} + <p class="text-center text-secondary py-4">{$t("dm.noResults")}</p> + {:else} + {#each searchResults as u} + <a href="/messages/{u.pubkey}" class="flex items-center gap-3 p-3 rounded-2xl hover:bg-base-200"> + <div class="shrink-0"> + <Avatar user={{ pubkey: u.pubkey, username: u.name, picture: u.picture }} size={12} disabled /> + </div> + <div class="min-w-0 flex-1"> + <div class="font-semibold text-lg truncate">{u.name || u.pubkey.slice(0, 12) + "…"}</div> + {#if u.nip05} + <div class="text-secondary text-sm truncate">{u.nip05}</div> + {/if} + </div> + </a> + {/each} + {/if} + {:else if !loaded} <div class="flex justify-center py-8"><div class="loading loading-spinner"></div></div> {:else if groups.length === 0} <p class="text-center text-secondary">{$t("dm.noChats")}</p> |
