diff --git a/src/app.css b/src/app.css index 4988e6a..507dbf8 100644 --- a/src/app.css +++ b/src/app.css @@ -265,6 +265,9 @@ @apply bg-primary-50 text-primary-800 text-sm font-medium me-2 px-2.5 py-0.5 rounded-sm dark:bg-primary-900 dark:text-primary-200; } + .npub-badge { + @apply inline-flex items-center text-primary-600 dark:text-primary-500 hover:underline me-2 px-2.5 py-0.5 rounded-sm border border-primary-600 dark:border-primary-500; + } } @layer components { diff --git a/src/lib/snippets/UserSnippets.svelte b/src/lib/snippets/UserSnippets.svelte new file mode 100644 index 0000000..62f9f60 --- /dev/null +++ b/src/lib/snippets/UserSnippets.svelte @@ -0,0 +1,13 @@ + + +{#snippet userBadge(identifier: string, displayText: string | undefined)} + {#await createProfileLinkWithVerification(identifier, displayText)} + {@html createProfileLink(identifier, displayText)} + {:then html} + {@html html} + {:catch} + {@html createProfileLink(identifier, displayText)} + {/await} +{/snippet} diff --git a/src/lib/utils/nostrUtils.ts b/src/lib/utils/nostrUtils.ts index f702d24..48f593a 100644 --- a/src/lib/utils/nostrUtils.ts +++ b/src/lib/utils/nostrUtils.ts @@ -2,6 +2,11 @@ import { get } from 'svelte/store'; import { nip19 } from 'nostr-tools'; import { ndkInstance } from '$lib/ndk'; import { npubCache } from './npubCache'; +import { NDKUser } from "@nostr-dev-kit/ndk"; + +const badgeCheckSvg = '' + +const graduationCapSvg = ''; // Regular expressions for Nostr identifiers - match the entire identifier including any prefix export const NOSTR_PROFILE_REGEX = /(?@${escapedText}`; + return `@${escapedText}`; } +/** + * Create a profile link element with a NIP-05 verification indicator. + */ +export async function createProfileLinkWithVerification(identifier: string, displayText: string | undefined): Promise { + const ndk = get(ndkInstance) as NDK; + if (!ndk) { + return createProfileLink(identifier, displayText); + } + + const cleanId = identifier.replace(/^nostr:/, ''); + const isNpub = cleanId.startsWith('npub'); + + let user: NDKUser; + if (isNpub) { + user = ndk.getUser({ npub: cleanId }); + } else { + user = ndk.getUser({ pubkey: cleanId }); + } + + const profile = await user.fetchProfile(); + const nip05 = profile?.nip05; + + if (!nip05) { + return createProfileLink(identifier, displayText); + } + + const defaultText = `${cleanId.slice(0, 8)}...${cleanId.slice(-4)}`; + const escapedText = escapeHtml(displayText || defaultText); + const displayIdentifier = profile?.displayName ?? profile?.name ?? escapedText; + + const isVerified = await user.validateNip05(nip05); + + if (!isVerified) { + return createProfileLink(identifier, displayText); + } + + // TODO: Make this work with an enum in case we add more types. + const type = nip05.endsWith('edu') ? 'edu' : 'standard'; + switch (type) { + case 'edu': + return `${graduationCapSvg}@${displayIdentifier}`; + case 'standard': + return `${badgeCheckSvg}@${displayIdentifier}`; + } +} /** * Create a note link element */