Fixes, Tweaks, and Series Filtering (#1217)

* From previous fix, added the other locking conditions on the update series metadata.

* Fixed a bug where custom series, collection tag, and reading list covers weren't being removed on cleanup.

* Ensure reading list detail has a margin to align to the standard

* Refactored some event stuff to use dedicated consts. Introduced a new event when users read something, which can update progress bars on cards.

* Added recomended and library tags to the library detail page. This will eventually offer more custom analytics

* Cleanup some code onc arousel

* Adjusted scale to height/width css to better fit

* Small css tweaks to better center images in the manga reader in both axis. This takes care of double page rendering as well.

* When a special has a Title set in the metadata, on series detail page, show that on the card rather than filename.

* Fixed a bug where when paging in manga reader, the scroll to top wasn't working due to changing where scrolling is done

* More css goodness for rendering images in manga reader

* Fixed a bug where clearing a typeahead externally wouldn't clear the x button

* Fixed a bug where filering then using keyboard would select wrong option

* Added a new sorting field for Last Chapter Added (new field) to get a similar on deck feel.

* Tweaked recently updated to hit the NFR of 500ms (300ms fresh start) and still give a much better experience.

* Refactored On deck to now go to all series and also sort by last updated. Recently Added Series now loads all series with sort by created.

* Some tweaks on css for cover image chooser

* Fixed a bug in pagination control where multiple pagination events could trigger on load and thus multiple requests for data on parent controller.

* Updated edit series modal to show when the last chapter was added and when user last read it.

* Implemented a highlight on the fitler button when a filter is active.

* Refactored metadata filter screens to perserve the filters in the url and thus when navigating back and forth, it will retain. users should click side nav to reset the state.

* Hide middle section on companion bar on phones

* Cleaned up some prefilters and console.logs

* Don't open drawer by default when a filter is active
This commit is contained in:
Joseph Milazzo 2022-04-14 16:55:06 -05:00 committed by GitHub
parent 5e629913b7
commit 553f9b0d98
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
63 changed files with 864 additions and 537 deletions

View file

@ -1,24 +1,38 @@
<app-side-nav-companion-bar [hasFilter]="true" [filterOpenByDefault]="filterSettings.openByDefault" (filterOpen)="filterOpen.emit($event)">
<app-side-nav-companion-bar [hasFilter]="true" [filterOpenByDefault]="filterSettings.openByDefault" (filterOpen)="filterOpen.emit($event)" [filterActive]="filterActive">
<h2 title>
<app-card-actionables [actions]="actions"></app-card-actionables>
{{libraryName}}
</h2>
<h6 subtitle style="margin-left:40px;">{{pagination?.totalItems}} Series</h6>
<h6 subtitle style="margin-left:40px;" *ngIf="active.fragment === ''">{{pagination?.totalItems}} Series</h6>
<div main>
<!-- TODO: Implement Tabs here for Recommended and Library view -->
<ul ngbNav #nav="ngbNav" [(activeId)]="active" class="nav nav-pills" style="flex-wrap: nowrap;">
<li *ngFor="let tab of tabs" [ngbNavItem]="tab">
<a ngbNavLink>{{tab.title | sentenceCase}}</a>
<ng-template ngbNavContent>
<ng-container *ngIf="tab.title === 'Recommended'">
<app-library [libraryId]="libraryId"></app-library>
</ng-container>
<ng-container *ngIf="tab.title === 'Library'">
<app-card-detail-layout
[isLoading]="loadingSeries"
[items]="series"
[pagination]="pagination"
[filterSettings]="filterSettings"
[filterOpen]="filterOpen"
(applyFilter)="updateFilter($event)"
(pageChange)="onPageChange($event)"
>
<ng-template #cardItem let-item let-position="idx">
<app-series-card [data]="item" [libraryId]="libraryId" [suppressLibraryLink]="true" (reload)="loadPage()" (selection)="bulkSelectionService.handleCardSelection('series', position, series.length, $event)" [selected]="bulkSelectionService.isCardSelected('series', position)" [allowSelection]="true"></app-series-card>
</ng-template>
</app-card-detail-layout>
</ng-container>
</ng-template>
</li>
</ul>
</div>
</app-side-nav-companion-bar>
<app-bulk-operations [actionCallback]="bulkActionCallback"></app-bulk-operations>
<app-card-detail-layout
[isLoading]="loadingSeries"
[items]="series"
[pagination]="pagination"
[filterSettings]="filterSettings"
[filterOpen]="filterOpen"
(applyFilter)="updateFilter($event)"
(pageChange)="onPageChange($event)"
>
<ng-template #cardItem let-item let-position="idx">
<app-series-card [data]="item" [libraryId]="libraryId" [suppressLibraryLink]="true" (reload)="loadPage()" (selection)="bulkSelectionService.handleCardSelection('series', position, series.length, $event)" [selected]="bulkSelectionService.isCardSelected('series', position)" [allowSelection]="true"></app-series-card>
</ng-template>
</app-card-detail-layout>
<div [ngbNavOutlet]="nav" class="mt-3"></div>

View file

@ -17,6 +17,7 @@ import { LibraryService } from '../_services/library.service';
import { EVENTS, MessageHubService } from '../_services/message-hub.service';
import { SeriesService } from '../_services/series.service';
import { NavService } from '../_services/nav.service';
import { FilterUtilitiesService } from '../shared/_services/filter-utilities.service';
@Component({
selector: 'app-library-detail',
@ -35,6 +36,13 @@ export class LibraryDetailComponent implements OnInit, OnDestroy {
onDestroy: Subject<void> = new Subject<void>();
filterSettings: FilterSettings = new FilterSettings();
filterOpen: EventEmitter<boolean> = new EventEmitter();
filterActive: boolean = false;
tabs: Array<{title: string, fragment: string}> = [
{title: 'Library', fragment: ''},
{title: 'Recommended', fragment: 'recomended'},
];
active = this.tabs[0];
bulkActionCallback = (action: Action, data: any) => {
@ -77,13 +85,14 @@ export class LibraryDetailComponent implements OnInit, OnDestroy {
constructor(private route: ActivatedRoute, private router: Router, private seriesService: SeriesService,
private libraryService: LibraryService, private titleService: Title, private actionFactoryService: ActionFactoryService,
private actionService: ActionService, public bulkSelectionService: BulkSelectionService, private hubService: MessageHubService,
private utilityService: UtilityService, public navService: NavService) {
private utilityService: UtilityService, public navService: NavService, private filterUtilityService: FilterUtilitiesService) {
const routeId = this.route.snapshot.paramMap.get('id');
if (routeId === null) {
this.router.navigateByUrl('/libraries');
return;
}
this.router.routeReuseStrategy.shouldReuseRoute = () => false;
this.libraryId = parseInt(routeId, 10);
this.libraryService.getLibraryNames().pipe(take(1)).subscribe(names => {
@ -91,9 +100,9 @@ export class LibraryDetailComponent implements OnInit, OnDestroy {
this.titleService.setTitle('Kavita - ' + this.libraryName);
});
this.actions = this.actionFactoryService.getLibraryActions(this.handleAction.bind(this));
this.pagination = {currentPage: 0, itemsPerPage: 30, totalItems: 0, totalPages: 1};
[this.filterSettings.presets, this.filterSettings.openByDefault] = this.utilityService.filterPresetsFromUrl(this.route.snapshot, this.seriesService.createSeriesFilter());
this.pagination = this.filterUtilityService.pagination();
[this.filterSettings.presets, this.filterSettings.openByDefault] = this.filterUtilityService.filterPresetsFromUrl();
this.filterSettings.presets.libraries = [this.libraryId];
}
@ -142,30 +151,21 @@ export class LibraryDetailComponent implements OnInit, OnDestroy {
}
}
updateFilter(event: FilterEvent) {
this.filter = event.filter;
const page = this.getPage();
if (page === undefined || page === null || !event.isFirst) {
this.pagination.currentPage = 1;
this.onPageChange(this.pagination);
} else {
this.loadPage();
}
updateFilter(data: FilterEvent) {
this.filter = data.filter;
if (!data.isFirst) this.filterUtilityService.updateUrlFromFilter(this.pagination, this.filter);
this.loadPage();
}
loadPage() {
const page = this.getPage();
if (page != null) {
this.pagination.currentPage = parseInt(page, 10);
}
this.loadingSeries = true;
// The filter is out of sync with the presets from typeaheads on first load but syncs afterwards
if (this.filter == undefined) {
this.filter = this.seriesService.createSeriesFilter();
this.filter.libraries.push(this.libraryId);
}
this.loadingSeries = true;
this.filterActive = !this.utilityService.deepEqual(this.filter, this.filterSettings.presets);
this.seriesService.getSeriesForLibrary(0, this.pagination?.currentPage, this.pagination?.itemsPerPage, this.filter).pipe(take(1)).subscribe(series => {
this.series = series.result;
this.pagination = series.pagination;
@ -175,7 +175,7 @@ export class LibraryDetailComponent implements OnInit, OnDestroy {
}
onPageChange(pagination: Pagination) {
window.history.replaceState(window.location.href, '', window.location.href.split('?')[0] + '?' + 'page=' + this.pagination.currentPage);
this.filterUtilityService.updateUrlFromPagination(this.pagination);
this.loadPage();
}
@ -184,10 +184,4 @@ export class LibraryDetailComponent implements OnInit, OnDestroy {
}
trackByIdentity = (index: number, item: Series) => `${item.name}_${item.originalName}_${item.localizedName}_${item.pagesRead}`;
getPage() {
const urlParams = new URLSearchParams(window.location.search);
return urlParams.get('page');
}
}