Added the TV Requests grid

pull/3895/head
Jamie Rees 6 years ago
parent 40c9c82eaf
commit f80ef6bb24

@ -23,5 +23,6 @@ namespace Ombi.Core.Engine.Interfaces
Task<IEnumerable<TvRequests>> GetRequestsLite();
Task UpdateQualityProfile(int requestId, int profileId);
Task UpdateRootPath(int requestId, int rootPath);
Task<RequestsViewModel<TvRequests>> GetRequests(int count, int position, string sortProperty, string sortOrder);
}
}

@ -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<RequestsViewModel<TvRequests>> GetRequests(int count, int position, string sortProperty, string sortOrder)
{
var shouldHide = await HideFromOtherUsers();
List<TvRequests> 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<TvRequests>();
}
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<TvRequests>
{
Collection = allRequests,
Total = total,
};
}
public async Task<IEnumerable<TvRequests>> GetRequestsLite()
{
var shouldHide = await HideFromOtherUsers();
@ -280,19 +338,24 @@ namespace Ombi.Core.Engine
}
private static void FilterChildren(TvRequests t, HideResult shouldHide)
{
// Filter out children
FilterChildren(t.ChildRequests, shouldHide);
}
private static void FilterChildren(List<ChildRequests> t, HideResult shouldHide)
{
// Filter out children
for (var j = 0; j < t.ChildRequests.Count; j++)
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<IEnumerable<ChildRequests>> GetAllChldren(int tvId)
@ -570,7 +633,7 @@ namespace Ombi.Core.Engine
return await AfterRequest(model.ChildRequests.FirstOrDefault());
}
private static List<ChildRequests> SortEpisodes(List<ChildRequests> items)
private static List<ChildRequests> SortEpisodes(List<ChildRequests> 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),

@ -114,6 +114,8 @@ export interface ITvRequests {
export interface IChildRequests extends IBaseRequest {
seasonRequests: INewSeasonRequests[];
parentRequestId: number;
parentRequest: ITvRequests;
subscribed: boolean;
showSubscribe: boolean;
}

@ -0,0 +1,5 @@
<div class="loading-shade" *ngIf="loading">
<div class="row justify-content-md-center top-spacing loading-spinner">
<mat-spinner [color]="'accent'"></mat-spinner>
</div>
</div>

@ -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;
}

@ -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;
}

@ -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,
];

@ -1,9 +1,5 @@
<div class="mat-elevation-z8">
<div class="loading-shade" *ngIf="isLoadingResults">
<div *ngIf="isLoadingResults" class="row justify-content-md-center top-spacing loading-spinner">
<mat-spinner [color]="'accent'"></mat-spinner>
</div>
</div>
<grid-spinner [loading]="isLoading"></grid-spinner>
<mat-form-field>
<mat-select placeholder="Requests to Display" [(value)]="gridCount" (selectionChange)="ngAfterViewInit()">

@ -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';

@ -1,11 +1,14 @@
<div class="small-middle-container">
<mat-tab-group>
<mat-tab label="Movies">
<movies-grid></movies-grid>
<ng-template matTabContent>
<movies-grid></movies-grid>
</ng-template>
</mat-tab>
<mat-tab label="TV Shows">
<h1>Some more tab content</h1>
<p>...</p>
<ng-template matTabContent>
<tv-grid></tv-grid>
</ng-template>
</mat-tab>
<mat-tab label="Albums">
<h1>Some more tab content</h1>

@ -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;
}

@ -0,0 +1,62 @@
<div class="mat-elevation-z8">
<grid-spinner [loading]="isLoading"></grid-spinner>
<mat-form-field>
<mat-select placeholder="Requests to Display" [(value)]="gridCount" (selectionChange)="ngAfterViewInit()">
<mat-option value="10">10</mat-option>
<mat-option value="15">15</mat-option>
<mat-option value="30">30</mat-option>
<mat-option value="100">100</mat-option>
</mat-select>
</mat-form-field>
<table mat-table [dataSource]="dataSource" class="table" matSort matSortActive="title"
matSortDisableClear matSortDirection="desc">
<ng-container matColumnDef="title">
<th mat-header-cell *matHeaderCellDef mat-sort-header disableClear> Title </th>
<td mat-cell *matCellDef="let element"> {{element.title}} </td>
</ng-container>
<ng-container matColumnDef="requestCount">
<th mat-header-cell *matHeaderCellDef > Request Count </th>
<td mat-cell *matCellDef="let element"> {{element.childRequests.length}} </td>
</ng-container>
<ng-container matColumnDef="overview">
<th mat-header-cell *matHeaderCellDef > Overview </th>
<td mat-cell *matCellDef="let element">
<small>{{element.overview}}</small>
</td>
</ng-container>
<ng-container matColumnDef="status">
<th mat-header-cell *matHeaderCellDef mat-sort-header disableClear> Status </th>
<td mat-cell *matCellDef="let element">
<small>{{element.status}}</small>
</td>
</ng-container>
<ng-container matColumnDef="releaseDate">
<th mat-header-cell *matHeaderCellDef mat-sort-header disableClear> Release Date </th>
<td mat-cell *matCellDef="let element">
<small>{{element.releaseDate | amLocal | amDateFormat: 'LL'}}</small>
</td>
</ng-container>
<ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef> </th>
<td mat-cell *matCellDef="let element">
<button mat-raised-button color="primary" [routerLink]="'/details/movie/' + element.theMovieDbId">Details</button>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
<mat-paginator [length]="resultsLength" [pageSize]="gridCount"></mat-paginator>
</div>

@ -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<ITvRequests>) => {
// 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);
}
}

@ -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<IRequestsViewModel<IMovieRequests>>(`${this.url}movie/${count}/${position}/${sortProperty}/${order}`, {headers: this.headers});
}
public getTvRequests(count: number, position: number, sortProperty: string , order: string): Observable<IRequestsViewModel<ITvRequests>> {
return this.http.get<IRequestsViewModel<ITvRequests>>(`${this.url}tv/${count}/${position}/${sortProperty}/${order}`, {headers: this.headers});
}
}

@ -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;
/// <summary>
/// Gets movie requests.
@ -34,5 +36,18 @@ namespace Ombi.Controllers.V2
{
return await _movieRequestEngine.GetRequests(count, position, sort, sortOrder);
}
/// <summary>
/// Gets Tv requests.
/// </summary>
/// <param name="count">The count of items you want to return. e.g. 30</param>
/// <param name="position">The position. e.g. position 60 for a 2nd page (since we have already got the first 30 items)</param>
/// <param name="sort">The item to sort on e.g. "requestDate"</param>
/// <param name="sortOrder">asc or desc</param>
[HttpGet("tv/{count:int}/{position:int}/{sort}/{sortOrder}")]
public async Task<RequestsViewModel<TvRequests>> GetTvRequests(int count, int position, string sort, string sortOrder)
{
return await _tvRequestEngine.GetRequests(count, position, sort, sortOrder);
}
}
}
Loading…
Cancel
Save