This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

add description

+89 -16
+64 -16
app/src/app/profile/[handle]/page.tsx
··· 25 25 const [entries, setEntries] = useState<FlushingEntry[]>([]); 26 26 const [totalCount, setTotalCount] = useState<number>(0); 27 27 const [loading, setLoading] = useState(true); 28 + const [profileLoading, setProfileLoading] = useState(true); 28 29 const [error, setError] = useState<string | null>(null); 30 + const [profileError, setProfileError] = useState<string | null>(null); 29 31 const [flushesPerDay, setFlushesPerDay] = useState<number>(0); 30 32 const [chartData, setChartData] = useState<{date: string, count: number}[]>([]); 33 + // Match Bluesky's API response format 31 34 interface ProfileData { 32 - did?: string; 33 - handle?: string; 35 + did: string; 36 + handle: string; 34 37 displayName?: string; 35 38 description?: string; 39 + avatar?: string; 40 + banner?: string; 41 + followersCount?: number; 42 + followsCount?: number; 43 + postsCount?: number; 44 + indexedAt?: string; 45 + viewer?: any; 36 46 } 37 47 const [profileData, setProfileData] = useState<ProfileData | null>(null); 38 48 39 49 useEffect(() => { 40 - // Fetch the user's statuses when the component mounts 50 + // Fetch the user's statuses and profile data when the component mounts 41 51 fetchUserStatuses(); 52 + fetchProfileData(); 42 53 }, [handle]); 54 + 55 + // Function to fetch the user's profile data directly 56 + const fetchProfileData = async () => { 57 + try { 58 + setProfileLoading(true); 59 + setProfileError(null); 60 + 61 + // Fetch profile data directly from Bluesky API 62 + const profileResponse = await fetch(`https://public.api.bsky.app/xrpc/app.bsky.actor.getProfile?actor=${encodeURIComponent(handle)}`); 63 + 64 + if (profileResponse.ok) { 65 + const profileData = await profileResponse.json(); 66 + setProfileData(profileData); 67 + console.log("Fetched profile data:", profileData); 68 + } else { 69 + const errorText = await profileResponse.text(); 70 + console.warn(`Failed to fetch profile data: ${profileResponse.statusText}`, errorText); 71 + setProfileError(`Failed to fetch profile: ${profileResponse.status}`); 72 + } 73 + } catch (error: any) { 74 + console.error("Error fetching profile:", error); 75 + setProfileError(error.message || "Failed to fetch profile data"); 76 + } finally { 77 + setProfileLoading(false); 78 + } 79 + }; 43 80 44 81 // Function to fetch the user's statuses 45 82 const fetchUserStatuses = async () => { ··· 65 102 setEntries(userEntries); 66 103 setTotalCount(data.count || 0); 67 104 68 - // Set profile data if available 69 - if (data.profile) { 70 - setProfileData(data.profile); 71 - } 105 + // We now fetch profile data separately 72 106 73 107 // Calculate statistics and chart data 74 108 if (userEntries.length > 0) { ··· 125 159 126 160 <div className={styles.profileHeader}> 127 161 <div className={styles.profileInfo}> 128 - {profileData?.displayName ? ( 162 + {profileLoading ? ( 163 + <div className={styles.profileLoading}> 164 + <h2 className={`${styles.profileTitle} font-bold`}>@{handle}</h2> 165 + <div className={styles.smallLoader}></div> 166 + </div> 167 + ) : profileError ? ( 168 + <div> 169 + <h2 className={`${styles.profileTitle} font-bold`}>@{handle}</h2> 170 + <p className={styles.smallError}>Unable to load profile details</p> 171 + </div> 172 + ) : ( 129 173 <> 130 - <h2 className={`${styles.profileTitle} font-bold`}>{profileData.displayName}</h2> 131 - <h3 className={`${styles.profileHandle} font-medium`}>@{handle}</h3> 174 + {profileData?.displayName ? ( 175 + <> 176 + <h2 className={`${styles.profileTitle} font-bold`}>{profileData.displayName}</h2> 177 + <h3 className={`${styles.profileHandle} font-medium`}>@{handle}</h3> 178 + </> 179 + ) : ( 180 + <h2 className={`${styles.profileTitle} font-bold`}>@{handle}</h2> 181 + )} 182 + 183 + {profileData?.description && ( 184 + <p className={`${styles.description} font-regular`}>{profileData.description}</p> 185 + )} 132 186 </> 133 - ) : ( 134 - <h2 className={`${styles.profileTitle} font-bold`}>@{handle}</h2> 135 - )} 136 - 137 - {profileData?.description && ( 138 - <p className={`${styles.description} font-regular`}>{profileData.description}</p> 139 187 )} 140 188 141 189 <a
+25
app/src/app/profile/[handle]/profile.module.css
··· 263 263 margin-bottom: 1rem; 264 264 } 265 265 266 + .smallLoader { 267 + border: 3px solid var(--input-background); 268 + border-top: 3px solid var(--primary-color); 269 + border-radius: 50%; 270 + width: 18px; 271 + height: 18px; 272 + animation: spin 1s linear infinite; 273 + margin-top: 0.5rem; 274 + display: inline-block; 275 + } 276 + 277 + .profileLoading { 278 + display: flex; 279 + flex-direction: column; 280 + align-items: flex-start; 281 + gap: 0.5rem; 282 + } 283 + 284 + .smallError { 285 + color: var(--timestamp-color); 286 + font-size: 0.9rem; 287 + font-style: italic; 288 + margin-top: 0.3rem; 289 + } 290 + 266 291 @keyframes spin { 267 292 0% { transform: rotate(0deg); } 268 293 100% { transform: rotate(360deg); }