diff --git a/client/components/ui/MultiSelect.vue b/client/components/ui/MultiSelect.vue
index 064aa2a3..71bd9539 100644
--- a/client/components/ui/MultiSelect.vue
+++ b/client/components/ui/MultiSelect.vue
@@ -57,6 +57,12 @@ export default {
menu: null
}
},
+ watch: {
+ showMenu(newVal) {
+ if (newVal) this.setListener()
+ else this.removeListener()
+ }
+ },
computed: {
selected: {
get() {
@@ -99,7 +105,18 @@ export default {
recalcMenuPos() {
if (!this.menu) return
var boundingBox = this.$refs.inputWrapper.getBoundingClientRect()
- this.menu.style.top = boundingBox.y + boundingBox.height - 4 + 'px'
+ if (boundingBox.y > window.innerHeight - 8) {
+ // Input is off the page
+ return this.forceBlur()
+ }
+ var menuHeight = this.menu.clientHeight
+ var top = boundingBox.y + boundingBox.height - 4
+ if (top + menuHeight > window.innerHeight - 20) {
+ // Reverse menu to open upwards
+ top = boundingBox.y - menuHeight - 4
+ }
+
+ this.menu.style.top = top + 'px'
this.menu.style.left = boundingBox.x + 'px'
this.menu.style.width = boundingBox.width + 'px'
},
@@ -119,7 +136,7 @@ export default {
this.unmountMountMenu()
}
this.isFocused = true
- this.recalcMenuPos()
+ this.$nextTick(this.recalcMenuPos)
},
inputBlur() {
if (!this.isFocused) return
@@ -200,6 +217,15 @@ export default {
} else {
this.insertNewItem(this.textInput)
}
+ },
+ scroll() {
+ this.recalcMenuPos()
+ },
+ setListener() {
+ document.addEventListener('scroll', this.scroll, true)
+ },
+ removeListener() {
+ document.removeEventListener('scroll', this.scroll, true)
}
},
mounted() {}
diff --git a/client/components/ui/MultiSelectDropdown.vue b/client/components/ui/MultiSelectDropdown.vue
index f2bcf009..526fa6c2 100644
--- a/client/components/ui/MultiSelectDropdown.vue
+++ b/client/components/ui/MultiSelectDropdown.vue
@@ -103,9 +103,12 @@ export default {
},
closeMenu() {
this.showMenu = false
+ this.removeListener()
},
clickWrapper() {
this.showMenu = !this.showMenu
+ if (this.showMenu) this.setListener()
+ else this.removeListener()
},
removeItem(itemValue) {
var remaining = this.selected.filter((i) => i !== itemValue)
@@ -113,6 +116,15 @@ export default {
this.$nextTick(() => {
this.recalcMenuPos()
})
+ },
+ scroll() {
+ this.recalcMenuPos()
+ },
+ setListener() {
+ document.addEventListener('scroll', this.scroll, true)
+ },
+ removeListener() {
+ document.removeEventListener('scroll', this.scroll, true)
}
},
mounted() {}
diff --git a/client/pages/batch/index.vue b/client/pages/batch/index.vue
index 6a410cd6..4191d629 100644
--- a/client/pages/batch/index.vue
+++ b/client/pages/batch/index.vue
@@ -55,7 +55,7 @@
@@ -178,11 +178,55 @@ export default {
}
})
},
+ compareStringArrays(arr1, arr2) {
+ if (!arr1 || !arr2) return false
+ return arr1.join(',') !== arr2.join(',')
+ },
+ compareAudiobooks(newAb, origAb) {
+ const bookKeysToCheck = ['title', 'subtitle', 'narrator', 'author', 'publishYear', 'series', 'volumeNumber', 'description']
+ var newBook = newAb.book
+ var origBook = origAb.book
+ var diffObj = {}
+ for (const key in newBook) {
+ if (bookKeysToCheck.includes(key)) {
+ if (newBook[key] !== origBook[key]) {
+ if (!diffObj.book) diffObj.book = {}
+ diffObj.book[key] = newBook[key]
+ }
+ }
+ if (key === 'genres') {
+ if (this.compareStringArrays(newBook[key], origBook[key])) {
+ diffObj[key] = newBook[key]
+ }
+ }
+ }
+ if (newAb.tags && origAb.tags && newAb.tags.join(',') !== origAb.tags.join(',')) {
+ diffObj.tags = newAb.tags
+ }
+ return diffObj
+ },
saveClick() {
- this.isProcessing = true
+ var updates = []
+ for (let i = 0; i < this.audiobookCopies.length; i++) {
+ var ab = { ...this.audiobookCopies[i] }
+ var origAb = ab.originalAudiobook
+ delete ab.originalAudiobook
+ var res = this.compareAudiobooks(ab, origAb)
+ if (res && Object.keys(res).length) {
+ updates.push({
+ id: ab.id,
+ updates: res
+ })
+ }
+ }
+ if (!updates.length) {
+ return this.$toast.warning('No updates were made')
+ }
+ console.log('Pushing updates', updates)
+ this.isProcessing = true
this.$axios
- .$post('/api/books/batch/update', this.audiobookCopies)
+ .$post('/api/books/batch/update', updates)
.then((data) => {
this.isProcessing = false
if (data.updates) {
diff --git a/client/plugins/init.client.js b/client/plugins/init.client.js
index 2983b58c..61d04852 100644
--- a/client/plugins/init.client.js
+++ b/client/plugins/init.client.js
@@ -101,42 +101,6 @@ Vue.prototype.$calculateTextSize = (text, styles = {}) => {
}
}
-// function isClickedOutsideEl(clickEvent, elToCheckOutside, ignoreSelectors = [], ignoreElems = []) {
-// const isDOMElement = (element) => {
-// return element instanceof Element || element instanceof HTMLDocument
-// }
-
-// const clickedEl = clickEvent.srcElement
-// const didClickOnIgnoredEl = ignoreElems.filter((el) => el).some((element) => element.contains(clickedEl) || element.isEqualNode(clickedEl))
-// const didClickOnIgnoredSelector = ignoreSelectors.length ? ignoreSelectors.map((selector) => clickedEl.closest(selector)).reduce((curr, accumulator) => curr && accumulator, true) : false
-
-// if (isDOMElement(elToCheckOutside) && !elToCheckOutside.contains(clickedEl) && !didClickOnIgnoredEl && !didClickOnIgnoredSelector) {
-// return true
-// }
-// return false
-// }
-
-// Vue.directive('click-outside', {
-// bind: function (el, binding, vnode) {
-// let vm = vnode.context;
-// let callback = binding.value;
-// if (typeof callback !== 'function') {
-// console.error('Invalid callback', binding)
-// return
-// }
-// el['__click_outside__'] = (ev) => {
-// if (isClickedOutsideEl(ev, el)) {
-// callback.call(vm, ev)
-// }
-// }
-// document.addEventListener('click', el['__click_outside__'], false)
-// },
-// unbind: function (el, binding, vnode) {
-// document.removeEventListener('click', el['__click_outside__'], false)
-// delete el['__click_outside__']
-// }
-// })
-
Vue.prototype.$sanitizeFilename = (input, replacement = '') => {
if (typeof input !== 'string') {
return false
diff --git a/server/controllers/BookController.js b/server/controllers/BookController.js
index 1ba8f4d3..01e264c9 100644
--- a/server/controllers/BookController.js
+++ b/server/controllers/BookController.js
@@ -99,19 +99,20 @@ class BookController {
Logger.warn('User attempted to batch update without permission', req.user)
return res.sendStatus(403)
}
- var audiobooks = req.body
- if (!audiobooks || !audiobooks.length) {
+ var updatePayloads = req.body
+ if (!updatePayloads || !updatePayloads.length) {
return res.sendStatus(500)
}
var audiobooksUpdated = 0
- audiobooks = audiobooks.map((ab) => {
- var _ab = this.db.audiobooks.find(__ab => __ab.id === ab.id)
- if (!_ab) return null
- var hasUpdated = _ab.update(ab)
+ var audiobooks = updatePayloads.map((up) => {
+ var audiobookUpdates = up.updates
+ var ab = this.db.audiobooks.find(_ab => _ab.id === up.id)
+ if (!ab) return null
+ var hasUpdated = ab.update(audiobookUpdates)
if (!hasUpdated) return null
audiobooksUpdated++
- return _ab
+ return ab
}).filter(ab => ab)
if (audiobooksUpdated) {