Misc bunch of changes (#2815)
This commit is contained in:
parent
18792b7b56
commit
63c9bff32e
81 changed files with 4567 additions and 339 deletions
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
|
|
|||
|
|
@ -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--{{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--{{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>
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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(',');
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue