This commit is contained in:
Joe Milazzo 2024-11-20 07:17:36 -06:00 committed by GitHub
parent cb810a2d8f
commit 3e3b6ba92b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
26 changed files with 1631 additions and 212 deletions

View file

@ -1,7 +1,6 @@
import {inject, Injectable, OnDestroy} from '@angular/core';
import {inject, Injectable} from '@angular/core';
import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
import { ToastrService } from 'ngx-toastr';
import {Subject, tap} from 'rxjs';
import { take } from 'rxjs/operators';
import { BulkAddToCollectionComponent } from '../cards/_modals/bulk-add-to-collection/bulk-add-to-collection.component';
import { AddToListModalComponent, ADD_FLOW } from '../reading-list/_modals/add-to-list-modal/add-to-list-modal.component';
@ -22,7 +21,6 @@ import { SeriesService } from './series.service';
import {translate} from "@jsverse/transloco";
import {UserCollection} from "../_models/collection-tag";
import {CollectionTagService} from "./collection-tag.service";
import {SmartFilter} from "../_models/metadata/v2/smart-filter";
import {FilterService} from "./filter.service";
import {ReadingListService} from "./reading-list.service";
import {ChapterService} from "./chapter.service";
@ -468,6 +466,16 @@ export class ActionService {
});
}
async deleteMultipleChapters(seriesId: number, chapterIds: Array<Chapter>, callback?: BooleanActionCallback) {
if (!await this.confirmService.confirm(translate('toasts.confirm-delete-multiple-chapters'))) return;
this.chapterService.deleteMultipleChapters(seriesId, chapterIds.map(c => c.id)).subscribe(() => {
if (callback) {
callback(true);
}
});
}
/**
* Deletes multiple collections
* @param readingLists ReadingList, should have id

View file

@ -21,6 +21,10 @@ export class ChapterService {
return this.httpClient.delete<boolean>(this.baseUrl + 'chapter?chapterId=' + chapterId);
}
deleteMultipleChapters(seriesId: number, chapterIds: Array<number>) {
return this.httpClient.post<boolean>(this.baseUrl + `chapter/delete-multiple?seriesId=${seriesId}`, {chapterIds});
}
updateChapter(chapter: Chapter) {
return this.httpClient.post(this.baseUrl + 'chapter/update', chapter, TextResonse);
}

View file

@ -15,7 +15,6 @@
scrollbar-width: thin;
mask-image: linear-gradient(to bottom, transparent, black 0%, black 95%, transparent 100%);
-webkit-mask-image: linear-gradient(to bottom, transparent, black 0%, black 95%, transparent 100%);
//margin-top: 7px;
// For firefox
@supports (-moz-appearance:none) {

View file

@ -138,11 +138,14 @@ export class BulkSelectionService {
return ret;
}
/**
* Returns the appropriate set of supported actions for the given mix of cards
* @param callback
*/
getActions(callback: (action: ActionItem<any>, data: any) => void) {
// checks if series is present. If so, returns only series actions
// else returns volume/chapter items
const allowedActions = [Action.AddToReadingList, Action.MarkAsRead, Action.MarkAsUnread, Action.AddToCollection,
Action.Delete, Action.AddToWantToReadList, Action.RemoveFromWantToReadList];
if (Object.keys(this.selectedCards).filter(item => item === 'series').length > 0) {
return this.applyFilterToList(this.actionFactory.getSeriesActions(callback), allowedActions);
}
@ -163,7 +166,8 @@ export class BulkSelectionService {
return this.applyFilterToList(this.actionFactory.getReadingListActions(callback), [Action.Promote, Action.UnPromote, Action.Delete]);
}
return this.applyFilterToList(this.actionFactory.getVolumeActions(callback), allowedActions);
// Chapter/Volume
return this.applyFilterToList(this.actionFactory.getVolumeActions(callback), [...allowedActions, Action.SendTo]);
}
private debugLog(message: string, extraData?: any) {
@ -177,18 +181,25 @@ export class BulkSelectionService {
}
private applyFilter(action: ActionItem<any>, allowedActions: Array<Action>) {
let hasValidAction = false;
let ret = false;
// Check if the current action is valid or a submenu
if (action.action === Action.Submenu || allowedActions.includes(action.action)) {
// Do something
ret = true;
hasValidAction = true;
}
if (action.children === null || action.children?.length === 0) return ret;
// If the action has children, filter them recursively
if (action.children && action.children.length > 0) {
action.children = action.children.filter((childAction) => this.applyFilter(childAction, allowedActions));
action.children = action.children.filter((childAction) => this.applyFilter(childAction, allowedActions));
// If no valid children remain, the parent submenu should not be considered valid
if (action.children.length === 0 && action.action === Action.Submenu) {
hasValidAction = false;
}
}
return ret;
// Return whether this action or its children are valid
return hasValidAction;
}
private applyFilterToList(list: Array<ActionItem<any>>, allowedActions: Array<Action>): Array<ActionItem<any>> {

View file

@ -7,13 +7,8 @@ import {
OnInit,
ViewChild
} from '@angular/core';
import {BulkOperationsComponent} from "../cards/bulk-operations/bulk-operations.component";
import {TagBadgeComponent} from "../shared/tag-badge/tag-badge.component";
import {AsyncPipe, DecimalPipe, DOCUMENT, NgStyle, NgClass, DatePipe, Location} from "@angular/common";
import {AsyncPipe, DOCUMENT, NgStyle, NgClass, DatePipe, Location} from "@angular/common";
import {CardActionablesComponent} from "../_single-module/card-actionables/card-actionables.component";
import {CarouselReelComponent} from "../carousel/_components/carousel-reel/carousel-reel.component";
import {ExternalSeriesCardComponent} from "../cards/external-series-card/external-series-card.component";
import {ImageComponent} from "../shared/image/image.component";
import {LoadingComponent} from "../shared/loading/loading.component";
import {
NgbDropdown,
@ -23,12 +18,8 @@ import {
NgbNav, NgbNavChangeEvent,
NgbNavContent, NgbNavItem,
NgbNavLink, NgbNavOutlet,
NgbProgressbar,
NgbTooltip
} from "@ng-bootstrap/ng-bootstrap";
import {PersonBadgeComponent} from "../shared/person-badge/person-badge.component";
import {ReviewCardComponent} from "../_single-module/review-card/review-card.component";
import {SeriesCardComponent} from "../cards/series-card/series-card.component";
import {VirtualScrollerModule} from "@iharbeck/ngx-virtual-scroller";
import {ActivatedRoute, Router, RouterLink} from "@angular/router";
import {ImageService} from "../_services/image.service";
@ -38,9 +29,6 @@ import {forkJoin, map, Observable, tap} from "rxjs";
import {SeriesService} from "../_services/series.service";
import {Series} from "../_models/series";
import {AgeRating} from "../_models/metadata/age-rating";
import {AgeRatingPipe} from "../_pipes/age-rating.pipe";
import {TimeDurationPipe} from "../_pipes/time-duration.pipe";
import {ExternalRatingComponent} from "../series-detail/_components/external-rating/external-rating.component";
import {LibraryType} from "../_models/library/library";
import {LibraryService} from "../_services/library.service";
import {ThemeService} from "../_services/theme.service";
@ -54,18 +42,13 @@ import {ReadMoreComponent} from "../shared/read-more/read-more.component";
import {DetailsTabComponent} from "../_single-module/details-tab/details-tab.component";
import {EntityTitleComponent} from "../cards/entity-title/entity-title.component";
import {EditChapterModalComponent} from "../_single-module/edit-chapter-modal/edit-chapter-modal.component";
import {ReadTimePipe} from "../_pipes/read-time.pipe";
import {FilterField} from "../_models/metadata/v2/filter-field";
import {FilterComparison} from "../_models/metadata/v2/filter-comparison";
import {FilterUtilitiesService} from "../shared/_services/filter-utilities.service";
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
import {DefaultValuePipe} from "../_pipes/default-value.pipe";
import {ReadingList} from "../_models/reading-list";
import {ReadingListService} from "../_services/reading-list.service";
import {CardItemComponent} from "../cards/card-item/card-item.component";
import {RelatedTabComponent} from "../_single-modules/related-tab/related-tab.component";
import {AgeRatingImageComponent} from "../_single-modules/age-rating-image/age-rating-image.component";
import {CompactNumberPipe} from "../_pipes/compact-number.pipe";
import {BadgeExpanderComponent} from "../shared/badge-expander/badge-expander.component";
import {
MetadataDetailRowComponent
@ -79,9 +62,7 @@ import {ChapterRemovedEvent} from "../_models/events/chapter-removed-event";
import {Action, ActionFactoryService, ActionItem} from "../_services/action-factory.service";
import {Device} from "../_models/device/device";
import {ActionService} from "../_services/action.service";
import {PublicationStatusPipe} from "../_pipes/publication-status.pipe";
import {DefaultDatePipe} from "../_pipes/default-date.pipe";
import {MangaFormatPipe} from "../_pipes/manga-format.pipe";
import {CoverImageComponent} from "../_single-module/cover-image/cover-image.component";
import {DefaultModalOptions} from "../_models/default-modal-options";
@ -95,13 +76,8 @@ enum TabID {
selector: 'app-chapter-detail',
standalone: true,
imports: [
BulkOperationsComponent,
AsyncPipe,
CardActionablesComponent,
CarouselReelComponent,
DecimalPipe,
ExternalSeriesCardComponent,
ImageComponent,
LoadingComponent,
NgbDropdown,
NgbDropdownItem,
@ -110,18 +86,10 @@ enum TabID {
NgbNav,
NgbNavContent,
NgbNavLink,
NgbProgressbar,
NgbTooltip,
PersonBadgeComponent,
ReviewCardComponent,
SeriesCardComponent,
TagBadgeComponent,
VirtualScrollerModule,
NgStyle,
NgClass,
AgeRatingPipe,
TimeDurationPipe,
ExternalRatingComponent,
TranslocoDirective,
ReadMoreComponent,
NgbNavItem,
@ -129,19 +97,12 @@ enum TabID {
DetailsTabComponent,
RouterLink,
EntityTitleComponent,
ReadTimePipe,
DefaultValuePipe,
CardItemComponent,
RelatedTabComponent,
AgeRatingImageComponent,
CompactNumberPipe,
BadgeExpanderComponent,
MetadataDetailRowComponent,
DownloadButtonComponent,
PublicationStatusPipe,
DatePipe,
DefaultDatePipe,
MangaFormatPipe,
CoverImageComponent
],
templateUrl: './chapter-detail.component.html',

View file

@ -13,7 +13,6 @@ import {
Component,
DestroyRef,
ElementRef,
HostListener,
Inject,
inject,
OnInit,
@ -37,7 +36,7 @@ import {
NgbTooltip
} from '@ng-bootstrap/ng-bootstrap';
import {ToastrService} from 'ngx-toastr';
import {catchError, forkJoin, Observable, of, tap} from 'rxjs';
import {catchError, debounceTime, forkJoin, Observable, of, ReplaySubject, tap} from 'rxjs';
import {map} from 'rxjs/operators';
import {BulkSelectionService} from 'src/app/cards/bulk-selection.service';
import {
@ -45,7 +44,7 @@ import {
EditSeriesModalComponent
} from 'src/app/cards/_modals/edit-series-modal/edit-series-modal.component';
import {DownloadEvent, DownloadService} from 'src/app/shared/_services/download.service';
import {Breakpoint, KEY_CODES, UtilityService} from 'src/app/shared/_services/utility.service';
import {Breakpoint, UtilityService} from 'src/app/shared/_services/utility.service';
import {Chapter, LooseLeafOrDefaultNumber, SpecialVolumeNumber} from 'src/app/_models/chapter';
import {Device} from 'src/app/_models/device/device';
import {ScanSeriesEvent} from 'src/app/_models/events/scan-series-event';
@ -247,6 +246,8 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
downloadInProgress: boolean = false;
nextExpectedChapter: NextExpectedChapter | undefined;
loadPageSource = new ReplaySubject<boolean>(1);
loadPage$ = this.loadPageSource.asObservable();
/**
* Track by function for Volume to tell when to refresh card data
@ -256,14 +257,6 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
* Track by function for Chapter to tell when to refresh card data
*/
trackByChapterIdentity = (index: number, item: Chapter) => `${item.title}_${item.minNumber}_${item.maxNumber}_${item.volumeId}_${item.pagesRead}`;
trackByRelatedSeriesIdentify = (index: number, item: RelatedSeriesPair) => `${item.series.name}_${item.series.libraryId}_${item.series.pagesRead}_${item.relation}`;
trackBySeriesIdentify = (index: number, item: Series) => `${item.name}_${item.libraryId}_${item.pagesRead}`;
trackByStoryLineIdentity = (index: number, item: StoryLineItem) => {
if (item.isChapter) {
return this.trackByChapterIdentity(index, item!.chapter!)
}
return this.trackByVolumeIdentity(index, item!.volume!);
};
/**
* Are there any related series
@ -307,7 +300,7 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
*/
download$: Observable<DownloadEvent | null> | null = null;
bulkActionCallback = (action: ActionItem<any>, data: any) => {
bulkActionCallback = async (action: ActionItem<any>, data: any) => {
if (this.series === undefined) {
return;
}
@ -355,6 +348,19 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
this.cdRef.markForCheck();
});
break;
case Action.SendTo:
const device = (action._extra!.data as Device);
this.actionService.sendToDevice(chapters.map(c => c.id), device);
this.bulkSelectionService.deselectAll();
this.cdRef.markForCheck();
break;
case Action.Delete:
await this.actionService.deleteMultipleChapters(seriesId, chapters, () => {
// No need to update the page as the backend will spam volume/chapter deletions
this.bulkSelectionService.deselectAll();
this.cdRef.markForCheck();
});
break;
}
}
@ -459,6 +465,8 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
return this.downloadService.mapToEntityType(events, this.series);
}));
this.loadPage$.pipe(takeUntilDestroyed(this.destroyRef), debounceTime(300), tap(val => this.loadSeries(this.seriesId, val))).subscribe();
this.messageHub.messages$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(event => {
if (event.event === EVENTS.SeriesRemoved) {
const seriesRemovedEvent = event.payload as SeriesRemovedEvent;
@ -469,7 +477,8 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
} else if (event.event === EVENTS.ScanSeries) {
const seriesScanEvent = event.payload as ScanSeriesEvent;
if (seriesScanEvent.seriesId === this.seriesId) {
this.loadSeries(this.seriesId);
//this.loadSeries(this.seriesId);
this.loadPageSource.next(false);
}
} else if (event.event === EVENTS.CoverUpdate) {
const coverUpdateEvent = event.payload as CoverUpdateEvent;
@ -479,7 +488,8 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
} else if (event.event === EVENTS.ChapterRemoved) {
const removedEvent = event.payload as ChapterRemovedEvent;
if (removedEvent.seriesId !== this.seriesId) return;
this.loadSeries(this.seriesId, false);
//this.loadSeries(this.seriesId, false);
this.loadPageSource.next(false);
}
});
@ -508,7 +518,8 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
}
}), takeUntilDestroyed(this.destroyRef)).subscribe();
this.loadSeries(this.seriesId, true);
//this.loadSeries(this.seriesId, true);
this.loadPageSource.next(true);
this.pageExtrasGroup.get('renderMode')?.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((val: PageLayoutMode | null) => {
if (val == null) return;
@ -535,12 +546,12 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
switch(action.action) {
case(Action.MarkAsRead):
this.actionService.markSeriesAsRead(series, (series: Series) => {
this.loadSeries(series.id);
this.loadPageSource.next(false);
});
break;
case(Action.MarkAsUnread):
this.actionService.markSeriesAsUnread(series, (series: Series) => {
this.loadSeries(series.id);
this.loadPageSource.next(false);
});
break;
case(Action.Scan):
@ -600,7 +611,7 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
case(Action.Delete):
await this.actionService.deleteVolume(volume.id, (b) => {
if (!b) return;
this.loadSeries(this.seriesId, false);
this.loadPageSource.next(false);
});
break;
case(Action.AddToReadingList):
@ -1010,7 +1021,7 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
ref.closed.subscribe((res: EditChapterModalCloseResult) => {
if (res.success && res.isDeleted) {
this.loadSeries(this.seriesId, false);
this.loadPageSource.next(false);
}
});
}
@ -1024,7 +1035,7 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
ref.closed.subscribe((res: EditChapterModalCloseResult) => {
if (res.success && res.isDeleted) {
this.loadSeries(this.seriesId, false);
this.loadPageSource.next(false);
}
});
}
@ -1035,9 +1046,9 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
modalRef.closed.subscribe((closeResult: EditSeriesModalCloseResult) => {
if (closeResult.success) {
window.scrollTo(0, 0);
this.loadSeries(this.seriesId, closeResult.updateExternal);
this.loadPageSource.next(closeResult.updateExternal);
} else if (closeResult.updateExternal) {
this.loadSeries(this.seriesId, closeResult.updateExternal);
this.loadPageSource.next(closeResult.updateExternal);
}
});
}

View file

@ -3,10 +3,12 @@
<div class="d-flex flex-column">
@if (HasCoverImage) {
<div class="mx-auto">
<app-image height="24px" width="24px" [styles]="{'background': 'none', 'max-height': '48px', 'height': '48px', 'width': '48px', 'border-radius': '50%'}"
[imageUrl]="ImageUrl"
[errorImage]="imageService.noPersonImage">
</app-image>
<a class="btn btn-icon p-0" routerLink="/person/{{person.name}}">
<app-image height="24px" width="24px" [styles]="{'background': 'none', 'max-height': '48px', 'height': '48px', 'width': '48px', 'border-radius': '50%'}"
[imageUrl]="ImageUrl"
[errorImage]="imageService.noPersonImage">
</app-image>
</a>
</div>
} @else {
<div style="background: none; max-height: 48px; height: 48px; width: 48px; border-radius: 50%" class="mx-auto">

View file

@ -8,7 +8,7 @@ import {
OnInit,
ViewChild
} from '@angular/core';
import {AsyncPipe, DecimalPipe, DOCUMENT, NgStyle, NgClass, DatePipe, Location} from "@angular/common";
import {AsyncPipe, DOCUMENT, NgStyle, NgClass, Location} from "@angular/common";
import {ActivatedRoute, Router, RouterLink} from "@angular/router";
import {ImageService} from "../_services/image.service";
import {SeriesService} from "../_services/series.service";
@ -30,7 +30,6 @@ import {
NgbNavItem,
NgbNavLink,
NgbNavOutlet,
NgbProgressbar,
NgbTooltip
} from "@ng-bootstrap/ng-bootstrap";
import {FilterUtilitiesService} from "../shared/_services/filter-utilities.service";
@ -49,19 +48,13 @@ import {LoadingComponent} from "../shared/loading/loading.component";
import {DetailsTabComponent} from "../_single-module/details-tab/details-tab.component";
import {ReadMoreComponent} from "../shared/read-more/read-more.component";
import {Person} from "../_models/metadata/person";
import {hasAnyCast, IHasCast} from "../_models/common/i-has-cast";
import {ReadTimePipe} from "../_pipes/read-time.pipe";
import {AgeRatingPipe} from "../_pipes/age-rating.pipe";
import {IHasCast} from "../_models/common/i-has-cast";
import {EntityTitleComponent} from "../cards/entity-title/entity-title.component";
import {ImageComponent} from "../shared/image/image.component";
import {CardItemComponent} from "../cards/card-item/card-item.component";
import {VirtualScrollerModule} from "@iharbeck/ngx-virtual-scroller";
import {Action, ActionFactoryService, ActionItem} from "../_services/action-factory.service";
import {Breakpoint, UtilityService} from "../shared/_services/utility.service";
import {ChapterCardComponent} from "../cards/chapter-card/chapter-card.component";
import {DefaultValuePipe} from "../_pipes/default-value.pipe";
import {
EditVolumeModalCloseResult,
EditVolumeModalComponent
} from "../_single-module/edit-volume-modal/edit-volume-modal.component";
import {Genre} from "../_models/metadata/genre";
@ -69,8 +62,6 @@ import {Tag} from "../_models/tag";
import {RelatedTabComponent} from "../_single-modules/related-tab/related-tab.component";
import {ReadingList} from "../_models/reading-list";
import {ReadingListService} from "../_services/reading-list.service";
import {AgeRatingImageComponent} from "../_single-modules/age-rating-image/age-rating-image.component";
import {CompactNumberPipe} from "../_pipes/compact-number.pipe";
import {BadgeExpanderComponent} from "../shared/badge-expander/badge-expander.component";
import {
MetadataDetailRowComponent
@ -85,8 +76,6 @@ import {CardActionablesComponent} from "../_single-module/card-actionables/card-
import {Device} from "../_models/device/device";
import {EditChapterModalComponent} from "../_single-module/edit-chapter-modal/edit-chapter-modal.component";
import {BulkOperationsComponent} from "../cards/bulk-operations/bulk-operations.component";
import {DefaultDatePipe} from "../_pipes/default-date.pipe";
import {MangaFormatPipe} from "../_pipes/manga-format.pipe";
import {CoverImageComponent} from "../_single-module/cover-image/cover-image.component";
import {DefaultModalOptions} from "../_models/default-modal-options";
@ -145,32 +134,20 @@ interface VolumeCast extends IHasCast {
NgbDropdownMenu,
NgbDropdown,
NgbDropdownToggle,
ReadTimePipe,
AgeRatingPipe,
EntityTitleComponent,
RouterLink,
NgbProgressbar,
DecimalPipe,
NgbTooltip,
ImageComponent,
NgStyle,
NgClass,
TranslocoDirective,
CardItemComponent,
VirtualScrollerModule,
ChapterCardComponent,
DefaultValuePipe,
RelatedTabComponent,
AgeRatingImageComponent,
CompactNumberPipe,
BadgeExpanderComponent,
MetadataDetailRowComponent,
DownloadButtonComponent,
CardActionablesComponent,
BulkOperationsComponent,
DatePipe,
DefaultDatePipe,
MangaFormatPipe,
CoverImageComponent
],
templateUrl: './volume-detail.component.html',
@ -226,7 +203,7 @@ export class VolumeDetailComponent implements OnInit {
volumeActions: Array<ActionItem<Volume>> = this.actionFactoryService.getVolumeActions(this.handleVolumeAction.bind(this));
chapterActions: Array<ActionItem<Chapter>> = this.actionFactoryService.getChapterActions(this.handleChapterActionCallback.bind(this));
bulkActionCallback = (action: ActionItem<Chapter>, data: any) => {
bulkActionCallback = async (action: ActionItem<Chapter>, _: any) => {
if (this.volume === null) {
return;
}
@ -256,6 +233,19 @@ export class VolumeDetailComponent implements OnInit {
this.cdRef.markForCheck();
});
break;
case Action.SendTo:
const device = (action._extra!.data as Device);
this.actionService.sendToDevice(selectedChapterIds.map(c => c.id), device);
this.bulkSelectionService.deselectAll();
this.cdRef.markForCheck();
break;
case Action.Delete:
await this.actionService.deleteMultipleChapters(this.seriesId, selectedChapterIds, () => {
// No need to update the page as the backend will spam volume/chapter deletions
this.bulkSelectionService.deselectAll();
this.cdRef.markForCheck();
});
break;
}
}
@ -609,14 +599,14 @@ export class VolumeDetailComponent implements OnInit {
});
break;
case Action.MarkAsRead:
this.actionService.markVolumeAsRead(this.seriesId, this.volume!, res => {
this.actionService.markVolumeAsRead(this.seriesId, this.volume!, _ => {
this.volume!.pagesRead = this.volume!.pages;
this.setContinuePoint();
this.cdRef.markForCheck();
});
break;
case Action.MarkAsUnread:
this.actionService.markVolumeAsUnread(this.seriesId, this.volume!, res => {
this.actionService.markVolumeAsUnread(this.seriesId, this.volume!, _ => {
this.volume!.pagesRead = 0;
this.setContinuePoint();
this.cdRef.markForCheck();

View file

@ -2397,6 +2397,7 @@
"confirm-regen-covers": "Refresh covers will force all cover images to be recalculated. This is a heavy operation. Are you sure you don't want to perform a Scan instead?",
"alert-long-running": "This is a long running process. Please give it the time to complete before invoking again.",
"confirm-delete-multiple-series": "Are you sure you want to delete {{count}} series? It will not modify files on disk.",
"confirm-delete-multiple-chapters": "Are you sure you want to delete {{count}} chapter/volumes? It will not modify files on disk.",
"confirm-delete-series": "Are you sure you want to delete this series? It will not modify files on disk.",
"confirm-delete-chapter": "Are you sure you want to delete this chapter? It will not modify files on disk.",
"confirm-delete-volume": "Are you sure you want to delete this volume? It will not modify files on disk.",

View file

@ -1,8 +1,10 @@
@import '../variables';
.navbar {
background-color: var(--navbar-bg-color);
color: var(--navbar-text-color);
z-index: 1040;
border-radius: 4px;
border-radius: var(--navbar-border-radius);
left: 0px;
margin: var(--navbar-header-margin);
padding: 0;
@ -20,6 +22,6 @@ i.fa.nav {
@media (max-width: $grid-breakpoints-lg) {
.navbar {
margin: 8px 12px;
margin: var(--navbar-header-mobile-x-margin) var(--navbar-header-mobile-y-margin);
}
}

View file

@ -105,8 +105,11 @@
--navbar-bg-color: black;
--navbar-text-color: white;
--navbar-fa-icon-color: white;
--navbar-border-radius: 0px; // 4px for Plex navbar
--navbar-btn-hover-outline-color: rgba(255, 255, 255, 1);
--navbar-header-margin: 0px; // 8px allows for the Plex navbar + --nav-offset: 56px;
--navbar-header-mobile-x-margin: 0px; // 8px allows for the Plex navbar
--navbar-header-mobile-y-margin: 0px; // 12px allows for the Plex navbar
/* Inputs */
--input-bg-color: #343a40;
@ -388,7 +391,7 @@
/* Bulk Selection */
--bulk-selection-text-color: var(--navbar-text-color);
--bulk-selection-highlight-text-color: var(--primary-color);
--bulk-selection-bg-color: var(--elevation-layer11-dark);
--bulk-selection-bg-color: black;
/* List Card Item */
--card-list-item-bg-color: linear-gradient(180deg, rgba(0,0,0,0.15) 0%, rgba(0,0,0,0.15) 1%, rgba(0,0,0,0) 100%);