mirror of
https://github.com/goharbor/harbor.git
synced 2024-12-26 10:38:00 +01:00
Support accessory recursion for artifact list (#18366)
1. Update accessory UI to support recursion Signed-off-by: AllForNothing <sshijun@vmware.com>
This commit is contained in:
parent
321d8a0885
commit
53d86f872e
@ -546,7 +546,6 @@
|
||||
ngProjectAs="clr-dg-row-detail"
|
||||
*ngIf="artifact?.accessories?.length">
|
||||
<sub-accessories
|
||||
(deleteAccessory)="deleteAccessory($event)"
|
||||
[projectName]="projectName"
|
||||
[repositoryName]="repoName"
|
||||
[artifactDigest]="artifact?.digest"
|
||||
|
@ -189,6 +189,7 @@ export class ArtifactListTabComponent
|
||||
);
|
||||
copiedHiddenArray: boolean[] = [];
|
||||
private _hasViewInit: boolean = false;
|
||||
deleteAccessorySub: Subscription;
|
||||
constructor(
|
||||
private errorHandlerService: ErrorHandler,
|
||||
private artifactService: ArtifactService,
|
||||
@ -242,6 +243,14 @@ export class ArtifactListTabComponent
|
||||
}
|
||||
);
|
||||
}
|
||||
if (!this.deleteAccessorySub) {
|
||||
this.deleteAccessorySub = this.eventService.subscribe(
|
||||
HarborEvent.DELETE_ACCESSORY,
|
||||
(a: Accessory) => {
|
||||
this.deleteAccessory(a);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
@ -253,6 +262,10 @@ export class ArtifactListTabComponent
|
||||
this.updateArtifactSub.unsubscribe();
|
||||
this.updateArtifactSub = null;
|
||||
}
|
||||
if (this.deleteAccessorySub) {
|
||||
this.deleteAccessorySub.unsubscribe();
|
||||
this.deleteAccessorySub = null;
|
||||
}
|
||||
}
|
||||
get withNotary(): boolean {
|
||||
return this.appConfigService.getConfig()?.with_notary;
|
||||
|
@ -16,10 +16,14 @@
|
||||
</button>
|
||||
</clr-dg-action-overflow>
|
||||
<clr-dg-cell class="relative">
|
||||
<!--it will cause a ExpressionChangedAfterItHasBeenCheckedError when read datagrid['el']?.nativeElement?.offsetHeight so
|
||||
use OnPush Strategy to avoid ExpressionChangedAfterItHasBeenCheckedError-->
|
||||
<hr
|
||||
class="y-dash-line"
|
||||
*ngIf="i === displayedAccessories?.length - 1 && viewInit"
|
||||
[style.height.px]="dashLineHeight" />
|
||||
[hidden]="i !== displayedAccessories?.length - 1"
|
||||
[style.height.px]="
|
||||
datagrid['el']?.nativeElement?.offsetHeight || 0
|
||||
" />
|
||||
<hr class="x-dash-line" />
|
||||
<div class="cell">
|
||||
<div
|
||||
@ -55,6 +59,17 @@
|
||||
{{ a.creation_time | harborDatetime : 'short' }}
|
||||
</div>
|
||||
</clr-dg-cell>
|
||||
<ng-container
|
||||
ngProjectAs="clr-dg-row-detail"
|
||||
*ngIf="a?.accessories?.length">
|
||||
<sub-accessories
|
||||
[projectName]="projectName"
|
||||
[repositoryName]="repositoryName"
|
||||
[artifactDigest]="a?.digest"
|
||||
[total]="a?.accessoryNumber"
|
||||
[accessories]="a?.accessories"
|
||||
*clrIfExpanded></sub-accessories>
|
||||
</ng-container>
|
||||
</clr-dg-row>
|
||||
<clr-dg-footer *ngIf="total > pageSize">
|
||||
<clr-dg-pagination
|
||||
|
@ -64,6 +64,9 @@ describe('SubAccessoriesComponent', () => {
|
||||
listAccessories() {
|
||||
return of(page2).pipe(delay(0));
|
||||
},
|
||||
listAccessoriesResponse() {
|
||||
return of({}).pipe(delay(0));
|
||||
},
|
||||
};
|
||||
|
||||
let component: SubAccessoriesComponent;
|
||||
|
@ -1,10 +1,10 @@
|
||||
import {
|
||||
AfterViewInit,
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
EventEmitter,
|
||||
Input,
|
||||
OnInit,
|
||||
Output,
|
||||
ViewChild,
|
||||
} from '@angular/core';
|
||||
import {
|
||||
clone,
|
||||
@ -22,8 +22,15 @@ import { ErrorHandler } from '../../../../../../../../shared/units/error-handler
|
||||
import { finalize } from 'rxjs/operators';
|
||||
import { SafeUrl } from '@angular/platform-browser';
|
||||
import { ArtifactService } from '../../../../artifact.service';
|
||||
import { AccessoryQueryParams, artifactDefault } from '../../../../artifact';
|
||||
import { ClrDatagrid } from '@clr/angular';
|
||||
import {
|
||||
AccessoryFront,
|
||||
AccessoryQueryParams,
|
||||
artifactDefault,
|
||||
} from '../../../../artifact';
|
||||
import {
|
||||
EventService,
|
||||
HarborEvent,
|
||||
} from '../../../../../../../../services/event-service/event.service';
|
||||
|
||||
export const ACCESSORY_PAGE_SIZE: number = 5;
|
||||
|
||||
@ -31,8 +38,9 @@ export const ACCESSORY_PAGE_SIZE: number = 5;
|
||||
selector: 'sub-accessories',
|
||||
templateUrl: 'sub-accessories.component.html',
|
||||
styleUrls: ['./sub-accessories.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush, // use OnPush Strategy to avoid ExpressionChangedAfterItHasBeenCheckedError
|
||||
})
|
||||
export class SubAccessoriesComponent implements OnInit {
|
||||
export class SubAccessoriesComponent implements OnInit, AfterViewInit {
|
||||
@Input()
|
||||
projectName: string;
|
||||
@Input()
|
||||
@ -41,32 +49,29 @@ export class SubAccessoriesComponent implements OnInit {
|
||||
artifactDigest: string;
|
||||
@Input()
|
||||
accessories: Accessory[] = [];
|
||||
@Output()
|
||||
deleteAccessory: EventEmitter<Accessory> = new EventEmitter<Accessory>();
|
||||
currentPage: number = 1;
|
||||
@Input()
|
||||
total: number = 0;
|
||||
pageSize: number = ACCESSORY_PAGE_SIZE;
|
||||
page: number = 1;
|
||||
displayedAccessories: Accessory[] = [];
|
||||
displayedAccessories: AccessoryFront[] = [];
|
||||
loading: boolean = false;
|
||||
@ViewChild('datagrid')
|
||||
datagrid: ClrDatagrid;
|
||||
viewInit: boolean = false;
|
||||
constructor(
|
||||
private activatedRoute: ActivatedRoute,
|
||||
private router: Router,
|
||||
private newArtifactService: NewArtifactService,
|
||||
private artifactService: ArtifactService,
|
||||
private errorHandlerService: ErrorHandler
|
||||
private errorHandlerService: ErrorHandler,
|
||||
private cdf: ChangeDetectorRef,
|
||||
private event: EventService
|
||||
) {}
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
this.cdf.detectChanges();
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.displayedAccessories = clone(this.accessories);
|
||||
// avoid ng checking error
|
||||
setTimeout(() => {
|
||||
this.viewInit = true;
|
||||
});
|
||||
}
|
||||
size(size: number) {
|
||||
return formatSize(size.toString());
|
||||
@ -103,13 +108,14 @@ export class SubAccessoriesComponent implements OnInit {
|
||||
}
|
||||
|
||||
delete(a: Accessory) {
|
||||
this.deleteAccessory.emit(a);
|
||||
this.event.publish(HarborEvent.DELETE_ACCESSORY, a);
|
||||
}
|
||||
|
||||
clrLoad() {
|
||||
if (this.currentPage === 1) {
|
||||
this.displayedAccessories = clone(this.accessories);
|
||||
this.getIconFromBackend();
|
||||
this.getAccessoriesAsync(this.displayedAccessories);
|
||||
return;
|
||||
}
|
||||
this.loading = true;
|
||||
@ -126,7 +132,9 @@ export class SubAccessoriesComponent implements OnInit {
|
||||
.subscribe(
|
||||
res => {
|
||||
this.displayedAccessories = res;
|
||||
this.cdf.detectChanges();
|
||||
this.getIconFromBackend();
|
||||
this.getAccessoriesAsync(this.displayedAccessories);
|
||||
},
|
||||
error => {
|
||||
this.errorHandlerService.error(error);
|
||||
@ -139,14 +147,37 @@ export class SubAccessoriesComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
get dashLineHeight() {
|
||||
if (
|
||||
this.datagrid &&
|
||||
this.datagrid['el'] &&
|
||||
this.datagrid['el']?.nativeElement?.offsetHeight
|
||||
) {
|
||||
return this.datagrid['el'].nativeElement?.offsetHeight;
|
||||
// get accessories
|
||||
getAccessoriesAsync(artifacts: AccessoryFront[]) {
|
||||
if (artifacts && artifacts.length) {
|
||||
artifacts.forEach(item => {
|
||||
const listTagParams: NewArtifactService.ListAccessoriesParams =
|
||||
{
|
||||
projectName: this.projectName,
|
||||
repositoryName: dbEncodeURIComponent(
|
||||
this.repositoryName
|
||||
),
|
||||
reference: item.digest,
|
||||
page: 1,
|
||||
pageSize: ACCESSORY_PAGE_SIZE,
|
||||
};
|
||||
this.newArtifactService
|
||||
.listAccessoriesResponse(listTagParams)
|
||||
.subscribe(res => {
|
||||
if (res.headers) {
|
||||
let xHeader: string =
|
||||
res.headers.get('x-total-count');
|
||||
if (xHeader) {
|
||||
item.accessoryNumber = Number.parseInt(
|
||||
xHeader,
|
||||
10
|
||||
);
|
||||
}
|
||||
}
|
||||
item.accessories = res.body;
|
||||
this.cdf.detectChanges();
|
||||
});
|
||||
});
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -14,9 +14,9 @@ export interface ArtifactFront extends Artifact {
|
||||
}
|
||||
|
||||
export interface AccessoryFront extends Accessory {
|
||||
pullCommand?: string;
|
||||
tagNumber?: number;
|
||||
scan_overview?: any;
|
||||
coSigned?: string;
|
||||
accessoryNumber?: number;
|
||||
accessories?: any;
|
||||
}
|
||||
|
||||
export const multipleFilter: Array<{
|
||||
|
@ -79,4 +79,5 @@ export enum HarborEvent {
|
||||
STOP_SCAN_ARTIFACT = 'stopScanArtifact',
|
||||
UPDATE_VULNERABILITY_INFO = 'UpdateVulnerabilityInfo',
|
||||
REFRESH_EXPORT_JOBS = 'refreshExportJobs',
|
||||
DELETE_ACCESSORY = 'deleteAccessory',
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user