[web] Refactor lists to improve performance

Reduces the number of Vue components that need to be created/managed.
Instead of a Vue component for each item, we now only have one Vue
component for the whole list of items.
This commit is contained in:
chme
2022-02-19 07:47:54 +01:00
parent a24cb11e17
commit 27e2274d8a
40 changed files with 979 additions and 1378 deletions

View File

@@ -4,7 +4,7 @@
<content-with-heading>
<template #options>
<index-button-list :index="albums_list.indexList" />
<index-button-list :index="albums.indexList" />
<div class="columns">
<div class="column">
@@ -44,17 +44,20 @@
</div>
<div class="column">
<p class="heading" style="margin-bottom: 24px">Sort by</p>
<dropdown-menu v-model="sort" :options="sort_options" />
<dropdown-menu
v-model="selected_groupby_option_name"
:options="groupby_option_names"
/>
</div>
</div>
</template>
<template #heading-left>
<p class="title is-4">Albums</p>
<p class="heading">{{ albums_list.sortedAndFiltered.length }} Albums</p>
<p class="heading">{{ albums.count }} Albums</p>
</template>
<template #heading-right />
<template #content>
<list-albums :albums="albums_list" />
<list-albums :albums="albums" />
</template>
</content-with-heading>
</div>
@@ -68,7 +71,7 @@ import ListAlbums from '@/components/ListAlbums.vue'
import DropdownMenu from '@/components/DropdownMenu.vue'
import webapi from '@/webapi'
import * as types from '@/store/mutation_types'
import Albums from '@/lib/Albums'
import { bySortName, byYear, GroupByList } from '@/lib/GroupByList'
const dataObject = {
load: function (to) {
@@ -76,16 +79,7 @@ const dataObject = {
},
set: function (vm, response) {
vm.albums = response.data
vm.index_list = [
...new Set(
vm.albums.items
.filter(
(album) => !vm.$store.state.hide_singles || album.track_count > 2
)
.map((album) => album.name_sort.charAt(0).toUpperCase())
)
]
vm.albums_list = new GroupByList(response.data)
}
}
@@ -104,8 +98,9 @@ export default {
next((vm) => dataObject.set(vm, response))
})
},
beforeRouteUpdate(to, from, next) {
if (this.albums.items.length > 0) {
if (!this.albums_list.isEmpty()) {
next()
return
}
@@ -118,19 +113,53 @@ export default {
data() {
return {
albums: { items: [] },
sort_options: ['Name', 'Recently added', 'Recently released']
albums_list: new GroupByList(),
// List of group by/sort options for itemsGroupByList
groupby_options: [
{ name: 'Name', options: bySortName('name_sort') },
{
name: 'Recently added',
options: byYear('time_added', {
direction: 'desc',
defaultValue: '0000'
})
},
{
name: 'Recently released',
options: byYear('date_released', {
direction: 'desc',
defaultValue: '0000'
})
}
]
}
},
computed: {
albums_list() {
return new Albums(this.albums.items, {
hideSingles: this.hide_singles,
hideSpotify: this.hide_spotify,
sort: this.sort,
group: true
})
albums() {
const groupBy = this.groupby_options.find(
(o) => o.name === this.selected_groupby_option_name
)
this.albums_list.group(groupBy.options, [
(album) => !this.hide_singles || album.track_count <= 2,
(album) => !this.hide_spotify || album.data_kind !== 'spotify'
])
return this.albums_list
},
groupby_option_names() {
return [...this.groupby_options].map((o) => o.name)
},
selected_groupby_option_name: {
get() {
return this.$store.state.albums_sort
},
set(value) {
this.$store.commit(types.ALBUMS_SORT, value)
}
},
spotify_enabled() {
@@ -153,15 +182,6 @@ export default {
set(value) {
this.$store.commit(types.HIDE_SPOTIFY, value)
}
},
sort: {
get() {
return this.$store.state.albums_sort
},
set(value) {
this.$store.commit(types.ALBUMS_SORT, value)
}
}
},

View File

@@ -4,7 +4,10 @@
<div class="columns">
<div class="column">
<p class="heading" style="margin-bottom: 24px">Sort by</p>
<dropdown-menu v-model="sort" :options="sort_options" />
<dropdown-menu
v-model="selected_groupby_option_name"
:options="groupby_option_names"
/>
</div>
</div>
</template>
@@ -36,7 +39,7 @@
>{{ artist.track_count }} tracks</a
>
</p>
<list-albums :albums="albums_list" />
<list-albums :albums="albums" :hide_group_title="true" />
<modal-dialog-artist
:show="show_artist_details_modal"
:artist="artist"
@@ -53,7 +56,7 @@ import ModalDialogArtist from '@/components/ModalDialogArtist.vue'
import DropdownMenu from '@/components/DropdownMenu.vue'
import webapi from '@/webapi'
import * as types from '@/store/mutation_types'
import Albums from '@/lib/Albums'
import { bySortName, byYear, GroupByList } from '@/lib/GroupByList'
const dataObject = {
load: function (to) {
@@ -65,7 +68,7 @@ const dataObject = {
set: function (vm, response) {
vm.artist = response[0].data
vm.albums = response[1].data
vm.albums_list = new GroupByList(response[1].data)
}
}
@@ -94,22 +97,39 @@ export default {
data() {
return {
artist: {},
albums: { items: [] },
albums_list: new GroupByList(),
// List of group by/sort options for itemsGroupByList
groupby_options: [
{ name: 'Name', options: bySortName('name_sort') },
{
name: 'Release date',
options: byYear('date_released', {
direction: 'asc',
defaultValue: '0000'
})
}
],
sort_options: ['Name', 'Release date'],
show_artist_details_modal: false
}
},
computed: {
albums_list() {
return new Albums(this.albums.items, {
sort: this.sort,
group: false
})
albums() {
const groupBy = this.groupby_options.find(
(o) => o.name === this.selected_groupby_option_name
)
this.albums_list.group(groupBy.options)
return this.albums_list
},
sort: {
groupby_option_names() {
return [...this.groupby_options].map((o) => o.name)
},
selected_groupby_option_name: {
get() {
return this.$store.state.artist_albums_sort
},

View File

@@ -4,7 +4,7 @@
<content-with-heading>
<template #options>
<index-button-list :index="artists_list.indexList" />
<index-button-list :index="artists.indexList" />
<div class="columns">
<div class="column">
@@ -44,19 +44,20 @@
</div>
<div class="column">
<p class="heading" style="margin-bottom: 24px">Sort by</p>
<dropdown-menu v-model="sort" :options="sort_options" />
<dropdown-menu
v-model="selected_groupby_option_name"
:options="groupby_option_names"
/>
</div>
</div>
</template>
<template #heading-left>
<p class="title is-4">Artists</p>
<p class="heading">
{{ artists_list.sortedAndFiltered.length }} Artists
</p>
<p class="heading">{{ artists.count }} Artists</p>
</template>
<template #heading-right />
<template #content>
<list-artists :artists="artists_list" />
<list-artists :artists="artists" />
</template>
</content-with-heading>
</div>
@@ -70,7 +71,7 @@ import ListArtists from '@/components/ListArtists.vue'
import DropdownMenu from '@/components/DropdownMenu.vue'
import webapi from '@/webapi'
import * as types from '@/store/mutation_types'
import Artists from '@/lib/Artists'
import { bySortName, byYear, GroupByList } from '@/lib/GroupByList'
const dataObject = {
load: function (to) {
@@ -78,7 +79,7 @@ const dataObject = {
},
set: function (vm, response) {
vm.artists = response.data
vm.artists_list = new GroupByList(response.data)
}
}
@@ -97,8 +98,9 @@ export default {
next((vm) => dataObject.set(vm, response))
})
},
beforeRouteUpdate(to, from, next) {
if (this.artists.items.length > 0) {
if (!this.artists_list.isEmpty()) {
next()
return
}
@@ -111,19 +113,54 @@ export default {
data() {
return {
artists: { items: [] },
sort_options: ['Name', 'Recently added']
// Original data from API call
artists_list: new GroupByList(),
// List of group by/sort options for itemsGroupByList
groupby_options: [
{ name: 'Name', options: bySortName('name_sort') },
{
name: 'Recently added',
options: byYear('time_added', {
direction: 'desc',
defaultValue: '0000'
})
}
]
}
},
computed: {
artists_list() {
return new Artists(this.artists.items, {
hideSingles: this.hide_singles,
hideSpotify: this.hide_spotify,
sort: this.sort,
group: true
})
// Wraps GroupByList and updates it if filter or sort changes
artists() {
if (!this.artists_list) {
return []
}
const groupBy = this.groupby_options.find(
(o) => o.name === this.selected_groupby_option_name
)
this.artists_list.group(groupBy.options, [
(artist) =>
!this.hide_singles || artist.track_count <= artist.album_count * 2,
(artist) => !this.hide_spotify || artist.data_kind !== 'spotify'
])
return this.artists_list
},
// List for the drop down menu
groupby_option_names() {
return [...this.groupby_options].map((o) => o.name)
},
selected_groupby_option_name: {
get() {
return this.$store.state.artists_sort
},
set(value) {
this.$store.commit(types.ARTISTS_SORT, value)
}
},
spotify_enabled() {
@@ -146,23 +183,10 @@ export default {
set(value) {
this.$store.commit(types.HIDE_SPOTIFY, value)
}
},
sort: {
get() {
return this.$store.state.artists_sort
},
set(value) {
this.$store.commit(types.ARTISTS_SORT, value)
}
}
},
methods: {
scrollToTop: function () {
window.scrollTo({ top: 0, behavior: 'smooth' })
}
}
methods: {}
}
</script>

View File

@@ -4,16 +4,14 @@
<content-with-heading>
<template #options>
<index-button-list :index="albums_list.indexList" />
<index-button-list :index="albums.indexList" />
</template>
<template #heading-left>
<p class="title is-4">Audiobooks</p>
<p class="heading">
{{ albums_list.sortedAndFiltered.length }} Audiobooks
</p>
<p class="heading">{{ albums.count }} Audiobooks</p>
</template>
<template #content>
<list-albums :albums="albums_list" />
<list-albums :albums="albums" />
</template>
</content-with-heading>
</div>
@@ -25,7 +23,7 @@ import IndexButtonList from '@/components/IndexButtonList.vue'
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
import ListAlbums from '@/components/ListAlbums.vue'
import webapi from '@/webapi'
import Albums from '@/lib/Albums'
import { bySortName, GroupByList } from '@/lib/GroupByList'
const dataObject = {
load: function (to) {
@@ -33,7 +31,8 @@ const dataObject = {
},
set: function (vm, response) {
vm.albums = response.data
vm.albums = new GroupByList(response.data)
vm.albums.group(bySortName('name_sort'))
}
}
@@ -51,7 +50,12 @@ export default {
next((vm) => dataObject.set(vm, response))
})
},
beforeRouteUpdate(to, from, next) {
if (!this.albums.isEmpty()) {
next()
return
}
const vm = this
dataObject.load(to).then((response) => {
dataObject.set(vm, response)
@@ -61,16 +65,7 @@ export default {
data() {
return {
albums: { items: [] }
}
},
computed: {
albums_list() {
return new Albums(this.albums.items, {
sort: 'Name',
group: true
})
albums: new GroupByList()
}
},

View File

@@ -25,7 +25,7 @@
<p class="heading has-text-centered-mobile">
{{ artist.album_count }} albums
</p>
<list-albums :albums="albums.items" />
<list-albums :albums="albums" />
<modal-dialog-artist
:show="show_artist_details_modal"
:artist="artist"
@@ -40,6 +40,7 @@ import ContentWithHeading from '@/templates/ContentWithHeading.vue'
import ListAlbums from '@/components/ListAlbums.vue'
import ModalDialogArtist from '@/components/ModalDialogArtist.vue'
import webapi from '@/webapi'
import { GroupByList } from '../lib/GroupByList'
const dataObject = {
load: function (to) {
@@ -51,7 +52,7 @@ const dataObject = {
set: function (vm, response) {
vm.artist = response[0].data
vm.albums = response[1].data
vm.albums = new GroupByList(response[1].data)
}
}
@@ -64,7 +65,12 @@ export default {
next((vm) => dataObject.set(vm, response))
})
},
beforeRouteUpdate(to, from, next) {
if (!this.albums.isEmpty()) {
next()
return
}
const vm = this
dataObject.load(to).then((response) => {
dataObject.set(vm, response)
@@ -75,7 +81,7 @@ export default {
data() {
return {
artist: {},
albums: {},
albums: new GroupByList(),
show_artist_details_modal: false
}

View File

@@ -4,17 +4,15 @@
<content-with-heading>
<template #options>
<index-button-list :index="artists_list.indexList" />
<index-button-list :index="artists.indexList" />
</template>
<template #heading-left>
<p class="title is-4">Authors</p>
<p class="heading">
{{ artists_list.sortedAndFiltered.length }} Authors
</p>
<p class="heading">{{ artists.count }} Authors</p>
</template>
<template #heading-right />
<template #content>
<list-artists :artists="artists_list" />
<list-artists :artists="artists" />
</template>
</content-with-heading>
</div>
@@ -26,7 +24,7 @@ import TabsAudiobooks from '@/components/TabsAudiobooks.vue'
import IndexButtonList from '@/components/IndexButtonList.vue'
import ListArtists from '@/components/ListArtists.vue'
import webapi from '@/webapi'
import Artists from '@/lib/Artists'
import { bySortName, GroupByList } from '@/lib/GroupByList'
const dataObject = {
load: function (to) {
@@ -34,7 +32,7 @@ const dataObject = {
},
set: function (vm, response) {
vm.artists = response.data
vm.artists_list = new GroupByList(response.data)
}
}
@@ -52,7 +50,12 @@ export default {
next((vm) => dataObject.set(vm, response))
})
},
beforeRouteUpdate(to, from, next) {
if (!this.artists_list.isEmpty()) {
next()
return
}
const vm = this
dataObject.load(to).then((response) => {
dataObject.set(vm, response)
@@ -62,16 +65,17 @@ export default {
data() {
return {
artists: { items: [] }
artists_list: new GroupByList()
}
},
computed: {
artists_list() {
return new Artists(this.artists.items, {
sort: 'Name',
group: true
})
artists() {
if (!this.artists_list) {
return []
}
this.artists_list.group(bySortName('name_sort'))
return this.artists_list
}
},

View File

@@ -9,7 +9,7 @@
<p class="heading">albums</p>
</template>
<template #content>
<list-albums :albums="recently_added.items" />
<list-albums :albums="recently_added" />
</template>
<template #footer>
<nav class="level">
@@ -54,6 +54,7 @@ import TabsMusic from '@/components/TabsMusic.vue'
import ListAlbums from '@/components/ListAlbums.vue'
import ListTracks from '@/components/ListTracks.vue'
import webapi from '@/webapi'
import { GroupByList } from '@/lib/GroupByList'
const dataObject = {
load: function (to) {
@@ -74,7 +75,7 @@ const dataObject = {
},
set: function (vm, response) {
vm.recently_added = response[0].data.albums
vm.recently_added = new GroupByList(response[0].data.albums)
vm.recently_played = response[1].data.tracks
}
}
@@ -88,6 +89,7 @@ export default {
next((vm) => dataObject.set(vm, response))
})
},
beforeRouteUpdate(to, from, next) {
const vm = this
dataObject.load(to).then((response) => {
@@ -98,7 +100,7 @@ export default {
data() {
return {
recently_added: { items: [] },
recently_added: [],
recently_played: { items: [] },
show_track_details_modal: false,

View File

@@ -8,7 +8,7 @@
<p class="heading">albums</p>
</template>
<template #content>
<list-albums :albums="albums_list" />
<list-albums :albums="recently_added" />
</template>
</content-with-heading>
</div>
@@ -20,7 +20,7 @@ import TabsMusic from '@/components/TabsMusic.vue'
import ListAlbums from '@/components/ListAlbums.vue'
import webapi from '@/webapi'
import store from '@/store'
import Albums from '@/lib/Albums'
import { byDateSinceToday, GroupByList } from '@/lib/GroupByList'
const dataObject = {
load: function (to) {
@@ -34,7 +34,13 @@ const dataObject = {
},
set: function (vm, response) {
vm.recently_added = response.data.albums
vm.recently_added = new GroupByList(response.data.albums)
vm.recently_added.group(
byDateSinceToday('time_added', {
direction: 'desc',
defaultValue: '0000'
})
)
}
}
@@ -47,7 +53,12 @@ export default {
next((vm) => dataObject.set(vm, response))
})
},
beforeRouteUpdate(to, from, next) {
if (!this.recently_added.isEmpty()) {
next()
return
}
const vm = this
dataObject.load(to).then((response) => {
dataObject.set(vm, response)
@@ -57,18 +68,7 @@ export default {
data() {
return {
recently_added: { items: [] }
}
},
computed: {
albums_list() {
return new Albums(this.recently_added.items, {
hideSingles: false,
hideSpotify: false,
sort: 'Recently added (browse)',
group: true
})
recently_added: new GroupByList()
}
}
}

View File

@@ -1,9 +1,6 @@
<template>
<div>
<content-with-heading>
<template #options>
<index-button-list :index="index_list" />
</template>
<template #heading-left>
<p class="title is-4">
{{ name }}
@@ -27,28 +24,11 @@
</template>
<template #content>
<p class="heading has-text-centered-mobile">
{{ composer_albums.total }} albums |
{{ albums_list.total }} albums |
<a class="has-text-link" @click="open_tracks">tracks</a>
</p>
<list-item-albums
v-for="album in composer_albums.items"
:key="album.id"
:album="album"
@click="open_album(album)"
>
<template #actions>
<a @click="open_dialog(album)">
<span class="icon has-text-dark"
><i class="mdi mdi-dots-vertical mdi-18px"
/></span>
</a>
</template>
</list-item-albums>
<modal-dialog-album
:show="show_details_modal"
:album="selected_album"
@close="show_details_modal = false"
/>
<list-albums :albums="albums_list" :hide_group_title="true" />
<modal-dialog-composer
:show="show_composer_details_modal"
:composer="{ name: name }"
@@ -61,10 +41,10 @@
<script>
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
import ListItemAlbums from '@/components/ListItemAlbum.vue'
import ModalDialogAlbum from '@/components/ModalDialogAlbum.vue'
import ListAlbums from '@/components/ListAlbums.vue'
import ModalDialogComposer from '@/components/ModalDialogComposer.vue'
import webapi from '@/webapi'
import { GroupByList } from '@/lib/GroupByList'
const dataObject = {
load: function (to) {
@@ -73,7 +53,7 @@ const dataObject = {
set: function (vm, response) {
vm.name = vm.$route.params.composer
vm.composer_albums = response.data.albums
vm.albums_list = new GroupByList(response.data.albums)
}
}
@@ -81,8 +61,7 @@ export default {
name: 'PageComposer',
components: {
ContentWithHeading,
ListItemAlbums,
ModalDialogAlbum,
ListAlbums,
ModalDialogComposer
},
@@ -102,29 +81,13 @@ export default {
data() {
return {
name: '',
composer_albums: { items: [] },
show_details_modal: false,
selected_album: {},
albums_list: new GroupByList(),
show_composer_details_modal: false
}
},
computed: {
index_list() {
return [
...new Set(
this.composer_albums.items.map((album) =>
album.name_sort.charAt(0).toUpperCase()
)
)
]
}
},
methods: {
open_tracks: function () {
this.show_details_modal = false
this.$router.push({
name: 'ComposerTracks',
params: { composer: this.name }
@@ -136,15 +99,6 @@ export default {
'composer is "' + this.name + '" and media_kind is music',
true
)
},
open_album: function (album) {
this.$router.push({ path: '/music/albums/' + album.id })
},
open_dialog: function (album) {
this.selected_album = album
this.show_details_modal = true
}
}
}

View File

@@ -1,9 +1,6 @@
<template>
<div>
<content-with-heading>
<template #options>
<index-button-list :index="index_list" />
</template>
<template #heading-left>
<p class="title is-4">
{{ composer }}
@@ -30,25 +27,7 @@
<a class="has-text-link" @click="open_albums">albums</a> |
{{ tracks.total }} tracks
</p>
<list-item-track
v-for="(track, index) in rated_tracks"
:key="track.id"
:track="track"
@click="play_track(index)"
>
<template #actions>
<a @click.prevent.stop="open_dialog(track)">
<span class="icon has-text-dark"
><i class="mdi mdi-dots-vertical mdi-18px"
/></span>
</a>
</template>
</list-item-track>
<modal-dialog-track
:show="show_details_modal"
:track="selected_track"
@close="show_details_modal = false"
/>
<list-tracks :tracks="tracks.items" :expression="play_expression" />
<modal-dialog-composer
:show="show_composer_details_modal"
:composer="{ name: composer }"
@@ -61,8 +40,7 @@
<script>
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
import ListItemTrack from '@/components/ListItemTrack.vue'
import ModalDialogTrack from '@/components/ModalDialogTrack.vue'
import ListTracks from '@/components/ListTracks.vue'
import ModalDialogComposer from '@/components/ModalDialogComposer.vue'
import webapi from '@/webapi'
@@ -81,8 +59,7 @@ export default {
name: 'PageComposerTracks',
components: {
ContentWithHeading,
ListItemTrack,
ModalDialogTrack,
ListTracks,
ModalDialogComposer
},
@@ -104,30 +81,13 @@ export default {
tracks: { items: [] },
composer: '',
min_rating: 0,
show_details_modal: false,
selected_track: {},
show_composer_details_modal: false
}
},
computed: {
index_list() {
return [
...new Set(
this.tracks.items.map((track) =>
track.title_sort.charAt(0).toUpperCase()
)
)
]
},
rated_tracks() {
return this.tracks.items.filter(
(track) => track.rating >= this.min_rating
)
play_expression() {
return 'composer is "' + this.composer + '" and media_kind is music'
}
},
@@ -141,30 +101,7 @@ export default {
},
play: function () {
webapi.player_play_expression(
'composer is "' + this.composer + '" and media_kind is music',
true
)
},
play_track: function (position) {
webapi.player_play_expression(
'composer is "' + this.composer + '" and media_kind is music',
false,
position
)
},
show_rating: function (rating) {
if (rating === 0.5) {
rating = 0
}
this.min_rating = Math.ceil(rating) * 20
},
open_dialog: function (track) {
this.selected_track = track
this.show_details_modal = true
webapi.player_play_expression(this.play_expression, true)
}
}
}

View File

@@ -4,16 +4,14 @@
<content-with-heading>
<template #options>
<index-button-list :index="composers_list.indexList" />
<index-button-list :index="composers.indexList" />
</template>
<template #heading-left>
<p class="title is-4">
{{ heading }}
</p>
<p class="title is-4">Composers</p>
<p class="heading">{{ composers.total }} composers</p>
</template>
<template #content>
<list-composers :composers="composers_list" />
<list-composers :composers="composers" />
</template>
</content-with-heading>
</div>
@@ -25,7 +23,7 @@ import TabsMusic from '@/components/TabsMusic.vue'
import IndexButtonList from '@/components/IndexButtonList.vue'
import ListComposers from '@/components/ListComposers.vue'
import webapi from '@/webapi'
import Composers from '@/lib/Composers'
import { byName, GroupByList } from '@/lib/GroupByList'
const dataObject = {
load: function (to) {
@@ -33,13 +31,8 @@ const dataObject = {
},
set: function (vm, response) {
if (response.data.composers) {
vm.composers = response.data.composers
vm.heading = vm.$route.params.genre
} else {
vm.composers = response.data
vm.heading = 'Composers'
}
vm.composers = new GroupByList(response.data)
vm.composers.group(byName('name_sort'))
}
}
@@ -52,7 +45,12 @@ export default {
next((vm) => dataObject.set(vm, response))
})
},
beforeRouteUpdate(to, from, next) {
if (!this.composers.isEmpty()) {
next()
return
}
const vm = this
dataObject.load(to).then((response) => {
dataObject.set(vm, response)
@@ -62,46 +60,11 @@ export default {
data() {
return {
composers: { items: [] },
heading: '',
show_details_modal: false,
selected_composer: {}
composers: new GroupByList()
}
},
computed: {
index_list() {
return [
...new Set(
this.composers.items.map((composer) =>
composer.name.charAt(0).toUpperCase()
)
)
]
},
composers_list() {
return new Composers(this.composers.items, {
sort: 'Name',
group: true
})
}
},
methods: {
open_composer: function (composer) {
this.$router.push({
name: 'ComposerAlbums',
params: { composer: composer.name }
})
},
open_dialog: function (composer) {
this.selected_composer = composer
this.show_details_modal = true
}
}
methods: {}
}
</script>

View File

@@ -24,94 +24,21 @@
</div>
</template>
<template #content>
<div
v-if="$route.query.directory"
class="media"
@click="open_parent_directory()"
>
<figure class="media-left fd-has-action">
<span class="icon">
<i class="mdi mdi-subdirectory-arrow-left" />
</span>
</figure>
<div class="media-content fd-has-action is-clipped">
<h1 class="title is-6">..</h1>
</div>
<div class="media-right">
<slot name="actions" />
</div>
</div>
<list-directories :directories="files.directories" />
<list-item-directory
v-for="directory in files.directories"
:key="directory.path"
:directory="directory"
@click="open_directory(directory)"
>
<template #actions>
<a @click="open_directory_dialog(directory)">
<span class="icon has-text-dark"
><i class="mdi mdi-dots-vertical mdi-18px"
/></span>
</a>
</template>
</list-item-directory>
<list-playlists :playlists="files.playlists.items" />
<list-item-playlist
v-for="playlist in files.playlists.items"
:key="playlist.id"
:playlist="playlist"
@click="open_playlist(playlist)"
>
<template #icon>
<span class="icon">
<i class="mdi mdi-library-music" />
</span>
</template>
<template #actions>
<a @click="open_playlist_dialog(playlist)">
<span class="icon has-text-dark"
><i class="mdi mdi-dots-vertical mdi-18px"
/></span>
</a>
</template>
</list-item-playlist>
<list-item-track
v-for="(track, index) in files.tracks.items"
:key="track.id"
:track="track"
@click="play_track(index)"
>
<template #icon>
<span class="icon">
<i class="mdi mdi-file-outline" />
</span>
</template>
<template #actions>
<a @click="open_track_dialog(track)">
<span class="icon has-text-dark"
><i class="mdi mdi-dots-vertical mdi-18px"
/></span>
</a>
</template>
</list-item-track>
<list-tracks
:tracks="files.tracks.items"
:expression="play_expression"
:show_icon="true"
/>
<modal-dialog-directory
:show="show_directory_details_modal"
:directory="selected_directory"
@close="show_directory_details_modal = false"
/>
<modal-dialog-playlist
:show="show_playlist_details_modal"
:playlist="selected_playlist"
@close="show_playlist_details_modal = false"
/>
<modal-dialog-track
:show="show_track_details_modal"
:track="selected_track"
@close="show_track_details_modal = false"
/>
</template>
</content-with-heading>
</div>
@@ -119,12 +46,9 @@
<script>
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
import ListItemDirectory from '@/components/ListItemDirectory.vue'
import ListItemPlaylist from '@/components/ListItemPlaylist.vue'
import ListItemTrack from '@/components/ListItemTrack.vue'
import ModalDialogDirectory from '@/components/ModalDialogDirectory.vue'
import ModalDialogPlaylist from '@/components/ModalDialogPlaylist.vue'
import ModalDialogTrack from '@/components/ModalDialogTrack.vue'
import ListDirectories from '@/components/ListDirectories.vue'
import ListPlaylists from '@/components/ListPlaylists.vue'
import ListTracks from '@/components/ListTracks.vue'
import webapi from '@/webapi'
const dataObject = {
@@ -154,12 +78,9 @@ export default {
name: 'PageFiles',
components: {
ContentWithHeading,
ListItemDirectory,
ListItemPlaylist,
ListItemTrack,
ModalDialogDirectory,
ModalDialogPlaylist,
ModalDialogTrack
ListDirectories,
ListPlaylists,
ListTracks
},
beforeRouteEnter(to, from, next) {
@@ -181,16 +102,7 @@ export default {
directories: [],
tracks: { items: [] },
playlists: { items: [] }
},
show_directory_details_modal: false,
selected_directory: {},
show_playlist_details_modal: false,
selected_playlist: {},
show_track_details_modal: false,
selected_track: {}
}
}
},
@@ -200,72 +112,18 @@ export default {
return this.$route.query.directory
}
return '/'
},
play_expression() {
return (
'path starts with "' + this.current_directory + '" order by path asc'
)
}
},
methods: {
open_parent_directory: function () {
const parent = this.current_directory.slice(
0,
this.current_directory.lastIndexOf('/')
)
if (
parent === '' ||
this.$store.state.config.directories.includes(this.current_directory)
) {
this.$router.push({ path: '/files' })
} else {
this.$router.push({
path: '/files',
query: {
directory: this.current_directory.slice(
0,
this.current_directory.lastIndexOf('/')
)
}
})
}
},
open_directory: function (directory) {
this.$router.push({
path: '/files',
query: { directory: directory.path }
})
},
open_directory_dialog: function (directory) {
this.selected_directory = directory
this.show_directory_details_modal = true
},
play: function () {
webapi.player_play_expression(
'path starts with "' + this.current_directory + '" order by path asc',
false
)
},
play_track: function (position) {
webapi.player_play_uri(
this.files.tracks.items.map((a) => a.uri).join(','),
false,
position
)
},
open_track_dialog: function (track) {
this.selected_track = track
this.show_track_details_modal = true
},
open_playlist: function (playlist) {
this.$router.push({ path: '/playlists/' + playlist.id + '/tracks' })
},
open_playlist_dialog: function (playlist) {
this.selected_playlist = playlist
this.show_playlist_details_modal = true
webapi.player_play_expression(this.play_expression, false)
}
}
}

View File

@@ -2,7 +2,7 @@
<div>
<content-with-heading>
<template #options>
<index-button-list :index="index_list" />
<index-button-list :index="albums_list.indexList" />
</template>
<template #heading-left>
<p class="title is-4">
@@ -27,10 +27,10 @@
</template>
<template #content>
<p class="heading has-text-centered-mobile">
{{ genre_albums.total }} albums |
{{ albums_list.total }} albums |
<a class="has-text-link" @click="open_tracks">tracks</a>
</p>
<list-albums :albums="genre_albums.items" />
<list-albums :albums="albums_list" />
<modal-dialog-genre
:show="show_genre_details_modal"
:genre="{ name: name }"
@@ -47,6 +47,7 @@ import IndexButtonList from '@/components/IndexButtonList.vue'
import ListAlbums from '@/components/ListAlbums.vue'
import ModalDialogGenre from '@/components/ModalDialogGenre.vue'
import webapi from '@/webapi'
import { bySortName, GroupByList } from '@/lib/GroupByList'
const dataObject = {
load: function (to) {
@@ -55,7 +56,8 @@ const dataObject = {
set: function (vm, response) {
vm.name = vm.$route.params.genre
vm.genre_albums = response.data.albums
vm.albums_list = new GroupByList(response.data.albums)
vm.albums_list.group(bySortName('name_sort'))
}
}
@@ -74,6 +76,10 @@ export default {
})
},
beforeRouteUpdate(to, from, next) {
if (!this.albums_list.isEmpty()) {
next()
return
}
const vm = this
dataObject.load(to).then((response) => {
dataObject.set(vm, response)
@@ -84,24 +90,12 @@ export default {
data() {
return {
name: '',
genre_albums: { items: [] },
albums_list: new GroupByList(),
show_genre_details_modal: false
}
},
computed: {
index_list() {
return [
...new Set(
this.genre_albums.items.map((album) =>
album.name.charAt(0).toUpperCase()
)
)
]
}
},
methods: {
open_tracks: function () {
this.show_details_modal = false
@@ -113,11 +107,6 @@ export default {
'genre is "' + this.name + '" and media_kind is music',
true
)
},
open_dialog: function (album) {
this.selected_album = album
this.show_details_modal = true
}
}
}

View File

@@ -4,32 +4,14 @@
<content-with-heading>
<template #options>
<index-button-list :index="index_list" />
<index-button-list :index="genres.indexList" />
</template>
<template #heading-left>
<p class="title is-4">Genres</p>
<p class="heading">{{ genres.total }} genres</p>
</template>
<template #content>
<list-item-genre
v-for="genre in genres.items"
:key="genre.name"
:genre="genre"
@click="open_genre(genre)"
>
<template #actions>
<a @click.prevent.stop="open_dialog(genre)">
<span class="icon has-text-dark"
><i class="mdi mdi-dots-vertical mdi-18px"
/></span>
</a>
</template>
</list-item-genre>
<modal-dialog-genre
:show="show_details_modal"
:genre="selected_genre"
@close="show_details_modal = false"
/>
<list-genres :genres="genres" />
</template>
</content-with-heading>
</div>
@@ -39,9 +21,9 @@
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
import TabsMusic from '@/components/TabsMusic.vue'
import IndexButtonList from '@/components/IndexButtonList.vue'
import ListItemGenre from '@/components/ListItemGenre.vue'
import ModalDialogGenre from '@/components/ModalDialogGenre.vue'
import ListGenres from '@/components/ListGenres.vue'
import webapi from '@/webapi'
import { byName, GroupByList } from '@/lib/GroupByList'
const dataObject = {
load: function (to) {
@@ -50,6 +32,8 @@ const dataObject = {
set: function (vm, response) {
vm.genres = response.data
vm.genres = new GroupByList(response.data)
vm.genres.group(byName('name_sort'))
}
}
@@ -59,8 +43,7 @@ export default {
ContentWithHeading,
TabsMusic,
IndexButtonList,
ListItemGenre,
ModalDialogGenre
ListGenres
},
beforeRouteEnter(to, from, next) {
@@ -78,33 +61,13 @@ export default {
data() {
return {
genres: { items: [] },
show_details_modal: false,
selected_genre: {}
genres: new GroupByList()
}
},
computed: {
index_list() {
return [
...new Set(
this.genres.items.map((genre) => genre.name.charAt(0).toUpperCase())
)
]
}
},
computed: {},
methods: {
open_genre: function (genre) {
this.$router.push({ name: 'Genre', params: { genre: genre.name } })
},
open_dialog: function (genre) {
this.selected_genre = genre
this.show_details_modal = true
}
}
methods: {}
}
</script>

View File

@@ -27,27 +27,9 @@
<p class="heading has-text-centered-mobile">
{{ album.track_count }} tracks
</p>
<list-item-track
v-for="track in tracks"
:key="track.id"
:track="track"
@click="play_track(track)"
>
<template #progress>
<progress-bar :max="track.length_ms" :value="track.seek_ms" />
</template>
<template #actions>
<a @click.prevent.stop="open_dialog(track)">
<span class="icon has-text-dark"
><i class="mdi mdi-dots-vertical mdi-18px"
/></span>
</a>
</template>
</list-item-track>
<modal-dialog-track
:show="show_details_modal"
:track="selected_track"
@close="show_details_modal = false"
<list-tracks
:tracks="tracks"
:show_progress="true"
@play-count-changed="reload_tracks"
/>
<modal-dialog-album
@@ -81,11 +63,9 @@
<script>
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
import ListItemTrack from '@/components/ListItemTrack.vue'
import ModalDialogTrack from '@/components/ModalDialogTrack.vue'
import ListTracks from '@/components/ListTracks.vue'
import ModalDialogAlbum from '@/components/ModalDialogAlbum.vue'
import ModalDialog from '@/components/ModalDialog.vue'
import ProgressBar from '@/components/ProgressBar.vue'
import webapi from '@/webapi'
const dataObject = {
@@ -106,11 +86,9 @@ export default {
name: 'PagePodcast',
components: {
ContentWithHeading,
ListItemTrack,
ModalDialogTrack,
ListTracks,
ModalDialogAlbum,
ModalDialog,
ProgressBar
ModalDialog
},
beforeRouteEnter(to, from, next) {
@@ -131,9 +109,6 @@ export default {
album: {},
tracks: [],
show_details_modal: false,
selected_track: {},
show_album_details_modal: false,
show_remove_podcast_modal: false,
@@ -152,15 +127,6 @@ export default {
webapi.player_play_uri(this.album.uri, false)
},
play_track: function (track) {
webapi.player_play_uri(track.uri, false)
},
open_dialog: function (track) {
this.selected_track = track
this.show_details_modal = true
},
open_remove_podcast_dialog: function () {
this.show_album_details_modal = false
webapi.library_track_playlists(this.tracks[0].id).then(({ data }) => {

View File

@@ -15,27 +15,9 @@
</div>
</template>
<template #content>
<list-item-track
v-for="track in new_episodes.items"
:key="track.id"
:track="track"
@click="play_track(track)"
>
<template #progress>
<progress-bar :max="track.length_ms" :value="track.seek_ms" />
</template>
<template #actions>
<a @click="open_track_dialog(track)">
<span class="icon has-text-dark"
><i class="mdi mdi-dots-vertical mdi-18px"
/></span>
</a>
</template>
</list-item-track>
<modal-dialog-track
:show="show_track_details_modal"
:track="selected_track"
@close="show_track_details_modal = false"
<list-tracks
:tracks="new_episodes.items"
:show_progress="true"
@play-count-changed="reload_new_episodes"
/>
</template>
@@ -64,7 +46,7 @@
</template>
<template #content>
<list-albums
:albums="albums.items"
:albums="albums"
@play-count-changed="reload_new_episodes()"
@podcast-deleted="reload_podcasts()"
/>
@@ -80,13 +62,12 @@
<script>
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
import ListItemTrack from '@/components/ListItemTrack.vue'
import ListTracks from '@/components/ListTracks.vue'
import ListAlbums from '@/components/ListAlbums.vue'
import ModalDialogTrack from '@/components/ModalDialogTrack.vue'
import ModalDialogAddRss from '@/components/ModalDialogAddRss.vue'
import ProgressBar from '@/components/ProgressBar.vue'
import * as types from '@/store/mutation_types'
import webapi from '@/webapi'
import { GroupByList } from '@/lib/GroupByList'
const dataObject = {
load: function (to) {
@@ -97,7 +78,7 @@ const dataObject = {
},
set: function (vm, response) {
vm.albums = response[0].data
vm.albums = new GroupByList(response[0].data)
vm.new_episodes = response[1].data.tracks
}
}
@@ -106,11 +87,9 @@ export default {
name: 'PagePodcasts',
components: {
ContentWithHeading,
ListItemTrack,
ListTracks,
ListAlbums,
ModalDialogTrack,
ModalDialogAddRss,
ProgressBar
ModalDialogAddRss
},
beforeRouteEnter(to, from, next) {
@@ -118,6 +97,7 @@ export default {
next((vm) => dataObject.set(vm, response))
})
},
beforeRouteUpdate(to, from, next) {
const vm = this
dataObject.load(to).then((response) => {
@@ -128,13 +108,10 @@ export default {
data() {
return {
albums: { items: [] },
albums: [],
new_episodes: { items: [] },
show_url_modal: false,
show_track_details_modal: false,
selected_track: {}
show_url_modal: false
}
},
@@ -145,15 +122,6 @@ export default {
},
methods: {
play_track: function (track) {
webapi.player_play_uri(track.uri, false)
},
open_track_dialog: function (track) {
this.selected_track = track
this.show_track_details_modal = true
},
mark_all_played: function () {
this.new_episodes.items.forEach((ep) => {
webapi.library_track_update(ep.id, { play_count: 'increment' })
@@ -173,7 +141,7 @@ export default {
reload_podcasts: function () {
webapi.library_albums('podcast').then(({ data }) => {
this.albums = data
this.albums = new GroupByList(data)
this.reload_new_episodes()
})
},

View File

@@ -79,7 +79,7 @@
<p class="title is-4">Artists</p>
</template>
<template #content>
<list-artists :artists="artists.items" />
<list-artists :artists="artists" :hide_group_title="true" />
</template>
<template #footer>
<nav v-if="show_all_artists_button" class="level">
@@ -105,7 +105,7 @@
<p class="title is-4">Albums</p>
</template>
<template #content>
<list-albums :albums="albums.items" />
<list-albums :albums="albums" :hide_group_title="true" />
</template>
<template #footer>
<nav v-if="show_all_albums_button" class="level">
@@ -183,7 +183,7 @@
<p class="title is-4">Podcasts</p>
</template>
<template #content>
<list-albums :albums="podcasts.items" />
<list-albums :albums="podcasts" />
</template>
<template #footer>
<nav v-if="show_all_podcasts_button" class="level">
@@ -209,7 +209,7 @@
<p class="title is-4">Audiobooks</p>
</template>
<template #content>
<list-albums :albums="audiobooks.items" />
<list-albums :albums="audiobooks" />
</template>
<template #footer>
<nav v-if="show_all_audiobooks_button" class="level">
@@ -242,6 +242,7 @@ import ListComposers from '@/components/ListComposers.vue'
import ListPlaylists from '@/components/ListPlaylists.vue'
import webapi from '@/webapi'
import * as types from '@/store/mutation_types'
import { GroupByList } from '@/lib/GroupByList'
export default {
name: 'PageSearch',
@@ -261,12 +262,12 @@ export default {
search_query: '',
tracks: { items: [], total: 0 },
artists: { items: [], total: 0 },
albums: { items: [], total: 0 },
artists: new GroupByList(),
albums: new GroupByList(),
composers: { items: [], total: 0 },
playlists: { items: [], total: 0 },
audiobooks: { items: [], total: 0 },
podcasts: { items: [], total: 0 }
audiobooks: new GroupByList(),
podcasts: new GroupByList()
}
},
@@ -393,8 +394,8 @@ export default {
webapi.search(searchParams).then(({ data }) => {
this.tracks = data.tracks ? data.tracks : { items: [], total: 0 }
this.artists = data.artists ? data.artists : { items: [], total: 0 }
this.albums = data.albums ? data.albums : { items: [], total: 0 }
this.artists = new GroupByList(data.artists)
this.albums = new GroupByList(data.albums)
this.composers = data.composers
? data.composers
: { items: [], total: 0 }
@@ -431,7 +432,7 @@ export default {
}
webapi.search(searchParams).then(({ data }) => {
this.audiobooks = data.albums ? data.albums : { items: [], total: 0 }
this.audiobooks = new GroupByList(data.albums)
})
},
@@ -462,7 +463,7 @@ export default {
}
webapi.search(searchParams).then(({ data }) => {
this.podcasts = data.albums ? data.albums : { items: [], total: 0 }
this.podcasts = new GroupByList(data.albums)
})
},