mirror of
https://github.com/Kareadita/Kavita.git
synced 2025-07-09 03:04:19 -04:00
Last batch of bugfixes (#1262)
* Refactored code to show action bar instead of drawer in immersive mode * Card grid * adding margin for pagination gap * Fixed a rare routing case that wouldn't redirect * Fixed a bug where series detail would show blank filtering * Fixing image scaling and library card spacing * Refactored some methods to be static * Adding card grid to series detail * Fixed a bug with webtoon going to non-webtoon mode, resulting in black screen. * Ensure emails are trimmed when trying to invite. * Don't show More In if there is only 1 item in there on library recommended tab * Fixed some bugs around locking metadata fields where the correct param wasn't being sent to backend. * Added some UI error messaging when the email doesn't match the confirm-email (or rather any email in the system). * Fixed some pages where actions weren't working (library detail) and removed some actionable buttons where they didn't make sense * Refactored the series detail to use Robbie's new grid system. * some styling fixes * Styling fixes - Removing select border gap - fixing switches on lite theme - fixing search result text-light * better css var naming * changing search lite text color override * fixing as per feedback * Removing boolean from being visible in bookreader * Fixed some bugs in bulk operations not being visible on light/eink screens. Added --bulk-selection-highlight-text-color and --bulk-selection-text-color. Co-authored-by: Robbie Davis <robbie@therobbiedavis.com>
This commit is contained in:
parent
6f23a3bc6d
commit
1961b41268
@ -770,7 +770,7 @@ public class SeriesServiceTests
|
||||
{
|
||||
SeriesId = 1,
|
||||
Publishers = new List<PersonDto>() {new () {Id = 0, Name = "Existing Person", Role = PersonRole.Publisher}},
|
||||
PublisherLocked = true
|
||||
PublishersLocked = true
|
||||
},
|
||||
CollectionTags = new List<CollectionTagDto>()
|
||||
});
|
||||
|
@ -353,6 +353,7 @@ namespace API.Controllers
|
||||
_logger.LogInformation("{User} is inviting {Email} to the server", adminUser.UserName, dto.Email);
|
||||
|
||||
// Check if there is an existing invite
|
||||
dto.Email = dto.Email.Trim();
|
||||
var emailValidationErrors = await _accountService.ValidateEmail(dto.Email);
|
||||
if (emailValidationErrors.Any())
|
||||
{
|
||||
@ -454,6 +455,11 @@ namespace API.Controllers
|
||||
{
|
||||
var user = await _unitOfWork.UserRepository.GetUserByEmailAsync(dto.Email);
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
return BadRequest("The email does not match the registered email");
|
||||
}
|
||||
|
||||
// Validate Password and Username
|
||||
var validationErrors = new List<ApiException>();
|
||||
validationErrors.AddRange(await _accountService.ValidateUsername(dto.Username));
|
||||
|
@ -6,7 +6,7 @@ namespace API.DTOs.Account;
|
||||
public class InviteUserDto
|
||||
{
|
||||
[Required]
|
||||
public string Email { get; init; }
|
||||
public string Email { get; set; }
|
||||
/// <summary>
|
||||
/// List of Roles to assign to user. If admin not present, Pleb will be applied.
|
||||
/// If admin present, all libraries will be granted access and will ignore those from DTO.
|
||||
|
@ -69,16 +69,16 @@ namespace API.DTOs
|
||||
public bool PublicationStatusLocked { get; set; }
|
||||
public bool GenresLocked { get; set; }
|
||||
public bool TagsLocked { get; set; }
|
||||
public bool WriterLocked { get; set; }
|
||||
public bool CharacterLocked { get; set; }
|
||||
public bool ColoristLocked { get; set; }
|
||||
public bool EditorLocked { get; set; }
|
||||
public bool InkerLocked { get; set; }
|
||||
public bool LettererLocked { get; set; }
|
||||
public bool PencillerLocked { get; set; }
|
||||
public bool PublisherLocked { get; set; }
|
||||
public bool TranslatorLocked { get; set; }
|
||||
public bool CoverArtistLocked { get; set; }
|
||||
public bool WritersLocked { get; set; }
|
||||
public bool CharactersLocked { get; set; }
|
||||
public bool ColoristsLocked { get; set; }
|
||||
public bool EditorsLocked { get; set; }
|
||||
public bool InkersLocked { get; set; }
|
||||
public bool LetterersLocked { get; set; }
|
||||
public bool PencillersLocked { get; set; }
|
||||
public bool PublishersLocked { get; set; }
|
||||
public bool TranslatorsLocked { get; set; }
|
||||
public bool CoverArtistsLocked { get; set; }
|
||||
|
||||
|
||||
public int SeriesId { get; set; }
|
||||
|
@ -81,7 +81,7 @@ namespace API.Data
|
||||
}
|
||||
|
||||
|
||||
void OnEntityTracked(object sender, EntityTrackedEventArgs e)
|
||||
static void OnEntityTracked(object sender, EntityTrackedEventArgs e)
|
||||
{
|
||||
if (!e.FromQuery && e.Entry.State == EntityState.Added && e.Entry.Entity is IEntityDate entity)
|
||||
{
|
||||
@ -91,7 +91,7 @@ namespace API.Data
|
||||
|
||||
}
|
||||
|
||||
void OnEntityStateChanged(object sender, EntityStateChangedEventArgs e)
|
||||
static void OnEntityStateChanged(object sender, EntityStateChangedEventArgs e)
|
||||
{
|
||||
if (e.NewState == EntityState.Modified && e.Entry.Entity is IEntityDate entity)
|
||||
entity.LastModified = DateTime.Now;
|
||||
|
@ -156,16 +156,16 @@ public class SeriesService : ISeriesService
|
||||
series.Metadata.LanguageLocked = updateSeriesMetadataDto.SeriesMetadata.LanguageLocked;
|
||||
series.Metadata.GenresLocked = updateSeriesMetadataDto.SeriesMetadata.GenresLocked;
|
||||
series.Metadata.TagsLocked = updateSeriesMetadataDto.SeriesMetadata.TagsLocked;
|
||||
series.Metadata.CharacterLocked = updateSeriesMetadataDto.SeriesMetadata.CharacterLocked;
|
||||
series.Metadata.ColoristLocked = updateSeriesMetadataDto.SeriesMetadata.ColoristLocked;
|
||||
series.Metadata.EditorLocked = updateSeriesMetadataDto.SeriesMetadata.EditorLocked;
|
||||
series.Metadata.InkerLocked = updateSeriesMetadataDto.SeriesMetadata.InkerLocked;
|
||||
series.Metadata.LettererLocked = updateSeriesMetadataDto.SeriesMetadata.LettererLocked;
|
||||
series.Metadata.PencillerLocked = updateSeriesMetadataDto.SeriesMetadata.PencillerLocked;
|
||||
series.Metadata.PublisherLocked = updateSeriesMetadataDto.SeriesMetadata.PublisherLocked;
|
||||
series.Metadata.TranslatorLocked = updateSeriesMetadataDto.SeriesMetadata.TranslatorLocked;
|
||||
series.Metadata.CoverArtistLocked = updateSeriesMetadataDto.SeriesMetadata.CoverArtistLocked;
|
||||
series.Metadata.WriterLocked = updateSeriesMetadataDto.SeriesMetadata.WriterLocked;
|
||||
series.Metadata.CharacterLocked = updateSeriesMetadataDto.SeriesMetadata.CharactersLocked;
|
||||
series.Metadata.ColoristLocked = updateSeriesMetadataDto.SeriesMetadata.ColoristsLocked;
|
||||
series.Metadata.EditorLocked = updateSeriesMetadataDto.SeriesMetadata.EditorsLocked;
|
||||
series.Metadata.InkerLocked = updateSeriesMetadataDto.SeriesMetadata.InkersLocked;
|
||||
series.Metadata.LettererLocked = updateSeriesMetadataDto.SeriesMetadata.LetterersLocked;
|
||||
series.Metadata.PencillerLocked = updateSeriesMetadataDto.SeriesMetadata.PencillersLocked;
|
||||
series.Metadata.PublisherLocked = updateSeriesMetadataDto.SeriesMetadata.PublishersLocked;
|
||||
series.Metadata.TranslatorLocked = updateSeriesMetadataDto.SeriesMetadata.TranslatorsLocked;
|
||||
series.Metadata.CoverArtistLocked = updateSeriesMetadataDto.SeriesMetadata.CoverArtistsLocked;
|
||||
series.Metadata.WriterLocked = updateSeriesMetadataDto.SeriesMetadata.WritersLocked;
|
||||
series.Metadata.SummaryLocked = updateSeriesMetadataDto.SeriesMetadata.SummaryLocked;
|
||||
|
||||
if (!_unitOfWork.HasChanges())
|
||||
|
@ -35,7 +35,7 @@ export interface SeriesMetadata {
|
||||
tagsLocked: boolean;
|
||||
writersLocked: boolean;
|
||||
coverArtistsLocked: boolean;
|
||||
publisherLocked: boolean;
|
||||
publishersLocked: boolean;
|
||||
charactersLocked: boolean;
|
||||
pencillersLocked: boolean;
|
||||
inkersLocked: boolean;
|
||||
|
@ -42,7 +42,7 @@ export class InviteUserComponent implements OnInit {
|
||||
invite() {
|
||||
|
||||
this.isSending = true;
|
||||
const email = this.inviteForm.get('email')?.value;
|
||||
const email = this.inviteForm.get('email')?.value.trim();
|
||||
this.accountService.inviteUser({
|
||||
email,
|
||||
libraries: this.selectedLibraries,
|
||||
|
@ -78,6 +78,7 @@ const routes: Routes = [
|
||||
},
|
||||
{path: 'login', loadChildren: () => import('../app/registration/registration.module').then(m => m.RegistrationModule)},
|
||||
{path: '**', pathMatch: 'full', redirectTo: 'libraries'},
|
||||
{path: '', pathMatch: 'full', redirectTo: 'libraries'},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
|
@ -54,7 +54,7 @@
|
||||
(fullscreen)="toggleFullscreen()"
|
||||
(layoutModeUpdate)="updateLayoutMode($event)"
|
||||
(readingDirection)="updateReadingDirection($event)"
|
||||
(immersiveMode)="immersiveMode = $event"
|
||||
(immersiveMode)="updateImmersiveMode($event)"
|
||||
></app-reader-settings>
|
||||
</ng-template>
|
||||
</li>
|
||||
@ -96,7 +96,7 @@
|
||||
</div>
|
||||
|
||||
<ng-template #actionBar>
|
||||
<div class="action-bar row g-0 justify-content-between" *ngIf="!immersiveMode || drawerOpen">
|
||||
<div class="action-bar row g-0 justify-content-between" *ngIf="!immersiveMode || drawerOpen || actionBarVisible">
|
||||
<button class="btn btn-outline-secondary btn-icon col-2 col-xs-1" (click)="movePage(readingDirection === ReadingDirection.LeftToRight ? PAGING_DIRECTION.BACKWARDS : PAGING_DIRECTION.FORWARD)"
|
||||
[disabled]="readingDirection === ReadingDirection.LeftToRight ? IsPrevDisabled : IsNextDisabled"
|
||||
title="{{readingDirection === ReadingDirection.LeftToRight ? 'Previous' : 'Next'}} Page">
|
||||
|
@ -124,6 +124,10 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
* Belongs to drawer component
|
||||
*/
|
||||
drawerOpen = false;
|
||||
/**
|
||||
* If the action bar is visible
|
||||
*/
|
||||
actionBarVisible = true;
|
||||
/**
|
||||
* Book reader setting that hides the menuing system
|
||||
*/
|
||||
@ -1105,6 +1109,10 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
|
||||
toggleDrawer() {
|
||||
this.drawerOpen = !this.drawerOpen;
|
||||
|
||||
if (this.immersiveMode) {
|
||||
this.actionBarVisible = false;
|
||||
}
|
||||
}
|
||||
|
||||
scrollTo(partSelector: string) {
|
||||
@ -1197,6 +1205,10 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
this.updateImagesWithHeight();
|
||||
|
||||
// Calulate if bottom actionbar is needed. On a timeout to get accurate heights
|
||||
if (this.readingHtml == null) {
|
||||
setTimeout(() => this.updateLayoutMode(this.layoutMode), 10);
|
||||
return;
|
||||
}
|
||||
setTimeout(() => {this.scrollbarNeeded = this.readingHtml.nativeElement.clientHeight > this.reader.nativeElement.clientHeight;});
|
||||
}
|
||||
|
||||
@ -1204,6 +1216,13 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
this.readingDirection = readingDirection;
|
||||
}
|
||||
|
||||
updateImmersiveMode(immersiveMode: boolean) {
|
||||
this.immersiveMode = immersiveMode;
|
||||
if (this.immersiveMode && !this.drawerOpen) {
|
||||
this.actionBarVisible = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Table of Contents
|
||||
cleanIdSelector(id: string) {
|
||||
const tokens = id.split('/');
|
||||
@ -1299,7 +1318,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
Math.abs(this.mousePosition.x - event.screenX) <= mouseOffset &&
|
||||
Math.abs(this.mousePosition.y - event.screenY) <= mouseOffset
|
||||
) {
|
||||
this.drawerOpen = true;
|
||||
this.actionBarVisible = !this.actionBarVisible;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -216,6 +216,7 @@ export class ReaderSettingsComponent implements OnInit, OnDestroy {
|
||||
this.readingDirection.emit(this.readingDirectionModel);
|
||||
this.clickToPaginateChanged.emit(this.user.preferences.bookReaderTapToPaginate);
|
||||
this.layoutModeUpdate.emit(this.user.preferences.bookReaderLayoutMode);
|
||||
this.immersiveMode.emit(this.user.preferences.bookReaderImmersiveMode);
|
||||
|
||||
this.resetSettings();
|
||||
} else {
|
||||
|
@ -192,8 +192,8 @@
|
||||
<div class="mb-3">
|
||||
<label for="publisher" class="form-label">Publisher</label>
|
||||
<app-typeahead (selectedData)="updatePerson($event, PersonRole.Publisher)" [settings]="getPersonsSettings(PersonRole.Publisher)"
|
||||
[(locked)]="metadata.publisherLocked" (onUnlock)="metadata.publisherLocked = false"
|
||||
(newItemAdded)="metadata.publisherLocked = true" (selectedData)="metadata.publisherLocked = true">
|
||||
[(locked)]="metadata.publishersLocked" (onUnlock)="metadata.publishersLocked = false"
|
||||
(newItemAdded)="metadata.publishersLocked = true" (selectedData)="metadata.publishersLocked = true">
|
||||
<ng-template #badgeItem let-item let-position="idx">
|
||||
{{item.name}}
|
||||
</ng-template>
|
||||
|
@ -1,9 +1,17 @@
|
||||
.bulk-select {
|
||||
background-color: var(--navbar-bg-color);
|
||||
border-bottom: 2px solid var(--primary-color);
|
||||
color: var(--navbar-text-color);
|
||||
color: var(--bulk-selection-text-color) !important;
|
||||
|
||||
.btn-icon {
|
||||
color: var(--bulk-selection-text-color);
|
||||
}
|
||||
}
|
||||
|
||||
.highlight {
|
||||
color: var(--primary-color) !important;
|
||||
color: var(--bulk-selection-highlight-text-color) !important;
|
||||
}
|
||||
|
||||
::ng-deep button i.fa {
|
||||
color: var(--bulk-selection-text-color);
|
||||
}
|
@ -35,8 +35,8 @@
|
||||
<ng-container [ngTemplateOutlet]="paginationTemplate" [ngTemplateOutletContext]="{ id: 'bottom' }"></ng-container>
|
||||
|
||||
<ng-template #cardTemplate>
|
||||
<div class="row justify-content-evenly justify-content-sm-start g-0 mb-3">
|
||||
<div class="col-auto ps-1 pe-1 mt-1 mb-1" *ngFor="let item of items; trackBy:trackByIdentity; index as i">
|
||||
<div class="card-container row g-0 mt-3 mb-3">
|
||||
<div class="card col-auto mt-2 mb-2" *ngFor="let item of items; trackBy:trackByIdentity; index as i">
|
||||
<ng-container [ngTemplateOutlet]="itemTemplate" [ngTemplateOutletContext]="{ $implicit: item, idx: i }"></ng-container>
|
||||
</div>
|
||||
|
||||
|
@ -0,0 +1,6 @@
|
||||
.card-container {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, 158px);
|
||||
grid-gap: 0.5rem;
|
||||
justify-content: space-around;
|
||||
}
|
@ -4,7 +4,7 @@ import { FilterSettings } from 'src/app/metadata-filter/filter-settings';
|
||||
import { Breakpoint, UtilityService } from 'src/app/shared/_services/utility.service';
|
||||
import { Library } from 'src/app/_models/library';
|
||||
import { Pagination } from 'src/app/_models/pagination';
|
||||
import { FilterEvent, FilterItem, SeriesFilter, SortField } from 'src/app/_models/series-filter';
|
||||
import { FilterEvent, FilterItem, SeriesFilter } from 'src/app/_models/series-filter';
|
||||
import { ActionItem } from 'src/app/_services/action-factory.service';
|
||||
import { SeriesService } from 'src/app/_services/series.service';
|
||||
|
||||
|
@ -25,9 +25,8 @@ $image-width: 160px;
|
||||
padding-right: 0px;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
background-color: var(--card-bg-color);
|
||||
color: var(--card-text-color);
|
||||
border-color: var(--card-border-color);
|
||||
border: 1px var(--card-border-color);
|
||||
|
||||
}
|
||||
|
||||
@ -150,6 +149,11 @@ $image-width: 160px;
|
||||
|
||||
.card-body {
|
||||
padding: 5px !important;
|
||||
background-color: var(--card-bg-color);
|
||||
border-width: var(--card-border-width);
|
||||
border-style: var(--card-border-style);
|
||||
border-color: var(--card-border-color);
|
||||
border-radius: 0.25em;
|
||||
}
|
||||
|
||||
.library {
|
||||
|
@ -1,9 +1,8 @@
|
||||
<app-side-nav-companion-bar [hasFilter]="false" (filterOpen)="filterOpen.emit($event)">
|
||||
<h2 title>
|
||||
<app-card-actionables [actions]="collectionTagActions"></app-card-actionables>
|
||||
Collections
|
||||
</h2>
|
||||
<h6 subtitle style="margin-left:40px;">{{collections.length}} Items</h6>
|
||||
<h6 subtitle>{{collections.length}} Items</h6>
|
||||
</app-side-nav-companion-bar>
|
||||
<app-card-detail-layout
|
||||
[isLoading]="isLoading"
|
||||
|
@ -1,6 +1,6 @@
|
||||
<app-side-nav-companion-bar [hasFilter]="true" [filterOpenByDefault]="filterSettings.openByDefault" (filterOpen)="filterOpen.emit($event)" [filterActive]="filterActive">
|
||||
<h2 title>
|
||||
<app-card-actionables [actions]="actions"></app-card-actionables>
|
||||
<app-card-actionables [actions]="actions" (actionHandler)="performAction($event)"></app-card-actionables>
|
||||
{{libraryName}}
|
||||
</h2>
|
||||
<h6 subtitle style="margin-left:40px;" *ngIf="active.fragment === ''">{{pagination?.totalItems}} Series</h6>
|
||||
|
@ -3,7 +3,6 @@
|
||||
width: 200;
|
||||
}
|
||||
|
||||
|
||||
.viewport {
|
||||
width: 600px;
|
||||
height: 100%;
|
||||
|
@ -146,8 +146,10 @@ export class LibraryDetailComponent implements OnInit, OnDestroy {
|
||||
if (library === undefined) {
|
||||
lib = {id: this.libraryId, name: this.libraryName};
|
||||
}
|
||||
console.log('lib: ', lib);
|
||||
switch (action) {
|
||||
case(Action.ScanLibrary):
|
||||
console.log('action handler');
|
||||
this.actionService.scanLibrary(lib);
|
||||
break;
|
||||
case(Action.RefreshMetadata):
|
||||
@ -158,6 +160,12 @@ export class LibraryDetailComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
performAction(action: ActionItem<any>) {
|
||||
if (typeof action.callback === 'function') {
|
||||
action.callback(action.action, undefined);
|
||||
}
|
||||
}
|
||||
|
||||
updateFilter(data: FilterEvent) {
|
||||
this.filter = data.filter;
|
||||
|
||||
|
@ -38,6 +38,7 @@
|
||||
|
||||
<ng-container *ngIf="genre$ | async as genre">
|
||||
<ng-container *ngIf="moreIn$ | async as moreIn">
|
||||
<ng-container *ngIf="moreIn.length > 1">
|
||||
<app-carousel-reel [items]="moreIn" title="More In {{genre.title}}">
|
||||
<ng-template #carouselItem let-item let-position="idx">
|
||||
<app-series-card [data]="item" [libraryId]="item.libraryId" [suppressLibraryLink]="libraryId !== 0" (reload)="reloadInProgress($event)" (dataChanged)="reloadInProgress($event)"></app-series-card>
|
||||
@ -45,3 +46,4 @@
|
||||
</app-carousel-reel>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
|
@ -41,13 +41,13 @@
|
||||
|
||||
<div class="pagination-area">
|
||||
<!-- Pagination controls and screen hints-->
|
||||
<div class="{{readerMode === ReaderMode.LeftRight ? 'left' : 'top'}} {{clickOverlayClass('left')}}" (click)="handlePageChange($event, 'left')" [ngStyle]="{'height': (readerMode === ReaderMode.LeftRight ? WindowHeight: 25 + '%')}">
|
||||
<div class="{{readerMode === ReaderMode.LeftRight ? 'left' : 'top'}} {{clickOverlayClass('left')}}" (click)="handlePageChange($event, 'left')" [ngStyle]="{'height': (readerMode === ReaderMode.LeftRight ? ImageHeight: 25 + '%')}">
|
||||
<div *ngIf="showClickOverlay">
|
||||
<i class="fa fa-angle-{{readingDirection === ReadingDirection.RightToLeft ? 'double-' : ''}}{{readerMode === ReaderMode.LeftRight ? 'left' : 'up'}}"
|
||||
title="Previous Page" aria-hidden="true"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="{{readerMode === ReaderMode.LeftRight ? 'right' : 'bottom'}} {{clickOverlayClass('right')}}" (click)="handlePageChange($event, 'right')" [ngStyle]="{'height': (readerMode === ReaderMode.LeftRight ? WindowHeight: 25 + '%')}">
|
||||
<div class="{{readerMode === ReaderMode.LeftRight ? 'right' : 'bottom'}} {{clickOverlayClass('right')}}" (click)="handlePageChange($event, 'right')" [ngStyle]="{'height': (readerMode === ReaderMode.LeftRight ? ImageHeight: 25 + '%')}">
|
||||
<div *ngIf="showClickOverlay">
|
||||
<i class="fa fa-angle-{{readingDirection === ReadingDirection.LeftToRight ? 'double-' : ''}}{{readerMode === ReaderMode.LeftRight ? 'right' : 'down'}}"
|
||||
title="Next Page" aria-hidden="true"></i>
|
||||
@ -60,7 +60,7 @@
|
||||
'fit-to-height-double-offset': this.generalSettingsForm.get('fittingOption')?.value === FITTING_OPTION.HEIGHT && ShouldRenderDoublePage,
|
||||
'original-double-offset' : this.generalSettingsForm.get('fittingOption')?.value === FITTING_OPTION.ORIGINAL && ShouldRenderDoublePage,
|
||||
'reverse': ShouldRenderReverseDouble}">
|
||||
<img [src]="canvasImage.src" id="image-1"
|
||||
<img #image [src]="canvasImage.src" id="image-1"
|
||||
class="{{getFittingOptionClass()}} {{readerMode === ReaderMode.LeftRight || readerMode === ReaderMode.UpDown ? '' : 'd-none'}} {{showClickOverlay ? 'blur' : ''}}">
|
||||
|
||||
<ng-container *ngIf="ShouldRenderDoublePage && (this.pageNum + 1 <= maxPages - 1 && this.pageNum > 0)">
|
||||
|
@ -118,6 +118,7 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
@ViewChild('reader') reader!: ElementRef;
|
||||
@ViewChild('readingArea') readingArea!: ElementRef;
|
||||
@ViewChild('content') canvas: ElementRef | undefined;
|
||||
@ViewChild('image') image!: ElementRef;
|
||||
private ctx!: CanvasRenderingContext2D;
|
||||
/**
|
||||
* Used to render a page on the canvas or in the image tag. This Image element is prefetched by the cachedImages buffer
|
||||
@ -286,6 +287,9 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
return this.readingArea?.nativeElement.scrollHeight + 'px';
|
||||
}
|
||||
|
||||
get ImageHeight() {
|
||||
return this.image?.nativeElement.height + 'px';
|
||||
}
|
||||
|
||||
get splitIconClass() {
|
||||
if (this.isSplitLeftToRight()) {
|
||||
@ -438,6 +442,7 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
}
|
||||
this.ctx = this.canvas.nativeElement.getContext('2d', { alpha: false });
|
||||
this.canvasImage.onload = () => this.renderPage();
|
||||
this.getWindowDimensions();
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
@ -1050,6 +1055,8 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
|| document.documentElement.clientHeight
|
||||
|| document.body.clientHeight;
|
||||
|
||||
console.log(windowHeight);
|
||||
|
||||
const needsSplitting = this.isCoverImage();
|
||||
let newScale = this.generalSettingsForm.get('fittingOption')?.value;
|
||||
const widthRatio = windowWidth / (this.canvasImage.width / (needsSplitting ? 2 : 1));
|
||||
@ -1106,8 +1113,6 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
|
||||
|
||||
loadPage() {
|
||||
if (!this.canvas || !this.ctx) { return; }
|
||||
|
||||
this.isLoading = true;
|
||||
|
||||
this.canvasImage = this.cachedImages.current();
|
||||
@ -1128,6 +1133,7 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
this.renderPage();
|
||||
}
|
||||
this.prefetch();
|
||||
this.isLoading = false;
|
||||
}
|
||||
|
||||
setReadingDirection() {
|
||||
@ -1264,6 +1270,12 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
break;
|
||||
}
|
||||
|
||||
// We must set this here because loadPage from render doesn't call if we aren't page splitting
|
||||
if (this.readerMode !== ReaderMode.Webtoon) {
|
||||
this.canvasImage = this.cachedImages.current();
|
||||
this.isLoading = true;
|
||||
}
|
||||
|
||||
this.updateForm();
|
||||
|
||||
this.render();
|
||||
|
@ -85,3 +85,7 @@
|
||||
.scroll-to-top:hover {
|
||||
animation: MoveUpDown 1s linear infinite;
|
||||
}
|
||||
|
||||
.text-light {
|
||||
color: var(--search-result-text-lite-color) !important;
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
<app-side-nav-companion-bar pageHeader="Home">
|
||||
<h2 title>
|
||||
<app-card-actionables [actions]="actions"></app-card-actionables>
|
||||
Reading Lists
|
||||
</h2>
|
||||
<h6 subtitle>{{pagination?.totalItems}} Items</h6>
|
||||
|
@ -72,14 +72,14 @@
|
||||
<li [ngbNavItem]="TabID.Storyline" *ngIf="libraryType !== LibraryType.Book && (volumes.length > 0 || chapters.length > 0)">
|
||||
<a ngbNavLink>Storyline</a>
|
||||
<ng-template ngbNavContent>
|
||||
<div class="row g-0">
|
||||
<div class="card-container row g-0">
|
||||
<ng-container *ngFor="let volume of volumes; let idx = index; trackBy: trackByVolumeIdentity">
|
||||
<app-card-item class="col-auto p-2" *ngIf="volume.number != 0" [entity]="volume" [title]="volume.name" (click)="openVolume(volume)"
|
||||
<app-card-item class="col-auto mt-2 mb-2" *ngIf="volume.number != 0" [entity]="volume" [title]="volume.name" (click)="openVolume(volume)"
|
||||
[imageUrl]="imageService.getVolumeCoverImage(volume.id) + '&offset=' + coverImageOffset"
|
||||
[read]="volume.pagesRead" [total]="volume.pages" [actions]="volumeActions" (selection)="bulkSelectionService.handleCardSelection('volume', idx, volumes.length, $event)" [selected]="bulkSelectionService.isCardSelected('volume', idx)" [allowSelection]="true"></app-card-item>
|
||||
</ng-container>
|
||||
<ng-container *ngFor="let chapter of storyChapters; let idx = index; trackBy: trackByChapterIdentity">
|
||||
<app-card-item class="col-auto p-2" *ngIf="!chapter.isSpecial" [entity]="chapter" [title]="chapter.title" (click)="openChapter(chapter)"
|
||||
<app-card-item class="col-auto mt-2 mb-2" *ngIf="!chapter.isSpecial" [entity]="chapter" [title]="chapter.title" (click)="openChapter(chapter)"
|
||||
[imageUrl]="imageService.getChapterCoverImage(chapter.id) + '&offset=' + coverImageOffset"
|
||||
[read]="chapter.pagesRead" [total]="chapter.pages" [actions]="chapterActions" (selection)="bulkSelectionService.handleCardSelection('chapter', idx, storyChapters.length, $event)" [selected]="bulkSelectionService.isCardSelected('chapter', idx)" [allowSelection]="true"></app-card-item>
|
||||
</ng-container>
|
||||
@ -89,51 +89,44 @@
|
||||
<li [ngbNavItem]="TabID.Volumes" *ngIf="volumes.length > 0">
|
||||
<a ngbNavLink>{{libraryType === LibraryType.Book ? 'Books': 'Volumes'}}</a>
|
||||
<ng-template ngbNavContent>
|
||||
|
||||
<app-card-detail-layout
|
||||
[isLoading]="isLoading"
|
||||
[items]="volumes">
|
||||
<ng-template #cardItem let-item let-position="idx">
|
||||
<app-card-item class="col-auto" [entity]="item" [title]="item.name" (click)="openVolume(item)"
|
||||
<div class="card-container row g-0">
|
||||
<ng-container *ngFor="let item of volumes; let idx = index; trackBy: trackByVolumeIdentity">
|
||||
<app-card-item class="col-auto mt-2 mb-2" [entity]="item" [title]="item.name" (click)="openVolume(item)"
|
||||
[imageUrl]="imageService.getVolumeCoverImage(item.id) + '&offset=' + coverImageOffset"
|
||||
[read]="item.pagesRead" [total]="item.pages" [actions]="volumeActions"
|
||||
(selection)="bulkSelectionService.handleCardSelection('volume', position, volumes.length, $event)"
|
||||
[selected]="bulkSelectionService.isCardSelected('volume', position)" [allowSelection]="true">
|
||||
(selection)="bulkSelectionService.handleCardSelection('volume', idx, volumes.length, $event)"
|
||||
[selected]="bulkSelectionService.isCardSelected('volume', idx)" [allowSelection]="true">
|
||||
</app-card-item>
|
||||
</ng-template>
|
||||
</app-card-detail-layout>
|
||||
</ng-container>
|
||||
</div>
|
||||
</ng-template>
|
||||
</li>
|
||||
<li [ngbNavItem]="TabID.Chapters" *ngIf="chapters.length > 0">
|
||||
<a ngbNavLink>{{utilityService.formatChapterName(libraryType) + 's'}}</a>
|
||||
<ng-template ngbNavContent>
|
||||
<app-card-detail-layout
|
||||
[isLoading]="isLoading"
|
||||
[items]="chapters">
|
||||
<ng-template #cardItem let-item let-position="idx">
|
||||
<app-card-item class="col-auto" *ngIf="!item.isSpecial" [entity]="item" [title]="item.title" (click)="openChapter(item)"
|
||||
<div class="card-container row g-0">
|
||||
<ng-container *ngFor="let item of chapters; let idx = index; trackBy: trackByChapterIdentity">
|
||||
<app-card-item class="col-auto mt-2 mb-2" *ngIf="!item.isSpecial" [entity]="item" [title]="item.title" (click)="openChapter(item)"
|
||||
[imageUrl]="imageService.getChapterCoverImage(item.id) + '&offset=' + coverImageOffset"
|
||||
[read]="item.pagesRead" [total]="item.pages" [actions]="chapterActions"
|
||||
(selection)="bulkSelectionService.handleCardSelection('chapter', position, chapters.length, $event)"
|
||||
[selected]="bulkSelectionService.isCardSelected('chapter', position)" [allowSelection]="true"></app-card-item>
|
||||
</ng-template>
|
||||
</app-card-detail-layout>
|
||||
(selection)="bulkSelectionService.handleCardSelection('chapter', idx, chapters.length, $event)"
|
||||
[selected]="bulkSelectionService.isCardSelected('chapter', idx)" [allowSelection]="true"></app-card-item>
|
||||
</ng-container>
|
||||
</div>
|
||||
</ng-template>
|
||||
</li>
|
||||
<li [ngbNavItem]="TabID.Specials" *ngIf="hasSpecials">
|
||||
<a ngbNavLink>Specials</a>
|
||||
<ng-template ngbNavContent>
|
||||
<app-card-detail-layout
|
||||
[isLoading]="isLoading"
|
||||
[items]="specials">
|
||||
<ng-template #cardItem let-item let-position="idx">
|
||||
<app-card-item class="col-auto" [entity]="item" [title]="item.title || item.range" (click)="openChapter(item)"
|
||||
<div class="card-container row g-0">
|
||||
<ng-container *ngFor="let item of specials; let idx = index; trackBy: trackByChapterIdentity">
|
||||
<app-card-item class="col-auto mt-2 mb-2" [entity]="item" [title]="item.title || item.range" (click)="openChapter(item)"
|
||||
[imageUrl]="imageService.getChapterCoverImage(item.id)"
|
||||
[read]="item.pagesRead" [total]="item.pages" [actions]="chapterActions"
|
||||
(selection)="bulkSelectionService.handleCardSelection('special', position, chapters.length, $event)"
|
||||
[selected]="bulkSelectionService.isCardSelected('special', position)" [allowSelection]="true"></app-card-item>
|
||||
</ng-template>
|
||||
</app-card-detail-layout>
|
||||
(selection)="bulkSelectionService.handleCardSelection('special', idx, chapters.length, $event)"
|
||||
[selected]="bulkSelectionService.isCardSelected('special', idx)" [allowSelection]="true"></app-card-item>
|
||||
</ng-container>
|
||||
</div>
|
||||
</ng-template>
|
||||
</li>
|
||||
<li [ngbNavItem]="TabID.Related" *ngIf="hasRelations">
|
||||
|
@ -6,3 +6,10 @@
|
||||
margin-top: 2px;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.card-container{
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, 158px);
|
||||
grid-gap: 0.5rem;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
<form [formGroup]="typeaheadForm">
|
||||
<div class="input-group {{hasFocus ? 'open': ''}} {{locked ? 'lock-active' : ''}}">
|
||||
<ng-container *ngIf="settings.showLocked">
|
||||
<span class="input-group-text clickable" (click)="unlock($event)"><i class="fa fa-lock" aria-hidden="true"></i>
|
||||
<span class="input-group-text clickable" (click)="toggleLock($event)"><i class="fa fa-lock" aria-hidden="true"></i>
|
||||
<span class="visually-hidden">Field is locked</span>
|
||||
</span>
|
||||
</ng-container>
|
||||
|
@ -464,11 +464,14 @@ export class TypeaheadComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
unlock(event: any) {
|
||||
toggleLock(event: any) {
|
||||
if (this.disabled) return;
|
||||
this.locked = !this.locked;
|
||||
this.onUnlock.emit();
|
||||
this.lockedChange.emit(this.locked);
|
||||
|
||||
if (!this.locked) {
|
||||
this.onUnlock.emit();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -36,7 +36,6 @@
|
||||
@import './theme/components/progress';
|
||||
@import './theme/components/sidenav';
|
||||
@import './theme/components/carousel';
|
||||
@import './theme/components/progress';
|
||||
|
||||
|
||||
@import './theme/utilities/utilities';
|
||||
|
@ -3,6 +3,7 @@
|
||||
color: var(--card-text-color);
|
||||
border-color: var(--card-border-color);
|
||||
position: relative;
|
||||
border: var(--card-border);
|
||||
|
||||
.card-overlay {
|
||||
background-color: var(--card-overlay-bg-color);
|
||||
|
@ -184,7 +184,9 @@
|
||||
/* Card */
|
||||
--card-bg-color: rgba(22,27,34,0.5);
|
||||
--card-text-color: var(--body-text-color);
|
||||
--card-border-color: rgba(239, 239, 239, 0.125);
|
||||
--card-border-width: 0 1px 1px 1px;
|
||||
--card-border-style: solid;
|
||||
--card-border-color: transparent;
|
||||
--card-progress-bar-color: var(--primary-color);
|
||||
--card-overlay-bg-color: rgba(0, 0, 0, 0);
|
||||
--card-overlay-hover-bg-color: rgba(0, 0, 0, 0.2);
|
||||
@ -223,4 +225,12 @@
|
||||
--event-widget-text-color: var(--body-text-color);
|
||||
--event-widget-item-border-color: rgba(53, 53, 53, 0.5);
|
||||
--event-widget-border-color: rgba(1, 4, 9, 0.5);
|
||||
|
||||
/* Search */
|
||||
--search-result-text-lite-color: initial;
|
||||
|
||||
/* Bulk Selection */
|
||||
--bulk-selection-text-color: var(--navbar-text-color);
|
||||
--bulk-selection-highlight-text-color: var(--primary-color);
|
||||
|
||||
}
|
||||
|
@ -19,7 +19,7 @@
|
||||
/* Inputs */
|
||||
--input-bg-color: #fff;
|
||||
--input-focused-border-color: #ccc;
|
||||
--input-bg-readonly-color: unset;
|
||||
--input-bg-readonly-color: rgba(0,0,0,0.2);
|
||||
--input-placeholder-color: #aeaeae;
|
||||
--input-border-color: #ccc;
|
||||
--input-range-color: var(--primary-color);
|
||||
@ -113,6 +113,8 @@
|
||||
|
||||
/* Card */
|
||||
--card-text-color: #000;
|
||||
--card-border-width: 0 1px 1px 1px;
|
||||
--card-border-style: solid;
|
||||
--card-border-color: #ccc;
|
||||
--card-progress-bar-color: var(--primary-color);
|
||||
--card-overlay-hover-bg-color: rgba(0, 0, 0, 0.2);
|
||||
@ -178,4 +180,10 @@
|
||||
--popover-bg-color: lightgrey;
|
||||
--popover-border-color: lightgrey;
|
||||
|
||||
/* Search */
|
||||
--search-result-text-lite-color: rgba(0,0,0,1);
|
||||
|
||||
/* Bulk Selection */
|
||||
--bulk-selection-text-color: white;
|
||||
--bulk-selection-highlight-text-color: white;
|
||||
}
|
||||
|
@ -19,7 +19,7 @@
|
||||
/* Inputs */
|
||||
--input-bg-color: #fff;
|
||||
--input-focused-border-color: #ccc;
|
||||
--input-bg-readonly-color: unset;
|
||||
--input-bg-readonly-color: rgba(0,0,0,0.2);
|
||||
--input-placeholder-color: #aeaeae;
|
||||
--input-border-color: #ccc;
|
||||
--input-range-color: var(--primary-color);
|
||||
@ -114,6 +114,8 @@
|
||||
|
||||
/* Card */
|
||||
--card-text-color: #000;
|
||||
--card-border-width: 0 1px 1px 1px;
|
||||
--card-border-style: solid;
|
||||
--card-border-color: #ccc;
|
||||
--card-progress-bar-color: var(--primary-color);
|
||||
--card-overlay-bg-color: rgba(0, 0, 0, 0);
|
||||
@ -122,7 +124,7 @@
|
||||
/* List items */
|
||||
--list-group-item-text-color: var(--body-text-color);
|
||||
--list-group-item-bg-color: white;
|
||||
--list-group-hover-text-color: inherit;
|
||||
--list-group-hover-text-color: var(--body-text-color);
|
||||
--list-group-hover-bg-color: #eaeaea;
|
||||
--list-group-item-border-color: rgba(239, 239, 239, 0.125);
|
||||
--list-group-active-border-color: none;
|
||||
@ -192,4 +194,10 @@
|
||||
--accordion-button-focus-border-color: rgba(74, 198, 148, 0.9);
|
||||
--accordion-button-focus-box-shadow: unset;
|
||||
|
||||
/* Search */
|
||||
--search-result-text-lite-color: rgba(0,0,0,1);
|
||||
|
||||
/* Bulk Selection */
|
||||
--bulk-selection-text-color: var(--navbar-text-color);
|
||||
--bulk-selection-highlight-text-color: var(--primary-color);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user