From f22f9bd1e0b356e8ba8814aecd2f7292827178b3 Mon Sep 17 00:00:00 2001
From: Aditya Chandel <8075870+adityachandelgit@users.noreply.github.com>
Date: Wed, 3 Dec 2025 21:17:06 -0700
Subject: [PATCH] Add a user-facing option to enable or disable series view
(#1748)
---
.../custom/BookLoreUserTransformer.java | 1 +
.../booklore/model/dto/BookLoreUser.java | 1 +
.../model/dto/settings/UserSettingKey.java | 3 +-
.../book-browser/book-browser.component.html | 2 +-
.../book-browser/book-browser.component.ts | 4 ++
.../book-card/book-card.component.html | 8 ++--
.../book-card/book-card.component.ts | 6 ++-
.../bookdrop-file-review.component.scss | 1 +
.../metadata-viewer.component.html | 26 ++++++-----
.../metadata-viewer.component.ts | 2 +-
.../settings/user-management/user.service.ts | 3 +-
.../meta-center-view-mode-component.html | 44 +++++++++++++++++--
.../meta-center-view-mode-component.scss | 4 +-
.../meta-center-view-mode-component.ts | 25 +++++++++++
...sidebar-sorting-preferences.component.scss | 4 +-
15 files changed, 106 insertions(+), 28 deletions(-)
diff --git a/booklore-api/src/main/java/com/adityachandel/booklore/mapper/custom/BookLoreUserTransformer.java b/booklore-api/src/main/java/com/adityachandel/booklore/mapper/custom/BookLoreUserTransformer.java
index 9b03d85d..ff8172ce 100644
--- a/booklore-api/src/main/java/com/adityachandel/booklore/mapper/custom/BookLoreUserTransformer.java
+++ b/booklore-api/src/main/java/com/adityachandel/booklore/mapper/custom/BookLoreUserTransformer.java
@@ -69,6 +69,7 @@ public class BookLoreUserTransformer {
switch (settingKey) {
case FILTER_SORTING_MODE -> userSettings.setFilterSortingMode(value);
case METADATA_CENTER_VIEW_MODE -> userSettings.setMetadataCenterViewMode(value);
+ case ENABLE_SERIES_VIEW -> userSettings.setEnableSeriesView(Boolean.parseBoolean(value));
}
}
} catch (IllegalArgumentException e) {
diff --git a/booklore-api/src/main/java/com/adityachandel/booklore/model/dto/BookLoreUser.java b/booklore-api/src/main/java/com/adityachandel/booklore/model/dto/BookLoreUser.java
index e0d3d114..36c7bcb0 100644
--- a/booklore-api/src/main/java/com/adityachandel/booklore/model/dto/BookLoreUser.java
+++ b/booklore-api/src/main/java/com/adityachandel/booklore/model/dto/BookLoreUser.java
@@ -52,6 +52,7 @@ public class BookLoreUser {
public String filterSortingMode;
public String metadataCenterViewMode;
public boolean koReaderEnabled;
+ public boolean enableSeriesView;
public DashboardConfig dashboardConfig;
@Data
diff --git a/booklore-api/src/main/java/com/adityachandel/booklore/model/dto/settings/UserSettingKey.java b/booklore-api/src/main/java/com/adityachandel/booklore/model/dto/settings/UserSettingKey.java
index 0b1faf59..73ad2fcf 100644
--- a/booklore-api/src/main/java/com/adityachandel/booklore/model/dto/settings/UserSettingKey.java
+++ b/booklore-api/src/main/java/com/adityachandel/booklore/model/dto/settings/UserSettingKey.java
@@ -16,7 +16,8 @@ public enum UserSettingKey {
DASHBOARD_CONFIG("dashboardConfig", true),
FILTER_SORTING_MODE("filterSortingMode", false),
- METADATA_CENTER_VIEW_MODE("metadataCenterViewMode", false);
+ METADATA_CENTER_VIEW_MODE("metadataCenterViewMode", false),
+ ENABLE_SERIES_VIEW("enableSeriesView", false);
private final String dbKey;
diff --git a/booklore-ui/src/app/features/book/components/book-browser/book-browser.component.html b/booklore-ui/src/app/features/book/components/book-browser/book-browser.component.html
index 3b935e2d..1c5a228d 100644
--- a/booklore-ui/src/app/features/book/components/book-browser/book-browser.component.html
+++ b/booklore-ui/src/app/features/book/components/book-browser/book-browser.component.html
@@ -262,7 +262,7 @@
[index]="books.indexOf(book)"
[book]="book"
[isCheckboxEnabled]="true"
- [readButtonHidden]="!!book.seriesCount"
+ [seriesViewEnabled]="seriesViewEnabled"
[onBookSelect]="handleBookSelect.bind(this)"
[isSeriesCollapsed]="seriesCollapseFilter.isSeriesCollapsed"
(checkboxClick)="onCheckboxClicked($event)"
diff --git a/booklore-ui/src/app/features/book/components/book-browser/book-browser.component.ts b/booklore-ui/src/app/features/book/components/book-browser/book-browser.component.ts
index 93348976..ee2d76cf 100644
--- a/booklore-ui/src/app/features/book/components/book-browser/book-browser.component.ts
+++ b/booklore-ui/src/app/features/book/components/book-browser/book-browser.component.ts
@@ -841,4 +841,8 @@ export class BookBrowserComponent implements OnInit, AfterViewInit {
})
);
}
+
+ get seriesViewEnabled(): boolean {
+ return Boolean(this.userService.getCurrentUser()?.userSettings?.enableSeriesView);
+ }
}
diff --git a/booklore-ui/src/app/features/book/components/book-browser/book-card/book-card.component.html b/booklore-ui/src/app/features/book/components/book-browser/book-card/book-card.component.html
index da7db282..06dc1192 100644
--- a/booklore-ui/src/app/features/book/components/book-browser/book-card/book-card.component.html
+++ b/booklore-ui/src/app/features/book/components/book-browser/book-card/book-card.component.html
@@ -4,11 +4,11 @@
(mouseout)="isHovered = false"
(click)="onCardClick($event)">
-
+
![]()
}
- @if (book.seriesCount && book.seriesCount! >= 1) {
+ @if (isSeriesViewActive()) {
} @else {
}
-
+
@if (isCheckboxEnabled) {
void;
@Input() isSelected: boolean = false;
@Input() bottomBarHidden: boolean = false;
- @Input() readButtonHidden: boolean = false;
+ @Input() seriesViewEnabled: boolean = false;
@Input() isSeriesCollapsed: boolean = false;
@ViewChild('checkboxElem') checkboxElem!: ElementRef;
@@ -768,4 +768,8 @@ export class BookCardComponent implements OnInit, OnChanges, OnDestroy {
shouldShowStatusIcon(): boolean {
return this.readStatusHelper.shouldShowStatusIcon(this.book.readStatus);
}
+
+ isSeriesViewActive(): boolean {
+ return this.seriesViewEnabled && !!this.book.seriesCount && this.book.seriesCount! >= 1;
+ }
}
diff --git a/booklore-ui/src/app/features/bookdrop/component/bookdrop-file-review/bookdrop-file-review.component.scss b/booklore-ui/src/app/features/bookdrop/component/bookdrop-file-review/bookdrop-file-review.component.scss
index 7b321471..3ae26811 100644
--- a/booklore-ui/src/app/features/bookdrop/component/bookdrop-file-review/bookdrop-file-review.component.scss
+++ b/booklore-ui/src/app/features/bookdrop/component/bookdrop-file-review/bookdrop-file-review.component.scss
@@ -22,6 +22,7 @@
display: flex;
flex-direction: column;
border-bottom: 1px solid var(--p-content-border-color);
+ margin-bottom: 1.5rem;
@media (min-width: 768px) {
padding: 1.5rem 1.5rem 1rem;
diff --git a/booklore-ui/src/app/features/metadata/component/book-metadata-center/metadata-viewer/metadata-viewer.component.html b/booklore-ui/src/app/features/metadata/component/book-metadata-center/metadata-viewer/metadata-viewer.component.html
index 50f8959f..693432da 100644
--- a/booklore-ui/src/app/features/metadata/component/book-metadata-center/metadata-viewer/metadata-viewer.component.html
+++ b/booklore-ui/src/app/features/metadata/component/book-metadata-center/metadata-viewer/metadata-viewer.component.html
@@ -125,7 +125,7 @@
[pTooltip]="getRatingTooltip(book, 'amazon')"
tooltipPosition="top"
>
-
+
{{ getRatingPercent(book.metadata!.amazonRating) }}%
}
@@ -139,7 +139,7 @@
[pTooltip]="getRatingTooltip(book, 'goodreads')"
tooltipPosition="top"
>
-
+
{{ getRatingPercent(book.metadata!.goodreadsRating) }}%
}
@@ -153,7 +153,7 @@
[pTooltip]="getRatingTooltip(book, 'hardcover')"
tooltipPosition="top"
>
-
+
{{ getRatingPercent(book.metadata!.hardcoverRating) }}%
}
@@ -165,7 +165,7 @@
target="_blank"
rel="noopener noreferrer"
>
-
+
}
@@ -176,7 +176,7 @@
target="_blank"
rel="noopener noreferrer"
>
-
+
}
@@ -328,7 +328,9 @@
@if (book?.metadata?.isbn13 || book?.metadata?.isbn10) {
{{ book.metadata!.isbn13 }}
- @if (book?.metadata?.isbn13 && book?.metadata?.isbn10) { / }
+ @if (book?.metadata?.isbn13 && book?.metadata?.isbn10) {
+ /
+ }
{{ book.metadata!.isbn10 }}
} @else {
@@ -523,7 +525,7 @@
- @if (bookInSeries.length > 0) {
+ @if (bookInSeries.length > 1) {
More in Series
@@ -541,12 +543,14 @@
- @if (bookInSeries.length > 0) {
+ @if (bookInSeries.length > 1) {
}
diff --git a/booklore-ui/src/app/features/metadata/component/book-metadata-center/metadata-viewer/metadata-viewer.component.ts b/booklore-ui/src/app/features/metadata/component/book-metadata-center/metadata-viewer/metadata-viewer.component.ts
index 42b82bd0..57e1fd71 100644
--- a/booklore-ui/src/app/features/metadata/component/book-metadata-center/metadata-viewer/metadata-viewer.component.ts
+++ b/booklore-ui/src/app/features/metadata/component/book-metadata-center/metadata-viewer/metadata-viewer.component.ts
@@ -348,7 +348,7 @@ export class MetadataViewerComponent implements OnInit, OnChanges {
}
get defaultTabValue(): number {
- return this.bookInSeries && this.bookInSeries.length > 0 ? 1 : 2;
+ return this.bookInSeries && this.bookInSeries.length > 1 ? 1 : 2;
}
toggleExpand(): void {
diff --git a/booklore-ui/src/app/features/settings/user-management/user.service.ts b/booklore-ui/src/app/features/settings/user-management/user.service.ts
index 4b56535e..af4b9dc8 100644
--- a/booklore-ui/src/app/features/settings/user-management/user.service.ts
+++ b/booklore-ui/src/app/features/settings/user-management/user.service.ts
@@ -129,6 +129,7 @@ export interface UserSettings {
sidebarShelfSorting: SidebarShelfSorting;
filterSortingMode: 'alphabetical' | 'count';
metadataCenterViewMode: 'route' | 'dialog';
+ enableSeriesView: boolean;
entityViewPreferences: EntityViewPreferences;
tableColumnPreference?: TableColumnPreference[];
dashboardConfig?: DashboardConfig;
@@ -173,7 +174,7 @@ export class UserService {
private http = inject(HttpClient);
private authService = inject(AuthService);
- private userStateSubject = new BehaviorSubject
({
+ userStateSubject = new BehaviorSubject({
user: null,
loaded: false,
error: null,
diff --git a/booklore-ui/src/app/features/settings/view-preferences-parent/meta-center-view-mode/meta-center-view-mode-component.html b/booklore-ui/src/app/features/settings/view-preferences-parent/meta-center-view-mode/meta-center-view-mode-component.html
index af137288..37aa219e 100644
--- a/booklore-ui/src/app/features/settings/view-preferences-parent/meta-center-view-mode/meta-center-view-mode-component.html
+++ b/booklore-ui/src/app/features/settings/view-preferences-parent/meta-center-view-mode/meta-center-view-mode-component.html
@@ -1,11 +1,11 @@
@@ -16,7 +16,7 @@
+
+
+
+
+
+
+
+
+ Enable or disable the series view mode for displaying series information.
+
+
+
diff --git a/booklore-ui/src/app/features/settings/view-preferences-parent/meta-center-view-mode/meta-center-view-mode-component.scss b/booklore-ui/src/app/features/settings/view-preferences-parent/meta-center-view-mode/meta-center-view-mode-component.scss
index b1bf36c4..16e080cb 100644
--- a/booklore-ui/src/app/features/settings/view-preferences-parent/meta-center-view-mode/meta-center-view-mode-component.scss
+++ b/booklore-ui/src/app/features/settings/view-preferences-parent/meta-center-view-mode/meta-center-view-mode-component.scss
@@ -10,8 +10,8 @@
}
.settings-header {
- margin-top: 1rem;
- margin-bottom: 2rem;
+ margin-top: 1.25rem;
+ margin-bottom: 1rem;
}
.settings-title {
diff --git a/booklore-ui/src/app/features/settings/view-preferences-parent/meta-center-view-mode/meta-center-view-mode-component.ts b/booklore-ui/src/app/features/settings/view-preferences-parent/meta-center-view-mode/meta-center-view-mode-component.ts
index 26a10ee5..c4fce8a2 100644
--- a/booklore-ui/src/app/features/settings/view-preferences-parent/meta-center-view-mode/meta-center-view-mode-component.ts
+++ b/booklore-ui/src/app/features/settings/view-preferences-parent/meta-center-view-mode/meta-center-view-mode-component.ts
@@ -19,6 +19,7 @@ import {Subject} from 'rxjs';
})
export class MetaCenterViewModeComponent implements OnInit, OnDestroy {
viewMode: 'route' | 'dialog' = 'route';
+ seriesViewMode: boolean = false;
private userService = inject(UserService);
private messageService = inject(MessageService);
@@ -34,6 +35,10 @@ export class MetaCenterViewModeComponent implements OnInit, OnDestroy {
if (preference === 'dialog' || preference === 'route') {
this.viewMode = preference;
}
+ const seriesPref = userState.user?.userSettings?.enableSeriesView;
+ if (typeof seriesPref === 'boolean') {
+ this.seriesViewMode = seriesPref;
+ }
});
}
@@ -47,6 +52,11 @@ export class MetaCenterViewModeComponent implements OnInit, OnDestroy {
this.savePreference(value);
}
+ onSeriesViewModeChange(value: boolean): void {
+ this.seriesViewMode = value;
+ this.saveSeriesViewPreference(value);
+ }
+
private savePreference(value: 'route' | 'dialog'): void {
const user = this.userService.getCurrentUser();
if (!user) return;
@@ -61,4 +71,19 @@ export class MetaCenterViewModeComponent implements OnInit, OnDestroy {
life: 1500,
});
}
+
+ private saveSeriesViewPreference(value: boolean): void {
+ const user = this.userService.getCurrentUser();
+ if (!user) return;
+
+ user.userSettings.enableSeriesView = value;
+ this.userService.updateUserSetting(user.id, 'enableSeriesView', value);
+
+ this.messageService.add({
+ severity: 'success',
+ summary: 'Preferences Updated',
+ detail: 'Your series view mode preference has been saved.',
+ life: 1500,
+ });
+ }
}
diff --git a/booklore-ui/src/app/features/settings/view-preferences-parent/sidebar-sorting-preferences/sidebar-sorting-preferences.component.scss b/booklore-ui/src/app/features/settings/view-preferences-parent/sidebar-sorting-preferences/sidebar-sorting-preferences.component.scss
index 046b2822..606a4b35 100644
--- a/booklore-ui/src/app/features/settings/view-preferences-parent/sidebar-sorting-preferences/sidebar-sorting-preferences.component.scss
+++ b/booklore-ui/src/app/features/settings/view-preferences-parent/sidebar-sorting-preferences/sidebar-sorting-preferences.component.scss
@@ -10,8 +10,8 @@
}
.settings-header {
- margin-top: 1rem;
- margin-bottom: 2rem;
+ margin-top: 1.25rem;
+ margin-bottom: 1rem;
}
.settings-title {