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,4 +1,4 @@
<app-side-nav-companion-bar *ngIf="series !== undefined" [hasFilter]="false" (filterOpen)="filterOpen.emit($event)">
<app-side-nav-companion-bar *ngIf="series !== undefined" [hasFilter]="true" (filterOpen)="filterOpen.emit($event)" [filterActive]="filterActive">
<ng-container title>
<h2 style="margin-bottom: 0px" *ngIf="collectionTag !== undefined">
<app-card-actionables [disabled]="actionInProgress" (actionHandler)="performAction($event)" [actions]="collectionTagActions" [labelBy]="collectionTag.title" iconClass="fa-ellipsis-v"></app-card-actionables>

View file

@ -13,4 +13,4 @@
.read-btn--text {
display: none;
}
}
}

View file

@ -4,10 +4,11 @@ import { Router, ActivatedRoute } from '@angular/router';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { ToastrService } from 'ngx-toastr';
import { Subject } from 'rxjs';
import { debounceTime, take, takeUntil, takeWhile } from 'rxjs/operators';
import { debounceTime, take, takeUntil } from 'rxjs/operators';
import { BulkSelectionService } from 'src/app/cards/bulk-selection.service';
import { EditCollectionTagsComponent } from 'src/app/cards/_modals/edit-collection-tags/edit-collection-tags.component';
import { FilterSettings } from 'src/app/metadata-filter/filter-settings';
import { FilterUtilitiesService } from 'src/app/shared/_services/filter-utilities.service';
import { KEY_CODES, UtilityService } from 'src/app/shared/_services/utility.service';
import { CollectionTag } from 'src/app/_models/collection-tag';
import { SeriesAddedToCollectionEvent } from 'src/app/_models/events/series-added-to-collection-event';
@ -32,17 +33,15 @@ export class CollectionDetailComponent implements OnInit, OnDestroy {
collectionTag!: CollectionTag;
tagImage: string = '';
isLoading: boolean = true;
collections: CollectionTag[] = [];
collectionTagName: string = '';
series: Array<Series> = [];
seriesPagination!: Pagination;
collectionTagActions: ActionItem<CollectionTag>[] = [];
isAdmin: boolean = false;
filter: SeriesFilter | undefined = undefined;
filterSettings: FilterSettings = new FilterSettings();
summary: string = '';
actionInProgress: boolean = false;
filterActive: boolean = false;
filterOpen: EventEmitter<boolean> = new EventEmitter();
@ -87,26 +86,20 @@ export class CollectionDetailComponent implements OnInit, OnDestroy {
constructor(public imageService: ImageService, private collectionService: CollectionTagService, private router: Router, private route: ActivatedRoute,
private seriesService: SeriesService, private toastr: ToastrService, private actionFactoryService: ActionFactoryService,
private modalService: NgbModal, private titleService: Title, private accountService: AccountService,
private modalService: NgbModal, private titleService: Title,
public bulkSelectionService: BulkSelectionService, private actionService: ActionService, private messageHub: MessageHubService,
private utilityService: UtilityService) {
private filterUtilityService: FilterUtilitiesService, private utilityService: UtilityService) {
this.router.routeReuseStrategy.shouldReuseRoute = () => false;
this.accountService.currentUser$.pipe(take(1)).subscribe(user => {
if (user) {
this.isAdmin = this.accountService.hasAdminRole(user);
}
});
const routeId = this.route.snapshot.paramMap.get('id');
if (routeId === null) {
this.router.navigate(['collections']);
return;
}
const tagId = parseInt(routeId, 10);
this.seriesPagination = {currentPage: 0, itemsPerPage: 30, totalItems: 0, totalPages: 1};
[this.filterSettings.presets, this.filterSettings.openByDefault] = this.utilityService.filterPresetsFromUrl(this.route.snapshot, this.seriesService.createSeriesFilter());
this.seriesPagination = this.filterUtilityService.pagination();
[this.filterSettings.presets, this.filterSettings.openByDefault] = this.filterUtilityService.filterPresetsFromUrl();
this.filterSettings.presets.collectionTags = [tagId];
this.updateTag(tagId);
@ -148,13 +141,13 @@ export class CollectionDetailComponent implements OnInit, OnDestroy {
updateTag(tagId: number) {
this.collectionService.allTags().subscribe(tags => {
this.collections = tags;
const matchingTags = this.collections.filter(t => t.id === tagId);
const matchingTags = tags.filter(t => t.id === tagId);
if (matchingTags.length === 0) {
this.toastr.error('You don\'t have access to any libraries this tag belongs to or this tag is invalid');
this.router.navigateByUrl('/');
return;
}
this.collectionTag = matchingTags[0];
this.summary = (this.collectionTag.summary === null ? '' : this.collectionTag.summary).replace(/\n/g, '<br>');
this.tagImage = this.imageService.randomize(this.imageService.getCollectionCoverImage(this.collectionTag.id));
@ -163,46 +156,25 @@ export class CollectionDetailComponent implements OnInit, OnDestroy {
}
onPageChange(pagination: Pagination) {
this.router.navigate(['collections', this.collectionTag.id], {replaceUrl: true, queryParamsHandling: 'merge', queryParams: {page: this.seriesPagination.currentPage} });
this.filterUtilityService.updateUrlFromPagination(this.seriesPagination);
this.loadPage();
}
loadPage() {
const page = this.getPage();
if (page != null) {
this.seriesPagination.currentPage = parseInt(page, 10);
}
// 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.collectionTags.push(this.collectionTag.id);
}
// TODO: Add ability to filter series for a collection
// Reload page after a series is updated or first load
this.seriesService.getSeriesForTag(this.collectionTag.id, this.seriesPagination?.currentPage, this.seriesPagination?.itemsPerPage).subscribe(tags => {
this.series = tags.result;
this.seriesPagination = tags.pagination;
this.filterActive = !this.utilityService.deepEqual(this.filter, this.filterSettings.presets);
this.seriesService.getAllSeries(this.seriesPagination?.currentPage, this.seriesPagination?.itemsPerPage, this.filter).pipe(take(1)).subscribe(series => {
this.series = series.result;
this.seriesPagination = series.pagination;
this.isLoading = false;
window.scrollTo(0, 0);
});
}
updateFilter(event: FilterEvent) {
this.filter = event.filter;
const page = this.getPage();
if (page === undefined || page === null || !event.isFirst) {
this.seriesPagination.currentPage = 1;
this.onPageChange(this.seriesPagination);
} else {
this.loadPage();
}
}
getPage() {
const urlParams = new URLSearchParams(window.location.search);
return urlParams.get('page');
updateFilter(data: FilterEvent) {
this.filter = data.filter;
if (!data.isFirst) this.filterUtilityService.updateUrlFromFilter(this.seriesPagination, this.filter);
this.loadPage();
}
handleCollectionActionCallback(action: Action, collectionTag: CollectionTag) {