Merge branch 'DotNetCore' of https://github.com/tidusjar/Ombi into DotNetCore

pull/1389/head
tidusjar 8 years ago
commit 4c95b1f70b

@ -11,5 +11,9 @@ namespace Ombi.Core.Engine
Task<RequestEngineResult> RequestMovie(SearchMovieViewModel model); Task<RequestEngineResult> RequestMovie(SearchMovieViewModel model);
bool ShouldAutoApprove(RequestType requestType); bool ShouldAutoApprove(RequestType requestType);
Task<IEnumerable<RequestViewModel>> GetRequests(); Task<IEnumerable<RequestViewModel>> GetRequests();
Task<IEnumerable<RequestViewModel>> GetRequests(int count, int position);
Task<IEnumerable<RequestViewModel>> SearchRequest(string search);
Task RemoveRequest(int requestId);
Task<RequestViewModel> UpdateRequest(RequestViewModel request);
} }
} }

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Ombi.Core.Models.Requests; using Ombi.Core.Models.Requests;
@ -7,6 +8,7 @@ using Ombi.Core.Models.Search;
using Ombi.Core.Requests.Models; using Ombi.Core.Requests.Models;
using Ombi.Store.Entities; using Ombi.Store.Entities;
using Ombi.TheMovieDbApi; using Ombi.TheMovieDbApi;
using Ombi.Helpers;
namespace Ombi.Core.Engine namespace Ombi.Core.Engine
{ {
@ -233,7 +235,41 @@ namespace Ombi.Core.Engine
public async Task<IEnumerable<RequestViewModel>> GetRequests() public async Task<IEnumerable<RequestViewModel>> GetRequests()
{ {
var allRequests = await RequestService.GetAllAsync(); var allRequests = await RequestService.GetAllAsync();
var viewModel = allRequests.Select(movie => new RequestViewModel var viewModel = MapToVm(allRequests);
return viewModel;
}
public async Task<IEnumerable<RequestViewModel>> GetRequests(int count, int position)
{
var allRequests = await RequestService.GetAllAsync(count, position);
var viewModel = MapToVm(allRequests);
return viewModel;
}
public async Task<IEnumerable<RequestViewModel>> SearchRequest(string search)
{
var allRequests = await RequestService.GetAllAsync();
var results = allRequests.Where(x => x.Title.Contains(search, CompareOptions.IgnoreCase));
var viewModel = MapToVm(results);
return viewModel;
}
public async Task<RequestViewModel> UpdateRequest(RequestViewModel request)
{
var allRequests = await RequestService.GetAllAsync();
var results = allRequests.FirstOrDefault(x => x.Id == request.Id);
var model = RequestService.UpdateRequest(results);
return MapToVm(new List<RequestModel>{model}).FirstOrDefault();
}
public async Task RemoveRequest(int requestId)
{
await RequestService.DeleteRequestAsync(requestId);
}
private IEnumerable<RequestViewModel> MapToVm(IEnumerable<RequestModel> model)
{
return model.Select(movie => new RequestViewModel
{ {
ProviderId = movie.ProviderId, ProviderId = movie.ProviderId,
Type = movie.Type, Type = movie.Type,
@ -259,8 +295,6 @@ namespace Ombi.Core.Engine
//RootFolders = rootFolders.ToArray(), //RootFolders = rootFolders.ToArray(),
//CurrentRootPath = radarr.Enabled ? GetRootPath(movie.RootFolderSelected, radarr).Result : null //CurrentRootPath = radarr.Enabled ? GetRootPath(movie.RootFolderSelected, radarr).Result : null
}).ToList(); }).ToList();
return viewModel;
} }
} }
} }

@ -16,11 +16,13 @@ namespace Ombi.Core.Requests.Models
Task<RequestModel> CheckRequestAsync(int providerId); Task<RequestModel> CheckRequestAsync(int providerId);
Task<RequestModel> CheckRequestAsync(string musicId); Task<RequestModel> CheckRequestAsync(string musicId);
void DeleteRequest(RequestModel request); void DeleteRequest(RequestModel request);
Task DeleteRequestAsync(int request);
Task DeleteRequestAsync(RequestModel request); Task DeleteRequestAsync(RequestModel request);
RequestModel Get(int id); RequestModel Get(int id);
IEnumerable<RequestModel> GetAll(); IEnumerable<RequestModel> GetAll();
Task<IEnumerable<RequestModel>> GetAllAsync(); Task<IEnumerable<RequestModel>> GetAllAsync();
Task<IEnumerable<RequestModel>> GetAllAsync(int count, int position);
Task<RequestModel> GetAsync(int id); Task<RequestModel> GetAsync(int id);
RequestBlobs UpdateRequest(RequestModel model); RequestModel UpdateRequest(RequestModel model);
} }
} }

@ -92,11 +92,19 @@ namespace Ombi.Core.Models.Requests
var blob = await Repo.GetAsync(request.Id).ConfigureAwait(false); var blob = await Repo.GetAsync(request.Id).ConfigureAwait(false);
Repo.Delete(blob); Repo.Delete(blob);
} }
public async Task DeleteRequestAsync(int request)
{
var blob = await Repo.GetAsync(request).ConfigureAwait(false);
Repo.Delete(blob);
}
public RequestBlobs UpdateRequest(RequestModel model) public RequestModel UpdateRequest(RequestModel model)
{ {
var entity = new RequestBlobs { Type = model.Type, Content = ByteConverterHelper.ReturnBytes(model), ProviderId = model.ProviderId, Id = model.Id }; var b = Repo.Get(model.Id);
return Repo.Update(entity); b.Content = ByteConverterHelper.ReturnBytes(model);
var blob = Repo.Update(b);
return model;
} }
public RequestModel Get(int id) public RequestModel Get(int id)
@ -159,6 +167,24 @@ namespace Ombi.Core.Models.Requests
return retVal; return retVal;
} }
public async Task<IEnumerable<RequestModel>> GetAllAsync(int count, int position)
{
var blobs = await Repo.GetAllAsync(count, position).ConfigureAwait(false);
var retVal = new List<RequestModel>();
foreach (var b in blobs)
{
if (b == null)
{
continue;
}
var model = ByteConverterHelper.ReturnObject<RequestModel>(b.Content);
model.Id = b.Id;
retVal.Add(model);
}
return retVal;
}
public void BatchUpdate(IEnumerable<RequestModel> model) public void BatchUpdate(IEnumerable<RequestModel> model)
{ {
var entities = model.Select(m => new RequestBlobs { Type = m.Type, Content = ByteConverterHelper.ReturnBytes(m), ProviderId = m.ProviderId, Id = m.Id }).ToList(); var entities = model.Select(m => new RequestBlobs { Type = m.Type, Content = ByteConverterHelper.ReturnBytes(m), ProviderId = m.ProviderId, Id = m.Id }).ToList();

@ -0,0 +1,12 @@
using System.Globalization;
namespace Ombi.Helpers
{
public static class StringHelper
{
public static bool Contains(this string paragraph, string word, CompareOptions opts)
{
return CultureInfo.CurrentUICulture.CompareInfo.IndexOf(paragraph, word, CompareOptions.IgnoreCase) >= 0;
}
}
}

@ -11,6 +11,7 @@ namespace Ombi.Store.Repository
RequestBlobs Get(int id); RequestBlobs Get(int id);
IEnumerable<RequestBlobs> GetAll(); IEnumerable<RequestBlobs> GetAll();
Task<IEnumerable<RequestBlobs>> GetAllAsync(); Task<IEnumerable<RequestBlobs>> GetAllAsync();
Task<IEnumerable<RequestBlobs>> GetAllAsync(int count, int position);
Task<RequestBlobs> GetAsync(int id); Task<RequestBlobs> GetAsync(int id);
RequestBlobs Insert(RequestBlobs entity); RequestBlobs Insert(RequestBlobs entity);
Task<RequestBlobs> InsertAsync(RequestBlobs entity); Task<RequestBlobs> InsertAsync(RequestBlobs entity);

@ -1,4 +1,5 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
@ -61,6 +62,18 @@ namespace Ombi.Store.Repository
//}, 5); //}, 5);
//return item; //return item;
} }
public async Task<IEnumerable<RequestBlobs>> GetAllAsync(int count, int position)
{
//var key = "GetAll";
//var item = await Cache.GetOrSetAsync(key, async () =>
//{
var page = await Db.Requests.ToListAsync().ConfigureAwait(false);
return page.Skip(position).Take(count);
//}, 5);
//return item;
}
public RequestBlobs Get(int id) public RequestBlobs Get(int id)
{ {
@ -99,10 +112,18 @@ namespace Ombi.Store.Repository
public RequestBlobs Update(RequestBlobs entity) public RequestBlobs Update(RequestBlobs entity)
{ {
try
return Db.Requests.Update(entity).Entity; {
Db.SaveChanges(); Db.SaveChanges();
return entity;
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
} }
public void UpdateAll(IEnumerable<RequestBlobs> entity) public void UpdateAll(IEnumerable<RequestBlobs> entity)

@ -1,7 +1,7 @@
#region Copyright #region Copyright
// /************************************************************************ // /************************************************************************
// Copyright (c) 2017 Jamie Rees // Copyright (c) 2017 Jamie Rees
// File: BaseApiController.cs // File: BaseV1ApiController.cs
// Created By: Jamie Rees // Created By: Jamie Rees
// //
// Permission is hereby granted, free of charge, to any person obtaining // Permission is hereby granted, free of charge, to any person obtaining
@ -29,8 +29,9 @@ using Microsoft.AspNetCore.Mvc;
namespace Ombi.Controllers namespace Ombi.Controllers
{ {
[Route("api/[controller]")] [Route(ApiBase)]
public class BaseApiController : Controller public class BaseV1ApiController : Controller
{ {
protected const string ApiBase = "api/v1/[controller]";
} }
} }

@ -7,7 +7,7 @@ using Ombi.Core.Models.Search;
namespace Ombi.Controllers namespace Ombi.Controllers
{ {
public class RequestController : BaseApiController public class RequestController : BaseV1ApiController
{ {
public RequestController(IRequestEngine engine) public RequestController(IRequestEngine engine)
{ {
@ -22,10 +22,34 @@ namespace Ombi.Controllers
return await RequestEngine.GetRequests(); return await RequestEngine.GetRequests();
} }
[HttpGet("{count:int}/{position:int}", Name = "GetRequestsByCount")]
public async Task<IEnumerable<RequestViewModel>> GetRequests(int count, int position)
{
return await RequestEngine.GetRequests(count, position);
}
[HttpPost("movie")] [HttpPost("movie")]
public async Task<RequestEngineResult> SearchMovie([FromBody]SearchMovieViewModel movie) public async Task<RequestEngineResult> RequestMovie([FromBody]SearchMovieViewModel movie)
{ {
return await RequestEngine.RequestMovie(movie); return await RequestEngine.RequestMovie(movie);
} }
[HttpGet("search/{searchTerm}")]
public async Task<IEnumerable<RequestViewModel>> Search(string searchTerm)
{
return await RequestEngine.SearchRequest(searchTerm);
}
[HttpDelete("{requestId:int}")]
public async Task DeleteRequest(int requestId)
{
await RequestEngine.RemoveRequest(requestId);
}
[HttpPost]
public async Task<RequestViewModel> UpdateRequest([FromBody]RequestViewModel model)
{
return await RequestEngine.UpdateRequest(model);
}
} }
} }

@ -8,7 +8,7 @@ using Ombi.Core.Models.Search;
namespace Ombi.Controllers namespace Ombi.Controllers
{ {
public class SearchController : BaseApiController public class SearchController : BaseV1ApiController
{ {
public SearchController(IMovieEngine movie) public SearchController(IMovieEngine movie)
{ {

@ -31,7 +31,7 @@ var paths = {
'@angular/platform-browser-dynamic', '@angular/platform-browser-dynamic',
'@angular/http', '@angular/http',
'@angular/router', '@angular/router',
'@angular/forms' '@angular/forms',
], ],
dest: './lib' dest: './lib'
}, },
@ -56,7 +56,7 @@ var paths = {
'./bower_components/PACE/pace.js', './bower_components/PACE/pace.js',
'./node_modules/bootstrap/dist/js/bootstrap.js', './node_modules/bootstrap/dist/js/bootstrap.js',
'./node_modules/tether/dist/js/tether.js', './node_modules/tether/dist/js/tether.js',
'./systemjs.config.js' './systemjs.config.js',
], ],
dest: './lib/' dest: './lib/'
}, },
@ -118,7 +118,12 @@ var paths = {
name: 'primeng', name: 'primeng',
src: './node_modules/primeng/**/*.js', src: './node_modules/primeng/**/*.js',
dest: './lib/primeng/' dest: './lib/primeng/'
} },
{
name: "angular2-infinite-scroll",
src: ['./node_modules/angular2-infinite-scroll/**/*.js', '!./node_modules/angular2-infinite-scroll/bundles/**/*.js'],
dest:"./lib/angular2-infinite-scroll/"
},
], ],
sass: { // Simple sass->css compilation sass: { // Simple sass->css compilation
src: ['./Styles/**/*.scss', '!./Styles/primeng/**'], src: ['./Styles/**/*.scss', '!./Styles/primeng/**'],

@ -16,6 +16,8 @@
"@angular/router": "^4.0.0", "@angular/router": "^4.0.0",
"@types/jquery": "^2.0.33", "@types/jquery": "^2.0.33",
"@types/systemjs": "^0.20.2", "@types/systemjs": "^0.20.2",
"angular2-infinite-scroll": "^0.3.4",
"bootstrap": "3.3.6",
"core-js": "^2.4.1", "core-js": "^2.4.1",
"del": "^2.2.2", "del": "^2.2.2",
"gulp": "~3.9.1", "gulp": "~3.9.1",
@ -39,7 +41,6 @@
"systemjs-builder": "^0.15.34", "systemjs-builder": "^0.15.34",
"tether": "^1.4.0", "tether": "^1.4.0",
"typescript": "^2.2.1", "typescript": "^2.2.1",
"zone.js": "^0.8.5", "zone.js": "^0.8.5"
"bootstrap": "3.3.6"
} }
} }

@ -8,6 +8,8 @@ import { AppComponent } from './app.component';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
import { HttpModule } from '@angular/http'; import { HttpModule } from '@angular/http';
import { InfiniteScrollModule } from 'angular2-infinite-scroll/angular2-infinite-scroll'
import { SearchComponent } from './search/search.component'; import { SearchComponent } from './search/search.component';
import { RequestComponent } from './requests/request.component'; import { RequestComponent } from './requests/request.component';
import { PageNotFoundComponent } from './errors/not-found.component'; import { PageNotFoundComponent } from './errors/not-found.component';
@ -41,7 +43,8 @@ const routes: Routes = [
FormsModule, FormsModule,
SettingsModule, SettingsModule,
DataTableModule, DataTableModule,
SharedModule SharedModule,
InfiniteScrollModule
], ],
declarations: [ declarations: [
AppComponent, AppComponent,

@ -25,3 +25,8 @@ export enum RequestType {
movie = 1, movie = 1,
tvShow = 2 tvShow = 2
} }
export interface IRequestsPageScroll {
count: number,
position:number
}

@ -1,46 +1,182 @@
<h1 id="searchTitle">Requests</h1> <h1 id="searchTitle">Requests</h1>
<div class="form-group">
<div>
<input type="text" class="form-control form-control-custom" placeholder="Search" (keyup)="search($event)">
</div>
</div>
<br/>
<div infinite-scroll
[infiniteScrollDistance]="1"
[infiniteScrollThrottle]="100"
(scrolled)="loadMore()">
<div *ngFor="let request of requests">
<div class="row">
<div class="col-sm-2">
<img *ngIf="request.type == 1" class="img-responsive" src="https://image.tmdb.org/t/p/w150/{{request.posterPath}}" alt="poster">
<img *ngIf="request.type == 2" class="img-responsive" src="https://image.tmdb.org/t/p/w150/{{request.posterPath}}" alt="poster">
</div>
<div class="col-sm-5 ">
<div>
<a href="http://www.imdb.com/title/{{request.imdb}}/" target="_blank">
<h4 class="request-title">{{request.title}} ({{request.releaseDate | date: yyyy}})</h4>
</a>
</div>
<br />
<div>
<span>Status: </span>
<span class="label label-success">{{request.status}}</span>
</div>
<div>
<span>Request status: </span>
<span *ngIf="request.available" class="label label-success">Request Available</span>
<span *ngIf="request.approved" class="label label-info">Processing Request</span>
<span *ngIf="request.denied" class="label label-danger">Request Denied</span>
<span *ngIf="request.deniedReason" title="{{request.deniedReason}}"><i class="fa fa-info-circle"></i></span>
<span *ngIf="!request.approved && !request.availble" class="label label-warning">Pending Approval</span>
</div>
<div *ngIf="request.denied">
Denied: <i style="color:red;" class="fa fa-check"></i>
</div>
<div>Release Date: {{request.releaseDate}}</div>
<!--{{#if_eq type "tv"}}
{{#if episodes}}
Episodes: <span class="customTooltip" data-tooltip-content="#{{requestId}}toolTipContent"><i class="fa fa-info-circle"></i></span>
{{else}}
<div>@UI.Requests_SeasonsRequested: {{seriesRequested}}</div>
{{/if}}
{{/if_eq}}-->
<div *ngIf="request.requestedUsers">Requested By: {{request.requestedUsers}}</div>
<div>Requested Date: {{request.requestedDate}}</div>
<!--{{#if admin}}
{{#if currentRootPath}}
<div class="{{requestId}}rootPathMain">Root Path: <span id="{{requestId}}currentRootPath">{{currentRootPath}}</span></div>
{{/if}}
{{/if}}
<div>
{{#if_eq issueId 0}}
@*Nothing*@
{{else}}
@UI.Issues_Issue: <a href="@formAction/issues/{{issueId}}"><i class="fa fa-check"></i></a>
{{/if_eq}}
</div>-->
</div>
<div class="col-sm-3 col-sm-push-3">
<div *ngIf="!request.admin">
<div *ngIf="!request.approved">
<input name="requestId" type="text" value="{{request.requestId}}" hidden="hidden" />
<div *ngIf="request.hasQualities" class="btn-group btn-split">
<button type="button" (click)="approve(request)" class="btn btn-sm btn-success-outline approve"><i class="fa fa-plus"></i> Approve</button>
<button type="button" class="btn btn-success-outline dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="caret"></span>
<span class="sr-only">Toggle Dropdown</span>
</button>
<!--<ul class="dropdown-menu">
{{#each qualities}}
<li><a href="#" class="approve-with-quality" id="{{id}}">{{name}}</a></li>
{{/each}}
</ul>-->
</div>
<input #gb type="text" pInputText size="50" placeholder="Search">
<p-dataTable [value]="requests" expandableRows="true" [rows]="15" [paginator]="true" [pageLinks]="3" [rowsPerPageOptions]="[5,10,20,50]" [globalFilter]="gb"> <button *ngIf="!request.hasQualities" (click)="approve(request)" style="text-align: right" class="btn btn-sm btn-success-outline approve" type="submit"><i class="fa fa-plus"></i> Approve</button>
<p-column expander="true" styleClass="col-icon"></p-column>
<p-column field="title" header="Title" [sortable]="true"></p-column>
<p-column field="requestedDate" header="Requested Date" [sortable]="true"></p-column>
<p-column field="approved" header="Approved" [sortable]="true">
<ng-template let-col let-request="rowData" pTemplate="body"> <!--<form method="POST" action="@formAction/requests/changeRootFolder{{#if_eq type "tv"}}tv{{else}}movie{{/if_eq}}" id="changeFolder{{requestId}}">
<span *ngIf="requests[col]" class="fa fa-check"></span> <input name="requestId" type="text" value="{{requestId}}" hidden="hidden" />
<span *ngIf="!requests[col]" class="fa fa-times"></span> {{#if_eq hasRootFolders true}}
</ng-template> <div class="btn-group btn-split">
</p-column> <button type="button" class="btn btn-sm btn-success-outline" id="changeRootFolderBtn{{requestId}}" custom-button="{{requestId}}">@*<i class="fa fa-plus"></i>*@ Change Root Folder</button>
<ng-template let-request pTemplate="rowexpansion"> <button type="button" class="btn btn-success-outline dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<div class="ui-grid ui-grid-responsive ui-fluid" style="font-size:16px;padding:20px"> <span class="caret"></span>
<div class="ui-grid-row"> <span class="sr-only">@UI.Requests_ToggleDropdown</span>
<div class="ui-grid-col-3" style="text-align:center"> </button>
<i class="fa fa-search" (click)="showCar(car)" style="cursor:pointer;float:left;margin-top:40px"></i> <ul class="dropdown-menu">
<img src="https://image.tmdb.org/t/p/w150/{{request.posterPath}}"> {{#each rootFolders}}
<img *ngIf="request.type === 2" src="{{request.posterPath}}"> <li><a href="#" class="change-root-folder" id="{{id}}" requestId="{{requestId}}">{{path}}</a></li>
</div> {{/each}}
<div class="ui-grid-col-9"> </ul>
<div class="ui-grid ui-grid-responsive ui-grid-pad"> </div>
<div class="ui-grid-row"> {{/if_eq}}
<div class="ui-grid-col-2 label">Type: </div> </form>-->
<div class="ui-grid-col-10">{{request.type}}</div>
</div>
<div class="ui-grid-row">
<div class="ui-grid-col-2 label">Status: </div> <div *ngIf="!request.denied">
<div class="ui-grid-col-10">{{request.status}}</div> <input name="requestId" type="text" value="{{request.requestId}}" hidden="hidden" />
</div> <input name="reason" type="text" hidden="hidden" />
<div class="ui-grid-row"> <div class="btn-group btn-split">
<div class="ui-grid-col-2 label">Approved: </div> <button type="button" (click)="deny(request)" class="btn btn-sm btn-danger-outline deny"><i class="fa fa-times"></i> Deny</button>
<div class="ui-grid-col-10">{{request.approved}}</div> <button type="button" class="btn btn-danger-outline dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
</div> <span class="caret"></span>
<div class="ui-grid-row"> <span class="sr-only">@UI.Requests_ToggleDropdown</span>
<div class="ui-grid-col-2 label">Available: </div> </button>
<div class="ui-grid-col-10">{{request.available}}</div> <ul class="dropdown-menu">
</div> <li><a class="deny-with-reason" id="denyReason{{request.requestId}}" href="#" data-toggle="modal" data-target="#denyReasonModal">Deny with a reason</a></li>
</div> </ul>
</div> </div>
</div> </div>
</div> </div>
</ng-template>
</p-dataTable> <button (click)="removeRequest(request)" style="text-align: right" class="btn btn-sm btn-danger-outline delete"><i class="fa fa-minus"></i> Remove</button>
<button *ngIf="request.available" (click)="changeAvailability(request, true)" style="text-align: right" value="false" class="btn btn-sm btn-info-outline change"><i class="fa fa-minus"></i> Mark Unavailable</button>
<button *ngIf="!request.available" (click)="changeAvailability(request, false)" style="text-align: right" value="true" class="btn btn-sm btn-success-outline change"><i class="fa fa-plus"></i> Mark Available</button>
</div>
<input name="requestId" type="text" value="{{request.requestId}}" hidden="hidden" />
<div class="dropdown">
<button id="{{request.requestId}}" class="btn btn-sm btn-primary-outline dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
<i class="fa fa-plus"></i> Report Issue
<span class="caret"></span>
</button>
<ul class="dropdown-menu" aria-labelledby="dropdownMenu1">
<li><a id="{{request.requestId}}" issue-select="0" class="dropdownIssue" href="#">@UI.Issues_WrongAudio</a></li>
<li><a id="{{request.requestId}}" issue-select="1" class="dropdownIssue" href="#">@UI.Issues_NoSubs</a></li>
<li><a id="{{request.requestId}}" issue-select="2" class="dropdownIssue" href="#">@UI.Issues_WrongContent</a></li>
<li><a id="{{request.requestId}}" issue-select="3" class="dropdownIssue" href="#">@UI.Issues_Playback</a></li>
<li><a id="{{request.requestId}}" issue-select="4" class="dropdownIssue" href="#" data-toggle="modal" data-target="#myModal">@UI.Issues_Other</a></li>
</ul>
</div>
</div>
</div>
<hr />
</div>
</div>

@ -1,4 +1,10 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { Subject } from 'rxjs/Subject';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/distinctUntilChanged';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/debounceTime'; import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/distinctUntilChanged'; import 'rxjs/add/operator/distinctUntilChanged';
import 'rxjs/add/operator/map'; import 'rxjs/add/operator/map';
@ -14,11 +20,83 @@ import { IRequestModel } from '../interfaces/IRequestModel';
providers: [RequestService] providers: [RequestService]
}) })
export class RequestComponent implements OnInit { export class RequestComponent implements OnInit {
constructor(private requestService: RequestService) { } constructor(private requestService: RequestService) {
this.searchChanged
.debounceTime(600) // Wait Xms afterthe last event before emitting last event
.distinctUntilChanged() // only emit if value is different from previous value
.subscribe(x => {
this.searchText = x as string;
if (this.searchText === "") {
this.resetSearch();
return;
}
this.requestService.searchRequests(this.searchText).subscribe(x => this.requests = x);
});
}
requests: IRequestModel[]; requests: IRequestModel[];
searchChanged: Subject<string> = new Subject<string>();
searchText: string;
private currentlyLoaded: number;
private amountToLoad : number;
ngOnInit() { ngOnInit() {
this.requestService.getAllRequests().subscribe(x => this.requests = x); this.amountToLoad = 5;
this.currentlyLoaded = 5;
this.loadInit();
}
loadMore() {
this.requestService.getRequests(this.amountToLoad, this.currentlyLoaded + 1).subscribe(x => {
this.requests.push.apply(this.requests, x);
this.currentlyLoaded = this.currentlyLoaded + this.amountToLoad;
});
}
search(text: any) {
this.searchChanged.next(text.target.value);
}
removeRequest(request: IRequestModel) {
this.requestService.removeRequest(request).subscribe();
this.removeRequestFromUi(request);
}
changeAvailability(request: IRequestModel, available: boolean) {
request.available = available;
this.updateRequest(request);
}
approve(request: IRequestModel) {
request.approved = true;
this.updateRequest(request);
}
deny(request: IRequestModel) {
request.approved = false;
request.denied = true;
this.updateRequest(request);
}
private updateRequest(request: IRequestModel) {
this.requestService.updateRequest(request).subscribe(x => request = x);
}
private loadInit() {
this.requestService.getRequests(this.amountToLoad, 0).subscribe(x => this.requests = x);
}
private resetSearch() {
this.currentlyLoaded = 5;
this.loadInit();
}
private removeRequestFromUi(key : IRequestModel) {
var index = this.requests.indexOf(key, 0);
if (index > -1) {
this.requests.splice(index, 1);
}
} }
} }

@ -8,16 +8,32 @@ import { ISearchMovieResult } from '../interfaces/ISearchMovieResult';
import { IRequestModel } from '../interfaces/IRequestModel'; import { IRequestModel } from '../interfaces/IRequestModel';
@Injectable() @Injectable()
export class RequestService { export class RequestService extends ServiceHelpers {
constructor(private http: Http) { constructor(http: Http) {
super(http, '/api/v1/Request/');
} }
requestMovie(movie: ISearchMovieResult): Observable<IRequestEngineResult> { requestMovie(movie: ISearchMovieResult): Observable<IRequestEngineResult> {
return this.http.post('/api/Request/Movie/', JSON.stringify(movie), ServiceHelpers.RequestOptions).map(ServiceHelpers.extractData); return this.http.post(`${this.url}/Movie/`, JSON.stringify(movie), { headers: this.headers }).map(this.extractData);
} }
getAllRequests(): Observable<IRequestModel[]> { getAllRequests(): Observable<IRequestModel[]> {
return this.http.get('/api/request').map(ServiceHelpers.extractData); return this.http.get(this.url).map(this.extractData);
} }
getRequests(count: number, position: number): Observable<IRequestModel[]> {
return this.http.get(`${this.url}/${count}/${position}`).map(this.extractData);
}
searchRequests(search: string): Observable<IRequestModel[]> {
return this.http.get(`${this.url}/search/${search}`).map(this.extractData);
}
removeRequest(request: IRequestModel): Observable<void> {
return this.http.delete(`${this.url}/${request.id}`).map(this.extractData);
}
updateRequest(request: IRequestModel) : Observable<IRequestModel> {
return this.http.post(`${this.url}/`, JSON.stringify(request), { headers: this.headers }).map(this.extractData);
}
} }

@ -6,24 +6,25 @@ import { ServiceHelpers } from './service.helpers';
import { ISearchMovieResult } from '../interfaces/ISearchMovieResult'; import { ISearchMovieResult } from '../interfaces/ISearchMovieResult';
@Injectable() @Injectable()
export class SearchService { export class SearchService extends ServiceHelpers {
constructor(private http: Http) { constructor(http: Http) {
super(http, "/api/v1/search");
} }
searchMovie(searchTerm: string): Observable<ISearchMovieResult[]> { searchMovie(searchTerm: string): Observable<ISearchMovieResult[]> {
return this.http.get('/api/Search/Movie/' + searchTerm).map(ServiceHelpers.extractData); return this.http.get(`${this.url}/Movie/` + searchTerm).map(this.extractData);
} }
popularMovies(): Observable<ISearchMovieResult[]> { popularMovies(): Observable<ISearchMovieResult[]> {
return this.http.get('/api/Search/Movie/Popular').map(ServiceHelpers.extractData); return this.http.get(`${this.url}/Movie/Popular`).map(this.extractData);
} }
upcomignMovies(): Observable<ISearchMovieResult[]> { upcomignMovies(): Observable<ISearchMovieResult[]> {
return this.http.get('/api/Search/Movie/upcoming').map(ServiceHelpers.extractData); return this.http.get(`${this.url}/Movie/upcoming`).map(this.extractData);
} }
nowPlayingMovies(): Observable<ISearchMovieResult[]> { nowPlayingMovies(): Observable<ISearchMovieResult[]> {
return this.http.get('/api/Search/Movie/nowplaying').map(ServiceHelpers.extractData); return this.http.get(`${this.url}/Movie/nowplaying`).map(this.extractData);
} }
topRatedMovies(): Observable<ISearchMovieResult[]> { topRatedMovies(): Observable<ISearchMovieResult[]> {
return this.http.get('/api/Search/Movie/toprated').map(ServiceHelpers.extractData); return this.http.get(`${this.url}/Movie/toprated`).map(this.extractData);
} }
} }

@ -1,15 +1,29 @@
import { Headers, RequestOptions, Response } from '@angular/http'; import { Headers, Response, Http } from '@angular/http';
import { Observable } from 'rxjs/Observable';
export class ServiceHelpers { export class ServiceHelpers {
public static Headers = new Headers({ 'Content-Type': 'application/json' });
public static RequestOptions = new RequestOptions({ constructor(protected http: Http, protected url: string) {
headers: ServiceHelpers.Headers this.headers = new Headers();
}); this.headers.append('Content-Type', 'application/json; charset=utf-8');
}
protected headers: Headers;
public static extractData(res: Response) { protected extractData(res: Response) {
console.log(res); let body = res.json();
return res.json(); //console.log('extractData', body || {});
return body || {};
} }
protected handleError(error: any) {
// In a real world app, we might use a remote logging infrastructure
// We'd also dig deeper into the error to get a better message
let errMsg = (error.message) ? error.message :
error.status ? `${error.status} - ${error.statusText}` : 'Server error';
console.error(errMsg); // log to console instead
return Observable.throw(errMsg);
}
} }
Loading…
Cancel
Save