mirror of
				https://github.com/advplyr/audiobookshelf.git
				synced 2025-10-27 11:18:14 +01:00 
			
		
		
		
	Merge pull request #3761 from advplyr/remove_old_collection_object
Remove old Collection object
This commit is contained in:
		
						commit
						6780ef9b37
					
				| @ -138,7 +138,6 @@ export default { | ||||
|           .$post(`/api/collections/${collection.id}/batch/remove`, { books: this.selectedBookIds }) | ||||
|           .then((updatedCollection) => { | ||||
|             console.log(`Books removed from collection`, updatedCollection) | ||||
|             this.$toast.success(this.$strings.ToastCollectionItemsRemoveSuccess) | ||||
|             this.processing = false | ||||
|           }) | ||||
|           .catch((error) => { | ||||
| @ -152,7 +151,6 @@ export default { | ||||
|           .$delete(`/api/collections/${collection.id}/book/${this.selectedLibraryItemId}`) | ||||
|           .then((updatedCollection) => { | ||||
|             console.log(`Book removed from collection`, updatedCollection) | ||||
|             this.$toast.success(this.$strings.ToastCollectionItemsRemoveSuccess) | ||||
|             this.processing = false | ||||
|           }) | ||||
|           .catch((error) => { | ||||
| @ -167,12 +165,11 @@ export default { | ||||
|       this.processing = true | ||||
| 
 | ||||
|       if (this.showBatchCollectionModal) { | ||||
|         // BATCH Remove books | ||||
|         // BATCH Add books | ||||
|         this.$axios | ||||
|           .$post(`/api/collections/${collection.id}/batch/add`, { books: this.selectedBookIds }) | ||||
|           .then((updatedCollection) => { | ||||
|             console.log(`Books added to collection`, updatedCollection) | ||||
|             this.$toast.success(this.$strings.ToastCollectionItemsAddSuccess) | ||||
|             this.processing = false | ||||
|           }) | ||||
|           .catch((error) => { | ||||
| @ -187,7 +184,6 @@ export default { | ||||
|           .$post(`/api/collections/${collection.id}/book`, { id: this.selectedLibraryItemId }) | ||||
|           .then((updatedCollection) => { | ||||
|             console.log(`Book added to collection`, updatedCollection) | ||||
|             this.$toast.success(this.$strings.ToastCollectionItemsAddSuccess) | ||||
|             this.processing = false | ||||
|           }) | ||||
|           .catch((error) => { | ||||
| @ -214,7 +210,6 @@ export default { | ||||
|         .$post('/api/collections', newCollection) | ||||
|         .then((data) => { | ||||
|           console.log('New Collection Created', data) | ||||
|           this.$toast.success(`Collection "${data.name}" created`) | ||||
|           this.processing = false | ||||
|           this.newCollectionName = '' | ||||
|         }) | ||||
|  | ||||
| @ -729,7 +729,6 @@ | ||||
|   "ToastBookmarkUpdateSuccess": "Отметката е обновена", | ||||
|   "ToastChaptersHaveErrors": "Главите имат грешки", | ||||
|   "ToastChaptersMustHaveTitles": "Главите трябва да имат заглавия", | ||||
|   "ToastCollectionItemsRemoveSuccess": "Елемент(и) премахнати от колекция", | ||||
|   "ToastCollectionRemoveSuccess": "Колекцията е премахната", | ||||
|   "ToastCollectionUpdateSuccess": "Колекцията е обновена", | ||||
|   "ToastItemCoverUpdateSuccess": "Корицата на елемента е обновена", | ||||
|  | ||||
| @ -951,8 +951,6 @@ | ||||
|   "ToastChaptersRemoved": "অধ্যায়গুলো মুছে ফেলা হয়েছে", | ||||
|   "ToastChaptersUpdated": "অধ্যায় আপডেট করা হয়েছে", | ||||
|   "ToastCollectionItemsAddFailed": "আইটেম(গুলি) সংগ্রহে যোগ করা ব্যর্থ হয়েছে", | ||||
|   "ToastCollectionItemsAddSuccess": "আইটেম(গুলি) সংগ্রহে যোগ করা সফল হয়েছে", | ||||
|   "ToastCollectionItemsRemoveSuccess": "আইটেম(গুলি) সংগ্রহ থেকে সরানো হয়েছে", | ||||
|   "ToastCollectionRemoveSuccess": "সংগ্রহ সরানো হয়েছে", | ||||
|   "ToastCollectionUpdateSuccess": "সংগ্রহ আপডেট করা হয়েছে", | ||||
|   "ToastCoverUpdateFailed": "কভার আপডেট ব্যর্থ হয়েছে", | ||||
|  | ||||
| @ -904,8 +904,6 @@ | ||||
|   "ToastChaptersRemoved": "Capítols eliminats", | ||||
|   "ToastChaptersUpdated": "Capítols actualitzats", | ||||
|   "ToastCollectionItemsAddFailed": "Error en afegir elements a la col·lecció", | ||||
|   "ToastCollectionItemsAddSuccess": "Elements afegits a la col·lecció", | ||||
|   "ToastCollectionItemsRemoveSuccess": "Elements eliminats de la col·lecció", | ||||
|   "ToastCollectionRemoveSuccess": "Col·lecció eliminada", | ||||
|   "ToastCollectionUpdateSuccess": "Col·lecció actualitzada", | ||||
|   "ToastCoverUpdateFailed": "Error en actualitzar la portada", | ||||
|  | ||||
| @ -943,7 +943,6 @@ | ||||
|   "ToastChaptersHaveErrors": "Kapitoly obsahují chyby", | ||||
|   "ToastChaptersMustHaveTitles": "Kapitoly musí mít názvy", | ||||
|   "ToastChaptersRemoved": "Kapitoly odstraněny", | ||||
|   "ToastCollectionItemsRemoveSuccess": "Položky odstraněny z kolekce", | ||||
|   "ToastCollectionRemoveSuccess": "Kolekce odstraněna", | ||||
|   "ToastCollectionUpdateSuccess": "Kolekce aktualizována", | ||||
|   "ToastCoverUpdateFailed": "Aktualizace obálky selhala", | ||||
|  | ||||
| @ -640,7 +640,6 @@ | ||||
|   "ToastBookmarkUpdateSuccess": "Bogmærke opdateret", | ||||
|   "ToastChaptersHaveErrors": "Kapitler har fejl", | ||||
|   "ToastChaptersMustHaveTitles": "Kapitler skal have titler", | ||||
|   "ToastCollectionItemsRemoveSuccess": "Element(er) fjernet fra samlingen", | ||||
|   "ToastCollectionRemoveSuccess": "Samling fjernet", | ||||
|   "ToastCollectionUpdateSuccess": "Samling opdateret", | ||||
|   "ToastItemCoverUpdateSuccess": "Varens omslag opdateret", | ||||
|  | ||||
| @ -959,8 +959,6 @@ | ||||
|   "ToastChaptersRemoved": "Kapitel entfernt", | ||||
|   "ToastChaptersUpdated": "Kapitel aktualisiert", | ||||
|   "ToastCollectionItemsAddFailed": "Das Hinzufügen von Element(en) zur Sammlung ist fehlgeschlagen", | ||||
|   "ToastCollectionItemsAddSuccess": "Element(e) erfolgreich zur Sammlung hinzugefügt", | ||||
|   "ToastCollectionItemsRemoveSuccess": "Medien aus der Sammlung entfernt", | ||||
|   "ToastCollectionRemoveSuccess": "Sammlung entfernt", | ||||
|   "ToastCollectionUpdateSuccess": "Sammlung aktualisiert", | ||||
|   "ToastCoverUpdateFailed": "Cover-Update fehlgeschlagen", | ||||
|  | ||||
| @ -961,8 +961,6 @@ | ||||
|   "ToastChaptersRemoved": "Chapters removed", | ||||
|   "ToastChaptersUpdated": "Chapters updated", | ||||
|   "ToastCollectionItemsAddFailed": "Item(s) added to collection failed", | ||||
|   "ToastCollectionItemsAddSuccess": "Item(s) added to collection success", | ||||
|   "ToastCollectionItemsRemoveSuccess": "Item(s) removed from collection", | ||||
|   "ToastCollectionRemoveSuccess": "Collection removed", | ||||
|   "ToastCollectionUpdateSuccess": "Collection updated", | ||||
|   "ToastCoverUpdateFailed": "Cover update failed", | ||||
|  | ||||
| @ -959,8 +959,6 @@ | ||||
|   "ToastChaptersRemoved": "Capítulos eliminados", | ||||
|   "ToastChaptersUpdated": "Capítulos actualizados", | ||||
|   "ToastCollectionItemsAddFailed": "Artículo(s) añadido(s) a la colección fallido(s)", | ||||
|   "ToastCollectionItemsAddSuccess": "Artículo(s) añadido(s) a la colección correctamente", | ||||
|   "ToastCollectionItemsRemoveSuccess": "Elementos(s) removidos de la colección", | ||||
|   "ToastCollectionRemoveSuccess": "Colección removida", | ||||
|   "ToastCollectionUpdateSuccess": "Colección actualizada", | ||||
|   "ToastCoverUpdateFailed": "Error al actualizar la cubierta", | ||||
|  | ||||
| @ -713,7 +713,6 @@ | ||||
|   "ToastBookmarkUpdateSuccess": "Järjehoidja värskendatud", | ||||
|   "ToastChaptersHaveErrors": "Peatükkidel on vigu", | ||||
|   "ToastChaptersMustHaveTitles": "Peatükkidel peab olema pealkiri", | ||||
|   "ToastCollectionItemsRemoveSuccess": "Üksus(ed) eemaldatud kogumist", | ||||
|   "ToastCollectionRemoveSuccess": "Kogum eemaldatud", | ||||
|   "ToastCollectionUpdateSuccess": "Kogum värskendatud", | ||||
|   "ToastItemCoverUpdateSuccess": "Üksuse kaas värskendatud", | ||||
|  | ||||
| @ -953,8 +953,6 @@ | ||||
|   "ToastChaptersRemoved": "Chapitres supprimés", | ||||
|   "ToastChaptersUpdated": "Chapitres mis à jour", | ||||
|   "ToastCollectionItemsAddFailed": "Échec de l’ajout de(s) élément(s) à la collection", | ||||
|   "ToastCollectionItemsAddSuccess": "Ajout de(s) élément(s) à la collection réussi", | ||||
|   "ToastCollectionItemsRemoveSuccess": "Élément(s) supprimé(s) de la collection", | ||||
|   "ToastCollectionRemoveSuccess": "Collection supprimée", | ||||
|   "ToastCollectionUpdateSuccess": "Collection mise à jour", | ||||
|   "ToastCoverUpdateFailed": "Échec de la mise à jour de la couverture", | ||||
|  | ||||
| @ -744,7 +744,6 @@ | ||||
|   "ToastBookmarkUpdateSuccess": "הסימניה עודכנה בהצלחה", | ||||
|   "ToastChaptersHaveErrors": "פרקים מכילים שגיאות", | ||||
|   "ToastChaptersMustHaveTitles": "פרקים חייבים לכלול כותרות", | ||||
|   "ToastCollectionItemsRemoveSuccess": "הפריט(ים) הוסרו מהאוסף בהצלחה", | ||||
|   "ToastCollectionRemoveSuccess": "האוסף הוסר בהצלחה", | ||||
|   "ToastCollectionUpdateSuccess": "האוסף עודכן בהצלחה", | ||||
|   "ToastItemCoverUpdateSuccess": "כריכת הפריט עודכנה בהצלחה", | ||||
|  | ||||
| @ -959,8 +959,6 @@ | ||||
|   "ToastChaptersRemoved": "Poglavlja uklonjena", | ||||
|   "ToastChaptersUpdated": "Poglavlja su ažurirana", | ||||
|   "ToastCollectionItemsAddFailed": "Neuspješno dodavanje stavki u zbirku", | ||||
|   "ToastCollectionItemsAddSuccess": "Uspješno dodavanje stavki u zbirku", | ||||
|   "ToastCollectionItemsRemoveSuccess": "Stavke izbrisane iz zbirke", | ||||
|   "ToastCollectionRemoveSuccess": "Zbirka izbrisana", | ||||
|   "ToastCollectionUpdateSuccess": "Zbirka ažurirana", | ||||
|   "ToastCoverUpdateFailed": "Ažuriranje naslovnice nije uspjelo", | ||||
|  | ||||
| @ -945,7 +945,6 @@ | ||||
|   "ToastChaptersMustHaveTitles": "A fejezeteknek címekkel kell rendelkezniük", | ||||
|   "ToastChaptersRemoved": "Fejezetek eltávolítva", | ||||
|   "ToastChaptersUpdated": "Fejezetek frissítve", | ||||
|   "ToastCollectionItemsRemoveSuccess": "Elem(ek) eltávolítva a gyűjteményből", | ||||
|   "ToastCollectionRemoveSuccess": "Gyűjtemény eltávolítva", | ||||
|   "ToastCollectionUpdateSuccess": "Gyűjtemény frissítve", | ||||
|   "ToastCoverUpdateFailed": "A borító frissítése nem sikerült", | ||||
|  | ||||
| @ -950,8 +950,6 @@ | ||||
|   "ToastChaptersRemoved": "Capitoli rimossi", | ||||
|   "ToastChaptersUpdated": "Capitoli aggiornati", | ||||
|   "ToastCollectionItemsAddFailed": "l'aggiunta dell'elemento(i) alla raccolta non è riuscito", | ||||
|   "ToastCollectionItemsAddSuccess": "L'aggiunta dell'elemento(i) alla raccolta è riuscito", | ||||
|   "ToastCollectionItemsRemoveSuccess": "Oggetto(i) rimossi dalla Raccolta", | ||||
|   "ToastCollectionRemoveSuccess": "Collezione rimossa", | ||||
|   "ToastCollectionUpdateSuccess": "Raccolta aggiornata", | ||||
|   "ToastCoverUpdateFailed": "Aggiornamento cover fallito", | ||||
|  | ||||
| @ -666,8 +666,6 @@ | ||||
|   "ToastChaptersMustHaveTitles": "Skyriai turi turėti pavadinimus", | ||||
|   "ToastChaptersRemoved": "Skyriai pašalinti", | ||||
|   "ToastCollectionItemsAddFailed": "Nepavyko pridėti į kolekciją", | ||||
|   "ToastCollectionItemsAddSuccess": "Pridėta į kolekciją", | ||||
|   "ToastCollectionItemsRemoveSuccess": "Elementai pašalinti iš kolekcijos", | ||||
|   "ToastCollectionRemoveSuccess": "Kolekcija pašalinta", | ||||
|   "ToastCollectionUpdateSuccess": "Kolekcija atnaujinta", | ||||
|   "ToastCoverUpdateFailed": "Viršelio atnaujinimas nepavyko", | ||||
|  | ||||
| @ -946,8 +946,6 @@ | ||||
|   "ToastChaptersRemoved": "Hoofdstukken verwijderd", | ||||
|   "ToastChaptersUpdated": "Hoofdstukken bijgewerkt", | ||||
|   "ToastCollectionItemsAddFailed": "Item(s) toegevoegd aan collectie mislukt", | ||||
|   "ToastCollectionItemsAddSuccess": "Item(s) toegevoegd aan collectie gelukt", | ||||
|   "ToastCollectionItemsRemoveSuccess": "Onderdeel (of onderdelen) verwijderd uit collectie", | ||||
|   "ToastCollectionRemoveSuccess": "Collectie verwijderd", | ||||
|   "ToastCollectionUpdateSuccess": "Collectie bijgewerkt", | ||||
|   "ToastCoverUpdateFailed": "Cover update mislukt", | ||||
|  | ||||
| @ -882,8 +882,6 @@ | ||||
|   "ToastChaptersRemoved": "Kapitler fjernet", | ||||
|   "ToastChaptersUpdated": "Kapitler oppdatert", | ||||
|   "ToastCollectionItemsAddFailed": "Feil med å legge til element(er)", | ||||
|   "ToastCollectionItemsAddSuccess": "Element(er) lagt til samlingen", | ||||
|   "ToastCollectionItemsRemoveSuccess": "Gjenstand(er) fjernet fra samling", | ||||
|   "ToastCollectionRemoveSuccess": "Samling fjernet", | ||||
|   "ToastCollectionUpdateSuccess": "samlingupdated", | ||||
|   "ToastCoverUpdateFailed": "Oppdatering av bilde feilet", | ||||
|  | ||||
| @ -772,7 +772,6 @@ | ||||
|   "ToastBookmarkCreateSuccess": "Dodano zakładkę", | ||||
|   "ToastBookmarkRemoveSuccess": "Zakładka została usunięta", | ||||
|   "ToastBookmarkUpdateSuccess": "Zaktualizowano zakładkę", | ||||
|   "ToastCollectionItemsRemoveSuccess": "Przedmiot(y) zostały usunięte z kolekcji", | ||||
|   "ToastCollectionRemoveSuccess": "Kolekcja usunięta", | ||||
|   "ToastCollectionUpdateSuccess": "Zaktualizowano kolekcję", | ||||
|   "ToastItemCoverUpdateSuccess": "Zaktualizowano okładkę", | ||||
|  | ||||
| @ -735,7 +735,6 @@ | ||||
|   "ToastCachePurgeSuccess": "Cache apagado com sucesso", | ||||
|   "ToastChaptersHaveErrors": "Capítulos com erro", | ||||
|   "ToastChaptersMustHaveTitles": "Capítulos precisam ter títulos", | ||||
|   "ToastCollectionItemsRemoveSuccess": "Item(ns) removidos da coleção", | ||||
|   "ToastCollectionRemoveSuccess": "Coleção removida", | ||||
|   "ToastCollectionUpdateSuccess": "Coleção atualizada", | ||||
|   "ToastDeleteFileFailed": "Falha ao apagar arquivo", | ||||
|  | ||||
| @ -959,8 +959,6 @@ | ||||
|   "ToastChaptersRemoved": "Удалены главы", | ||||
|   "ToastChaptersUpdated": "Обновленные главы", | ||||
|   "ToastCollectionItemsAddFailed": "Не удалось добавить элемент(ы) в коллекцию", | ||||
|   "ToastCollectionItemsAddSuccess": "Элемент(ы) добавлены в коллекцию", | ||||
|   "ToastCollectionItemsRemoveSuccess": "Элемент(ы), удалены из коллекции", | ||||
|   "ToastCollectionRemoveSuccess": "Коллекция удалена", | ||||
|   "ToastCollectionUpdateSuccess": "Коллекция обновлена", | ||||
|   "ToastCoverUpdateFailed": "Не удалось обновить обложку", | ||||
|  | ||||
| @ -959,8 +959,6 @@ | ||||
|   "ToastChaptersRemoved": "Poglavja so odstranjena", | ||||
|   "ToastChaptersUpdated": "Poglavja so posodobljena", | ||||
|   "ToastCollectionItemsAddFailed": "Dodajanje elementov v zbirko ni uspelo", | ||||
|   "ToastCollectionItemsAddSuccess": "Dodajanje elementov v zbirko je bilo uspešno", | ||||
|   "ToastCollectionItemsRemoveSuccess": "Elementi so bili odstranjeni iz zbirke", | ||||
|   "ToastCollectionRemoveSuccess": "Zbirka je bila odstranjena", | ||||
|   "ToastCollectionUpdateSuccess": "Zbirka je bila posodobljena", | ||||
|   "ToastCoverUpdateFailed": "Posodobitev naslovnice ni uspela", | ||||
|  | ||||
| @ -705,7 +705,6 @@ | ||||
|   "ToastBookmarkUpdateSuccess": "Bokmärket har uppdaterats", | ||||
|   "ToastChaptersHaveErrors": "Kapitlen har fel", | ||||
|   "ToastChaptersMustHaveTitles": "Kapitel måste ha titlar", | ||||
|   "ToastCollectionItemsRemoveSuccess": "Objekt borttagna från samlingen", | ||||
|   "ToastCollectionRemoveSuccess": "Samlingen har raderats", | ||||
|   "ToastCollectionUpdateSuccess": "Samlingen har uppdaterats", | ||||
|   "ToastItemCoverUpdateSuccess": "Objektets omslag uppdaterat", | ||||
|  | ||||
| @ -959,8 +959,6 @@ | ||||
|   "ToastChaptersRemoved": "Розділи видалені", | ||||
|   "ToastChaptersUpdated": "Розділи оновлені", | ||||
|   "ToastCollectionItemsAddFailed": "Не вдалося додати елемент(и) до колекції", | ||||
|   "ToastCollectionItemsAddSuccess": "Елемент(и) успішно додано до колекції", | ||||
|   "ToastCollectionItemsRemoveSuccess": "Елемент(и) видалено з добірки", | ||||
|   "ToastCollectionRemoveSuccess": "Добірку видалено", | ||||
|   "ToastCollectionUpdateSuccess": "Добірку оновлено", | ||||
|   "ToastCoverUpdateFailed": "Не вдалося оновити обкладинку", | ||||
|  | ||||
| @ -683,7 +683,6 @@ | ||||
|   "ToastBookmarkUpdateSuccess": "Đánh dấu đã được cập nhật", | ||||
|   "ToastChaptersHaveErrors": "Các chương có lỗi", | ||||
|   "ToastChaptersMustHaveTitles": "Các chương phải có tiêu đề", | ||||
|   "ToastCollectionItemsRemoveSuccess": "Mục đã được xóa khỏi bộ sưu tập", | ||||
|   "ToastCollectionRemoveSuccess": "Bộ sưu tập đã được xóa", | ||||
|   "ToastCollectionUpdateSuccess": "Bộ sưu tập đã được cập nhật", | ||||
|   "ToastItemCoverUpdateSuccess": "Ảnh bìa mục đã được cập nhật", | ||||
|  | ||||
| @ -959,8 +959,6 @@ | ||||
|   "ToastChaptersRemoved": "已删除章节", | ||||
|   "ToastChaptersUpdated": "章节已更新", | ||||
|   "ToastCollectionItemsAddFailed": "项目添加到收藏夹失败", | ||||
|   "ToastCollectionItemsAddSuccess": "项目添加到收藏夹成功", | ||||
|   "ToastCollectionItemsRemoveSuccess": "项目从收藏夹移除", | ||||
|   "ToastCollectionRemoveSuccess": "收藏夹已删除", | ||||
|   "ToastCollectionUpdateSuccess": "收藏夹已更新", | ||||
|   "ToastCoverUpdateFailed": "封面更新失败", | ||||
|  | ||||
| @ -727,7 +727,6 @@ | ||||
|   "ToastBookmarkUpdateSuccess": "書籤已更新", | ||||
|   "ToastChaptersHaveErrors": "章節有錯誤", | ||||
|   "ToastChaptersMustHaveTitles": "章節必須有標題", | ||||
|   "ToastCollectionItemsRemoveSuccess": "項目從收藏夾移除", | ||||
|   "ToastCollectionRemoveSuccess": "收藏夾已刪除", | ||||
|   "ToastCollectionUpdateSuccess": "收藏夾已更新", | ||||
|   "ToastItemCoverUpdateSuccess": "項目封面已更新", | ||||
|  | ||||
| @ -406,11 +406,6 @@ class Database { | ||||
|     return Promise.all(oldBooks.map((oldBook) => this.models.book.saveFromOld(oldBook))) | ||||
|   } | ||||
| 
 | ||||
|   createBulkCollectionBooks(collectionBooks) { | ||||
|     if (!this.sequelize) return false | ||||
|     return this.models.collectionBook.bulkCreate(collectionBooks) | ||||
|   } | ||||
| 
 | ||||
|   createPlaylistMediaItem(playlistMediaItem) { | ||||
|     if (!this.sequelize) return false | ||||
|     return this.models.playlistMediaItem.create(playlistMediaItem) | ||||
|  | ||||
| @ -5,13 +5,17 @@ const SocketAuthority = require('../SocketAuthority') | ||||
| const Database = require('../Database') | ||||
| 
 | ||||
| const RssFeedManager = require('../managers/RssFeedManager') | ||||
| const Collection = require('../objects/Collection') | ||||
| 
 | ||||
| /** | ||||
|  * @typedef RequestUserObject | ||||
|  * @property {import('../models/User')} user | ||||
|  * | ||||
|  * @typedef {Request & RequestUserObject} RequestWithUser | ||||
|  * | ||||
|  * @typedef RequestEntityObject | ||||
|  * @property {import('../models/Collection')} collection | ||||
|  * | ||||
|  * @typedef {RequestWithUser & RequestEntityObject} CollectionControllerRequest | ||||
|  */ | ||||
| 
 | ||||
| class CollectionController { | ||||
| @ -25,36 +29,68 @@ class CollectionController { | ||||
|    * @param {Response} res | ||||
|    */ | ||||
|   async create(req, res) { | ||||
|     const newCollection = new Collection() | ||||
|     req.body.userId = req.user.id | ||||
|     if (!newCollection.setData(req.body)) { | ||||
|     const reqBody = req.body || {} | ||||
| 
 | ||||
|     // Validation
 | ||||
|     if (!reqBody.name || !reqBody.libraryId) { | ||||
|       return res.status(400).send('Invalid collection data') | ||||
|     } | ||||
|     const libraryItemIds = (reqBody.books || []).filter((b) => !!b && typeof b == 'string') | ||||
|     if (!libraryItemIds.length) { | ||||
|       return res.status(400).send('Invalid collection data. No books') | ||||
|     } | ||||
| 
 | ||||
|     // Create collection record
 | ||||
|     await Database.collectionModel.createFromOld(newCollection) | ||||
| 
 | ||||
|     // Get library items in collection
 | ||||
|     const libraryItemsInCollection = await Database.libraryItemModel.getForCollection(newCollection) | ||||
| 
 | ||||
|     // Create collectionBook records
 | ||||
|     let order = 1 | ||||
|     const collectionBooksToAdd = [] | ||||
|     for (const libraryItemId of newCollection.books) { | ||||
|       const libraryItem = libraryItemsInCollection.find((li) => li.id === libraryItemId) | ||||
|       if (libraryItem) { | ||||
|         collectionBooksToAdd.push({ | ||||
|           collectionId: newCollection.id, | ||||
|           bookId: libraryItem.media.id, | ||||
|           order: order++ | ||||
|         }) | ||||
|     // Load library items
 | ||||
|     const libraryItems = await Database.libraryItemModel.findAll({ | ||||
|       attributes: ['id', 'mediaId', 'mediaType', 'libraryId'], | ||||
|       where: { | ||||
|         id: libraryItemIds, | ||||
|         libraryId: reqBody.libraryId, | ||||
|         mediaType: 'book' | ||||
|       } | ||||
|     } | ||||
|     if (collectionBooksToAdd.length) { | ||||
|       await Database.createBulkCollectionBooks(collectionBooksToAdd) | ||||
|     }) | ||||
|     if (libraryItems.length !== libraryItemIds.length) { | ||||
|       return res.status(400).send('Invalid collection data. Invalid books') | ||||
|     } | ||||
| 
 | ||||
|     const jsonExpanded = newCollection.toJSONExpanded(libraryItemsInCollection) | ||||
|     /** @type {import('../models/Collection')} */ | ||||
|     let newCollection = null | ||||
| 
 | ||||
|     const transaction = await Database.sequelize.transaction() | ||||
|     try { | ||||
|       // Create collection
 | ||||
|       newCollection = await Database.collectionModel.create( | ||||
|         { | ||||
|           libraryId: reqBody.libraryId, | ||||
|           name: reqBody.name, | ||||
|           description: reqBody.description || null | ||||
|         }, | ||||
|         { transaction } | ||||
|       ) | ||||
| 
 | ||||
|       // Create collectionBooks
 | ||||
|       const collectionBookPayloads = libraryItemIds.map((llid, index) => { | ||||
|         const libraryItem = libraryItems.find((li) => li.id === llid) | ||||
|         return { | ||||
|           collectionId: newCollection.id, | ||||
|           bookId: libraryItem.mediaId, | ||||
|           order: index + 1 | ||||
|         } | ||||
|       }) | ||||
|       await Database.collectionBookModel.bulkCreate(collectionBookPayloads, { transaction }) | ||||
| 
 | ||||
|       await transaction.commit() | ||||
|     } catch (error) { | ||||
|       await transaction.rollback() | ||||
|       Logger.error('[CollectionController] create:', error) | ||||
|       return res.status(500).send('Failed to create collection') | ||||
|     } | ||||
| 
 | ||||
|     // Load books expanded
 | ||||
|     newCollection.books = await newCollection.getBooksExpandedWithLibraryItem() | ||||
| 
 | ||||
|     // Note: The old collection model stores expanded libraryItems in the books property
 | ||||
|     const jsonExpanded = newCollection.toOldJSONExpanded() | ||||
|     SocketAuthority.emitter('collection_added', jsonExpanded) | ||||
|     res.json(jsonExpanded) | ||||
|   } | ||||
| @ -75,7 +111,7 @@ class CollectionController { | ||||
|   /** | ||||
|    * GET: /api/collections/:id | ||||
|    * | ||||
|    * @param {RequestWithUser} req | ||||
|    * @param {CollectionControllerRequest} req | ||||
|    * @param {Response} res | ||||
|    */ | ||||
|   async findOne(req, res) { | ||||
| @ -94,7 +130,7 @@ class CollectionController { | ||||
|    * PATCH: /api/collections/:id | ||||
|    * Update collection | ||||
|    * | ||||
|    * @param {RequestWithUser} req | ||||
|    * @param {CollectionControllerRequest} req | ||||
|    * @param {Response} res | ||||
|    */ | ||||
|   async update(req, res) { | ||||
| @ -158,7 +194,7 @@ class CollectionController { | ||||
|    * | ||||
|    * @this {import('../routers/ApiRouter')} | ||||
|    * | ||||
|    * @param {RequestWithUser} req | ||||
|    * @param {CollectionControllerRequest} req | ||||
|    * @param {Response} res | ||||
|    */ | ||||
|   async delete(req, res) { | ||||
| @ -178,7 +214,7 @@ class CollectionController { | ||||
|    * Add a single book to a collection | ||||
|    * Req.body { id: <library item id> } | ||||
|    * | ||||
|    * @param {RequestWithUser} req | ||||
|    * @param {CollectionControllerRequest} req | ||||
|    * @param {Response} res | ||||
|    */ | ||||
|   async addBook(req, res) { | ||||
| @ -212,7 +248,7 @@ class CollectionController { | ||||
|    * Remove a single book from a collection. Re-order books | ||||
|    * TODO: bookId is actually libraryItemId. Clients need updating to use bookId | ||||
|    * | ||||
|    * @param {RequestWithUser} req | ||||
|    * @param {CollectionControllerRequest} req | ||||
|    * @param {Response} res | ||||
|    */ | ||||
|   async removeBook(req, res) { | ||||
| @ -257,29 +293,31 @@ class CollectionController { | ||||
|    * Add multiple books to collection | ||||
|    * Req.body { books: <Array of library item ids> } | ||||
|    * | ||||
|    * @param {RequestWithUser} req | ||||
|    * @param {CollectionControllerRequest} req | ||||
|    * @param {Response} res | ||||
|    */ | ||||
|   async addBatch(req, res) { | ||||
|     // filter out invalid libraryItemIds
 | ||||
|     const bookIdsToAdd = (req.body.books || []).filter((b) => !!b && typeof b == 'string') | ||||
|     if (!bookIdsToAdd.length) { | ||||
|       return res.status(500).send('Invalid request body') | ||||
|       return res.status(400).send('Invalid request body') | ||||
|     } | ||||
| 
 | ||||
|     // Get library items associated with ids
 | ||||
|     const libraryItems = await Database.libraryItemModel.findAll({ | ||||
|       attributes: ['id', 'mediaId', 'mediaType', 'libraryId'], | ||||
|       where: { | ||||
|         id: { | ||||
|           [Sequelize.Op.in]: bookIdsToAdd | ||||
|         } | ||||
|       }, | ||||
|       include: { | ||||
|         model: Database.bookModel | ||||
|         id: bookIdsToAdd, | ||||
|         libraryId: req.collection.libraryId, | ||||
|         mediaType: 'book' | ||||
|       } | ||||
|     }) | ||||
|     if (!libraryItems.length) { | ||||
|       return res.status(400).send('Invalid request body. No valid books') | ||||
|     } | ||||
| 
 | ||||
|     // Get collection books already in collection
 | ||||
|     /** @type {import('../models/CollectionBook')[]} */ | ||||
|     const collectionBooks = await req.collection.getCollectionBooks() | ||||
| 
 | ||||
|     let order = collectionBooks.length + 1 | ||||
| @ -288,10 +326,10 @@ class CollectionController { | ||||
| 
 | ||||
|     // Check and set new collection books to add
 | ||||
|     for (const libraryItem of libraryItems) { | ||||
|       if (!collectionBooks.some((cb) => cb.bookId === libraryItem.media.id)) { | ||||
|       if (!collectionBooks.some((cb) => cb.bookId === libraryItem.mediaId)) { | ||||
|         collectionBooksToAdd.push({ | ||||
|           collectionId: req.collection.id, | ||||
|           bookId: libraryItem.media.id, | ||||
|           bookId: libraryItem.mediaId, | ||||
|           order: order++ | ||||
|         }) | ||||
|         hasUpdated = true | ||||
| @ -302,7 +340,8 @@ class CollectionController { | ||||
| 
 | ||||
|     let jsonExpanded = null | ||||
|     if (hasUpdated) { | ||||
|       await Database.createBulkCollectionBooks(collectionBooksToAdd) | ||||
|       await Database.collectionBookModel.bulkCreate(collectionBooksToAdd) | ||||
| 
 | ||||
|       jsonExpanded = await req.collection.getOldJsonExpanded() | ||||
|       SocketAuthority.emitter('collection_updated', jsonExpanded) | ||||
|     } else { | ||||
| @ -316,7 +355,7 @@ class CollectionController { | ||||
|    * Remove multiple books from collection | ||||
|    * Req.body { books: <Array of library item ids> } | ||||
|    * | ||||
|    * @param {RequestWithUser} req | ||||
|    * @param {CollectionControllerRequest} req | ||||
|    * @param {Response} res | ||||
|    */ | ||||
|   async removeBatch(req, res) { | ||||
| @ -329,9 +368,7 @@ class CollectionController { | ||||
|     // Get library items associated with ids
 | ||||
|     const libraryItems = await Database.libraryItemModel.findAll({ | ||||
|       where: { | ||||
|         id: { | ||||
|           [Sequelize.Op.in]: bookIdsToRemove | ||||
|         } | ||||
|         id: bookIdsToRemove | ||||
|       }, | ||||
|       include: { | ||||
|         model: Database.bookModel | ||||
| @ -339,6 +376,7 @@ class CollectionController { | ||||
|     }) | ||||
| 
 | ||||
|     // Get collection books already in collection
 | ||||
|     /** @type {import('../models/CollectionBook')[]} */ | ||||
|     const collectionBooks = await req.collection.getCollectionBooks({ | ||||
|       order: [['order', 'ASC']] | ||||
|     }) | ||||
|  | ||||
| @ -1,7 +1,5 @@ | ||||
| const { DataTypes, Model, Sequelize } = require('sequelize') | ||||
| 
 | ||||
| const oldCollection = require('../objects/Collection') | ||||
| 
 | ||||
| class Collection extends Model { | ||||
|   constructor(values, options) { | ||||
|     super(values, options) | ||||
| @ -26,12 +24,12 @@ class Collection extends Model { | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Get all old collections toJSONExpanded, items filtered for user permissions | ||||
|    * Get all toOldJSONExpanded, items filtered for user permissions | ||||
|    * | ||||
|    * @param {import('./User')} user | ||||
|    * @param {string} [libraryId] | ||||
|    * @param {string[]} [include] | ||||
|    * @returns {Promise<oldCollection[]>} oldCollection.toJSONExpanded | ||||
|    * @async | ||||
|    */ | ||||
|   static async getOldCollectionsJsonExpanded(user, libraryId, include) { | ||||
|     let collectionWhere = null | ||||
| @ -79,8 +77,6 @@ class Collection extends Model { | ||||
|     // TODO: Handle user permission restrictions on initial query
 | ||||
|     return collections | ||||
|       .map((c) => { | ||||
|         const oldCollection = this.getOldCollection(c) | ||||
| 
 | ||||
|         // Filter books using user permissions
 | ||||
|         const books = | ||||
|           c.books?.filter((b) => { | ||||
| @ -95,20 +91,14 @@ class Collection extends Model { | ||||
|             return true | ||||
|           }) || [] | ||||
| 
 | ||||
|         // Map to library items
 | ||||
|         const libraryItems = books.map((b) => { | ||||
|           const libraryItem = b.libraryItem | ||||
|           delete b.libraryItem | ||||
|           libraryItem.media = b | ||||
|           return this.sequelize.models.libraryItem.getOldLibraryItem(libraryItem) | ||||
|         }) | ||||
| 
 | ||||
|         // Users with restricted permissions will not see this collection
 | ||||
|         if (!books.length && oldCollection.books.length) { | ||||
|         if (!books.length && c.books.length) { | ||||
|           return null | ||||
|         } | ||||
| 
 | ||||
|         const collectionExpanded = oldCollection.toJSONExpanded(libraryItems) | ||||
|         this.books = books | ||||
| 
 | ||||
|         const collectionExpanded = c.toOldJSONExpanded() | ||||
| 
 | ||||
|         // Map feed if found
 | ||||
|         if (c.feeds?.length) { | ||||
| @ -153,69 +143,6 @@ class Collection extends Model { | ||||
|     }) | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Get old collection from Collection | ||||
|    * @param {Collection} collectionExpanded | ||||
|    * @returns {oldCollection} | ||||
|    */ | ||||
|   static getOldCollection(collectionExpanded) { | ||||
|     const libraryItemIds = collectionExpanded.books?.map((b) => b.libraryItem?.id || null).filter((lid) => lid) || [] | ||||
|     return new oldCollection({ | ||||
|       id: collectionExpanded.id, | ||||
|       libraryId: collectionExpanded.libraryId, | ||||
|       name: collectionExpanded.name, | ||||
|       description: collectionExpanded.description, | ||||
|       books: libraryItemIds, | ||||
|       lastUpdate: collectionExpanded.updatedAt.valueOf(), | ||||
|       createdAt: collectionExpanded.createdAt.valueOf() | ||||
|     }) | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * | ||||
|    * @param {oldCollection} oldCollection | ||||
|    * @returns {Promise<Collection>} | ||||
|    */ | ||||
|   static createFromOld(oldCollection) { | ||||
|     const collection = this.getFromOld(oldCollection) | ||||
|     return this.create(collection) | ||||
|   } | ||||
| 
 | ||||
|   static getFromOld(oldCollection) { | ||||
|     return { | ||||
|       id: oldCollection.id, | ||||
|       name: oldCollection.name, | ||||
|       description: oldCollection.description, | ||||
|       libraryId: oldCollection.libraryId | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   static removeById(collectionId) { | ||||
|     return this.destroy({ | ||||
|       where: { | ||||
|         id: collectionId | ||||
|       } | ||||
|     }) | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Get old collection by id | ||||
|    * @param {string} collectionId | ||||
|    * @returns {Promise<oldCollection|null>} returns null if not found | ||||
|    */ | ||||
|   static async getOldById(collectionId) { | ||||
|     if (!collectionId) return null | ||||
|     const collection = await this.findByPk(collectionId, { | ||||
|       include: { | ||||
|         model: this.sequelize.models.book, | ||||
|         include: this.sequelize.models.libraryItem | ||||
|       }, | ||||
|       order: [[this.sequelize.models.book, this.sequelize.models.collectionBook, 'order', 'ASC']] | ||||
|     }) | ||||
|     if (!collection) return null | ||||
|     return this.getOldCollection(collection) | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Remove all collections belonging to library | ||||
|    * @param {string} libraryId | ||||
| @ -286,64 +213,37 @@ class Collection extends Model { | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Get old collection toJSONExpanded, items filtered for user permissions | ||||
|    * Get toOldJSONExpanded, items filtered for user permissions | ||||
|    * | ||||
|    * @param {import('./User')|null} user | ||||
|    * @param {string[]} [include] | ||||
|    * @returns {Promise<oldCollection>} oldCollection.toJSONExpanded | ||||
|    * @async | ||||
|    */ | ||||
|   async getOldJsonExpanded(user, include) { | ||||
|     this.books = | ||||
|       (await this.getBooks({ | ||||
|         include: [ | ||||
|           { | ||||
|             model: this.sequelize.models.libraryItem | ||||
|           }, | ||||
|           { | ||||
|             model: this.sequelize.models.author, | ||||
|             through: { | ||||
|               attributes: [] | ||||
|             } | ||||
|           }, | ||||
|           { | ||||
|             model: this.sequelize.models.series, | ||||
|             through: { | ||||
|               attributes: ['sequence'] | ||||
|             } | ||||
|           } | ||||
|         ], | ||||
|         order: [Sequelize.literal('`collectionBook.order` ASC')] | ||||
|       })) || [] | ||||
|     this.books = await this.getBooksExpandedWithLibraryItem() | ||||
| 
 | ||||
|     // Filter books using user permissions
 | ||||
|     // TODO: Handle user permission restrictions on initial query
 | ||||
|     const books = | ||||
|       this.books?.filter((b) => { | ||||
|         if (user) { | ||||
|           if (b.tags?.length && !user.checkCanAccessLibraryItemWithTags(b.tags)) { | ||||
|             return false | ||||
|           } | ||||
|           if (b.explicit === true && !user.canAccessExplicitContent) { | ||||
|             return false | ||||
|           } | ||||
|     if (user) { | ||||
|       const books = this.books.filter((b) => { | ||||
|         if (b.tags?.length && !user.checkCanAccessLibraryItemWithTags(b.tags)) { | ||||
|           return false | ||||
|         } | ||||
|         if (b.explicit === true && !user.canAccessExplicitContent) { | ||||
|           return false | ||||
|         } | ||||
|         return true | ||||
|       }) || [] | ||||
|       }) | ||||
| 
 | ||||
|     // Map to library items
 | ||||
|     const libraryItems = books.map((b) => { | ||||
|       const libraryItem = b.libraryItem | ||||
|       delete b.libraryItem | ||||
|       libraryItem.media = b | ||||
|       return this.sequelize.models.libraryItem.getOldLibraryItem(libraryItem) | ||||
|     }) | ||||
|       // Users with restricted permissions will not see this collection
 | ||||
|       if (!books.length && this.books.length) { | ||||
|         return null | ||||
|       } | ||||
| 
 | ||||
|     // Users with restricted permissions will not see this collection
 | ||||
|     if (!books.length && this.books.length) { | ||||
|       return null | ||||
|       this.books = books | ||||
|     } | ||||
| 
 | ||||
|     const collectionExpanded = this.toOldJSONExpanded(libraryItems) | ||||
|     const collectionExpanded = this.toOldJSONExpanded() | ||||
| 
 | ||||
|     if (include?.includes('rssfeed')) { | ||||
|       const feeds = await this.getFeeds() | ||||
| @ -357,10 +257,10 @@ class Collection extends Model { | ||||
| 
 | ||||
|   /** | ||||
|    * | ||||
|    * @param {string[]} libraryItemIds | ||||
|    * @param {string[]} [libraryItemIds=[]] | ||||
|    * @returns | ||||
|    */ | ||||
|   toOldJSON(libraryItemIds) { | ||||
|   toOldJSON(libraryItemIds = []) { | ||||
|     return { | ||||
|       id: this.id, | ||||
|       libraryId: this.libraryId, | ||||
| @ -372,19 +272,19 @@ class Collection extends Model { | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * | ||||
|    * @param {import('../objects/LibraryItem')} oldLibraryItems | ||||
|    * @returns | ||||
|    */ | ||||
|   toOldJSONExpanded(oldLibraryItems) { | ||||
|     const json = this.toOldJSON(oldLibraryItems.map((li) => li.id)) | ||||
|     json.books = json.books | ||||
|       .map((libraryItemId) => { | ||||
|         const book = oldLibraryItems.find((li) => li.id === libraryItemId) | ||||
|         return book ? book.toJSONExpanded() : null | ||||
|       }) | ||||
|       .filter((b) => !!b) | ||||
|   toOldJSONExpanded() { | ||||
|     if (!this.books) { | ||||
|       throw new Error('Books are required to expand Collection') | ||||
|     } | ||||
| 
 | ||||
|     const json = this.toOldJSON() | ||||
|     json.books = this.books.map((book) => { | ||||
|       const libraryItem = book.libraryItem | ||||
|       delete book.libraryItem | ||||
|       libraryItem.media = book | ||||
|       return this.sequelize.models.libraryItem.getOldLibraryItem(libraryItem).toJSONExpanded() | ||||
|     }) | ||||
| 
 | ||||
|     return json | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -16,15 +16,6 @@ class CollectionBook extends Model { | ||||
|     this.createdAt | ||||
|   } | ||||
| 
 | ||||
|   static removeByIds(collectionId, bookId) { | ||||
|     return this.destroy({ | ||||
|       where: { | ||||
|         bookId, | ||||
|         collectionId | ||||
|       } | ||||
|     }) | ||||
|   } | ||||
| 
 | ||||
|   static init(sequelize) { | ||||
|     super.init( | ||||
|       { | ||||
|  | ||||
| @ -123,7 +123,7 @@ class LibraryItem extends Model { | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Currently unused because this is too slow and uses too much mem | ||||
|    * | ||||
|    * @param {import('sequelize').WhereOptions} [where] | ||||
|    * @returns {Array<objects.LibraryItem>} old library items | ||||
|    */ | ||||
|  | ||||
| @ -1,115 +0,0 @@ | ||||
| const uuidv4 = require("uuid").v4 | ||||
| 
 | ||||
| class Collection { | ||||
|   constructor(collection) { | ||||
|     this.id = null | ||||
|     this.libraryId = null | ||||
| 
 | ||||
|     this.name = null | ||||
|     this.description = null | ||||
| 
 | ||||
|     this.cover = null | ||||
|     this.coverFullPath = null | ||||
|     this.books = [] | ||||
| 
 | ||||
|     this.lastUpdate = null | ||||
|     this.createdAt = null | ||||
| 
 | ||||
|     if (collection) { | ||||
|       this.construct(collection) | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   toJSON() { | ||||
|     return { | ||||
|       id: this.id, | ||||
|       libraryId: this.libraryId, | ||||
|       name: this.name, | ||||
|       description: this.description, | ||||
|       cover: this.cover, | ||||
|       coverFullPath: this.coverFullPath, | ||||
|       books: [...this.books], | ||||
|       lastUpdate: this.lastUpdate, | ||||
|       createdAt: this.createdAt | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   toJSONExpanded(libraryItems, minifiedBooks = false) { | ||||
|     const json = this.toJSON() | ||||
|     json.books = json.books.map(bookId => { | ||||
|       const book = libraryItems.find(li => li.id === bookId) | ||||
|       return book ? minifiedBooks ? book.toJSONMinified() : book.toJSONExpanded() : null | ||||
|     }).filter(b => !!b) | ||||
|     return json | ||||
|   } | ||||
| 
 | ||||
|   // Expanded and filtered out items not accessible to user
 | ||||
|   toJSONExpandedForUser(user, libraryItems) { | ||||
|     const json = this.toJSON() | ||||
|     json.books = json.books.map(libraryItemId => { | ||||
|       const libraryItem = libraryItems.find(li => li.id === libraryItemId) | ||||
|       return libraryItem ? libraryItem.toJSONExpanded() : null | ||||
|     }).filter(li => { | ||||
|       return li && user.checkCanAccessLibraryItem(li) | ||||
|     }) | ||||
|     return json | ||||
|   } | ||||
| 
 | ||||
|   construct(collection) { | ||||
|     this.id = collection.id | ||||
|     this.libraryId = collection.libraryId | ||||
|     this.name = collection.name | ||||
|     this.description = collection.description || null | ||||
|     this.cover = collection.cover || null | ||||
|     this.coverFullPath = collection.coverFullPath || null | ||||
|     this.books = collection.books ? [...collection.books] : [] | ||||
|     this.lastUpdate = collection.lastUpdate || null | ||||
|     this.createdAt = collection.createdAt || null | ||||
|   } | ||||
| 
 | ||||
|   setData(data) { | ||||
|     if (!data.libraryId || !data.name) { | ||||
|       return false | ||||
|     } | ||||
|     this.id = uuidv4() | ||||
|     this.libraryId = data.libraryId | ||||
|     this.name = data.name | ||||
|     this.description = data.description || null | ||||
|     this.cover = data.cover || null | ||||
|     this.coverFullPath = data.coverFullPath || null | ||||
|     this.books = data.books ? [...data.books] : [] | ||||
|     this.lastUpdate = Date.now() | ||||
|     this.createdAt = Date.now() | ||||
|     return true | ||||
|   } | ||||
| 
 | ||||
|   addBook(bookId) { | ||||
|     this.books.push(bookId) | ||||
|     this.lastUpdate = Date.now() | ||||
|   } | ||||
| 
 | ||||
|   removeBook(bookId) { | ||||
|     this.books = this.books.filter(bid => bid !== bookId) | ||||
|     this.lastUpdate = Date.now() | ||||
|   } | ||||
| 
 | ||||
|   update(payload) { | ||||
|     let hasUpdates = false | ||||
|     for (const key in payload) { | ||||
|       if (key === 'books') { | ||||
|         if (payload.books && this.books.join(',') !== payload.books.join(',')) { | ||||
|           this.books = [...payload.books] | ||||
|           hasUpdates = true | ||||
|         } | ||||
|       } else if (this[key] !== undefined && this[key] !== payload[key]) { | ||||
|         hasUpdates = true | ||||
|         this[key] = payload[key] | ||||
|       } | ||||
|     } | ||||
|     if (hasUpdates) { | ||||
|       this.lastUpdate = Date.now() | ||||
|     } | ||||
|     return hasUpdates | ||||
|   } | ||||
| } | ||||
| module.exports = Collection | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user