You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
183 lines
5.2 KiB
183 lines
5.2 KiB
<script lang='ts'> |
|
import { Avatar } from 'flowbite-svelte'; |
|
import NDK, { type NDKUserProfile } from "@nostr-dev-kit/ndk"; |
|
import { ndkInstance } from '$lib/ndk'; |
|
import { userBadge } from '$lib/snippets/UserSnippets.svelte'; |
|
|
|
// Component configuration types |
|
type AvatarSize = 'sm' | 'md' | 'lg'; |
|
|
|
// Component props interface |
|
interface $$Props { |
|
pubkey: string; // Required: The Nostr public key of the user |
|
name?: string | null; // Optional: Display name override |
|
showAvatar?: boolean; // Optional: Whether to show the avatar (default: true) |
|
avatarSize?: AvatarSize; // Optional: Size of the avatar (default: 'md') |
|
} |
|
|
|
// Destructure and set default props |
|
let { |
|
pubkey, |
|
name = null, |
|
showAvatar = true, |
|
avatarSize = 'md' as AvatarSize |
|
} = $props(); |
|
|
|
console.log('[InlineProfile] Initialized with props:', { |
|
pubkey, |
|
name, |
|
showAvatar, |
|
avatarSize |
|
}); |
|
|
|
// Constants |
|
const EXTERNAL_PROFILE_DESTINATION = './events?id='; |
|
|
|
// Component state type definition |
|
type ProfileState = { |
|
loading: boolean; // Whether we're currently loading the profile |
|
error: string | null; // Any error that occurred during loading |
|
profile: NDKUserProfile | null; // The user's profile data |
|
npub: string; // The user's npub (bech32 encoded pubkey) |
|
}; |
|
|
|
// Initialize component state |
|
let state = $state<ProfileState>({ |
|
loading: true, |
|
error: null, |
|
profile: null, |
|
npub: '' |
|
}); |
|
|
|
// Derived values from state |
|
const pfp = $derived(state.profile?.image); |
|
const username = $derived(state.profile?.name); |
|
const isAnonymous = $derived(!state.profile?.name && !name); |
|
|
|
// Log derived values reactively when they change |
|
$effect(() => { |
|
console.log('[InlineProfile] Derived values updated:', { |
|
pfp, |
|
username, |
|
isAnonymous, |
|
hasProfile: !!state.profile, |
|
hasNpub: !!state.npub, |
|
profileState: { |
|
loading: state.loading, |
|
error: state.error, |
|
hasProfile: !!state.profile, |
|
npub: state.npub |
|
} |
|
}); |
|
}); |
|
|
|
// Avatar size classes mapping |
|
const avatarClasses: Record<AvatarSize, string> = { |
|
sm: 'h-5 w-5', |
|
md: 'h-7 w-7', |
|
lg: 'h-9 w-9' |
|
}; |
|
|
|
/** |
|
* Fetches user data from NDK |
|
* @param pubkey - The Nostr public key to fetch data for |
|
*/ |
|
async function fetchUserData(pubkey: string) { |
|
console.log('[InlineProfile] fetchUserData called with pubkey:', pubkey); |
|
|
|
if (!pubkey) { |
|
console.warn('[InlineProfile] No pubkey provided to fetchUserData'); |
|
state.error = 'No pubkey provided'; |
|
state.loading = false; |
|
return; |
|
} |
|
|
|
try { |
|
console.log('[InlineProfile] Getting NDK instance'); |
|
const ndk = $ndkInstance as NDK; |
|
|
|
console.log('[InlineProfile] Creating NDK user object'); |
|
const user = ndk.getUser({ pubkey }); |
|
|
|
console.log('[InlineProfile] Getting npub'); |
|
state.npub = user.npub; |
|
console.log('[InlineProfile] Got npub:', state.npub); |
|
|
|
console.log('[InlineProfile] Fetching user profile'); |
|
const userProfile = await user.fetchProfile(); |
|
console.log('[InlineProfile] Got user profile:', { |
|
name: userProfile?.name, |
|
displayName: userProfile?.displayName, |
|
nip05: userProfile?.nip05, |
|
hasImage: !!userProfile?.image |
|
}); |
|
|
|
state.profile = userProfile; |
|
state.loading = false; |
|
} catch (error) { |
|
console.error('[InlineProfile] Error fetching user data:', error); |
|
state.error = error instanceof Error ? error.message : 'Failed to fetch profile'; |
|
state.loading = false; |
|
} |
|
} |
|
|
|
/** |
|
* Shortens an npub string for display |
|
* @param long - The npub string to shorten |
|
* @returns Shortened npub string |
|
*/ |
|
function shortenNpub(long: string | undefined): string { |
|
if (!long) return ''; |
|
const shortened = `${long.slice(0, 8)}…${long.slice(-4)}`; |
|
console.log('[InlineProfile] Shortened npub:', { original: long, shortened }); |
|
return shortened; |
|
} |
|
|
|
// Effect to fetch user data when pubkey changes |
|
$effect(() => { |
|
console.log('[InlineProfile] Effect triggered, pubkey:', pubkey); |
|
if (pubkey) { |
|
fetchUserData(pubkey); |
|
} else { |
|
console.warn('[InlineProfile] No pubkey available for effect'); |
|
} |
|
}); |
|
</script> |
|
|
|
<!-- Component Template --> |
|
{#if state.loading} |
|
<!-- Loading state --> |
|
<span class="animate-pulse" title="Loading profile..."> |
|
{name ?? '…'} |
|
</span> |
|
{:else if state.error} |
|
<!-- Error state --> |
|
<span class="text-red-500" title={state.error}> |
|
{name ?? shortenNpub(pubkey)} |
|
</span> |
|
{:else if isAnonymous} |
|
<!-- Anonymous user state --> |
|
{@render userBadge(pubkey, name)} |
|
{:else if state.npub} |
|
<!-- Authenticated user with profile --> |
|
<a |
|
href={EXTERNAL_PROFILE_DESTINATION + state.npub} |
|
title={name ?? username} |
|
class="inline-flex items-center hover:opacity-80 transition-opacity" |
|
> |
|
{#if showAvatar} |
|
<Avatar |
|
rounded |
|
class={`${avatarClasses[avatarSize]} mx-1 cursor-pointer inline bg-transparent`} |
|
src={pfp} |
|
alt={username ?? 'User avatar'} |
|
/> |
|
{/if} |
|
{@render userBadge(pubkey, name)} |
|
</a> |
|
{:else} |
|
<!-- Fallback state --> |
|
<span title="No profile data available"> |
|
{name ?? shortenNpub(pubkey)} |
|
</span> |
|
{/if} |