This repository has no description
0

Configure Feed

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

switch to direct calling

+132 -16
+132 -16
app/src/app/profile/[handle]/page.tsx
··· 95 95 setLoading(true); 96 96 setError(null); 97 97 98 - // Call our API endpoint to get the user's statuses 99 - // The endpoint parameter is named "handle" but it accepts both handles and DIDs 100 - const response = await fetch(`/api/bluesky/profile?handle=${encodeURIComponent(handle)}`, { 101 - cache: 'no-store', 98 + // Step 1: Resolve handle to DID 99 + console.log(`Resolving handle ${handle} to DID...`); 100 + const resolveResponse = await fetch(`https://public.api.bsky.app/xrpc/com.atproto.identity.resolveHandle?handle=${encodeURIComponent(handle)}`); 101 + 102 + if (!resolveResponse.ok) { 103 + throw new Error(`Failed to resolve handle: ${resolveResponse.statusText}`); 104 + } 105 + 106 + const resolveData = await resolveResponse.json(); 107 + const did = resolveData.did; 108 + console.log(`Resolved handle ${handle} to DID: ${did}`); 109 + 110 + // Step 2: Get PDS endpoint from PLC directory 111 + console.log(`Getting PDS endpoint for DID ${did}...`); 112 + const plcResponse = await fetch(`https://plc.directory/${did}/data`); 113 + 114 + if (!plcResponse.ok) { 115 + throw new Error(`Failed to get PDS endpoint: ${plcResponse.statusText}`); 116 + } 117 + 118 + const plcData = await plcResponse.json(); 119 + console.log('PLC directory data:', plcData); 120 + 121 + // Find the PDS service 122 + const pdsService = plcData.service?.find((s: any) => 123 + s.type === 'AtprotoPersonalDataServer' || s.type === 'AtprotoDataServer' 124 + ); 125 + 126 + if (!pdsService?.endpoint) { 127 + throw new Error('No PDS service endpoint found'); 128 + } 129 + 130 + // Extract the hostname from the PDS endpoint 131 + const serviceUrl = new URL(pdsService.endpoint); 132 + const servicePds = serviceUrl.hostname; 133 + const serviceEndpoint = `https://${servicePds}`; 134 + console.log(`Using PDS endpoint: ${serviceEndpoint}`); 135 + 136 + // Step 3: Fetch records from PDS 137 + const listRecordsUrl = `${serviceEndpoint}/xrpc/com.atproto.repo.listRecords?repo=${encodeURIComponent(did)}&collection=im.flushing.right.now&limit=100`; 138 + console.log(`Fetching records from: ${listRecordsUrl}`); 139 + 140 + const recordsResponse = await fetch(listRecordsUrl, { 102 141 headers: { 103 - 'Cache-Control': 'no-cache', 104 - 'Pragma': 'no-cache' 142 + 'Accept': 'application/json' 105 143 } 106 144 }); 107 145 108 - if (!response.ok) { 109 - throw new Error(`Failed to fetch profile: ${response.status}`); 146 + if (!recordsResponse.ok) { 147 + // If first attempt fails, try alternative URL pattern 148 + const altUrl = `${serviceEndpoint}/com.atproto.repo.listRecords?repo=${encodeURIComponent(did)}&collection=im.flushing.right.now&limit=100`; 149 + console.log(`First attempt failed, trying alternative URL: ${altUrl}`); 150 + 151 + const altResponse = await fetch(altUrl, { 152 + headers: { 153 + 'Accept': 'application/json' 154 + } 155 + }); 156 + 157 + if (!altResponse.ok) { 158 + throw new Error(`Failed to fetch records: ${altResponse.statusText}`); 159 + } 160 + 161 + const recordsData = await altResponse.json(); 162 + console.log('Successfully fetched records with alternative URL'); 163 + 164 + // Process the records 165 + const userEntries = recordsData.records 166 + .map((record: any) => { 167 + const text = record.value.text || ''; 168 + if (containsBannedWords(text)) return null; 169 + 170 + return { 171 + id: record.uri, 172 + uri: record.uri, 173 + cid: record.cid, 174 + did: did, 175 + text: sanitizeText(text), 176 + emoji: record.value.emoji || '🚽', 177 + created_at: record.value.createdAt 178 + }; 179 + }) 180 + .filter((entry: any): entry is FlushingEntry => entry !== null); 181 + 182 + setEntries(userEntries); 183 + setTotalCount(userEntries.length); 184 + 185 + // Calculate emoji stats 186 + const emojiCounts = new Map<string, number>(); 187 + userEntries.forEach((entry: FlushingEntry) => { 188 + const emoji = entry.emoji?.trim() || '🚽'; 189 + emojiCounts.set(emoji, (emojiCounts.get(emoji) || 0) + 1); 190 + }); 191 + 192 + const emojiStats = Array.from(emojiCounts.entries()) 193 + .map(([emoji, count]): EmojiStat => ({ emoji, count })) 194 + .sort((a, b) => b.count - a.count); 195 + 196 + setEmojiStats(emojiStats); 197 + } else { 198 + const recordsData = await recordsResponse.json(); 199 + console.log('Successfully fetched records'); 200 + 201 + // Process the records 202 + const userEntries = recordsData.records 203 + .map((record: any) => { 204 + const text = record.value.text || ''; 205 + if (containsBannedWords(text)) return null; 206 + 207 + return { 208 + id: record.uri, 209 + uri: record.uri, 210 + cid: record.cid, 211 + did: did, 212 + text: sanitizeText(text), 213 + emoji: record.value.emoji || '🚽', 214 + created_at: record.value.createdAt 215 + }; 216 + }) 217 + .filter((entry: any): entry is FlushingEntry => entry !== null); 218 + 219 + setEntries(userEntries); 220 + setTotalCount(userEntries.length); 221 + 222 + // Calculate emoji stats 223 + const emojiCounts = new Map<string, number>(); 224 + userEntries.forEach((entry: FlushingEntry) => { 225 + const emoji = entry.emoji?.trim() || '🚽'; 226 + emojiCounts.set(emoji, (emojiCounts.get(emoji) || 0) + 1); 227 + }); 228 + 229 + const emojiStats = Array.from(emojiCounts.entries()) 230 + .map(([emoji, count]): EmojiStat => ({ emoji, count })) 231 + .sort((a, b) => b.count - a.count); 232 + 233 + setEmojiStats(emojiStats); 110 234 } 111 - 112 - const data = await response.json(); 113 - const userEntries = data.entries || []; 114 - setEntries(userEntries); 115 - setTotalCount(data.count || 0); 116 - setEmojiStats(data.emojiStats || []); 117 - 118 - // We now fetch profile data separately 119 235 120 236 // Calculate statistics and chart data 121 237 if (userEntries.length > 0) {