my website at ewancroft.uk

feat: display pronouns in profile card

- Add pronouns field to ProfileData and PostAuthor types
- Fetch pronouns from profile record in addition to profile view
- Display pronouns below handle in ProfileCard component
- Style with subtle italic text for visual consistency
- Gracefully handle cases where pronouns are not set or fetch fails

ewancroft.uk d2e26a69 a236b046

verified
Changed files
+112 -1
src
lib
components
layout
main
services
atproto
+80
git_diff.txt
··· 1 + diff --git a/src/lib/components/layout/main/card/ProfileCard.svelte b/src/lib/components/layout/main/card/ProfileCard.svelte 2 + index dc23db8..5d6f030 100644 3 + --- a/src/lib/components/layout/main/card/ProfileCard.svelte 4 + +++ b/src/lib/components/layout/main/card/ProfileCard.svelte 5 + @@ -112,6 +112,9 @@ 6 + {safeProfile.displayName || safeProfile.handle} 7 + </h2> 8 + <p class="font-medium text-ink-700 dark:text-ink-200">@{safeProfile.handle}</p> 9 + + {#if safeProfile.pronouns} 10 + + <p class="text-sm italic text-ink-600 dark:text-ink-300">{safeProfile.pronouns}</p> 11 + + {/if} 12 + 13 + {#if safeProfile.description} 14 + <p 15 + diff --git a/src/lib/services/atproto/fetch.ts b/src/lib/services/atproto/fetch.ts 16 + index c719682..853a0c2 100644 17 + --- a/src/lib/services/atproto/fetch.ts 18 + +++ b/src/lib/services/atproto/fetch.ts 19 + @@ -40,6 +40,31 @@ export async function fetchProfile(fetchFn?: typeof fetch): Promise<ProfileData> 20 + fetchFn 21 + ); 22 + 23 + + // Fetch the actual profile record to get pronouns and other fields 24 + + // The profile view doesn't include pronouns, so we need the record 25 + + let pronouns: string | undefined; 26 + + try { 27 + + console.debug('[Profile] Attempting to fetch profile record for pronouns'); 28 + + const recordResponse = await withFallback( 29 + + PUBLIC_ATPROTO_DID, 30 + + async (agent) => { 31 + + const response = await agent.com.atproto.repo.getRecord({ 32 + + repo: PUBLIC_ATPROTO_DID, 33 + + collection: 'app.bsky.actor.profile', 34 + + rkey: 'self' 35 + + }); 36 + + return response.data; 37 + + }, 38 + + false, 39 + + fetchFn 40 + + ); 41 + + pronouns = (recordResponse.value as any).pronouns; 42 + + console.debug('[Profile] Successfully fetched pronouns:', pronouns); 43 + + } catch (error) { 44 + + console.debug('[Profile] Could not fetch profile record for pronouns:', error); 45 + + // Continue without pronouns if record fetch fails 46 + + } 47 + + 48 + const data: ProfileData = { 49 + did: profile.did, 50 + handle: profile.handle, 51 + @@ -49,7 +74,8 @@ export async function fetchProfile(fetchFn?: typeof fetch): Promise<ProfileData> 52 + banner: profile.banner, 53 + followersCount: profile.followersCount, 54 + followsCount: profile.followsCount, 55 + - postsCount: profile.postsCount 56 + + postsCount: profile.postsCount, 57 + + pronouns: pronouns 58 + }; 59 + 60 + console.info('[Profile] Successfully fetched profile data'); 61 + diff --git a/src/lib/services/atproto/types.ts b/src/lib/services/atproto/types.ts 62 + index f4d4b16..37440f6 100644 63 + --- a/src/lib/services/atproto/types.ts 64 + +++ b/src/lib/services/atproto/types.ts 65 + @@ -12,6 +12,7 @@ export interface ProfileData { 66 + followersCount?: number; 67 + followsCount?: number; 68 + postsCount?: number; 69 + + pronouns?: string; 70 + } 71 + 72 + export interface StatusData { 73 + @@ -150,6 +151,7 @@ export interface PostAuthor { 74 + handle: string; 75 + displayName?: string; 76 + avatar?: string; 77 + + pronouns?: string; 78 + } 79 + 80 + export interface BlueskyPost {
+3
src/lib/components/layout/main/card/ProfileCard.svelte
··· 112 112 {safeProfile.displayName || safeProfile.handle} 113 113 </h2> 114 114 <p class="font-medium text-ink-700 dark:text-ink-200">@{safeProfile.handle}</p> 115 + {#if safeProfile.pronouns} 116 + <p class="text-sm italic text-ink-600 dark:text-ink-300">{safeProfile.pronouns}</p> 117 + {/if} 115 118 116 119 {#if safeProfile.description} 117 120 <p
+27 -1
src/lib/services/atproto/fetch.ts
··· 40 40 fetchFn 41 41 ); 42 42 43 + // Fetch the actual profile record to get pronouns and other fields 44 + // The profile view doesn't include pronouns, so we need the record 45 + let pronouns: string | undefined; 46 + try { 47 + console.debug('[Profile] Attempting to fetch profile record for pronouns'); 48 + const recordResponse = await withFallback( 49 + PUBLIC_ATPROTO_DID, 50 + async (agent) => { 51 + const response = await agent.com.atproto.repo.getRecord({ 52 + repo: PUBLIC_ATPROTO_DID, 53 + collection: 'app.bsky.actor.profile', 54 + rkey: 'self' 55 + }); 56 + return response.data; 57 + }, 58 + false, 59 + fetchFn 60 + ); 61 + pronouns = (recordResponse.value as any).pronouns; 62 + console.debug('[Profile] Successfully fetched pronouns:', pronouns); 63 + } catch (error) { 64 + console.debug('[Profile] Could not fetch profile record for pronouns:', error); 65 + // Continue without pronouns if record fetch fails 66 + } 67 + 43 68 const data: ProfileData = { 44 69 did: profile.did, 45 70 handle: profile.handle, ··· 49 74 banner: profile.banner, 50 75 followersCount: profile.followersCount, 51 76 followsCount: profile.followsCount, 52 - postsCount: profile.postsCount 77 + postsCount: profile.postsCount, 78 + pronouns: pronouns 53 79 }; 54 80 55 81 console.info('[Profile] Successfully fetched profile data');
+2
src/lib/services/atproto/types.ts
··· 12 12 followersCount?: number; 13 13 followsCount?: number; 14 14 postsCount?: number; 15 + pronouns?: string; 15 16 } 16 17 17 18 export interface StatusData { ··· 150 151 handle: string; 151 152 displayName?: string; 152 153 avatar?: string; 154 + pronouns?: string; 153 155 } 154 156 155 157 export interface BlueskyPost {