WIP PWA for Grain
0
fork

Configure Feed

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

feat: add getFollowers API method

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

+66
+66
src/services/grain-api.js
··· 209 209 return response.data?.socialGrainGraphFollow?.totalCount || 0; 210 210 } 211 211 212 + async getFollowers(handle, { first = 20, after = null } = {}) { 213 + // First get the user's DID 214 + const profileQuery = ` 215 + query GetDid($handle: String!) { 216 + socialGrainActorProfile(first: 1, where: { actorHandle: { eq: $handle } }) { 217 + edges { 218 + node { did } 219 + } 220 + } 221 + } 222 + `; 223 + const profileResponse = await this.#execute(profileQuery, { handle }); 224 + const did = profileResponse.data?.socialGrainActorProfile?.edges?.[0]?.node?.did; 225 + 226 + if (!did) { 227 + return { profiles: [], pageInfo: { hasNextPage: false, endCursor: null }, totalCount: 0 }; 228 + } 229 + 230 + // Query followers (people who follow this user) 231 + const query = ` 232 + query GetFollowers($did: String!, $first: Int, $after: String) { 233 + socialGrainGraphFollow( 234 + first: $first 235 + after: $after 236 + where: { subject: { eq: $did } } 237 + sortBy: [{ field: createdAt, direction: DESC }] 238 + ) { 239 + edges { 240 + node { 241 + appBskyActorProfileByDid { 242 + actorHandle 243 + displayName 244 + description 245 + avatar { url } 246 + } 247 + } 248 + } 249 + pageInfo { 250 + hasNextPage 251 + endCursor 252 + } 253 + totalCount 254 + } 255 + } 256 + `; 257 + 258 + const response = await this.#execute(query, { did, first, after }); 259 + const connection = response.data?.socialGrainGraphFollow; 260 + 261 + const profiles = connection?.edges 262 + ?.map(edge => edge.node.appBskyActorProfileByDid) 263 + ?.filter(Boolean) 264 + ?.map(profile => ({ 265 + handle: profile.actorHandle, 266 + displayName: profile.displayName || '', 267 + description: profile.description || '', 268 + avatarUrl: profile.avatar?.url || '' 269 + })) || []; 270 + 271 + return { 272 + profiles, 273 + pageInfo: connection?.pageInfo || { hasNextPage: false, endCursor: null }, 274 + totalCount: connection?.totalCount || 0 275 + }; 276 + } 277 + 212 278 async getGalleryDetail(handle, rkey) { 213 279 const query = ` 214 280 query GetGalleryDetail($handle: String!, $rkey: String!) {