mirror of
https://github.com/Kareadita/Kavita.git
synced 2025-07-31 14:33:50 -04:00
Merged main into develop
This commit is contained in:
commit
848885f103
@ -9,6 +9,7 @@ using Flurl.Http;
|
|||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using NetVips;
|
||||||
|
|
||||||
namespace API.Controllers
|
namespace API.Controllers
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using API.Entities.Enums;
|
using System;
|
||||||
|
using API.Entities.Enums;
|
||||||
|
|
||||||
namespace API.DTOs.Reader
|
namespace API.DTOs.Reader
|
||||||
{
|
{
|
||||||
|
10
API/DTOs/UpdateUserRole.cs
Normal file
10
API/DTOs/UpdateUserRole.cs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using MediatR;
|
||||||
|
|
||||||
|
namespace API.DTOs;
|
||||||
|
|
||||||
|
public class UpdateUserRole : IRequest<bool>
|
||||||
|
{
|
||||||
|
public string Username { get; init; }
|
||||||
|
public IList<string> Roles { get; init; }
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.IO.Abstractions;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using API.Services;
|
using API.Services;
|
||||||
using Kavita.Common;
|
using Kavita.Common;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using API.DTOs.CollectionTags;
|
using API.DTOs.CollectionTags;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using API.Entities.Interfaces;
|
using API.Entities.Interfaces;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace API.Entities
|
namespace API.Entities
|
||||||
{
|
{
|
||||||
|
@ -131,6 +131,7 @@ namespace API.Helpers
|
|||||||
|
|
||||||
CreateMap<IEnumerable<ServerSetting>, ServerSettingDto>()
|
CreateMap<IEnumerable<ServerSetting>, ServerSettingDto>()
|
||||||
.ConvertUsing<ServerSettingConverter>();
|
.ConvertUsing<ServerSettingConverter>();
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Data;
|
using System.Data;
|
||||||
using System.Data.Common;
|
using System.Data.Common;
|
||||||
|
using API.DTOs;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace API.Helpers
|
namespace API.Helpers
|
||||||
|
@ -19,6 +19,7 @@ using Hangfire;
|
|||||||
using Hangfire.MemoryStorage;
|
using Hangfire.MemoryStorage;
|
||||||
using Kavita.Common;
|
using Kavita.Common;
|
||||||
using Kavita.Common.EnvironmentInfo;
|
using Kavita.Common.EnvironmentInfo;
|
||||||
|
using MediatR;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
@ -131,6 +132,8 @@ namespace API
|
|||||||
// Add IHostedService for startup tasks
|
// Add IHostedService for startup tasks
|
||||||
// Any services that should be bootstrapped go here
|
// Any services that should be bootstrapped go here
|
||||||
services.AddHostedService<StartupTasksHostedService>();
|
services.AddHostedService<StartupTasksHostedService>();
|
||||||
|
|
||||||
|
services.AddMediatR(typeof(Startup));
|
||||||
}
|
}
|
||||||
|
|
||||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||||
|
BIN
API/config/kavita.db.dik
Normal file
BIN
API/config/kavita.db.dik
Normal file
Binary file not shown.
BIN
API/config/kavita.db.new
Normal file
BIN
API/config/kavita.db.new
Normal file
Binary file not shown.
4
UI/Web/src/app/_models/events/scan-library-event.ts
Normal file
4
UI/Web/src/app/_models/events/scan-library-event.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export interface ScanLibraryEvent {
|
||||||
|
libraryId: number;
|
||||||
|
stage: 'complete';
|
||||||
|
}
|
@ -107,15 +107,15 @@
|
|||||||
</app-drawer>
|
</app-drawer>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div #readingSection class="reading-section" [ngStyle]="{'padding-top': topOffset + 20 + 'px'}"
|
<div #readingSection class="reading-section" [ngStyle]="{'padding-top': topOffset + 20 + 'px'}"
|
||||||
[@isLoading]="isLoading ? true : false" (click)="handleReaderClick($event)">
|
[@isLoading]="isLoading ? true : false" (click)="handleReaderClick($event)">
|
||||||
|
|
||||||
<div #readingHtml class="book-content" [ngStyle]="{'padding-bottom': topOffset + 20 + 'px', 'margin': '0px 0px'}"
|
<div #readingHtml class="book-content" [ngStyle]="{'padding-bottom': topOffset + 20 + 'px', 'margin': '0px 0px'}"
|
||||||
[innerHtml]="page" *ngIf="page !== undefined"></div>
|
[innerHtml]="page" *ngIf="page !== undefined"></div>
|
||||||
|
|
||||||
<div class="left {{clickOverlayClass('left')}} no-observe" (click)="prevPage()" *ngIf="clickToPaginate" tabindex="-1"></div>
|
<div class="left {{clickOverlayClass('left')}} no-observe" (click)="prevPage()" *ngIf="clickToPaginate" tabindex="-1"></div>
|
||||||
<div class="{{scrollbarNeeded ? 'right-with-scrollbar' : 'right'}} {{clickOverlayClass('right')}} no-observe" (click)="nextPage()" *ngIf="clickToPaginate" tabindex="-1"></div>
|
<div class="{{scrollbarNeeded ? 'right-with-scrollbar' : 'right'}} {{clickOverlayClass('right')}} no-observe" (click)="nextPage()" *ngIf="clickToPaginate" tabindex="-1"></div>
|
||||||
|
|
||||||
<div *ngIf="page !== undefined && scrollbarNeeded" (click)="$event.stopPropagation();">
|
<div *ngIf="page !== undefined && scrollbarNeeded" (click)="$event.stopPropagation();">
|
||||||
<ng-container [ngTemplateOutlet]="actionBar"></ng-container>
|
<ng-container [ngTemplateOutlet]="actionBar"></ng-container>
|
||||||
</div>
|
</div>
|
||||||
@ -123,8 +123,8 @@
|
|||||||
|
|
||||||
<ng-template #actionBar>
|
<ng-template #actionBar>
|
||||||
<div class="reading-bar row g-0 justify-content-between">
|
<div class="reading-bar row g-0 justify-content-between">
|
||||||
<button class="btn btn-outline-secondary btn-icon col-2 col-xs-1" (click)="prevPage()"
|
<button class="btn btn-outline-secondary btn-icon col-2 col-xs-1" (click)="prevPage()"
|
||||||
[disabled]="IsPrevDisabled"
|
[disabled]="IsPrevDisabled"
|
||||||
title="{{readingDirection === ReadingDirection.LeftToRight ? 'Previous' : 'Next'}} Page">
|
title="{{readingDirection === ReadingDirection.LeftToRight ? 'Previous' : 'Next'}} Page">
|
||||||
<i class="fa {{(readingDirection === ReadingDirection.LeftToRight ? IsPrevChapter : IsNextChapter) ? 'fa-angle-double-left' : 'fa-angle-left'}}" aria-hidden="true"></i>
|
<i class="fa {{(readingDirection === ReadingDirection.LeftToRight ? IsPrevChapter : IsNextChapter) ? 'fa-angle-double-left' : 'fa-angle-left'}}" aria-hidden="true"></i>
|
||||||
<span class="d-none d-sm-block"> {{readingDirection === ReadingDirection.LeftToRight ? 'Previous' : 'Next'}}</span>
|
<span class="d-none d-sm-block"> {{readingDirection === ReadingDirection.LeftToRight ? 'Previous' : 'Next'}}</span>
|
||||||
@ -143,8 +143,8 @@
|
|||||||
</ng-template>
|
</ng-template>
|
||||||
</div>
|
</div>
|
||||||
<button class="btn btn-secondary col-2 col-xs-1" (click)="closeReader()"><i class="fa fa-times-circle" aria-hidden="true"></i><span class="d-none d-sm-block"> Close</span></button>
|
<button class="btn btn-secondary col-2 col-xs-1" (click)="closeReader()"><i class="fa fa-times-circle" aria-hidden="true"></i><span class="d-none d-sm-block"> Close</span></button>
|
||||||
<button class="btn btn-outline-secondary btn-icon col-2 col-xs-1"
|
<button class="btn btn-outline-secondary btn-icon col-2 col-xs-1"
|
||||||
[disabled]="IsNextDisabled"
|
[disabled]="IsNextDisabled"
|
||||||
(click)="nextPage()" title="{{readingDirection === ReadingDirection.LeftToRight ? 'Next' : 'Previous'}} Page">
|
(click)="nextPage()" title="{{readingDirection === ReadingDirection.LeftToRight ? 'Next' : 'Previous'}} Page">
|
||||||
<i class="fa {{(readingDirection === ReadingDirection.LeftToRight ? IsNextChapter : IsPrevChapter) ? 'fa-angle-double-right' : 'fa-angle-right'}}" aria-hidden="true"></i>
|
<i class="fa {{(readingDirection === ReadingDirection.LeftToRight ? IsNextChapter : IsPrevChapter) ? 'fa-angle-double-right' : 'fa-angle-right'}}" aria-hidden="true"></i>
|
||||||
<span class="d-none d-sm-block">{{readingDirection === ReadingDirection.LeftToRight ? 'Next' : 'Previous'}} </span>
|
<span class="d-none d-sm-block">{{readingDirection === ReadingDirection.LeftToRight ? 'Next' : 'Previous'}} </span>
|
||||||
@ -152,4 +152,4 @@
|
|||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -43,7 +43,7 @@ export class CoverImageChooserComponent implements OnInit, OnDestroy {
|
|||||||
mode: 'file' | 'url' | 'all' = 'all';
|
mode: 'file' | 'url' | 'all' = 'all';
|
||||||
private readonly onDestroy = new Subject<void>();
|
private readonly onDestroy = new Subject<void>();
|
||||||
|
|
||||||
constructor(public imageService: ImageService, private fb: FormBuilder, private toastr: ToastrService, private uploadService: UploadService,
|
constructor(public imageService: ImageService, private fb: FormBuilder, private toastr: ToastrService, private uploadService: UploadService,
|
||||||
@Inject(DOCUMENT) private document: Document) { }
|
@Inject(DOCUMENT) private document: Document) { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
@ -89,7 +89,7 @@ export class CoverImageChooserComponent implements OnInit, OnDestroy {
|
|||||||
img.onload = (e) => this.handleUrlImageAdd(img);
|
img.onload = (e) => this.handleUrlImageAdd(img);
|
||||||
img.onerror = (e) => {
|
img.onerror = (e) => {
|
||||||
this.toastr.error('The image could not be fetched due to server refusing request. Please download and upload from file instead.');
|
this.toastr.error('The image could not be fetched due to server refusing request. Please download and upload from file instead.');
|
||||||
this.form.get('coverImageUrl')?.setValue('');
|
this.form.get('coverImageUrl')?.setValue('');
|
||||||
};
|
};
|
||||||
this.form.get('coverImageUrl')?.setValue('');
|
this.form.get('coverImageUrl')?.setValue('');
|
||||||
});
|
});
|
||||||
@ -97,7 +97,7 @@ export class CoverImageChooserComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
changeMode(mode: 'url') {
|
changeMode(mode: 'url') {
|
||||||
this.mode = mode;
|
this.mode = mode;
|
||||||
this.setupEnterHandler();
|
this.setupEnterHandler();
|
||||||
|
|
||||||
setTimeout(() => (this.document.querySelector('#load-image') as HTMLInputElement)?.focus(), 10);
|
setTimeout(() => (this.document.querySelector('#load-image') as HTMLInputElement)?.focus(), 10);
|
||||||
@ -164,7 +164,7 @@ export class CoverImageChooserComponent implements OnInit, OnDestroy {
|
|||||||
this.loadImage();
|
this.loadImage();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case KEY_CODES.ESC_KEY:
|
case KEY_CODES.ESC_KEY:
|
||||||
this.mode = 'all';
|
this.mode = 'all';
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
@ -31,6 +31,10 @@ img, .full-width {
|
|||||||
height: auto;
|
height: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// .img-container {
|
||||||
|
// overflow: auto;
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
@keyframes move-up-down {
|
@keyframes move-up-down {
|
||||||
0%, 100% {
|
0%, 100% {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user