mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-07-09 03:04:20 -04:00
Implementing infinite scroll for library items
This commit is contained in:
parent
7e77e804b6
commit
e8c505cb56
2668
package-lock.json
generated
2668
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
35
package.json
35
package.json
@ -9,37 +9,38 @@
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "^9.1.9",
|
||||
"@angular/animations": "^9.1.12",
|
||||
"@angular/cdk": "^9.2.4",
|
||||
"@angular/common": "^9.1.9",
|
||||
"@angular/compiler": "^9.1.9",
|
||||
"@angular/core": "^9.1.9",
|
||||
"@angular/forms": "^9.1.9",
|
||||
"@angular/common": "^9.1.12",
|
||||
"@angular/compiler": "^9.1.12",
|
||||
"@angular/core": "^9.1.12",
|
||||
"@angular/forms": "^9.1.12",
|
||||
"@angular/material": "^9.2.4",
|
||||
"@angular/platform-browser": "^9.1.9",
|
||||
"@angular/platform-browser-dynamic": "^9.1.9",
|
||||
"@angular/router": "^9.1.9",
|
||||
"@angular/platform-browser": "^9.1.12",
|
||||
"@angular/platform-browser-dynamic": "^9.1.12",
|
||||
"@angular/router": "^9.1.12",
|
||||
"angular-auth-oidc-client": "10.0.14",
|
||||
"bootstrap": "^4.5.0",
|
||||
"detect-browser": "^5.1.0",
|
||||
"detect-browser": "^5.1.1",
|
||||
"hammerjs": "^2.0.8",
|
||||
"hls.js": "^0.13.2",
|
||||
"jquery": "^3.5.1",
|
||||
"ngx-infinite-scroll": "^9.0.0",
|
||||
"popper.js": "^1.16.1",
|
||||
"zone.js": "^0.10.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "^0.901.7",
|
||||
"@angular/cli": "^9.1.7",
|
||||
"@angular/compiler-cli": "^9.1.9",
|
||||
"@angular/language-service": "^9.1.9",
|
||||
"@angular-devkit/build-angular": "^0.901.12",
|
||||
"@angular/cli": "^9.1.12",
|
||||
"@angular/compiler-cli": "^9.1.12",
|
||||
"@angular/language-service": "^9.1.12",
|
||||
"@types/bootstrap": "^4.5.0",
|
||||
"@types/hls.js": "^0.12.6",
|
||||
"@types/jasmine": "^3.5.10",
|
||||
"@types/jasmine": "^3.5.11",
|
||||
"@types/jasminewd2": "^2.0.8",
|
||||
"@types/jquery": "^3.3.38",
|
||||
"@types/node": "^13.13.10",
|
||||
"@types/video.js": "^7.3.9",
|
||||
"@types/jquery": "^3.5.1",
|
||||
"@types/node": "^13.13.15",
|
||||
"@types/video.js": "^7.3.10",
|
||||
"codelyzer": "^5.2.2",
|
||||
"protractor": "^7.0.0",
|
||||
"ts-node": "~8.6.2",
|
||||
|
@ -14,7 +14,7 @@ import {StreamResolverService} from "./services/resolvers/stream-resolver.servic
|
||||
import {ShowDetailsComponent} from './pages/show-details/show-details.component';
|
||||
import {AuthGuard} from "./auth/misc/authenticated-guard.service";
|
||||
import {LibraryItem} from "../models/library-item";
|
||||
import {CrudApi, LibraryItemService, LibraryService} from "./services/api.service";
|
||||
import {LibraryItemService, LibraryService} from "./services/api.service";
|
||||
|
||||
const routes: Routes = [
|
||||
{path: "browse", component: LibraryItemGridComponent, pathMatch: "full",
|
||||
|
@ -33,13 +33,14 @@ import {MatDialogModule} from '@angular/material/dialog';
|
||||
import {FallbackDirective} from "./misc/fallback.directive";
|
||||
import {AuthModule} from "./auth/auth.module";
|
||||
import {AuthRoutingModule} from "./auth/auth-routing.module";
|
||||
import { TrailerDialogComponent } from './pages/trailer-dialog/trailer-dialog.component';
|
||||
import {TrailerDialogComponent} from './pages/trailer-dialog/trailer-dialog.component';
|
||||
import {CollectionsListComponent} from "./collection-list/collections-list.component";
|
||||
import { MetadataEditComponent } from './pages/metadata-edit/metadata-edit.component';
|
||||
import {MetadataEditComponent} from './pages/metadata-edit/metadata-edit.component';
|
||||
import {MatChipsModule} from "@angular/material/chips";
|
||||
import {MatAutocompleteModule} from "@angular/material/autocomplete";
|
||||
import {MatExpansionModule} from "@angular/material/expansion";
|
||||
import { ShowGridComponent } from './components/show-grid/show-grid.component';
|
||||
import {ShowGridComponent} from './components/show-grid/show-grid.component';
|
||||
import {InfiniteScrollModule} from "ngx-infinite-scroll";
|
||||
|
||||
|
||||
@NgModule({
|
||||
@ -87,7 +88,8 @@ import { ShowGridComponent } from './components/show-grid/show-grid.component';
|
||||
AuthModule,
|
||||
MatChipsModule,
|
||||
MatAutocompleteModule,
|
||||
MatExpansionModule
|
||||
MatExpansionModule,
|
||||
InfiniteScrollModule
|
||||
],
|
||||
bootstrap: [AppComponent]
|
||||
})
|
||||
|
@ -24,7 +24,7 @@
|
||||
</div>
|
||||
</mat-menu>
|
||||
|
||||
<div class="container-fluid justify-content-center">
|
||||
<div class="container-fluid justify-content-center" infinite-scroll (scrolled)="this.page?.loadNext(this.client)">
|
||||
<a class="show" *ngFor="let item of this.page.items" [href]="getLink(item)" [routerLink]="getLink(item)">
|
||||
<div matRipple [style.background-image]="getThumb(item.slug)" > </div>
|
||||
<p class="title">{{item.title}}</p>
|
||||
|
@ -4,6 +4,7 @@ import {DomSanitizer} from '@angular/platform-browser';
|
||||
import {ItemType, LibraryItem} from "../../../models/library-item";
|
||||
import {Page} from "../../../models/page";
|
||||
import {LibraryItemService} from "../../services/api.service";
|
||||
import {HttpClient} from "@angular/common/http";
|
||||
|
||||
@Component({
|
||||
selector: 'app-browse',
|
||||
@ -18,7 +19,10 @@ export class LibraryItemGridComponent
|
||||
sortKeys: string[] = ["title", "start year", "end year", "status", "type"]
|
||||
sortUp: boolean = true;
|
||||
|
||||
constructor(private route: ActivatedRoute, private sanitizer: DomSanitizer, private items: LibraryItemService)
|
||||
constructor(private route: ActivatedRoute,
|
||||
private sanitizer: DomSanitizer,
|
||||
private items: LibraryItemService,
|
||||
public client: HttpClient)
|
||||
{
|
||||
this.route.data.subscribe((data) =>
|
||||
{
|
||||
|
@ -1,11 +1,11 @@
|
||||
import {Injectable} from "@angular/core";
|
||||
import {HttpClient} from "@angular/common/http";
|
||||
import {Observable} from "rxjs"
|
||||
import {map} from "rxjs/operators"
|
||||
import {Page} from "../../models/page";
|
||||
import {IResource} from "../../models/resources/resource";
|
||||
import {Library} from "../../models/library";
|
||||
import {LibraryItem} from "../../models/library-item";
|
||||
import {map} from "rxjs/operators";
|
||||
|
||||
class CrudApi<T extends IResource>
|
||||
{
|
||||
@ -23,21 +23,10 @@ class CrudApi<T extends IResource>
|
||||
params += "sortBy=" + args.sort;
|
||||
if (params == "?")
|
||||
params = "";
|
||||
return this.client.get<Page<T>>(`/api/${this.route}${params}`);
|
||||
return this.client.get<Page<T>>(`/api/${this.route}${params}`)
|
||||
.pipe(map(x => Object.assign(new Page<T>(), x)));
|
||||
}
|
||||
|
||||
loadNext(page: Page<T>): Observable<Page<T>>
|
||||
{
|
||||
if (page.next == null)
|
||||
return;
|
||||
return this.client.get<Page<T>>(page.next).pipe(map(x =>
|
||||
{
|
||||
x.items = page.items.concat(x.items);
|
||||
x.count += page.count;
|
||||
return x;
|
||||
}));
|
||||
}
|
||||
|
||||
create(item: T): Observable<T>
|
||||
{
|
||||
return this.client.post<T>(`/api/${this.route}`, item);
|
||||
|
@ -3,7 +3,7 @@ import { Injectable } from '@angular/core';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { ActivatedRouteSnapshot, Resolve } from '@angular/router';
|
||||
import { EMPTY, Observable } from 'rxjs';
|
||||
import { catchError } from 'rxjs/operators';
|
||||
import {catchError, map} from 'rxjs/operators';
|
||||
import {Page} from "../../../models/page";
|
||||
import {IResource} from "../../../models/resources/resource";
|
||||
|
||||
@ -15,22 +15,25 @@ export class PageResolver
|
||||
static forResource<T extends IResource>(resource: string)
|
||||
{
|
||||
@Injectable()
|
||||
class Resolver<T> implements Resolve<Page<T>>
|
||||
class Resolver implements Resolve<Page<T>>
|
||||
{
|
||||
constructor(private http: HttpClient, private snackBar: MatSnackBar) { }
|
||||
|
||||
resolve(route: ActivatedRouteSnapshot): Page<T> | Observable<Page<T>> | Promise<Page<T>>
|
||||
{
|
||||
return this.http.get<Page<T>>(`api/${resource}`).pipe(catchError((error: HttpErrorResponse) =>
|
||||
{
|
||||
console.log(error.status + " - " + error.message);
|
||||
this.snackBar.open(`An unknown error occurred: ${error.message}.`, null, {
|
||||
horizontalPosition: "left",
|
||||
panelClass: ['snackError'],
|
||||
duration: 2500 }
|
||||
);
|
||||
return EMPTY;
|
||||
}));
|
||||
return this.http.get<Page<T>>(`api/${resource}`)
|
||||
.pipe(
|
||||
map(x => Object.assign(new Page<T>(), x)),
|
||||
catchError((error: HttpErrorResponse) =>
|
||||
{
|
||||
console.log(error.status + " - " + error.message);
|
||||
this.snackBar.open(`An unknown error occurred: ${error.message}.`, null, {
|
||||
horizontalPosition: "left",
|
||||
panelClass: ['snackError'],
|
||||
duration: 2500
|
||||
});
|
||||
return EMPTY;
|
||||
}));
|
||||
}
|
||||
}
|
||||
PageResolver.resolvers.push(Resolver);
|
||||
|
@ -1,8 +1,26 @@
|
||||
export interface Page<T>
|
||||
import {HttpClient} from "@angular/common/http";
|
||||
|
||||
export class Page<T>
|
||||
{
|
||||
this: string
|
||||
next: string
|
||||
first: string
|
||||
count: number
|
||||
items: T[]
|
||||
|
||||
constructor() {}
|
||||
|
||||
loadNext(client: HttpClient)
|
||||
{
|
||||
if (this.next == null)
|
||||
return;
|
||||
|
||||
client.get<Page<T>>(this.next).subscribe(x =>
|
||||
{
|
||||
this.items.push(...x.items);
|
||||
this.count += x.count;
|
||||
this.next = x.next;
|
||||
this.this = x.this;
|
||||
});
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user