From 73fc859b57dd6c90a330034d845175e42bcd9da5 Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Wed, 11 Dec 2024 18:53:36 +0700 Subject: [PATCH] so tired --- .../app/core/interceptors/api.interceptor.ts | 13 +----- .../core/services/loading/loading.service.ts | 32 ++++++++++--- ...roduct-detail-information-tab.component.ts | 46 +++++++++++++++---- .../product-detail/product-detail.service.ts | 2 + .../modules/product/product.component.html | 6 +-- .../app/modules/product/product.component.ts | 2 + .../app/modules/product/product.service.ts | 8 +++- .../loading-spinner.component.html | 10 ++-- .../loading-spinner.component.ts | 12 ++--- .../app/shared/enums/loading-component-id.ts | 7 +++ 10 files changed, 94 insertions(+), 44 deletions(-) create mode 100644 marketplace-ui/src/app/shared/enums/loading-component-id.ts diff --git a/marketplace-ui/src/app/core/interceptors/api.interceptor.ts b/marketplace-ui/src/app/core/interceptors/api.interceptor.ts index a9447f370..9b9c13583 100644 --- a/marketplace-ui/src/app/core/interceptors/api.interceptor.ts +++ b/marketplace-ui/src/app/core/interceptors/api.interceptor.ts @@ -4,9 +4,8 @@ import { HttpInterceptorFn } from '@angular/common/http'; import { environment } from '../../../environments/environment'; -import { LoadingService } from '../services/loading/loading.service'; import { inject } from '@angular/core'; -import { catchError, EMPTY, finalize } from 'rxjs'; +import { catchError, EMPTY } from 'rxjs'; import { Router } from '@angular/router'; import { ERROR_CODES, ERROR_PAGE_PATH } from '../../shared/constants/common.constant'; @@ -25,7 +24,6 @@ export const ForwardingError = new HttpContextToken(() => false); export const apiInterceptor: HttpInterceptorFn = (req, next) => { const router = inject(Router); - const loadingService = inject(LoadingService); if (req.url.includes('i18n')) { return next(req); @@ -41,10 +39,6 @@ export const apiInterceptor: HttpInterceptorFn = (req, next) => { headers: addIvyHeaders(req.headers) }); - if (!req.context.get(SkipLoading)) { - loadingService.show(); - } - if (req.context.get(ForwardingError)) { return next(cloneReq); } @@ -57,11 +51,6 @@ export const apiInterceptor: HttpInterceptorFn = (req, next) => { router.navigate([ERROR_PAGE_PATH]); } return EMPTY; - }), - finalize(() => { - if (!req.context.get(SkipLoading)) { - loadingService.hide(); - } }) ); }; diff --git a/marketplace-ui/src/app/core/services/loading/loading.service.ts b/marketplace-ui/src/app/core/services/loading/loading.service.ts index f3981781c..803ab18ca 100644 --- a/marketplace-ui/src/app/core/services/loading/loading.service.ts +++ b/marketplace-ui/src/app/core/services/loading/loading.service.ts @@ -1,17 +1,35 @@ -import { computed, Injectable, signal } from '@angular/core'; +import { Injectable } from '@angular/core'; +import { BehaviorSubject } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class LoadingService { - private readonly isShow = signal(false); - isLoading = computed(() => this.isShow()); + private readonly loadingSubject = new BehaviorSubject<{ + [key: string]: boolean; + }>({}); + loading$ = this.loadingSubject.asObservable; - show() { - this.isShow.set(true); + private setLoading(componentIds: string[], isLoading: boolean): void { + const currentState = this.loadingSubject.value; + + // Update the loading state for each component ID + componentIds.forEach(id => { + currentState[id] = isLoading; + }); + + this.loadingSubject.next({ ...currentState }); + } + + showLoading(...componentIds: string[]): void { + this.setLoading(componentIds, true); + } + + hideLoading(...componentIds: string[]) { + this.setLoading(componentIds, false); } - hide() { - this.isShow.set(false); + isLoading(compnentId: string): boolean { + return this.loadingSubject.value[compnentId] || false; } } diff --git a/marketplace-ui/src/app/modules/product/product-detail/product-detail-information-tab/product-detail-information-tab.component.ts b/marketplace-ui/src/app/modules/product/product-detail/product-detail-information-tab/product-detail-information-tab.component.ts index 8b4c674ab..455b867b4 100644 --- a/marketplace-ui/src/app/modules/product/product-detail/product-detail-information-tab/product-detail-information-tab.component.ts +++ b/marketplace-ui/src/app/modules/product/product-detail/product-detail-information-tab/product-detail-information-tab.component.ts @@ -1,5 +1,12 @@ import { CommonModule } from '@angular/common'; -import { Component, inject, Input, OnChanges, SimpleChange, SimpleChanges } from '@angular/core'; +import { + Component, + inject, + Input, + OnChanges, + SimpleChange, + SimpleChanges +} from '@angular/core'; import { TranslateModule } from '@ngx-translate/core'; import { ProductDetail } from '../../../../shared/models/product-detail.model'; import { LanguageService } from '../../../../core/services/language/language.service'; @@ -7,14 +14,20 @@ import { ProductDetailService } from '../product-detail.service'; import { VERSION } from '../../../../shared/constants/common.constant'; import { LoadingService } from '../../../../core/services/loading/loading.service'; import { ThemeService } from '../../../../core/services/theme/theme.service'; +import { finalize } from 'rxjs'; import { MissingProductInformationContentPipe } from '../../../../shared/pipes/missing-product-Information-content-pipe'; +import { LoadingComponentId } from '../../../../shared/enums/loading-component-id'; const SELECTED_VERSION = 'selectedVersion'; const PRODUCT_DETAIL = 'productDetail'; @Component({ selector: 'app-product-detail-information-tab', standalone: true, - imports: [CommonModule, TranslateModule, MissingProductInformationContentPipe], + imports: [ + CommonModule, + TranslateModule, + MissingProductInformationContentPipe + ], templateUrl: './product-detail-information-tab.component.html', styleUrl: './product-detail-information-tab.component.scss' }) @@ -23,6 +36,7 @@ export class ProductDetailInformationTabComponent implements OnChanges { productDetail!: ProductDetail; @Input() selectedVersion!: string; + protected LoadingComponentId = LoadingComponentId; externalDocumentLink = ''; displayVersion = ''; displayExternalDocName: string | null = ''; @@ -34,7 +48,11 @@ export class ProductDetailInformationTabComponent implements OnChanges { ngOnChanges(changes: SimpleChanges): void { let version = ''; const changedSelectedVersion = changes[SELECTED_VERSION]; - if (changedSelectedVersion && changedSelectedVersion.currentValue === changedSelectedVersion.previousValue) { + if ( + changedSelectedVersion && + changedSelectedVersion.currentValue === + changedSelectedVersion.previousValue + ) { return; } const changedProduct = changes[PRODUCT_DETAIL]; @@ -48,7 +66,18 @@ export class ProductDetailInformationTabComponent implements OnChanges { return; } - this.productDetailService.getExternalDocumentForProductByVersion(this.productDetail.id, this.extractVersionValue(version)) + this.productDetailService + .getExternalDocumentForProductByVersion( + this.productDetail.id, + this.extractVersionValue(version) + ) + .pipe( + finalize(() => { + this.loadingService.hideLoading( + LoadingComponentId.DETAIL_INFORMATION_TAB + ); + }) + ) .subscribe({ next: response => { if (response) { @@ -57,11 +86,9 @@ export class ProductDetailInformationTabComponent implements OnChanges { } else { this.resetValues(); } - this.loadingService.hide(); }, error: () => { this.resetValues(); - this.loadingService.hide(); } }); this.displayVersion = this.extractVersionValue(this.selectedVersion); @@ -79,9 +106,10 @@ export class ProductDetailInformationTabComponent implements OnChanges { // To ensure the function always returns a boolean, you can explicitly coerce the result into a boolean using the !! operator or default it to false // Adding !! in case of changedProduct is undefined, it will return false instead of returning undefined isProductChanged(changedProduct: SimpleChange) { - return !!(changedProduct?.previousValue && + return !!( + changedProduct?.previousValue && Object.keys(changedProduct.previousValue).length > 0 && - changedProduct.currentValue !== changedProduct.previousValue); + changedProduct.currentValue !== changedProduct.previousValue + ); } - } diff --git a/marketplace-ui/src/app/modules/product/product-detail/product-detail.service.ts b/marketplace-ui/src/app/modules/product/product-detail/product-detail.service.ts index 6862eb0d8..73e8a7e84 100644 --- a/marketplace-ui/src/app/modules/product/product-detail/product-detail.service.ts +++ b/marketplace-ui/src/app/modules/product/product-detail/product-detail.service.ts @@ -6,6 +6,7 @@ import { Observable } from 'rxjs'; import { API_URI } from '../../../shared/constants/api.constant'; import { ForwardingError } from '../../../core/interceptors/api.interceptor'; import { ExternalDocument } from '../../../shared/models/external-document.model'; +import { LoadingComponentId } from '../../../shared/enums/loading-component-id'; @Injectable({ providedIn: 'root' @@ -20,6 +21,7 @@ export class ProductDetailService { noFeedbackLabel: WritableSignal = signal(''); getExternalDocumentForProductByVersion(productId: string, version: string): Observable { + this.loadingService.showLoading(LoadingComponentId.DETAIL_INFORMATION_TAB); return this.httpClient.get( `${API_URI.EXTERNAL_DOCUMENT}/${productId}/${version}`, { context: new HttpContext().set(ForwardingError, true)} ); diff --git a/marketplace-ui/src/app/modules/product/product.component.html b/marketplace-ui/src/app/modules/product/product.component.html index 21a5e6c93..a0ea7844c 100644 --- a/marketplace-ui/src/app/modules/product/product.component.html +++ b/marketplace-ui/src/app/modules/product/product.component.html @@ -47,10 +47,6 @@

} - - @if (loadingService.isLoading()) { - - } - +
diff --git a/marketplace-ui/src/app/modules/product/product.component.ts b/marketplace-ui/src/app/modules/product/product.component.ts index f9de02844..0306eb194 100644 --- a/marketplace-ui/src/app/modules/product/product.component.ts +++ b/marketplace-ui/src/app/modules/product/product.component.ts @@ -36,6 +36,7 @@ import { import { ItemDropdown } from '../../shared/models/item-dropdown.model'; import { LoadingSpinnerComponent } from '../../shared/components/loading-spinner/loading-spinner.component'; import { LoadingService } from '../../core/services/loading/loading.service'; +import { LoadingComponentId } from '../../shared/enums/loading-component-id'; const SEARCH_DEBOUNCE_TIME = 500; @@ -55,6 +56,7 @@ const SEARCH_DEBOUNCE_TIME = 500; styleUrl: './product.component.scss' }) export class ProductComponent implements AfterViewInit, OnDestroy { + protected LoadingComponentId = LoadingComponentId; products: WritableSignal = signal([]); productDetail!: ProductDetail; subscriptions: Subscription[] = []; diff --git a/marketplace-ui/src/app/modules/product/product.service.ts b/marketplace-ui/src/app/modules/product/product.service.ts index 2be41d84a..01c7f8699 100644 --- a/marketplace-ui/src/app/modules/product/product.service.ts +++ b/marketplace-ui/src/app/modules/product/product.service.ts @@ -1,6 +1,6 @@ import { HttpClient, HttpContext, HttpParams } from '@angular/common/http'; import { Injectable, inject } from '@angular/core'; -import { delay, Observable } from 'rxjs'; +import { delay, finalize, Observable } from 'rxjs'; import { LoadingService } from '../../core/services/loading/loading.service'; import { RequestParam } from '../../shared/enums/request-param'; import { ProductApiResponse } from '../../shared/models/apis/product-response.model'; @@ -10,6 +10,7 @@ import { VersionData } from '../../shared/models/vesion-artifact.model'; import { SkipLoading } from '../../core/interceptors/api.interceptor'; import { VersionAndUrl } from '../../shared/models/version-and-url'; import { API_URI } from '../../shared/constants/api.constant'; +import { LoadingComponentId } from '../../shared/enums/loading-component-id'; @Injectable() export class ProductService { @@ -17,6 +18,7 @@ export class ProductService { loadingService = inject(LoadingService); findProductsByCriteria(criteria: Criteria): Observable { + this.loadingService.showLoading(LoadingComponentId.LANDING_PAGE); let requestParams = new HttpParams(); let requestURL = API_URI.PRODUCT; if (criteria.nextPageHref) { @@ -36,7 +38,9 @@ export class ProductService { } return this.httpClient.get(requestURL, { params: requestParams - }); + }).pipe(finalize(()=> { + this.loadingService.hideLoading(LoadingComponentId.LANDING_PAGE); + })); } getProductDetailsWithVersion( diff --git a/marketplace-ui/src/app/shared/components/loading-spinner/loading-spinner.component.html b/marketplace-ui/src/app/shared/components/loading-spinner/loading-spinner.component.html index 21fe9e4e9..993ceab34 100644 --- a/marketplace-ui/src/app/shared/components/loading-spinner/loading-spinner.component.html +++ b/marketplace-ui/src/app/shared/components/loading-spinner/loading-spinner.component.html @@ -1,5 +1,9 @@ -
-
-
+@if (isLoading()) { +
+
+
+
+
+} diff --git a/marketplace-ui/src/app/shared/components/loading-spinner/loading-spinner.component.ts b/marketplace-ui/src/app/shared/components/loading-spinner/loading-spinner.component.ts index 74a54a837..085e41ba2 100644 --- a/marketplace-ui/src/app/shared/components/loading-spinner/loading-spinner.component.ts +++ b/marketplace-ui/src/app/shared/components/loading-spinner/loading-spinner.component.ts @@ -1,15 +1,15 @@ -import { Component } from '@angular/core'; -import { NgClass } from '@angular/common'; +import { Component, computed, inject, Input } from '@angular/core'; +import { LoadingService } from '../../../core/services/loading/loading.service'; @Component({ selector: 'app-loading-spinner', standalone: true, - imports: [ - NgClass - ], templateUrl: './loading-spinner.component.html', styleUrl: './loading-spinner.component.scss' }) export class LoadingSpinnerComponent { - spinners = Array(5).fill(null); + @Input() key: string = ''; + @Input() containerClasses: string = ''; + loadingService = inject(LoadingService); + isLoading = computed(() => this.loadingService.loadingSubject.value[this.key]); } diff --git a/marketplace-ui/src/app/shared/enums/loading-component-id.ts b/marketplace-ui/src/app/shared/enums/loading-component-id.ts new file mode 100644 index 000000000..a30b622a0 --- /dev/null +++ b/marketplace-ui/src/app/shared/enums/loading-component-id.ts @@ -0,0 +1,7 @@ +export enum LoadingComponentId { + LANDING_PAGE = 'landing-page', + PRODUCT_DETAIL = 'product-detial', + PRODUCT_VERSION = 'product-version', + DETAIL_PAGE = 'detail-page', + DETAIL_INFORMATION_TAB = 'detail-information-tab' +}