Change Detection: On Push aka UI Smoothness (#1369)
* Updated Series Info Cards to use OnPush and hooked in progress events when we do a mark as read/unread on entities. These events update progress bars but also will now trigger a re-calculation on Read Time Left. * Removed Library Card Component * Refactored manga reader title and subtitle calculation to the backend. * Coverted card actionables to onPush * Series Card on push cleanup * Updated edit collection tags for on push * Update cover image chooser for on push * Cleaned up carsouel reel * Updated cover image to allow for uploading gif and webp files * Bulk add to collection on push * Updated bulk operation to use on push. Updated bulk operation to have mark as unread and read buttons explicitly. Updated so add to collection is visible and delete. Fixed a bug where manage library component wasn't invoking the trackBy function * Updating entity title for on push * Removed file info component * Updated Mange Library for on push * Entity info cards on push * List item on push * Updated icon and title for on push and fixed some missing change detection on series detail * Restricted the typeahead interface to simplify the design * Edit Series Relation now shows a value in the dropdown for Parent relationships and disables the field. * Updated edit series relation to focus on new typeahead when adding a new relationship * Added some documentation and when Scanning a library, don't allow the user to enqueue the same job multiple times. * Applied the No-enqueue if already enqueued logic to other tasks * Library detail on push * Updated events widget to onpush * Card detail drawer on push. Card detail cover chooser now will show all chapter's covers for selection in cover chooser. * Chapter metadata detail on push * Removed Card Detail modal * All collections on push * Removed some comments * Updated bulk selection to use an observable rather than function calls so new on push strategy works * collection detail now uses on push and scroller is placed on correct element * Updated library recommended to on push. Ensure that when mark as read occurs, the appropriate streams are refreshed. * Updated library detail to on push * Update metadata fiter to onpush. Bugs found and reported to Project * person badge on push * Read more on push * Updated tag badge to on push * User login on push * When initing side nav, don't call an authenticated api until we are sure a user is logged in * Updated splash container to on push * Dashboard on push * Side nav slight refactor around some api calls * Cleaned up series card on push to use same cdRef naming convention * Updated Static Files to use caching * Added width and height to logo image * shortcuts modal on push * reading lists on push * Reading list detail on push * draggable ordered list on push * Refactored reading-list-detail to use a new item which drastically reduces renders on operations * series format on push * circular loader on push * Badge Expander on push * update notification modal on push * drawer on push * Edit Series Modal on push * reset password on push * review series modal on push * series metadata detail on push * theme manager on push * confirm reset password on push * register on push * confirm migration email on push * confirm email on push * add email to account migration on push * user preferences on push. Made global settings default open * edit series relation on push * Fixed an edge case bug for next chapter where if the current volume had a single chapter of 1 and the next volume had a chapter number of 0, it would say there are no more chapters. * Updated infinite scroller with on push support * Moved some animations over to typeahead, not integrated yet. * Manga reader is now on push * Reader settings on push * refactored how we close the book * Updated table of contents for on push * Updated book reader for on push. Fixed a bug where table of contents wasn't showing current page anchor due to a scroll calulation bug * Small code tweak * Icon and title on push * nav header on push * grouped typeahead on push * typeahead on push and added a new trackby identity function to allow even faster rendering of big lists * pdf reader on push * code cleanup
This commit is contained in:
parent
f5be0fac58
commit
4e49aa47ce
126 changed files with 1658 additions and 1674 deletions
|
|
@ -1,19 +1,21 @@
|
|||
<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)">
|
||||
<div class="example-box" *ngFor="let item of items; index as i" cdkDrag [cdkDragData]="item" cdkDragBoundary=".example-list">
|
||||
<div class="me-3 align-middle">
|
||||
<i class="fa fa-grip-vertical drag-handle" aria-hidden="true" cdkDragHandle></i>
|
||||
<div class="d-flex">
|
||||
<div class="me-3 align-middle">
|
||||
<i class="fa fa-grip-vertical drag-handle" aria-hidden="true" cdkDragHandle></i>
|
||||
</div>
|
||||
|
||||
<ng-container [ngTemplateOutlet]="itemTemplate" [ngTemplateOutletContext]="{ $implicit: item, idx: i }"></ng-container>
|
||||
|
||||
<div class="align-middle" style="padding-top: 40px">
|
||||
<label for="reorder-{{i}}" class="form-label visually-hidden">Reorder</label>
|
||||
<input *ngIf="accessibilityMode" id="reorder-{{i}}" class="form-control" type="number" min="0" [max]="items.length - 1" [value]="i" style="width: 60px" (focusout)="updateIndex(i, item)" (keydown.enter)="updateIndex(i, item)" aria-describedby="instructions">
|
||||
</div>
|
||||
<button class="btn btn-icon float-end" (click)="removeItem(item, i)">
|
||||
<i class="fa fa-times" aria-hidden="true"></i>
|
||||
<span class="visually-hidden" attr.aria-labelledby="item.id--{{i}}">Remove item</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<ng-container style="display: inline-block" [ngTemplateOutlet]="itemTemplate" [ngTemplateOutletContext]="{ $implicit: item, idx: i }"></ng-container>
|
||||
|
||||
<div class="align-middle" style="padding-top: 40px">
|
||||
<label for="reorder-{{i}}" class="form-label visually-hidden">Reorder</label>
|
||||
<input *ngIf="accessibilityMode" id="reorder-{{i}}" class="form-control" type="number" min="0" [max]="items.length - 1" [value]="i" style="width: 60px" (focusout)="updateIndex(i, item)" (keydown.enter)="updateIndex(i, item)" aria-describedby="instructions">
|
||||
</div>
|
||||
<button class="btn btn-icon pull-right" (click)="removeItem(item, i)">
|
||||
<i class="fa fa-times" aria-hidden="true"></i>
|
||||
<span class="visually-hidden" attr.aria-labelledby="item.id--{{i}}">Remove item</span>
|
||||
</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -44,4 +44,12 @@
|
|||
|
||||
.example-list.cdk-drop-list-dragging .example-box:not(.cdk-drag-placeholder) {
|
||||
transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
|
||||
.example-custom-placeholder {
|
||||
background: #ccc;
|
||||
border: dotted 3px #999;
|
||||
min-height: 60px;
|
||||
transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';
|
||||
import { Component, ContentChild, EventEmitter, Input, OnInit, Output, TemplateRef } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, EventEmitter, Input, OnInit, Output, TemplateRef } from '@angular/core';
|
||||
|
||||
export interface IndexUpdateEvent {
|
||||
fromPosition: number;
|
||||
|
|
@ -15,7 +15,8 @@ export interface ItemRemoveEvent {
|
|||
@Component({
|
||||
selector: 'app-draggable-ordered-list',
|
||||
templateUrl: './draggable-ordered-list.component.html',
|
||||
styleUrls: ['./draggable-ordered-list.component.scss']
|
||||
styleUrls: ['./draggable-ordered-list.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class DraggableOrderedListComponent implements OnInit {
|
||||
|
||||
|
|
@ -25,7 +26,7 @@ export class DraggableOrderedListComponent implements OnInit {
|
|||
@Output() itemRemove: EventEmitter<ItemRemoveEvent> = new EventEmitter<ItemRemoveEvent>();
|
||||
@ContentChild('draggableItem') itemTemplate!: TemplateRef<any>;
|
||||
|
||||
constructor() { }
|
||||
constructor(private readonly cdRef: ChangeDetectorRef) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
|
@ -38,6 +39,7 @@ export class DraggableOrderedListComponent implements OnInit {
|
|||
toPosition: event.currentIndex,
|
||||
item: this.items[event.currentIndex]
|
||||
});
|
||||
this.cdRef.markForCheck();
|
||||
}
|
||||
|
||||
updateIndex(previousIndex: number, item: any) {
|
||||
|
|
@ -51,6 +53,7 @@ export class DraggableOrderedListComponent implements OnInit {
|
|||
toPosition: newIndex,
|
||||
item: this.items[newIndex]
|
||||
});
|
||||
this.cdRef.markForCheck();
|
||||
}
|
||||
|
||||
removeItem(item: any, position: number) {
|
||||
|
|
@ -58,6 +61,6 @@ export class DraggableOrderedListComponent implements OnInit {
|
|||
position,
|
||||
item
|
||||
});
|
||||
this.cdRef.markForCheck();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-2 col-xs-4 col-sm-6 d-none d-sm-block" *ngIf="readingList.coverImage !== '' || readingList.coverImage !== undefined">
|
||||
<app-image maxWidth="300px" [imageUrl]="readingListImage"></app-image>
|
||||
<app-image maxWidth="300px" maxHeight="400px" [imageUrl]="readingListImage"></app-image>
|
||||
</div>
|
||||
<div class="col-md-10 col-xs-8 col-sm-6 mt-2">
|
||||
<div class="row g-0 mb-3">
|
||||
|
|
@ -46,38 +46,22 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mx-auto" *ngIf="items.length === 0" style="width: 200px;">
|
||||
Nothing added
|
||||
<div class="mx-auto" style="width: 200px;">
|
||||
<ng-container *ngIf="items.length === 0 && !isLoading">
|
||||
Nothing added
|
||||
</ng-container>
|
||||
<ng-container *ngIf="isLoading">
|
||||
<div class="spinner-border text-secondary" role="status">
|
||||
<span class="invisible">Loading...</span>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
<!-- TODO: This needs virtualization -->
|
||||
<app-draggable-ordered-list [items]="items" (orderUpdated)="orderUpdated($event)" (itemRemove)="itemRemoved($event)" [accessibilityMode]="accessibilityMode">
|
||||
<ng-template #draggableItem let-item let-position="idx">
|
||||
<div class="d-flex" style="width: 100%;">
|
||||
<app-image width="74px" class="img-top me-3" [imageUrl]="imageService.getChapterCoverImage(item.chapterId)"></app-image>
|
||||
<div class="flex-grow-1">
|
||||
<h5 class="mt-0 mb-1" id="item.id--{{position}}">
|
||||
{{formatTitle(item)}}
|
||||
<!-- TODO: Create a read/unread badge -->
|
||||
<span class="badge bg-primary rounded-pill">
|
||||
<span *ngIf="item.pagesRead > 0 && item.pagesRead < item.pagesTotal">{{item.pagesRead}} / {{item.pagesTotal}}</span>
|
||||
<span *ngIf="item.pagesRead === 0">UNREAD</span>
|
||||
<span *ngIf="item.pagesRead === item.pagesTotal">READ</span>
|
||||
</span>
|
||||
</h5>
|
||||
<ng-container *ngIf="item.seriesFormat | mangaFormat as formatString">
|
||||
<i class="fa {{item.seriesFormat | mangaFormatIcon}}" aria-hidden="true" *ngIf="item.seriesFormat != MangaFormat.UNKNOWN" title="{{formatString}}"></i>
|
||||
<span class="visually-hidden">{{formatString}}</span>
|
||||
</ng-container>
|
||||
|
||||
<a href="/library/{{item.libraryId}}/series/{{item.seriesId}}">{{item.seriesName}}</a>
|
||||
<span *ngIf="item.promoted">
|
||||
<i class="fa fa-angle-double-up" aria-hidden="true"></i>
|
||||
</span>
|
||||
<br/>
|
||||
<a href="javascript:void(0);" (click)="readChapter(item)">Read</a>
|
||||
</div>
|
||||
</div>
|
||||
<app-reading-list-item [item]="item" [position]="position" [libraryTypes]="libraryTypes"
|
||||
[promoted]="item.promoted" (read)="readChapter($event)"></app-reading-list-item>
|
||||
</ng-template>
|
||||
</app-draggable-ordered-list>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,10 +0,0 @@
|
|||
.container-sm {
|
||||
padding-left: 0px;
|
||||
padding-right: 0px;
|
||||
}
|
||||
|
||||
.virtual-scroller, virtual-scroller {
|
||||
width: 100%;
|
||||
height: calc(100vh - 85px);
|
||||
max-height: calc(var(--vh)*100 - 170px);
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { ToastrService } from 'ngx-toastr';
|
||||
import { take } from 'rxjs/operators';
|
||||
import { shareReplay, take } from 'rxjs/operators';
|
||||
import { ConfirmService } from 'src/app/shared/confirm.service';
|
||||
import { UtilityService } from 'src/app/shared/_services/utility.service';
|
||||
import { LibraryType } from 'src/app/_models/library';
|
||||
|
|
@ -20,7 +20,8 @@ import { ReaderService } from 'src/app/_services/reader.service';
|
|||
@Component({
|
||||
selector: 'app-reading-list-detail',
|
||||
templateUrl: './reading-list-detail.component.html',
|
||||
styleUrls: ['./reading-list-detail.component.scss']
|
||||
styleUrls: ['./reading-list-detail.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class ReadingListDetailComponent implements OnInit {
|
||||
items: Array<ReadingListItem> = [];
|
||||
|
|
@ -48,7 +49,8 @@ export class ReadingListDetailComponent implements OnInit {
|
|||
constructor(private route: ActivatedRoute, private router: Router, private readingListService: ReadingListService,
|
||||
private actionService: ActionService, private actionFactoryService: ActionFactoryService, public utilityService: UtilityService,
|
||||
public imageService: ImageService, private accountService: AccountService, private toastr: ToastrService,
|
||||
private confirmService: ConfirmService, private libraryService: LibraryService, private readerService: ReaderService) {}
|
||||
private confirmService: ConfirmService, private libraryService: LibraryService, private readerService: ReaderService,
|
||||
private readonly cdRef: ChangeDetectorRef) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
const listId = this.route.snapshot.paramMap.get('id');
|
||||
|
|
@ -57,15 +59,8 @@ export class ReadingListDetailComponent implements OnInit {
|
|||
this.router.navigateByUrl('/libraries');
|
||||
return;
|
||||
}
|
||||
|
||||
this.listId = parseInt(listId, 10);
|
||||
|
||||
//this.readingListImage = this.imageService.randomize(this.imageService.getReadingListCoverImage(this.listId));
|
||||
|
||||
this.libraryService.getLibraries().subscribe(libs => {
|
||||
|
||||
});
|
||||
|
||||
forkJoin([
|
||||
this.libraryService.getLibraries(),
|
||||
this.readingListService.getReadingList(this.listId)
|
||||
|
|
@ -86,12 +81,15 @@ export class ReadingListDetailComponent implements OnInit {
|
|||
this.readingList = readingList;
|
||||
this.readingListSummary = (this.readingList.summary === null ? '' : this.readingList.summary).replace(/\n/g, '<br>');
|
||||
|
||||
this.cdRef.markForCheck();
|
||||
|
||||
this.accountService.currentUser$.pipe(take(1)).subscribe(user => {
|
||||
if (user) {
|
||||
this.isAdmin = this.accountService.hasAdminRole(user);
|
||||
this.hasDownloadingRole = this.accountService.hasDownloadRole(user);
|
||||
|
||||
this.actions = this.actionFactoryService.getReadingListActions(this.handleReadingListActionCallback.bind(this)).filter(action => this.readingListService.actionListFilter(action, readingList, this.isAdmin));
|
||||
this.cdRef.markForCheck();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
@ -100,9 +98,12 @@ export class ReadingListDetailComponent implements OnInit {
|
|||
|
||||
getListItems() {
|
||||
this.isLoading = true;
|
||||
this.cdRef.markForCheck();
|
||||
|
||||
this.readingListService.getListItems(this.listId).subscribe(items => {
|
||||
this.items = items;
|
||||
this.isLoading = false;
|
||||
this.cdRef.markForCheck();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -131,6 +132,7 @@ export class ReadingListDetailComponent implements OnInit {
|
|||
// Reload information around list
|
||||
this.readingList = readingList;
|
||||
this.readingListSummary = (this.readingList.summary === null ? '' : this.readingList.summary).replace(/\n/g, '<br>');
|
||||
this.cdRef.markForCheck();
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
|
@ -145,24 +147,6 @@ export class ReadingListDetailComponent implements OnInit {
|
|||
});
|
||||
}
|
||||
|
||||
formatTitle(item: ReadingListItem) {
|
||||
// TODO: Use new app-entity-title component instead
|
||||
if (item.chapterNumber === '0') {
|
||||
return 'Volume ' + item.volumeNumber;
|
||||
}
|
||||
|
||||
if (item.seriesFormat === MangaFormat.EPUB) {
|
||||
return 'Volume ' + this.utilityService.cleanSpecialTitle(item.chapterNumber);
|
||||
}
|
||||
|
||||
let chapterNum = item.chapterNumber;
|
||||
if (!item.chapterNumber.match(/^\d+$/)) {
|
||||
chapterNum = this.utilityService.cleanSpecialTitle(item.chapterNumber);
|
||||
}
|
||||
|
||||
return this.utilityService.formatChapterName(this.libraryTypes[item.libraryId], true, true) + chapterNum;
|
||||
}
|
||||
|
||||
orderUpdated(event: IndexUpdateEvent) {
|
||||
this.readingListService.updatePosition(this.readingList.id, event.item.id, event.fromPosition, event.toPosition).subscribe(() => { /* No Operation */ });
|
||||
}
|
||||
|
|
@ -170,12 +154,14 @@ export class ReadingListDetailComponent implements OnInit {
|
|||
itemRemoved(event: ItemRemoveEvent) {
|
||||
this.readingListService.deleteItem(this.readingList.id, event.item.id).subscribe(() => {
|
||||
this.items.splice(event.position, 1);
|
||||
this.cdRef.markForCheck();
|
||||
this.toastr.success('Item removed');
|
||||
});
|
||||
}
|
||||
|
||||
removeRead() {
|
||||
this.isLoading = true;
|
||||
this.cdRef.markForCheck();
|
||||
this.readingListService.removeRead(this.readingList.id).subscribe((resp) => {
|
||||
if (resp === 'Nothing to remove') {
|
||||
this.toastr.info(resp);
|
||||
|
|
@ -186,6 +172,7 @@ export class ReadingListDetailComponent implements OnInit {
|
|||
}
|
||||
|
||||
read() {
|
||||
// TODO: Can I do this in the backend?
|
||||
let currentlyReadingChapter = this.items[0];
|
||||
for (let i = 0; i < this.items.length; i++) {
|
||||
if (this.items[i].pagesRead >= this.items[i].pagesTotal) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
<div class="d-flex" style="width: 100%;">
|
||||
<app-image width="74px" maxHeight="104px" class="img-top me-3" [imageUrl]="imageService.getChapterCoverImage(item.chapterId)"></app-image>
|
||||
<div class="flex-grow-1">
|
||||
<h5 class="mt-0 mb-1" id="item.id--{{position}}">
|
||||
{{title}}
|
||||
<!-- TODO: Create a read/unread badge -->
|
||||
<span class="badge bg-primary rounded-pill">
|
||||
<span *ngIf="item.pagesRead > 0 && item.pagesRead < item.pagesTotal">{{item.pagesRead}} / {{item.pagesTotal}}</span>
|
||||
<span *ngIf="item.pagesRead === 0">UNREAD</span>
|
||||
<span *ngIf="item.pagesRead === item.pagesTotal">READ</span>
|
||||
</span>
|
||||
</h5>
|
||||
<ng-container *ngIf="item.seriesFormat | mangaFormat as formatString">
|
||||
<i class="fa {{item.seriesFormat | mangaFormatIcon}}" aria-hidden="true" *ngIf="item.seriesFormat != MangaFormat.UNKNOWN" title="{{formatString}}"></i>
|
||||
<span class="visually-hidden">{{formatString}}</span>
|
||||
</ng-container>
|
||||
|
||||
<a href="/library/{{item.libraryId}}/series/{{item.seriesId}}">{{item.seriesName}}</a>
|
||||
<span *ngIf="promoted">
|
||||
<i class="fa fa-angle-double-up" aria-hidden="true"></i>
|
||||
</span>
|
||||
<br/>
|
||||
<a href="javascript:void(0);" (click)="readChapter(item)">Read</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
import { UtilityService } from 'src/app/shared/_services/utility.service';
|
||||
import { LibraryType } from 'src/app/_models/library';
|
||||
import { MangaFormat } from 'src/app/_models/manga-format';
|
||||
import { ReadingListItem } from 'src/app/_models/reading-list';
|
||||
import { ImageService } from 'src/app/_services/image.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-reading-list-item',
|
||||
templateUrl: './reading-list-item.component.html',
|
||||
styleUrls: ['./reading-list-item.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class ReadingListItemComponent implements OnInit {
|
||||
|
||||
@Input() item!: ReadingListItem;
|
||||
@Input() position: number = 0;
|
||||
@Input() libraryTypes: {[key: number]: LibraryType} = {};
|
||||
/**
|
||||
* If the Reading List is promoted or not
|
||||
*/
|
||||
@Input() promoted: boolean = false;
|
||||
|
||||
@Output() read: EventEmitter<ReadingListItem> = new EventEmitter();
|
||||
|
||||
title: string = '';
|
||||
|
||||
get MangaFormat(): typeof MangaFormat {
|
||||
return MangaFormat;
|
||||
}
|
||||
|
||||
constructor(public imageService: ImageService, private utilityService: UtilityService,
|
||||
private readonly cdRef: ChangeDetectorRef) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.formatTitle(this.item);
|
||||
}
|
||||
|
||||
formatTitle(item: ReadingListItem) {
|
||||
if (item.chapterNumber === '0') {
|
||||
this.title = 'Volume ' + item.volumeNumber;
|
||||
}
|
||||
|
||||
if (item.seriesFormat === MangaFormat.EPUB) {
|
||||
this.title = 'Volume ' + this.utilityService.cleanSpecialTitle(item.chapterNumber);
|
||||
}
|
||||
|
||||
let chapterNum = item.chapterNumber;
|
||||
if (!item.chapterNumber.match(/^\d+$/)) {
|
||||
chapterNum = this.utilityService.cleanSpecialTitle(item.chapterNumber);
|
||||
}
|
||||
|
||||
this.title = this.utilityService.formatChapterName(this.libraryTypes[item.libraryId], true, true) + chapterNum;
|
||||
this.cdRef.markForCheck();
|
||||
}
|
||||
|
||||
readChapter(item: ReadingListItem) {
|
||||
this.read.emit(item);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -12,6 +12,7 @@ import { PipeModule } from '../pipe/pipe.module';
|
|||
import { SharedModule } from '../shared/shared.module';
|
||||
import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { SharedSideNavCardsModule } from '../shared-side-nav-cards/shared-side-nav-cards.module';
|
||||
import { ReadingListItemComponent } from './reading-list-item/reading-list-item.component';
|
||||
|
||||
|
||||
|
||||
|
|
@ -21,7 +22,8 @@ import { SharedSideNavCardsModule } from '../shared-side-nav-cards/shared-side-n
|
|||
ReadingListDetailComponent,
|
||||
AddToListModalComponent,
|
||||
ReadingListsComponent,
|
||||
EditReadingListModalComponent
|
||||
EditReadingListModalComponent,
|
||||
ReadingListItemComponent
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<app-side-nav-companion-bar pageHeader="Home">
|
||||
<app-side-nav-companion-bar>
|
||||
<h2 title>
|
||||
Reading Lists
|
||||
</h2>
|
||||
|
|
@ -8,11 +8,12 @@
|
|||
<app-card-detail-layout
|
||||
[isLoading]="loadingLists"
|
||||
[items]="lists"
|
||||
[actions]="actions"
|
||||
[pagination]="pagination"
|
||||
[filteringDisabled]="true"
|
||||
>
|
||||
<ng-template #cardItem let-item let-position="idx">
|
||||
<app-card-item [title]="item.title" [entity]="item" [actions]="getActions(item)" [suppressLibraryLink]="true" [imageUrl]="imageService.getReadingListCoverImage(item.id)" (clicked)="handleClick(item)"></app-card-item>
|
||||
<app-card-item [title]="item.title" [entity]="item" [actions]="getActions(item)"
|
||||
[suppressLibraryLink]="true" [imageUrl]="imageService.getReadingListCoverImage(item.id)"
|
||||
(clicked)="handleClick(item)"></app-card-item>
|
||||
</ng-template>
|
||||
</app-card-detail-layout>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { ToastrService } from 'ngx-toastr';
|
||||
import { take } from 'rxjs/operators';
|
||||
import { FilterUtilitiesService } from 'src/app/shared/_services/filter-utilities.service';
|
||||
import { PaginatedResult, Pagination } from 'src/app/_models/pagination';
|
||||
import { ReadingList } from 'src/app/_models/reading-list';
|
||||
import { AccountService } from 'src/app/_services/account.service';
|
||||
|
|
@ -14,32 +13,32 @@ import { ReadingListService } from 'src/app/_services/reading-list.service';
|
|||
@Component({
|
||||
selector: 'app-reading-lists',
|
||||
templateUrl: './reading-lists.component.html',
|
||||
styleUrls: ['./reading-lists.component.scss']
|
||||
styleUrls: ['./reading-lists.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class ReadingListsComponent implements OnInit {
|
||||
|
||||
lists: ReadingList[] = [];
|
||||
loadingLists = false;
|
||||
pagination!: Pagination;
|
||||
actions: ActionItem<ReadingList>[] = [];
|
||||
isAdmin: boolean = false;
|
||||
|
||||
constructor(private readingListService: ReadingListService, public imageService: ImageService, private actionFactoryService: ActionFactoryService,
|
||||
private accountService: AccountService, private toastr: ToastrService, private router: Router, private actionService: ActionService,
|
||||
private filterUtilityService: FilterUtilitiesService) { }
|
||||
private readonly cdRef: ChangeDetectorRef) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.loadPage();
|
||||
|
||||
this.accountService.currentUser$.pipe(take(1)).subscribe(user => {
|
||||
if (user) {
|
||||
this.isAdmin = this.accountService.hasAdminRole(user);
|
||||
this.loadPage();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getActions(readingList: ReadingList) {
|
||||
return this.actionFactoryService.getReadingListActions(this.handleReadingListActionCallback.bind(this)).filter(action => this.readingListService.actionListFilter(action, readingList, this.isAdmin));
|
||||
return this.actionFactoryService.getReadingListActions(this.handleReadingListActionCallback.bind(this))
|
||||
.filter(action => this.readingListService.actionListFilter(action, readingList, this.isAdmin));
|
||||
}
|
||||
|
||||
performAction(action: ActionItem<any>, readingList: ReadingList) {
|
||||
|
|
@ -60,6 +59,7 @@ export class ReadingListsComponent implements OnInit {
|
|||
this.actionService.editReadingList(readingList, (updatedList: ReadingList) => {
|
||||
// Reload information around list
|
||||
readingList = updatedList;
|
||||
this.cdRef.markForCheck();
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
|
@ -76,20 +76,17 @@ export class ReadingListsComponent implements OnInit {
|
|||
this.pagination.currentPage = parseInt(page, 10);
|
||||
}
|
||||
this.loadingLists = true;
|
||||
this.cdRef.markForCheck();
|
||||
|
||||
this.readingListService.getReadingLists(true).pipe(take(1)).subscribe((readingLists: PaginatedResult<ReadingList[]>) => {
|
||||
this.lists = readingLists.result;
|
||||
this.pagination = readingLists.pagination;
|
||||
this.loadingLists = false;
|
||||
window.scrollTo(0, 0);
|
||||
this.cdRef.markForCheck();
|
||||
});
|
||||
}
|
||||
|
||||
onPageChange(pagination: Pagination) {
|
||||
this.filterUtilityService.updateUrlFromFilter(this.pagination, undefined);;
|
||||
this.loadPage();
|
||||
}
|
||||
|
||||
handleClick(list: ReadingList) {
|
||||
this.router.navigateByUrl('lists/' + list.id);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue