Misc bunch of changes (#2815)

This commit is contained in:
Joe Milazzo 2024-03-23 16:20:16 -05:00 committed by GitHub
parent 18792b7b56
commit 63c9bff32e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
81 changed files with 4567 additions and 339 deletions

View file

@ -9,15 +9,19 @@
</span>
<span>
<button *ngIf="hasMarkAsUnread" class="btn btn-icon" (click)="executeAction(Action.MarkAsUnread)" [ngbTooltip]="t('mark-as-unread')" placement="bottom">
@if (hasMarkAsUnread) {
<button class="btn btn-icon" (click)="executeAction(Action.MarkAsUnread)" [ngbTooltip]="t('mark-as-unread')" placement="bottom">
<i class="fa-regular fa-circle-check" aria-hidden="true"></i>
<span class="visually-hidden">{{t('mark-as-unread')}}</span>
</button>
<button *ngIf="hasMarkAsRead" class="btn btn-icon" (click)="executeAction(Action.MarkAsRead)" [ngbTooltip]="t('mark-as-read')" placement="bottom">
}
@if (hasMarkAsRead) {
<button class="btn btn-icon" (click)="executeAction(Action.MarkAsRead)" [ngbTooltip]="t('mark-as-read')" placement="bottom">
<i class="fa-solid fa-circle-check" aria-hidden="true"></i>
<span class="visually-hidden">{{t('mark-as-read')}}</span>
</button>
<app-card-actionables [actions]="actions" labelBy="bulk-actions-header" iconClass="fa-ellipsis-h" (actionHandler)="performAction($event)"></app-card-actionables>
}
<app-card-actionables [actions]="actions" labelBy="bulk-actions-header" iconClass="fa-ellipsis-h" (actionHandler)="performAction($event)"></app-card-actionables>
</span>
<span id="bulk-actions-header" class="visually-hidden">Bulk Actions</span>

View file

@ -32,59 +32,6 @@
<app-entity-info-cards [entity]="data" [libraryId]="libraryId"></app-entity-info-cards>
<!-- 2 rows to show some tags-->
<ng-container *ngIf="chapterMetadata !== undefined">
<div class="row g-0 mb-2">
<div class="col-md-6 col-sm-12">
<h6>{{t('writers-title')}}</h6>
<ng-container *ngIf="chapterMetadata.writers.length > 0; else noBadges">
<app-badge-expander [items]="chapterMetadata.writers">
<ng-template #badgeExpanderItem let-item let-position="idx">
<app-person-badge [person]="item"></app-person-badge>
</ng-template>
</app-badge-expander>
</ng-container>
</div>
<div class="col-md-6 col-sm-12">
<h6>{{t('genres-title')}}</h6>
<ng-container *ngIf="chapterMetadata.genres.length > 0; else noBadges">
<app-badge-expander [items]="chapterMetadata.genres">
<ng-template #badgeExpanderItem let-item let-position="idx">
<app-tag-badge>{{item.title}}</app-tag-badge>
</ng-template>
</app-badge-expander>
</ng-container>
</div>
</div>
<div class="row g-0 mb-2">
<div class="col-md-6 col-sm-12">
<h6>{{t('publishers-title')}}</h6>
<ng-container *ngIf="chapterMetadata.publishers.length > 0; else noBadges">
<app-badge-expander [items]="chapterMetadata.publishers">
<ng-template #badgeExpanderItem let-item let-position="idx">
<app-person-badge [person]="item"></app-person-badge>
</ng-template>
</app-badge-expander>
</ng-container>
</div>
<div class="col-md-6 col-sm-12">
<h6>{{t('tags-title')}}</h6>
<ng-container *ngIf="chapterMetadata.tags.length > 0; else noBadges">
<app-badge-expander [items]="chapterMetadata.tags">
<ng-template #badgeExpanderItem let-item let-position="idx">
<app-tag-badge>{{item.title}}</app-tag-badge>
</ng-template>
</app-badge-expander>
</ng-container>
</div>
</div>
<ng-template #noBadges>
{{t('not-defined')}}
</ng-template>
</ng-container>
</div>
</ng-template>
</li>
@ -96,6 +43,13 @@
</ng-template>
</li>
<li [ngbNavItem]="tabs[TabID.Progress]">
<a ngbNavLink>{{t(tabs[TabID.Progress].title)}}</a>
<ng-template ngbNavContent>
<app-edit-chapter-progress [chapter]="chapter"></app-edit-chapter-progress>
</ng-template>
</li>
<li [ngbNavItem]="tabs[TabID.Cover]" [disabled]="(isAdmin$ | async) === false">
<a ngbNavLink>{{t(tabs[TabID.Cover].title)}}</a>
<ng-template ngbNavContent>
@ -113,8 +67,7 @@
}
<ul class="list-unstyled">
<li class="d-flex my-4" *ngFor="let chapter of chapters">
<!-- TODO: Localize title -->
<a (click)="readChapter(chapter)" href="javascript:void(0);" title="Read">
<a (click)="readChapter(chapter)" href="javascript:void(0);" [title]="t('read')">
<app-image class="me-2" width="74px" [imageUrl]="imageService.getChapterCoverImage(chapter.id)"></app-image>
</a>
<div class="flex-grow-1">

View file

@ -50,18 +50,20 @@ import {TagBadgeComponent} from "../../shared/tag-badge/tag-badge.component";
import {PersonBadgeComponent} from "../../shared/person-badge/person-badge.component";
import {translate, TranslocoDirective, TranslocoService} from "@ngneat/transloco";
import {CardActionablesComponent} from "../../_single-module/card-actionables/card-actionables.component";
import {EditChapterProgressComponent} from "../edit-chapter-progress/edit-chapter-progress.component";
enum TabID {
General = 0,
Metadata = 1,
Cover = 2,
Files = 3
Progress = 3,
Files = 4
}
@Component({
selector: 'app-card-detail-drawer',
standalone: true,
imports: [CommonModule, EntityTitleComponent, NgbNav, NgbNavItem, NgbNavLink, NgbNavContent, ImageComponent, ReadMoreComponent, EntityInfoCardsComponent, CoverImageChooserComponent, ChapterMetadataDetailComponent, CardActionablesComponent, DefaultDatePipe, BytesPipe, NgbNavOutlet, BadgeExpanderComponent, TagBadgeComponent, PersonBadgeComponent, TranslocoDirective],
imports: [CommonModule, EntityTitleComponent, NgbNav, NgbNavItem, NgbNavLink, NgbNavContent, ImageComponent, ReadMoreComponent, EntityInfoCardsComponent, CoverImageChooserComponent, ChapterMetadataDetailComponent, CardActionablesComponent, DefaultDatePipe, BytesPipe, NgbNavOutlet, BadgeExpanderComponent, TagBadgeComponent, PersonBadgeComponent, TranslocoDirective, EditChapterProgressComponent],
templateUrl: './card-detail-drawer.component.html',
styleUrls: ['./card-detail-drawer.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
@ -106,6 +108,7 @@ export class CardDetailDrawerComponent implements OnInit {
{title: 'general-tab', disabled: false},
{title: 'metadata-tab', disabled: false},
{title: 'cover-tab', disabled: false},
{title: 'progress-tab', disabled: false},
{title: 'info-tab', disabled: false}
];
active = this.tabs[0];

View file

@ -0,0 +1,48 @@
<ng-container *transloco="let t; read: 'edit-chapter-progress'">
<table class="table table-striped" [formGroup]="formGroup">
<thead>
<tr>
<th scope="col">{{t('user-header')}}</th>
<th scope="col">{{t('page-read-header')}}</th>
<th scope="col">{{t('date-created-header')}}</th>
<th scope="col">{{t('date-updated-header')}}</th>
<!-- <th scope="col">{{t('action-header')}}</th>-->
</tr>
</thead>
<tbody>
@for(rowForm of items.controls; track rowForm; let idx = $index) {
<tr >
<td id="progress-event--{{idx}}">
{{progressEvents[idx].userName}}
</td>
<td>
@if(editMode[idx]) {
<input type="number" formControlName="pagesRead" class="form-control"/>
} @else {
{{progressEvents[idx].pagesRead}}
}
</td>
<td>
{{progressEvents[idx].createdUtc}}
</td>
<td>
{{progressEvents[idx].lastModifiedUtc}}
</td>
<!-- <td>-->
<!-- @if(editMode[idx]) {-->
<!-- <button class="btn btn-primary" (click)="save(progressEvents[idx], idx)" attr.aria-labelledby="progress-event&#45;&#45;{{idx}}">-->
<!-- <i class="fa-solid fa-floppy-disk" aria-hidden="true"></i>-->
<!-- <span class="visually-hidden">{{t('save-alt')}}</span>-->
<!-- </button>-->
<!-- } @else {-->
<!-- <button class="btn btn-primary" (click)="edit(progressEvents[idx], idx)" attr.aria-labelledby="progress-event&#45;&#45;{{idx}}">-->
<!-- <i class="fa-solid fa-pencil" aria-hidden="true"></i>-->
<!-- <span class="visually-hidden">{{t('edit-alt')}}</span>-->
<!-- </button>-->
<!-- }-->
<!-- </td>-->
</tr>
}
</tbody>
</table>
</ng-container>

View file

@ -0,0 +1,80 @@
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, inject, Input, OnInit} from '@angular/core';
import {Chapter} from "../../_models/chapter";
import {AsyncPipe, NgForOf, TitleCasePipe} from "@angular/common";
import {DefaultValuePipe} from "../../_pipes/default-value.pipe";
import {UtcToLocalTimePipe} from "../../_pipes/utc-to-local-time.pipe";
import {FullProgress} from "../../_models/readers/full-progress";
import {ReaderService} from "../../_services/reader.service";
import {TranslocoDirective} from "@ngneat/transloco";
import {FormArray, FormBuilder, FormGroup, ReactiveFormsModule, Validators} from "@angular/forms";
@Component({
selector: 'app-edit-chapter-progress',
standalone: true,
imports: [
AsyncPipe,
DefaultValuePipe,
NgForOf,
TitleCasePipe,
UtcToLocalTimePipe,
TranslocoDirective,
ReactiveFormsModule
],
templateUrl: './edit-chapter-progress.component.html',
styleUrl: './edit-chapter-progress.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class EditChapterProgressComponent implements OnInit {
private readonly readerService = inject(ReaderService);
private readonly cdRef = inject(ChangeDetectorRef);
private readonly fb = inject(FormBuilder);
@Input({required: true}) chapter!: Chapter;
progressEvents: Array<FullProgress> = [];
editMode: {[key: number]: boolean} = {};
formGroup = this.fb.group({
items: this.fb.array([])
});
get items() {
return this.formGroup.get('items') as FormArray;
}
ngOnInit() {
this.readerService.getAllProgressForChapter(this.chapter!.id).subscribe(res => {
this.progressEvents = res;
this.progressEvents.forEach((v, i) => {
this.editMode[i] = false;
this.items.push(this.createRowForm(v));
});
this.cdRef.markForCheck();
});
}
createRowForm(progress: FullProgress): FormGroup {
return this.fb.group({
pagesRead: [progress.pagesRead, [Validators.required, Validators.min(0), Validators.max(this.chapter!.pages)]],
created: [progress.createdUtc, [Validators.required]],
lastModified: [progress.lastModifiedUtc, [Validators.required]],
});
}
edit(progress: FullProgress, idx: number) {
this.editMode[idx] = !this.editMode[idx];
this.cdRef.markForCheck();
}
save(progress: FullProgress, idx: number) {
// todo
this.editMode[idx] = !this.editMode[idx];
// this.formGroup[idx + ''].patchValue({
// pagesRead: progress.pagesRead,
// // Patch other form values as needed
// });
this.cdRef.markForCheck();
}
}

View file

@ -1,6 +1,6 @@
<ng-container *transloco="let t; read: 'entity-info-cards'">
<div class="mt-4 mb-3">
<div class="mt-3 mb-3">
<div class="row g-0" *ngIf="chapterMetadata ">
<!-- Tags and Characters are used a lot of Hentai and Doujinshi type content, so showing in list item has value add on first glance -->
<app-metadata-detail [tags]="chapterMetadata.tags" [libraryId]="libraryId" [queryParam]="FilterField.Tags" heading="Tags">
@ -90,11 +90,8 @@
<div class="col-auto">
<app-icon-and-title [label]="t('links-title')" [clickable]="false" fontClasses="fa-solid fa-link" [title]="t('links-title')">
<a class="me-1" [href]="link | safeHtml" *ngFor="let link of WebLinks" target="_blank" rel="noopener noreferrer" [title]="link">
<img width="24" height="24" #img class="lazyload img-placeholder"
src="data:image/gif;base64,R0lGODlhAQABAPAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=="
[attr.data-src]="imageService.getWebLinkImage(link)"
(error)="imageService.updateErroredWebLinkImage($event)"
aria-hidden="true" alt="">
<app-image height="24px" width="24px" aria-hidden="true" [imageUrl]="imageService.getWebLinkImage(link)"
[errorImage]="imageService.errorWebLinkImage"></app-image>
</a>
</app-icon-and-title>
</div>
@ -117,6 +114,15 @@
</app-icon-and-title>
</div>
</ng-container>
<ng-container *ngIf="isChapter">
<div class="vr d-none d-lg-block m-2"></div>
<div class="col-auto">
<app-icon-and-title [label]="t('sort-order-title')" [clickable]="false" fontClasses="fa-solid fa-arrow-down-1-9" [title]="t('sort-order-title')">
{{chapter.sortOrder}}
</app-icon-and-title>
</div>
</ng-container>
</ng-container>
</div>
</div>

View file

@ -10,7 +10,6 @@ import { UtilityService } from 'src/app/shared/_services/utility.service';
import { Chapter } from 'src/app/_models/chapter';
import { ChapterMetadata } from 'src/app/_models/metadata/chapter-metadata';
import { HourEstimateRange } from 'src/app/_models/series-detail/hour-estimate-range';
import { LibraryType } from 'src/app/_models/library/library';
import { MangaFormat } from 'src/app/_models/manga-format';
import { AgeRating } from 'src/app/_models/metadata/age-rating';
import { Volume } from 'src/app/_models/volume';
@ -29,17 +28,27 @@ import {TranslocoModule} from "@ngneat/transloco";
import {TranslocoLocaleModule} from "@ngneat/transloco-locale";
import {FilterField} from "../../_models/metadata/v2/filter-field";
import {UtcToLocalTimePipe} from "../../_pipes/utc-to-local-time.pipe";
import {ImageComponent} from "../../shared/image/image.component";
@Component({
selector: 'app-entity-info-cards',
standalone: true,
imports: [CommonModule, IconAndTitleComponent, SafeHtmlPipe, DefaultDatePipe, BytesPipe, CompactNumberPipe, AgeRatingPipe, NgbTooltip, MetadataDetailComponent, TranslocoModule, CompactNumberPipe, TranslocoLocaleModule, UtcToLocalTimePipe],
imports: [CommonModule, IconAndTitleComponent, SafeHtmlPipe, DefaultDatePipe, BytesPipe, CompactNumberPipe,
AgeRatingPipe, NgbTooltip, MetadataDetailComponent, TranslocoModule, CompactNumberPipe, TranslocoLocaleModule,
UtcToLocalTimePipe, ImageComponent],
templateUrl: './entity-info-cards.component.html',
styleUrls: ['./entity-info-cards.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class EntityInfoCardsComponent implements OnInit {
protected readonly AgeRating = AgeRating;
protected readonly MangaFormat = MangaFormat;
protected readonly FilterField = FilterField;
public readonly imageService = inject(ImageService);
@Input({required: true}) entity!: Volume | Chapter;
@Input({required: true}) libraryId!: number;
/**
@ -48,7 +57,7 @@ export class EntityInfoCardsComponent implements OnInit {
@Input() includeMetadata: boolean = false;
/**
* Hide more system based fields, like Id or Date Added
* Hide more system based fields, like id or Date Added
*/
@Input() showExtendedProperties: boolean = true;
@ -62,22 +71,6 @@ export class EntityInfoCardsComponent implements OnInit {
readingTime: HourEstimateRange = {maxHours: 1, minHours: 1, avgHours: 1};
size: number = 0;
imageService = inject(ImageService);
get LibraryType() {
return LibraryType;
}
get MangaFormat() {
return MangaFormat;
}
get AgeRating() {
return AgeRating;
}
get FilterField() { return FilterField; }
get WebLinks() {
if (this.chapter.webLinks === '') return [];
return this.chapter.webLinks.split(',');