diff --git a/client/components/ui/ContextMenuDropdown.vue b/client/components/ui/ContextMenuDropdown.vue
new file mode 100644
index 00000000..988f7469
--- /dev/null
+++ b/client/components/ui/ContextMenuDropdown.vue
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/client/pages/collection/_id.vue b/client/pages/collection/_id.vue
index 70b446bd..0aa313ea 100644
--- a/client/pages/collection/_id.vue
+++ b/client/pages/collection/_id.vue
@@ -19,9 +19,11 @@
{{ streaming ? $strings.ButtonPlaying : $strings.ButtonPlay }}
-
+
-
+
@@ -32,7 +34,7 @@
-
@@ -64,7 +66,7 @@ export default {
},
data() {
return {
- processingRemove: false
+ processing: false
}
},
computed: {
@@ -102,15 +104,55 @@ export default {
},
userCanDelete() {
return this.$store.getters['user/getUserCanDelete']
+ },
+ contextMenuItems() {
+ const items = [
+ {
+ text: 'Create playlist from collection',
+ action: 'create-playlist'
+ }
+ ]
+ if (this.userCanDelete) {
+ items.push({
+ text: 'Delete collection',
+ action: 'delete'
+ })
+ }
+ return items
}
},
methods: {
+ contextMenuAction(action) {
+ if (action === 'delete') {
+ this.removeClick()
+ } else if (action === 'create-playlist') {
+ this.createPlaylistFromCollection()
+ }
+ },
+ createPlaylistFromCollection() {
+ this.processing = true
+ this.$axios
+ .$post(`/api/playlists/collection/${this.collectionId}`)
+ .then((playlist) => {
+ if (playlist) {
+ this.$toast.success('Playlist created')
+ this.$router.push(`/playlist/${playlist.id}`)
+ }
+ })
+ .catch((error) => {
+ const errMsg = error.response ? error.response.data || '' : ''
+ this.$toast.error(errMsg || 'Failed to create playlist')
+ })
+ .finally(() => {
+ this.processing = false
+ })
+ },
editClick() {
this.$store.commit('globals/setEditCollection', this.collection)
},
removeClick() {
if (confirm(this.$getString('MessageConfirmRemoveCollection', [this.collectionName]))) {
- this.processingRemove = true
+ this.processing = true
this.$axios
.$delete(`/api/collections/${this.collection.id}`)
.then(() => {
@@ -121,7 +163,7 @@ export default {
this.$toast.error(this.$strings.ToastCollectionRemoveFailed)
})
.finally(() => {
- this.processingRemove = false
+ this.processing = false
})
}
},
diff --git a/server/controllers/CollectionController.js b/server/controllers/CollectionController.js
index a00ae867..f4161f37 100644
--- a/server/controllers/CollectionController.js
+++ b/server/controllers/CollectionController.js
@@ -123,7 +123,7 @@ class CollectionController {
middleware(req, res, next) {
if (req.params.id) {
- var collection = this.db.collections.find(c => c.id === req.params.id)
+ const collection = this.db.collections.find(c => c.id === req.params.id)
if (!collection) {
return res.status(404).send('Collection not found')
}
diff --git a/server/controllers/PlaylistController.js b/server/controllers/PlaylistController.js
index ecabb686..e923cf82 100644
--- a/server/controllers/PlaylistController.js
+++ b/server/controllers/PlaylistController.js
@@ -174,9 +174,42 @@ class PlaylistController {
res.json(jsonExpanded)
}
+ // POST: api/playlists/collection/:collectionId
+ async createFromCollection(req, res) {
+ let collection = this.db.collections.find(c => c.id === req.params.collectionId)
+ if (!collection) {
+ return res.status(404).send('Collection not found')
+ }
+ // Expand collection to get library items
+ collection = collection.toJSONExpanded(this.db.libraryItems)
+
+ // Filter out library items not accessible to user
+ const libraryItems = collection.books.filter(item => req.user.checkCanAccessLibraryItem(item))
+
+ if (!libraryItems.length) {
+ return res.status(400).send('Collection has no books accessible to user')
+ }
+
+ const newPlaylist = new Playlist()
+
+ const newPlaylistData = {
+ userId: req.user.id,
+ libraryId: collection.libraryId,
+ name: collection.name,
+ description: collection.description || null,
+ items: libraryItems.map(li => ({ libraryItemId: li.id }))
+ }
+ newPlaylist.setData(newPlaylistData)
+
+ const jsonExpanded = newPlaylist.toJSONExpanded(this.db.libraryItems)
+ await this.db.insertEntity('playlist', newPlaylist)
+ SocketAuthority.clientEmitter(newPlaylist.userId, 'playlist_added', jsonExpanded)
+ res.json(jsonExpanded)
+ }
+
middleware(req, res, next) {
if (req.params.id) {
- var playlist = this.db.playlists.find(p => p.id === req.params.id)
+ const playlist = this.db.playlists.find(p => p.id === req.params.id)
if (!playlist) {
return res.status(404).send('Playlist not found')
}
diff --git a/server/objects/Collection.js b/server/objects/Collection.js
index cab8348d..813ced21 100644
--- a/server/objects/Collection.js
+++ b/server/objects/Collection.js
@@ -38,10 +38,10 @@ class Collection {
}
toJSONExpanded(libraryItems, minifiedBooks = false) {
- var json = this.toJSON()
+ const json = this.toJSON()
json.books = json.books.map(bookId => {
- var _ab = libraryItems.find(li => li.id === bookId)
- return _ab ? minifiedBooks ? _ab.toJSONMinified() : _ab.toJSONExpanded() : null
+ const book = libraryItems.find(li => li.id === bookId)
+ return book ? minifiedBooks ? book.toJSONMinified() : book.toJSONExpanded() : null
}).filter(b => !!b)
return json
}
diff --git a/server/routers/ApiRouter.js b/server/routers/ApiRouter.js
index b219763a..88129244 100644
--- a/server/routers/ApiRouter.js
+++ b/server/routers/ApiRouter.js
@@ -143,7 +143,7 @@ class ApiRouter {
//
// Playlist Routes
//
- this.router.post('/playlists', PlaylistController.middleware.bind(this), PlaylistController.create.bind(this))
+ this.router.post('/playlists', PlaylistController.create.bind(this))
this.router.get('/playlists', PlaylistController.findAllForUser.bind(this))
this.router.get('/playlists/:id', PlaylistController.middleware.bind(this), PlaylistController.findOne.bind(this))
this.router.patch('/playlists/:id', PlaylistController.middleware.bind(this), PlaylistController.update.bind(this))
@@ -152,6 +152,7 @@ class ApiRouter {
this.router.delete('/playlists/:id/item/:libraryItemId/:episodeId?', PlaylistController.middleware.bind(this), PlaylistController.removeItem.bind(this))
this.router.post('/playlists/:id/batch/add', PlaylistController.middleware.bind(this), PlaylistController.addBatch.bind(this))
this.router.post('/playlists/:id/batch/remove', PlaylistController.middleware.bind(this), PlaylistController.removeBatch.bind(this))
+ this.router.post('/playlists/collection/:collectionId', PlaylistController.createFromCollection.bind(this))
//
// Current User Routes (Me)