Polish 7 (#3381)
This commit is contained in:
parent
cb810a2d8f
commit
3e3b6ba92b
26 changed files with 1631 additions and 212 deletions
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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>> {
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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.",
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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%);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue