This repository has no description
0

Configure Feed

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

semble / src / modules / user / application / useCases / queries / GetFollowingUsersUseCase.ts
4.3 kB 137 lines
1import { err, ok, Result } from 'src/shared/core/Result'; 2import { UseCase } from 'src/shared/core/UseCase'; 3import { IFollowsRepository } from '../../../domain/repositories/IFollowsRepository'; 4import { IIdentityResolutionService } from 'src/modules/atproto/domain/services/IIdentityResolutionService'; 5import { IProfileService } from 'src/modules/cards/domain/services/IProfileService'; 6import { DIDOrHandle } from 'src/modules/atproto/domain/DIDOrHandle'; 7import { FollowTargetType } from '../../../domain/value-objects/FollowTargetType'; 8import { User } from '@semble/types'; 9import { ProfileEnricher } from 'src/modules/cards/application/services/ProfileEnricher'; 10 11export interface GetFollowingUsersQuery { 12 userId: string; // DID or handle 13 callingUserId?: string; 14 page?: number; 15 limit?: number; 16} 17 18export interface GetFollowingUsersResult { 19 users: User[]; 20 pagination: { 21 currentPage: number; 22 totalPages: number; 23 totalCount: number; 24 hasMore: boolean; 25 limit: number; 26 }; 27} 28 29export class ValidationError extends Error { 30 constructor(message: string) { 31 super(message); 32 this.name = 'ValidationError'; 33 } 34} 35 36export class GetFollowingUsersUseCase 37 implements UseCase<GetFollowingUsersQuery, Result<GetFollowingUsersResult>> 38{ 39 constructor( 40 private followsRepository: IFollowsRepository, 41 private identityResolver: IIdentityResolutionService, 42 private profileService: IProfileService, 43 ) {} 44 45 async execute( 46 query: GetFollowingUsersQuery, 47 ): Promise<Result<GetFollowingUsersResult>> { 48 // Set defaults 49 const page = query.page || 1; 50 const limit = Math.min(query.limit || 20, 100); // Cap at 100 51 52 // Parse and validate user identifier 53 const identifierResult = DIDOrHandle.create(query.userId); 54 if (identifierResult.isErr()) { 55 return err(new ValidationError('Invalid user identifier')); 56 } 57 58 // Resolve to DID 59 const didResult = await this.identityResolver.resolveToDID( 60 identifierResult.value, 61 ); 62 if (didResult.isErr()) { 63 return err( 64 new ValidationError( 65 `Could not resolve user identifier: ${didResult.error.message}`, 66 ), 67 ); 68 } 69 70 try { 71 // Get paginated list of users that this user follows 72 const followsResult = await this.followsRepository.getFollowing( 73 didResult.value.value, 74 FollowTargetType.USER, 75 { page, limit }, 76 ); 77 78 if (followsResult.isErr()) { 79 return err( 80 new Error( 81 `Failed to retrieve following users: ${followsResult.error instanceof Error ? followsResult.error.message : 'Unknown error'}`, 82 ), 83 ); 84 } 85 86 const { follows, totalCount } = followsResult.value; 87 88 // Extract unique target IDs (the users being followed) 89 const uniqueTargetIds = Array.from( 90 new Set(follows.map((follow) => follow.targetId)), 91 ); 92 93 // Fetch profiles for all followed users using ProfileEnricher 94 const profileEnricher = new ProfileEnricher(this.profileService); 95 const profileMapResult = await profileEnricher.buildProfileMap( 96 uniqueTargetIds, 97 query.callingUserId, 98 { 99 skipFailures: false, // Fail if any profile fetch fails (preserving original behavior) 100 mapToUser: true, // Use full User DTO with isFollowing 101 }, 102 ); 103 104 if (profileMapResult.isErr()) { 105 return err( 106 new Error( 107 `Failed to fetch user profiles: ${profileMapResult.error.message}`, 108 ), 109 ); 110 } 111 112 const profileMap = profileMapResult.value; 113 114 // Build users array in the order of follows (chronological) 115 const users: User[] = follows 116 .map((follow) => profileMap.get(follow.targetId)) 117 .filter((user): user is User => user !== undefined); 118 119 return ok({ 120 users, 121 pagination: { 122 currentPage: page, 123 totalPages: Math.ceil(totalCount / limit), 124 totalCount, 125 hasMore: page * limit < totalCount, 126 limit, 127 }, 128 }); 129 } catch (error) { 130 return err( 131 new Error( 132 `Failed to retrieve following users: ${error instanceof Error ? error.message : 'Unknown error'}`, 133 ), 134 ); 135 } 136 } 137}