#1473 Add sort by rating for composer, genre, and artist tracks.

This commit is contained in:
Alain Nussbaumer 2023-03-23 23:19:55 +01:00
parent 90e0be07e2
commit a55c6ed941
19 changed files with 310 additions and 117 deletions

View File

@ -1,11 +1,17 @@
<template> <template>
<template v-for="track in tracks" :key="track.itemId">
<div v-if="!track.isItem" class="mt-6 mb-5 py-2">
<span
:id="'index_' + track.groupKey"
class="tag is-info is-light is-small has-text-weight-bold"
v-text="track.groupKey"
/>
</div>
<div <div
v-for="(track, index) in tracks" v-else-if="track.isItem"
:id="'index_' + track.title_sort.charAt(0).toUpperCase()"
:key="track.id"
class="media" class="media"
:class="{ 'with-progress': show_progress }" :class="{ 'with-progress': show_progress }"
@click="play_track(index, track)" @click="play_track(index, track.item)"
> >
<figure v-if="show_icon" class="media-left fd-has-action"> <figure v-if="show_icon" class="media-left fd-has-action">
<span class="icon"><mdicon name="file-outline" size="16" /></span> <span class="icon"><mdicon name="file-outline" size="16" /></span>
@ -15,26 +21,27 @@
class="title is-6" class="title is-6"
:class="{ :class="{
'has-text-grey': 'has-text-grey':
track.media_kind === 'podcast' && track.play_count > 0 track.item.media_kind === 'podcast' && track.item.play_count > 0
}" }"
v-text="track.title" v-text="track.item.title"
/> />
<h2 class="subtitle is-7 has-text-grey" v-text="track.artist" /> <h2 class="subtitle is-7 has-text-grey" v-text="track.item.artist" />
<h2 class="subtitle is-7 has-text-grey" v-text="track.album" /> <h2 class="subtitle is-7 has-text-grey" v-text="track.item.album" />
<progress-bar <progress-bar
v-if="show_progress" v-if="show_progress"
:max="track.length_ms" :max="track.item.length_ms"
:value="track.seek_ms" :value="track.item.seek_ms"
/> />
</div> </div>
<div class="media-right"> <div class="media-right">
<a @click.prevent.stop="open_dialog(track)"> <a @click.prevent.stop="open_dialog(track.item)">
<span class="icon has-text-dark" <span class="icon has-text-dark"
><mdicon name="dots-vertical" size="16" ><mdicon name="dots-vertical" size="16"
/></span> /></span>
</a> </a>
</div> </div>
</div> </div>
</template>
<teleport to="#app"> <teleport to="#app">
<modal-dialog-track <modal-dialog-track
:show="show_details_modal" :show="show_details_modal"

View File

@ -42,6 +42,22 @@ export function byName(field, defaultValue = '_') {
} }
} }
export function byRating(field, { direction = 'asc', defaultValue = 0 }) {
return {
compareFn: (a, b) => {
const fieldA = a[field] || defaultValue
const fieldB = b[field] || defaultValue
const result = fieldA > fieldB
return direction === 'asc' ? result : result * -1
},
groupKeyFn: (item) => {
const fieldValue = item[field] || defaultValue
return Math.floor(fieldValue / 10)
}
}
}
export function byYear(field, { direction = 'asc', defaultValue = '0000' }) { export function byYear(field, { direction = 'asc', defaultValue = '0000' }) {
return { return {
compareFn: (a, b) => { compareFn: (a, b) => {

View File

@ -270,6 +270,7 @@
"sort-by": { "sort-by": {
"title": "Sortieren nach", "title": "Sortieren nach",
"name": "Name", "name": "Name",
"rating": "Bewertung",
"release-date": "Erscheinungsdatum" "release-date": "Erscheinungsdatum"
} }
}, },
@ -333,7 +334,12 @@
"composer": { "composer": {
"album-count": "{count} Alben", "album-count": "{count} Alben",
"shuffle": "Zufallswiedergabe", "shuffle": "Zufallswiedergabe",
"track-count": "{count} Tracks" "track-count": "{count} Tracks",
"sort-by": {
"title": "Sortieren nach",
"name": "Name",
"rating": "Bewertung"
}
}, },
"composers": { "composers": {
"count": "{count} Komponisten", "count": "{count} Komponisten",
@ -346,7 +352,12 @@
"genre": { "genre": {
"album-count": "{count} Alben", "album-count": "{count} Alben",
"shuffle": "Zufallswiedergabe", "shuffle": "Zufallswiedergabe",
"track-count": "{count} Tracks" "track-count": "{count} Tracks",
"sort-by": {
"title": "Sortieren nach",
"name": "Name",
"rating": "Bewertung"
}
}, },
"genres": { "genres": {
"count": "{count} Genres", "count": "{count} Genres",
@ -396,7 +407,6 @@
"artists": "Künstler", "artists": "Künstler",
"audiobooks": "Hörbücher", "audiobooks": "Hörbücher",
"composers": "Komponisten", "composers": "Komponisten",
"help": "Tip: Du kannst mit einer Smart-Playlist-Abfrage-Sprache nach <a href=\"https://owntone.github.io/owntone-server/smart-playlists/\" target=\"_blank\">Ausdrücken</a> suchen wenn Du dem Ausdruck ein <code>query:</code> voranstellst.", "help": "Tip: Du kannst mit einer Smart-Playlist-Abfrage-Sprache nach <a href=\"https://owntone.github.io/owntone-server/smart-playlists/\" target=\"_blank\">Ausdrücken</a> suchen wenn Du dem Ausdruck ein <code>query:</code> voranstellst.",
"no-albums": "Keine Alben gefunden", "no-albums": "Keine Alben gefunden",
"no-artists": "Keine Künstler gefunden", "no-artists": "Keine Künstler gefunden",
@ -546,5 +556,10 @@
"today": "Heute", "today": "Heute",
"last-week": "Letzte Woche", "last-week": "Letzte Woche",
"last-month": "Letzer Monat" "last-month": "Letzer Monat"
},
"filter": {
"mono": "Mono",
"stereo": "Stereo",
"channels": "{count} Kanäle"
} }
} }

View File

@ -270,6 +270,7 @@
"sort-by": { "sort-by": {
"title": "Sort by", "title": "Sort by",
"name": "Name", "name": "Name",
"rating": "Rating",
"release-date": "Release date" "release-date": "Release date"
} }
}, },
@ -333,7 +334,12 @@
"composer": { "composer": {
"album-count": "{count} albums", "album-count": "{count} albums",
"shuffle": "Shuffle", "shuffle": "Shuffle",
"track-count": "{count} tracks" "track-count": "{count} tracks",
"sort-by": {
"title": "Sort by",
"name": "Name",
"rating": "Rating"
}
}, },
"composers": { "composers": {
"count": "{count} composers", "count": "{count} composers",
@ -346,7 +352,12 @@
"genre": { "genre": {
"album-count": "{count} albums", "album-count": "{count} albums",
"shuffle": "Shuffle", "shuffle": "Shuffle",
"track-count": "{count} tracks" "track-count": "{count} tracks",
"sort-by": {
"title": "Sort by",
"name": "Name",
"rating": "Rating"
}
}, },
"genres": { "genres": {
"count": "{count} genres", "count": "{count} genres",

View File

@ -41,7 +41,7 @@
"type": "Type", "type": "Type",
"year": "Année" "year": "Année"
}, },
"artiste": { "artist": {
"add-next": "Ajouter ensuite", "add-next": "Ajouter ensuite",
"add": "Ajouter", "add": "Ajouter",
"added-on": "Ajouté le", "added-on": "Ajouté le",
@ -270,6 +270,7 @@
"sort-by": { "sort-by": {
"title": "Trier par", "title": "Trier par",
"name": "Nom", "name": "Nom",
"rating": "Classement",
"release-date": "Date de sortie" "release-date": "Date de sortie"
} }
}, },
@ -333,7 +334,12 @@
"composer": { "composer": {
"album-count": "{count} albums", "album-count": "{count} albums",
"shuffle": "Lecture aléatoire", "shuffle": "Lecture aléatoire",
"track-count": "{count} pistes" "track-count": "{count} pistes",
"sort-by": {
"title": "Trier par",
"name": "Nom",
"rating": "Classement"
}
}, },
"composers": { "composers": {
"count": "{count} compositeurs", "count": "{count} compositeurs",
@ -346,7 +352,12 @@
"genre": { "genre": {
"album-count": "{count} albums", "album-count": "{count} albums",
"shuffle": "Lecture aléatoire", "shuffle": "Lecture aléatoire",
"track-count": "{count} pistes" "track-count": "{count} pistes",
"sort-by": {
"title": "Trier par",
"name": "Nom",
"rating": "Classement"
}
}, },
"genres": { "genres": {
"count": "{count} genres", "count": "{count} genres",

View File

@ -49,6 +49,7 @@ import ListTracks from '@/components/ListTracks.vue'
import ModalDialogAlbum from '@/components/ModalDialogAlbum.vue' import ModalDialogAlbum from '@/components/ModalDialogAlbum.vue'
import CoverArtwork from '@/components/CoverArtwork.vue' import CoverArtwork from '@/components/CoverArtwork.vue'
import webapi from '@/webapi' import webapi from '@/webapi'
import { GroupByList } from '@/lib/GroupByList'
const dataObject = { const dataObject = {
load: function (to) { load: function (to) {
@ -60,7 +61,7 @@ const dataObject = {
set: function (vm, response) { set: function (vm, response) {
vm.album = response[0].data vm.album = response[0].data
vm.tracks = response[1].data.items vm.tracks = new GroupByList(response[1].data)
} }
} }
@ -84,8 +85,7 @@ export default {
data() { data() {
return { return {
album: {}, album: {},
tracks: [], tracks: new GroupByList(),
show_album_details_modal: false show_album_details_modal: false
} }
}, },

View File

@ -2,7 +2,20 @@
<div> <div>
<content-with-heading> <content-with-heading>
<template #options> <template #options>
<index-button-list :index="index_list" /> <index-button-list :index="tracks.indexList" />
<div class="columns">
<div class="column">
<p
class="heading"
style="margin-bottom: 24px"
v-text="$t('page.artist.sort-by.title')"
/>
<dropdown-menu
v-model="selected_groupby_option_id"
:options="groupby_options"
/>
</div>
</div>
</template> </template>
<template #heading-left> <template #heading-left>
<p class="title is-4" v-text="artist.name" /> <p class="title is-4" v-text="artist.name" />
@ -39,7 +52,7 @@
" "
/> />
</p> </p>
<list-tracks :tracks="tracks.items" :uris="track_uris" /> <list-tracks :tracks="tracks" :uris="track_uris" />
<modal-dialog-artist <modal-dialog-artist
:show="show_artist_details_modal" :show="show_artist_details_modal"
:artist="artist" :artist="artist"
@ -52,10 +65,13 @@
<script> <script>
import ContentWithHeading from '@/templates/ContentWithHeading.vue' import ContentWithHeading from '@/templates/ContentWithHeading.vue'
import DropdownMenu from '@/components/DropdownMenu.vue'
import IndexButtonList from '@/components/IndexButtonList.vue' import IndexButtonList from '@/components/IndexButtonList.vue'
import ListTracks from '@/components/ListTracks.vue' import ListTracks from '@/components/ListTracks.vue'
import ModalDialogArtist from '@/components/ModalDialogArtist.vue' import ModalDialogArtist from '@/components/ModalDialogArtist.vue'
import webapi from '@/webapi' import webapi from '@/webapi'
import * as types from '@/store/mutation_types'
import { byName, byRating, GroupByList } from '@/lib/GroupByList'
const dataObject = { const dataObject = {
load: function (to) { load: function (to) {
@ -67,7 +83,7 @@ const dataObject = {
set: function (vm, response) { set: function (vm, response) {
vm.artist = response[0].data vm.artist = response[0].data
vm.tracks = response[1].data.tracks vm.tracks_list = new GroupByList(response[1].data.tracks)
} }
} }
@ -75,8 +91,9 @@ export default {
name: 'PageArtistTracks', name: 'PageArtistTracks',
components: { components: {
ContentWithHeading, ContentWithHeading,
ListTracks, DropdownMenu,
IndexButtonList, IndexButtonList,
ListTracks,
ModalDialogArtist ModalDialogArtist
}, },
@ -96,25 +113,43 @@ export default {
data() { data() {
return { return {
artist: {}, artist: {},
tracks: { items: [] }, groupby_options: [
{
show_artist_details_modal: false id: 1,
name: this.$t('page.artist.sort-by.name'),
options: byName('title_sort')
},
{
id: 2,
name: this.$t('page.artist.sort-by.rating'),
options: byRating('rating', {
direction: 'desc'
})
}
],
show_artist_details_modal: false,
tracks_list: new GroupByList()
} }
}, },
computed: { computed: {
index_list() { selected_groupby_option_id: {
return [ get() {
...new Set( return this.$store.state.artist_tracks_sort
this.tracks.items.map((track) => },
track.title_sort.charAt(0).toUpperCase() set(value) {
) this.$store.commit(types.ARTIST_TRACKS_SORT, value)
) }
] },
tracks() {
const groupBy = this.groupby_options.find(
(o) => o.id === this.selected_groupby_option_id
)
this.tracks_list.group(groupBy.options)
return this.tracks_list
}, },
track_uris() { track_uris() {
return this.tracks.items.map((a) => a.uri).join(',') return this.tracks_list.items.map((a) => a.uri).join(',')
} }
}, },
@ -125,10 +160,7 @@ export default {
}, },
play: function () { play: function () {
webapi.player_play_uri( webapi.player_play_uri(this.tracks_list.map((a) => a.uri).join(','), true)
this.tracks.items.map((a) => a.uri).join(','),
true
)
} }
} }
} }

View File

@ -52,6 +52,7 @@ import ListTracks from '@/components/ListTracks.vue'
import ModalDialogAlbum from '@/components/ModalDialogAlbum.vue' import ModalDialogAlbum from '@/components/ModalDialogAlbum.vue'
import CoverArtwork from '@/components/CoverArtwork.vue' import CoverArtwork from '@/components/CoverArtwork.vue'
import webapi from '@/webapi' import webapi from '@/webapi'
import { GroupByList } from '@/lib/GroupByList'
const dataObject = { const dataObject = {
load: function (to) { load: function (to) {
@ -63,7 +64,7 @@ const dataObject = {
set: function (vm, response) { set: function (vm, response) {
vm.album = response[0].data vm.album = response[0].data
vm.tracks = response[1].data.items vm.tracks = new GroupByList(response[1].data)
} }
} }
@ -87,8 +88,7 @@ export default {
data() { data() {
return { return {
album: {}, album: {},
tracks: [], tracks: new GroupByList(),
show_album_details_modal: false show_album_details_modal: false
} }
}, },

View File

@ -32,7 +32,7 @@
<p class="heading" v-text="$t('page.browse.tracks')" /> <p class="heading" v-text="$t('page.browse.tracks')" />
</template> </template>
<template #content> <template #content>
<list-tracks :tracks="recently_played.items" /> <list-tracks :tracks="recently_played" />
</template> </template>
<template #footer> <template #footer>
<nav class="level"> <nav class="level">
@ -77,7 +77,7 @@ const dataObject = {
set: function (vm, response) { set: function (vm, response) {
vm.recently_added = new GroupByList(response[0].data.albums) vm.recently_added = new GroupByList(response[0].data.albums)
vm.recently_played = response[1].data.tracks vm.recently_played = new GroupByList(response[1].data.tracks)
} }
} }

View File

@ -10,7 +10,7 @@
<p class="heading" v-text="$t('page.browse.recently-played.tracks')" /> <p class="heading" v-text="$t('page.browse.recently-played.tracks')" />
</template> </template>
<template #content> <template #content>
<list-tracks :tracks="recently_played.items" /> <list-tracks :tracks="recently_played" />
</template> </template>
</content-with-heading> </content-with-heading>
</div> </div>
@ -21,6 +21,7 @@ import ContentWithHeading from '@/templates/ContentWithHeading.vue'
import TabsMusic from '@/components/TabsMusic.vue' import TabsMusic from '@/components/TabsMusic.vue'
import ListTracks from '@/components/ListTracks.vue' import ListTracks from '@/components/ListTracks.vue'
import webapi from '@/webapi' import webapi from '@/webapi'
import { GroupByList } from '@/lib/GroupByList'
const dataObject = { const dataObject = {
load: function (to) { load: function (to) {
@ -33,7 +34,7 @@ const dataObject = {
}, },
set: function (vm, response) { set: function (vm, response) {
vm.recently_played = response.data.tracks vm.recently_played = new GroupByList(response.data.tracks)
} }
} }

View File

@ -1,6 +1,22 @@
<template> <template>
<div> <div>
<content-with-heading> <content-with-heading>
<template #options>
<index-button-list :index="tracks.indexList" />
<div class="columns">
<div class="column">
<p
class="heading"
style="margin-bottom: 24px"
v-text="$t('page.artist.sort-by.title')"
/>
<dropdown-menu
v-model="selected_groupby_option_id"
:options="groupby_options"
/>
</div>
</div>
</template>
<template #heading-left> <template #heading-left>
<p class="title is-4" v-text="composer.name" /> <p class="title is-4" v-text="composer.name" />
</template> </template>
@ -38,7 +54,7 @@
" "
/> />
</p> </p>
<list-tracks :tracks="tracks.items" :expression="play_expression" /> <list-tracks :tracks="tracks" :expression="expression" />
<modal-dialog-composer <modal-dialog-composer
:show="show_composer_details_modal" :show="show_composer_details_modal"
:composer="composer" :composer="composer"
@ -51,9 +67,13 @@
<script> <script>
import ContentWithHeading from '@/templates/ContentWithHeading.vue' import ContentWithHeading from '@/templates/ContentWithHeading.vue'
import DropdownMenu from '@/components/DropdownMenu.vue'
import IndexButtonList from '@/components/IndexButtonList.vue'
import ListTracks from '@/components/ListTracks.vue' import ListTracks from '@/components/ListTracks.vue'
import ModalDialogComposer from '@/components/ModalDialogComposer.vue' import ModalDialogComposer from '@/components/ModalDialogComposer.vue'
import webapi from '@/webapi' import webapi from '@/webapi'
import * as types from '@/store/mutation_types'
import { byName, byRating, GroupByList } from '@/lib/GroupByList'
const dataObject = { const dataObject = {
load: function (to) { load: function (to) {
@ -65,7 +85,7 @@ const dataObject = {
set: function (vm, response) { set: function (vm, response) {
vm.composer = response[0].data vm.composer = response[0].data
vm.tracks = response[1].data.tracks vm.tracks_list = new GroupByList(response[1].data.tracks)
} }
} }
@ -73,6 +93,8 @@ export default {
name: 'PageComposerTracks', name: 'PageComposerTracks',
components: { components: {
ContentWithHeading, ContentWithHeading,
DropdownMenu,
IndexButtonList,
ListTracks, ListTracks,
ModalDialogComposer ModalDialogComposer
}, },
@ -92,16 +114,44 @@ export default {
data() { data() {
return { return {
tracks: { items: [] }, groupby_options: [
{
id: 1,
name: this.$t('page.composer.sort-by.name'),
options: byName('title_sort')
},
{
id: 2,
name: this.$t('page.composer.sort-by.rating'),
options: byRating('rating', {
direction: 'desc'
})
}
],
composer: {}, composer: {},
show_composer_details_modal: false,
show_composer_details_modal: false tracks_list: new GroupByList()
} }
}, },
computed: { computed: {
play_expression() { expression() {
return 'composer is "' + this.composer.name + '" and media_kind is music' return 'composer is "' + this.composer.name + '" and media_kind is music'
},
selected_groupby_option_id: {
get() {
return this.$store.state.composer_tracks_sort
},
set(value) {
this.$store.commit(types.COMPOSER_TRACKS_SORT, value)
}
},
tracks() {
const groupBy = this.groupby_options.find(
(o) => o.id === this.selected_groupby_option_id
)
this.tracks_list.group(groupBy.options)
return this.tracks_list
} }
}, },
@ -115,7 +165,7 @@ export default {
}, },
play: function () { play: function () {
webapi.player_play_expression(this.play_expression, true) webapi.player_play_expression(this.expression, true)
} }
} }
} }

View File

@ -2,7 +2,20 @@
<div> <div>
<content-with-heading> <content-with-heading>
<template #options> <template #options>
<index-button-list :index="index_list" /> <index-button-list :index="tracks.indexList" />
<div class="columns">
<div class="column">
<p
class="heading"
style="margin-bottom: 24px"
v-text="$t('page.genre.sort-by.title')"
/>
<dropdown-menu
v-model="selected_groupby_option_id"
:options="groupby_options"
/>
</div>
</div>
</template> </template>
<template #heading-left> <template #heading-left>
<p class="title is-4" v-text="genre.name" /> <p class="title is-4" v-text="genre.name" />
@ -35,7 +48,7 @@
v-text="$t('page.genre.track-count', { count: genre.track_count })" v-text="$t('page.genre.track-count', { count: genre.track_count })"
/> />
</p> </p>
<list-tracks :tracks="tracks.items" :expression="expression" /> <list-tracks :tracks="tracks" :expression="expression" />
<modal-dialog-genre <modal-dialog-genre
:show="show_genre_details_modal" :show="show_genre_details_modal"
:genre="genre" :genre="genre"
@ -48,10 +61,13 @@
<script> <script>
import ContentWithHeading from '@/templates/ContentWithHeading.vue' import ContentWithHeading from '@/templates/ContentWithHeading.vue'
import DropdownMenu from '@/components/DropdownMenu.vue'
import IndexButtonList from '@/components/IndexButtonList.vue' import IndexButtonList from '@/components/IndexButtonList.vue'
import ListTracks from '@/components/ListTracks.vue' import ListTracks from '@/components/ListTracks.vue'
import ModalDialogGenre from '@/components/ModalDialogGenre.vue' import ModalDialogGenre from '@/components/ModalDialogGenre.vue'
import webapi from '@/webapi' import webapi from '@/webapi'
import * as types from '@/store/mutation_types'
import { byName, byRating, GroupByList } from '@/lib/GroupByList'
const dataObject = { const dataObject = {
load: function (to) { load: function (to) {
@ -63,7 +79,7 @@ const dataObject = {
set: function (vm, response) { set: function (vm, response) {
vm.genre = response[0].data vm.genre = response[0].data
vm.tracks = response[1].data.tracks vm.tracks_list = new GroupByList(response[1].data.tracks)
} }
} }
@ -71,8 +87,9 @@ export default {
name: 'PageGenreTracks', name: 'PageGenreTracks',
components: { components: {
ContentWithHeading, ContentWithHeading,
ListTracks, DropdownMenu,
IndexButtonList, IndexButtonList,
ListTracks,
ModalDialogGenre ModalDialogGenre
}, },
@ -91,26 +108,44 @@ export default {
data() { data() {
return { return {
tracks: { items: [] }, genre: {},
genre: '', groupby_options: [
{
show_genre_details_modal: false id: 1,
name: this.$t('page.genre.sort-by.name'),
options: byName('title_sort')
},
{
id: 2,
name: this.$t('page.genre.sort-by.rating'),
options: byRating('rating', {
direction: 'desc'
})
}
],
show_genre_details_modal: false,
tracks_list: new GroupByList()
} }
}, },
computed: { computed: {
index_list() {
return [
...new Set(
this.tracks.items.map((track) =>
track.title_sort.charAt(0).toUpperCase()
)
)
]
},
expression() { expression() {
return 'genre is "' + this.genre.name + '" and media_kind is music' return 'genre is "' + this.genre.name + '" and media_kind is music'
},
selected_groupby_option_id: {
get() {
return this.$store.state.genre_tracks_sort
},
set(value) {
this.$store.commit(types.GENRE_TRACKS_SORT, value)
}
},
tracks() {
const groupBy = this.groupby_options.find(
(o) => o.id === this.selected_groupby_option_id
)
this.tracks_list.group(groupBy.options)
return this.tracks_list
} }
}, },

View File

@ -20,7 +20,7 @@
<template #content> <template #content>
<p <p
class="heading has-text-centered-mobile" class="heading has-text-centered-mobile"
v-text="$t('page.playlist.length', { length: tracks.length })" v-text="$t('page.playlist.count', { count: tracks.count })"
/> />
<list-tracks :tracks="tracks" :uris="uris" /> <list-tracks :tracks="tracks" :uris="uris" />
<modal-dialog-playlist <modal-dialog-playlist
@ -38,6 +38,7 @@ import ContentWithHeading from '@/templates/ContentWithHeading.vue'
import ListTracks from '@/components/ListTracks.vue' import ListTracks from '@/components/ListTracks.vue'
import ModalDialogPlaylist from '@/components/ModalDialogPlaylist.vue' import ModalDialogPlaylist from '@/components/ModalDialogPlaylist.vue'
import webapi from '@/webapi' import webapi from '@/webapi'
import { GroupByList } from '@/lib/GroupByList'
const dataObject = { const dataObject = {
load: function (to) { load: function (to) {
@ -49,7 +50,7 @@ const dataObject = {
set: function (vm, response) { set: function (vm, response) {
vm.playlist = response[0].data vm.playlist = response[0].data
vm.tracks = response[1].data.items vm.tracks = new GroupByList(response[1].data)
} }
} }
@ -73,8 +74,7 @@ export default {
data() { data() {
return { return {
playlist: {}, playlist: {},
tracks: [], tracks: new GroupByList(),
show_playlist_details_modal: false show_playlist_details_modal: false
} }
}, },

View File

@ -61,6 +61,7 @@ import ListTracks from '@/components/ListTracks.vue'
import ModalDialogAlbum from '@/components/ModalDialogAlbum.vue' import ModalDialogAlbum from '@/components/ModalDialogAlbum.vue'
import ModalDialog from '@/components/ModalDialog.vue' import ModalDialog from '@/components/ModalDialog.vue'
import webapi from '@/webapi' import webapi from '@/webapi'
import { GroupByList } from '@/lib/GroupByList'
const dataObject = { const dataObject = {
load: function (to) { load: function (to) {
@ -72,7 +73,7 @@ const dataObject = {
set: function (vm, response) { set: function (vm, response) {
vm.album = response[0].data vm.album = response[0].data
vm.tracks = response[1].data.tracks.items vm.tracks = new GroupByList(response[1].data.tracks)
} }
} }
@ -101,10 +102,8 @@ export default {
data() { data() {
return { return {
album: {}, album: {},
tracks: [], tracks: new GroupByList(),
show_album_details_modal: false, show_album_details_modal: false,
show_remove_podcast_modal: false, show_remove_podcast_modal: false,
rss_playlist_to_remove: {} rss_playlist_to_remove: {}
} }
@ -112,7 +111,7 @@ export default {
computed: { computed: {
new_tracks() { new_tracks() {
return this.tracks.filter((track) => track.play_count === 0).length return this.tracks.items.filter((track) => track.play_count === 0).length
} }
}, },
@ -149,7 +148,7 @@ export default {
reload_tracks: function () { reload_tracks: function () {
webapi.library_podcast_episodes(this.album.id).then(({ data }) => { webapi.library_podcast_episodes(this.album.id).then(({ data }) => {
this.tracks = data.tracks.items this.tracks = new GroupByList(data.tracks)
}) })
} }
} }

View File

@ -14,7 +14,7 @@
</template> </template>
<template #content> <template #content>
<list-tracks <list-tracks
:tracks="new_episodes.items" :tracks="new_episodes"
:show_progress="true" :show_progress="true"
@play-count-changed="reload_new_episodes" @play-count-changed="reload_new_episodes"
/> />
@ -75,7 +75,7 @@ const dataObject = {
set: function (vm, response) { set: function (vm, response) {
vm.albums = new GroupByList(response[0].data) vm.albums = new GroupByList(response[0].data)
vm.new_episodes = response[1].data.tracks vm.new_episodes = new GroupByList(response[1].data.tracks)
} }
} }
@ -131,7 +131,7 @@ export default {
reload_new_episodes: function () { reload_new_episodes: function () {
webapi.library_podcasts_new_episodes().then(({ data }) => { webapi.library_podcasts_new_episodes().then(({ data }) => {
this.new_episodes = data.tracks this.new_episodes = new GroupByList(data.tracks)
}) })
}, },

View File

@ -9,7 +9,7 @@
class="heading has-text-centered-mobile" class="heading has-text-centered-mobile"
v-text="$t('page.radio.count', { count: tracks.total })" v-text="$t('page.radio.count', { count: tracks.total })"
/> />
<list-tracks :tracks="tracks.items" /> <list-tracks :tracks="tracks" />
</template> </template>
</content-with-heading> </content-with-heading>
</div> </div>
@ -19,6 +19,7 @@
import ContentWithHeading from '@/templates/ContentWithHeading.vue' import ContentWithHeading from '@/templates/ContentWithHeading.vue'
import ListTracks from '@/components/ListTracks.vue' import ListTracks from '@/components/ListTracks.vue'
import webapi from '@/webapi' import webapi from '@/webapi'
import { GroupByList } from '../lib/GroupByList'
const dataObject = { const dataObject = {
load: function (to) { load: function (to) {
@ -26,7 +27,7 @@ const dataObject = {
}, },
set: function (vm, response) { set: function (vm, response) {
vm.tracks = response.data.tracks vm.tracks = new GroupByList(response.data.tracks)
} }
} }
@ -49,7 +50,7 @@ export default {
data() { data() {
return { return {
tracks: { items: [] } tracks: new GroupByList()
} }
} }
} }

View File

@ -45,7 +45,7 @@
<p class="title is-4" v-text="$t('page.search.tracks')" /> <p class="title is-4" v-text="$t('page.search.tracks')" />
</template> </template>
<template #content> <template #content>
<list-tracks :tracks="tracks.items" /> <list-tracks :tracks="tracks" />
</template> </template>
<template #footer> <template #footer>
<nav v-if="show_all_tracks_button" class="level"> <nav v-if="show_all_tracks_button" class="level">
@ -275,7 +275,7 @@ export default {
data() { data() {
return { return {
search_query: '', search_query: '',
tracks: { items: [], total: 0 }, tracks: new GroupByList(),
artists: new GroupByList(), artists: new GroupByList(),
albums: new GroupByList(), albums: new GroupByList(),
composers: new GroupByList(), composers: new GroupByList(),
@ -407,7 +407,7 @@ export default {
} }
webapi.search(searchParams).then(({ data }) => { webapi.search(searchParams).then(({ data }) => {
this.tracks = data.tracks ? data.tracks : { items: [], total: 0 } this.tracks = new GroupByList(data.tracks)
this.artists = new GroupByList(data.artists) this.artists = new GroupByList(data.artists)
this.albums = new GroupByList(data.albums) this.albums = new GroupByList(data.albums)
this.composers = new GroupByList(data.composers) this.composers = new GroupByList(data.composers)

View File

@ -53,10 +53,13 @@ export default createStore({
search_path: '/search/library', search_path: '/search/library',
recent_searches: [], recent_searches: [],
composer_tracks_sort: 1,
genre_tracks_sort: 1,
hide_singles: false, hide_singles: false,
hide_spotify: false, hide_spotify: false,
artists_sort: 1, artists_sort: 1,
artist_albums_sort: 1, artist_albums_sort: 1,
artist_tracks_sort: 1,
albums_sort: 1, albums_sort: 1,
show_only_next_items: false, show_only_next_items: false,
show_burger_menu: false, show_burger_menu: false,
@ -234,6 +237,12 @@ export default createStore({
state.recent_searches.pop() state.recent_searches.pop()
} }
}, },
[types.COMPOSER_TRACKS_SORT](state, sort) {
state.composer_tracks_sort = sort
},
[types.GENRE_TRACKS_SORT](state, sort) {
state.genre_tracks_sort = sort
},
[types.HIDE_SINGLES](state, hideSingles) { [types.HIDE_SINGLES](state, hideSingles) {
state.hide_singles = hideSingles state.hide_singles = hideSingles
}, },
@ -246,6 +255,9 @@ export default createStore({
[types.ARTIST_ALBUMS_SORT](state, sort) { [types.ARTIST_ALBUMS_SORT](state, sort) {
state.artist_albums_sort = sort state.artist_albums_sort = sort
}, },
[types.ARTIST_TRACKS_SORT](state, sort) {
state.artist_tracks_sort = sort
},
[types.ALBUMS_SORT](state, sort) { [types.ALBUMS_SORT](state, sort) {
state.albums_sort = sort state.albums_sort = sort
}, },

View File

@ -21,10 +21,13 @@ export const DELETE_NOTIFICATION = 'DELETE_NOTIFICATION'
export const SEARCH_PATH = 'SEARCH_PATH' export const SEARCH_PATH = 'SEARCH_PATH'
export const ADD_RECENT_SEARCH = 'ADD_RECENT_SEARCH' export const ADD_RECENT_SEARCH = 'ADD_RECENT_SEARCH'
export const COMPOSER_TRACKS_SORT = 'COMPOSER_TRACKS_SORT'
export const GENRE_TRACKS_SORT = 'GENRE_TRACKS_SORT'
export const HIDE_SINGLES = 'HIDE_SINGLES' export const HIDE_SINGLES = 'HIDE_SINGLES'
export const HIDE_SPOTIFY = 'HIDE_SPOTIFY' export const HIDE_SPOTIFY = 'HIDE_SPOTIFY'
export const ARTISTS_SORT = 'ARTISTS_SORT' export const ARTISTS_SORT = 'ARTISTS_SORT'
export const ARTIST_ALBUMS_SORT = 'ARTIST_ALBUMS_SORT' export const ARTIST_ALBUMS_SORT = 'ARTIST_ALBUMS_SORT'
export const ARTIST_TRACKS_SORT = 'ARTIST_TRACKS_SORT'
export const ALBUMS_SORT = 'ALBUMS_SORT' export const ALBUMS_SORT = 'ALBUMS_SORT'
export const SHOW_ONLY_NEXT_ITEMS = 'SHOW_ONLY_NEXT_ITEMS' export const SHOW_ONLY_NEXT_ITEMS = 'SHOW_ONLY_NEXT_ITEMS'
export const SHOW_BURGER_MENU = 'SHOW_BURGER_MENU' export const SHOW_BURGER_MENU = 'SHOW_BURGER_MENU'