Skip to content

Commit 24433fe

Browse files
committed
Only show images included in metadata JSON in landing page carousel
1 parent 2771d41 commit 24433fe

File tree

9 files changed

+204
-170
lines changed

9 files changed

+204
-170
lines changed

src/app/features/containers/components/search-list-2/search-list.component.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import { ThemeService } from "../../../../services/theme.service";
66
import { TabsComponent } from "../../../../shared/components/tabs/tabs.component";
77
import { ContainerIconComponent } from "../container-icon/container-icon.component";
88
import { ImageMetadata } from "../../../../models/image-metadata";
9-
import { DockerHubImage } from "../../../../models/docker-hub-image";
109
import { DropdownComponent } from "../../../../shared/components/dropdown/dropdown.component";
1110
import { IconDropdownComponent } from "../../../../shared/components/icon-dropdown/icon-dropdown.component";
1211

@@ -30,7 +29,7 @@ export class SearchListComponent {
3029
private containerService: ContainerService = inject(ContainerService);
3130
containers = this.containerService.getContainersMapRes().value;
3231
containersMetadata = this.containerService.getAllContainersMetadataRes().value;
33-
containersInfo = signal<Map<string, DockerHubImage>>(new Map<string, DockerHubImage>());
32+
containersInfo = this.containerService.getAllContainersInfoRes().value;
3433

3534
/**
3635
* This computed property generates a set of container names that match the current search criteria sorted alphabetically.
@@ -76,11 +75,7 @@ export class SearchListComponent {
7675
];
7776
selectedFilterOption = signal<number>(-1);
7877

79-
constructor() {
80-
this.containerService.getAllContainersInfo().subscribe((containersInfo) => {
81-
this.containersInfo.set(containersInfo);
82-
});
83-
}
78+
constructor() { }
8479

8580
getContainersByCategories(categories: TermStanza[], matchedContainers: Set<string>) {
8681
categories.forEach((category) => {

src/app/features/containers/pages/container/container.component.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,8 @@ <h2>{{ container.name }}</h2>
9494
[clipboardButtonComponent]="clipboardButton"
9595
class="markdown-body"
9696
[data]="removeReadmeOwnershipHeader(container.full_description)" />
97-
} @else if (selectedTab() === 'tags' && containerTags != undefined) {
98-
<ng-container *ngTemplateOutlet="tags; context: { containerTags: containerTags, containerMetadata: containerMetadata }"></ng-container>
97+
} @else if (selectedTab() === 'tags' && containerTags() != undefined) {
98+
<ng-container *ngTemplateOutlet="tags; context: { containerTags: containerTags(), containerMetadata: containerMetadata }"></ng-container>
9999
} @else if (selectedTab() === 'testing') {
100100
<ng-container *ngTemplateOutlet="testing; context: { containerMetadata: containerMetadata }"></ng-container>
101101
}
@@ -108,7 +108,7 @@ <h2>Container not found</h2>
108108

109109
<ng-template #tags let-containerMetadata="containerMetadata">
110110
<div class="tags">
111-
@for (tag of containerTags; track $index) {
111+
@for (tag of containerTags(); track $index) {
112112
@let versionStatus = containerMetadata ? getVersionStatus(tag, containerMetadata) : undefined;
113113
<div class="tag"
114114
[class.recommended]="versionStatus === VersionStatus.RECOMMENDED"

src/app/features/containers/pages/container/container.component.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Component, inject, signal, Signal } from "@angular/core";
1+
import { Component, inject, Injector, runInInjectionContext, signal, Signal } from "@angular/core";
22
import { DockerHubImage } from '../../../../models/docker-hub-image';
33
import { DockerHubTag } from '../../../../models/docker-hub-tag';
44
import { ActivatedRoute } from "@angular/router";
@@ -23,6 +23,7 @@ import { ReplacePipe } from "../../../../shared/pipes/replace/replace.pipe";
2323
})
2424
export class ContainerComponent {
2525
/* Services */
26+
private readonly injector = inject(Injector);
2627
private activatedRoute: ActivatedRoute = inject(ActivatedRoute);
2728
private viewportScroller = inject(ViewportScroller);
2829
private containerService: ContainerService = inject(ContainerService);
@@ -33,7 +34,7 @@ export class ContainerComponent {
3334

3435
status: Status = Status.LOADING;
3536
container?: DockerHubImage;
36-
containerTags?: DockerHubTag[];
37+
containerTags: Signal<DockerHubTag[]> = signal([]);
3738

3839
selectedTab = signal<TabName>(TabName.README);
3940

@@ -57,9 +58,9 @@ export class ContainerComponent {
5758
}
5859
},
5960
});
60-
this.containerService.getContainerTags(containerName).subscribe(
61-
containerTags => this.containerTags = containerTags,
62-
);
61+
runInInjectionContext(this.injector, () => {
62+
this.containerTags = this.containerService.getContainerTagsRes(containerName).value;
63+
});
6364
});
6465
this.viewportScroller.setOffset([0, 150]);
6566
this.activatedRoute.fragment.subscribe(fragment => {

src/app/shared/components/stacked-card-carousel/stacked-card-carousel.component.css renamed to src/app/features/landing/components/stacked-card-carousel/stacked-card-carousel.component.css

File renamed without changes.

src/app/shared/components/stacked-card-carousel/stacked-card-carousel.component.html renamed to src/app/features/landing/components/stacked-card-carousel/stacked-card-carousel.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
<span>{{ (item.type === "new" ? item.image.date_registered : item.image.last_updated) | date }}</span>
1515
</div>
1616
<p [class]="getCardPosition(i) === 'center' ? 'content-center' : 'content-side'">
17-
{{ item.image.name }} {{ item.version }}
17+
{{ item.image.name }} {{ item.version | async }}
1818
</p>
1919
</div>
2020
</div>

src/app/shared/components/stacked-card-carousel/stacked-card-carousel.component.ts renamed to src/app/features/landing/components/stacked-card-carousel/stacked-card-carousel.component.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { Component, signal, inject, input, ChangeDetectionStrategy } from "@angular/core";
22
import { CommonModule } from '@angular/common';
3-
import { interval, Subject } from 'rxjs';
3+
import { interval, Observable, Subject } from "rxjs";
44
import { takeUntil, filter } from 'rxjs/operators';
5-
import { DockerHubImage } from "../../../models/docker-hub-image";
5+
import { DockerHubImage } from "../../../../models/docker-hub-image";
66
import { SvgIconComponent } from "angular-svg-icon";
77
import { Router } from "@angular/router";
88

@@ -21,7 +21,7 @@ export class StackedCardCarouselComponent {
2121
isAnimating = signal(false);
2222

2323
// Data
24-
readonly items = input.required<{ type: string; image: DockerHubImage; version: string }[]>();
24+
readonly items = input.required<{ type: string; image: DockerHubImage; version: Observable<string> }[]>();
2525

2626
// For proper cleanup
2727
private destroy$ = new Subject<void>();

src/app/features/landing/pages/landing/landing.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ <h2>pegi3s Bioinformatics<br />Docker Images Project</h2>
2020
comprehensive Docker repository
2121
</p>
2222
</div>
23-
<app-stacked-card-carousel [items]="(recentImages$ | async) ?? []" />
23+
<app-stacked-card-carousel [items]="mostRecentImages()" />
2424
</div>
2525
</section>
2626
<section class="about-project">

src/app/features/landing/pages/landing/landing.component.ts

Lines changed: 42 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ChangeDetectionStrategy, Component, ElementRef, inject, Signal, viewChild } from "@angular/core";
1+
import { ChangeDetectionStrategy, Component, computed, ElementRef, inject, Signal, viewChild } from "@angular/core";
22
import { AsyncPipe } from "@angular/common";
33
import { ContributorCardComponent } from "../../components/contributor-card/contributor-card.component";
44
import { ContributorService } from "../../../../services/contributor.service";
@@ -9,13 +9,9 @@ import { ThemeService } from "../../../../services/theme.service";
99
import { ClipboardButtonComponent } from "../../../../shared/components/clipboard-button/clipboard-button.component";
1010
import { ReasonCardComponent } from "../../../../shared/components/reason-card/reason-card.component";
1111
import { SvgIconComponent } from "angular-svg-icon";
12-
import { forkJoin, map, Observable, of, switchMap } from "rxjs";
13-
import {
14-
StackedCardCarouselComponent
15-
} from "../../../../shared/components/stacked-card-carousel/stacked-card-carousel.component";
12+
import { map, Observable } from "rxjs";
13+
import { StackedCardCarouselComponent } from "../../components/stacked-card-carousel/stacked-card-carousel.component";
1614
import { ContainerService } from "../../../../services/container.service";
17-
import { DockerHubImage } from "../../../../models/docker-hub-image";
18-
import { shareReplay } from "rxjs/operators";
1915

2016
@Component({
2117
selector: 'app-landing',
@@ -71,15 +67,52 @@ export class LandingComponent {
7167
color: 300,
7268
}
7369
];
70+
readonly containerMetadata = this.containerService.getAllContainersMetadataRes().value;
71+
readonly containersInfo = this.containerService.getAllContainersInfoRes().value;
72+
readonly mostRecentImages = computed(() => {
73+
const numberOfImages = 5;
74+
const containerMetadata = this.containerMetadata();
75+
const containersInfo = this.containersInfo();
76+
77+
if (!containerMetadata || !containersInfo) return [];
78+
79+
return [...containersInfo.values()]
80+
.sort((a, b) => {
81+
const lastUpdatedA = Date.parse(a.last_updated);
82+
const lastUpdatedB = Date.parse(b.last_updated);
83+
const creationDateA = Date.parse(a.date_registered);
84+
const creationDateB = Date.parse(b.date_registered);
85+
// Compare the most recent of either last_updated or creation_date
86+
return Math.max(lastUpdatedB, creationDateB) - Math.max(lastUpdatedA, creationDateA);
87+
})
88+
.filter(image => containerMetadata.has(image.name))
89+
.slice(0, numberOfImages)
90+
.map(image => {
91+
const lastUpdatedDate = Date.parse(image.last_updated);
92+
const creationDate = Date.parse(image.date_registered);
93+
const type = lastUpdatedDate > creationDate ? 'updated' : 'new';
94+
95+
const version = this.containerService.getContainerTags(image.name).pipe(
96+
map(tags => {
97+
if (tags.length > 1) {
98+
// Find the most recent tag based on last_updated, excluding tag latest
99+
return tags
100+
.filter(tag => tag.name !== 'latest')
101+
.sort((a, b) => Date.parse(b.last_updated) - Date.parse(a.last_updated))[0].name;
102+
}
103+
return tags[0]?.name || '';
104+
})
105+
);
74106

75-
recentImages$: Observable<{ type: string; image: DockerHubImage; version: string }[]>;
107+
return { type, image, version };
108+
});
109+
});
76110

77111
/* State */
78112
searchClicked: boolean = false;
79113

80114
constructor() {
81115
this.isDarkTheme = this.themeService.isDarkTheme();
82-
this.recentImages$ = this.getMostRecentImages();
83116
}
84117

85118
onSearchClick() {
@@ -96,49 +129,4 @@ export class LandingComponent {
96129
getContributors(): Observable<Contributor[]> {
97130
return this.contributorService.getContributors();
98131
}
99-
100-
getMostRecentImages(): Observable<{ type: string; image: DockerHubImage; version: string }[]> {
101-
return this.containerService.getAllContainersInfo().pipe(
102-
switchMap((containersInfo) => {
103-
const sortedImages = [...containersInfo.values()]
104-
.sort((a, b) => {
105-
const lastUpdatedA = Date.parse(a.last_updated);
106-
const lastUpdatedB = Date.parse(b.last_updated);
107-
const creationDateA = Date.parse(a.date_registered);
108-
const creationDateB = Date.parse(b.date_registered);
109-
110-
// Compare the most recent of either last_updated or creation_date
111-
return Math.max(lastUpdatedB, creationDateB) - Math.max(lastUpdatedA, creationDateA);
112-
})
113-
.slice(0, 5); // Take the top 5 most recent images
114-
115-
if (sortedImages.length === 0) {
116-
return of([]);
117-
}
118-
119-
// Batch the tag requests
120-
const tagRequests = sortedImages.map(image =>
121-
this.containerService.getContainerTags(image.name).pipe(
122-
map(tags => {
123-
const lastUpdatedDate = Date.parse(image.last_updated);
124-
const creationDate = Date.parse(image.date_registered);
125-
const type = lastUpdatedDate > creationDate ? 'updated' : 'new';
126-
let version = '';
127-
128-
if (type === 'updated') {
129-
// Find the most recent tag based on last_updated, excluding tag latest
130-
version = tags.filter(tag => tag.name !== 'latest').sort((a, b) => Date.parse(b.last_updated) - Date.parse(a.last_updated))[0].name;
131-
}
132-
133-
return { type, image, version };
134-
})
135-
)
136-
);
137-
138-
return forkJoin(tagRequests);
139-
}),
140-
// Add shareReplay to prevent multiple subscriptions from triggering multiple API calls
141-
shareReplay(1)
142-
);
143-
}
144132
}

0 commit comments

Comments
 (0)