Skip to content
GitLab
Menu
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
Maxence Bothorel
funkwhale
Commits
1e64f3db
Verified
Commit
1e64f3db
authored
Mar 20, 2018
by
Eliot Berriot
Browse files
Playlist detail page and editor
parent
bed66db5
Changes
3
Hide whitespace changes
Inline
Side-by-side
front/src/components/playlists/Editor.vue
0 → 100644
View file @
1e64f3db
<
template
>
<div
class=
"ui text container"
>
<h2
class=
"ui header"
>
Playlist editor
</h2>
<p>
Drag and drop rows to reorder tracks in the playlist
</p>
<div
class=
"ui buttons"
>
<div
@
click=
"insertMany(queueTracks)"
:disabled=
"queueTracks.length === 0"
:class=
"['ui',
{disabled: queueTracks.length === 0}, 'button']"
title="Copy tracks from current queue to playlist">Insert from queue (
{{
queueTracks
.
length
}}
tracks)
</div>
</div>
<h5
class=
"ui header"
>
Status
</h5>
<div>
<template
v-if=
"status === 'loading'"
>
<div
class=
"ui active tiny inline loader"
></div>
Syncing changes to server...
</
template
>
<
template
v-else-if=
"status === 'errored'"
>
<i
class=
"red x icon"
></i>
An error occured while saving your changes
<div
v-if=
"errors.length > 0"
class=
"ui negative message"
>
<ul
class=
"list"
>
<li
v-for=
"error in errors"
>
{{
error
}}
</li>
</ul>
</div>
</
template
>
<
template
v-else-if=
"status === 'saved'"
>
<i
class=
"green check icon"
></i>
Changes synced with server
</
template
>
</div>
<table
class=
"ui compact very basic fixed single line unstackable table"
>
<draggable
v-model=
"plts"
element=
"tbody"
@
update=
"reorder"
>
<tr
v-for=
"(plt, index) in plts"
:key=
"plt.id"
>
<td
class=
"left aligned"
>
{{ plt.index + 1}}
</td>
<td
class=
"center aligned"
>
<img
class=
"ui mini image"
v-if=
"plt.track.album.cover"
:src=
"plt.track.album.cover"
>
<img
class=
"ui mini image"
v-else
src=
"../../assets/audio/default-cover.png"
>
</td>
<td
colspan=
"4"
>
<strong>
{{ plt.track.title }}
</strong><br
/>
{{ plt.track.artist.name }}
</td>
<td
class=
"right aligned"
>
<i
@
click.stop=
"removePlt(index)"
class=
"circular red trash icon"
></i>
</td>
</tr>
</draggable>
</table>
</div>
</template>
<
script
>
import
{
mapState
}
from
'
vuex
'
import
axios
from
'
axios
'
import
draggable
from
'
vuedraggable
'
export
default
{
components
:
{
draggable
},
props
:
[
'
playlist
'
,
'
playlistTracks
'
],
data
()
{
return
{
plts
:
this
.
playlistTracks
,
isLoading
:
false
,
errors
:
[]
}
},
methods
:
{
success
()
{
this
.
isLoading
=
false
this
.
errors
=
[]
},
errored
(
errors
)
{
this
.
isLoading
=
false
this
.
errors
=
errors
},
reorder
({
oldIndex
,
newIndex
})
{
let
self
=
this
self
.
isLoading
=
true
let
plt
=
this
.
plts
[
newIndex
]
let
url
=
'
playlist-tracks/
'
+
plt
.
id
+
'
/
'
axios
.
patch
(
url
,
{
index
:
newIndex
}).
then
((
response
)
=>
{
self
.
success
()
},
error
=>
{
self
.
errored
(
error
.
backendErrors
)
})
},
removePlt
(
index
)
{
let
plt
=
this
.
plts
[
index
]
this
.
plts
.
splice
(
index
,
1
)
let
self
=
this
self
.
isLoading
=
true
let
url
=
'
playlist-tracks/
'
+
plt
.
id
+
'
/
'
axios
.
delete
(
url
).
then
((
response
)
=>
{
self
.
success
()
},
error
=>
{
self
.
errored
(
error
.
backendErrors
)
})
},
insertMany
(
tracks
)
{
let
self
=
this
let
ids
=
tracks
.
map
(
t
=>
{
return
t
.
id
})
self
.
isLoading
=
true
let
url
=
'
playlists/
'
+
this
.
playlist
.
id
+
'
/add/
'
axios
.
post
(
url
,
{
tracks
:
ids
}).
then
((
response
)
=>
{
response
.
data
.
results
.
forEach
(
r
=>
{
self
.
plts
.
push
(
r
)
})
self
.
success
()
},
error
=>
{
self
.
errored
(
error
.
backendErrors
)
})
}
},
computed
:
{
...
mapState
({
queueTracks
:
state
=>
state
.
queue
.
tracks
}),
status
()
{
if
(
this
.
isLoading
)
{
return
'
loading
'
}
if
(
this
.
errors
.
length
>
0
)
{
return
'
errored
'
}
return
'
saved
'
}
},
watch
:
{
plts
:
{
handler
(
newValue
)
{
newValue
.
forEach
((
e
,
i
)
=>
{
e
.
index
=
i
})
this
.
$emit
(
'
tracks-updated
'
,
newValue
)
},
deep
:
true
}
}
}
</
script
>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<
style
scoped
>
</
style
>
front/src/router/index.js
View file @
1e64f3db
...
...
@@ -21,7 +21,7 @@ import RadioBuilder from '@/components/library/radios/Builder'
import
BatchList
from
'
@/components/library/import/BatchList
'
import
BatchDetail
from
'
@/components/library/import/BatchDetail
'
import
RequestsList
from
'
@/components/requests/RequestsList
'
import
PlaylistDetail
from
'
@/views/playlists/Detail
'
import
Favorites
from
'
@/components/favorites/List
'
Vue
.
use
(
Router
)
...
...
@@ -110,6 +110,7 @@ export default new Router({
},
{
path
:
'
radios/build
'
,
name
:
'
library.radios.build
'
,
component
:
RadioBuilder
,
props
:
true
},
{
path
:
'
radios/build/:id
'
,
name
:
'
library.radios.edit
'
,
component
:
RadioBuilder
,
props
:
true
},
{
path
:
'
playlists/:id
'
,
name
:
'
library.playlists.detail
'
,
component
:
PlaylistDetail
,
props
:
true
},
{
path
:
'
artists/:id
'
,
name
:
'
library.artists.detail
'
,
component
:
LibraryArtist
,
props
:
true
},
{
path
:
'
albums/:id
'
,
name
:
'
library.albums.detail
'
,
component
:
LibraryAlbum
,
props
:
true
},
{
path
:
'
tracks/:id
'
,
name
:
'
library.tracks.detail
'
,
component
:
LibraryTrack
,
props
:
true
},
...
...
front/src/views/playlists/Detail.vue
0 → 100644
View file @
1e64f3db
<
template
>
<div
v-if=
"playlist"
>
<div
class=
"ui head vertical center aligned stripe segment"
>
<div
class=
"segment-content"
>
<h2
class=
"ui center aligned icon header"
>
<i
class=
"circular inverted list yellow icon"
></i>
<div
class=
"content"
>
{{
playlist
.
name
}}
<div
class=
"sub header"
>
Playlist containing
{{
playlistTracks
.
length
}}
tracks,
by
<username
:username=
"playlist.user.username"
></username>
</div>
</div>
</h2>
<div
class=
"ui hidden divider"
></div>
</button>
<play-button
class=
"orange"
:tracks=
"tracks"
>
Play all
</play-button>
<button
class=
"ui icon button"
v-if=
"playlist.user.id === $store.state.auth.profile.id"
@
click=
"edit = !edit"
>
<i
class=
"pencil icon"
></i>
<template
v-if=
"edit"
>
End edition
</
template
>
<
template
v-else
>
Edit...
</
template
>
</button>
</div>
</div>
<div
v-if=
"tracks.length > 0"
class=
"ui vertical stripe segment"
>
<
template
v-if=
"edit"
>
<playlist-editor
@
tracks-updated=
"updatePlts"
:playlist=
"playlist"
:playlist-tracks=
"playlistTracks"
></playlist-editor>
</
template
>
<
template
v-else
>
<h2>
Tracks
</h2>
<track-table
:display-position=
"true"
:tracks=
"tracks"
></track-table>
</
template
>
</div>
</div>
</template>
<
script
>
import
axios
from
'
axios
'
import
TrackTable
from
'
@/components/audio/track/Table
'
import
RadioButton
from
'
@/components/radios/Button
'
import
PlayButton
from
'
@/components/audio/PlayButton
'
import
PlaylistEditor
from
'
@/components/playlists/Editor
'
export
default
{
props
:
{
id
:
{
required
:
true
}
},
components
:
{
PlaylistEditor
,
TrackTable
,
PlayButton
,
RadioButton
},
data
:
function
()
{
return
{
edit
:
false
,
playlist
:
null
,
tracks
:
[],
playlistTracks
:
[]
}
},
created
:
function
()
{
this
.
fetch
()
},
methods
:
{
updatePlts
(
v
)
{
this
.
playlistTracks
=
v
this
.
tracks
=
v
.
map
((
e
,
i
)
=>
{
let
track
=
e
.
track
track
.
position
=
i
+
1
return
track
})
},
fetch
:
function
()
{
let
self
=
this
let
url
=
'
playlists/
'
+
this
.
id
+
'
/
'
axios
.
get
(
url
).
then
((
response
)
=>
{
self
.
playlist
=
response
.
data
axios
.
get
(
url
+
'
tracks
'
).
then
((
response
)
=>
{
self
.
updatePlts
(
response
.
data
.
results
)
})
})
}
}
}
</
script
>
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment