Kavita/UI/Web/src/app/events-widget/events-widget.component.ts
Joseph Milazzo 242d8b106d
Bugfixes (#1177)
* Fixed an underline on hover of pagination link

* Ensure title of companion bar eats full width if there is no filter

* If a user doesn't have the Download role, they will not be able to download over OPDS.

* Fixed a bug where after going into webtoon reader mode then leaving, the bookmark effect would continue using the webtoon mode styling

* Fixed a bug where continuous reader wasn't being triggered due to moving scrollbar to body and a floating point percision error on scroll top

* Fixed how continuous trigger is shown so that we properly adjust scroll on the top (for prev chapter)

* Fixed a bad merge that broke saving any edits to series metadata

* When a epub key is not correct, even after we correct it, ignore the inlining of the style so the book is at least still readable.

* Disabled double rendering (this feature is being postponed to a later release)

* Disabled user setting and forced it to Single on any save

* Removed cache directory from UpdateSettings validation as we don't allow changing it.

* Fix security issue with url parse

* After all migrations run, update the installed version in the Database. Send that installed version on the stat service.

* Dependency bot to update some security stuff

* Some misc code cleanup and fixes on the typeahead (still broken)
2022-03-25 16:38:13 -07:00

163 lines
5.6 KiB
TypeScript

import { Component, Input, OnDestroy, OnInit } from '@angular/core';
import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
import { BehaviorSubject, Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { UpdateNotificationModalComponent } from '../shared/update-notification/update-notification-modal.component';
import { NotificationProgressEvent } from '../_models/events/notification-progress-event';
import { UpdateVersionEvent } from '../_models/events/update-version-event';
import { User } from '../_models/user';
import { AccountService } from '../_services/account.service';
import { EVENTS, Message, MessageHubService } from '../_services/message-hub.service';
import { ErrorEvent } from '../_models/events/error-event';
import { ConfirmService } from '../shared/confirm.service';
import { ConfirmConfig } from '../shared/confirm-dialog/_models/confirm-config';
import { ServerService } from '../_services/server.service';
@Component({
selector: 'app-nav-events-toggle',
templateUrl: './events-widget.component.html',
styleUrls: ['./events-widget.component.scss']
})
export class EventsWidgetComponent implements OnInit, OnDestroy {
@Input() user!: User;
isAdmin: boolean = false;
private readonly onDestroy = new Subject<void>();
/**
* Progress events (Event Type: 'started', 'ended', 'updated' that have progress property)
*/
progressEventsSource = new BehaviorSubject<NotificationProgressEvent[]>([]);
progressEvents$ = this.progressEventsSource.asObservable();
singleUpdateSource = new BehaviorSubject<NotificationProgressEvent[]>([]);
singleUpdates$ = this.singleUpdateSource.asObservable();
errorSource = new BehaviorSubject<ErrorEvent[]>([]);
errors$ = this.errorSource.asObservable();
private updateNotificationModalRef: NgbModalRef | null = null;
activeEvents: number = 0;
debugMode: boolean = false;
get EVENTS() {
return EVENTS;
}
constructor(public messageHub: MessageHubService, private modalService: NgbModal,
private accountService: AccountService, private confirmService: ConfirmService) { }
ngOnDestroy(): void {
this.onDestroy.next();
this.onDestroy.complete();
this.progressEventsSource.complete();
this.singleUpdateSource.complete();
this.errorSource.complete();
}
ngOnInit(): void {
// Debounce for testing. Kavita's too fast
this.messageHub.messages$.pipe(takeUntil(this.onDestroy)).subscribe(event => {
if (event.event === EVENTS.NotificationProgress) {
this.processNotificationProgressEvent(event);
} else if (event.event === EVENTS.Error) {
const values = this.errorSource.getValue();
values.push(event.payload as ErrorEvent);
this.errorSource.next(values);
this.activeEvents += 1;
}
});
this.accountService.currentUser$.pipe(takeUntil(this.onDestroy)).subscribe(user => {
if (user) {
this.isAdmin = this.accountService.hasAdminRole(user);
} else {
this.isAdmin = false;
}
});
}
processNotificationProgressEvent(event: Message<NotificationProgressEvent>) {
const message = event.payload as NotificationProgressEvent;
let data;
switch (event.payload.eventType) {
case 'single':
const values = this.singleUpdateSource.getValue();
values.push(message);
this.singleUpdateSource.next(values);
this.activeEvents += 1;
break;
case 'started':
data = this.progressEventsSource.getValue();
data.push(message);
this.progressEventsSource.next(data);
this.activeEvents += 1;
break;
case 'updated':
data = this.progressEventsSource.getValue();
const index = data.findIndex(m => m.name === message.name);
if (index < 0) {
data.push(message);
this.activeEvents += 1;
} else {
data[index] = message;
}
this.progressEventsSource.next(data);
break;
case 'ended':
data = this.progressEventsSource.getValue();
data = data.filter(m => m.name !== message.name);
this.progressEventsSource.next(data);
this.activeEvents = Math.max(this.activeEvents - 1, 0);
break;
default:
break;
}
}
handleUpdateAvailableClick(message: NotificationProgressEvent) {
if (this.updateNotificationModalRef != null) { return; }
this.updateNotificationModalRef = this.modalService.open(UpdateNotificationModalComponent, { scrollable: true, size: 'lg' });
this.updateNotificationModalRef.componentInstance.updateData = message.body as UpdateVersionEvent;
this.updateNotificationModalRef.closed.subscribe(() => {
this.updateNotificationModalRef = null;
});
this.updateNotificationModalRef.dismissed.subscribe(() => {
this.updateNotificationModalRef = null;
});
}
async seeMoreError(error: ErrorEvent) {
const config = new ConfirmConfig();
config.buttons = [
{text: 'Dismiss', type: 'primary'},
{text: 'Ok', type: 'secondary'},
];
config.header = error.title;
config.content = error.subTitle;
var result = await this.confirmService.alert(error.subTitle || error.title, config);
if (result) {
this.removeError(error);
}
}
removeError(error: ErrorEvent, event?: MouseEvent) {
if (event) {
event.stopPropagation();
event.preventDefault();
}
let data = this.errorSource.getValue();
data = data.filter(m => m !== error);
this.errorSource.next(data);
this.activeEvents = Math.max(this.activeEvents - 1, 0);
}
prettyPrintProgress(progress: number) {
return Math.trunc(progress * 100);
}
}