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" > - Amazon + Amazon {{ getRatingPercent(book.metadata!.amazonRating) }}% } @@ -139,7 +139,7 @@ [pTooltip]="getRatingTooltip(book, 'goodreads')" tooltipPosition="top" > - Goodreads + Goodreads {{ getRatingPercent(book.metadata!.goodreadsRating) }}% } @@ -153,7 +153,7 @@ [pTooltip]="getRatingTooltip(book, 'hardcover')" tooltipPosition="top" > - Hardcover + Hardcover {{ getRatingPercent(book.metadata!.hardcoverRating) }}% } @@ -165,7 +165,7 @@ target="_blank" rel="noopener noreferrer" > - Google Books + Google Books } @@ -176,7 +176,7 @@ target="_blank" rel="noopener noreferrer" > - Comic Vine + Comic Vine }
@@ -328,7 +328,9 @@ @if (book?.metadata?.isbn13 || 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) {
@for (bookInSeriesItem of bookInSeries; track bookInSeriesItem) { -
- -
+ @if (bookInSeriesItem.id !== book.id) { +
+ +
+ } }
} 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 @@

- - Open Metadata Center + + View Preferences

- Decide how you want to view book details - inline as a full page or in a pop-up dialog. + Customize how you view metadata and series information.

@@ -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 {