(Kavita+) External Series Detail (#2309)
This commit is contained in:
parent
bd62e00ec5
commit
6067c9233c
32 changed files with 2354 additions and 726 deletions
|
@ -0,0 +1,119 @@
|
|||
<ng-container *transloco="let t">
|
||||
<div class="offcanvas-header">
|
||||
<h5 class="offcanvas-title">
|
||||
<ng-container *ngIf="CoverUrl as coverUrl">
|
||||
<app-image *ngIf="coverUrl" height="230px" width="160px" maxHeight="230px" objectFit="contain" [imageUrl]="coverUrl"></app-image>
|
||||
<div class="">
|
||||
{{name}}
|
||||
</div>
|
||||
</ng-container>
|
||||
</h5>
|
||||
<button type="button" class="btn-close text-reset" [attr.aria-label]="t('common.close')" (click)="close()"></button>
|
||||
</div>
|
||||
|
||||
<div class="offcanvas-body">
|
||||
<ng-container *ngIf="externalSeries; else localSeriesBody">
|
||||
<span *ngIf="(externalSeries.volumeCount || 0) > 0 || (externalSeries.chapterCount || 0) > 0" class="text-muted" style="font-size: 14px; color: lightgrey">{{t('series-preview-drawer.vols-and-chapters', {volCount: externalSeries.volumeCount, chpCount: externalSeries.chapterCount})}}</span>
|
||||
<app-read-more *ngIf="externalSeries.summary" [maxLength]="300" [text]="externalSeries.summary"></app-read-more>
|
||||
|
||||
<div class="mt-3">
|
||||
<app-metadata-detail [tags]="externalSeries.genres" [libraryId]="0" [heading]="t('series-preview-drawer.genres-label')">
|
||||
<ng-template #itemTemplate let-item>
|
||||
<app-tag-badge>
|
||||
{{item}}
|
||||
</app-tag-badge>
|
||||
</ng-template>
|
||||
</app-metadata-detail>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<app-metadata-detail [tags]="externalSeries.tags" [libraryId]="0" [heading]="t('series-preview-drawer.tags-label')">
|
||||
<ng-template #itemTemplate let-item>
|
||||
<app-tag-badge>
|
||||
{{item.name}}
|
||||
</app-tag-badge>
|
||||
</ng-template>
|
||||
</app-metadata-detail>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<app-metadata-detail [tags]="externalSeries.staff" [libraryId]="0" [heading]="t('series-preview-drawer.staff-label')">
|
||||
<ng-template #itemTemplate let-item>
|
||||
<div class="card mb-3" style="max-width: 180px;">
|
||||
<div class="row g-0">
|
||||
<div class="col-md-4">
|
||||
<ng-container *ngIf="item.imageUrl && !item.imageUrl.endsWith('default.jpg'); else localPerson">
|
||||
<app-image height="24px" width="24px" objectFit="contain" [imageUrl]="item.imageUrl" classes="person-img"></app-image>
|
||||
</ng-container>
|
||||
<ng-template #localPerson>
|
||||
<i class="fa fa-user-circle align-self-center person-img" style="font-size: 28px;" aria-hidden="true"></i>
|
||||
</ng-template>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<div class="card-body">
|
||||
<h6 class="card-title">{{item.name}}</h6>
|
||||
<p class="card-text" style="font-size: 14px"><small class="text-muted">{{item.role}}</small></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
</app-metadata-detail>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ng-template #localSeriesBody>
|
||||
<ng-container *ngIf="localSeries">
|
||||
<span class="text-muted" style="font-size: 14px; color: lightgrey">{{localSeries.publicationStatus | publicationStatus}}</span>
|
||||
<app-read-more [maxLength]="300" [text]="localSeries.summary"></app-read-more>
|
||||
|
||||
<div class="mt-3">
|
||||
<app-metadata-detail [tags]="localSeries.genres" [libraryId]="0" [heading]="t('series-preview-drawer.genres-label')">
|
||||
<ng-template #itemTemplate let-item>
|
||||
<app-tag-badge>
|
||||
{{item.title}}
|
||||
</app-tag-badge>
|
||||
</ng-template>
|
||||
</app-metadata-detail>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<app-metadata-detail [tags]="localSeries.tags" [libraryId]="0" [heading]="t('series-preview-drawer.tags-label')">
|
||||
<ng-template #itemTemplate let-item>
|
||||
<app-tag-badge>
|
||||
{{item.title}}
|
||||
</app-tag-badge>
|
||||
</ng-template>
|
||||
</app-metadata-detail>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<app-metadata-detail [tags]="localStaff" [libraryId]="0" [heading]="t('series-preview-drawer.staff-label')">
|
||||
<ng-template #itemTemplate let-item>
|
||||
<div class="card mb-3" style="max-width: 180px;">
|
||||
<div class="row g-0">
|
||||
<div class="col-md-4">
|
||||
<i class="fa fa-user-circle align-self-center" style="font-size: 28px; margin-top: 24px; margin-left: 24px" aria-hidden="true"></i>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<div class="card-body">
|
||||
<h6 class="card-title">{{item.name}}</h6>
|
||||
<p class="card-text" style="font-size: 14px"><small class="text-muted">{{item.role}}</small></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
</app-metadata-detail>
|
||||
</div>
|
||||
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
|
||||
<app-loading [loading]="isLoading"></app-loading>
|
||||
|
||||
<a class="btn btn-primary col-12 " [href]="url" target="_blank" rel="noopener noreferrer">
|
||||
{{t('series-preview-drawer.view-series')}}
|
||||
</a>
|
||||
</div>
|
||||
</ng-container>
|
|
@ -0,0 +1,10 @@
|
|||
// You must add this on a component based drawer
|
||||
:host {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
::ng-deep .person-img {
|
||||
margin-top: 24px; margin-left: 24px;
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, inject, Input, OnInit} from '@angular/core';
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {TranslocoDirective} from "@ngneat/transloco";
|
||||
import {NgbActiveOffcanvas} from "@ng-bootstrap/ng-bootstrap";
|
||||
import {ExternalSeriesDetail, SeriesStaff} from "../../_models/series-detail/external-series-detail";
|
||||
import {SeriesService} from "../../_services/series.service";
|
||||
import {ImageComponent} from "../../shared/image/image.component";
|
||||
import {LoadingComponent} from "../../shared/loading/loading.component";
|
||||
import {SafeHtmlPipe} from "../../pipe/safe-html.pipe";
|
||||
import {A11yClickDirective} from "../../shared/a11y-click.directive";
|
||||
import {MetadataDetailComponent} from "../../series-detail/_components/metadata-detail/metadata-detail.component";
|
||||
import {PersonBadgeComponent} from "../../shared/person-badge/person-badge.component";
|
||||
import {TagBadgeComponent} from "../../shared/tag-badge/tag-badge.component";
|
||||
import {ImageService} from "../../_services/image.service";
|
||||
import {PublicationStatusPipe} from "../../pipe/publication-status.pipe";
|
||||
import {SeriesMetadata} from "../../_models/metadata/series-metadata";
|
||||
import {ReadMoreComponent} from "../../shared/read-more/read-more.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-series-preview-drawer',
|
||||
standalone: true,
|
||||
imports: [CommonModule, TranslocoDirective, ImageComponent, LoadingComponent, SafeHtmlPipe, A11yClickDirective, MetadataDetailComponent, PersonBadgeComponent, TagBadgeComponent, PublicationStatusPipe, ReadMoreComponent],
|
||||
templateUrl: './series-preview-drawer.component.html',
|
||||
styleUrls: ['./series-preview-drawer.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class SeriesPreviewDrawerComponent implements OnInit {
|
||||
@Input({required: true}) name!: string;
|
||||
@Input() aniListId?: number;
|
||||
@Input() malId?: number;
|
||||
@Input() seriesId?: number;
|
||||
@Input() libraryId: number = 0;
|
||||
@Input({required: true}) isExternalSeries: boolean = true;
|
||||
|
||||
isLoading: boolean = true;
|
||||
localStaff: Array<SeriesStaff> = [];
|
||||
externalSeries: ExternalSeriesDetail | undefined;
|
||||
localSeries: SeriesMetadata | undefined;
|
||||
url: string = '';
|
||||
|
||||
private readonly activeOffcanvas = inject(NgbActiveOffcanvas);
|
||||
private readonly seriesService = inject(SeriesService);
|
||||
private readonly imageService = inject(ImageService);
|
||||
private readonly cdRef = inject(ChangeDetectorRef);
|
||||
|
||||
get CoverUrl() {
|
||||
if (this.isExternalSeries) {
|
||||
if (this.externalSeries) return this.externalSeries.coverUrl;
|
||||
return this.imageService.placeholderImage;
|
||||
}
|
||||
return this.imageService.getSeriesCoverImage(this.seriesId!);
|
||||
}
|
||||
|
||||
|
||||
ngOnInit() {
|
||||
if (this.isExternalSeries) {
|
||||
this.seriesService.getExternalSeriesDetails(this.aniListId, this.malId).subscribe(externalSeries => {
|
||||
this.externalSeries = externalSeries;
|
||||
this.isLoading = false;
|
||||
if (this.externalSeries.siteUrl) {
|
||||
this.url = this.externalSeries.siteUrl;
|
||||
}
|
||||
|
||||
console.log('External Series Detail: ', this.externalSeries);
|
||||
this.cdRef.markForCheck();
|
||||
});
|
||||
} else {
|
||||
this.seriesService.getMetadata(this.seriesId!).subscribe(data => {
|
||||
this.localSeries = data;
|
||||
this.isLoading = false;
|
||||
this.url = 'library/' + this.libraryId + '/series/' + this.seriesId;
|
||||
this.localStaff = data.writers.map(p => {
|
||||
return {name: p.name, role: 'Story & Art'} as SeriesStaff;
|
||||
});
|
||||
this.cdRef.markForCheck();
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
close() {
|
||||
this.activeOffcanvas.close();
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue