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