
* Implemented ability to see downloads users are performing on the events widget. * Fixed a bug where version update task was calling wrong code * Fixed a bug where when checking for updates, the event wouldn't be pushed to server with correct name. Added update check to the event widget rather than opening a modal on the user. * Relaxed password requirements to only be 6-32 characters and inform user on register form about the requirements * Removed a ton of duplicate logic for series cards where the logic was already defined in action service * Fixed OPDS total items giving a rounded number rather than total items. * Fixed off by one issue on OPDS pagination
177 lines
6.1 KiB
TypeScript
177 lines
6.1 KiB
TypeScript
import { Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output } from '@angular/core';
|
|
import { Router } from '@angular/router';
|
|
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
|
import { ToastrService } from 'ngx-toastr';
|
|
import { take, takeUntil, takeWhile } from 'rxjs/operators';
|
|
import { Series } from 'src/app/_models/series';
|
|
import { AccountService } from 'src/app/_services/account.service';
|
|
import { ImageService } from 'src/app/_services/image.service';
|
|
import { ActionFactoryService, Action, ActionItem } from 'src/app/_services/action-factory.service';
|
|
import { SeriesService } from 'src/app/_services/series.service';
|
|
import { ConfirmService } from 'src/app/shared/confirm.service';
|
|
import { ActionService } from 'src/app/_services/action.service';
|
|
import { EditSeriesModalComponent } from '../_modals/edit-series-modal/edit-series-modal.component';
|
|
import { RefreshMetadataEvent } from 'src/app/_models/events/refresh-metadata-event';
|
|
import { MessageHubService } from 'src/app/_services/message-hub.service';
|
|
import { Subject } from 'rxjs';
|
|
|
|
@Component({
|
|
selector: 'app-series-card',
|
|
templateUrl: './series-card.component.html',
|
|
styleUrls: ['./series-card.component.scss']
|
|
})
|
|
export class SeriesCardComponent implements OnInit, OnChanges, OnDestroy {
|
|
@Input() data!: Series;
|
|
@Input() libraryId = 0;
|
|
@Input() suppressLibraryLink = false;
|
|
/**
|
|
* If the entity is selected or not.
|
|
*/
|
|
@Input() selected: boolean = false;
|
|
/**
|
|
* If the entity should show selection code
|
|
*/
|
|
@Input() allowSelection: boolean = false;
|
|
|
|
@Output() clicked = new EventEmitter<Series>();
|
|
@Output() reload = new EventEmitter<boolean>();
|
|
@Output() dataChanged = new EventEmitter<Series>();
|
|
/**
|
|
* When the card is selected.
|
|
*/
|
|
@Output() selection = new EventEmitter<boolean>();
|
|
|
|
isAdmin = false;
|
|
actions: ActionItem<Series>[] = [];
|
|
imageUrl: string = '';
|
|
onDestroy: Subject<void> = new Subject<void>();
|
|
|
|
constructor(private accountService: AccountService, private router: Router,
|
|
private seriesService: SeriesService, private toastr: ToastrService,
|
|
private modalService: NgbModal, private confirmService: ConfirmService,
|
|
public imageService: ImageService, private actionFactoryService: ActionFactoryService,
|
|
private actionService: ActionService, private hubService: MessageHubService) {
|
|
this.accountService.currentUser$.pipe(take(1)).subscribe(user => {
|
|
if (user) {
|
|
this.isAdmin = this.accountService.hasAdminRole(user);
|
|
}
|
|
});
|
|
}
|
|
|
|
|
|
ngOnInit(): void {
|
|
if (this.data) {
|
|
this.imageUrl = this.imageService.randomize(this.imageService.getSeriesCoverImage(this.data.id));
|
|
|
|
this.hubService.refreshMetadata.pipe(takeWhile(event => event.libraryId === this.libraryId), takeUntil(this.onDestroy)).subscribe((event: RefreshMetadataEvent) => {
|
|
if (this.data.id === event.seriesId) {
|
|
this.imageUrl = this.imageService.randomize(this.imageService.getSeriesCoverImage(this.data.id));
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
ngOnChanges(changes: any) {
|
|
if (this.data) {
|
|
this.actions = this.actionFactoryService.getSeriesActions((action: Action, series: Series) => this.handleSeriesActionCallback(action, series)).filter(action => this.actionFactoryService.filterBookmarksForFormat(action, this.data));
|
|
this.imageUrl = this.imageService.randomize(this.imageService.getSeriesCoverImage(this.data.id));
|
|
}
|
|
}
|
|
|
|
ngOnDestroy() {
|
|
this.onDestroy.next();
|
|
this.onDestroy.complete();
|
|
}
|
|
|
|
handleSeriesActionCallback(action: Action, series: Series) {
|
|
switch (action) {
|
|
case(Action.MarkAsRead):
|
|
this.markAsRead(series);
|
|
break;
|
|
case(Action.MarkAsUnread):
|
|
this.markAsUnread(series);
|
|
break;
|
|
case(Action.ScanLibrary):
|
|
this.scanLibrary(series);
|
|
break;
|
|
case(Action.RefreshMetadata):
|
|
this.refreshMetdata(series);
|
|
break;
|
|
case(Action.Delete):
|
|
this.deleteSeries(series);
|
|
break;
|
|
case(Action.Edit):
|
|
this.openEditModal(series);
|
|
break;
|
|
case(Action.Bookmarks):
|
|
this.actionService.openBookmarkModal(series, (series) => {/* No Operation */ });
|
|
break;
|
|
case(Action.AddToReadingList):
|
|
this.actionService.addSeriesToReadingList(series, (series) => {/* No Operation */ });
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
openEditModal(data: Series) {
|
|
const modalRef = this.modalService.open(EditSeriesModalComponent, { size: 'lg' });
|
|
modalRef.componentInstance.series = data;
|
|
modalRef.closed.subscribe((closeResult: {success: boolean, series: Series, coverImageUpdate: boolean}) => {
|
|
window.scrollTo(0, 0);
|
|
if (closeResult.success) {
|
|
if (closeResult.coverImageUpdate) {
|
|
this.imageUrl = this.imageService.randomize(this.imageService.getSeriesCoverImage(closeResult.series.id));
|
|
}
|
|
this.seriesService.getSeries(data.id).subscribe(series => {
|
|
this.data = series;
|
|
this.reload.emit(true);
|
|
this.dataChanged.emit(series);
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
async refreshMetdata(series: Series) {
|
|
this.actionService.refreshMetdata(series);
|
|
}
|
|
|
|
async scanLibrary(series: Series) {
|
|
this.seriesService.scan(series.libraryId, series.id).subscribe((res: any) => {
|
|
this.toastr.success('Scan started for ' + series.name);
|
|
});
|
|
}
|
|
|
|
async deleteSeries(series: Series) {
|
|
this.actionService.deleteSeries(series, (result: boolean) => {
|
|
if (result) {
|
|
this.reload.emit(true);
|
|
}
|
|
});
|
|
}
|
|
|
|
markAsUnread(series: Series) {
|
|
this.actionService.markSeriesAsUnread(series, () => {
|
|
if (this.data) {
|
|
this.data.pagesRead = 0;
|
|
}
|
|
|
|
this.dataChanged.emit(series);
|
|
});
|
|
}
|
|
|
|
markAsRead(series: Series) {
|
|
this.actionService.markSeriesAsRead(series, () => {
|
|
if (this.data) {
|
|
this.data.pagesRead = series.pages;
|
|
}
|
|
this.dataChanged.emit(series);
|
|
});
|
|
}
|
|
|
|
handleClick() {
|
|
this.clicked.emit(this.data);
|
|
this.router.navigate(['library', this.libraryId, 'series', this.data?.id]);
|
|
}
|
|
|
|
}
|