diff --git a/client/components/app/BookShelfRow.vue b/client/components/app/BookShelfRow.vue index 3af76e3d..217c67a3 100644 --- a/client/components/app/BookShelfRow.vue +++ b/client/components/app/BookShelfRow.vue @@ -22,7 +22,7 @@
@@ -43,6 +43,7 @@ + @@ -64,7 +65,9 @@ export default { canScrollLeft: false, isScrolling: false, scrollTimer: null, - updateTimer: null + updateTimer: null, + showAuthorModal: false, + selectedAuthor: null } }, watch: { @@ -94,6 +97,10 @@ export default { } }, methods: { + editAuthor(author) { + this.selectedAuthor = author + this.showAuthorModal = true + }, editBook(audiobook) { var bookIds = this.shelf.entities.map((e) => e.id) this.$store.commit('setBookshelfBookIds', bookIds) diff --git a/client/components/app/SideRail.vue b/client/components/app/SideRail.vue index 082c9e61..77e32c55 100644 --- a/client/components/app/SideRail.vue +++ b/client/components/app/SideRail.vue @@ -39,7 +39,7 @@
- +
- - - - - - - -
-
- -
+
@@ -27,8 +11,11 @@
-
- search +
+ search +
+
+ edit
@@ -56,8 +43,7 @@ export default { data() { return { searching: false, - isHovering: false, - showCoverBg: false + isHovering: false } }, computed: { @@ -73,28 +59,8 @@ export default { name() { return this._author.name || '' }, - imagePath() { - return this._author.imagePath || null - }, - description() { - return this._author.description - }, - updatedAt() { - return this._author.updatedAt - }, numBooks() { return this._author.numBooks || 0 - }, - imgSrc() { - if (!this.imagePath) return null - if (process.env.NODE_ENV !== 'production') { - // Testing - return `http://localhost:3333/api/authors/${this.authorId}/image?token=${this.userToken}&ts=${this.updatedAt}` - } - return `/api/authors/${this.authorId}/image?token=${this.userToken}&ts=${this.updatedAt}` - }, - aspectRatio() { - return this.height / this.width } }, methods: { @@ -104,19 +70,6 @@ export default { mouseout() { this.isHovering = false }, - imageLoaded() { - if (this.$refs.img) { - var { naturalWidth, naturalHeight } = this.$refs.img - var aspectRatio = naturalHeight / naturalWidth - var arDiff = Math.abs(aspectRatio - this.aspectRatio) - - if (arDiff > 0.15) { - this.showCoverBg = true - } else { - this.showCoverBg = false - } - } - }, async searchAuthor() { this.searching = true var response = await this.$axios.$post(`/api/authors/${this.authorId}/match`, { q: this.name }).catch((error) => { diff --git a/client/components/cards/AuthorSearchCard.vue b/client/components/cards/AuthorSearchCard.vue index 10d184b5..82029ac6 100644 --- a/client/components/cards/AuthorSearchCard.vue +++ b/client/components/cards/AuthorSearchCard.vue @@ -1,26 +1,8 @@ @@ -34,7 +35,9 @@ export default { data() { return { loading: true, - authors: [] + authors: [], + showAuthorModal: false, + selectedAuthor: null } }, computed: { @@ -59,6 +62,9 @@ export default { } }, authorUpdated(author) { + if (this.selectedAuthor && this.selectedAuthor.id === author.id) { + this.selectedAuthor = author + } this.authors = this.authors.map((au) => { if (au.id === author.id) { return author @@ -68,6 +74,10 @@ export default { }, authorRemoved(author) { this.authors = this.authors.filter((au) => au.id !== author.id) + }, + editAuthor(author) { + this.selectedAuthor = author + this.showAuthorModal = true } }, mounted() { diff --git a/client/tailwind.config.js b/client/tailwind.config.js index 53644f92..5191bdaa 100644 --- a/client/tailwind.config.js +++ b/client/tailwind.config.js @@ -16,7 +16,8 @@ module.exports = { extend: { height: { '7.5': '1.75rem', - '18': '4.5rem' + '18': '4.5rem', + '45': '11.25rem' }, width: { '18': '4.5rem' diff --git a/server/ApiController.js b/server/ApiController.js index a050bb69..3f0d7db5 100644 --- a/server/ApiController.js +++ b/server/ApiController.js @@ -152,6 +152,7 @@ class ApiController { // this.router.get('/authors/search', AuthorController.search.bind(this)) this.router.get('/authors/:id', AuthorController.middleware.bind(this), AuthorController.findOne.bind(this)) + this.router.patch('/authors/:id', AuthorController.middleware.bind(this), AuthorController.update.bind(this)) this.router.post('/authors/:id/match', AuthorController.middleware.bind(this), AuthorController.match.bind(this)) this.router.get('/authors/:id/image', AuthorController.middleware.bind(this), AuthorController.getImage.bind(this)) diff --git a/server/CacheManager.js b/server/CacheManager.js index 4de0a22a..f84052bb 100644 --- a/server/CacheManager.js +++ b/server/CacheManager.js @@ -43,13 +43,21 @@ class CacheManager { readStream.pipe(res) } - async purgeCoverCache(libraryItemId) { + purgeCoverCache(libraryItemId) { + return this.purgeEntityCache(libraryItemId, this.CoverCachePath) + } + + purgeImageCache(entityId) { + return this.purgeEntityCache(entityId, this.ImageCachePath) + } + + async purgeEntityCache(entityId, cachePath) { // If purgeAll has been called... The cover cache directory no longer exists - await fs.ensureDir(this.CoverCachePath) - return Promise.all((await fs.readdir(this.CoverCachePath)).reduce((promises, file) => { - if (file.startsWith(libraryItemId)) { + await fs.ensureDir(cachePath) + return Promise.all((await fs.readdir(cachePath)).reduce((promises, file) => { + if (file.startsWith(entityId)) { Logger.debug(`[CacheManager] Going to purge ${file}`); - promises.push(this.removeCache(Path.join(this.CoverCachePath, file))) + promises.push(this.removeCache(Path.join(cachePath, file))) } return promises }, [])) diff --git a/server/CoverController.js b/server/CoverController.js index 4af620b9..bd0b344f 100644 --- a/server/CoverController.js +++ b/server/CoverController.js @@ -248,6 +248,5 @@ class CoverController { } return false } - } module.exports = CoverController \ No newline at end of file diff --git a/server/controllers/AuthorController.js b/server/controllers/AuthorController.js index 5993f370..ea786658 100644 --- a/server/controllers/AuthorController.js +++ b/server/controllers/AuthorController.js @@ -8,6 +8,29 @@ class AuthorController { return res.json(req.author) } + async update(req, res) { + var payload = req.body + + // If updating or removing cover image then clear cache + if (payload.imagePath !== undefined && req.author.imagePath && payload.imagePath !== req.author.imagePath) { + this.cacheManager.purgeImageCache(req.author.id) + if (!payload.imagePath) { // If removing image then remove file + var currentImagePath = req.author.imagePath + await this.coverController.removeFile(currentImagePath) + } + } + + var hasUpdated = req.author.update(payload) + if (hasUpdated) { + await this.db.updateEntity('author', req.author) + this.emitter('author_updated', req.author.toJSON()) + } + res.json({ + author: req.author.toJSON(), + updated: hasUpdated + }) + } + async search(req, res) { var q = (req.query.q || '').toLowerCase() if (!q) return res.json([]) diff --git a/server/objects/entities/Author.js b/server/objects/entities/Author.js index 0f9271cd..ebbe2b76 100644 --- a/server/objects/entities/Author.js +++ b/server/objects/entities/Author.js @@ -64,6 +64,21 @@ class Author { this.updatedAt = Date.now() } + update(payload) { + var json = this.toJSON() + delete json.id + delete json.addedAt + delete json.updatedAt + var hasUpdates = false + for (const key in json) { + if (payload[key] !== undefined && json[key] != payload[key]) { + this[key] = payload[key] + hasUpdates = true + } + } + return hasUpdates + } + checkNameEquals(name) { if (!name) return false return this.name.toLowerCase() == name.toLowerCase().trim()