From d4982dff0ccef6ea79ced796c79b4da10105bfac Mon Sep 17 00:00:00 2001 From: dtonon Date: Fri, 15 May 2026 11:47:01 +0100 Subject: [PATCH] Add Contacts page --- src/lib/components/LeftSidebar.svelte | 4 - src/lib/components/Navbar.svelte | 3 - src/lib/group.svelte.ts | 11 ++- src/lib/profiles.svelte.ts | 8 ++ src/routes/contacts/+page.svelte | 125 ++++++++++++++++++++++++++ 5 files changed, 141 insertions(+), 10 deletions(-) create mode 100644 src/routes/contacts/+page.svelte diff --git a/src/lib/components/LeftSidebar.svelte b/src/lib/components/LeftSidebar.svelte index bb2e75a..be75922 100644 --- a/src/lib/components/LeftSidebar.svelte +++ b/src/lib/components/LeftSidebar.svelte @@ -59,10 +59,6 @@ > More

- Settings Contacts{name} - diff --git a/src/lib/group.svelte.ts b/src/lib/group.svelte.ts index 07ea513..0721ea5 100644 --- a/src/lib/group.svelte.ts +++ b/src/lib/group.svelte.ts @@ -7,6 +7,7 @@ export type GroupMetadata = { about?: string; isPrivate: boolean; isClosed: boolean; + admins: string[]; }; let group = $state(null); @@ -21,18 +22,22 @@ export async function loadGroup() { const pool = new SimplePool(); try { const events = await pool.querySync([RELAY_URL], { - kinds: [39000], + kinds: [39000, 39001], "#d": [GROUP_ID], - limit: 1, }); - const event = events[0]; + const event = events.find((e) => e.kind === 39000); if (!event) return; + // Admins live in the NIP-29 kind 39001 event as `p` tags. + const adminsEvent = events.find((e) => e.kind === 39001); + const admins = + adminsEvent?.tags.filter((t) => t[0] === "p").map((t) => t[1]) ?? []; group = { name: event.tags.find((t) => t[0] === "name")?.[1] ?? GROUP_ID, picture: event.tags.find((t) => t[0] === "picture")?.[1], about: event.tags.find((t) => t[0] === "about")?.[1], isPrivate: event.tags.some((t) => t[0] === "private"), isClosed: event.tags.some((t) => t[0] === "closed"), + admins, }; } finally { pool.close([RELAY_URL]); diff --git a/src/lib/profiles.svelte.ts b/src/lib/profiles.svelte.ts index b3001d3..66436ac 100644 --- a/src/lib/profiles.svelte.ts +++ b/src/lib/profiles.svelte.ts @@ -12,6 +12,8 @@ export type ProfileEntry = { nip05?: string; picture?: string; about?: string; + website?: string; + lud16?: string; fetchedAt: number; }; @@ -56,6 +58,8 @@ function parseProfileEvent(evt: Event): ProfileEntry | null { nip05?: string; picture?: string; about?: string; + website?: string; + lud16?: string; } = {}; try { md = JSON.parse(evt.content); @@ -70,6 +74,8 @@ function parseProfileEvent(evt: Event): ProfileEntry | null { nip05: md.nip05, picture: md.picture, about: md.about, + website: md.website, + lud16: md.lud16, fetchedAt: evt.created_at, }; } @@ -315,6 +321,8 @@ export function ingestNostrUser(user: NostrUser) { nip05: md.nip05, picture: md.picture ?? user.image, about: md.about, + website: md.website, + lud16: md.lud16, fetchedAt: user.lastUpdated || 0, }; upsertProfile(entry); diff --git a/src/routes/contacts/+page.svelte b/src/routes/contacts/+page.svelte new file mode 100644 index 0000000..b6c1b6e --- /dev/null +++ b/src/routes/contacts/+page.svelte @@ -0,0 +1,125 @@ + + + + Contacts + + + +
+

Contacts

+ + {#if !groupStore.data} +

Loading…

+ {:else if contacts.length === 0} +

No admins listed.

+ {:else} +
    + {#each contacts as c (c.pubkey)} +
  • + {#if c.entry?.picture} + + {:else} + + {displayName(c)[0].toUpperCase()} + + {/if} + +
    +
    +
    +

    + {displayName(c)} +

    + {#if c.entry?.nip05} +

    + {c.entry.nip05} +

    + {/if} +
    + + View profile ↗ + +
    + + {#if c.entry?.about} +

    + {c.entry.about} +

    + {/if} + + {#if c.entry?.website || c.entry?.lud16} +
    + {#if c.entry?.website} + + {websiteLabel(c.entry.website)} + + {/if} + {#if c.entry?.lud16} + + ⚡ {c.entry.lud16} + + {/if} +
    + {/if} +
    +
  • + {/each} +
+ {/if} +