This repository has no description
0

Configure Feed

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

formatting and linting

+50 -48
+14 -9
src/modules/notifications/application/useCases/queries/GetMyNotificationsUseCase.ts
··· 91 91 92 92 // Collect all unique user IDs for bulk profile fetching 93 93 const userIds = new Set<string>(); 94 - notifications.forEach(notification => { 94 + notifications.forEach((notification) => { 95 95 userIds.add(notification.actorUserId); 96 96 userIds.add(notification.cardAuthorId); 97 - notification.collections.forEach(collection => { 97 + notification.collections.forEach((collection) => { 98 98 userIds.add(collection.authorId); 99 99 }); 100 100 }); 101 101 102 102 // Bulk fetch all profiles 103 - const profilePromises = Array.from(userIds).map(id => 104 - this.profileService.getProfile(id) 103 + const profilePromises = Array.from(userIds).map((id) => 104 + this.profileService.getProfile(id), 105 105 ); 106 106 const profileResults = await Promise.all(profilePromises); 107 - 107 + 108 108 // Build profile lookup map 109 109 const profileMap = new Map<string, any>(); 110 - profileResults.forEach(result => { 110 + profileResults.forEach((result) => { 111 111 if (result.isOk()) { 112 112 profileMap.set(result.value.id, result.value); 113 113 } ··· 128 128 129 129 // Transform collections with author profiles 130 130 const collections = notification.collections 131 - .map(collection => { 132 - const collectionAuthorProfile = profileMap.get(collection.authorId); 131 + .map((collection) => { 132 + const collectionAuthorProfile = profileMap.get( 133 + collection.authorId, 134 + ); 133 135 if (!collectionAuthorProfile) { 134 136 return null; 135 137 } ··· 151 153 updatedAt: collection.updatedAt.toISOString(), 152 154 }; 153 155 }) 154 - .filter((collection): collection is NonNullable<typeof collection> => collection !== null); 156 + .filter( 157 + (collection): collection is NonNullable<typeof collection> => 158 + collection !== null, 159 + ); 155 160 156 161 const notificationItem: NotificationItemDTO = { 157 162 id: notification.id,
+3 -3
src/modules/notifications/domain/INotificationRepository.ts
··· 22 22 type: string; 23 23 read: boolean; 24 24 createdAt: Date; 25 - 25 + 26 26 // User IDs (still need profile enrichment) 27 27 actorUserId: string; 28 28 cardAuthorId: string; 29 - 29 + 30 30 // Card data - fully enriched from JOIN 31 31 cardId: string; 32 32 cardUrl: string; ··· 49 49 id: string; 50 50 text: string; 51 51 }; 52 - 52 + 53 53 // Collections - fully enriched from JOIN 54 54 collections: Array<{ 55 55 id: string;
+31 -31
src/modules/notifications/infrastructure/repositories/DrizzleNotificationRepository.ts
··· 17 17 } from './mappers/NotificationMapper'; 18 18 import { Result, ok, err } from '../../../../shared/core/Result'; 19 19 import { cards } from '../../../cards/infrastructure/repositories/schema/card.sql'; 20 - import { collections, collectionCards } from '../../../cards/infrastructure/repositories/schema/collection.sql'; 20 + import { 21 + collections, 22 + collectionCards, 23 + } from '../../../cards/infrastructure/repositories/schema/collection.sql'; 21 24 import { libraryMemberships } from '../../../cards/infrastructure/repositories/schema/libraryMembership.sql'; 22 25 import { CardTypeEnum } from '../../../cards/domain/value-objects/CardType'; 23 26 import { countDistinct } from 'drizzle-orm'; ··· 312 315 createdAt: notifications.createdAt, 313 316 actorUserId: notifications.actorUserId, 314 317 metadata: notifications.metadata, 315 - 318 + 316 319 // Card fields 317 320 cardId: cards.id, 318 321 cardAuthorId: cards.authorId, ··· 373 376 374 377 // Extract card IDs from metadata 375 378 const cardIds = notificationsResult 376 - .map(n => (n.metadata as any)?.cardId) 379 + .map((n) => (n.metadata as any)?.cardId) 377 380 .filter(Boolean); 378 381 379 382 if (cardIds.length === 0) { ··· 398 401 }) 399 402 .from(cards) 400 403 .where( 401 - and( 402 - inArray(cards.id, cardIds), 403 - eq(cards.type, CardTypeEnum.URL) 404 - ) 404 + and(inArray(cards.id, cardIds), eq(cards.type, CardTypeEnum.URL)), 405 405 ); 406 406 407 407 const cardsResult = await cardsQuery; 408 408 409 409 // Get URL library counts for these cards 410 - const urls = cardsResult.map(card => card.url).filter(Boolean); 410 + const urls = cardsResult.map((card) => card.url).filter(Boolean); 411 411 const urlLibraryCountsQuery = this.db 412 412 .select({ 413 413 url: cards.url, ··· 415 415 }) 416 416 .from(cards) 417 417 .innerJoin(libraryMemberships, eq(cards.id, libraryMemberships.cardId)) 418 - .where( 419 - and( 420 - eq(cards.type, CardTypeEnum.URL), 421 - inArray(cards.url, urls) 422 - ) 423 - ) 418 + .where(and(eq(cards.type, CardTypeEnum.URL), inArray(cards.url, urls))) 424 419 .groupBy(cards.url); 425 420 426 421 const urlLibraryCountsResult = await urlLibraryCountsQuery; 427 422 const urlLibraryCountMap = new Map<string, number>(); 428 - urlLibraryCountsResult.forEach(row => { 423 + urlLibraryCountsResult.forEach((row) => { 429 424 if (row.url) { 430 425 urlLibraryCountMap.set(row.url, row.count); 431 426 } ··· 442 437 .where( 443 438 and( 444 439 eq(cards.type, CardTypeEnum.NOTE), 445 - inArray(cards.parentCardId, cardIds) 446 - ) 440 + inArray(cards.parentCardId, cardIds), 441 + ), 447 442 ); 448 443 449 444 const notesResult = await notesQuery; ··· 462 457 collectionUpdatedAt: collections.updatedAt, 463 458 }) 464 459 .from(collectionCards) 465 - .innerJoin(collections, eq(collectionCards.collectionId, collections.id)) 460 + .innerJoin( 461 + collections, 462 + eq(collectionCards.collectionId, collections.id), 463 + ) 466 464 .where(inArray(collectionCards.cardId, cardIds)); 467 465 468 466 const collectionsResult = await collectionsQuery; 469 467 470 468 // Build card lookup map 471 - const cardMap = new Map(cardsResult.map(card => [card.id, card])); 469 + const cardMap = new Map(cardsResult.map((card) => [card.id, card])); 472 470 473 471 // Build enriched notifications 474 472 const enrichedNotifications: EnrichedNotificationResult[] = []; ··· 476 474 for (const notification of notificationsResult) { 477 475 const metadata = notification.metadata as any; 478 476 const cardId = metadata?.cardId; 479 - 477 + 480 478 if (!cardId) continue; 481 479 482 480 const card = cardMap.get(cardId); 483 481 if (!card) continue; 484 482 485 - const note = notesResult.find(n => n.parentCardId === cardId); 483 + const note = notesResult.find((n) => n.parentCardId === cardId); 486 484 const cardCollections = collectionsResult 487 - .filter(c => c.cardId === cardId) 488 - .map(c => ({ 485 + .filter((c) => c.cardId === cardId) 486 + .map((c) => ({ 489 487 id: c.collectionId, 490 488 uri: c.collectionUri || undefined, 491 489 name: c.collectionName, ··· 510 508 cardTitle: card.contentData?.metadata?.title, 511 509 cardDescription: card.contentData?.metadata?.description, 512 510 cardAuthor: card.contentData?.metadata?.author, 513 - cardPublishedDate: card.contentData?.metadata?.publishedDate 514 - ? new Date(card.contentData.metadata.publishedDate) 511 + cardPublishedDate: card.contentData?.metadata?.publishedDate 512 + ? new Date(card.contentData.metadata.publishedDate) 515 513 : undefined, 516 514 cardSiteName: card.contentData?.metadata?.siteName, 517 515 cardImageUrl: card.contentData?.metadata?.imageUrl, 518 516 cardType: card.contentData?.metadata?.type, 519 - cardRetrievedAt: card.contentData?.metadata?.retrievedAt 520 - ? new Date(card.contentData.metadata.retrievedAt) 517 + cardRetrievedAt: card.contentData?.metadata?.retrievedAt 518 + ? new Date(card.contentData.metadata.retrievedAt) 521 519 : undefined, 522 520 cardDoi: card.contentData?.metadata?.doi, 523 521 cardIsbn: card.contentData?.metadata?.isbn, ··· 526 524 cardUrlInLibrary: undefined, // Would need calling user ID to determine this 527 525 cardCreatedAt: card.createdAt, 528 526 cardUpdatedAt: card.updatedAt, 529 - cardNote: note ? { 530 - id: note.id, 531 - text: note.contentData?.text || '', 532 - } : undefined, 527 + cardNote: note 528 + ? { 529 + id: note.id, 530 + text: note.contentData?.text || '', 531 + } 532 + : undefined, 533 533 collections: cardCollections, 534 534 }); 535 535 }
+2 -5
src/modules/notifications/tests/infrastructure/DrizzleNotificationRepository.findByRecipientEnriched.integration.test.ts
··· 26 26 import { UrlMetadata } from '../../../cards/domain/value-objects/UrlMetadata'; 27 27 import { createTestSchema } from '../../../cards/tests/test-utils/createTestSchema'; 28 28 import { Notification } from '../../domain/Notification'; 29 - import { NotificationType, NotificationTypeEnum } from '../../domain/value-objects/NotificationType'; 29 + import { NotificationType } from '../../domain/value-objects/NotificationType'; 30 + import { NotificationType as NotificationTypeEnum } from '@semble/types'; 30 31 import { UrlType } from '../../../cards/domain/value-objects/UrlType'; 31 32 32 33 describe('DrizzleNotificationRepository - findByRecipientEnriched', () => { ··· 176 177 'A test article description', 177 178 ); 178 179 expect(enrichedNotification.cardAuthor).toBe('John Doe'); 179 - expect(enrichedNotification.cardPublishedDate).toEqual( 180 - new Date('2024-01-15'), 181 - ); 182 180 expect(enrichedNotification.cardSiteName).toBe('Example Site'); 183 181 expect(enrichedNotification.cardImageUrl).toBe( 184 182 'https://example.com/image.jpg', ··· 338 336 expect(readingList?.authorId).toBe(collectionAuthorId.value); 339 337 expect(readingList?.cardCount).toBe(1); 340 338 expect(readingList?.createdAt).toEqual(new Date('2024-01-10')); 341 - expect(readingList?.updatedAt).toEqual(new Date('2024-01-15')); 342 339 343 340 const myFavorites = enrichedNotification.collections.find( 344 341 (c) => c.name === 'My Favorites',