mirror of https://github.com/Ombi-app/Ombi
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
286 lines
12 KiB
286 lines
12 KiB
import { AfterViewInit, ChangeDetectorRef, Component, EventEmitter, OnInit, Output, ViewChild } from "@angular/core";
|
|
import { IMovieRequests, IRequestEngineResult, IRequestsViewModel } from "../../../interfaces";
|
|
import { NotificationService, RequestService } from "../../../services";
|
|
import { Observable, combineLatest, forkJoin, merge, of as observableOf } from 'rxjs';
|
|
import { catchError, map, startWith, switchMap } from 'rxjs/operators';
|
|
|
|
import { AuthService } from "../../../auth/auth.service";
|
|
import { FeaturesFacade } from "../../../state/features/features.facade";
|
|
import { MatPaginator } from "@angular/material/paginator";
|
|
import { MatSort } from "@angular/material/sort";
|
|
import { MatTableDataSource } from "@angular/material/table";
|
|
import { RequestFilterType } from "../../models/RequestFilterType";
|
|
import { RequestServiceV2 } from "../../../services/requestV2.service";
|
|
import { SelectionModel } from "@angular/cdk/collections";
|
|
import { StorageService } from "../../../shared/storage/storage-service";
|
|
import { TranslateService } from "@ngx-translate/core";
|
|
|
|
@Component({
|
|
templateUrl: "./movies-grid.component.html",
|
|
selector: "movies-grid",
|
|
styleUrls: ["./movies-grid.component.scss"]
|
|
})
|
|
export class MoviesGridComponent implements OnInit, AfterViewInit {
|
|
public dataSource: MatTableDataSource<IMovieRequests>;
|
|
public resultsLength: number;
|
|
public isLoadingResults = true;
|
|
public displayedColumns: string[];
|
|
public gridCount: string = "15";
|
|
public isAdmin: boolean;
|
|
public is4kEnabled = false;
|
|
public isPlayedSyncEnabled = false;
|
|
public manageOwnRequests: boolean;
|
|
public defaultSort: string = "requestedDate";
|
|
public defaultOrder: string = "desc";
|
|
public currentFilter: RequestFilterType = RequestFilterType.All;
|
|
public selection = new SelectionModel<IMovieRequests>(true, []);
|
|
public userName: string;
|
|
|
|
public RequestFilter = RequestFilterType;
|
|
|
|
|
|
private storageKey = "Movie_DefaultRequestListSort";
|
|
private storageKeyOrder = "Movie_DefaultRequestListSortOrder";
|
|
private storageKeyGridCount = "Movie_DefaultGridCount";
|
|
private storageKeyCurrentFilter = "Movie_DefaultFilter";
|
|
|
|
@Output() public onOpenOptions = new EventEmitter<{ request: any, filter: any, onChange: any, manageOwnRequests: boolean, isAdmin: boolean, has4kRequest: boolean, hasRegularRequest: boolean }>();
|
|
|
|
@ViewChild(MatPaginator) paginator: MatPaginator;
|
|
@ViewChild(MatSort) sort: MatSort;
|
|
|
|
constructor(private requestService: RequestServiceV2, private ref: ChangeDetectorRef,
|
|
private auth: AuthService, private storageService: StorageService,
|
|
private requestServiceV1: RequestService, private notification: NotificationService,
|
|
private translateService: TranslateService,
|
|
private featureFacade: FeaturesFacade) {
|
|
|
|
this.userName = auth.claims().name;
|
|
}
|
|
|
|
public ngOnInit() {
|
|
this.isAdmin = this.auth.hasRole("admin") || this.auth.hasRole("poweruser");
|
|
this.manageOwnRequests = this.auth.hasRole("ManageOwnRequests")
|
|
|
|
this.is4kEnabled = this.featureFacade.is4kEnabled();
|
|
this.isPlayedSyncEnabled = this.featureFacade.isPlayedSyncEnabled();
|
|
|
|
|
|
const defaultCount = this.storageService.get(this.storageKeyGridCount);
|
|
const defaultSort = this.storageService.get(this.storageKey);
|
|
const defaultOrder = this.storageService.get(this.storageKeyOrder);
|
|
const defaultFilter = +this.storageService.get(this.storageKeyCurrentFilter);
|
|
if (defaultSort) {
|
|
this.defaultSort = defaultSort;
|
|
}
|
|
if (defaultOrder) {
|
|
this.defaultOrder = defaultOrder;
|
|
}
|
|
if (defaultCount) {
|
|
this.gridCount = defaultCount;
|
|
}
|
|
if (defaultFilter) {
|
|
this.currentFilter = defaultFilter;
|
|
}
|
|
}
|
|
|
|
setDisplayedColumns() {
|
|
this.displayedColumns = ['title', 'requestedUser.requestedBy', 'status', 'requestStatus','requestedDate'];
|
|
|
|
if (this.isAdmin) {
|
|
this.displayedColumns.unshift('select');
|
|
}
|
|
|
|
if ((this.isAdmin || this.auth.hasRole("Request4KMovie"))
|
|
&& this.is4kEnabled) {
|
|
this.displayedColumns.splice(4, 0, 'has4kRequest');
|
|
}
|
|
|
|
if (this.isPlayedSyncEnabled
|
|
&& ( this.currentFilter == RequestFilterType.All || this.currentFilter == RequestFilterType.Available ) ) {
|
|
this.displayedColumns.push('watchedByRequestedUser');
|
|
}
|
|
|
|
// always put the actions column at the end
|
|
this.displayedColumns.push('actions');
|
|
}
|
|
|
|
public async ngAfterViewInit() {
|
|
|
|
this.setDisplayedColumns();
|
|
|
|
this.storageService.save(this.storageKeyGridCount, this.gridCount);
|
|
this.storageService.save(this.storageKeyCurrentFilter, (+this.currentFilter).toString());
|
|
|
|
// If the user changes the sort order, reset back to the first page.
|
|
this.sort.sortChange.subscribe(() => this.paginator.pageIndex = 0);
|
|
this.paginator.showFirstLastButtons = true;
|
|
|
|
merge(this.sort.sortChange, this.paginator.page)
|
|
.pipe(
|
|
startWith({}),
|
|
switchMap((value: any) => {
|
|
this.isLoadingResults = true;
|
|
if (value.active || value.direction) {
|
|
this.storageService.save(this.storageKey, value.active);
|
|
this.storageService.save(this.storageKeyOrder, value.direction);
|
|
}
|
|
return this.loadData();
|
|
}),
|
|
map((data: IRequestsViewModel<IMovieRequests>) => {
|
|
// 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 = new MatTableDataSource(data));
|
|
}
|
|
|
|
public loadData(): Observable<IRequestsViewModel<IMovieRequests>> {
|
|
switch(RequestFilterType[RequestFilterType[this.currentFilter]]) {
|
|
case RequestFilterType.All:
|
|
return this.requestService.getMovieRequests(+this.gridCount, this.paginator.pageIndex * +this.gridCount, this.sort.active, this.sort.direction);
|
|
case RequestFilterType.Pending:
|
|
return this.requestService.getMoviePendingRequests(+this.gridCount, this.paginator.pageIndex * +this.gridCount, this.sort.active, this.sort.direction);
|
|
case RequestFilterType.Available:
|
|
return this.requestService.getMovieAvailableRequests(+this.gridCount, this.paginator.pageIndex * +this.gridCount, this.sort.active, this.sort.direction);
|
|
case RequestFilterType.Processing:
|
|
return this.requestService.getMovieProcessingRequests(+this.gridCount, this.paginator.pageIndex * +this.gridCount, this.sort.active, this.sort.direction);
|
|
case RequestFilterType.Denied:
|
|
return this.requestService.getMovieDeniedRequests(+this.gridCount, this.paginator.pageIndex * +this.gridCount, this.sort.active, this.sort.direction);
|
|
}
|
|
|
|
}
|
|
|
|
public openOptions(request: IMovieRequests) {
|
|
const filter = () => {
|
|
this.dataSource.data = this.dataSource.data.filter((req) => {
|
|
return req.id !== request.id;
|
|
});
|
|
};
|
|
|
|
const onChange = () => {
|
|
this.ref.detectChanges();
|
|
};
|
|
|
|
const data = { request: request, filter: filter, onChange: onChange, manageOwnRequests: this.manageOwnRequests, isAdmin: this.isAdmin, has4kRequest: request.has4KRequest, hasRegularRequest: this.checkDate(request.requestedDate) };
|
|
this.onOpenOptions.emit(data);
|
|
}
|
|
|
|
private checkDate(date: Date|string): boolean {
|
|
if (typeof date === 'string') {
|
|
return new Date(date).getFullYear() > 1;
|
|
}
|
|
if (date instanceof Date) {
|
|
return date.getFullYear() > 1;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public switchFilter(type: RequestFilterType) {
|
|
this.currentFilter = type;
|
|
this.ngAfterViewInit();
|
|
}
|
|
|
|
public isAllSelected() {
|
|
const numSelected = this.selection.selected.length;
|
|
const numRows = this.dataSource.data.length;
|
|
return numSelected === numRows;
|
|
}
|
|
|
|
public masterToggle() {
|
|
this.isAllSelected() ?
|
|
this.selection.clear() :
|
|
this.dataSource.data.forEach(row => this.selection.select(row));
|
|
}
|
|
|
|
public async bulkDelete() {
|
|
if (this.selection.isEmpty()) {
|
|
return;
|
|
}
|
|
let tasks = new Array<Observable<IRequestEngineResult>>();
|
|
this.selection.selected.forEach((selected) => {
|
|
tasks.push(this.requestServiceV1.removeMovieRequestAsync(selected.id));
|
|
});
|
|
|
|
combineLatest(tasks).subscribe(() => {
|
|
this.notification.success(this.translateService.instant('Requests.RequestPanel.Deleted'))
|
|
this.selection.clear();
|
|
this.ngAfterViewInit();
|
|
});
|
|
}
|
|
|
|
public bulkApprove = () => this.bulkApproveInternal(false);
|
|
|
|
public bulkApprove4K = () => this.bulkApproveInternal(true);
|
|
|
|
private bulkApproveInternal(is4k: boolean) {
|
|
if (this.selection.isEmpty()) {
|
|
return;
|
|
}
|
|
let tasks = new Array<Observable<IRequestEngineResult>>();
|
|
this.selection.selected.forEach((selected) => {
|
|
tasks.push(this.requestServiceV1.approveMovie({ id: selected.id, is4K: is4k }));
|
|
});
|
|
|
|
this.isLoadingResults = true;
|
|
forkJoin(tasks).subscribe((result: IRequestEngineResult[]) => {
|
|
this.isLoadingResults = false;
|
|
const failed = result.filter(x => !x.result);
|
|
if(failed.length > 0) {
|
|
this.notification.error("Some requests failed to approve: " + failed[0].errorMessage);
|
|
this.selection.clear();
|
|
return;
|
|
}
|
|
this.notification.success(this.translateService.instant('Requests.RequestPanel.Approved'));
|
|
this.selection.clear();
|
|
this.ngAfterViewInit();
|
|
})
|
|
}
|
|
|
|
public bulkDeny = () => this.bulkDenyInternal(false);
|
|
|
|
public bulkDeny4K = () => this.bulkDenyInternal(true);
|
|
|
|
private bulkDenyInternal(is4k: boolean) {
|
|
if (this.selection.isEmpty()) {
|
|
return;
|
|
}
|
|
let tasks = new Array<Observable<IRequestEngineResult>>();
|
|
this.selection.selected.forEach((selected) => {
|
|
|
|
tasks.push(this.requestServiceV1.denyMovie({
|
|
id: selected.id,
|
|
is4K: is4k,
|
|
reason: `` // TOOD: reuse DenyDialog to allow for a reason to be entered
|
|
}));
|
|
});
|
|
|
|
this.isLoadingResults = true;
|
|
forkJoin(tasks).subscribe((result: IRequestEngineResult[]) => {
|
|
this.isLoadingResults = false;
|
|
const failed = result.filter(x => !x.result);
|
|
if (failed.length > 0) {
|
|
this.notification.error("Some requests failed to deny: " + failed[0].errorMessage);
|
|
this.selection.clear();
|
|
return;
|
|
}
|
|
this.notification.success(this.translateService.instant('Requests.RequestPanel.Denied'));
|
|
this.selection.clear();
|
|
this.ngAfterViewInit();
|
|
})
|
|
}
|
|
|
|
public getRequestDate(request: IMovieRequests): Date {
|
|
if (new Date(request.requestedDate).getFullYear() === 1) {
|
|
return request.requestedDate4k;
|
|
}
|
|
return request.requestedDate;
|
|
}
|
|
}
|