autowp/autowp-frontend

View on GitHub
src/app/catalogue/mixed/picture/picture.component.ts

Summary

Maintainability
A
0 mins
Test Coverage
import {AsyncPipe} from '@angular/common';
import {ChangeDetectionStrategy, Component, inject} from '@angular/core';
import {ActivatedRoute, Router, RouterLink} from '@angular/router';
import {
APIItem,
CommentsType,
ItemFields,
ItemListOptions,
ItemsRequest,
Picture,
PictureFields,
PictureItemListOptions,
PictureItemType,
PictureListOptions,
PictureModerVoteRequest,
PicturesRequest,
} from '@grpc/spec.pb';
import {ItemsClient, PicturesClient} from '@grpc/spec.pbsc';
import {GrpcStatusEvent} from '@ngx-grpc/common';
import {LanguageService} from '@services/language';
import {PageEnvService} from '@services/page-env.service';
import {BehaviorSubject, combineLatest, EMPTY, Observable, of, throwError} from 'rxjs';
import {catchError, debounceTime, distinctUntilChanged, map, shareReplay, switchMap, tap} from 'rxjs/operators';
 
import {CommentsComponent} from '../../../comments/comments/comments.component';
import {PictureComponent} from '../../../picture/picture.component';
import {BrandPerspectivePageData} from '../../catalogue.module';
 
@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [RouterLink, CommentsComponent, AsyncPipe, PictureComponent],
selector: 'app-catalogue-mixed-picture',
templateUrl: './picture.component.html',
})
export class CatalogueMixedPictureComponent {
readonly #route = inject(ActivatedRoute);
readonly #pageEnv = inject(PageEnvService);
readonly #router = inject(Router);
readonly #itemsClient = inject(ItemsClient);
readonly #picturesClient = inject(PicturesClient);
readonly #languageService = inject(LanguageService);
 
readonly changed$ = new BehaviorSubject<void>(void 0);
 
protected readonly CommentsType = CommentsType;
 
protected readonly brand$: Observable<APIItem> = this.#route.paramMap.pipe(
map((params) => params.get('brand')),
distinctUntilChanged(),
debounceTime(10),
switchMap((catname) => {
if (!catname) {
return EMPTY;
}
return this.#itemsClient.list(
new ItemsRequest({
fields: new ItemFields({
nameHtml: true,
nameText: true,
}),
language: this.#languageService.language,
limit: 1,
options: new ItemListOptions({
catname,
}),
}),
);
}),
map((response) => (response.items?.length ? response.items[0] : null)),
switchMap((brand) => {
if (!brand) {
this.#router.navigate(['/error-404'], {
skipLocationChange: true,
});
return EMPTY;
}
return of(brand);
}),
shareReplay({bufferSize: 1, refCount: false}),
);
 
protected readonly data$ = (this.#route.data as Observable<BrandPerspectivePageData>).pipe(
shareReplay({bufferSize: 1, refCount: false}),
);
 
protected readonly identity$ = this.#route.paramMap.pipe(
map((route) => route.get('identity')),
distinctUntilChanged(),
debounceTime(10),
switchMap((identity) => {
if (!identity) {
this.#router.navigate(['/error-404'], {
skipLocationChange: true,
});
return EMPTY;
}
return of(identity);
}),
);
 
protected readonly picture$: Observable<Picture> = combineLatest([
this.brand$,
this.data$,
this.identity$,
this.changed$,
]).pipe(
switchMap(([brand, data, identity]) =>
this.#picturesClient
.getPicture(
new PicturesRequest({
fields: new PictureFields({
copyrights: true,
image: true,
moderVoted: true,
nameHtml: true,
nameText: true,
paginator: new PicturesRequest({
options: new PictureListOptions({
pictureItem: new PictureItemListOptions({
excludePerspectiveId: data.perspective_exclude_id || [],
itemId: brand.id,
perspectiveId: data.perspective_id,
typeId: PictureItemType.PICTURE_ITEM_CONTENT,
}),
}),
order: PicturesRequest.Order.ORDER_RESOLUTION_DESC,
}),
pictureModerVotes: new PictureModerVoteRequest(),
previewLarge: true,
replaceable: new PicturesRequest({
fields: new PictureFields({nameHtml: true}),
}),
rights: true,
subscribed: true,
votes: true,
}),
language: this.#languageService.language,
options: new PictureListOptions({
identity,
pictureItem: new PictureItemListOptions({
excludePerspectiveId: data.perspective_exclude_id || [],
itemId: brand.id,
perspectiveId: data.perspective_id,
typeId: PictureItemType.PICTURE_ITEM_CONTENT,
}),
}),
}),
)
.pipe(
catchError((error: unknown) => {
if (error instanceof GrpcStatusEvent && error.statusCode == 5) {
// NOT_FOUND
return of(null);
}
console.error(error);
return throwError(() => error);
}),
switchMap((picture) => {
if (!picture) {
this.#router.navigate(['/error-404'], {
skipLocationChange: true,
});
return EMPTY;
}
return of(picture);
}),
tap((picture) => {
this.#pageEnv.set({
pageId: data.picture_page.id,
title: picture.nameText,
});
}),
),
),
);
 
protected reloadPicture() {
this.changed$.next();
}
}