From f80ef6bb2441be0e25c50ae865a3654e1b09eb7d Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Mon, 18 Mar 2019 13:37:40 +0000 Subject: [PATCH] Added the TV Requests grid --- .../Engine/Interfaces/ITvRequestEngine.cs | 1 + src/Ombi.Core/Engine/TvRequestEngine.cs | 81 ++++++++++++++++--- .../src/app/interfaces/IRequestModel.ts | 2 + .../grid-spinner/grid-spinner.component.html | 5 ++ .../grid-spinner/grid-spinner.component.scss | 12 +++ .../grid-spinner/grid-spinner.component.ts | 11 +++ .../src/app/requests-list/components/index.ts | 4 + .../movies-grid/movies-grid.component.html | 6 +- .../movies-grid/movies-grid.component.ts | 2 +- .../components/requests-list.component.html | 9 ++- .../components/requests-list.component.scss | 12 --- .../components/tv-grid/tv-grid.component.html | 62 ++++++++++++++ .../components/tv-grid/tv-grid.component.ts | 53 ++++++++++++ .../src/app/services/requestV2.service.ts | 6 +- src/Ombi/Controllers/V2/RequestsController.cs | 17 +++- 15 files changed, 251 insertions(+), 32 deletions(-) create mode 100644 src/Ombi/ClientApp/src/app/requests-list/components/grid-spinner/grid-spinner.component.html create mode 100644 src/Ombi/ClientApp/src/app/requests-list/components/grid-spinner/grid-spinner.component.scss create mode 100644 src/Ombi/ClientApp/src/app/requests-list/components/grid-spinner/grid-spinner.component.ts create mode 100644 src/Ombi/ClientApp/src/app/requests-list/components/tv-grid/tv-grid.component.html create mode 100644 src/Ombi/ClientApp/src/app/requests-list/components/tv-grid/tv-grid.component.ts diff --git a/src/Ombi.Core/Engine/Interfaces/ITvRequestEngine.cs b/src/Ombi.Core/Engine/Interfaces/ITvRequestEngine.cs index 63de72bd1..f726f1b4c 100644 --- a/src/Ombi.Core/Engine/Interfaces/ITvRequestEngine.cs +++ b/src/Ombi.Core/Engine/Interfaces/ITvRequestEngine.cs @@ -23,5 +23,6 @@ namespace Ombi.Core.Engine.Interfaces Task> GetRequestsLite(); Task UpdateQualityProfile(int requestId, int profileId); Task UpdateRootPath(int requestId, int rootPath); + Task> GetRequests(int count, int position, string sortProperty, string sortOrder); } } \ No newline at end of file diff --git a/src/Ombi.Core/Engine/TvRequestEngine.cs b/src/Ombi.Core/Engine/TvRequestEngine.cs index 58144d3ce..b62f7f96a 100644 --- a/src/Ombi.Core/Engine/TvRequestEngine.cs +++ b/src/Ombi.Core/Engine/TvRequestEngine.cs @@ -7,6 +7,7 @@ using Ombi.Core.Models.Search; using Ombi.Helpers; using Ombi.Store.Entities; using System.Collections.Generic; +using System.ComponentModel; using System.Globalization; using System.Linq; using System.Security.Principal; @@ -160,7 +161,7 @@ namespace Ombi.Core.Engine .ThenInclude(x => x.Episodes) .OrderByDescending(x => x.ChildRequests.Select(y => y.RequestedDate).FirstOrDefault()) .Skip(position).Take(count).ToListAsync(); - + } allRequests.ForEach(async r => { await CheckForSubscription(shouldHide, r); }); @@ -225,6 +226,63 @@ namespace Ombi.Core.Engine } + public async Task> GetRequests(int count, int position, string sortProperty, string sortOrder) + { + var shouldHide = await HideFromOtherUsers(); + List allRequests = null; + if (shouldHide.Hide) + { + var tv = TvRepository.GetLite(shouldHide.UserId); + if (tv.Any() && tv.Select(x => x.ChildRequests).Any()) + { + allRequests = await tv.OrderByDescending(x => x.ChildRequests.Select(y => y.RequestedDate).FirstOrDefault()).Skip(position).Take(count).ToListAsync(); + } + + // Filter out children + FilterChildren(allRequests, shouldHide); + } + else + { + var tv = TvRepository.GetLite(); + if (tv.Any() && tv.Select(x => x.ChildRequests).Any()) + { + allRequests = await tv.OrderByDescending(x => x.ChildRequests.Select(y => y.RequestedDate).FirstOrDefault()).Skip(position).Take(count).ToListAsync(); + } + } + + if (allRequests == null) + { + return new RequestsViewModel(); + } + + var total = allRequests.Count; + + + var prop = TypeDescriptor.GetProperties(typeof(TvRequests)).Find(sortProperty, true); + + if (sortProperty.Contains('.')) + { + // This is a navigation property currently not supported + prop = TypeDescriptor.GetProperties(typeof(TvRequests)).Find("Title", true); + //var properties = sortProperty.Split(new []{'.'}, StringSplitOptions.RemoveEmptyEntries); + //var firstProp = TypeDescriptor.GetProperties(typeof(MovieRequests)).Find(properties[0], true); + //var propType = firstProp.PropertyType; + //var secondProp = TypeDescriptor.GetProperties(propType).Find(properties[1], true); + } + allRequests = sortOrder.Equals("asc", StringComparison.InvariantCultureIgnoreCase) + ? allRequests.OrderBy(x => prop.GetValue(x)).ToList() + : allRequests.OrderByDescending(x => prop.GetValue(x)).ToList(); + allRequests.ForEach(async r => { await CheckForSubscription(shouldHide, r); }); + + + return new RequestsViewModel + { + Collection = allRequests, + Total = total, + }; + } + + public async Task> GetRequestsLite() { var shouldHide = await HideFromOtherUsers(); @@ -282,17 +340,22 @@ namespace Ombi.Core.Engine private static void FilterChildren(TvRequests t, HideResult shouldHide) { // Filter out children + FilterChildren(t.ChildRequests, shouldHide); + } - for (var j = 0; j < t.ChildRequests.Count; j++) + private static void FilterChildren(List t, HideResult shouldHide) + { + // Filter out children + + for (var j = 0; j < t.Count; j++) { - var child = t.ChildRequests[j]; + var child = t[j]; if (child.RequestedUserId != shouldHide.UserId) { - t.ChildRequests.RemoveAt(j); + t.RemoveAt(j); j--; } } - } public async Task> GetAllChldren(int tvId) @@ -570,7 +633,7 @@ namespace Ombi.Core.Engine return await AfterRequest(model.ChildRequests.FirstOrDefault()); } - private static List SortEpisodes(List items) + private static List SortEpisodes(List items) { foreach (var value in items) { @@ -607,7 +670,7 @@ namespace Ombi.Core.Engine var result = await TvSender.Send(model); if (result.Success) { - return new RequestEngineResult { Result = true, RequestId = model.Id}; + return new RequestEngineResult { Result = true, RequestId = model.Id }; } return new RequestEngineResult { @@ -660,10 +723,10 @@ namespace Ombi.Core.Engine DateTime oldestRequestedAt = await log.OrderBy(x => x.RequestDate) .Select(x => x.RequestDate) .FirstOrDefaultAsync(); - + return new RequestQuotaCountModel() { - HasLimit = true, + HasLimit = true, Limit = limit, Remaining = count, NextRequest = DateTime.SpecifyKind(oldestRequestedAt.AddDays(7), DateTimeKind.Utc), diff --git a/src/Ombi/ClientApp/src/app/interfaces/IRequestModel.ts b/src/Ombi/ClientApp/src/app/interfaces/IRequestModel.ts index 5f71ad529..a45871770 100644 --- a/src/Ombi/ClientApp/src/app/interfaces/IRequestModel.ts +++ b/src/Ombi/ClientApp/src/app/interfaces/IRequestModel.ts @@ -114,6 +114,8 @@ export interface ITvRequests { export interface IChildRequests extends IBaseRequest { seasonRequests: INewSeasonRequests[]; + parentRequestId: number; + parentRequest: ITvRequests; subscribed: boolean; showSubscribe: boolean; } diff --git a/src/Ombi/ClientApp/src/app/requests-list/components/grid-spinner/grid-spinner.component.html b/src/Ombi/ClientApp/src/app/requests-list/components/grid-spinner/grid-spinner.component.html new file mode 100644 index 000000000..ae5bbe1f9 --- /dev/null +++ b/src/Ombi/ClientApp/src/app/requests-list/components/grid-spinner/grid-spinner.component.html @@ -0,0 +1,5 @@ +
+
+ +
+
\ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/requests-list/components/grid-spinner/grid-spinner.component.scss b/src/Ombi/ClientApp/src/app/requests-list/components/grid-spinner/grid-spinner.component.scss new file mode 100644 index 000000000..497a59e76 --- /dev/null +++ b/src/Ombi/ClientApp/src/app/requests-list/components/grid-spinner/grid-spinner.component.scss @@ -0,0 +1,12 @@ +.loading-shade { + position: absolute; + top: 0; + left: 0; + bottom: 56px; + right: 0; + background: rgba(0, 0, 0, 0.15); + z-index: 1; + display: flex; + align-items: center; + justify-content: center; +} \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/requests-list/components/grid-spinner/grid-spinner.component.ts b/src/Ombi/ClientApp/src/app/requests-list/components/grid-spinner/grid-spinner.component.ts new file mode 100644 index 000000000..d3b0f78fd --- /dev/null +++ b/src/Ombi/ClientApp/src/app/requests-list/components/grid-spinner/grid-spinner.component.ts @@ -0,0 +1,11 @@ +import { Component, Input } from "@angular/core"; + + +@Component({ + templateUrl: "./grid-spinner.component.html", + selector: "grid-spinner", + styleUrls: ["./grid-spinner.component.scss"] +}) +export class GridSpinnerComponent{ + @Input() public loading = false; +} diff --git a/src/Ombi/ClientApp/src/app/requests-list/components/index.ts b/src/Ombi/ClientApp/src/app/requests-list/components/index.ts index 9fee1c6d6..7f226fea5 100644 --- a/src/Ombi/ClientApp/src/app/requests-list/components/index.ts +++ b/src/Ombi/ClientApp/src/app/requests-list/components/index.ts @@ -3,10 +3,14 @@ import { MoviesGridComponent } from "./movies-grid/movies-grid.component"; import { RequestServiceV2 } from "../../services/requestV2.service"; import { RequestService } from "../../services"; +import { TvGridComponent } from "./tv-grid/tv-grid.component"; +import { GridSpinnerComponent } from "./grid-spinner/grid-spinner.component"; export const components: any[] = [ RequestsListComponent, MoviesGridComponent, + TvGridComponent, + GridSpinnerComponent, ]; diff --git a/src/Ombi/ClientApp/src/app/requests-list/components/movies-grid/movies-grid.component.html b/src/Ombi/ClientApp/src/app/requests-list/components/movies-grid/movies-grid.component.html index cf1bc5166..639372201 100644 --- a/src/Ombi/ClientApp/src/app/requests-list/components/movies-grid/movies-grid.component.html +++ b/src/Ombi/ClientApp/src/app/requests-list/components/movies-grid/movies-grid.component.html @@ -1,9 +1,5 @@
-
-
- -
-
+ diff --git a/src/Ombi/ClientApp/src/app/requests-list/components/movies-grid/movies-grid.component.ts b/src/Ombi/ClientApp/src/app/requests-list/components/movies-grid/movies-grid.component.ts index 60c0d0604..bec54739c 100644 --- a/src/Ombi/ClientApp/src/app/requests-list/components/movies-grid/movies-grid.component.ts +++ b/src/Ombi/ClientApp/src/app/requests-list/components/movies-grid/movies-grid.component.ts @@ -1,5 +1,5 @@ import { Component, AfterViewInit, ViewChild } from "@angular/core"; -import { IMovieRequests, OrderType, FilterType, IRequestsViewModel } from "../../../interfaces"; +import { IMovieRequests, IRequestsViewModel } from "../../../interfaces"; import { MatPaginator, MatSort } from "@angular/material"; import { merge, Observable, of as observableOf } from 'rxjs'; import { catchError, map, startWith, switchMap } from 'rxjs/operators'; diff --git a/src/Ombi/ClientApp/src/app/requests-list/components/requests-list.component.html b/src/Ombi/ClientApp/src/app/requests-list/components/requests-list.component.html index 0ae25240f..4c0111d06 100644 --- a/src/Ombi/ClientApp/src/app/requests-list/components/requests-list.component.html +++ b/src/Ombi/ClientApp/src/app/requests-list/components/requests-list.component.html @@ -1,11 +1,14 @@
- + + + -

Some more tab content

-

...

+ + +

Some more tab content

diff --git a/src/Ombi/ClientApp/src/app/requests-list/components/requests-list.component.scss b/src/Ombi/ClientApp/src/app/requests-list/components/requests-list.component.scss index 1d1ce17cf..eab4058e6 100644 --- a/src/Ombi/ClientApp/src/app/requests-list/components/requests-list.component.scss +++ b/src/Ombi/ClientApp/src/app/requests-list/components/requests-list.component.scss @@ -7,15 +7,3 @@ width: 100%; height:100%; } -.loading-shade { - position: absolute; - top: 0; - left: 0; - bottom: 56px; - right: 0; - background: rgba(0, 0, 0, 0.15); - z-index: 1; - display: flex; - align-items: center; - justify-content: center; - } \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/requests-list/components/tv-grid/tv-grid.component.html b/src/Ombi/ClientApp/src/app/requests-list/components/tv-grid/tv-grid.component.html new file mode 100644 index 000000000..6664ec19c --- /dev/null +++ b/src/Ombi/ClientApp/src/app/requests-list/components/tv-grid/tv-grid.component.html @@ -0,0 +1,62 @@ +
+ + + + + + 10 + 15 + 30 + 100 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Title {{element.title}} Request Count {{element.childRequests.length}} Overview + {{element.overview}} + Status + {{element.status}} + Release Date + {{element.releaseDate | amLocal | amDateFormat: 'LL'}} + + +
+ + +
diff --git a/src/Ombi/ClientApp/src/app/requests-list/components/tv-grid/tv-grid.component.ts b/src/Ombi/ClientApp/src/app/requests-list/components/tv-grid/tv-grid.component.ts new file mode 100644 index 000000000..f14f9a25c --- /dev/null +++ b/src/Ombi/ClientApp/src/app/requests-list/components/tv-grid/tv-grid.component.ts @@ -0,0 +1,53 @@ +import { Component, AfterViewInit, ViewChild } from "@angular/core"; +import { IRequestsViewModel, ITvRequests } from "../../../interfaces"; +import { MatPaginator, MatSort } from "@angular/material"; +import { merge, Observable, of as observableOf } from 'rxjs'; +import { catchError, map, startWith, switchMap } from 'rxjs/operators'; + +import { RequestServiceV2 } from "../../../services/requestV2.service"; + +@Component({ + templateUrl: "./tv-grid.component.html", + selector: "tv-grid", + styleUrls: ["../requests-list.component.scss"] +}) +export class TvGridComponent implements AfterViewInit { + public dataSource: ITvRequests[] = []; + public resultsLength: number; + public isLoadingResults = true; + public displayedColumns: string[] = ['title', 'overview', 'status', 'requestCount', 'releaseDate','actions']; + public gridCount: string = "15"; + + @ViewChild(MatPaginator) paginator: MatPaginator; + @ViewChild(MatSort) sort: MatSort; + + constructor(private requestService: RequestServiceV2) { + + } + + public async ngAfterViewInit() { + + // If the user changes the sort order, reset back to the first page. + this.sort.sortChange.subscribe(() => this.paginator.pageIndex = 0); + + merge(this.sort.sortChange, this.paginator.page) + .pipe( + startWith({}), + switchMap(() => { + this.isLoadingResults = true; + return this.requestService.getTvRequests(+this.gridCount, this.paginator.pageIndex * +this.gridCount, this.sort.active, this.sort.direction); + }), + map((data: IRequestsViewModel) => { + // Flip flag to show that loading has finished. + this.isLoadingResults = false; + this.resultsLength = data.total; + + return data.collection; + }), + catchError((err) => { + this.isLoadingResults = false; + return observableOf([]); + }) + ).subscribe(data => this.dataSource = data); + } +} diff --git a/src/Ombi/ClientApp/src/app/services/requestV2.service.ts b/src/Ombi/ClientApp/src/app/services/requestV2.service.ts index 3c439214f..b95c3d2f0 100644 --- a/src/Ombi/ClientApp/src/app/services/requestV2.service.ts +++ b/src/Ombi/ClientApp/src/app/services/requestV2.service.ts @@ -4,7 +4,7 @@ import { Injectable } from "@angular/core"; import { HttpClient } from "@angular/common/http"; import { Observable } from "rxjs"; import { ServiceHelpers } from "./service.helpers"; -import { IRequestsViewModel, IMovieRequests } from "../interfaces"; +import { IRequestsViewModel, IMovieRequests, ITvRequests } from "../interfaces"; @Injectable() @@ -17,4 +17,8 @@ export class RequestServiceV2 extends ServiceHelpers { return this.http.get>(`${this.url}movie/${count}/${position}/${sortProperty}/${order}`, {headers: this.headers}); } + public getTvRequests(count: number, position: number, sortProperty: string , order: string): Observable> { + return this.http.get>(`${this.url}tv/${count}/${position}/${sortProperty}/${order}`, {headers: this.headers}); + } + } diff --git a/src/Ombi/Controllers/V2/RequestsController.cs b/src/Ombi/Controllers/V2/RequestsController.cs index 5d351f752..2446d9fae 100644 --- a/src/Ombi/Controllers/V2/RequestsController.cs +++ b/src/Ombi/Controllers/V2/RequestsController.cs @@ -15,12 +15,14 @@ namespace Ombi.Controllers.V2 [ApiController] public class RequestsController : ControllerBase { - public RequestsController(IMovieRequestEngine movieRequestEngine) + public RequestsController(IMovieRequestEngine movieRequestEngine, ITvRequestEngine tvRequestEngine) { _movieRequestEngine = movieRequestEngine; + _tvRequestEngine = tvRequestEngine; } private readonly IMovieRequestEngine _movieRequestEngine; + private readonly ITvRequestEngine _tvRequestEngine; /// /// Gets movie requests. @@ -34,5 +36,18 @@ namespace Ombi.Controllers.V2 { return await _movieRequestEngine.GetRequests(count, position, sort, sortOrder); } + + /// + /// Gets Tv requests. + /// + /// The count of items you want to return. e.g. 30 + /// The position. e.g. position 60 for a 2nd page (since we have already got the first 30 items) + /// The item to sort on e.g. "requestDate" + /// asc or desc + [HttpGet("tv/{count:int}/{position:int}/{sort}/{sortOrder}")] + public async Task> GetTvRequests(int count, int position, string sort, string sortOrder) + { + return await _tvRequestEngine.GetRequests(count, position, sort, sortOrder); + } } } \ No newline at end of file