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
2632
package-lock.json
generated
2632
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,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/animations": "^9.1.9",
|
"@angular/animations": "^9.1.12",
|
||||||
"@angular/cdk": "^9.2.4",
|
"@angular/cdk": "^9.2.4",
|
||||||
"@angular/common": "^9.1.9",
|
"@angular/common": "^9.1.12",
|
||||||
"@angular/compiler": "^9.1.9",
|
"@angular/compiler": "^9.1.12",
|
||||||
"@angular/core": "^9.1.9",
|
"@angular/core": "^9.1.12",
|
||||||
"@angular/forms": "^9.1.9",
|
"@angular/forms": "^9.1.12",
|
||||||
"@angular/material": "^9.2.4",
|
"@angular/material": "^9.2.4",
|
||||||
"@angular/platform-browser": "^9.1.9",
|
"@angular/platform-browser": "^9.1.12",
|
||||||
"@angular/platform-browser-dynamic": "^9.1.9",
|
"@angular/platform-browser-dynamic": "^9.1.12",
|
||||||
"@angular/router": "^9.1.9",
|
"@angular/router": "^9.1.12",
|
||||||
"angular-auth-oidc-client": "10.0.14",
|
"angular-auth-oidc-client": "10.0.14",
|
||||||
"bootstrap": "^4.5.0",
|
"bootstrap": "^4.5.0",
|
||||||
"detect-browser": "^5.1.0",
|
"detect-browser": "^5.1.1",
|
||||||
"hammerjs": "^2.0.8",
|
"hammerjs": "^2.0.8",
|
||||||
"hls.js": "^0.13.2",
|
"hls.js": "^0.13.2",
|
||||||
"jquery": "^3.5.1",
|
"jquery": "^3.5.1",
|
||||||
|
"ngx-infinite-scroll": "^9.0.0",
|
||||||
"popper.js": "^1.16.1",
|
"popper.js": "^1.16.1",
|
||||||
"zone.js": "^0.10.3"
|
"zone.js": "^0.10.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@angular-devkit/build-angular": "^0.901.7",
|
"@angular-devkit/build-angular": "^0.901.12",
|
||||||
"@angular/cli": "^9.1.7",
|
"@angular/cli": "^9.1.12",
|
||||||
"@angular/compiler-cli": "^9.1.9",
|
"@angular/compiler-cli": "^9.1.12",
|
||||||
"@angular/language-service": "^9.1.9",
|
"@angular/language-service": "^9.1.12",
|
||||||
"@types/bootstrap": "^4.5.0",
|
"@types/bootstrap": "^4.5.0",
|
||||||
"@types/hls.js": "^0.12.6",
|
"@types/hls.js": "^0.12.6",
|
||||||
"@types/jasmine": "^3.5.10",
|
"@types/jasmine": "^3.5.11",
|
||||||
"@types/jasminewd2": "^2.0.8",
|
"@types/jasminewd2": "^2.0.8",
|
||||||
"@types/jquery": "^3.3.38",
|
"@types/jquery": "^3.5.1",
|
||||||
"@types/node": "^13.13.10",
|
"@types/node": "^13.13.15",
|
||||||
"@types/video.js": "^7.3.9",
|
"@types/video.js": "^7.3.10",
|
||||||
"codelyzer": "^5.2.2",
|
"codelyzer": "^5.2.2",
|
||||||
"protractor": "^7.0.0",
|
"protractor": "^7.0.0",
|
||||||
"ts-node": "~8.6.2",
|
"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 {ShowDetailsComponent} from './pages/show-details/show-details.component';
|
||||||
import {AuthGuard} from "./auth/misc/authenticated-guard.service";
|
import {AuthGuard} from "./auth/misc/authenticated-guard.service";
|
||||||
import {LibraryItem} from "../models/library-item";
|
import {LibraryItem} from "../models/library-item";
|
||||||
import {CrudApi, LibraryItemService, LibraryService} from "./services/api.service";
|
import {LibraryItemService, LibraryService} from "./services/api.service";
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{path: "browse", component: LibraryItemGridComponent, pathMatch: "full",
|
{path: "browse", component: LibraryItemGridComponent, pathMatch: "full",
|
||||||
|
@ -40,6 +40,7 @@ import {MatChipsModule} from "@angular/material/chips";
|
|||||||
import {MatAutocompleteModule} from "@angular/material/autocomplete";
|
import {MatAutocompleteModule} from "@angular/material/autocomplete";
|
||||||
import {MatExpansionModule} from "@angular/material/expansion";
|
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({
|
@NgModule({
|
||||||
@ -87,7 +88,8 @@ import { ShowGridComponent } from './components/show-grid/show-grid.component';
|
|||||||
AuthModule,
|
AuthModule,
|
||||||
MatChipsModule,
|
MatChipsModule,
|
||||||
MatAutocompleteModule,
|
MatAutocompleteModule,
|
||||||
MatExpansionModule
|
MatExpansionModule,
|
||||||
|
InfiniteScrollModule
|
||||||
],
|
],
|
||||||
bootstrap: [AppComponent]
|
bootstrap: [AppComponent]
|
||||||
})
|
})
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</mat-menu>
|
</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)">
|
<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>
|
<div matRipple [style.background-image]="getThumb(item.slug)" > </div>
|
||||||
<p class="title">{{item.title}}</p>
|
<p class="title">{{item.title}}</p>
|
||||||
|
@ -4,6 +4,7 @@ import {DomSanitizer} from '@angular/platform-browser';
|
|||||||
import {ItemType, LibraryItem} from "../../../models/library-item";
|
import {ItemType, LibraryItem} from "../../../models/library-item";
|
||||||
import {Page} from "../../../models/page";
|
import {Page} from "../../../models/page";
|
||||||
import {LibraryItemService} from "../../services/api.service";
|
import {LibraryItemService} from "../../services/api.service";
|
||||||
|
import {HttpClient} from "@angular/common/http";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-browse',
|
selector: 'app-browse',
|
||||||
@ -18,7 +19,10 @@ export class LibraryItemGridComponent
|
|||||||
sortKeys: string[] = ["title", "start year", "end year", "status", "type"]
|
sortKeys: string[] = ["title", "start year", "end year", "status", "type"]
|
||||||
sortUp: boolean = true;
|
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) =>
|
this.route.data.subscribe((data) =>
|
||||||
{
|
{
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import {Injectable} from "@angular/core";
|
import {Injectable} from "@angular/core";
|
||||||
import {HttpClient} from "@angular/common/http";
|
import {HttpClient} from "@angular/common/http";
|
||||||
import {Observable} from "rxjs"
|
import {Observable} from "rxjs"
|
||||||
import {map} from "rxjs/operators"
|
|
||||||
import {Page} from "../../models/page";
|
import {Page} from "../../models/page";
|
||||||
import {IResource} from "../../models/resources/resource";
|
import {IResource} from "../../models/resources/resource";
|
||||||
import {Library} from "../../models/library";
|
import {Library} from "../../models/library";
|
||||||
import {LibraryItem} from "../../models/library-item";
|
import {LibraryItem} from "../../models/library-item";
|
||||||
|
import {map} from "rxjs/operators";
|
||||||
|
|
||||||
class CrudApi<T extends IResource>
|
class CrudApi<T extends IResource>
|
||||||
{
|
{
|
||||||
@ -23,19 +23,8 @@ class CrudApi<T extends IResource>
|
|||||||
params += "sortBy=" + args.sort;
|
params += "sortBy=" + args.sort;
|
||||||
if (params == "?")
|
if (params == "?")
|
||||||
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>
|
create(item: T): Observable<T>
|
||||||
|
@ -3,7 +3,7 @@ import { Injectable } from '@angular/core';
|
|||||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||||
import { ActivatedRouteSnapshot, Resolve } from '@angular/router';
|
import { ActivatedRouteSnapshot, Resolve } from '@angular/router';
|
||||||
import { EMPTY, Observable } from 'rxjs';
|
import { EMPTY, Observable } from 'rxjs';
|
||||||
import { catchError } from 'rxjs/operators';
|
import {catchError, map} from 'rxjs/operators';
|
||||||
import {Page} from "../../../models/page";
|
import {Page} from "../../../models/page";
|
||||||
import {IResource} from "../../../models/resources/resource";
|
import {IResource} from "../../../models/resources/resource";
|
||||||
|
|
||||||
@ -15,20 +15,23 @@ export class PageResolver
|
|||||||
static forResource<T extends IResource>(resource: string)
|
static forResource<T extends IResource>(resource: string)
|
||||||
{
|
{
|
||||||
@Injectable()
|
@Injectable()
|
||||||
class Resolver<T> implements Resolve<Page<T>>
|
class Resolver implements Resolve<Page<T>>
|
||||||
{
|
{
|
||||||
constructor(private http: HttpClient, private snackBar: MatSnackBar) { }
|
constructor(private http: HttpClient, private snackBar: MatSnackBar) { }
|
||||||
|
|
||||||
resolve(route: ActivatedRouteSnapshot): Page<T> | Observable<Page<T>> | Promise<Page<T>>
|
resolve(route: ActivatedRouteSnapshot): Page<T> | Observable<Page<T>> | Promise<Page<T>>
|
||||||
{
|
{
|
||||||
return this.http.get<Page<T>>(`api/${resource}`).pipe(catchError((error: HttpErrorResponse) =>
|
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);
|
console.log(error.status + " - " + error.message);
|
||||||
this.snackBar.open(`An unknown error occurred: ${error.message}.`, null, {
|
this.snackBar.open(`An unknown error occurred: ${error.message}.`, null, {
|
||||||
horizontalPosition: "left",
|
horizontalPosition: "left",
|
||||||
panelClass: ['snackError'],
|
panelClass: ['snackError'],
|
||||||
duration: 2500 }
|
duration: 2500
|
||||||
);
|
});
|
||||||
return EMPTY;
|
return EMPTY;
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,26 @@
|
|||||||
export interface Page<T>
|
import {HttpClient} from "@angular/common/http";
|
||||||
|
|
||||||
|
export class Page<T>
|
||||||
{
|
{
|
||||||
this: string
|
this: string
|
||||||
next: string
|
next: string
|
||||||
first: string
|
first: string
|
||||||
count: number
|
count: number
|
||||||
items: T[]
|
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