diff --git a/client/components/modals/podcast/tabs/EpisodeDetails.vue b/client/components/modals/podcast/tabs/EpisodeDetails.vue index 9a2eb77d..642cd046 100644 --- a/client/components/modals/podcast/tabs/EpisodeDetails.vue +++ b/client/components/modals/podcast/tabs/EpisodeDetails.vue @@ -2,10 +2,10 @@
- +
- +
@@ -14,10 +14,10 @@
- +
- +
diff --git a/client/components/ui/MultiSelectQueryInput.vue b/client/components/ui/MultiSelectQueryInput.vue index 25107b79..7c8e7005 100644 --- a/client/components/ui/MultiSelectQueryInput.vue +++ b/client/components/ui/MultiSelectQueryInput.vue @@ -215,6 +215,10 @@ export default { inputBlur() { if (!this.isFocused) return + if (typeof this.textInput === 'string') { + this.textInput = this.textInput.trim() + } + setTimeout(() => { if (document.activeElement === this.$refs.input) { return @@ -231,6 +235,11 @@ export default { }, forceBlur() { this.isFocused = false + + if (typeof this.textInput === 'string') { + this.textInput = this.textInput.trim() + } + if (this.textInput) this.submitForm() if (this.$refs.input) this.$refs.input.blur() }, @@ -289,11 +298,12 @@ export default { this.selectedMenuItemIndex = null }, submitForm() { - if (!this.textInput) return + if (!this.textInput || !this.textInput.trim?.()) return + + this.textInput = this.textInput.trim() - const cleaned = this.textInput.trim() const matchesItem = this.items.find((i) => { - return i.name === cleaned + return i.name === this.textInput }) if (matchesItem) { diff --git a/client/components/ui/TextInput.vue b/client/components/ui/TextInput.vue index 6e621870..23478dee 100644 --- a/client/components/ui/TextInput.vue +++ b/client/components/ui/TextInput.vue @@ -40,7 +40,8 @@ export default { showCopy: Boolean, step: [String, Number], min: [String, Number], - customInputClass: String + customInputClass: String, + trimWhitespace: Boolean }, data() { return { @@ -101,9 +102,13 @@ export default { this.$emit('focus') }, blurred() { + if (this.trimWhitespace && typeof this.inputValue === 'string') { + this.inputValue = this.inputValue.trim() + } this.isFocused = false this.$emit('blur') }, + change(e) { this.$emit('change', e.target.value) }, diff --git a/client/components/ui/TextInputWithLabel.vue b/client/components/ui/TextInputWithLabel.vue index a10394bd..3d1c8209 100644 --- a/client/components/ui/TextInputWithLabel.vue +++ b/client/components/ui/TextInputWithLabel.vue @@ -6,7 +6,7 @@ {{ note }} - +
@@ -24,7 +24,8 @@ export default { readonly: Boolean, disabled: Boolean, inputClass: String, - showCopy: Boolean + showCopy: Boolean, + trimWhitespace: Boolean }, data() { return {} diff --git a/client/components/widgets/BookDetailsEdit.vue b/client/components/widgets/BookDetailsEdit.vue index fa26bcf5..a7af02c8 100644 --- a/client/components/widgets/BookDetailsEdit.vue +++ b/client/components/widgets/BookDetailsEdit.vue @@ -3,10 +3,10 @@
- +
- +
@@ -42,19 +42,19 @@
- +
- +
- +
- +
diff --git a/client/components/widgets/PodcastDetailsEdit.vue b/client/components/widgets/PodcastDetailsEdit.vue index 389ca894..e665dc4e 100644 --- a/client/components/widgets/PodcastDetailsEdit.vue +++ b/client/components/widgets/PodcastDetailsEdit.vue @@ -3,14 +3,14 @@
- +
- +
- + @@ -25,13 +25,13 @@
- +
- +
- +
diff --git a/client/pages/batch/index.vue b/client/pages/batch/index.vue index 263dee58..575f1db1 100644 --- a/client/pages/batch/index.vue +++ b/client/pages/batch/index.vue @@ -22,7 +22,7 @@
- +
@@ -31,7 +31,7 @@
- +
@@ -51,11 +51,11 @@
- +
- +
diff --git a/server/controllers/CollectionController.js b/server/controllers/CollectionController.js index 00b82ce9..475adfe0 100644 --- a/server/controllers/CollectionController.js +++ b/server/controllers/CollectionController.js @@ -251,6 +251,7 @@ class CollectionController { /** * DELETE: /api/collections/:id/book/:bookId * Remove a single book from a collection. Re-order books + * Users with update permission can remove books from collections * TODO: bookId is actually libraryItemId. Clients need updating to use bookId * * @param {CollectionControllerRequest} req @@ -427,7 +428,8 @@ class CollectionController { req.collection = collection } - if (req.method == 'DELETE' && !req.user.canDelete) { + // Users with update permission can remove books from collections + if (req.method == 'DELETE' && !req.params.bookId && !req.user.canDelete) { Logger.warn(`[CollectionController] User "${req.user.username}" attempted to delete without permission`) return res.sendStatus(403) } else if ((req.method == 'PATCH' || req.method == 'POST') && !req.user.canUpdate) { diff --git a/server/utils/podcastUtils.js b/server/utils/podcastUtils.js index d6983047..485fccfb 100644 --- a/server/utils/podcastUtils.js +++ b/server/utils/podcastUtils.js @@ -311,6 +311,7 @@ module.exports.getPodcastFeed = (feedUrl, excludeEpisodeMetadata = false) => { responseType: 'arraybuffer', headers: { Accept: 'application/rss+xml, application/xhtml+xml, application/xml, */*;q=0.8', + 'Accept-Encoding': 'gzip, compress, deflate', 'User-Agent': userAgent }, httpAgent: global.DisableSsrfRequestFilter?.(feedUrl) ? null : ssrfFilter(feedUrl),