This repository has no description
0

Configure Feed

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

Merge pull request #397 from cosmik-network/fix/unified-url-metadata-types-in-frontend-20260105

unify url metadata between frontend and backend

+175 -54
+13 -1
src/modules/cards/application/useCases/queries/GetLibrariesForUrlUseCase.ts
··· 128 128 id: item.card.id, 129 129 type: 'URL', 130 130 url: item.card.url, 131 - cardContent: item.card.cardContent, 131 + cardContent: { 132 + url: item.card.cardContent.url, 133 + title: item.card.cardContent.title, 134 + description: item.card.cardContent.description, 135 + author: item.card.cardContent.author, 136 + publishedDate: item.card.cardContent.publishedDate?.toISOString(), 137 + siteName: item.card.cardContent.siteName, 138 + imageUrl: item.card.cardContent.imageUrl, 139 + type: item.card.cardContent.type, 140 + retrievedAt: item.card.cardContent.retrievedAt?.toISOString(), 141 + doi: item.card.cardContent.doi, 142 + isbn: item.card.cardContent.isbn, 143 + }, 132 144 libraryCount: item.card.libraryCount, 133 145 urlLibraryCount: item.card.urlLibraryCount, 134 146 urlInLibrary: item.card.urlInLibrary,
+13 -1
src/modules/cards/application/useCases/queries/GetUrlStatusForMyLibraryUseCase.ts
··· 118 118 id: cardView.id, 119 119 type: 'URL', 120 120 url: cardView.url, 121 - cardContent: cardView.cardContent, 121 + cardContent: { 122 + url: cardView.cardContent.url, 123 + title: cardView.cardContent.title, 124 + description: cardView.cardContent.description, 125 + author: cardView.cardContent.author, 126 + publishedDate: cardView.cardContent.publishedDate?.toISOString(), 127 + siteName: cardView.cardContent.siteName, 128 + imageUrl: cardView.cardContent.imageUrl, 129 + type: cardView.cardContent.type, 130 + retrievedAt: cardView.cardContent.retrievedAt?.toISOString(), 131 + doi: cardView.cardContent.doi, 132 + isbn: cardView.cardContent.isbn, 133 + }, 122 134 libraryCount: cardView.libraryCount, 123 135 urlLibraryCount: cardView.urlLibraryCount, 124 136 urlInLibrary: cardView.urlInLibrary,
+14 -2
src/modules/cards/domain/ICardQueryRepository.ts
··· 36 36 title?: string; 37 37 description?: string; 38 38 author?: string; 39 - thumbnailUrl?: string; 39 + publishedDate?: Date; 40 + siteName?: string; 41 + imageUrl?: string; 42 + type?: string; 43 + retrievedAt?: Date; 44 + doi?: string; 45 + isbn?: string; 40 46 }; 41 47 libraryCount: number; 42 48 urlLibraryCount: number; ··· 75 81 title?: string; 76 82 description?: string; 77 83 author?: string; 78 - thumbnailUrl?: string; 84 + publishedDate?: Date; 85 + siteName?: string; 86 + imageUrl?: string; 87 + type?: string; 88 + retrievedAt?: Date; 89 + doi?: string; 90 + isbn?: string; 79 91 }; 80 92 libraryCount: number; 81 93 urlLibraryCount: number;
+37 -3
src/modules/cards/infrastructure/repositories/mappers/CardMapper.ts
··· 29 29 imageUrl?: string; 30 30 type?: string; 31 31 retrievedAt: string; 32 + doi?: string; 33 + isbn?: string; 32 34 }; 33 35 } 34 36 ··· 291 293 imageUrl: urlContent.metadata.imageUrl, 292 294 type: urlContent.metadata.type, 293 295 retrievedAt: urlContent.metadata.retrievedAt?.toISOString(), 296 + doi: urlContent.metadata.doi, 297 + isbn: urlContent.metadata.isbn, 294 298 } 295 299 : undefined, 296 300 } as UrlContentData; ··· 374 378 title: raw.contentData?.metadata?.title, 375 379 description: raw.contentData?.metadata?.description, 376 380 author: raw.contentData?.metadata?.author, 377 - thumbnailUrl: raw.contentData?.metadata?.imageUrl, 381 + publishedDate: raw.contentData?.metadata?.publishedDate 382 + ? new Date(raw.contentData.metadata.publishedDate) 383 + : undefined, 384 + siteName: raw.contentData?.metadata?.siteName, 385 + imageUrl: raw.contentData?.metadata?.imageUrl, 386 + type: raw.contentData?.metadata?.type, 387 + retrievedAt: raw.contentData?.metadata?.retrievedAt 388 + ? new Date(raw.contentData.metadata.retrievedAt) 389 + : undefined, 390 + doi: raw.contentData?.metadata?.doi, 391 + isbn: raw.contentData?.metadata?.isbn, 378 392 }; 379 393 380 394 // Extract note text from note's contentData ··· 422 436 title: raw.contentData?.metadata?.title, 423 437 description: raw.contentData?.metadata?.description, 424 438 author: raw.contentData?.metadata?.author, 425 - thumbnailUrl: raw.contentData?.metadata?.imageUrl, 439 + publishedDate: raw.contentData?.metadata?.publishedDate 440 + ? new Date(raw.contentData.metadata.publishedDate) 441 + : undefined, 442 + siteName: raw.contentData?.metadata?.siteName, 443 + imageUrl: raw.contentData?.metadata?.imageUrl, 444 + type: raw.contentData?.metadata?.type, 445 + retrievedAt: raw.contentData?.metadata?.retrievedAt 446 + ? new Date(raw.contentData.metadata.retrievedAt) 447 + : undefined, 448 + doi: raw.contentData?.metadata?.doi, 449 + isbn: raw.contentData?.metadata?.isbn, 426 450 }; 427 451 428 452 // Extract note text from note's contentData ··· 478 502 title: raw.contentData?.metadata?.title, 479 503 description: raw.contentData?.metadata?.description, 480 504 author: raw.contentData?.metadata?.author, 481 - thumbnailUrl: raw.contentData?.metadata?.imageUrl, 505 + publishedDate: raw.contentData?.metadata?.publishedDate 506 + ? new Date(raw.contentData.metadata.publishedDate) 507 + : undefined, 508 + siteName: raw.contentData?.metadata?.siteName, 509 + imageUrl: raw.contentData?.metadata?.imageUrl, 510 + type: raw.contentData?.metadata?.type, 511 + retrievedAt: raw.contentData?.metadata?.retrievedAt 512 + ? new Date(raw.contentData.metadata.retrievedAt) 513 + : undefined, 514 + doi: raw.contentData?.metadata?.doi, 515 + isbn: raw.contentData?.metadata?.isbn, 482 516 }; 483 517 484 518 // Extract note text from note's contentData
+22 -2
src/modules/cards/infrastructure/repositories/query-services/UrlCardQueryService.ts
··· 694 694 title: lib.contentData?.metadata?.title, 695 695 description: lib.contentData?.metadata?.description, 696 696 author: lib.contentData?.metadata?.author, 697 - thumbnailUrl: lib.contentData?.metadata?.imageUrl, 697 + publishedDate: lib.contentData?.metadata?.publishedDate 698 + ? new Date(lib.contentData.metadata.publishedDate) 699 + : undefined, 700 + siteName: lib.contentData?.metadata?.siteName, 701 + imageUrl: lib.contentData?.metadata?.imageUrl, 702 + type: lib.contentData?.metadata?.type, 703 + retrievedAt: lib.contentData?.metadata?.retrievedAt 704 + ? new Date(lib.contentData.metadata.retrievedAt) 705 + : undefined, 706 + doi: lib.contentData?.metadata?.doi, 707 + isbn: lib.contentData?.metadata?.isbn, 698 708 }, 699 709 libraryCount: lib.libraryCount, 700 710 urlLibraryCount: lib.urlLibraryCount, ··· 801 811 title: lib.contentData?.metadata?.title, 802 812 description: lib.contentData?.metadata?.description, 803 813 author: lib.contentData?.metadata?.author, 804 - thumbnailUrl: lib.contentData?.metadata?.imageUrl, 814 + publishedDate: lib.contentData?.metadata?.publishedDate 815 + ? new Date(lib.contentData.metadata.publishedDate) 816 + : undefined, 817 + siteName: lib.contentData?.metadata?.siteName, 818 + imageUrl: lib.contentData?.metadata?.imageUrl, 819 + type: lib.contentData?.metadata?.type, 820 + retrievedAt: lib.contentData?.metadata?.retrievedAt 821 + ? new Date(lib.contentData.metadata.retrievedAt) 822 + : undefined, 823 + doi: lib.contentData?.metadata?.doi, 824 + isbn: lib.contentData?.metadata?.isbn, 805 825 }, 806 826 libraryCount: lib.libraryCount, 807 827 urlLibraryCount,
+1 -1
src/modules/cards/tests/application/GetCollectionPageUseCase.test.ts
··· 839 839 expect(response.urlCards[0]?.cardContent.title).toBeUndefined(); 840 840 expect(response.urlCards[0]?.cardContent.description).toBeUndefined(); 841 841 expect(response.urlCards[0]?.cardContent.author).toBeUndefined(); 842 - expect(response.urlCards[0]?.cardContent.thumbnailUrl).toBeUndefined(); 842 + expect(response.urlCards[0]?.cardContent.imageUrl).toBeUndefined(); 843 843 }); 844 844 845 845 it('should handle author profile with minimal data', async () => {
+1 -1
src/modules/cards/tests/application/GetMyUrlCardsUseCase.test.ts
··· 735 735 expect(response.cards[0]?.cardContent.title).toBeUndefined(); 736 736 expect(response.cards[0]?.cardContent.description).toBeUndefined(); 737 737 expect(response.cards[0]?.cardContent.author).toBeUndefined(); 738 - expect(response.cards[0]?.cardContent.thumbnailUrl).toBeUndefined(); 738 + expect(response.cards[0]?.cardContent.imageUrl).toBeUndefined(); 739 739 expect(response.cards[0]?.author.id).toBe(curatorId.value); 740 740 expect(response.cards[0]?.author.name).toBe('Test Curator'); 741 741 });
+2 -2
src/modules/cards/tests/application/GetUrlCardViewUseCase.test.ts
··· 152 152 'Description of test article', 153 153 ); 154 154 expect(response.cardContent.author).toBe('John Doe'); 155 - expect(response.cardContent.thumbnailUrl).toBe( 155 + expect(response.cardContent.imageUrl).toBe( 156 156 'https://example.com/thumb1.jpg', 157 157 ); 158 158 expect(response.libraryCount).toBe(1); ··· 268 268 expect(response.cardContent.title).toBeUndefined(); 269 269 expect(response.cardContent.description).toBeUndefined(); 270 270 expect(response.cardContent.author).toBeUndefined(); 271 - expect(response.cardContent.thumbnailUrl).toBeUndefined(); 271 + expect(response.cardContent.imageUrl).toBeUndefined(); 272 272 expect(response.libraries).toHaveLength(1); 273 273 }); 274 274
+1 -1
src/modules/cards/tests/infrastructure/CitoidMetadataService.integration.test.ts
··· 40 40 expect(metadata.description).not.toBe(''); 41 41 42 42 // arXiv specific checks 43 - expect(metadata.type).toBe('preprint'); 43 + expect(metadata.type).toBe('research'); 44 44 expect(metadata.siteName).toContain('arXiv'); 45 45 46 46 // Date should be parsed correctly
+1 -1
src/modules/cards/tests/infrastructure/DrizzleCardQueryRepository.getCardsInCollection.integration.test.ts
··· 183 183 'First article in collection', 184 184 ); 185 185 expect(card1Result?.cardContent.author).toBe('John Doe'); 186 - expect(card1Result?.cardContent.thumbnailUrl).toBe( 186 + expect(card1Result?.cardContent.imageUrl).toBe( 187 187 'https://example.com/image1.jpg', 188 188 ); 189 189
+2 -2
src/modules/cards/tests/infrastructure/DrizzleCardQueryRepository.getUrlCardBasic.integration.test.ts
··· 119 119 'A test article description', 120 120 ); 121 121 expect(result?.cardContent.author).toBe('John Doe'); 122 - expect(result?.cardContent.thumbnailUrl).toBe( 122 + expect(result?.cardContent.imageUrl).toBe( 123 123 'https://example.com/image.jpg', 124 124 ); 125 125 expect(result?.note).toBeUndefined(); ··· 204 204 expect(result?.cardContent.title).toBeUndefined(); 205 205 expect(result?.cardContent.description).toBeUndefined(); 206 206 expect(result?.cardContent.author).toBeUndefined(); 207 - expect(result?.cardContent.thumbnailUrl).toBeUndefined(); 207 + expect(result?.cardContent.imageUrl).toBeUndefined(); 208 208 expect(result?.note).toBeUndefined(); 209 209 }); 210 210 });
+4 -4
src/modules/cards/tests/infrastructure/DrizzleCardQueryRepository.getUrlCardView.integration.test.ts
··· 131 131 'A test article description', 132 132 ); 133 133 expect(result?.cardContent.author).toBe('John Doe'); 134 - expect(result?.cardContent.thumbnailUrl).toBe( 134 + expect(result?.cardContent.imageUrl).toBe( 135 135 'https://example.com/image.jpg', 136 136 ); 137 137 expect(result?.libraries).toEqual([]); ··· 159 159 expect(result?.cardContent.title).toBeUndefined(); 160 160 expect(result?.cardContent.description).toBeUndefined(); 161 161 expect(result?.cardContent.author).toBeUndefined(); 162 - expect(result?.cardContent.thumbnailUrl).toBeUndefined(); 162 + expect(result?.cardContent.imageUrl).toBeUndefined(); 163 163 }); 164 164 165 165 it('should include creator in library when URL card is in their library', async () => { ··· 337 337 'An article with everything', 338 338 ); 339 339 expect(result?.cardContent.author).toBe('Jane Smith'); 340 - expect(result?.cardContent.thumbnailUrl).toBe( 340 + expect(result?.cardContent.imageUrl).toBe( 341 341 'https://example.com/comprehensive.jpg', 342 342 ); 343 343 ··· 581 581 'An article that has a connected note', 582 582 ); 583 583 expect(result?.cardContent.author).toBe('Jane Doe'); 584 - expect(result?.cardContent.thumbnailUrl).toBe( 584 + expect(result?.cardContent.imageUrl).toBe( 585 585 'https://example.com/note-article.jpg', 586 586 ); 587 587
+2 -2
src/modules/cards/tests/infrastructure/DrizzleCardQueryRepository.getUrlCardsOfUser.integration.test.ts
··· 152 152 'A great article about testing', 153 153 ); 154 154 expect(card1Result?.cardContent.author).toBe('John Doe'); 155 - expect(card1Result?.cardContent.thumbnailUrl).toBe( 155 + expect(card1Result?.cardContent.imageUrl).toBe( 156 156 'https://example.com/image1.jpg', 157 157 ); 158 158 ··· 479 479 'An article with notes and collections', 480 480 ); 481 481 expect(urlCardResult?.cardContent.author).toBe('Jane Smith'); 482 - expect(urlCardResult?.cardContent.thumbnailUrl).toBe( 482 + expect(urlCardResult?.cardContent.imageUrl).toBe( 483 483 'https://example.com/complex.jpg', 484 484 ); 485 485
+3 -3
src/modules/cards/tests/utils/InMemoryCardQueryRepository.ts
··· 156 156 title: card.content.urlContent.metadata?.title, 157 157 description: card.content.urlContent.metadata?.description, 158 158 author: card.content.urlContent.metadata?.author, 159 - thumbnailUrl: card.content.urlContent.metadata?.imageUrl, 159 + imageUrl: card.content.urlContent.metadata?.imageUrl, 160 160 }, 161 161 libraryCount: this.getLibraryCountForCard(card.cardId.getStringValue()), 162 162 urlLibraryCount: this.getUrlLibraryCount( ··· 407 407 title: card.content.urlContent!.metadata?.title, 408 408 description: card.content.urlContent!.metadata?.description, 409 409 author: card.content.urlContent!.metadata?.author, 410 - thumbnailUrl: card.content.urlContent!.metadata?.imageUrl, 410 + imageUrl: card.content.urlContent!.metadata?.imageUrl, 411 411 }, 412 412 libraryCount: this.getLibraryCountForCard(card.cardId.getStringValue()), 413 413 urlLibraryCount: this.getUrlLibraryCount( ··· 470 470 title: card.content.urlContent.metadata?.title, 471 471 description: card.content.urlContent.metadata?.description, 472 472 author: card.content.urlContent.metadata?.author, 473 - thumbnailUrl: card.content.urlContent.metadata?.imageUrl, 473 + imageUrl: card.content.urlContent.metadata?.imageUrl, 474 474 }, 475 475 libraryCount: this.getLibraryCountForCard( 476 476 card.cardId.getStringValue(),
+13 -1
src/modules/feeds/application/useCases/queries/GetGemActivityFeedUseCase.ts
··· 385 385 id: cardView.id, 386 386 type: 'URL' as const, 387 387 url: cardView.url, 388 - cardContent: cardView.cardContent, 388 + cardContent: { 389 + url: cardView.cardContent.url, 390 + title: cardView.cardContent.title, 391 + description: cardView.cardContent.description, 392 + author: cardView.cardContent.author, 393 + publishedDate: cardView.cardContent.publishedDate?.toISOString(), 394 + siteName: cardView.cardContent.siteName, 395 + imageUrl: cardView.cardContent.imageUrl, 396 + type: cardView.cardContent.type, 397 + retrievedAt: cardView.cardContent.retrievedAt?.toISOString(), 398 + doi: cardView.cardContent.doi, 399 + isbn: cardView.cardContent.isbn, 400 + }, 389 401 libraryCount: cardView.libraryCount, 390 402 urlLibraryCount: cardView.urlLibraryCount, 391 403 urlInLibrary: cardView.urlInLibrary,
+13 -1
src/modules/feeds/application/useCases/queries/GetGlobalFeedUseCase.ts
··· 326 326 id: cardView.id, 327 327 type: 'URL' as const, 328 328 url: cardView.url, 329 - cardContent: cardView.cardContent, 329 + cardContent: { 330 + url: cardView.cardContent.url, 331 + title: cardView.cardContent.title, 332 + description: cardView.cardContent.description, 333 + author: cardView.cardContent.author, 334 + publishedDate: cardView.cardContent.publishedDate?.toISOString(), 335 + siteName: cardView.cardContent.siteName, 336 + imageUrl: cardView.cardContent.imageUrl, 337 + type: cardView.cardContent.type, 338 + retrievedAt: cardView.cardContent.retrievedAt?.toISOString(), 339 + doi: cardView.cardContent.doi, 340 + isbn: cardView.cardContent.isbn, 341 + }, 330 342 libraryCount: cardView.libraryCount, 331 343 urlLibraryCount: cardView.urlLibraryCount, 332 344 urlInLibrary: cardView.urlInLibrary,
+14 -1
src/modules/notifications/application/useCases/queries/GetMyNotificationsUseCase.ts
··· 174 174 id: cardView.id, 175 175 type: 'URL' as const, 176 176 url: cardView.url, 177 - cardContent: cardView.cardContent, 177 + cardContent: { 178 + url: cardView.cardContent.url, 179 + title: cardView.cardContent.title, 180 + description: cardView.cardContent.description, 181 + author: cardView.cardContent.author, 182 + publishedDate: 183 + cardView.cardContent.publishedDate?.toISOString(), 184 + siteName: cardView.cardContent.siteName, 185 + imageUrl: cardView.cardContent.imageUrl, 186 + type: cardView.cardContent.type, 187 + retrievedAt: cardView.cardContent.retrievedAt?.toISOString(), 188 + doi: cardView.cardContent.doi, 189 + isbn: cardView.cardContent.isbn, 190 + }, 178 191 libraryCount: cardView.libraryCount, 179 192 urlLibraryCount: cardView.urlLibraryCount, 180 193 urlInLibrary: cardView.urlInLibrary,
+3 -1
src/modules/search/domain/services/SearchService.ts
··· 168 168 siteName: result.metadata.siteName, 169 169 imageUrl: result.metadata.imageUrl, 170 170 type: result.metadata.type, 171 - thumbnailUrl: result.metadata.imageUrl, // Use imageUrl as thumbnailUrl 171 + retrievedAt: result.metadata.retrievedAt?.toISOString(), 172 + doi: result.metadata.doi, 173 + isbn: result.metadata.isbn, 172 174 }, 173 175 urlLibraryCount, 174 176 urlInLibrary,
+6 -14
src/types/src/api/responses.ts
··· 57 57 title?: string; 58 58 description?: string; 59 59 author?: string; 60 + publishedDate?: string; 60 61 siteName?: string; 61 62 imageUrl?: string; 62 63 type?: string; 64 + retrievedAt?: string; 65 + doi?: string; 66 + isbn?: string; 63 67 } 64 68 65 69 export interface GetUrlMetadataResponse { ··· 71 75 id: string; 72 76 type: 'URL'; 73 77 url: string; 74 - cardContent: { 75 - url: string; 76 - title?: string; 77 - description?: string; 78 - author?: string; 79 - thumbnailUrl?: string; 80 - }; 78 + cardContent: UrlMetadata; 81 79 libraryCount: number; 82 80 urlLibraryCount: number; 83 81 urlInLibrary?: boolean; ··· 226 224 // Search response types 227 225 export interface UrlView { 228 226 url: string; 229 - metadata: { 230 - url: string; 231 - title?: string; 232 - description?: string; 233 - author?: string; 234 - thumbnailUrl?: string; 235 - }; 227 + metadata: UrlMetadata; 236 228 urlLibraryCount: number; 237 229 urlInLibrary?: boolean; 238 230 }
+1 -1
src/webapp/features/cards/components/addCardToModal/AddCardToModalContent.tsx
··· 138 138 <CardToBeAddedPreview 139 139 url={props.url} 140 140 title={props.cardContent?.title} 141 - thumbnailUrl={props.cardContent?.thumbnailUrl} 141 + imageUrl={props.cardContent?.imageUrl} 142 142 /> 143 143 <AddCardActions 144 144 note={isMyCard ? note : cardStatus.data.card?.note?.text}
+3 -3
src/webapp/features/cards/components/cardToBeAddedPreview/CardToBeAddedPreview.tsx
··· 14 14 15 15 interface Props { 16 16 url: string; 17 - thumbnailUrl?: string; 17 + imageUrl?: string; 18 18 title?: string; 19 19 } 20 20 ··· 25 25 <Card withBorder component="article" p={'xs'} radius={'lg'}> 26 26 <Stack> 27 27 <Group gap={'sm'} justify="space-between"> 28 - {props.thumbnailUrl && ( 28 + {props.imageUrl && ( 29 29 <AspectRatio ratio={1 / 1} flex={0.1}> 30 30 <Image 31 - src={props.thumbnailUrl} 31 + src={props.imageUrl} 32 32 alt={`${props.url} social preview image`} 33 33 radius={'md'} 34 34 w={45}
+2 -2
src/webapp/features/cards/components/urlCardContent/LinkCardContent.tsx
··· 61 61 </Text> 62 62 )} 63 63 </Stack> 64 - {props.cardContent.thumbnailUrl && !imageError && ( 64 + {props.cardContent.imageUrl && !imageError && ( 65 65 <AspectRatio ratio={1 / 1}> 66 66 <Image 67 - src={props.cardContent.thumbnailUrl} 67 + src={props.cardContent.imageUrl} 68 68 alt={`${props.cardContent.url} social preview image`} 69 69 radius={'md'} 70 70 w={settings.cardView === 'list' ? 45 : 75}
+2 -2
src/webapp/features/collections/components/collectionCardPreview/CollectionCardPreview.tsx
··· 24 24 <Grid gutter={'xs'}> 25 25 {cards.map((c) => ( 26 26 <Grid.Col key={c.id} span={3}> 27 - {c.cardContent.thumbnailUrl && !imageError ? ( 27 + {c.cardContent.imageUrl && !imageError ? ( 28 28 <AspectRatio ratio={16 / 9}> 29 29 <Image 30 - src={c.cardContent.thumbnailUrl} 30 + src={c.cardContent.imageUrl} 31 31 alt={`${c.cardContent.url} social preview image`} 32 32 radius={'md'} 33 33 h={45}
+2 -2
src/webapp/features/notes/components/noteCardModal/NoteCardModalContent.tsx
··· 152 152 <Card withBorder component="article" p={'xs'} radius={'lg'}> 153 153 <Stack> 154 154 <Group gap={'sm'} justify="space-between"> 155 - {props.cardContent.thumbnailUrl && ( 155 + {props.cardContent.imageUrl && ( 156 156 <AspectRatio ratio={1 / 1} flex={0.1}> 157 157 <Image 158 - src={props.cardContent.thumbnailUrl} 158 + src={props.cardContent.imageUrl} 159 159 alt={`${props.cardContent.url} social preview image`} 160 160 radius={'md'} 161 161 w={45}