aboutsummaryrefslogtreecommitdiffstats
path: root/src/routes
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-03-06 17:14:31 -0800
committerAdam Soltys <[email protected]>2026-03-06 17:14:31 -0800
commit07c2d1afc6d8512fda97ce5c74c44dade1ddb218 (patch)
treef1c0d52c809896decad58960195374e64050676e /src/routes
parentf84e49bdfefa674b6b6a56ea5ba2bd467326e87e (diff)
downloadcoinos-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.svelte57
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>