This repository has no description
1import { err, ok, Result } from 'src/shared/core/Result';
2import { UseCase } from 'src/shared/core/UseCase';
3import { ICollectionQueryRepository } from '../../../domain/ICollectionQueryRepository';
4import { IProfileService } from 'src/modules/cards/domain/services/IProfileService';
5import { ICollectionRepository } from 'src/modules/cards/domain/ICollectionRepository';
6import { CollectionId } from 'src/modules/cards/domain/value-objects/CollectionId';
7import { ContributorUser } from '@semble/types';
8import { ProfileEnricher } from 'src/modules/cards/application/services/ProfileEnricher';
9
10export interface GetCollectionContributorsQuery {
11 collectionId: string; // Collection UUID
12 callingUserId?: string;
13 page?: number;
14 limit?: number;
15}
16
17export interface GetCollectionContributorsResult {
18 users: ContributorUser[];
19 pagination: {
20 currentPage: number;
21 totalPages: number;
22 totalCount: number;
23 hasMore: boolean;
24 limit: number;
25 };
26}
27
28export class ValidationError extends Error {
29 constructor(message: string) {
30 super(message);
31 this.name = 'ValidationError';
32 }
33}
34
35export class GetCollectionContributorsUseCase
36 implements
37 UseCase<
38 GetCollectionContributorsQuery,
39 Result<GetCollectionContributorsResult>
40 >
41{
42 constructor(
43 private collectionQueryRepository: ICollectionQueryRepository,
44 private profileService: IProfileService,
45 private collectionRepository: ICollectionRepository,
46 ) {}
47
48 async execute(
49 query: GetCollectionContributorsQuery,
50 ): Promise<Result<GetCollectionContributorsResult>> {
51 // Set defaults
52 const page = query.page || 1;
53 const limit = Math.min(query.limit || 20, 100); // Cap at 100
54
55 // Validate collection ID
56 const collectionIdResult = CollectionId.createFromString(
57 query.collectionId,
58 );
59 if (collectionIdResult.isErr()) {
60 return err(
61 new ValidationError(
62 `Invalid collection ID: ${collectionIdResult.error.message}`,
63 ),
64 );
65 }
66
67 try {
68 // Verify collection exists
69 const collectionResult = await this.collectionRepository.findById(
70 collectionIdResult.value,
71 );
72 if (collectionResult.isErr()) {
73 return err(
74 new Error(
75 `Failed to fetch collection: ${collectionResult.error instanceof Error ? collectionResult.error.message : 'Unknown error'}`,
76 ),
77 );
78 }
79 if (!collectionResult.value) {
80 return err(new ValidationError('Collection not found'));
81 }
82
83 const collection = collectionResult.value;
84 const authorId = collection.authorId.value;
85
86 // Get contributors from repository (excluding author)
87 const contributorsResult =
88 await this.collectionQueryRepository.getCollectionContributors(
89 query.collectionId,
90 authorId,
91 { page, limit },
92 );
93
94 const totalCount = contributorsResult.totalCount;
95
96 // Extract unique contributor IDs
97 const uniqueContributorIds = contributorsResult.items.map(
98 (item) => item.userId,
99 );
100
101 if (uniqueContributorIds.length === 0) {
102 return ok({
103 users: [],
104 pagination: {
105 currentPage: page,
106 totalPages: 0,
107 totalCount: 0,
108 hasMore: false,
109 limit,
110 },
111 });
112 }
113
114 // Fetch profiles for all contributors using ProfileEnricher
115 const profileEnricher = new ProfileEnricher(this.profileService);
116 const profileMapResult = await profileEnricher.buildProfileMap(
117 uniqueContributorIds,
118 query.callingUserId,
119 {
120 skipFailures: false, // Fail if any profile fetch fails
121 mapToUser: true, // Use full User DTO with isFollowing
122 },
123 );
124
125 if (profileMapResult.isErr()) {
126 return err(
127 new Error(
128 `Failed to fetch user profiles: ${profileMapResult.error.message}`,
129 ),
130 );
131 }
132
133 const profileMap = profileMapResult.value;
134
135 // Build users array in the order of contributors (chronological by most recent contribution)
136 // Add contributionCount to each user
137 const users: ContributorUser[] = contributorsResult.items
138 .map((item) => {
139 const user = profileMap.get(item.userId);
140 if (!user) {
141 return undefined;
142 }
143 return {
144 ...user,
145 contributionCount: item.contributionCount,
146 };
147 })
148 .filter((user): user is ContributorUser => user !== undefined);
149
150 return ok({
151 users,
152 pagination: {
153 currentPage: page,
154 totalPages: Math.ceil(totalCount / limit),
155 totalCount,
156 hasMore: page * limit < totalCount,
157 limit,
158 },
159 });
160 } catch (error) {
161 return err(
162 new Error(
163 `Failed to retrieve collection contributors: ${error instanceof Error ? error.message : 'Unknown error'}`,
164 ),
165 );
166 }
167 }
168}