mirror of
https://github.com/Kareadita/Kavita.git
synced 2025-07-09 03:04:19 -04:00
Release Shakeout Part 1 (#1440)
* Bumped docnet back up, as user issue was not related to the version. Reworked the logic flow for ConfirmEmailToken. Added new test cases for a bug reported around Docnet and weird characters. * Removed a duplicate remove from want to read list * Fixed an issue where series detail didn't appopriately handle remove from want to read. * Added pagination information to want to read, fixed remove from want to read not reloading page * When clearing a series of bookmarks, automatically refresh page. * Added a continue button on reading list page so user can continue where they left off (progress) or start at beginning * Added todo about design idea * Added a bug marker
This commit is contained in:
parent
a3a0b61fc0
commit
9d90652792
@ -22,16 +22,18 @@ namespace API.Tests.Services
|
|||||||
[InlineData("The Golden Harpoon; Or, Lost Among the Floes A Story of the Whaling Grounds.epub", 16)]
|
[InlineData("The Golden Harpoon; Or, Lost Among the Floes A Story of the Whaling Grounds.epub", 16)]
|
||||||
[InlineData("Non-existent file.epub", 0)]
|
[InlineData("Non-existent file.epub", 0)]
|
||||||
[InlineData("Non an ebub.pdf", 0)]
|
[InlineData("Non an ebub.pdf", 0)]
|
||||||
|
[InlineData("test_ſ.pdf", 1)] // This is dependent on Docnet bug https://github.com/GowenGit/docnet/issues/80
|
||||||
|
[InlineData("test.pdf", 1)]
|
||||||
public void GetNumberOfPagesTest(string filePath, int expectedPages)
|
public void GetNumberOfPagesTest(string filePath, int expectedPages)
|
||||||
{
|
{
|
||||||
var testDirectory = Path.Join(Directory.GetCurrentDirectory(), "../../../Services/Test Data/BookService/EPUB");
|
var testDirectory = Path.Join(Directory.GetCurrentDirectory(), "../../../Services/Test Data/BookService");
|
||||||
Assert.Equal(expectedPages, _bookService.GetNumberOfPages(Path.Join(testDirectory, filePath)));
|
Assert.Equal(expectedPages, _bookService.GetNumberOfPages(Path.Join(testDirectory, filePath)));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ShouldHaveComicInfo()
|
public void ShouldHaveComicInfo()
|
||||||
{
|
{
|
||||||
var testDirectory = Path.Join(Directory.GetCurrentDirectory(), "../../../Services/Test Data/BookService/EPUB");
|
var testDirectory = Path.Join(Directory.GetCurrentDirectory(), "../../../Services/Test Data/BookService");
|
||||||
var archive = Path.Join(testDirectory, "The Golden Harpoon; Or, Lost Among the Floes A Story of the Whaling Grounds.epub");
|
var archive = Path.Join(testDirectory, "The Golden Harpoon; Or, Lost Among the Floes A Story of the Whaling Grounds.epub");
|
||||||
const string summaryInfo = "Book Description";
|
const string summaryInfo = "Book Description";
|
||||||
|
|
||||||
@ -44,7 +46,7 @@ namespace API.Tests.Services
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void ShouldHaveComicInfo_WithAuthors()
|
public void ShouldHaveComicInfo_WithAuthors()
|
||||||
{
|
{
|
||||||
var testDirectory = Path.Join(Directory.GetCurrentDirectory(), "../../../Services/Test Data/BookService/EPUB");
|
var testDirectory = Path.Join(Directory.GetCurrentDirectory(), "../../../Services/Test Data/BookService");
|
||||||
var archive = Path.Join(testDirectory, "The Golden Harpoon; Or, Lost Among the Floes A Story of the Whaling Grounds.epub");
|
var archive = Path.Join(testDirectory, "The Golden Harpoon; Or, Lost Among the Floes A Story of the Whaling Grounds.epub");
|
||||||
|
|
||||||
var comicInfo = _bookService.GetComicInfo(archive);
|
var comicInfo = _bookService.GetComicInfo(archive);
|
||||||
@ -52,16 +54,5 @@ namespace API.Tests.Services
|
|||||||
Assert.Equal("Roger Starbuck,Junya Inoue", comicInfo.Writer);
|
Assert.Equal("Roger Starbuck,Junya Inoue", comicInfo.Writer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#region BookEscaping
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void EscapeCSSImportReferencesTest()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2014,7 +2014,6 @@ public class ReaderServiceTests
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
#region MarkSeriesAsRead
|
#region MarkSeriesAsRead
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
BIN
API.Tests/Services/Test Data/BookService/test.pdf
Normal file
BIN
API.Tests/Services/Test Data/BookService/test.pdf
Normal file
Binary file not shown.
BIN
API.Tests/Services/Test Data/BookService/test_ſ.pdf
Normal file
BIN
API.Tests/Services/Test Data/BookService/test_ſ.pdf
Normal file
Binary file not shown.
@ -40,7 +40,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="11.0.0" />
|
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="11.0.0" />
|
||||||
<PackageReference Include="Docnet.Core" Version="2.4.0-alpha.2" />
|
<PackageReference Include="Docnet.Core" Version="2.4.0-alpha.4" />
|
||||||
<PackageReference Include="ExCSS" Version="4.1.0" />
|
<PackageReference Include="ExCSS" Version="4.1.0" />
|
||||||
<PackageReference Include="Flurl" Version="3.0.6" />
|
<PackageReference Include="Flurl" Version="3.0.6" />
|
||||||
<PackageReference Include="Flurl.Http" Version="3.2.4" />
|
<PackageReference Include="Flurl.Http" Version="3.2.4" />
|
||||||
|
@ -727,21 +727,18 @@ namespace API.Controllers
|
|||||||
private async Task<bool> ConfirmEmailToken(string token, AppUser user)
|
private async Task<bool> ConfirmEmailToken(string token, AppUser user)
|
||||||
{
|
{
|
||||||
var result = await _userManager.ConfirmEmailAsync(user, token);
|
var result = await _userManager.ConfirmEmailAsync(user, token);
|
||||||
if (!result.Succeeded)
|
if (result.Succeeded) return true;
|
||||||
{
|
|
||||||
_logger.LogCritical("Email validation failed");
|
_logger.LogCritical("[Account] Email validation failed");
|
||||||
if (result.Errors.Any())
|
if (!result.Errors.Any()) return false;
|
||||||
{
|
|
||||||
foreach (var error in result.Errors)
|
foreach (var error in result.Errors)
|
||||||
{
|
{
|
||||||
_logger.LogCritical("Email validation error: {Message}", error.Description);
|
_logger.LogCritical("[Account] Email validation error: {Message}", error.Description);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,8 @@
|
|||||||
[isLoading]="loadingBookmarks"
|
[isLoading]="loadingBookmarks"
|
||||||
[items]="series"
|
[items]="series"
|
||||||
[trackByIdentity]="trackByIdentity"
|
[trackByIdentity]="trackByIdentity"
|
||||||
|
[refresh]="refresh"
|
||||||
|
|
||||||
>
|
>
|
||||||
<ng-template #cardItem let-item let-position="idx">
|
<ng-template #cardItem let-item let-position="idx">
|
||||||
<app-card-item [entity]="item" (reload)="loadBookmarks()" [title]="item.name" [imageUrl]="imageService.getSeriesCoverImage(item.id)"
|
<app-card-item [entity]="item" (reload)="loadBookmarks()" [title]="item.name" [imageUrl]="imageService.getSeriesCoverImage(item.id)"
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, HostListener, OnDestroy, OnInit } from '@angular/core';
|
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, HostListener, OnDestroy, OnInit } from '@angular/core';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { ToastrService } from 'ngx-toastr';
|
import { ToastrService } from 'ngx-toastr';
|
||||||
import { take, Subject } from 'rxjs';
|
import { take, Subject } from 'rxjs';
|
||||||
@ -30,6 +30,7 @@ export class BookmarksComponent implements OnInit, OnDestroy {
|
|||||||
actions: ActionItem<Series>[] = [];
|
actions: ActionItem<Series>[] = [];
|
||||||
|
|
||||||
trackByIdentity = (index: number, item: Series) => `${item.name}_${item.localizedName}_${item.pagesRead}`;
|
trackByIdentity = (index: number, item: Series) => `${item.name}_${item.localizedName}_${item.pagesRead}`;
|
||||||
|
refresh: EventEmitter<void> = new EventEmitter();
|
||||||
|
|
||||||
private onDestroy: Subject<void> = new Subject<void>();
|
private onDestroy: Subject<void> = new Subject<void>();
|
||||||
|
|
||||||
@ -153,6 +154,7 @@ export class BookmarksComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
this.clearingSeries[series.id] = false;
|
this.clearingSeries[series.id] = false;
|
||||||
this.toastr.success(series.name + '\'s bookmarks have been removed');
|
this.toastr.success(series.name + '\'s bookmarks have been removed');
|
||||||
|
this.refresh.emit();
|
||||||
this.cdRef.markForCheck();
|
this.cdRef.markForCheck();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -146,14 +146,7 @@ export class BulkSelectionService {
|
|||||||
// else returns volume/chapter items
|
// else returns volume/chapter items
|
||||||
const allowedActions = [Action.AddToReadingList, Action.MarkAsRead, Action.MarkAsUnread, Action.AddToCollection, Action.Delete, Action.AddToWantToReadList, Action.RemoveFromWantToReadList];
|
const allowedActions = [Action.AddToReadingList, Action.MarkAsRead, Action.MarkAsUnread, Action.AddToCollection, Action.Delete, Action.AddToWantToReadList, Action.RemoveFromWantToReadList];
|
||||||
if (Object.keys(this.selectedCards).filter(item => item === 'series').length > 0) {
|
if (Object.keys(this.selectedCards).filter(item => item === 'series').length > 0) {
|
||||||
let actions = this.actionFactory.getSeriesActions(callback).filter(item => allowedActions.includes(item.action));
|
return this.actionFactory.getSeriesActions(callback).filter(item => allowedActions.includes(item.action));
|
||||||
if (this.activeRoute.startsWith('/want-to-read')) {
|
|
||||||
const removeFromWantToRead = {...actions[0]};
|
|
||||||
removeFromWantToRead.action = Action.RemoveFromWantToReadList;
|
|
||||||
removeFromWantToRead.title = 'Remove from Want to Read';
|
|
||||||
actions.push(removeFromWantToRead);
|
|
||||||
}
|
|
||||||
return actions;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Object.keys(this.selectedCards).filter(item => item === 'bookmark').length > 0) {
|
if (Object.keys(this.selectedCards).filter(item => item === 'bookmark').length > 0) {
|
||||||
|
@ -44,6 +44,7 @@ export class CardDetailLayoutComponent implements OnInit, OnDestroy, OnChanges,
|
|||||||
@Input() actions: ActionItem<any>[] = [];
|
@Input() actions: ActionItem<any>[] = [];
|
||||||
@Input() trackByIdentity!: TrackByFunction<any>; //(index: number, item: any) => string
|
@Input() trackByIdentity!: TrackByFunction<any>; //(index: number, item: any) => string
|
||||||
@Input() filterSettings!: FilterSettings;
|
@Input() filterSettings!: FilterSettings;
|
||||||
|
@Input() refresh!: EventEmitter<void>;
|
||||||
|
|
||||||
|
|
||||||
@Input() jumpBarKeys: Array<JumpKey> = []; // This is aprox 784 pixels wide
|
@Input() jumpBarKeys: Array<JumpKey> = []; // This is aprox 784 pixels wide
|
||||||
@ -100,6 +101,13 @@ export class CardDetailLayoutComponent implements OnInit, OnDestroy, OnChanges,
|
|||||||
this.pagination = {currentPage: 1, itemsPerPage: this.items.length, totalItems: this.items.length, totalPages: 1};
|
this.pagination = {currentPage: 1, itemsPerPage: this.items.length, totalItems: this.items.length, totalPages: 1};
|
||||||
this.changeDetectionRef.markForCheck();
|
this.changeDetectionRef.markForCheck();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.refresh) {
|
||||||
|
this.refresh.subscribe(() => {
|
||||||
|
this.changeDetectionRef.markForCheck();
|
||||||
|
this.virtualScroller.refresh();
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ngAfterViewInit(): void {
|
ngAfterViewInit(): void {
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output } from '@angular/core';
|
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output } from '@angular/core';
|
||||||
import { Router } from '@angular/router';
|
import { NavigationStart, Router } from '@angular/router';
|
||||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||||
import { ToastrService } from 'ngx-toastr';
|
import { ToastrService } from 'ngx-toastr';
|
||||||
import { take } from 'rxjs/operators';
|
import { filter, take } from 'rxjs/operators';
|
||||||
import { Series } from 'src/app/_models/series';
|
import { Series } from 'src/app/_models/series';
|
||||||
import { AccountService } from 'src/app/_services/account.service';
|
import { AccountService } from 'src/app/_services/account.service';
|
||||||
import { ImageService } from 'src/app/_services/image.service';
|
import { ImageService } from 'src/app/_services/image.service';
|
||||||
@ -102,6 +102,9 @@ export class SeriesCardComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
break;
|
break;
|
||||||
case Action.RemoveFromWantToReadList:
|
case Action.RemoveFromWantToReadList:
|
||||||
this.actionService.removeMultipleSeriesFromWantToReadList([series.id]);
|
this.actionService.removeMultipleSeriesFromWantToReadList([series.id]);
|
||||||
|
if (this.router.url.startsWith('/want-to-read')) {
|
||||||
|
this.reload.emit(true);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case(Action.AddToCollection):
|
case(Action.AddToCollection):
|
||||||
this.actionService.addMultipleSeriesToCollectionTag([series]);
|
this.actionService.addMultipleSeriesToCollectionTag([series]);
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
<div cdkDropList class="{{items.length > 0 ? 'example-list list-group-flush' : ''}}" (cdkDropListDropped)="drop($event)">
|
<div cdkDropList class="{{items.length > 0 ? 'example-list list-group-flush' : ''}}" (cdkDropListDropped)="drop($event)">
|
||||||
|
<!-- BUG: https://github.com/angular/components/issues/14098 -->
|
||||||
<div class="example-box" *ngFor="let item of items; index as i" cdkDrag [cdkDragData]="item" cdkDragBoundary=".example-list">
|
<div class="example-box" *ngFor="let item of items; index as i" cdkDrag [cdkDragData]="item" cdkDragBoundary=".example-list">
|
||||||
<div class="d-flex list-container">
|
<div class="d-flex list-container">
|
||||||
<div class="me-3 align-middle">
|
<div class="me-3 align-middle">
|
||||||
|
@ -18,10 +18,16 @@
|
|||||||
<div class="row g-0 mb-3">
|
<div class="row g-0 mb-3">
|
||||||
<div class="col-auto me-2">
|
<div class="col-auto me-2">
|
||||||
<!-- Action row-->
|
<!-- Action row-->
|
||||||
<button class="btn btn-primary" title="Read" (click)="read()">
|
<button class="btn btn-primary" title="Read from beginning" (click)="read()">
|
||||||
|
<span>
|
||||||
|
<i class="fa fa-book" aria-hidden="true"></i>
|
||||||
|
<span class="read-btn--text"> Read</span>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-primary ms-2" title="Continue from last reading position" (click)="continue()">
|
||||||
<span>
|
<span>
|
||||||
<i class="fa fa-book-open" aria-hidden="true"></i>
|
<i class="fa fa-book-open" aria-hidden="true"></i>
|
||||||
<span class="read-btn--text"> Read</span>
|
<span class="read-btn--text"> Continue</span>
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@ -33,6 +39,7 @@
|
|||||||
<span class="read-btn--text"> Remove Read</span>
|
<span class="read-btn--text"> Remove Read</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- TODO: Move this in companion bar's page actions -->
|
||||||
<div class="col-auto ms-2 mt-2" *ngIf="!(readingList?.promoted && !this.isAdmin)">
|
<div class="col-auto ms-2 mt-2" *ngIf="!(readingList?.promoted && !this.isAdmin)">
|
||||||
<div class="form-check form-check-inline">
|
<div class="form-check form-check-inline">
|
||||||
<input class="form-check-input" type="checkbox" id="accessibilit-mode" [value]="accessibilityMode" (change)="accessibilityMode = !accessibilityMode">
|
<input class="form-check-input" type="checkbox" id="accessibilit-mode" [value]="accessibilityMode" (change)="accessibilityMode = !accessibilityMode">
|
||||||
|
@ -176,6 +176,12 @@ export class ReadingListDetailComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
read() {
|
read() {
|
||||||
|
if (!this.readingList) return;
|
||||||
|
const firstItem = this.items[0];
|
||||||
|
this.router.navigate(this.readerService.getNavigationArray(firstItem.libraryId, firstItem.seriesId, firstItem.chapterId, firstItem.seriesFormat), {queryParams: {readingListId: this.readingList.id}});
|
||||||
|
}
|
||||||
|
|
||||||
|
continue() {
|
||||||
// TODO: Can I do this in the backend?
|
// TODO: Can I do this in the backend?
|
||||||
if (!this.readingList) return;
|
if (!this.readingList) return;
|
||||||
let currentlyReadingChapter = this.items[0];
|
let currentlyReadingChapter = this.items[0];
|
||||||
|
@ -384,6 +384,12 @@ export class SeriesDetailComponent implements OnInit, OnDestroy, AfterContentChe
|
|||||||
this.changeDetectionRef.markForCheck();
|
this.changeDetectionRef.markForCheck();
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
case Action.RemoveFromWantToReadList:
|
||||||
|
this.actionService.removeMultipleSeriesFromWantToReadList([series.id], () => {
|
||||||
|
this.actionInProgress = false;
|
||||||
|
this.changeDetectionRef.markForCheck();
|
||||||
|
});
|
||||||
|
break;
|
||||||
case (Action.Download):
|
case (Action.Download):
|
||||||
if (this.downloadInProgress) return;
|
if (this.downloadInProgress) return;
|
||||||
this.downloadSeries();
|
this.downloadSeries();
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
Want To Read
|
Want To Read
|
||||||
</h2>
|
</h2>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
<h6 subtitle>{{seriesPagination.totalItems}} Series</h6>
|
||||||
</app-side-nav-companion-bar>
|
</app-side-nav-companion-bar>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -43,7 +43,6 @@ export class WantToReadComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
filterOpen: EventEmitter<boolean> = new EventEmitter();
|
filterOpen: EventEmitter<boolean> = new EventEmitter();
|
||||||
|
|
||||||
|
|
||||||
private onDestory: Subject<void> = new Subject<void>();
|
private onDestory: Subject<void> = new Subject<void>();
|
||||||
trackByIdentity = (index: number, item: Series) => `${item.name}_${item.localizedName}_${item.pagesRead}`;
|
trackByIdentity = (index: number, item: Series) => `${item.name}_${item.localizedName}_${item.pagesRead}`;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user