From a5999fb9df566656cd00c35dcee7e4cc12134130 Mon Sep 17 00:00:00 2001 From: Rapha149 <49787110+Rapha149@users.noreply.github.com> Date: Mon, 16 Mar 2026 19:46:45 +0100 Subject: [PATCH 1/3] Add favorite property for items and associated filters. --- client/components/cards/LazyBookCard.vue | 35 ++++++++++ .../controls/LibraryFilterSelect.vue | 10 +++ client/pages/item/_id/index.vue | 22 +++++++ .../pages/library/_library/podcast/latest.vue | 26 ++++++-- client/store/user.js | 4 ++ client/strings/de.json | 2 + client/strings/en-us.json | 2 + server/Database.js | 6 ++ server/controllers/MeController.js | 61 +++++++++++++++++ server/models/User.js | 53 +++++++++++++-- server/models/UserFavorite.js | 66 +++++++++++++++++++ server/routers/ApiRouter.js | 2 + .../utils/queries/libraryItemsBookFilters.js | 15 ++++- .../queries/libraryItemsPodcastFilters.js | 15 ++++- 14 files changed, 308 insertions(+), 11 deletions(-) create mode 100644 server/models/UserFavorite.js diff --git a/client/components/cards/LazyBookCard.vue b/client/components/cards/LazyBookCard.vue index 51f657db..cb3ee600 100644 --- a/client/components/cards/LazyBookCard.vue +++ b/client/components/cards/LazyBookCard.vue @@ -65,6 +65,14 @@
{{ ebookFormat }}
+ + +
+ +
@@ -93,6 +101,14 @@ + +
+ +
+

#{{ seriesSequence }}

@@ -653,6 +669,9 @@ export default { mediaItemShare() { return this._libraryItem.mediaItemShare || null }, + isFavorite() { + return this.store.getters['user/getIsLibraryItemFavorite'](this.libraryItemId) + }, showSubtitles() { return !this.isPodcast && this.store.getters['user/getUserSetting']('showSubtitles') } @@ -757,6 +776,22 @@ export default { toast.error(updatePayload.isFinished ? this.$strings.ToastItemMarkedAsFinishedFailed : this.$strings.ToastItemMarkedAsNotFinishedFailed) }) }, + toggleFavorite() { + const axios = this.$axios || this.$nuxt.$axios + const endpoint = `/api/me/item/${this.libraryItemId}/favorite` + + if (this.isFavorite) { + axios.$delete(endpoint).catch(error => { + console.error('Failed to remove favorite', error) + this.$toast.error('Failed to remove from favorites') + }) + } else { + axios.$post(endpoint).catch(error => { + console.error('Failed to add favorite', error) + this.$toast.error('Failed to add to favorites') + }) + } + }, editPodcast() { this.$emit('editPodcast', this.libraryItem) }, diff --git a/client/components/controls/LibraryFilterSelect.vue b/client/components/controls/LibraryFilterSelect.vue index 4834a1a2..837b6c89 100644 --- a/client/components/controls/LibraryFilterSelect.vue +++ b/client/components/controls/LibraryFilterSelect.vue @@ -158,6 +158,11 @@ export default { text: this.$strings.LabelAll, value: 'all' }, + { + text: this.$strings.LabelFavorite, + value: 'favorite', + sublist: false + }, { text: this.$strings.LabelGenre, textPlural: this.$strings.LabelGenres, @@ -266,6 +271,11 @@ export default { text: this.$strings.LabelAll, value: 'all' }, + { + text: this.$strings.LabelFavorite, + value: 'favorite', + sublist: false + }, { text: this.$strings.LabelGenre, textPlural: this.$strings.LabelGenres, diff --git a/client/pages/item/_id/index.vue b/client/pages/item/_id/index.vue index 1d8f0f20..437b7390 100644 --- a/client/pages/item/_id/index.vue +++ b/client/pages/item/_id/index.vue @@ -29,6 +29,9 @@ {{ title }} +
@@ -228,6 +231,9 @@ export default { isAbridged() { return !!this.mediaMetadata.abridged }, + isFavorite() { + return this.$store.getters['user/getIsLibraryItemFavorite'](this.libraryItemId) + }, showPlayButton() { if (this.isMissing || this.isInvalid) return false if (this.isPodcast) return this.podcastEpisodes.length @@ -530,6 +536,22 @@ export default { this.$toast.error(updatePayload.isFinished ? this.$strings.ToastItemMarkedAsFinishedFailed : this.$strings.ToastItemMarkedAsNotFinishedFailed) }) }, + toggleFavorite() { + const axios = this.$axios || this.$nuxt.$axios + const endpoint = `/api/me/item/${this.libraryItemId}/favorite` + + if (this.isFavorite) { + axios.$delete(endpoint).catch(error => { + console.error('Failed to remove favorite', error) + this.$toast.error('Failed to remove from favorites') + }) + } else { + axios.$post(endpoint).catch(error => { + console.error('Failed to add favorite', error) + this.$toast.error('Failed to add to favorites') + }) + } + }, playItem(startTime = null) { let episodeId = null const queueItems = [] diff --git a/client/pages/library/_library/podcast/latest.vue b/client/pages/library/_library/podcast/latest.vue index 4f12043e..399b7e30 100644 --- a/client/pages/library/_library/podcast/latest.vue +++ b/client/pages/library/_library/podcast/latest.vue @@ -4,9 +4,15 @@
-

{{ $strings.HeaderLatestEpisodes }}

-

{{ $strings.MessageNoEpisodes }}

-