Library Recomendations (#1236)
* Updated cover regex for finding cover images in archives to ignore back_cover or back-cover * Fixed an issue where Tags wouldn't save due to not pulling them from the DB. * Refactored All series to it's own lazy loaded module * Modularized Dashboard and library detail. Had to change main dashboard page to be libraries. Subject to change. * Refactored login component into registration module * Series Detail module created * Refactored nav stuff into it's own module, not lazy loaded, but self contained. * Refactored theme component into a dev only module so we don't incur load for temp testing modules * Finished off modularization code. Only missing thing is to re-introduce some dashboard functionality for library view. * Implemented a basic recommendation page for library detail
This commit is contained in:
parent
743a3ba935
commit
f237aa7ab7
77 changed files with 1077 additions and 501 deletions
|
|
@ -0,0 +1,26 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { Routes, RouterModule } from '@angular/router';
|
||||
import { AuthGuard } from '../_guards/auth.guard';
|
||||
import { LibraryAccessGuard } from '../_guards/library-access.guard';
|
||||
import { LibraryDetailComponent } from './library-detail.component';
|
||||
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: ':libraryId',
|
||||
runGuardsAndResolvers: 'always',
|
||||
canActivate: [AuthGuard, LibraryAccessGuard],
|
||||
component: LibraryDetailComponent
|
||||
},
|
||||
{
|
||||
path: '',
|
||||
component: LibraryDetailComponent
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes), ],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class LibraryDetailRoutingModule { }
|
||||
|
|
@ -10,7 +10,7 @@
|
|||
<a ngbNavLink>{{tab.title | sentenceCase}}</a>
|
||||
<ng-template ngbNavContent>
|
||||
<ng-container *ngIf="tab.title === 'Recommended'">
|
||||
<app-library [libraryId]="libraryId"></app-library>
|
||||
<app-library-recommended [libraryId]="libraryId"></app-library-recommended>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="tab.title === 'Library'">
|
||||
<app-card-detail-layout
|
||||
|
|
|
|||
|
|
@ -2,9 +2,8 @@ import { Component, EventEmitter, HostListener, OnDestroy, OnInit } from '@angul
|
|||
import { Title } from '@angular/platform-browser';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { Subject } from 'rxjs';
|
||||
import { debounceTime, take, takeUntil, takeWhile } from 'rxjs/operators';
|
||||
import { debounceTime, take, takeUntil } from 'rxjs/operators';
|
||||
import { BulkSelectionService } from '../cards/bulk-selection.service';
|
||||
import { FilterSettings } from '../metadata-filter/filter-settings';
|
||||
import { KEY_CODES, UtilityService } from '../shared/_services/utility.service';
|
||||
import { SeriesAddedEvent } from '../_models/events/series-added-event';
|
||||
import { Library } from '../_models/library';
|
||||
|
|
@ -18,6 +17,7 @@ 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';
|
||||
import { FilterSettings } from '../metadata-filter/filter-settings';
|
||||
|
||||
@Component({
|
||||
selector: 'app-library-detail',
|
||||
|
|
@ -87,8 +87,9 @@ export class LibraryDetailComponent implements OnInit, OnDestroy {
|
|||
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 filterUtilityService: FilterUtilitiesService) {
|
||||
const routeId = this.route.snapshot.paramMap.get('id');
|
||||
const routeId = this.route.snapshot.paramMap.get('libraryId');
|
||||
if (routeId === null) {
|
||||
console.log('Redirecting due to not seeing libraryId in route');
|
||||
this.router.navigateByUrl('/libraries');
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
28
UI/Web/src/app/library-detail/library-detail.module.ts
Normal file
28
UI/Web/src/app/library-detail/library-detail.module.ts
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { LibraryDetailComponent } from './library-detail.component';
|
||||
import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { PipeModule } from '../pipe/pipe.module';
|
||||
import { LibraryDetailRoutingModule } from './library-detail-routing.module';
|
||||
import { SharedSideNavCardsModule } from '../shared-side-nav-cards/shared-side-nav-cards.module';
|
||||
import { LibraryRecommendedComponent } from './library-recommended/library-recommended.component';
|
||||
import { CarouselModule } from '../carousel/carousel.module';
|
||||
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [LibraryDetailComponent, LibraryRecommendedComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
|
||||
NgbNavModule,
|
||||
|
||||
CarouselModule, // because this is heavy, we might want recommended in a new url
|
||||
|
||||
PipeModule,
|
||||
SharedSideNavCardsModule,
|
||||
|
||||
LibraryDetailRoutingModule
|
||||
]
|
||||
})
|
||||
export class LibraryDetailModule { }
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
|
||||
<ng-container *ngIf="onDeck$ | async as onDeck">
|
||||
<app-carousel-reel [items]="onDeck" title="On Deck">
|
||||
<ng-template #carouselItem let-item let-position="idx">
|
||||
<app-series-card [data]="item" [libraryId]="item.libraryId" [suppressLibraryLink]="libraryId !== 0" (reload)="reloadInProgress($event)" (dataChanged)="reloadInProgress($event)"></app-series-card>
|
||||
</ng-template>
|
||||
</app-carousel-reel>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="quickReads$ | async as quickReads">
|
||||
<app-carousel-reel [items]="quickReads" title="Quick Reads">
|
||||
<ng-template #carouselItem let-item let-position="idx">
|
||||
<app-series-card [data]="item" [libraryId]="item.libraryId" [suppressLibraryLink]="libraryId !== 0" (reload)="reloadInProgress($event)" (dataChanged)="reloadInProgress($event)"></app-series-card>
|
||||
</ng-template>
|
||||
</app-carousel-reel>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="highlyRated$ | async as highlyRated">
|
||||
<app-carousel-reel [items]="highlyRated" title="Highly Rated">
|
||||
<ng-template #carouselItem let-item let-position="idx">
|
||||
<app-series-card [data]="item" [libraryId]="item.libraryId" [suppressLibraryLink]="libraryId !== 0" (reload)="reloadInProgress($event)" (dataChanged)="reloadInProgress($event)"></app-series-card>
|
||||
</ng-template>
|
||||
</app-carousel-reel>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="rediscover$ | async as rediscover">
|
||||
<app-carousel-reel [items]="rediscover" title="Rediscover">
|
||||
<ng-template #carouselItem let-item let-position="idx">
|
||||
<app-series-card [data]="item" [libraryId]="item.libraryId" [suppressLibraryLink]="libraryId !== 0" (reload)="reloadInProgress($event)" (dataChanged)="reloadInProgress($event)"></app-series-card>
|
||||
</ng-template>
|
||||
</app-carousel-reel>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="genre$ | async as genre">
|
||||
<ng-container *ngIf="moreIn$ | async as moreIn">
|
||||
<app-carousel-reel [items]="moreIn" title="More In {{genre.title}}">
|
||||
<ng-template #carouselItem let-item let-position="idx">
|
||||
<app-series-card [data]="item" [libraryId]="item.libraryId" [suppressLibraryLink]="libraryId !== 0" (reload)="reloadInProgress($event)" (dataChanged)="reloadInProgress($event)"></app-series-card>
|
||||
</ng-template>
|
||||
</app-carousel-reel>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { map, Observable, shareReplay } from 'rxjs';
|
||||
import { Genre } from 'src/app/_models/genre';
|
||||
import { Series } from 'src/app/_models/series';
|
||||
import { MetadataService } from 'src/app/_services/metadata.service';
|
||||
import { RecommendationService } from 'src/app/_services/recommendation.service';
|
||||
import { SeriesService } from 'src/app/_services/series.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-library-recommended',
|
||||
templateUrl: './library-recommended.component.html',
|
||||
styleUrls: ['./library-recommended.component.scss']
|
||||
})
|
||||
export class LibraryRecommendedComponent implements OnInit {
|
||||
|
||||
@Input() libraryId: number = 0;
|
||||
|
||||
quickReads$!: Observable<Series[]>;
|
||||
highlyRated$!: Observable<Series[]>;
|
||||
onDeck$!: Observable<Series[]>;
|
||||
rediscover$!: Observable<Series[]>;
|
||||
|
||||
moreIn$!: Observable<Series[]>;
|
||||
genre: string = '';
|
||||
genre$!: Observable<Genre>;
|
||||
|
||||
|
||||
constructor(private recommendationService: RecommendationService, private seriesService: SeriesService, private metadataService: MetadataService) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
|
||||
this.quickReads$ = this.recommendationService.getQuickReads(this.libraryId)
|
||||
.pipe(map(p => p.result), shareReplay());
|
||||
|
||||
this.highlyRated$ = this.recommendationService.getHighlyRated(this.libraryId)
|
||||
.pipe(map(p => p.result), shareReplay());
|
||||
|
||||
this.rediscover$ = this.recommendationService.getRediscover(this.libraryId)
|
||||
.pipe(map(p => p.result), shareReplay());
|
||||
|
||||
this.onDeck$ = this.seriesService.getOnDeck(this.libraryId)
|
||||
.pipe(map(p => p.result), shareReplay());
|
||||
|
||||
this.genre$ = this.metadataService.getAllGenres([this.libraryId]).pipe(map(genres => genres[Math.floor(Math.random() * genres.length)]), shareReplay());
|
||||
this.genre$.subscribe(genre => {
|
||||
this.moreIn$ = this.recommendationService.getMoreIn(this.libraryId, genre.id).pipe(map(p => p.result), shareReplay());
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
reloadInProgress(series: Series | boolean) {
|
||||
if (series === true || series === false) {
|
||||
if (!series) {return;}
|
||||
}
|
||||
// If the update to Series doesn't affect the requirement to be in this stream, then ignore update request
|
||||
const seriesObj = (series as Series);
|
||||
if (seriesObj.pagesRead !== seriesObj.pages && seriesObj.pagesRead !== 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
//this.loadOnDeck();
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue