-
-
-
+
+ @if(mobileSeriesImgBackground === 'true') {
+
+ } @else {
+
+ }
@if (series.pagesRead < series.pages && hasReadingProgress) {
@@ -29,7 +32,7 @@
-
+
{{series.name}}
@@ -52,7 +55,8 @@
[ageRating]="seriesMetadata.ageRating"
[hasReadingProgress]="hasReadingProgress"
[readingTimeEntity]="series"
- [libraryType]="libraryType">
+ [libraryType]="libraryType"
+ [mangaFormat]="series.format">
@@ -133,12 +137,13 @@
{{series.name}}
@@ -52,7 +55,8 @@
[ageRating]="seriesMetadata.ageRating"
[hasReadingProgress]="hasReadingProgress"
[readingTimeEntity]="series"
- [libraryType]="libraryType">
+ [libraryType]="libraryType"
+ [mangaFormat]="series.format">
@@ -133,12 +137,13 @@
-
+
{{t('writers-title')}}
+ [allowToggle]="false"
+ (toggle)="switchTabsToDetail()">
{{item.name}}
@@ -162,12 +167,13 @@
-
+
{{t('genres-title')}}
+ [allowToggle]="false"
+ (toggle)="switchTabsToDetail()">
{{item.title}}
@@ -180,7 +186,8 @@
+ [allowToggle]="false"
+ (toggle)="switchTabsToDetail()">
{{item.title}}
@@ -189,45 +196,11 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
@if (showStorylineTab) {
-
@@ -316,138 +289,16 @@
}
- @if (hasRelations && relationShips) {
+ @if (hasRelations || readingLists.length > 0 || collections.length > 0) {
-
{{t(TabID.Related)}}
- {{relations.length}}
+ {{relations.length + readingLists.length + collections.length}}
@defer (when activeTabId === TabID.Related; prefetch on idle) {
-
-
- @for(item of scroll.viewPortItems; let idx = $index; track item.id) {
-
- }
-
-
+
}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
}
diff --git a/UI/Web/src/app/series-detail/_components/series-detail/series-detail.component.scss b/UI/Web/src/app/series-detail/_components/series-detail/series-detail.component.scss
index 7cd23c73c..0a7b2cb3c 100644
--- a/UI/Web/src/app/series-detail/_components/series-detail/series-detail.component.scss
+++ b/UI/Web/src/app/series-detail/_components/series-detail/series-detail.component.scss
@@ -7,19 +7,7 @@
left: 20px;
}
-.under-image {
- background-color: var(--breadcrumb-bg-color);
- color: white;
- border-bottom-left-radius: 5px;
- border-bottom-right-radius: 5px;
- text-align: center;
-}
-//
-//.rating-star {
-// margin-top: 2px;
-// font-size: 1.5rem;
-//}
-//
+
.card-container{
display: grid;
grid-template-columns: repeat(auto-fill, 160px);
@@ -27,7 +15,7 @@
justify-content: space-around;
}
-::ng-deep .carousel-container .header i.fa-plus, ::ng-deep .carousel-container .header i.fa-pen{
+::ng-deep .carousel-container .header i.fa-plus, ::ng-deep .carousel-container .header i.fa-pen {
border-width: 1px;
border-style: solid;
border-radius: 5px;
@@ -40,13 +28,4 @@
}
}
-.upper-details {
- font-size: 0.9rem;
-}
-@media (max-width: 768px) {
- .carousel-tabs-container {
- mask-image: linear-gradient(transparent, black 0%, black 90%, transparent 100%);
- -webkit-mask-image: linear-gradient(to right, transparent, black 0%, black 90%, transparent 100%);
- }
-}
diff --git a/UI/Web/src/app/series-detail/_components/series-detail/series-detail.component.ts b/UI/Web/src/app/series-detail/_components/series-detail/series-detail.component.ts
index 533efe3cc..49805325c 100644
--- a/UI/Web/src/app/series-detail/_components/series-detail/series-detail.component.ts
+++ b/UI/Web/src/app/series-detail/_components/series-detail/series-detail.component.ts
@@ -98,10 +98,6 @@ import {
} from '../../../sidenav/_components/side-nav-companion-bar/side-nav-companion-bar.component';
import {translate, TranslocoDirective, TranslocoService} from "@jsverse/transloco";
import {CardActionablesComponent} from "../../../_single-module/card-actionables/card-actionables.component";
-import {ExternalSeries} from "../../../_models/series-detail/external-series";
-import {
- SeriesPreviewDrawerComponent
-} from "../../../_single-module/series-preview-drawer/series-preview-drawer.component";
import {PublicationStatus} from "../../../_models/metadata/publication-status";
import {NextExpectedChapter} from "../../../_models/series-detail/next-expected-chapter";
import {NextExpectedCardComponent} from "../../../cards/next-expected-card/next-expected-card.component";
@@ -143,11 +139,13 @@ import {MetadataDetailRowComponent} from "../metadata-detail-row/metadata-detail
import {DownloadButtonComponent} from "../download-button/download-button.component";
import {hasAnyCast} from "../../../_models/common/i-has-cast";
import {EditVolumeModalComponent} from "../../../_single-module/edit-volume-modal/edit-volume-modal.component";
+import {CoverUpdateEvent} from "../../../_models/events/cover-update-event";
+import {RelatedSeriesPair, RelatedTabComponent} from "../../../_single-modules/related-tab/related-tab.component";
+import {CollectionTagService} from "../../../_services/collection-tag.service";
+import {UserCollection} from "../../../_models/collection-tag";
+import {SeriesFormatComponent} from "../../../shared/series-format/series-format.component";
+import {MangaFormatPipe} from "../../../_pipes/manga-format.pipe";
-interface RelatedSeriesPair {
- series: Series;
- relation: RelationKind;
-}
enum TabID {
Related = 'related-tab',
@@ -176,12 +174,12 @@ interface StoryLineItem {
TagBadgeComponent, ImageComponent, NgbTooltip, NgbProgressbar, NgbDropdown, NgbDropdownToggle, NgbDropdownMenu,
NgbDropdownItem, CarouselReelComponent, ReviewCardComponent, BulkOperationsComponent,
NgbNav, NgbNavItem, NgbNavLink, NgbNavContent, VirtualScrollerModule, CardItemComponent,
- EntityTitleComponent, SeriesCardComponent, ExternalSeriesCardComponent, NgbNavOutlet,
+ EntityTitleComponent, SeriesCardComponent, ExternalSeriesCardComponent, NgbNavOutlet,
LoadingComponent, DecimalPipe, TranslocoDirective, NgTemplateOutlet, NextExpectedCardComponent,
NgClass, NgOptimizedImage, ProviderImagePipe, AsyncPipe, PersonBadgeComponent, DetailsTabComponent, ChapterCardComponent,
VolumeCardComponent, JsonPipe, AgeRatingPipe, DefaultValuePipe, ExternalRatingComponent, ReadMoreComponent, ReadTimePipe,
RouterLink, TimeAgoPipe, AgeRatingImageComponent, CompactNumberPipe, IconAndTitleComponent, SafeHtmlPipe, BadgeExpanderComponent,
- A11yClickDirective, ReadTimeLeftPipe, PublicationStatusPipe, MetadataDetailRowComponent, DownloadButtonComponent]
+ A11yClickDirective, ReadTimeLeftPipe, PublicationStatusPipe, MetadataDetailRowComponent, DownloadButtonComponent, RelatedTabComponent, SeriesFormatComponent, MangaFormatPipe]
})
export class SeriesDetailComponent implements OnInit, AfterContentChecked {
@@ -200,10 +198,9 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
private readonly actionService = inject(ActionService);
private readonly messageHub = inject(MessageHubService);
private readonly readingListService = inject(ReadingListService);
- private readonly offcanvasService = inject(NgbOffcanvas);
+ private readonly collectionTagService = inject(CollectionTagService);
private readonly cdRef = inject(ChangeDetectorRef);
private readonly scrollService = inject(ScrollService);
- private readonly deviceService = inject(DeviceService);
private readonly translocoService = inject(TranslocoService);
protected readonly bulkSelectionService = inject(BulkSelectionService);
protected readonly utilityService = inject(UtilityService);
@@ -243,6 +240,7 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
isLoadingExtra = false;
libraryAllowsScrobbling = false;
isScrobbling: boolean = true;
+ mobileSeriesImgBackground: string | undefined;
currentlyReadingChapter: Chapter | undefined = undefined;
hasReadingProgress = false;
@@ -262,6 +260,7 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
libraryType: LibraryType = LibraryType.Manga;
seriesMetadata: SeriesMetadata | null = null;
readingLists: Array = [];
+ collections: Array = [];
isWantToRead: boolean = false;
unreadCount: number = 0;
totalCount: number = 0;
@@ -384,6 +383,7 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
}
}
+
get UseBookLogic() {
return this.libraryType === LibraryType.Book || this.libraryType === LibraryType.LightNovel;
}
@@ -472,6 +472,9 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
return;
}
+ this.mobileSeriesImgBackground = getComputedStyle(document.documentElement)
+ .getPropertyValue('--mobile-series-img-background').trim();
+
// Set up the download in progress
this.download$ = this.downloadService.activeDownloads$.pipe(takeUntilDestroyed(this.destroyRef), map((events) => {
@@ -486,12 +489,15 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
this.router.navigateByUrl('/home');
}
} else if (event.event === EVENTS.ScanSeries) {
- const seriesCoverUpdatedEvent = event.payload as ScanSeriesEvent;
- if (seriesCoverUpdatedEvent.seriesId === this.seriesId) {
+ const seriesScanEvent = event.payload as ScanSeriesEvent;
+ if (seriesScanEvent.seriesId === this.seriesId) {
this.loadSeries(this.seriesId);
}
} else if (event.event === EVENTS.CoverUpdate) {
- this.themeService.refreshColorScape('series', this.seriesId).subscribe();
+ const coverUpdateEvent = event.payload as CoverUpdateEvent;
+ if (coverUpdateEvent.id === this.seriesId) {
+ this.themeService.refreshColorScape('series', this.seriesId).subscribe();
+ }
} else if (event.event === EVENTS.ChapterRemoved) {
const removedEvent = event.payload as ChapterRemovedEvent;
if (removedEvent.seriesId !== this.seriesId) return;
@@ -554,13 +560,7 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
updateUrl(activeTab: TabID) {
var tokens = this.router.url.split('#');
const newUrl = `${tokens[0]}#${activeTab}`;
-
- // if (tokens.length === 1 || tokens[1] === activeTab + '') {
- // return;
- // }
- console.log('url:', newUrl);
-
- //this.router.navigateByUrl(newUrl, { skipLocationChange: true, replaceUrl: true });
+ window.history.replaceState({}, '', newUrl);
}
handleSeriesActionCallback(action: ActionItem, series: Series) {
@@ -580,10 +580,10 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
this.actionService.scanSeries(series);
break;
case(Action.RefreshMetadata):
- this.actionService.refreshSeriesMetadata(series, undefined, true);
+ this.actionService.refreshSeriesMetadata(series, undefined, true, false);
break;
case(Action.GenerateColorScape):
- this.actionService.refreshSeriesMetadata(series, undefined, false);
+ this.actionService.refreshSeriesMetadata(series, undefined, false, true);
break;
case(Action.Delete):
this.deleteSeries(series);
@@ -673,13 +673,9 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
this.openChapter(chapter, true);
break;
case (Action.SendTo):
- {
- const device = (action._extra!.data as Device);
- this.deviceService.sendTo([chapter.id], device.id).subscribe(() => {
- this.toastr.success(this.translocoService.translate('series-detail.send-to', {deviceName: device.name}));
- });
- break;
- }
+ const device = (action._extra!.data as Device);
+ this.actionService.sendToDevice([chapter.id], device);
+ break;
default:
break;
}
@@ -726,6 +722,11 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
this.cdRef.markForCheck();
});
+ this.collectionTagService.allCollectionsForSeries(seriesId, false).subscribe(tags => {
+ this.collections = tags;
+ this.cdRef.markForCheck();
+ })
+
this.readerService.getTimeLeft(seriesId).subscribe((timeLeft) => {
this.readingTimeLeft = timeLeft;
this.cdRef.markForCheck();
@@ -1147,23 +1148,6 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
this.cdRef.markForCheck();
}
- previewSeries(item: Series | ExternalSeries, isExternal: boolean) {
- const ref = this.offcanvasService.open(SeriesPreviewDrawerComponent, {position: 'end', panelClass: ''});
- ref.componentInstance.isExternalSeries = isExternal;
- ref.componentInstance.name = item.name;
-
- if (isExternal) {
- const external = item as ExternalSeries;
- ref.componentInstance.aniListId = external.aniListId;
- ref.componentInstance.malId = external.malId;
- } else {
- const local = item as Series;
- ref.componentInstance.seriesId = local.id;
- ref.componentInstance.libraryId = local.libraryId;
- }
-
- }
-
openFilter(field: FilterField, value: string | number) {
this.filterUtilityService.applyFilter(['all-series'], field, FilterComparison.Equal, `${value}`).subscribe();
}
@@ -1183,4 +1167,9 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
});
}
}
+
+ switchTabsToDetail() {
+ this.activeTabId = TabID.Details;
+ this.cdRef.markForCheck();
+ }
}
diff --git a/UI/Web/src/app/settings/_components/setting-button/setting-button.component.html b/UI/Web/src/app/settings/_components/setting-button/setting-button.component.html
index 9418983b7..ca0c38a54 100644
--- a/UI/Web/src/app/settings/_components/setting-button/setting-button.component.html
+++ b/UI/Web/src/app/settings/_components/setting-button/setting-button.component.html
@@ -1,12 +1,7 @@
-
-
-
-
-
@if (subtitle) {
}
diff --git a/UI/Web/src/app/settings/_components/setting-item/setting-item.component.html b/UI/Web/src/app/settings/_components/setting-item/setting-item.component.html
index 0b9794397..618ebe15e 100644
--- a/UI/Web/src/app/settings/_components/setting-item/setting-item.component.html
+++ b/UI/Web/src/app/settings/_components/setting-item/setting-item.component.html
@@ -1,7 +1,7 @@
-
+
@if(labelId) {
@@ -13,9 +13,9 @@
}
-
+
@if (showEdit) {
-
+
{{isEditMode ? t('common.close') : (editLabel || t('common.edit'))}}
}
diff --git a/UI/Web/src/app/settings/_components/setting-title/setting-title.component.html b/UI/Web/src/app/settings/_components/setting-title/setting-title.component.html
index ec5fd18e7..bb181e331 100644
--- a/UI/Web/src/app/settings/_components/setting-title/setting-title.component.html
+++ b/UI/Web/src/app/settings/_components/setting-title/setting-title.component.html
@@ -1,15 +1,15 @@
-
+
{{title}}
@if (titleExtraRef) {
}
-
- {{isEditMode ? t('common.close') : t('common.edit')}}
+
+ {{isEditMode ? t('common.close') : t('common.edit')}}
diff --git a/UI/Web/src/app/settings/_components/settings/settings.component.html b/UI/Web/src/app/settings/_components/settings/settings.component.html
index 70d643c7c..c1228f9a6 100644
--- a/UI/Web/src/app/settings/_components/settings/settings.component.html
+++ b/UI/Web/src/app/settings/_components/settings/settings.component.html
@@ -34,7 +34,7 @@
@defer (when fragment === SettingsTabId.Users; prefetch on idle) {
@if (fragment === SettingsTabId.Users) {
-
+
}
@@ -42,7 +42,7 @@
@defer (when fragment === SettingsTabId.Libraries; prefetch on idle) {
@if (fragment === SettingsTabId.Libraries) {
-
+
}
@@ -50,7 +50,7 @@
@defer (when fragment === SettingsTabId.MediaIssues; prefetch on idle) {
@if (fragment === SettingsTabId.MediaIssues) {
-
+
}
@@ -58,7 +58,7 @@
@defer (when fragment === SettingsTabId.System; prefetch on idle) {
@if (fragment === SettingsTabId.System) {
-
+
}
@@ -66,7 +66,7 @@
@defer (when fragment === SettingsTabId.Statistics; prefetch on idle) {
@if (fragment === SettingsTabId.Statistics) {
-
+
}
@@ -74,7 +74,7 @@
@defer (when fragment === SettingsTabId.Tasks; prefetch on idle) {
@if (fragment === SettingsTabId.Tasks) {
-
+
}
@@ -82,7 +82,7 @@
@defer (when fragment === SettingsTabId.KavitaPlus; prefetch on idle) {
@if (fragment === SettingsTabId.KavitaPlus) {
-
+
}
@@ -114,7 +114,7 @@
@defer (when fragment === SettingsTabId.Customize; prefetch on idle) {
@if (fragment === SettingsTabId.Customize) {
-
+
}
@@ -130,7 +130,7 @@
@defer (when fragment === SettingsTabId.Theme; prefetch on idle) {
@if (fragment === SettingsTabId.Theme) {
-
+
}
@@ -138,7 +138,7 @@
@defer (when fragment === SettingsTabId.Devices; prefetch on idle) {
@if (fragment === SettingsTabId.Devices) {
-
+
}
@@ -146,7 +146,7 @@
@defer (when fragment === SettingsTabId.UserStats; prefetch on idle) {
@if (fragment === SettingsTabId.UserStats) {
-
+
}
@@ -154,7 +154,7 @@
@defer (when fragment === SettingsTabId.CBLImport; prefetch on idle) {
@if (fragment === SettingsTabId.CBLImport) {
-
+
}
@@ -162,7 +162,7 @@
@defer (when fragment === SettingsTabId.Scrobbling; prefetch on idle) {
@if(hasActiveLicense && fragment === SettingsTabId.Scrobbling) {
-
+
}
@@ -170,7 +170,7 @@
@defer (when fragment === SettingsTabId.MALStackImport; prefetch on idle) {
@if(hasActiveLicense && fragment === SettingsTabId.MALStackImport) {
-
+
}
diff --git a/UI/Web/src/app/settings/_components/settings/settings.component.scss b/UI/Web/src/app/settings/_components/settings/settings.component.scss
index 220cefea4..a92da0bde 100644
--- a/UI/Web/src/app/settings/_components/settings/settings.component.scss
+++ b/UI/Web/src/app/settings/_components/settings/settings.component.scss
@@ -2,3 +2,9 @@ h2 {
color: white;
font-weight: bold;
}
+
+::ng-deep .content-wrapper:not(.closed) {
+ .scale {
+ width: calc(100dvw - 200px) !important;
+ }
+}
diff --git a/UI/Web/src/app/shared/badge-expander/badge-expander.component.html b/UI/Web/src/app/shared/badge-expander/badge-expander.component.html
index e0f5efb7e..e61649f5f 100644
--- a/UI/Web/src/app/shared/badge-expander/badge-expander.component.html
+++ b/UI/Web/src/app/shared/badge-expander/badge-expander.component.html
@@ -3,7 +3,7 @@
@for(item of visibleItems; track item; let i = $index; let last = $last) {
- @if (!last) {
+ @if (!last && includeComma) {
,
}
} @empty {
diff --git a/UI/Web/src/app/shared/badge-expander/badge-expander.component.ts b/UI/Web/src/app/shared/badge-expander/badge-expander.component.ts
index 0f7e6550d..2ba6be010 100644
--- a/UI/Web/src/app/shared/badge-expander/badge-expander.component.ts
+++ b/UI/Web/src/app/shared/badge-expander/badge-expander.component.ts
@@ -2,13 +2,13 @@ import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
- ContentChild,
+ ContentChild, EventEmitter,
inject,
Input,
- OnInit,
+ OnInit, Output,
TemplateRef
} from '@angular/core';
-import {CommonModule, NgTemplateOutlet} from "@angular/common";
+import {NgTemplateOutlet} from "@angular/common";
import {TranslocoDirective} from "@jsverse/transloco";
import {DefaultValuePipe} from "../../_pipes/default-value.pipe";
@@ -27,6 +27,11 @@ export class BadgeExpanderComponent implements OnInit {
@Input() items: Array = [];
@Input() itemsTillExpander: number = 4;
@Input() allowToggle: boolean = true;
+ @Input() includeComma: boolean = true;
+ /**
+ * Invoked when the "and more" is clicked
+ */
+ @Output() toggle = new EventEmitter();
@ContentChild('badgeExpanderItem') itemTemplate!: TemplateRef;
@@ -43,6 +48,7 @@ export class BadgeExpanderComponent implements OnInit {
}
toggleVisible() {
+ this.toggle.emit();
if (!this.allowToggle) return;
this.isCollapsed = !this.isCollapsed;
diff --git a/UI/Web/src/app/shared/circular-loader/circular-loader.component.ts b/UI/Web/src/app/shared/circular-loader/circular-loader.component.ts
index f03ae2e04..ec75252ef 100644
--- a/UI/Web/src/app/shared/circular-loader/circular-loader.component.ts
+++ b/UI/Web/src/app/shared/circular-loader/circular-loader.component.ts
@@ -1,5 +1,5 @@
import {ChangeDetectionStrategy, Component, Input} from '@angular/core';
-import {CommonModule, NgClass, NgStyle} from "@angular/common";
+import {NgClass, NgStyle} from "@angular/common";
import {NgCircleProgressModule } from "ng-circle-progress";
@Component({
diff --git a/UI/Web/src/app/shared/person-badge/person-badge.component.scss b/UI/Web/src/app/shared/person-badge/person-badge.component.scss
index f291ff69a..4e7e7d1a8 100644
--- a/UI/Web/src/app/shared/person-badge/person-badge.component.scss
+++ b/UI/Web/src/app/shared/person-badge/person-badge.component.scss
@@ -8,7 +8,7 @@
display: inline-block;
cursor: pointer;
width: 100px;
- height: 100px;
+ word-break: break-word;
i {
font-size: 2rem;
diff --git a/UI/Web/src/app/shared/series-format/series-format.component.html b/UI/Web/src/app/shared/series-format/series-format.component.html
index 2bf818a0a..a288890fa 100644
--- a/UI/Web/src/app/shared/series-format/series-format.component.html
+++ b/UI/Web/src/app/shared/series-format/series-format.component.html
@@ -1,4 +1,8 @@
-
+@if (format !== MangaFormat.UNKNOWN) {
+ @if (useTitle) {
-
-
+ } @else {
+
+ }
+
+}
diff --git a/UI/Web/src/app/shared/series-format/series-format.component.ts b/UI/Web/src/app/shared/series-format/series-format.component.ts
index c6c9ccac0..39a3b53b1 100644
--- a/UI/Web/src/app/shared/series-format/series-format.component.ts
+++ b/UI/Web/src/app/shared/series-format/series-format.component.ts
@@ -2,15 +2,15 @@ import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
import { MangaFormat } from 'src/app/_models/manga-format';
import {MangaFormatIconPipe} from "../../_pipes/manga-format-icon.pipe";
import {MangaFormatPipe} from "../../_pipes/manga-format.pipe";
-import {CommonModule} from "@angular/common";
+import {NgbTooltip} from "@ng-bootstrap/ng-bootstrap";
@Component({
selector: 'app-series-format',
standalone: true,
imports: [
- CommonModule,
MangaFormatIconPipe,
- MangaFormatPipe
+ MangaFormatPipe,
+ NgbTooltip
],
templateUrl: './series-format.component.html',
styleUrls: ['./series-format.component.scss'],
@@ -21,4 +21,8 @@ export class SeriesFormatComponent {
protected readonly MangaFormat = MangaFormat;
@Input() format: MangaFormat = MangaFormat.UNKNOWN;
+ /**
+ * Use the browser title vs ngbTooltip
+ */
+ @Input() useTitle: boolean = true;
}
diff --git a/UI/Web/src/app/sidenav/_components/side-nav-item/side-nav-item.component.scss b/UI/Web/src/app/sidenav/_components/side-nav-item/side-nav-item.component.scss
index ce979f26a..4fa4872ed 100644
--- a/UI/Web/src/app/sidenav/_components/side-nav-item/side-nav-item.component.scss
+++ b/UI/Web/src/app/sidenav/_components/side-nav-item/side-nav-item.component.scss
@@ -133,11 +133,11 @@ a {
color: var(--side-nav-text-color);
}
-@media (max-width: 576px) {
+@media (max-width: 768px) {
.side-nav-item {
align-items: center;
- padding: 15px 10px;
- height: 70px;
+ padding: 0 10px;
+ height: 55px;
font-size: 1rem;
.side-nav-text {
diff --git a/UI/Web/src/app/sidenav/_components/side-nav-item/side-nav-item.component.ts b/UI/Web/src/app/sidenav/_components/side-nav-item/side-nav-item.component.ts
index 589e9b54d..7e3dcbc77 100644
--- a/UI/Web/src/app/sidenav/_components/side-nav-item/side-nav-item.component.ts
+++ b/UI/Web/src/app/sidenav/_components/side-nav-item/side-nav-item.component.ts
@@ -153,6 +153,7 @@ export class SideNavItemComponent implements OnInit {
// If on mobile, automatically collapse the side nav after making a selection
collapseNavIfApplicable() {
if (this.utilityService.getActiveBreakpoint() < Breakpoint.Tablet) {
+ console.log('collapsing side nav');
this.navService.collapseSideNav(true);
}
}
diff --git a/UI/Web/src/app/sidenav/_components/side-nav/side-nav.component.html b/UI/Web/src/app/sidenav/_components/side-nav/side-nav.component.html
index e6315f135..ec5c475b9 100644
--- a/UI/Web/src/app/sidenav/_components/side-nav/side-nav.component.html
+++ b/UI/Web/src/app/sidenav/_components/side-nav/side-nav.component.html
@@ -1,85 +1,94 @@
@if (accountService.currentUser$ | async; as user) {
-
+
+
-
- @if (navStreams$ | async; as streams) {
- @if (showAll) {
-
-
- @if (streams.length > ItemLimit && (navService.sideNavCollapsed$ | async) === false) {
-
-
-
-
-
+
+
+ @if (navStreams$ | async; as streams) {
+ @if (showAll) {
+
+
+ @if (streams.length > ItemLimit && (navService.sideNavCollapsed$ | async) === false) {
+
+
+
+
+
+
-
- }
-
- }
-
- @for (navStream of streams | filter: filterLibrary; track navStream.name + navStream.order) {
- @switch (navStream.streamType) {
- @case (SideNavStreamType.Library) {
-
-
-
-
-
- }
-
- @case (SideNavStreamType.AllSeries) {
-
- }
-
- @case (SideNavStreamType.Bookmarks) {
-
- }
-
- @case (SideNavStreamType.ReadingLists) {
-
- }
-
- @case (SideNavStreamType.Collections) {
-
- }
-
- @case (SideNavStreamType.WantToRead) {
-
- }
-
- @case (SideNavStreamType.SmartFilter) {
-
- }
-
- @case (SideNavStreamType.ExternalSource) {
-
}
}
- }
- @if (totalSize > ItemLimit && !showAll) {
-
+ @for (navStream of streams | filter: filterLibrary; track navStream.name + navStream.order) {
+ @switch (navStream.streamType) {
+ @case (SideNavStreamType.Library) {
+
+
+
+
+
+ }
+
+ @case (SideNavStreamType.AllSeries) {
+
+ }
+
+ @case (SideNavStreamType.Bookmarks) {
+
+ }
+
+ @case (SideNavStreamType.ReadingLists) {
+
+ }
+
+ @case (SideNavStreamType.Collections) {
+
+ }
+
+ @case (SideNavStreamType.WantToRead) {
+
+ }
+
+ @case (SideNavStreamType.SmartFilter) {
+
+ }
+
+ @case (SideNavStreamType.ExternalSource) {
+
+ }
+
+ }
+ }
+
+ @if (totalSize > ItemLimit && !showAll) {
+
+ }
}
+
+
+ }
+
+ @if (utilityService.activeBreakpoint$ | async; as breakpoint) {
+ @if (breakpoint < Breakpoint.Tablet) {
+
+ }
+ }
+
+
+ @if ((accountService.hasValidLicense$ | async) === false) {
+
}
- }
-
-
- @if ((accountService.hasValidLicense$ | async) === false) {
-
- }
-
diff --git a/UI/Web/src/app/sidenav/_components/side-nav/side-nav.component.scss b/UI/Web/src/app/sidenav/_components/side-nav/side-nav.component.scss
index b4c1e471b..e69de29bb 100644
--- a/UI/Web/src/app/sidenav/_components/side-nav/side-nav.component.scss
+++ b/UI/Web/src/app/sidenav/_components/side-nav/side-nav.component.scss
@@ -1,155 +0,0 @@
-.bottom {
- position: absolute;
- bottom: 12px;
- width: 190px;
- background: var(--side-nav-bg-color);
- border-radius: var(--side-nav-border-radius);
- font-size: 12px;
- transition: width var(--side-nav-openclose-transition);
- z-index: 999;
- left: 10px;
-
- &.closed {
- width: 45px;
- overflow-x: hidden;
- overflow-y: auto;
- left: -50px;
- }
-}
-
-:host ::ng-deep .bottom .donate .side-nav-item {
- justify-content: center;
- min-height: 25px;
- align-items: center;
-
- :hover {
- background-color: unset;
- }
- }
-
-:host ::ng-deep .bottom .donate .side-nav-item span {
- flex-grow: unset !important;
- min-width: unset !important;;
-}
-
-:host ::ng-deep .bottom .donate .side-nav-item span div {
- min-width: unset !important;
-}
-
-:host ::ng-deep .bottom .donate .side-nav-item span div i{
- font-size: 12px !important;
-}
-
-
-
-:host ::ng-deep .bottom .donate .side-nav-item.closed span.phone-hidden div {
- width: 100%;
-}
-
-:host ::ng-deep .bottom .donate .side-nav-item.closed span.side-nav-text div {
- width: 0;
-}
-
-@media (max-width: 576px) {
- :host ::ng-deep .bottom .donate .side-nav-item.closed {
- display: none;
- }
-
- :host ::ng-deep .bottom .donate .side-nav-item span.phone-hidden {
- display: block !important;
- }
-}
-
-.side-nav {
- padding-bottom: 10px;
- width: 190px;
- background-color: var(--side-nav-bg-color);
- height: calc((var(--vh)*100) - 115px);
- position: fixed;
- margin: 0;
- left: 10px;
- top: 73px;
- border-radius: var(--side-nav-border-radius);
- transition: width var(--side-nav-openclose-transition), background-color var(--side-nav-bg-color-transition), border-color var(--side-nav-border-transition);
- overflow: auto;
- border: var(--side-nav-border);
-
- &.no-donate {
- height: calc((var(--vh)*100) - 82px);
- }
-
- &.hidden {
- display: none;
- opacity: 0;
- }
-
- &.closed {
- width: 55px;
- overflow-x: hidden;
- overflow-y: auto;
- background-color: var(--side-nav-closed-bg-color);
- border: var(--side-nav-border-closed);
- }
-
- .side-nav-item:first {
- border-top-left-radius: var(--side-nav-border-radius);
- border-top-right-radius: var(--side-nav-border-radius);
- }
-}
-
-@media (max-width: 576px) {
- .side-nav {
- padding: 10px 0;
- width: 55vw;
- background-color: var(--side-nav-mobile-bg-color);
- height: calc((var(--vh)*100));
- position: fixed;
- margin: 0;
- left: 0;
- top: var(--nav-mobile-offset);
- transition: width var(--side-nav-openclose-transition);
- z-index: 999;
- overflow: auto;
- border: var(--side-nav-mobile-border);
-
- &.closed {
- width: 0;
- overflow: hidden;
- box-shadow: none;
- }
-
- .side-nav-item:first {
- border-top-left-radius: var(--side-nav-border-radius);
- border-top-right-radius: var(--side-nav-border-radius);
- }
-
- &.no-donate {
- height: calc((var(--vh)*100) - var(--nav-mobile-offset));
- }
- }
-
- .bottom {
- display:none;
-
- }
-
- .side-nav-overlay {
- background-color: var(--side-nav-overlay-color);
- width: 100vw;
- height: calc((var(--vh)*100) - var(--nav-offset));
- position: absolute;
- left: 0;
- z-index: 998;
-
- &.closed {
- display: none;
- }
- }
-}
-
-.btn-close {
- margin-top: -28px;
- font-size: 0.8rem;
- position: absolute;
- right: 16px;
-}
diff --git a/UI/Web/src/app/sidenav/_components/side-nav/side-nav.component.ts b/UI/Web/src/app/sidenav/_components/side-nav/side-nav.component.ts
index 08d1654b5..4428eb41b 100644
--- a/UI/Web/src/app/sidenav/_components/side-nav/side-nav.component.ts
+++ b/UI/Web/src/app/sidenav/_components/side-nav/side-nav.component.ts
@@ -42,7 +42,7 @@ import {SettingsTabId} from "../../preference-nav/preference-nav.component";
export class SideNavComponent implements OnInit {
private readonly router = inject(Router);
- private readonly utilityService = inject(UtilityService);
+ protected readonly utilityService = inject(UtilityService);
private readonly messageHub = inject(MessageHubService);
private readonly actionService = inject(ActionService);
public readonly navService = inject(NavService);
@@ -132,8 +132,13 @@ export class SideNavComponent implements OnInit {
constructor() {
+ // Ensure that on mobile, we are collapsed by default
+ if (this.utilityService.getActiveBreakpoint() < Breakpoint.Tablet) {
+ this.navService.collapseSideNav(true);
+ }
+
this.collapseSideNavOnMobileNav$.subscribe(() => {
- this.navService.toggleSideNav();
+ this.navService.collapseSideNav(false);
this.cdRef.markForCheck();
});
}
@@ -209,4 +214,6 @@ export class SideNavComponent implements OnInit {
this.cdRef.markForCheck();
this.showAllSubject.next(false);
}
+
+ protected readonly Breakpoint = Breakpoint;
}
diff --git a/UI/Web/src/app/sidenav/preference-nav/preference-nav.component.html b/UI/Web/src/app/sidenav/preference-nav/preference-nav.component.html
index 13d407a27..3929b4fb9 100644
--- a/UI/Web/src/app/sidenav/preference-nav/preference-nav.component.html
+++ b/UI/Web/src/app/sidenav/preference-nav/preference-nav.component.html
@@ -2,20 +2,29 @@
@if (accountService.currentUser$ | async; as user) {
@if((navService.sideNavCollapsed$ | async) === false) {
-
+
+
- @for(section of sections; track section.title + section.children.length; let idx = $index;) {
- @if (hasAnyChildren(user, section)) {
-
+ @for(section of sections; track section.title + section.children.length; let idx = $index;) {
+ @if (hasAnyChildren(user, section)) {
+
+ }
}
}
}
- }
+
+ @if (utilityService.activeBreakpoint$ | async; as breakpoint) {
+ @if (breakpoint < Breakpoint.Tablet) {
+
+ }
+ }
}
}
diff --git a/UI/Web/src/app/sidenav/preference-nav/preference-nav.component.scss b/UI/Web/src/app/sidenav/preference-nav/preference-nav.component.scss
index d4e6d62da..206b609f1 100644
--- a/UI/Web/src/app/sidenav/preference-nav/preference-nav.component.scss
+++ b/UI/Web/src/app/sidenav/preference-nav/preference-nav.component.scss
@@ -3,7 +3,7 @@
padding-bottom: 10px;
width: 190px;
background-color: var(--side-nav-bg-color);
- height: calc((var(--vh)*100) - 115px);
+ height: calc((var(--vh)*100) - 75px);
position: fixed;
margin: 0;
left: 10px;
@@ -36,16 +36,16 @@
}
}
-@media (max-width: 576px) {
+@media (max-width: 768px) {
.side-nav {
padding: 10px 0;
width: 55vw;
background-color: var(--side-nav-mobile-bg-color);
- height: calc((var(--vh)*100) - 125px);
+ height: 100dvh;
position: fixed;
margin: 0;
left: 0;
- top: var(--nav-mobile-offset);
+ top: 0;
transition: width var(--side-nav-openclose-transition);
z-index: 999;
overflow: auto;
diff --git a/UI/Web/src/app/sidenav/preference-nav/preference-nav.component.ts b/UI/Web/src/app/sidenav/preference-nav/preference-nav.component.ts
index e1499577f..a1e4eb440 100644
--- a/UI/Web/src/app/sidenav/preference-nav/preference-nav.component.ts
+++ b/UI/Web/src/app/sidenav/preference-nav/preference-nav.component.ts
@@ -4,13 +4,15 @@ import {AsyncPipe, DOCUMENT, NgClass} from "@angular/common";
import {NavService} from "../../_services/nav.service";
import {AccountService, Role} from "../../_services/account.service";
import {SideNavItemComponent} from "../_components/side-nav-item/side-nav-item.component";
-import {ActivatedRoute, RouterLink} from "@angular/router";
+import {ActivatedRoute, NavigationEnd, Router, RouterLink} from "@angular/router";
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
import {SettingFragmentPipe} from "../../_pipes/setting-fragment.pipe";
-import {map, Observable, of, shareReplay, switchMap, take} from "rxjs";
+import {map, Observable, of, shareReplay, switchMap, take, tap} from "rxjs";
import {ServerService} from "../../_services/server.service";
import {ScrobblingService} from "../../_services/scrobbling.service";
import {User} from "../../_models/user";
+import {filter} from "rxjs/operators";
+import {Breakpoint, UtilityService} from "../../shared/_services/utility.service";
export enum SettingsTabId {
@@ -82,8 +84,11 @@ export class PreferenceNavComponent implements AfterViewInit {
private readonly route = inject(ActivatedRoute);
private readonly serverService = inject(ServerService);
private readonly scrobbleService = inject(ScrobblingService);
+ private readonly router = inject(Router);
+ protected readonly utilityService = inject(UtilityService);
private readonly document = inject(DOCUMENT);
+ hasActiveLicense = false;
/**
* This links to settings.component.html which has triggers on what underlying component to render out.
*/
@@ -147,13 +152,29 @@ export class PreferenceNavComponent implements AfterViewInit {
]
}
];
+ collapseSideNavOnMobileNav$ = this.router.events.pipe(
+ filter(event => event instanceof NavigationEnd),
+ takeUntilDestroyed(this.destroyRef),
+ map(evt => evt as NavigationEnd),
+ switchMap(_ => this.utilityService.activeBreakpoint$),
+ filter((b) => b < Breakpoint.Tablet),
+ switchMap(() => this.navService.sideNavCollapsed$),
+ take(1),
+ filter(collapsed => !collapsed),
+ tap(c => {
+ this.navService.collapseSideNav(true);
+ }),
+ );
- hasActiveLicense = false;
-
constructor() {
+ this.collapseSideNavOnMobileNav$.subscribe();
+
+ // Ensure that on mobile, we are collapsed by default
+ if (this.utilityService.getActiveBreakpoint() < Breakpoint.Tablet) {
+ this.navService.collapseSideNav(true);
+ }
- this.navService.collapseSideNav(false);
this.accountService.hasValidLicense$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(res => {
if (res) {
@@ -208,4 +229,9 @@ export class PreferenceNavComponent implements AfterViewInit {
return section.children.filter(item => this.accountService.hasAnyRole(user, item.roles)).length > 0;
}
+ collapse() {
+ this.navService.toggleSideNav();
+ }
+
+ protected readonly Breakpoint = Breakpoint;
}
diff --git a/UI/Web/src/app/user-settings/manga-user-preferences/manage-user-preferences.component.html b/UI/Web/src/app/user-settings/manga-user-preferences/manage-user-preferences.component.html
index 0d5ca9dd0..1d1e34e8e 100644
--- a/UI/Web/src/app/user-settings/manga-user-preferences/manage-user-preferences.component.html
+++ b/UI/Web/src/app/user-settings/manga-user-preferences/manage-user-preferences.component.html
@@ -26,7 +26,7 @@
-
+
@@ -39,7 +39,7 @@
-
+
@@ -51,7 +51,7 @@
-
+
@@ -63,7 +63,7 @@
-
+
@@ -75,7 +75,7 @@
-
+
@@ -189,7 +189,7 @@
-
+
@@ -201,7 +201,7 @@
-
+
@@ -213,7 +213,7 @@
-
+
@@ -225,7 +225,7 @@
-
+
@@ -242,7 +242,7 @@
-
+
@@ -254,7 +254,7 @@
-
+
@@ -350,12 +350,12 @@
-
+
- {{settingsForm.get('bookReaderFontSize')?.value + '%'}}
+ {{settingsForm.get('bookReaderFontSize')?.value + '%'}}
@@ -368,11 +368,11 @@
-
+
- {{settingsForm.get('bookReaderLineSpacing')?.value + '%'}}
+ {{settingsForm.get('bookReaderLineSpacing')?.value + '%'}}
@@ -385,11 +385,11 @@
-
+
- {{settingsForm.get('bookReaderMargin')?.value + '%'}}
+ {{settingsForm.get('bookReaderMargin')?.value + '%'}}
diff --git a/UI/Web/src/app/user-settings/user-holds/scrobbling-holds.component.ts b/UI/Web/src/app/user-settings/user-holds/scrobbling-holds.component.ts
index 752c870cf..b5b3c747a 100644
--- a/UI/Web/src/app/user-settings/user-holds/scrobbling-holds.component.ts
+++ b/UI/Web/src/app/user-settings/user-holds/scrobbling-holds.component.ts
@@ -13,7 +13,6 @@ import {
NgbAccordionItem
} from "@ng-bootstrap/ng-bootstrap";
import {TranslocoDirective} from "@jsverse/transloco";
-import {ListItemComponent} from "../../cards/list-item/list-item.component";
import {ImageService} from "../../_services/image.service";
import {ImageComponent} from "../../shared/image/image.component";
@@ -21,7 +20,7 @@ import {ImageComponent} from "../../shared/image/image.component";
selector: 'app-user-holds',
standalone: true,
imports: [ScrobbleEventTypePipe, NgbAccordionDirective, NgbAccordionCollapse, NgbAccordionBody,
- NgbAccordionItem, NgbAccordionHeader, TranslocoDirective, AsyncPipe, ListItemComponent, ImageComponent],
+ NgbAccordionItem, NgbAccordionHeader, TranslocoDirective, AsyncPipe, ImageComponent],
templateUrl: './scrobbling-holds.component.html',
styleUrls: ['./scrobbling-holds.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
diff --git a/UI/Web/src/app/volume-detail/volume-detail.component.html b/UI/Web/src/app/volume-detail/volume-detail.component.html
index eb0c4c701..b0d8b3b96 100644
--- a/UI/Web/src/app/volume-detail/volume-detail.component.html
+++ b/UI/Web/src/app/volume-detail/volume-detail.component.html
@@ -1,19 +1,41 @@
+
+
@if (volume && series && libraryType !== null) {
-
+
-
- @if (volume.pagesRead < volume.pages && volume.pagesRead > 0) {
-
+ @if(mobileSeriesImgBackground === 'true') {
+
+ } @else {
+
}
+ @if (volume.pagesRead < volume.pages && volume.pagesRead > 0) {
+
+ @if (currentlyReadingChapter) {
+
+ {{t('continue-from', {title: ContinuePointTitle})}}
+
+ }
+ }
+
-
+
+
}
+
+
+
+
+
+
@@ -93,7 +122,7 @@
{{t('writers-title')}}
-
+
{{item.name}}
@@ -103,7 +132,7 @@
{{t('cover-artists-title')}}
-
+
{{item.name}}
@@ -113,11 +142,44 @@
+
+
+
+ {{t('genres-title')}}
+
+
+
+ {{item.title}}
+
+
+
+
+
+
+ {{t('tags-title')}}
+
+
+
+ {{item.title}}
+
+
+
+
+
+
+
+
-
-
-
+
{{t('writers-title')}}
+ [allowToggle]="false"
+ (toggle)="switchTabsToDetail()">
{{item.name}}
@@ -162,12 +167,13 @@
-
-
+
{{t('genres-title')}}
+ [allowToggle]="false"
+ (toggle)="switchTabsToDetail()">
{{item.title}}
@@ -180,7 +186,8 @@
+ [allowToggle]="false"
+ (toggle)="switchTabsToDetail()">
{{item.title}}
@@ -189,45 +196,11 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
- @@ -316,138 +289,16 @@ } - @if (hasRelations && relationShips) { + @if (hasRelations || readingLists.length > 0 || collections.length > 0) {
-
{{t(TabID.Related)}}
- {{relations.length}}
+ {{relations.length + readingLists.length + collections.length}}
@defer (when activeTabId === TabID.Related; prefetch on idle) { - - +- @for(item of scroll.viewPortItems; let idx = $index; track item.id) { --- } - } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
}
diff --git a/UI/Web/src/app/series-detail/_components/series-detail/series-detail.component.scss b/UI/Web/src/app/series-detail/_components/series-detail/series-detail.component.scss
index 7cd23c73c..0a7b2cb3c 100644
--- a/UI/Web/src/app/series-detail/_components/series-detail/series-detail.component.scss
+++ b/UI/Web/src/app/series-detail/_components/series-detail/series-detail.component.scss
@@ -7,19 +7,7 @@
left: 20px;
}
-.under-image {
- background-color: var(--breadcrumb-bg-color);
- color: white;
- border-bottom-left-radius: 5px;
- border-bottom-right-radius: 5px;
- text-align: center;
-}
-//
-//.rating-star {
-// margin-top: 2px;
-// font-size: 1.5rem;
-//}
-//
+
.card-container{
display: grid;
grid-template-columns: repeat(auto-fill, 160px);
@@ -27,7 +15,7 @@
justify-content: space-around;
}
-::ng-deep .carousel-container .header i.fa-plus, ::ng-deep .carousel-container .header i.fa-pen{
+::ng-deep .carousel-container .header i.fa-plus, ::ng-deep .carousel-container .header i.fa-pen {
border-width: 1px;
border-style: solid;
border-radius: 5px;
@@ -40,13 +28,4 @@
}
}
-.upper-details {
- font-size: 0.9rem;
-}
-@media (max-width: 768px) {
- .carousel-tabs-container {
- mask-image: linear-gradient(transparent, black 0%, black 90%, transparent 100%);
- -webkit-mask-image: linear-gradient(to right, transparent, black 0%, black 90%, transparent 100%);
- }
-}
diff --git a/UI/Web/src/app/series-detail/_components/series-detail/series-detail.component.ts b/UI/Web/src/app/series-detail/_components/series-detail/series-detail.component.ts
index 533efe3cc..49805325c 100644
--- a/UI/Web/src/app/series-detail/_components/series-detail/series-detail.component.ts
+++ b/UI/Web/src/app/series-detail/_components/series-detail/series-detail.component.ts
@@ -98,10 +98,6 @@ import {
} from '../../../sidenav/_components/side-nav-companion-bar/side-nav-companion-bar.component';
import {translate, TranslocoDirective, TranslocoService} from "@jsverse/transloco";
import {CardActionablesComponent} from "../../../_single-module/card-actionables/card-actionables.component";
-import {ExternalSeries} from "../../../_models/series-detail/external-series";
-import {
- SeriesPreviewDrawerComponent
-} from "../../../_single-module/series-preview-drawer/series-preview-drawer.component";
import {PublicationStatus} from "../../../_models/metadata/publication-status";
import {NextExpectedChapter} from "../../../_models/series-detail/next-expected-chapter";
import {NextExpectedCardComponent} from "../../../cards/next-expected-card/next-expected-card.component";
@@ -143,11 +139,13 @@ import {MetadataDetailRowComponent} from "../metadata-detail-row/metadata-detail
import {DownloadButtonComponent} from "../download-button/download-button.component";
import {hasAnyCast} from "../../../_models/common/i-has-cast";
import {EditVolumeModalComponent} from "../../../_single-module/edit-volume-modal/edit-volume-modal.component";
+import {CoverUpdateEvent} from "../../../_models/events/cover-update-event";
+import {RelatedSeriesPair, RelatedTabComponent} from "../../../_single-modules/related-tab/related-tab.component";
+import {CollectionTagService} from "../../../_services/collection-tag.service";
+import {UserCollection} from "../../../_models/collection-tag";
+import {SeriesFormatComponent} from "../../../shared/series-format/series-format.component";
+import {MangaFormatPipe} from "../../../_pipes/manga-format.pipe";
-interface RelatedSeriesPair {
- series: Series;
- relation: RelationKind;
-}
enum TabID {
Related = 'related-tab',
@@ -176,12 +174,12 @@ interface StoryLineItem {
TagBadgeComponent, ImageComponent, NgbTooltip, NgbProgressbar, NgbDropdown, NgbDropdownToggle, NgbDropdownMenu,
NgbDropdownItem, CarouselReelComponent, ReviewCardComponent, BulkOperationsComponent,
NgbNav, NgbNavItem, NgbNavLink, NgbNavContent, VirtualScrollerModule, CardItemComponent,
- EntityTitleComponent, SeriesCardComponent, ExternalSeriesCardComponent, NgbNavOutlet,
+ EntityTitleComponent, SeriesCardComponent, ExternalSeriesCardComponent, NgbNavOutlet,
LoadingComponent, DecimalPipe, TranslocoDirective, NgTemplateOutlet, NextExpectedCardComponent,
NgClass, NgOptimizedImage, ProviderImagePipe, AsyncPipe, PersonBadgeComponent, DetailsTabComponent, ChapterCardComponent,
VolumeCardComponent, JsonPipe, AgeRatingPipe, DefaultValuePipe, ExternalRatingComponent, ReadMoreComponent, ReadTimePipe,
RouterLink, TimeAgoPipe, AgeRatingImageComponent, CompactNumberPipe, IconAndTitleComponent, SafeHtmlPipe, BadgeExpanderComponent,
- A11yClickDirective, ReadTimeLeftPipe, PublicationStatusPipe, MetadataDetailRowComponent, DownloadButtonComponent]
+ A11yClickDirective, ReadTimeLeftPipe, PublicationStatusPipe, MetadataDetailRowComponent, DownloadButtonComponent, RelatedTabComponent, SeriesFormatComponent, MangaFormatPipe]
})
export class SeriesDetailComponent implements OnInit, AfterContentChecked {
@@ -200,10 +198,9 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
private readonly actionService = inject(ActionService);
private readonly messageHub = inject(MessageHubService);
private readonly readingListService = inject(ReadingListService);
- private readonly offcanvasService = inject(NgbOffcanvas);
+ private readonly collectionTagService = inject(CollectionTagService);
private readonly cdRef = inject(ChangeDetectorRef);
private readonly scrollService = inject(ScrollService);
- private readonly deviceService = inject(DeviceService);
private readonly translocoService = inject(TranslocoService);
protected readonly bulkSelectionService = inject(BulkSelectionService);
protected readonly utilityService = inject(UtilityService);
@@ -243,6 +240,7 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
isLoadingExtra = false;
libraryAllowsScrobbling = false;
isScrobbling: boolean = true;
+ mobileSeriesImgBackground: string | undefined;
currentlyReadingChapter: Chapter | undefined = undefined;
hasReadingProgress = false;
@@ -262,6 +260,7 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
libraryType: LibraryType = LibraryType.Manga;
seriesMetadata: SeriesMetadata | null = null;
readingLists: Array
+
-
@if (showStorylineTab) {
-
-
-
-
-
@if (subtitle) {
}
diff --git a/UI/Web/src/app/settings/_components/setting-item/setting-item.component.html b/UI/Web/src/app/settings/_components/setting-item/setting-item.component.html
index 0b9794397..618ebe15e 100644
--- a/UI/Web/src/app/settings/_components/setting-item/setting-item.component.html
+++ b/UI/Web/src/app/settings/_components/setting-item/setting-item.component.html
@@ -1,7 +1,7 @@
-
+
-
@if(labelId) { @@ -13,9 +13,9 @@ }
+
@if (showEdit) {
-
+
{{isEditMode ? t('common.close') : (editLabel || t('common.edit'))}}
}
diff --git a/UI/Web/src/app/settings/_components/setting-title/setting-title.component.html b/UI/Web/src/app/settings/_components/setting-title/setting-title.component.html
index ec5fd18e7..bb181e331 100644
--- a/UI/Web/src/app/settings/_components/setting-title/setting-title.component.html
+++ b/UI/Web/src/app/settings/_components/setting-title/setting-title.component.html
@@ -1,15 +1,15 @@
-
+
{{title}}
@if (titleExtraRef) {
-
diff --git a/UI/Web/src/app/settings/_components/settings/settings.component.html b/UI/Web/src/app/settings/_components/settings/settings.component.html
index 70d643c7c..c1228f9a6 100644
--- a/UI/Web/src/app/settings/_components/settings/settings.component.html
+++ b/UI/Web/src/app/settings/_components/settings/settings.component.html
@@ -34,7 +34,7 @@
@defer (when fragment === SettingsTabId.Users; prefetch on idle) {
@if (fragment === SettingsTabId.Users) {
- {{title}}
@if (titleExtraRef) {
}
- {{isEditMode ? t('common.close') : t('common.edit')}}
+
+ {{isEditMode ? t('common.close') : t('common.edit')}}
+
}
@@ -42,7 +42,7 @@
@defer (when fragment === SettingsTabId.Libraries; prefetch on idle) {
@if (fragment === SettingsTabId.Libraries) {
-
+
}
@@ -50,7 +50,7 @@
@defer (when fragment === SettingsTabId.MediaIssues; prefetch on idle) {
@if (fragment === SettingsTabId.MediaIssues) {
-
+
}
@@ -58,7 +58,7 @@
@defer (when fragment === SettingsTabId.System; prefetch on idle) {
@if (fragment === SettingsTabId.System) {
-
+
}
@@ -66,7 +66,7 @@
@defer (when fragment === SettingsTabId.Statistics; prefetch on idle) {
@if (fragment === SettingsTabId.Statistics) {
-
+
}
@@ -74,7 +74,7 @@
@defer (when fragment === SettingsTabId.Tasks; prefetch on idle) {
@if (fragment === SettingsTabId.Tasks) {
-
+
}
@@ -82,7 +82,7 @@
@defer (when fragment === SettingsTabId.KavitaPlus; prefetch on idle) {
@if (fragment === SettingsTabId.KavitaPlus) {
-
+
}
@@ -114,7 +114,7 @@
@defer (when fragment === SettingsTabId.Customize; prefetch on idle) {
@if (fragment === SettingsTabId.Customize) {
-
+
}
@@ -130,7 +130,7 @@
@defer (when fragment === SettingsTabId.Theme; prefetch on idle) {
@if (fragment === SettingsTabId.Theme) {
-
+
}
@@ -138,7 +138,7 @@
@defer (when fragment === SettingsTabId.Devices; prefetch on idle) {
@if (fragment === SettingsTabId.Devices) {
-
+
}
@@ -146,7 +146,7 @@
@defer (when fragment === SettingsTabId.UserStats; prefetch on idle) {
@if (fragment === SettingsTabId.UserStats) {
-
+
}
@@ -154,7 +154,7 @@
@defer (when fragment === SettingsTabId.CBLImport; prefetch on idle) {
@if (fragment === SettingsTabId.CBLImport) {
-
+
}
@@ -162,7 +162,7 @@
@defer (when fragment === SettingsTabId.Scrobbling; prefetch on idle) {
@if(hasActiveLicense && fragment === SettingsTabId.Scrobbling) {
-
+
}
@@ -170,7 +170,7 @@
@defer (when fragment === SettingsTabId.MALStackImport; prefetch on idle) {
@if(hasActiveLicense && fragment === SettingsTabId.MALStackImport) {
-
+
}
diff --git a/UI/Web/src/app/settings/_components/settings/settings.component.scss b/UI/Web/src/app/settings/_components/settings/settings.component.scss
index 220cefea4..a92da0bde 100644
--- a/UI/Web/src/app/settings/_components/settings/settings.component.scss
+++ b/UI/Web/src/app/settings/_components/settings/settings.component.scss
@@ -2,3 +2,9 @@ h2 {
color: white;
font-weight: bold;
}
+
+::ng-deep .content-wrapper:not(.closed) {
+ .scale {
+ width: calc(100dvw - 200px) !important;
+ }
+}
diff --git a/UI/Web/src/app/shared/badge-expander/badge-expander.component.html b/UI/Web/src/app/shared/badge-expander/badge-expander.component.html
index e0f5efb7e..e61649f5f 100644
--- a/UI/Web/src/app/shared/badge-expander/badge-expander.component.html
+++ b/UI/Web/src/app/shared/badge-expander/badge-expander.component.html
@@ -3,7 +3,7 @@
@for(item of visibleItems; track item; let i = $index; let last = $last) {
- @if (!last) {
+ @if (!last && includeComma) {
,
}
} @empty {
diff --git a/UI/Web/src/app/shared/badge-expander/badge-expander.component.ts b/UI/Web/src/app/shared/badge-expander/badge-expander.component.ts
index 0f7e6550d..2ba6be010 100644
--- a/UI/Web/src/app/shared/badge-expander/badge-expander.component.ts
+++ b/UI/Web/src/app/shared/badge-expander/badge-expander.component.ts
@@ -2,13 +2,13 @@ import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
- ContentChild,
+ ContentChild, EventEmitter,
inject,
Input,
- OnInit,
+ OnInit, Output,
TemplateRef
} from '@angular/core';
-import {CommonModule, NgTemplateOutlet} from "@angular/common";
+import {NgTemplateOutlet} from "@angular/common";
import {TranslocoDirective} from "@jsverse/transloco";
import {DefaultValuePipe} from "../../_pipes/default-value.pipe";
@@ -27,6 +27,11 @@ export class BadgeExpanderComponent implements OnInit {
@Input() items: Array = [];
@Input() itemsTillExpander: number = 4;
@Input() allowToggle: boolean = true;
+ @Input() includeComma: boolean = true;
+ /**
+ * Invoked when the "and more" is clicked
+ */
+ @Output() toggle = new EventEmitter();
@ContentChild('badgeExpanderItem') itemTemplate!: TemplateRef;
@@ -43,6 +48,7 @@ export class BadgeExpanderComponent implements OnInit {
}
toggleVisible() {
+ this.toggle.emit();
if (!this.allowToggle) return;
this.isCollapsed = !this.isCollapsed;
diff --git a/UI/Web/src/app/shared/circular-loader/circular-loader.component.ts b/UI/Web/src/app/shared/circular-loader/circular-loader.component.ts
index f03ae2e04..ec75252ef 100644
--- a/UI/Web/src/app/shared/circular-loader/circular-loader.component.ts
+++ b/UI/Web/src/app/shared/circular-loader/circular-loader.component.ts
@@ -1,5 +1,5 @@
import {ChangeDetectionStrategy, Component, Input} from '@angular/core';
-import {CommonModule, NgClass, NgStyle} from "@angular/common";
+import {NgClass, NgStyle} from "@angular/common";
import {NgCircleProgressModule } from "ng-circle-progress";
@Component({
diff --git a/UI/Web/src/app/shared/person-badge/person-badge.component.scss b/UI/Web/src/app/shared/person-badge/person-badge.component.scss
index f291ff69a..4e7e7d1a8 100644
--- a/UI/Web/src/app/shared/person-badge/person-badge.component.scss
+++ b/UI/Web/src/app/shared/person-badge/person-badge.component.scss
@@ -8,7 +8,7 @@
display: inline-block;
cursor: pointer;
width: 100px;
- height: 100px;
+ word-break: break-word;
i {
font-size: 2rem;
diff --git a/UI/Web/src/app/shared/series-format/series-format.component.html b/UI/Web/src/app/shared/series-format/series-format.component.html
index 2bf818a0a..a288890fa 100644
--- a/UI/Web/src/app/shared/series-format/series-format.component.html
+++ b/UI/Web/src/app/shared/series-format/series-format.component.html
@@ -1,4 +1,8 @@
-
+@if (format !== MangaFormat.UNKNOWN) {
+ @if (useTitle) {
-
-
+ } @else {
+
+ }
+
+}
diff --git a/UI/Web/src/app/shared/series-format/series-format.component.ts b/UI/Web/src/app/shared/series-format/series-format.component.ts
index c6c9ccac0..39a3b53b1 100644
--- a/UI/Web/src/app/shared/series-format/series-format.component.ts
+++ b/UI/Web/src/app/shared/series-format/series-format.component.ts
@@ -2,15 +2,15 @@ import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
import { MangaFormat } from 'src/app/_models/manga-format';
import {MangaFormatIconPipe} from "../../_pipes/manga-format-icon.pipe";
import {MangaFormatPipe} from "../../_pipes/manga-format.pipe";
-import {CommonModule} from "@angular/common";
+import {NgbTooltip} from "@ng-bootstrap/ng-bootstrap";
@Component({
selector: 'app-series-format',
standalone: true,
imports: [
- CommonModule,
MangaFormatIconPipe,
- MangaFormatPipe
+ MangaFormatPipe,
+ NgbTooltip
],
templateUrl: './series-format.component.html',
styleUrls: ['./series-format.component.scss'],
@@ -21,4 +21,8 @@ export class SeriesFormatComponent {
protected readonly MangaFormat = MangaFormat;
@Input() format: MangaFormat = MangaFormat.UNKNOWN;
+ /**
+ * Use the browser title vs ngbTooltip
+ */
+ @Input() useTitle: boolean = true;
}
diff --git a/UI/Web/src/app/sidenav/_components/side-nav-item/side-nav-item.component.scss b/UI/Web/src/app/sidenav/_components/side-nav-item/side-nav-item.component.scss
index ce979f26a..4fa4872ed 100644
--- a/UI/Web/src/app/sidenav/_components/side-nav-item/side-nav-item.component.scss
+++ b/UI/Web/src/app/sidenav/_components/side-nav-item/side-nav-item.component.scss
@@ -133,11 +133,11 @@ a {
color: var(--side-nav-text-color);
}
-@media (max-width: 576px) {
+@media (max-width: 768px) {
.side-nav-item {
align-items: center;
- padding: 15px 10px;
- height: 70px;
+ padding: 0 10px;
+ height: 55px;
font-size: 1rem;
.side-nav-text {
diff --git a/UI/Web/src/app/sidenav/_components/side-nav-item/side-nav-item.component.ts b/UI/Web/src/app/sidenav/_components/side-nav-item/side-nav-item.component.ts
index 589e9b54d..7e3dcbc77 100644
--- a/UI/Web/src/app/sidenav/_components/side-nav-item/side-nav-item.component.ts
+++ b/UI/Web/src/app/sidenav/_components/side-nav-item/side-nav-item.component.ts
@@ -153,6 +153,7 @@ export class SideNavItemComponent implements OnInit {
// If on mobile, automatically collapse the side nav after making a selection
collapseNavIfApplicable() {
if (this.utilityService.getActiveBreakpoint() < Breakpoint.Tablet) {
+ console.log('collapsing side nav');
this.navService.collapseSideNav(true);
}
}
diff --git a/UI/Web/src/app/sidenav/_components/side-nav/side-nav.component.html b/UI/Web/src/app/sidenav/_components/side-nav/side-nav.component.html
index e6315f135..ec5c475b9 100644
--- a/UI/Web/src/app/sidenav/_components/side-nav/side-nav.component.html
+++ b/UI/Web/src/app/sidenav/_components/side-nav/side-nav.component.html
@@ -1,85 +1,94 @@
@if (accountService.currentUser$ | async; as user) {
-
+
+
-
- @if (navStreams$ | async; as streams) {
- @if (showAll) {
-
-
- @if (streams.length > ItemLimit && (navService.sideNavCollapsed$ | async) === false) {
-
+ }
+
+ @if (utilityService.activeBreakpoint$ | async; as breakpoint) {
+ @if (breakpoint < Breakpoint.Tablet) {
+
+ }
+ }
+
+
-
-
-
-
-
-
- }
-
- @case (SideNavStreamType.AllSeries) {
-
- }
-
- @case (SideNavStreamType.Bookmarks) {
-
- }
-
- @case (SideNavStreamType.ReadingLists) {
-
- }
-
- @case (SideNavStreamType.Collections) {
-
- }
-
- @case (SideNavStreamType.WantToRead) {
-
- }
-
- @case (SideNavStreamType.SmartFilter) {
-
- }
-
- @case (SideNavStreamType.ExternalSource) {
-
}
}
- }
- @if (totalSize > ItemLimit && !showAll) {
-
+ @for (navStream of streams | filter: filterLibrary; track navStream.name + navStream.order) {
+ @switch (navStream.streamType) {
+ @case (SideNavStreamType.Library) {
+
+
+
+
+
+ }
+
+ @case (SideNavStreamType.AllSeries) {
+
+ }
+
+ @case (SideNavStreamType.Bookmarks) {
+
+ }
+
+ @case (SideNavStreamType.ReadingLists) {
+
+ }
+
+ @case (SideNavStreamType.Collections) {
+
+ }
+
+ @case (SideNavStreamType.WantToRead) {
+
+ }
+
+ @case (SideNavStreamType.SmartFilter) {
+
+ }
+
+ @case (SideNavStreamType.ExternalSource) {
+
+ }
+
+ }
+ }
+
+ @if (totalSize > ItemLimit && !showAll) {
+
+ }
}
+
+
-
-
+
+
+ @if (navStreams$ | async; as streams) {
+ @if (showAll) {
+
+
+ @if (streams.length > ItemLimit && (navService.sideNavCollapsed$ | async) === false) {
+
- }
-
- }
-
- @for (navStream of streams | filter: filterLibrary; track navStream.name + navStream.order) {
- @switch (navStream.streamType) {
- @case (SideNavStreamType.Library) {
-
+
+
-
+
+
+
+ @if ((accountService.hasValidLicense$ | async) === false) {
+
}
- }
-
-
- @if ((accountService.hasValidLicense$ | async) === false) {
-
- }
-
diff --git a/UI/Web/src/app/sidenav/_components/side-nav/side-nav.component.scss b/UI/Web/src/app/sidenav/_components/side-nav/side-nav.component.scss
index b4c1e471b..e69de29bb 100644
--- a/UI/Web/src/app/sidenav/_components/side-nav/side-nav.component.scss
+++ b/UI/Web/src/app/sidenav/_components/side-nav/side-nav.component.scss
@@ -1,155 +0,0 @@
-.bottom {
- position: absolute;
- bottom: 12px;
- width: 190px;
- background: var(--side-nav-bg-color);
- border-radius: var(--side-nav-border-radius);
- font-size: 12px;
- transition: width var(--side-nav-openclose-transition);
- z-index: 999;
- left: 10px;
-
- &.closed {
- width: 45px;
- overflow-x: hidden;
- overflow-y: auto;
- left: -50px;
- }
-}
-
-:host ::ng-deep .bottom .donate .side-nav-item {
- justify-content: center;
- min-height: 25px;
- align-items: center;
-
- :hover {
- background-color: unset;
- }
- }
-
-:host ::ng-deep .bottom .donate .side-nav-item span {
- flex-grow: unset !important;
- min-width: unset !important;;
-}
-
-:host ::ng-deep .bottom .donate .side-nav-item span div {
- min-width: unset !important;
-}
-
-:host ::ng-deep .bottom .donate .side-nav-item span div i{
- font-size: 12px !important;
-}
-
-
-
-:host ::ng-deep .bottom .donate .side-nav-item.closed span.phone-hidden div {
- width: 100%;
-}
-
-:host ::ng-deep .bottom .donate .side-nav-item.closed span.side-nav-text div {
- width: 0;
-}
-
-@media (max-width: 576px) {
- :host ::ng-deep .bottom .donate .side-nav-item.closed {
- display: none;
- }
-
- :host ::ng-deep .bottom .donate .side-nav-item span.phone-hidden {
- display: block !important;
- }
-}
-
-.side-nav {
- padding-bottom: 10px;
- width: 190px;
- background-color: var(--side-nav-bg-color);
- height: calc((var(--vh)*100) - 115px);
- position: fixed;
- margin: 0;
- left: 10px;
- top: 73px;
- border-radius: var(--side-nav-border-radius);
- transition: width var(--side-nav-openclose-transition), background-color var(--side-nav-bg-color-transition), border-color var(--side-nav-border-transition);
- overflow: auto;
- border: var(--side-nav-border);
-
- &.no-donate {
- height: calc((var(--vh)*100) - 82px);
- }
-
- &.hidden {
- display: none;
- opacity: 0;
- }
-
- &.closed {
- width: 55px;
- overflow-x: hidden;
- overflow-y: auto;
- background-color: var(--side-nav-closed-bg-color);
- border: var(--side-nav-border-closed);
- }
-
- .side-nav-item:first {
- border-top-left-radius: var(--side-nav-border-radius);
- border-top-right-radius: var(--side-nav-border-radius);
- }
-}
-
-@media (max-width: 576px) {
- .side-nav {
- padding: 10px 0;
- width: 55vw;
- background-color: var(--side-nav-mobile-bg-color);
- height: calc((var(--vh)*100));
- position: fixed;
- margin: 0;
- left: 0;
- top: var(--nav-mobile-offset);
- transition: width var(--side-nav-openclose-transition);
- z-index: 999;
- overflow: auto;
- border: var(--side-nav-mobile-border);
-
- &.closed {
- width: 0;
- overflow: hidden;
- box-shadow: none;
- }
-
- .side-nav-item:first {
- border-top-left-radius: var(--side-nav-border-radius);
- border-top-right-radius: var(--side-nav-border-radius);
- }
-
- &.no-donate {
- height: calc((var(--vh)*100) - var(--nav-mobile-offset));
- }
- }
-
- .bottom {
- display:none;
-
- }
-
- .side-nav-overlay {
- background-color: var(--side-nav-overlay-color);
- width: 100vw;
- height: calc((var(--vh)*100) - var(--nav-offset));
- position: absolute;
- left: 0;
- z-index: 998;
-
- &.closed {
- display: none;
- }
- }
-}
-
-.btn-close {
- margin-top: -28px;
- font-size: 0.8rem;
- position: absolute;
- right: 16px;
-}
diff --git a/UI/Web/src/app/sidenav/_components/side-nav/side-nav.component.ts b/UI/Web/src/app/sidenav/_components/side-nav/side-nav.component.ts
index 08d1654b5..4428eb41b 100644
--- a/UI/Web/src/app/sidenav/_components/side-nav/side-nav.component.ts
+++ b/UI/Web/src/app/sidenav/_components/side-nav/side-nav.component.ts
@@ -42,7 +42,7 @@ import {SettingsTabId} from "../../preference-nav/preference-nav.component";
export class SideNavComponent implements OnInit {
private readonly router = inject(Router);
- private readonly utilityService = inject(UtilityService);
+ protected readonly utilityService = inject(UtilityService);
private readonly messageHub = inject(MessageHubService);
private readonly actionService = inject(ActionService);
public readonly navService = inject(NavService);
@@ -132,8 +132,13 @@ export class SideNavComponent implements OnInit {
constructor() {
+ // Ensure that on mobile, we are collapsed by default
+ if (this.utilityService.getActiveBreakpoint() < Breakpoint.Tablet) {
+ this.navService.collapseSideNav(true);
+ }
+
this.collapseSideNavOnMobileNav$.subscribe(() => {
- this.navService.toggleSideNav();
+ this.navService.collapseSideNav(false);
this.cdRef.markForCheck();
});
}
@@ -209,4 +214,6 @@ export class SideNavComponent implements OnInit {
this.cdRef.markForCheck();
this.showAllSubject.next(false);
}
+
+ protected readonly Breakpoint = Breakpoint;
}
diff --git a/UI/Web/src/app/sidenav/preference-nav/preference-nav.component.html b/UI/Web/src/app/sidenav/preference-nav/preference-nav.component.html
index 13d407a27..3929b4fb9 100644
--- a/UI/Web/src/app/sidenav/preference-nav/preference-nav.component.html
+++ b/UI/Web/src/app/sidenav/preference-nav/preference-nav.component.html
@@ -2,20 +2,29 @@
@if (accountService.currentUser$ | async; as user) {
@if((navService.sideNavCollapsed$ | async) === false) {
-
+
-
+
+ @if (utilityService.activeBreakpoint$ | async; as breakpoint) {
+ @if (breakpoint < Breakpoint.Tablet) {
+
+ }
+ }
}
}
diff --git a/UI/Web/src/app/sidenav/preference-nav/preference-nav.component.scss b/UI/Web/src/app/sidenav/preference-nav/preference-nav.component.scss
index d4e6d62da..206b609f1 100644
--- a/UI/Web/src/app/sidenav/preference-nav/preference-nav.component.scss
+++ b/UI/Web/src/app/sidenav/preference-nav/preference-nav.component.scss
@@ -3,7 +3,7 @@
padding-bottom: 10px;
width: 190px;
background-color: var(--side-nav-bg-color);
- height: calc((var(--vh)*100) - 115px);
+ height: calc((var(--vh)*100) - 75px);
position: fixed;
margin: 0;
left: 10px;
@@ -36,16 +36,16 @@
}
}
-@media (max-width: 576px) {
+@media (max-width: 768px) {
.side-nav {
padding: 10px 0;
width: 55vw;
background-color: var(--side-nav-mobile-bg-color);
- height: calc((var(--vh)*100) - 125px);
+ height: 100dvh;
position: fixed;
margin: 0;
left: 0;
- top: var(--nav-mobile-offset);
+ top: 0;
transition: width var(--side-nav-openclose-transition);
z-index: 999;
overflow: auto;
diff --git a/UI/Web/src/app/sidenav/preference-nav/preference-nav.component.ts b/UI/Web/src/app/sidenav/preference-nav/preference-nav.component.ts
index e1499577f..a1e4eb440 100644
--- a/UI/Web/src/app/sidenav/preference-nav/preference-nav.component.ts
+++ b/UI/Web/src/app/sidenav/preference-nav/preference-nav.component.ts
@@ -4,13 +4,15 @@ import {AsyncPipe, DOCUMENT, NgClass} from "@angular/common";
import {NavService} from "../../_services/nav.service";
import {AccountService, Role} from "../../_services/account.service";
import {SideNavItemComponent} from "../_components/side-nav-item/side-nav-item.component";
-import {ActivatedRoute, RouterLink} from "@angular/router";
+import {ActivatedRoute, NavigationEnd, Router, RouterLink} from "@angular/router";
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
import {SettingFragmentPipe} from "../../_pipes/setting-fragment.pipe";
-import {map, Observable, of, shareReplay, switchMap, take} from "rxjs";
+import {map, Observable, of, shareReplay, switchMap, take, tap} from "rxjs";
import {ServerService} from "../../_services/server.service";
import {ScrobblingService} from "../../_services/scrobbling.service";
import {User} from "../../_models/user";
+import {filter} from "rxjs/operators";
+import {Breakpoint, UtilityService} from "../../shared/_services/utility.service";
export enum SettingsTabId {
@@ -82,8 +84,11 @@ export class PreferenceNavComponent implements AfterViewInit {
private readonly route = inject(ActivatedRoute);
private readonly serverService = inject(ServerService);
private readonly scrobbleService = inject(ScrobblingService);
+ private readonly router = inject(Router);
+ protected readonly utilityService = inject(UtilityService);
private readonly document = inject(DOCUMENT);
+ hasActiveLicense = false;
/**
* This links to settings.component.html which has triggers on what underlying component to render out.
*/
@@ -147,13 +152,29 @@ export class PreferenceNavComponent implements AfterViewInit {
]
}
];
+ collapseSideNavOnMobileNav$ = this.router.events.pipe(
+ filter(event => event instanceof NavigationEnd),
+ takeUntilDestroyed(this.destroyRef),
+ map(evt => evt as NavigationEnd),
+ switchMap(_ => this.utilityService.activeBreakpoint$),
+ filter((b) => b < Breakpoint.Tablet),
+ switchMap(() => this.navService.sideNavCollapsed$),
+ take(1),
+ filter(collapsed => !collapsed),
+ tap(c => {
+ this.navService.collapseSideNav(true);
+ }),
+ );
- hasActiveLicense = false;
-
constructor() {
+ this.collapseSideNavOnMobileNav$.subscribe();
+
+ // Ensure that on mobile, we are collapsed by default
+ if (this.utilityService.getActiveBreakpoint() < Breakpoint.Tablet) {
+ this.navService.collapseSideNav(true);
+ }
- this.navService.collapseSideNav(false);
this.accountService.hasValidLicense$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(res => {
if (res) {
@@ -208,4 +229,9 @@ export class PreferenceNavComponent implements AfterViewInit {
return section.children.filter(item => this.accountService.hasAnyRole(user, item.roles)).length > 0;
}
+ collapse() {
+ this.navService.toggleSideNav();
+ }
+
+ protected readonly Breakpoint = Breakpoint;
}
diff --git a/UI/Web/src/app/user-settings/manga-user-preferences/manage-user-preferences.component.html b/UI/Web/src/app/user-settings/manga-user-preferences/manage-user-preferences.component.html
index 0d5ca9dd0..1d1e34e8e 100644
--- a/UI/Web/src/app/user-settings/manga-user-preferences/manage-user-preferences.component.html
+++ b/UI/Web/src/app/user-settings/manga-user-preferences/manage-user-preferences.component.html
@@ -26,7 +26,7 @@
- @for(section of sections; track section.title + section.children.length; let idx = $index;) {
- @if (hasAnyChildren(user, section)) {
-
+ @for(section of sections; track section.title + section.children.length; let idx = $index;) {
+ @if (hasAnyChildren(user, section)) {
+
+ }
}
}
}
- }
+
+
@@ -39,7 +39,7 @@
-
+
@@ -51,7 +51,7 @@
-
+
@@ -63,7 +63,7 @@
-
+
@@ -75,7 +75,7 @@
-
+
@@ -189,7 +189,7 @@
-
+
@@ -201,7 +201,7 @@
-
+
@@ -213,7 +213,7 @@
-
+
@@ -225,7 +225,7 @@
-
+
@@ -242,7 +242,7 @@
-
+
@@ -254,7 +254,7 @@
-
+
@@ -350,12 +350,12 @@
-
+
- {{settingsForm.get('bookReaderFontSize')?.value + '%'}}
+ {{settingsForm.get('bookReaderFontSize')?.value + '%'}}
@@ -368,11 +368,11 @@
-
+
- {{settingsForm.get('bookReaderLineSpacing')?.value + '%'}}
+ {{settingsForm.get('bookReaderLineSpacing')?.value + '%'}}
@@ -385,11 +385,11 @@
-
+
+
+
- {{settingsForm.get('bookReaderMargin')?.value + '%'}}
+ {{settingsForm.get('bookReaderMargin')?.value + '%'}}
diff --git a/UI/Web/src/app/user-settings/user-holds/scrobbling-holds.component.ts b/UI/Web/src/app/user-settings/user-holds/scrobbling-holds.component.ts
index 752c870cf..b5b3c747a 100644
--- a/UI/Web/src/app/user-settings/user-holds/scrobbling-holds.component.ts
+++ b/UI/Web/src/app/user-settings/user-holds/scrobbling-holds.component.ts
@@ -13,7 +13,6 @@ import {
NgbAccordionItem
} from "@ng-bootstrap/ng-bootstrap";
import {TranslocoDirective} from "@jsverse/transloco";
-import {ListItemComponent} from "../../cards/list-item/list-item.component";
import {ImageService} from "../../_services/image.service";
import {ImageComponent} from "../../shared/image/image.component";
@@ -21,7 +20,7 @@ import {ImageComponent} from "../../shared/image/image.component";
selector: 'app-user-holds',
standalone: true,
imports: [ScrobbleEventTypePipe, NgbAccordionDirective, NgbAccordionCollapse, NgbAccordionBody,
- NgbAccordionItem, NgbAccordionHeader, TranslocoDirective, AsyncPipe, ListItemComponent, ImageComponent],
+ NgbAccordionItem, NgbAccordionHeader, TranslocoDirective, AsyncPipe, ImageComponent],
templateUrl: './scrobbling-holds.component.html',
styleUrls: ['./scrobbling-holds.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
diff --git a/UI/Web/src/app/volume-detail/volume-detail.component.html b/UI/Web/src/app/volume-detail/volume-detail.component.html
index eb0c4c701..b0d8b3b96 100644
--- a/UI/Web/src/app/volume-detail/volume-detail.component.html
+++ b/UI/Web/src/app/volume-detail/volume-detail.component.html
@@ -1,19 +1,41 @@
@if (volume && series && libraryType !== null) {
-
+
-
- @if (volume.pagesRead < volume.pages && volume.pagesRead > 0) {
-
+ @if(mobileSeriesImgBackground === 'true') {
+
+ } @else {
+
}
+ @if (volume.pagesRead < volume.pages && volume.pagesRead > 0) {
+
+ @if (currentlyReadingChapter) {
+
-
+ {{t('continue-from', {title: ContinuePointTitle})}}
+
+ }
+ }
+
+
+
}
+
@@ -93,7 +122,7 @@
+
+
+
+
+
{{t('writers-title')}}
-
-
+
{{item.name}}
@@ -103,7 +132,7 @@
{{t('cover-artists-title')}}
+
-
+
{{item.name}}
@@ -113,11 +142,44 @@
+
+
+
+
+
+ {{t('genres-title')}}
+
+
+
+
+
+ {{item.title}}
+
+
+
+
+ {{t('tags-title')}}
+
+
+
+
+ {{item.title}}
+
+
+
+
-