Holiday Bugfixes (#1762)

* Don't show "not much going on" when we are actively downloading

* Swipe to paginate is now behind a flag in the user preferences.

* Added a new server setting for host name, if the server sits behind a reverse proxy. If this is set, email link generation will use it and will not perform any checks on accessibility (thus email will always send)

* Refactored the code that checks if the server is accessible to check if host name is set, and thus return rue if so.

* Added back the system drawing library for markdown parsing.

* Fixed a validation error

* Fixed a bug where folder watching could get re-triggered when it was disabled at a server level.

* Made the manga reader loader absolute positioned for better visibility

* Indentation
This commit is contained in:
Joe Milazzo 2023-01-30 00:50:19 -08:00 committed by GitHub
parent 2a47029209
commit 5e9bbd0768
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
31 changed files with 1986 additions and 49 deletions

View file

@ -19,6 +19,7 @@ export interface Preferences {
backgroundColor: string;
showScreenHints: boolean;
emulateBook: boolean;
swipeToPaginate: boolean;
// Book Reader
bookReaderMargin: number;

View file

@ -14,4 +14,5 @@ export interface ServerSettings {
totalBackups: number;
totalLogs: number;
enableFolderWatching: boolean;
hostName: string;
}

View file

@ -1,6 +1,6 @@
<div class="container-fluid">
<form [formGroup]="settingsForm" *ngIf="serverSettings !== undefined">
<p class="text-warning pt-2">Port and Swagger require a manual restart of Kavita to take effect.</p>
<p class="text-warning pt-2">Changing Port requires a manual restart of Kavita to take effect.</p>
<div class="mb-3">
<label for="settings-cachedir" class="form-label">Cache Directory</label>&nbsp;<i class="fa fa-info-circle" placement="right" [ngbTooltip]="cacheDirectoryTooltip" role="button" tabindex="0"></i>
<ng-template #cacheDirectoryTooltip>Where the server place temporary files when reading. This will be cleaned up on a regular basis.</ng-template>
@ -20,6 +20,19 @@
</div>
</div>
<div class="mb-3">
<label for="settings-hostname" class="form-label">Host Name</label>&nbsp;<i class="fa fa-info-circle" placement="right" [ngbTooltip]="hostNameTooltip" role="button" tabindex="0"></i>
<ng-template #hostNameTooltip>Domain Name (of Reverse Proxy). If set, email generation will always use this.</ng-template>
<span class="visually-hidden" id="settings-hostname-help">Domain Name (of Reverse Proxy). If set, email generation will always use this.</span>
<input id="settings-hostname" aria-describedby="settings-hostname-help" class="form-control" formControlName="hostName" type="text"
[class.is-invalid]="settingsForm.get('hostName')?.invalid && settingsForm.get('hostName')?.touched">
<div id="hostname-validations" class="invalid-feedback" *ngIf="settingsForm.dirty || settingsForm.touched">
<div *ngIf="settingsForm.get('hostName')?.errors?.pattern">
Host name must start with http(s) and not end in /
</div>
</div>
</div>
<div class="row g-0 mb-2">
<div class="col-md-3 col-sm-12 pe-2">
<label for="settings-port" class="form-label">Port</label>&nbsp;<i class="fa fa-info-circle" placement="right" [ngbTooltip]="portTooltip" role="button" tabindex="0"></i>

View file

@ -51,6 +51,7 @@ export class ManageSettingsComponent implements OnInit {
this.settingsForm.addControl('totalLogs', new FormControl(this.serverSettings.totalLogs, [Validators.required, Validators.min(1), Validators.max(30)]));
this.settingsForm.addControl('enableFolderWatching', new FormControl(this.serverSettings.enableFolderWatching, [Validators.required]));
this.settingsForm.addControl('convertBookmarkToWebP', new FormControl(this.serverSettings.convertBookmarkToWebP, []));
this.settingsForm.addControl('hostName', new FormControl(this.serverSettings.hostName, [Validators.pattern(/^(http:|https:)+[^\s]+[\w]$/)]));
});
}
@ -69,6 +70,7 @@ export class ManageSettingsComponent implements OnInit {
this.settingsForm.get('totalLogs')?.setValue(this.serverSettings.totalLogs);
this.settingsForm.get('enableFolderWatching')?.setValue(this.serverSettings.enableFolderWatching);
this.settingsForm.get('convertBookmarkToWebP')?.setValue(this.serverSettings.convertBookmarkToWebP);
this.settingsForm.get('hostName')?.setValue(this.serverSettings.hostName);
this.settingsForm.markAsPristine();
}

View file

@ -134,14 +134,12 @@ export class ManageUsersComponent implements OnInit, OnDestroy {
}
await this.confirmService.alert(
'Please click this link to confirm your email. You must confirm to be able to login. You may need to log out of the current account before clicking. <br/> <a href="' + email + '" target="_blank" rel="noopener noreferrer">' + email + '</a>');
});
});
}
setup(member: Member) {
this.accountService.getInviteUrl(member.id, false).subscribe(url => {
console.log('Invite Url: ', url);
if (url) {
this.router.navigateByUrl(url);
}

View file

@ -26,7 +26,7 @@
</div>
</div>
</div>
<app-loading [loading]="isLoading"></app-loading>
<app-loading [loading]="isLoading" [absolute]="true"></app-loading>
<div class="reading-area"
ngSwipe (swipeEnd)="onSwipeEnd($event)" (swipeMove)="onSwipeMove($event)"
[ngStyle]="{'background-color': backgroundColor, 'height': readerMode === ReaderMode.Webtoon ? 'inherit' : 'calc(var(--vh)*100)'}" #readingArea>
@ -233,6 +233,15 @@
</div>
</div>
</div>
<div class="mb-3">
<div class="mb-3">
<div class="form-check form-switch">
<input type="checkbox" id="swipe-to-paginate" formControlName="swipeToPaginate" class="form-check-input" >
<label class="form-check-label" for="swipe-to-paginate">Swipe Enabled</label>
</div>
</div>
</div>
</div>
<div class="col-md-3 col-sm-12">
<div class="mb-3">
@ -251,8 +260,10 @@
<input type="range" class="form-range" id="darkness"
min="10" max="100" step="1" formControlName="darkness">
</div>
<div class="col-md-6 col-sm-12">
<button class="btn btn-primary" (click)="savePref()">Save to Preferences</button>
<button class="btn btn-primary" (click)="savePref()">Save Globally</button>
</div>
</div>
</form>

View file

@ -467,7 +467,8 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
fittingOption: new FormControl(this.mangaReaderService.translateScalingOption(this.scalingOption)),
layoutMode: new FormControl(this.layoutMode),
darkness: new FormControl(100),
emulateBook: new FormControl(this.user.preferences.emulateBook)
emulateBook: new FormControl(this.user.preferences.emulateBook),
swipeToPaginate: new FormControl(this.user.preferences.swipeToPaginate)
});
this.readerModeSubject.next(this.readerMode);
@ -973,6 +974,8 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
}
triggerSwipePagination(direction: KeyDirection) {
if (!this.generalSettingsForm.get('swipeToPaginate')?.value) return;
switch(direction) {
case KeyDirection.Down:
this.nextPage();
@ -1097,6 +1100,9 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
}
this.resetSwipeModifiers();
this.isLoading = true;
this.cdRef.markForCheck();
this.pagingDirectionSubject.next(PAGING_DIRECTION.FORWARD);
@ -1125,6 +1131,9 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
this.resetSwipeModifiers();
this.isLoading = true;
this.cdRef.markForCheck();
this.pagingDirectionSubject.next(PAGING_DIRECTION.BACKWARDS);
@ -1241,6 +1250,7 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
// Originally this was only for fit to height, but when swiping was introduced, it made more sense to do it always to reset to the same view
this.readingArea.nativeElement.scroll(0,0);
this.isLoading = false;
this.cdRef.markForCheck();
}
@ -1601,6 +1611,7 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
data.autoCloseMenu = this.autoCloseMenu;
data.readingDirection = this.readingDirection;
data.emulateBook = modelSettings.emulateBook;
data.swipeToPaginate = modelSettings.swipeToPaginate;
this.accountService.updatePreferences(data).subscribe((updatedPrefs) => {
this.toastr.success('User preferences updated');
if (this.user) {

View file

@ -165,9 +165,13 @@
<li class="list-group-item dark-menu-item" *ngIf="onlineUsers.length > 1">
<div>{{onlineUsers.length}} Users online</div>
</li>
<li class="list-group-item dark-menu-item" *ngIf="activeEvents === 0 && onlineUsers.length <= 1">Not much going on here</li>
<li class="list-group-item dark-menu-item" *ngIf="debugMode">Active Events: {{activeEvents}}</li>
</ng-container>
<ng-container *ngIf="downloadService.activeDownloads$ | async as activeDownloads">
<li class="list-group-item dark-menu-item" *ngIf="activeEvents === 0 && activeDownloads.length === 0">Not much going on here</li>
</ng-container>
</ul>
</ng-template>
</ng-container>

View file

@ -55,7 +55,8 @@ export class EventsWidgetComponent implements OnInit, OnDestroy {
constructor(public messageHub: MessageHubService, private modalService: NgbModal,
private accountService: AccountService, private confirmService: ConfirmService,
private readonly cdRef: ChangeDetectorRef, public downloadService: DownloadService) { }
private readonly cdRef: ChangeDetectorRef, public downloadService: DownloadService) {
}
ngOnDestroy(): void {
this.onDestroy.next();

View file

@ -42,7 +42,7 @@ export class TimeAgoPipe implements PipeTransform, OnDestroy {
const seconds = Math.round(Math.abs((now.getTime() - d.getTime()) / 1000));
const timeToUpdate = (Number.isNaN(seconds)) ? 1000 : this.getSecondsUntilUpdate(seconds) * 1000;
this.timer = this.ngZone.runOutsideAngular(() => {
this.timer = this.ngZone.runOutsideAngular(() => {
if (typeof window !== 'undefined') {
return window.setTimeout(() => {
this.ngZone.run(() => this.changeDetectorRef.markForCheck());

View file

@ -1,7 +1,17 @@
<ng-container *ngIf="loading">
<ng-container *ngIf="absolute; else relative">
<div class="position-absolute top-50 start-50 translate-middle" style="z-index: 999">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">Loading...</span>
</div>
</div>
</ng-container>
<ng-template #relative>
<div class="d-flex justify-content-center">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">Loading...</span>
</div>
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">Loading...</span>
</div>
</div>
</ng-template>
</ng-container>

View file

@ -10,10 +10,15 @@ export class LoadingComponent implements OnInit {
@Input() loading: boolean = false;
@Input() message: string = '';
/**
* Uses absolute positioning to ensure it loads over content
*/
@Input() absolute: boolean = false;
constructor(private readonly cdRef: ChangeDetectorRef) { }
ngOnInit(): void {
console.log('absolute: ', this.absolute);
}
}

View file

@ -179,6 +179,12 @@
</div>
</div>
<div class="col-md-6 col-sm-12 pe-2 mb-2">
<div class="mb-3 mt-1">
<div class="form-check form-switch">
<input type="checkbox" id="swipe-to-paginate" role="switch" formControlName="swipeToPaginate" class="form-check-input" [value]="true">
<label class="form-check-label me-1" for="swipe-to-paginate">Swipe to Paginate</label><i class="fa fa-info-circle" aria-hidden="true" placement="top" ngbTooltip="Should swiping on the screen cause the next or previous page to be triggered" role="button" tabindex="0"></i>
</div>
</div>
</div>
</div>

View file

@ -127,6 +127,7 @@ export class UserPreferencesComponent implements OnInit, OnDestroy {
this.settingsForm.addControl('readerMode', new FormControl(this.user.preferences.readerMode, []));
this.settingsForm.addControl('layoutMode', new FormControl(this.user.preferences.layoutMode, []));
this.settingsForm.addControl('emulateBook', new FormControl(this.user.preferences.emulateBook, []));
this.settingsForm.addControl('swipeToPaginate', new FormControl(this.user.preferences.swipeToPaginate, []));
this.settingsForm.addControl('bookReaderFontFamily', new FormControl(this.user.preferences.bookReaderFontFamily, []));
this.settingsForm.addControl('bookReaderFontSize', new FormControl(this.user.preferences.bookReaderFontSize, []));
@ -187,6 +188,7 @@ export class UserPreferencesComponent implements OnInit, OnDestroy {
this.settingsForm.get('promptForDownloadSize')?.setValue(this.user.preferences.promptForDownloadSize);
this.settingsForm.get('noTransitions')?.setValue(this.user.preferences.noTransitions);
this.settingsForm.get('emulateBook')?.setValue(this.user.preferences.emulateBook);
this.settingsForm.get('swipeToPaginate')?.setValue(this.user.preferences.swipeToPaginate);
this.cdRef.markForCheck();
this.settingsForm.markAsPristine();
}
@ -217,7 +219,8 @@ export class UserPreferencesComponent implements OnInit, OnDestroy {
blurUnreadSummaries: modelSettings.blurUnreadSummaries,
promptForDownloadSize: modelSettings.promptForDownloadSize,
noTransitions: modelSettings.noTransitions,
emulateBook: modelSettings.emulateBook
emulateBook: modelSettings.emulateBook,
swipeToPaginate: modelSettings.swipeToPaginate
};
this.observableHandles.push(this.accountService.updatePreferences(data).subscribe((updatedPrefs) => {