PDF Polish (#2837)

Co-authored-by: William Brockhus <pickeringw@gmail.com>
This commit is contained in:
Joe Milazzo 2024-04-09 17:57:28 -05:00 committed by GitHub
parent 6ed634f5d1
commit 752fda0e93
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 103 additions and 106 deletions

View File

@ -37,6 +37,9 @@ namespace API.Controllers;
/// </summary>
public class AccountController : BaseApiController
{
// Hardcoded to avoid localization multiple enumeration: https://github.com/Kareadita/Kavita/issues/2829
private const string BadCredentialsMessage = "Your credentials are not correct";
private readonly UserManager<AppUser> _userManager;
private readonly SignInManager<AppUser> _signInManager;
private readonly ITokenService _tokenService;
@ -204,7 +207,7 @@ public class AccountController : BaseApiController
if (user == null)
{
_logger.LogWarning("Attempted login by {UserName} failed due to unable to find account", loginDto.Username);
return Unauthorized(await _localizationService.Get("en", "bad-credentials"));
return Unauthorized(BadCredentialsMessage);
}
var roles = await _userManager.GetRolesAsync(user);
if (!roles.Contains(PolicyConstants.LoginRole)) return Unauthorized(await _localizationService.Translate(user.Id, "disabled-account"));
@ -225,10 +228,10 @@ public class AccountController : BaseApiController
if (!result.Succeeded)
{
var errorStr = await _localizationService.Translate(user.Id,
result.IsNotAllowed ? "confirm-email" : "bad-credentials");
_logger.LogWarning("{UserName} failed to log in at {Time}: {Issue}", user.UserName, user.LastActive,
errorStr);
string errorStr = result.IsNotAllowed
? await _localizationService.Translate(user.Id, "confirm-email")
: BadCredentialsMessage;
_logger.LogWarning("{UserName} failed to log in at {Time}: {Issue}", user.UserName, user.LastActive, errorStr);
return Unauthorized(errorStr);
}
}
@ -858,7 +861,7 @@ public class AccountController : BaseApiController
var user = await _unitOfWork.UserRepository.GetUserByEmailAsync(dto.Email);
if (user == null)
{
return BadRequest(await _localizationService.Get("en", "bad-credentials"));
return BadRequest(BadCredentialsMessage);
}
try
@ -868,7 +871,7 @@ public class AccountController : BaseApiController
if (!result)
{
_logger.LogInformation("Unable to reset password, your email token is not correct: {@Dto}", dto);
return BadRequest(await _localizationService.Translate(user.Id, "bad-credentials"));
return BadRequest(BadCredentialsMessage);
}
var errors = await _accountService.ChangeUserPassword(user, dto.Password);
@ -948,12 +951,12 @@ public class AccountController : BaseApiController
public async Task<ActionResult<UserDto>> ConfirmMigrationEmail(ConfirmMigrationEmailDto dto)
{
var user = await _unitOfWork.UserRepository.GetUserByEmailAsync(dto.Email);
if (user == null) return BadRequest(await _localizationService.Get("en", "bad-credentials"));
if (user == null) return BadRequest(BadCredentialsMessage);
if (!await ConfirmEmailToken(dto.Token, user))
{
_logger.LogInformation("confirm-migration-email email token is invalid");
return BadRequest(await _localizationService.Translate(user.Id, "bad-credentials"));
return BadRequest(BadCredentialsMessage);
}
await _unitOfWork.CommitAsync();

View File

@ -119,7 +119,6 @@ public class UsersController : BaseApiController
existingPreferences.ShareReviews = preferencesDto.ShareReviews;
existingPreferences.PdfTheme = preferencesDto.PdfTheme;
existingPreferences.PdfLayoutMode = preferencesDto.PdfLayoutMode;
existingPreferences.PdfScrollMode = preferencesDto.PdfScrollMode;
existingPreferences.PdfSpreadMode = preferencesDto.PdfSpreadMode;

View File

@ -118,7 +118,8 @@ public class AppUserPreferences
/// <summary>
/// PDF Reader: Layout Mode of the reader
/// </summary>
public PdfLayoutMode PdfLayoutMode { get; set; } = PdfLayoutMode.Multiple;
/// Book mode is too buggy to include
//public PdfLayoutMode PdfLayoutMode { get; set; } = PdfLayoutMode.Multiple;
/// <summary>
/// PDF Reader: Spread Mode of the reader
/// </summary>

View File

@ -1,6 +1,5 @@
{
"confirm-email": "You must confirm your email first",
"bad-credentials": "Your credentials are not correct",
"locked-out": "You've been locked out from too many authorization attempts. Please wait 10 minutes.",
"disabled-account": "Your account is disabled. Contact the server admin.",
"register-user": "Something went wrong when registering user",

View File

@ -41,7 +41,6 @@ export interface Preferences {
// PDF Reader
pdfTheme: PdfTheme;
pdfScrollMode: PdfScrollMode;
pdfLayoutMode: PdfLayoutMode;
pdfSpreadMode: PdfSpreadMode;
// Global

View File

@ -142,7 +142,7 @@ export class AccountService {
login(model: {username: string, password: string, apiKey?: string}) {
return this.httpClient.post<User>(this.baseUrl + 'account/login', model).pipe(
map((response: User) => {
tap((response: User) => {
const user = response;
if (user) {
this.setCurrentUser(user);
@ -176,6 +176,8 @@ export class AccountService {
this.stopRefreshTokenTimer();
if (this.currentUser) {
// BUG: StopHubConnection has a promise in it, this needs to be async
// But that really messes everything up
this.messageHub.stopHubConnection();
this.messageHub.createHubConnection(this.currentUser);
this.hasValidLicense().subscribe();

View File

@ -17,12 +17,18 @@
>{{t('renew')}}</a>
}
<button class="btn btn-secondary btn-sm me-1" style="width: 58px" (click)="validateLicense()">
<span *ngIf="!isChecking">{{t('check')}}</span>
<app-loading [loading]="isChecking" size="spinner-border-sm"></app-loading>
@if (isChecking) {
<app-loading [loading]="isChecking" size="spinner-border-sm"></app-loading>
} @else {
<span>{{t('check')}}</span>
}
</button>
<button class="btn btn-secondary btn-sm" style="width: 62px" (click)="toggleViewMode()">
<span *ngIf="!isViewMode">{{t('cancel')}}</span>
<span *ngIf="isViewMode">{{t('edit')}}</span>
@if (isViewMode) {
<span>{{t('edit')}}</span>
} @else {
<span>{{t('cancel')}}</span>
}
</button>
} @else {
<a class="btn btn-secondary btn-sm me-1" [href]="buyLink" target="_blank" rel="noreferrer nofollow">{{t('buy')}}</a>
@ -35,24 +41,29 @@
@if (isViewMode) {
<div class="container-fluid row">
<span class="col-12">
<ng-container *ngIf="hasLicense; else noToken">
<span class="me-1">*********</span>
<ng-container *ngIf="!isChecking; else checking">
<i *ngIf="hasValidLicense" [ngbTooltip]="t('license-valid')" class="fa-solid fa-check-circle successful-validation ms-1">
@if (hasLicense) {
<span class="me-1">*********</span>
@if (isChecking) {
<div class="spinner-border spinner-border-sm text-primary" role="status">
<span class="visually-hidden">{{t('loading')}}</span>
</div>
} @else {
@if (hasValidLicense) {
<i [ngbTooltip]="t('license-valid')" class="fa-solid fa-check-circle successful-validation ms-1">
<span class="visually-hidden">{{t('license-valid')}}</span>
</i>
<i class="error fa-solid fa-exclamation-circle ms-1" [ngbTooltip]="t('license-not-valid')" *ngIf="!hasValidLicense">
<span class="visually-hidden">{{t('license-not-valid')}}</span>
</i>
</ng-container>
<ng-template #checking>
<div class="spinner-border spinner-border-sm text-primary" role="status">
<span class="visually-hidden">{{t('loading')}}</span>
</div>
</ng-template>
}
</ng-container>
<ng-template #noToken>{{t('no-license-key')}}</ng-template>
@if (!hasValidLicense) {
<i class="error fa-solid fa-exclamation-circle ms-1" [ngbTooltip]="t('license-not-valid')">
<span class="visually-hidden">{{t('license-not-valid')}}</span>
</i>
}
}
}
@else {
{{t('no-license-key')}}
}
</span>
</div>
}
@ -74,11 +85,13 @@
<i class="fa fa-circle-info ms-1" aria-hidden="true" [ngbTooltip]="t('activate-discordId-tooltip')"></i>
<a class="ms-1" href="https://wiki.kavitareader.com/en/kavita-plus#discord-id" target="_blank" rel="noopener noreferrer">Help</a>
<input id="discordId" type="text" class="form-control" formControlName="discordId" autocomplete="off" [class.is-invalid]="formGroup.get('discordId')?.invalid && formGroup.get('discordId')?.touched"/>
<div id="inviteForm-validations" class="invalid-feedback" *ngIf="formGroup.dirty || formGroup.touched">
<div *ngIf="formGroup.get('discordId')?.errors?.pattern">
{{t('discord-validation')}}
@if (formGroup.dirty || formGroup.touched) {
<div id="inviteForm-validations" class="invalid-feedback">
<div *ngIf="formGroup.get('discordId')?.errors?.pattern">
{{t('discord-validation')}}
</div>
</div>
</div>
}
</div>
</form>
<div class="col-auto d-flex d-md-block justify-content-sm-center text-md-end mb-3">
@ -93,7 +106,10 @@
</button>
<button type="submit" class="flex-fill btn btn-primary" aria-describedby="license-key-header"
[disabled]="!formGroup.get('email')?.value || !formGroup.get('licenseKey')?.value" (click)="saveForm()">
<span *ngIf="!isSaving">{{t('activate-save')}}</span>
@if (!isSaving) {
<span>{{t('activate-save')}}</span>
}
<app-loading [loading]="isSaving" size="spinner-border-sm"></app-loading>
</button>
</div>

View File

@ -22,7 +22,7 @@ import {catchError} from "rxjs";
styleUrls: ['./license.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
imports: [NgIf, NgbTooltip, LoadingComponent, NgbCollapse, ReactiveFormsModule, TranslocoDirective]
imports: [NgbTooltip, LoadingComponent, NgbCollapse, ReactiveFormsModule, TranslocoDirective]
})
export class LicenseComponent implements OnInit {
@ -36,7 +36,7 @@ export class LicenseComponent implements OnInit {
hasValidLicense: boolean = false;
hasLicense: boolean = false;
isChecking: boolean = false;
isChecking: boolean = true;
isSaving: boolean = false;
buyLink = environment.buyLink;
@ -53,8 +53,11 @@ export class LicenseComponent implements OnInit {
this.hasLicense = res;
this.cdRef.markForCheck();
});
this.isChecking = true;
this.cdRef.markForCheck();
this.accountService.hasValidLicense().subscribe(res => {
this.hasValidLicense = res;
this.isChecking = false;
this.cdRef.markForCheck();
});
}

View File

@ -43,11 +43,12 @@
(pdfLoaded)="updateLoading(false)"
(progress)="updateLoadProgress($event)"
(zoomChange)="calcScrollbarNeeded()"
(handToolChange)="updateHandTool($event)"
>
</ngx-extended-pdf-viewer>
@if (scrollMode === ScrollModeType.page) {
@if (scrollMode === ScrollModeType.page && !isLoading) {
<div class="left" (click)="prevPage()"></div>
<div class="{{scrollbarNeeded ? 'right-with-scrollbar' : 'right'}}" (click)="nextPage()"></div>
}
@ -85,13 +86,13 @@
<pdf-select-tool></pdf-select-tool>
<pdf-presentation-mode></pdf-presentation-mode>
@if (utilityService.getActiveBreakpoint() > Breakpoint.Mobile) {
<button (click)="toggleBookPageMode()" class="btn-icon toolbarButton" [ngbTooltip]="pageLayoutMode | pdfLayoutMode" [disabled]="scrollMode === ScrollModeType.page">
<i class="toolbar-icon fa-solid {{this.pageLayoutMode !== 'book' ? 'fa-book' : 'fa-book-open'}}" [ngStyle]="{color: fontColor}" aria-hidden="true"></i>
<span class="visually-hidden">{{this.pageLayoutMode | pdfLayoutMode}}</span>
</button>
}
<!-- The book mode is messy, not ready for prime time -->
<!-- @if (utilityService.getActiveBreakpoint() > Breakpoint.Mobile) {-->
<!-- <button (click)="toggleBookPageMode()" class="btn-icon toolbarButton" [ngbTooltip]="pageLayoutMode | pdfLayoutMode" [disabled]="scrollMode === ScrollModeType.page">-->
<!-- <i class="toolbar-icon fa-solid {{this.pageLayoutMode !== 'book' ? 'fa-book' : 'fa-book-open'}}" [ngStyle]="{color: fontColor}" aria-hidden="true"></i>-->
<!-- <span class="visually-hidden">{{this.pageLayoutMode | pdfLayoutMode}}</span>-->
<!-- </button>-->
<!-- }-->
<!-- scroll mode should be disabled when book mode is used -->

View File

@ -34,8 +34,11 @@ $pagination-opacity: 0;
//$pagination-color: red;
//$pagination-opacity: 0.7;
$action-bar-height: 36px;
$loading-bar-height: 24px;
// Tap to Paginate
.right {
position: absolute;
right: 0px;

View File

@ -34,6 +34,7 @@ import {SpreadType} from "ngx-extended-pdf-viewer/lib/options/spread-type";
import {PdfLayoutModePipe} from "../../_pipe/pdf-layout-mode.pipe";
import {PdfScrollModePipe} from "../../_pipe/pdf-scroll-mode.pipe";
import {PdfSpreadModePipe} from "../../_pipe/pdf-spread-mode.pipe";
import {HandtoolChanged} from "ngx-extended-pdf-viewer/lib/events/handtool-changed";
@Component({
selector: 'app-pdf-reader',
@ -236,7 +237,7 @@ export class PdfReaderComponent implements OnInit, OnDestroy {
init() {
this.pageLayoutMode = this.convertPdfLayoutMode(this.user.preferences.pdfLayoutMode || PdfLayoutMode.Multiple);
this.pageLayoutMode = this.convertPdfLayoutMode(PdfLayoutMode.Multiple);
this.scrollMode = this.convertPdfScrollMode(this.user.preferences.pdfScrollMode || PdfScrollMode.Vertical);
this.spreadMode = this.convertPdfSpreadMode(this.user.preferences.pdfSpreadMode || PdfSpreadMode.None);
this.theme = this.convertPdfTheme(this.user.preferences.pdfTheme || PdfTheme.Dark);
@ -350,6 +351,10 @@ export class PdfReaderComponent implements OnInit, OnDestroy {
this.cdRef.markForCheck();
}
updateHandTool(event: any) {
console.log('event.tool', event);
}
prevPage() {
this.currentPage--;
if (this.currentPage < 0) this.currentPage = 0;

View File

@ -542,45 +542,6 @@
<div ngbAccordionBody>
<ng-template>
<div class="row g-0">
<div class="col-md-6 col-sm-12 pe-2 mb-3">
<label for="settings-pdf-layout-mode" class="form-label">{{t('pdf-layout-mode-label')}}</label><i
class="fa fa-info-circle ms-1" aria-hidden="true" placement="right"
[ngbTooltip]="pdfLayoutModeTooltip" role="button" tabindex="0"></i>
<ng-template #pdfLayoutModeTooltip>{{t('pdf-layout-mode-tooltip')}}
</ng-template>
<span class="visually-hidden" id="settings-pdf-layout-mode-help">
<ng-container [ngTemplateOutlet]="pdfLayoutModeTooltip"></ng-container>
</span>
<select id="settings-pdf-layout-mode" class="form-select"
aria-describedby="settings-pdf-layout-mode-help"
formControlName="pdfLayoutMode">
<option *ngFor="let opt of pdfLayoutModesTranslated" [value]="opt.value">
{{opt.text | titlecase}}
</option>
</select>
</div>
<div class="col-md-6 col-sm-12 pe-2 mb-3">
<label for="settings-pdf-scroll-mode" class="form-label">{{t('pdf-scroll-mode-label')}}</label><i
class="fa fa-info-circle ms-1" aria-hidden="true" placement="right"
[ngbTooltip]="pdfScrollModeTooltip" role="button" tabindex="0"></i>
<ng-template #pdfScrollModeTooltip>{{t('pdf-scroll-mode-tooltip')}}
</ng-template>
<span class="visually-hidden" id="settings-pdf-scroll-mode-help">
<ng-container [ngTemplateOutlet]="pdfScrollModeTooltip"></ng-container>
</span>
<select id="settings-pdf-scroll-mode" class="form-select"
aria-describedby="settings-pdf-scroll-mode-help"
formControlName="pdfScrollMode">
<option *ngFor="let opt of pdfScrollModesTranslated" [value]="opt.value">
{{opt.text | titlecase}}
</option>
</select>
</div>
</div>
<div class="row g-0">
<div class="col-md-6 col-sm-12 pe-2 mb-3">
<label for="settings-pdf-spread-mode" class="form-label">{{t('pdf-spread-mode-label')}}</label><i
@ -612,6 +573,25 @@
</div>
</div>
<div class="row g-0">
<div class="col-md-6 col-sm-12 pe-2 mb-3">
<label for="settings-pdf-scroll-mode" class="form-label">{{t('pdf-scroll-mode-label')}}</label><i
class="fa fa-info-circle ms-1" aria-hidden="true" placement="right"
[ngbTooltip]="pdfScrollModeTooltip" role="button" tabindex="0"></i>
<ng-template #pdfScrollModeTooltip>{{t('pdf-scroll-mode-tooltip')}}
</ng-template>
<span class="visually-hidden" id="settings-pdf-scroll-mode-help">
<ng-container [ngTemplateOutlet]="pdfScrollModeTooltip"></ng-container>
</span>
<select id="settings-pdf-scroll-mode" class="form-select"
aria-describedby="settings-pdf-scroll-mode-help"
formControlName="pdfScrollMode">
<option *ngFor="let opt of pdfScrollModesTranslated" [value]="opt.value">
{{opt.text | titlecase}}
</option>
</select>
</div>
</div>
<div class="col-auto d-flex d-md-block justify-content-sm-center text-md-end mb-3">
<button type="button" class="flex-fill btn btn-secondary me-2" (click)="resetForm()"

View File

@ -136,7 +136,7 @@ export class UserPreferencesComponent implements OnInit, OnDestroy {
pageLayoutModesTranslated = pageLayoutModes.map(this.translatePrefOptions);
bookWritingStylesTranslated = bookWritingStyles.map(this.translatePrefOptions);
pdfLayoutModesTranslated = pdfLayoutModes.map(this.translatePrefOptions);
// pdfLayoutModesTranslated = pdfLayoutModes.map(this.translatePrefOptions);
pdfScrollModesTranslated = pdfScrollModes.map(this.translatePrefOptions);
pdfSpreadModesTranslated = pdfSpreadModes.map(this.translatePrefOptions);
pdfThemesTranslated = pdfThemes.map(this.translatePrefOptions);
@ -253,7 +253,6 @@ export class UserPreferencesComponent implements OnInit, OnDestroy {
this.settingsForm.addControl('pdfTheme', new FormControl(this.user?.preferences.pdfTheme || PdfTheme.Dark, []));
this.settingsForm.addControl('pdfScrollMode', new FormControl(this.user?.preferences.pdfScrollMode || PdfScrollMode.Vertical, []));
this.settingsForm.addControl('pdfLayoutMode', new FormControl(this.user?.preferences.pdfLayoutMode || PdfLayoutMode.Multiple, []));
this.settingsForm.addControl('pdfSpreadMode', new FormControl(this.user?.preferences.pdfSpreadMode || PdfSpreadMode.None, []));
this.settingsForm.addControl('theme', new FormControl(this.user.preferences.theme, []));
@ -318,7 +317,6 @@ export class UserPreferencesComponent implements OnInit, OnDestroy {
this.settingsForm.get('pdfTheme')?.setValue(this.user.preferences.pdfTheme);
this.settingsForm.get('pdfScrollMode')?.setValue(this.user.preferences.pdfScrollMode);
this.settingsForm.get('pdfLayoutMode')?.setValue(this.user.preferences.pdfLayoutMode);
this.settingsForm.get('pdfSpreadMode')?.setValue(this.user.preferences.pdfSpreadMode);
this.cdRef.markForCheck();
@ -359,7 +357,6 @@ export class UserPreferencesComponent implements OnInit, OnDestroy {
locale: modelSettings.locale,
pdfTheme: parseInt(modelSettings.pdfTheme, 10),
pdfScrollMode: parseInt(modelSettings.pdfScrollMode, 10),
pdfLayoutMode: parseInt(modelSettings.pdfLayoutMode, 10),
pdfSpreadMode: parseInt(modelSettings.pdfSpreadMode, 10),
};

View File

@ -160,8 +160,6 @@
"margin-book-tooltip": "How much spacing on each side of the screen. This will override to 0 on mobile devices regardless of this setting.",
"pdf-reader-settings-title": "PDF Reader",
"pdf-layout-mode-label": "Layout Mode",
"pdf-layout-mode-tooltip": "How the reader lays the pdf out. Default is pages stacked with scrolling and Book emulates a physical book",
"pdf-scroll-mode-label": "Scroll Mode",
"pdf-scroll-mode-tooltip": "How you scroll through pages. Vertical/Horizontal and Tap to Paginate (no scroll)",
"pdf-spread-mode-label": "Spread Mode",
@ -516,7 +514,7 @@
"book": "Book",
"comic": "Comic",
"manga": "Manga",
"comicVine": "ComicVine",
"comicVine": "Comic Vine",
"image": "Image",
"lightNovel": "Light Novel"
},
@ -1612,7 +1610,7 @@
"validate-cbl-step": "Validate CBL",
"dry-run-step": "Dry Run",
"final-import-step": "Final Step",
"comicvine-parsing-label": "Use ComicVine Series matching"
"comicvine-parsing-label": "Use Comic Vine Series matching"
},
"pdf-reader": {

View File

@ -7,7 +7,7 @@
"name": "GPL-3.0",
"url": "https://github.com/Kareadita/Kavita/blob/develop/LICENSE"
},
"version": "0.7.14.12"
"version": "0.7.14.13"
},
"servers": [
{
@ -13619,15 +13619,6 @@
"description": "PDF Reader: Scroll mode of the reader",
"format": "int32"
},
"pdfLayoutMode": {
"enum": [
0,
2
],
"type": "integer",
"description": "PDF Reader: Layout Mode of the reader",
"format": "int32"
},
"pdfSpreadMode": {
"enum": [
0,