Drawers, Estimated Reading Time, Korean Parsing Support (#1297)

* Started building out idea around detail drawer. Need code from word count to continue

* Fixed the logic for caluclating time to read on comics

* Adding styles

* more styling fixes

* Cleaned up the styles a bit more so it's at least functional. Not sure on the feature, might abandon.

* Pulled Robbie's changes in and partially migrated them to the drawer.

* Add offset overrides for offcanvas so it takes our header into account

* Implemented a basic time left to finish the series (or at least what's in Kavita). Rough around the edges.

* Cleaned up the drawer code.

* Added Quick Catch ups to recommended page. Updated the timeout for scan tasks to ensure we don't run 2 at the same time.

* Quick catchups implemented

* Added preliminary support for Korean filename parsing. Reduced an array alloc that is called many thousands of times per scan.

* Fixing drawer overflow

* Fixed a calculation bug with average reading time.

* Small spacing changes to drawer

* Don't show estimated reading time if the user hasn't read anything

* Bump eventsource from 1.1.1 to 2.0.2 in /UI/Web

Bumps [eventsource](https://github.com/EventSource/eventsource) from 1.1.1 to 2.0.2.
- [Release notes](https://github.com/EventSource/eventsource/releases)
- [Changelog](https://github.com/EventSource/eventsource/blob/master/HISTORY.md)
- [Commits](https://github.com/EventSource/eventsource/compare/v1.1.1...v2.0.2)

---
updated-dependencies:
- dependency-name: eventsource
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

* Added image to series detail drawer

Co-authored-by: Robbie Davis <robbie@therobbiedavis.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This commit is contained in:
Joseph Milazzo 2022-05-27 09:08:54 -05:00 committed by GitHub
parent d796bcdc0a
commit 63475722ea
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
40 changed files with 883 additions and 144 deletions

View file

@ -0,0 +1,248 @@
<div class="offcanvas-header">
<h5 class="offcanvas-title">
<ng-container [ngSwitch]="libraryType">
<ng-container *ngSwitchCase="LibraryType.Comic">
<span class="modal-title" id="modal-basic-title">
<ng-container *ngIf="chapter.titleName != ''; else fullComicTitle">
{{chapter.titleName}}
</ng-container>
<ng-template #fullComicTitle>
{{parentName}} - {{data.number != 0 ? (isChapter ? 'Chapter ' : 'Volume ') + data.number : 'Special'}}
</ng-template>
</span>
</ng-container>
<ng-container *ngSwitchCase="LibraryType.Manga">
<span class="modal-title" id="modal-basic-title">
<ng-container *ngIf="chapter.titleName != ''; else fullMangaTitle">
{{chapter.titleName}}
</ng-container>
<ng-template #fullMangaTitle>
{{parentName}} - {{data.number != 0 ? (isChapter ? 'Issue #' : 'Volume ') + data.number : 'Special'}}
</ng-template>
</span>
</ng-container>
<ng-container *ngSwitchCase="LibraryType.Book">
<span class="modal-title" id="modal-basic-title">
{{chapter.titleName}}
</span>
</ng-container>
</ng-container>
</h5>
<button type="button" class="btn-close text-reset" aria-label="Close" (click)="activeOffcanvas.dismiss()"></button>
</div>
<div class="offcanvas-body pb-3">
<div class="d-flex">
<ul ngbNav #nav="ngbNav" [(activeId)]="active" class="nav-pills" orientation="vertical" style="max-width: 135px;">
<li [ngbNavItem]="tabs[TabID.General]">
<a ngbNavLink>General</a>
<ng-template ngbNavContent>
<div class="container-fluid" style="overflow: auto">
<div class="row g-0">
<div class="d-none d-md-block col-md-2 col-lg-1">
<app-image class="me-2" width="74px" [imageUrl]="chapter.coverImage"></app-image>
</div>
<div *ngIf="summary$ | async as summary" class="col-md-10 col-lg-11">
<app-read-more [text]="summary" [maxLength]="250"></app-read-more>
</div>
</div>
<div class="row g-0 mt-4 mb-3">
<ng-container *ngIf="chapter.pages">
<div class="col-auto mb-2">
<app-icon-and-title [clickable]="false" fontClasses="fa-regular fa-file-lines" title="Pages">
{{chapter.pages}} Pages
</app-icon-and-title>
</div>
<div class="vr d-none d-lg-block m-2"></div>
</ng-container>
<ng-container *ngIf="chapterMetadata !== undefined && chapterMetadata.releaseDate">
<div class="col-auto mb-2">
<app-icon-and-title [clickable]="false" fontClasses="fa-regular fa-calendar" title="Release">
{{chapterMetadata.releaseDate | date:'shortDate'}}
</app-icon-and-title>
</div>
<div class="vr d-none d-lg-block m-2"></div>
</ng-container>
<ng-container *ngIf="chapter.files[0].format === MangaFormat.EPUB && chapterMetadata !== undefined && chapterMetadata.wordCount > 0 || chapter.files[0].format !== MangaFormat.EPUB">
<div class="col-auto mb-2">
<app-icon-and-title [clickable]="false" fontClasses="fa-regular fa-clock">
{{minHoursToRead}}{{maxHoursToRead !== minHoursToRead ? ('-' + maxHoursToRead) : ''}} Hour{{minHoursToRead > 1 ? 's' : ''}}
</app-icon-and-title>
</div>
</ng-container>
<ng-container *ngIf="chapter.files[0].format === MangaFormat.EPUB && chapterMetadata !== undefined && chapterMetadata.wordCount > 0">
<div class="vr d-none d-lg-block m-2"></div>
<div class="col-auto mb-2">
<app-icon-and-title [clickable]="false" fontClasses="fa-solid fa-book-open">
{{chapterMetadata.wordCount | compactNumber}} Words
</app-icon-and-title>
</div>
</ng-container>
<ng-container *ngIf="chapterMetadata !== undefined">
<ng-container *ngIf="ageRating !== '' && ageRating !== 'Unknown'">
<div class="vr d-none d-lg-block m-2"></div>
<div class="col-auto">
<app-icon-and-title [clickable]="false" fontClasses="fas fa-eye" title="Age Rating">
{{ageRating}}
</app-icon-and-title>
</div>
</ng-container>
</ng-container>
</div>
<!-- 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>Authors/Writers</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>Genres</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>Publisher</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>Tags</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>
Not defined
</ng-template>
</ng-container>
</div>
</ng-template>
</li>
<li [ngbNavItem]="tabs[TabID.Metadata]">
<a ngbNavLink>{{tabs[TabID.Metadata].title}}</a>
<ng-template ngbNavContent>
<app-chapter-metadata-detail [chapter]="chapterMetadata"></app-chapter-metadata-detail>
</ng-template>
</li>
<li [ngbNavItem]="tabs[TabID.Cover]">
<a ngbNavLink>{{tabs[TabID.Cover].title}}</a>
<ng-template ngbNavContent>
<app-cover-image-chooser [(imageUrls)]="imageUrls" (imageSelected)="updateSelectedIndex($event)" (selectedBase64Url)="updateSelectedImage($event)" [showReset]="chapter.coverImageLocked" (resetClicked)="handleReset()"></app-cover-image-chooser>
<div class="row g-0">
<button class="btn btn-primary flex-end mb-2" [disabled]="coverImageSaveLoading" (click)="saveCoverImage()">
<ng-container *ngIf="coverImageSaveLoading; else notSaving">
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
<span class="visually-hidden">Loading...</span>
</ng-container>
<ng-template #notSaving>
Save
</ng-template>
</button>
</div>
</ng-template>
</li>
<li [ngbNavItem]="tabs[TabID.Files]">
<a ngbNavLink>{{tabs[TabID.Files].title}}</a>
<ng-template ngbNavContent>
<div class="row g-0 mb-3">
<ng-container *ngIf="chapter.created && chapter.created !== '' && (chapter.created | date: 'shortDate') !== '1/1/01'">
<div class="col-auto">
<app-icon-and-title [clickable]="false" fontClasses="fa-solid fa-file-import" title="Date Added">
Created: {{chapter.created | date:'short' || '-'}}
</app-icon-and-title>
</div>
</ng-container>
<ng-container>
<div class="vr d-none d-lg-block m-2"></div>
<div class="col-auto">
<app-icon-and-title [clickable]="false" fontClasses="fa-solid fa-fingerprint" title="ID">
ID: {{data.id}}
</app-icon-and-title>
</div>
</ng-container>
</div>
<h4 *ngIf="!utilityService.isChapter(data)">{{utilityService.formatChapterName(libraryType) + 's'}}</h4>
<ul class="list-unstyled">
<li class="d-flex my-4" *ngFor="let chapter of chapters">
<a (click)="readChapter(chapter)" href="javascript:void(0);" title="Read {{utilityService.formatChapterName(libraryType, true, false)}} {{formatChapterNumber(chapter)}}">
<app-image class="me-2" width="74px" [imageUrl]="chapter.coverImage"></app-image>
</a>
<div class="flex-grow-1">
<h5 class="mt-0 mb-1">
<span >
<span>
<app-card-actionables (actionHandler)="performAction($event, chapter)" [actions]="chapterActions"
[labelBy]="utilityService.formatChapterName(libraryType, true, true) + formatChapterNumber(chapter)"></app-card-actionables>
<ng-container *ngIf="chapter.number !== '0'; else specialHeader">
{{utilityService.formatChapterName(libraryType, true, false) }} {{formatChapterNumber(chapter)}}
</ng-container>
</span>
<span class="badge bg-primary rounded-pill ms-1">
<span *ngIf="chapter.pagesRead > 0 && chapter.pagesRead < chapter.pages">{{chapter.pagesRead}} / {{chapter.pages}}</span>
<span *ngIf="chapter.pagesRead === 0">UNREAD</span>
<span *ngIf="chapter.pagesRead === chapter.pages">READ</span>
</span>
</span>
<ng-template #specialHeader>Files</ng-template>
</h5>
<ul class="list-group">
<li *ngFor="let file of chapter.files" class="list-group-item no-hover">
<span>{{file.filePath}}</span>
<div class="row g-0">
<div class="col">
Pages: {{file.pages}}
</div>
<div class="col" *ngIf="data.hasOwnProperty('created')">
Added: {{(data.created | date: 'short') || '-'}}
</div>
</div>
</li>
</ul>
</div>
</li>
</ul>
</ng-template>
</li>
</ul>
<div [ngbNavOutlet]="nav" class="tab-content {{utilityService.getActiveBreakpoint() === Breakpoint.Mobile ? 'mt-3' : 'ms-4 flex-fill'}}"></div>
</div>
</div>