Paginate/infinite scroll for albums, playlists (#1234)
* Infinite scroll, pagination to album, playlists * move pagination below tracks * Make page size configurable * remove renderer
This commit is contained in:
parent
575ef649ef
commit
8967cf7647
8 changed files with 328 additions and 151 deletions
|
@ -360,6 +360,8 @@
|
||||||
"settings.prompt.general.keybindings.update.success": "Keybind updated successfully. Press OK to relaunch Cider",
|
"settings.prompt.general.keybindings.update.success": "Keybind updated successfully. Press OK to relaunch Cider",
|
||||||
"settings.option.general.themeUpdateNotification": "Automatically check for theme updates",
|
"settings.option.general.themeUpdateNotification": "Automatically check for theme updates",
|
||||||
"settings.option.general.showLovedTracksInline": "Show loved tracks inline",
|
"settings.option.general.showLovedTracksInline": "Show loved tracks inline",
|
||||||
|
"settings.option.general.pagination": "Items to show per page",
|
||||||
|
"settings.options.general.pagination.description": "This determines how many songs/albums to show initially for infinite scrolling, or how many songs/albums to show for a single page",
|
||||||
"settings.description.search": "Search",
|
"settings.description.search": "Search",
|
||||||
"settings.description.albums": "Library Albums",
|
"settings.description.albums": "Library Albums",
|
||||||
"settings.description.artists": "Library Artists",
|
"settings.description.artists": "Library Artists",
|
||||||
|
|
|
@ -125,6 +125,7 @@ export class BrowserWindow {
|
||||||
"components/hello-world",
|
"components/hello-world",
|
||||||
"components/inline-collection-list",
|
"components/inline-collection-list",
|
||||||
"components/settings-window",
|
"components/settings-window",
|
||||||
|
"components/pagination",
|
||||||
"components/settings-keybinds",
|
"components/settings-keybinds",
|
||||||
"components/settings-themes",
|
"components/settings-themes",
|
||||||
"components/settings-themes-github",
|
"components/settings-themes-github",
|
||||||
|
|
|
@ -153,11 +153,16 @@ export class Store {
|
||||||
"size": "normal"
|
"size": "normal"
|
||||||
},
|
},
|
||||||
"albums": {
|
"albums": {
|
||||||
|
"scroll": "infinite",
|
||||||
"sort": "name",
|
"sort": "name",
|
||||||
"sortOrder": "asc",
|
"sortOrder": "asc",
|
||||||
"viewAs": "covers"
|
"viewAs": "covers"
|
||||||
},
|
},
|
||||||
"localPaths": []
|
"playlists": {
|
||||||
|
"scroll": "infinite"
|
||||||
|
},
|
||||||
|
"localPaths": [],
|
||||||
|
"pageSize": 250
|
||||||
},
|
},
|
||||||
"audio": {
|
"audio": {
|
||||||
"volume": 1,
|
"volume": 1,
|
||||||
|
|
175
src/renderer/views/components/pagination.ejs
Normal file
175
src/renderer/views/components/pagination.ejs
Normal file
|
@ -0,0 +1,175 @@
|
||||||
|
<script type="text/x-template" id="pagination">
|
||||||
|
<div class="row" style="margin-bottom: 16px" v-if="!isInfinite">
|
||||||
|
<button
|
||||||
|
class="col md-btn page-btn"
|
||||||
|
:disabled="effectivePage === 1"
|
||||||
|
@click="goToPage(1)"
|
||||||
|
>
|
||||||
|
<img class="md-ico-first"/>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="col md-btn page-btn prev"
|
||||||
|
:disabled="effectivePage === 1"
|
||||||
|
@click="goToPrevious()"
|
||||||
|
>
|
||||||
|
<img class="md-ico-prev"/>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
:class="`col md-btn page-btn${ isCurrentPage(page) ? ' md-btn-primary': ''}`"
|
||||||
|
@click="goToPage(page)"
|
||||||
|
v-for="page in pagesToShow"
|
||||||
|
>{{ page }}</button>
|
||||||
|
<button
|
||||||
|
class="col md-btn page-btn next"
|
||||||
|
:disabled="effectivePage === numPages"
|
||||||
|
@click="goToNext()"
|
||||||
|
>
|
||||||
|
<img class="md-ico-next"/>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="col md-btn page-btn last"
|
||||||
|
:disabled="effectivePage === numPages"
|
||||||
|
@click="goToEnd()"
|
||||||
|
>
|
||||||
|
<img class="md-ico-last"/>
|
||||||
|
</button>
|
||||||
|
<div class="col page-btn" style="min-width: 12em;">
|
||||||
|
<input type="number" min="1" :max="numPages" :value="effectivePage" @change="changePage" />
|
||||||
|
<span>/ {{ numPages }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
Vue.component('pagination', {
|
||||||
|
template: "#pagination",
|
||||||
|
props: {
|
||||||
|
'length': { type: Number, required: true },
|
||||||
|
'pageSize': { type: Number, required: true },
|
||||||
|
'scroll': { type: String, required: true },
|
||||||
|
'scrollSelector': { type: String, required: true }
|
||||||
|
},
|
||||||
|
data: function () {
|
||||||
|
return { currentPage: 1 }
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
document.querySelector(this.scrollSelector)
|
||||||
|
.addEventListener("scroll", this.handleScroll)
|
||||||
|
},
|
||||||
|
destroyed() {
|
||||||
|
document.querySelector(this.scrollSelector)
|
||||||
|
.removeEventListener("scroll", this.handleScroll)
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
'length': function () {
|
||||||
|
if (this.isInfinite) {
|
||||||
|
// If a search reduces the number of things to show, we want to limit
|
||||||
|
// the number of songs shown as well. This is to prevent you scrolling
|
||||||
|
// to load your entire library, searching for one song, and then having
|
||||||
|
// th re-render the entire library
|
||||||
|
if (this.currentPage > this.numPages) {
|
||||||
|
this.currentPage = this.numPages;
|
||||||
|
this.$emit("onRangeChange", this.currentRange);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.$emit("onRangeChange", this.currentRange);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'scroll': function () {
|
||||||
|
// When changing modes, set the page to 1. This is primarily to
|
||||||
|
// prevent going to a high page (e.g., 50) and then switching to infinite
|
||||||
|
// and showing 12.5k songs
|
||||||
|
this.currentPage = 1;
|
||||||
|
this.$emit("onRangeChange", this.currentRange);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
isInfinite: function () {
|
||||||
|
return this.scroll === "infinite"
|
||||||
|
},
|
||||||
|
currentRange: function () {
|
||||||
|
if (this.isInfinite) {
|
||||||
|
return [0, this.currentPage * this.pageSize];
|
||||||
|
} else {
|
||||||
|
const startingPage = Math.min(this.numPages, this.currentPage);
|
||||||
|
|
||||||
|
return [
|
||||||
|
(startingPage - 1) * this.pageSize,
|
||||||
|
startingPage * this.pageSize
|
||||||
|
];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
effectivePage: function () {
|
||||||
|
return Math.min(this.currentPage, this.numPages)
|
||||||
|
},
|
||||||
|
numPages: function () {
|
||||||
|
return Math.ceil(this.length / this.pageSize) || 1;
|
||||||
|
},
|
||||||
|
pagesToShow: function () {
|
||||||
|
let start = this.currentPage - 2;
|
||||||
|
let end = this.currentPage + 2;
|
||||||
|
|
||||||
|
if (start < 1) {
|
||||||
|
end += (1 - start);
|
||||||
|
start = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const endDifference = end - this.numPages;
|
||||||
|
if (endDifference > 0) {
|
||||||
|
end = this.numPages;
|
||||||
|
start = Math.max(1, start - endDifference);
|
||||||
|
}
|
||||||
|
|
||||||
|
const array = [];
|
||||||
|
for (let idx = start; idx <= end; idx++) {
|
||||||
|
array.push(idx);
|
||||||
|
}
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// Infinite Scrolling
|
||||||
|
handleScroll: function (event) {
|
||||||
|
if (this.isInfinite &&
|
||||||
|
this.currentPage < this.numPages &&
|
||||||
|
event.target.scrollTop >= event.target.scrollHeight - event.target.clientHeight) {
|
||||||
|
this.currentPage += 1;
|
||||||
|
this.$emit("onRangeChange", this.currentRange);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// Pagination
|
||||||
|
isCurrentPage: function (idx) {
|
||||||
|
return idx === this.currentPage ||
|
||||||
|
(idx === this.numPages && this.currentPage > this.numPages);
|
||||||
|
},
|
||||||
|
changePage: function (event) {
|
||||||
|
const value = event.target.valueAsNumber;
|
||||||
|
|
||||||
|
if (!isNaN(value) && value >= 1 && value <= this.numPages) {
|
||||||
|
this.currentPage = value;
|
||||||
|
this.$emit("onRangeChange", this.currentRange);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
goToPage: function (page) {
|
||||||
|
this.currentPage = page;
|
||||||
|
this.$emit("onRangeChange", this.currentRange);
|
||||||
|
},
|
||||||
|
goToPrevious: function () {
|
||||||
|
if (this.currentPage > 1) {
|
||||||
|
this.currentPage -= 1;
|
||||||
|
this.$emit("onRangeChange", this.currentRange);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
goToNext: function () {
|
||||||
|
if (this.currentPage < this.numPages) {
|
||||||
|
this.currentPage += 1;
|
||||||
|
this.$emit("onRangeChange", this.currentRange);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
goToEnd: function () {
|
||||||
|
this.currentPage = this.numPages;
|
||||||
|
this.$emit("onRangeChange", this.currentRange);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
|
@ -1262,6 +1262,27 @@
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="md-option-line">
|
||||||
|
<div class="md-option-segment">
|
||||||
|
{{$root.getLz('settings.option.general.pagination')}}<br>
|
||||||
|
<small>
|
||||||
|
{{$root.getLz('settings.options.general.pagination.description')}}<br>
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
<div class="md-option-segment md-option-segment_auto">
|
||||||
|
<label>
|
||||||
|
<select class="md-select" style="width:180px;"
|
||||||
|
v-model.number="$root.cfg.libraryPrefs.pageSize" type="number">
|
||||||
|
<option value="50">50</option>
|
||||||
|
<option value="100">100</option>
|
||||||
|
<option value="250">250</option>
|
||||||
|
<option value="500">500</option>
|
||||||
|
<option value="1000">1000</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -107,6 +107,12 @@
|
||||||
<img :class="(!inLibrary) ? 'md-ico-add' : 'md-ico-remove'">
|
<img :class="(!inLibrary) ? 'md-ico-add' : 'md-ico-remove'">
|
||||||
{{app.getLz('term.confirm')}}
|
{{app.getLz('term.confirm')}}
|
||||||
</button>
|
</button>
|
||||||
|
<select v-if="shouldPaginate" class="md-select" v-model="prefs.scroll">
|
||||||
|
<optgroup :label="app.getLz('term.scroll')">
|
||||||
|
<option value="infinite">{{app.getLz('term.scroll.infinite')}}</option>
|
||||||
|
<option value="paged">{{app.getLz('term.scroll.paged').replace("${songsPerPage}", pageSize)}}</option>
|
||||||
|
</optgroup>
|
||||||
|
</select>
|
||||||
<div style="display: flex; float: right;">
|
<div style="display: flex; float: right;">
|
||||||
<button :style="{ 'background': '#' + hasHeroObject()?.textColor4 ?? '' }" :class="['search-btn', showSearch ? 'active' : '']" @click="toggleSearch()" :aria-label="showSearch ? app.getLz('term.hideSearch') : app.getLz('term.showSearch')">
|
<button :style="{ 'background': '#' + hasHeroObject()?.textColor4 ?? '' }" :class="['search-btn', showSearch ? 'active' : '']" @click="toggleSearch()" :aria-label="showSearch ? app.getLz('term.hideSearch') : app.getLz('term.showSearch')">
|
||||||
<svg-icon :style="{ 'width': '15px', 'background-color': '#' + hasHeroObject()?.bgColor ?? '' }" :url="showSearch ? './assets/search-alt.svg' : './assets/search.svg'">
|
<svg-icon :style="{ 'width': '15px', 'background-color': '#' + hasHeroObject()?.bgColor ?? '' }" :url="showSearch ? './assets/search-alt.svg' : './assets/search.svg'">
|
||||||
|
@ -161,6 +167,12 @@
|
||||||
<img :class="(!inLibrary) ? 'md-ico-add' : 'md-ico-remove'">
|
<img :class="(!inLibrary) ? 'md-ico-add' : 'md-ico-remove'">
|
||||||
{{app.getLz('term.confirm')}}
|
{{app.getLz('term.confirm')}}
|
||||||
</button>
|
</button>
|
||||||
|
<select v-if="shouldPaginate" class="md-select" v-model="prefs.scroll">
|
||||||
|
<optgroup :label="app.getLz('term.scroll')">
|
||||||
|
<option value="infinite">{{app.getLz('term.scroll.infinite')}}</option>
|
||||||
|
<option value="paged">{{app.getLz('term.scroll.paged').replace("${songsPerPage}", pageSize)}}</option>
|
||||||
|
</optgroup>
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-auto cider-flex-center">
|
<div class="col-auto cider-flex-center">
|
||||||
|
@ -172,7 +184,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="playlist-body scrollbody">
|
<div class="playlist-body scrollbody">
|
||||||
<b-tabs pills class="track-pills pilldim fancy-pills" align="center" content-class="mt-3" :nav-wrapper-class="navClass(data)">
|
<b-tabs pills class="track-pills pilldim fancy-pills" align="center" content-class="mt-3" :nav-wrapper-class="navClass(data)">
|
||||||
<b-tab :title="$root.getLz('term.tracks')">
|
<b-tab :title="$root.getLz('term.tracks')" id="songList">
|
||||||
<div @wheel="minClass(true)" @scroll="minClass(true)">
|
<div @wheel="minClass(true)" @scroll="minClass(true)">
|
||||||
<div class="">
|
<div class="">
|
||||||
<div style="width:100%" @click="minClass(true)">
|
<div style="width:100%" @click="minClass(true)">
|
||||||
|
@ -186,18 +198,27 @@
|
||||||
class="search-input"
|
class="search-input"
|
||||||
ref="search-bar">
|
ref="search-bar">
|
||||||
</div>
|
</div>
|
||||||
|
<pagination
|
||||||
|
v-if="shouldPaginate"
|
||||||
|
style="margin-top: 10px"
|
||||||
|
:length="hasNestedPlaylist ? nestedDisplayLength: displayListing.length"
|
||||||
|
:pageSize="pageSize"
|
||||||
|
:scroll="prefs.scroll"
|
||||||
|
scrollSelector="#songList"
|
||||||
|
@onRangeChange="onRangeChange"
|
||||||
|
/>
|
||||||
<draggable :options="{disabled: !editing}"
|
<draggable :options="{disabled: !editing}"
|
||||||
v-model="data.relationships.tracks.data" @start="drag=true"
|
v-model="data.relationships.tracks.data" @start="drag=true"
|
||||||
@end="drag=false;put()">
|
@end="drag=false;put()">
|
||||||
<template v-if="!hasNestedPlaylist">
|
<template v-if="!hasNestedPlaylist">
|
||||||
<mediaitem-list-item :item="item" :parent="getItemParent(data)" :index="index"
|
<mediaitem-list-item :item="item" :parent="getItemParent(data)" :index="index + start"
|
||||||
:showIndex="true"
|
:showIndex="true"
|
||||||
:showIndexPlaylist="(data.attributes.playParams?.kind ?? data.type ?? '').includes('playlist')"
|
:showIndexPlaylist="(data.attributes.playParams?.kind ?? data.type ?? '').includes('playlist')"
|
||||||
:context-ext="buildContextMenu()"
|
:context-ext="buildContextMenu()"
|
||||||
v-for="(item,index) in displayListing"></mediaitem-list-item>
|
v-for="(item,index) in currentSlice"></mediaitem-list-item>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<div v-for="disc in nestedPlaylist">
|
<div v-for="disc in nestedSlices">
|
||||||
<div class="playlist-time">{{($root.getLz("term.discNumber") ??
|
<div class="playlist-time">{{($root.getLz("term.discNumber") ??
|
||||||
"").replace("${discNumber}",disc.disc)}}
|
"").replace("${discNumber}",disc.disc)}}
|
||||||
</div>
|
</div>
|
||||||
|
@ -278,6 +299,7 @@
|
||||||
props: ["data"],
|
props: ["data"],
|
||||||
|
|
||||||
data: function () {
|
data: function () {
|
||||||
|
const pageSize = this.$root.cfg.libraryPrefs.pageSize;
|
||||||
return {
|
return {
|
||||||
editorialNotesExpanded: false,
|
editorialNotesExpanded: false,
|
||||||
drag: false,
|
drag: false,
|
||||||
|
@ -291,6 +313,7 @@
|
||||||
headerVisible: true,
|
headerVisible: true,
|
||||||
useArtistChip: false,
|
useArtistChip: false,
|
||||||
nestedPlaylist: [],
|
nestedPlaylist: [],
|
||||||
|
nestedDisplayLength: 0,
|
||||||
classes: [],
|
classes: [],
|
||||||
editing: false,
|
editing: false,
|
||||||
inPlaylist: false,
|
inPlaylist: false,
|
||||||
|
@ -298,6 +321,10 @@
|
||||||
displayListing: [],
|
displayListing: [],
|
||||||
hasNestedPlaylist: false,
|
hasNestedPlaylist: false,
|
||||||
showSearch: false,
|
showSearch: false,
|
||||||
|
pageSize: pageSize,
|
||||||
|
start: 0,
|
||||||
|
end: pageSize,
|
||||||
|
prefs: this.$root.cfg.libraryPrefs.playlists
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted: function () {
|
mounted: function () {
|
||||||
|
@ -346,7 +373,48 @@
|
||||||
deep: true
|
deep: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
currentSlice() {
|
||||||
|
return this.displayListing.slice(this.start, this.end);
|
||||||
|
},
|
||||||
|
nestedSlices() {
|
||||||
|
if (this.shouldPaginate) {
|
||||||
|
let songsSeen = 0;
|
||||||
|
const discs = [];
|
||||||
|
|
||||||
|
for (const disc of this.nestedPlaylist) {
|
||||||
|
songsSeen += disc.tracks.length;
|
||||||
|
|
||||||
|
if (songsSeen >= this.end) {
|
||||||
|
discs.push({
|
||||||
|
disc: disc.disc,
|
||||||
|
tracks: disc.tracks.slice(0, this.end + disc.tracks.length - songsSeen)
|
||||||
|
})
|
||||||
|
break;
|
||||||
|
} else if (songsSeen > this.start) {
|
||||||
|
discs.push({
|
||||||
|
disc: disc.disc,
|
||||||
|
tracks: disc.tracks.slice(this.start - songsSeen)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return discs;
|
||||||
|
} else {
|
||||||
|
return this.nestedPlaylist
|
||||||
|
}
|
||||||
|
},
|
||||||
|
shouldPaginate() {
|
||||||
|
const result = this.data.relationships.tracks.data.length > this.pageSize
|
||||||
|
console.log(result)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
onRangeChange(newRange) {
|
||||||
|
this.start = newRange[0];
|
||||||
|
this.end = newRange[1];
|
||||||
|
},
|
||||||
isAlbum() {
|
isAlbum() {
|
||||||
return (this.data.attributes?.playParams?.kind ?? this.data.type ?? '').includes('album')
|
return (this.data.attributes?.playParams?.kind ?? this.data.type ?? '').includes('album')
|
||||||
},
|
},
|
||||||
|
@ -372,6 +440,8 @@
|
||||||
},
|
},
|
||||||
generateNestedPlaylist(songlists) {
|
generateNestedPlaylist(songlists) {
|
||||||
this.nestedPlaylist = [];
|
this.nestedPlaylist = [];
|
||||||
|
this.nestedDisplayLength = songlists.length;
|
||||||
|
|
||||||
if (this.data?.type?.includes("album")) {
|
if (this.data?.type?.includes("album")) {
|
||||||
let discs = songlists.map(x => {
|
let discs = songlists.map(x => {
|
||||||
return x.attributes.discNumber
|
return x.attributes.discNumber
|
||||||
|
|
|
@ -46,17 +46,33 @@
|
||||||
</optgroup>
|
</optgroup>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<select class="md-select" v-model="prefs.scroll">
|
||||||
|
<optgroup :label="app.getLz('term.scroll')">
|
||||||
|
<option value="infinite">{{app.getLz('term.scroll.infinite')}}</option>
|
||||||
|
<option value="paged">{{app.getLz('term.scroll.paged').replace("${songsPerPage}", pageSize)}}</option>
|
||||||
|
</optgroup>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<pagination
|
||||||
|
:length="app.library.albums.displayListing.length
|
||||||
|
"
|
||||||
|
:pageSize="pageSize"
|
||||||
|
:scroll="prefs.scroll"
|
||||||
|
scrollSelector="#app-content"
|
||||||
|
@onRangeChange="onRangeChange"
|
||||||
|
/>
|
||||||
<div class="well">
|
<div class="well">
|
||||||
<div class="albums-square-container">
|
<div class="albums-square-container">
|
||||||
<div>
|
<div>
|
||||||
<mediaitem-square v-if="prefs.viewAs == 'covers'" :size="'300'" :item="item" v-for="item in library.albums.displayListing">
|
<mediaitem-square v-if="prefs.viewAs == 'covers'" :size="'300'" :item="item" v-for="item in currentSlice">
|
||||||
</mediaitem-square>
|
</mediaitem-square>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<mediaitem-list-item v-if="prefs.viewAs == 'list'" :show-duration="false" :show-meta-data="true" :show-library-status="false" :item="item" v-for="item in library.albums.displayListing">
|
<mediaitem-list-item v-if="prefs.viewAs == 'list'" :show-duration="false" :show-meta-data="true" :show-library-status="false" :item="item" v-for="item in currentSlice">
|
||||||
</mediaitem-list-item>
|
</mediaitem-list-item>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -65,21 +81,34 @@
|
||||||
Vue.component('cider-library-albums', {
|
Vue.component('cider-library-albums', {
|
||||||
template: '#cider-library-albums',
|
template: '#cider-library-albums',
|
||||||
data: function () {
|
data: function () {
|
||||||
|
const pageSize = this.$root.cfg.libraryPrefs.pageSize;
|
||||||
return {
|
return {
|
||||||
library: this.$root.library,
|
library: this.$root.library,
|
||||||
mediaItemSize: "compact",
|
mediaItemSize: "compact",
|
||||||
prefs: this.$root.cfg.libraryPrefs.albums,
|
prefs: this.$root.cfg.libraryPrefs.albums,
|
||||||
app : this.$root
|
app: this.$root,
|
||||||
|
pageSize: pageSize,
|
||||||
|
start: 0,
|
||||||
|
end: pageSize
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.$root.getLibraryAlbumsFull(null, 1);
|
this.$root.getLibraryAlbumsFull(null, 1);
|
||||||
this.$root.getAlbumSort();
|
this.$root.getAlbumSort();
|
||||||
this.$root.searchLibraryAlbums(1);
|
this.$root.searchLibraryAlbums(1);
|
||||||
this.$root.getLibrarySongsFull() ;
|
this.$root.getLibrarySongsFull();
|
||||||
this.$root.searchLibraryAlbums(1);
|
this.$root.searchLibraryAlbums(1);
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
currentSlice: function () {
|
||||||
|
return this.library.albums.displayListing.slice(this.start, this.end);
|
||||||
|
}
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
onRangeChange: function (newRange) {
|
||||||
|
this.start = newRange[0];
|
||||||
|
this.end = newRange[1];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
|
@ -64,45 +64,13 @@
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row" style="margin-bottom: 16px" v-if="!isInfinite">
|
<pagination
|
||||||
<button
|
:length="library.songs.displayListing.length"
|
||||||
class="col md-btn page-btn"
|
:pageSize="pageSize"
|
||||||
:disabled="currentPage === 1"
|
:scroll="prefs.scroll"
|
||||||
@click="goToPage(1)"
|
scrollSelector="#app-content"
|
||||||
>
|
@onRangeChange="onRangeChange"
|
||||||
<img class="md-ico-first"/>
|
/>
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="col md-btn page-btn prev"
|
|
||||||
:disabled="currentPage === 1"
|
|
||||||
@click="goToPrevious()"
|
|
||||||
>
|
|
||||||
<img class="md-ico-prev"/>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
:class="`col md-btn page-btn${ isCurrentPage(page) ? ' md-btn-primary': ''}`"
|
|
||||||
@click="goToPage(page)"
|
|
||||||
v-for="page in pagesToShow"
|
|
||||||
>{{ page }}</button>
|
|
||||||
<button
|
|
||||||
class="col md-btn page-btn next"
|
|
||||||
:disabled="currentPage === numPages"
|
|
||||||
@click="goToNext()"
|
|
||||||
>
|
|
||||||
<img class="md-ico-next"/>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="col md-btn page-btn last"
|
|
||||||
:disabled="currentPage === numPages"
|
|
||||||
@click="goToEnd()"
|
|
||||||
>
|
|
||||||
<img class="md-ico-last"/>
|
|
||||||
</button>
|
|
||||||
<div class="col page-btn" style="min-width: 12em;">
|
|
||||||
<input type="number" min="1" :max="numPages" :value="currentPage" @change="changePage" />
|
|
||||||
<span>/ {{ numPages }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="library.songs.downloadState == 3">Library contains no songs.</div>
|
<div v-if="library.songs.downloadState == 3">Library contains no songs.</div>
|
||||||
|
@ -119,125 +87,31 @@
|
||||||
Vue.component('cider-library-songs', {
|
Vue.component('cider-library-songs', {
|
||||||
template: '#cider-library-songs',
|
template: '#cider-library-songs',
|
||||||
data: function () {
|
data: function () {
|
||||||
|
const pageSize = this.$root.cfg.libraryPrefs.pageSize;
|
||||||
return {
|
return {
|
||||||
// currentPage is oneIndexed
|
// currentPage is oneIndexed
|
||||||
currentPage: 1,
|
|
||||||
library: this.$root.library,
|
library: this.$root.library,
|
||||||
mediaItemSize: "compact",
|
mediaItemSize: "compact",
|
||||||
pageSize: 250,
|
|
||||||
prefs: this.$root.cfg.libraryPrefs.songs,
|
prefs: this.$root.cfg.libraryPrefs.songs,
|
||||||
app: this.$root
|
app: this.$root,
|
||||||
|
pageSize: pageSize,
|
||||||
|
start: 0,
|
||||||
|
end: pageSize
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
document.querySelector("#app-content")
|
|
||||||
.addEventListener("scroll", this.handleScroll)
|
|
||||||
this.$root.getLibrarySongsFull()
|
this.$root.getLibrarySongsFull()
|
||||||
},
|
},
|
||||||
destroyed() {
|
|
||||||
document.querySelector("#app-content")
|
|
||||||
.removeEventListener("scroll", this.handleScroll)
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
'library.songs.displayListing.length': function () {
|
|
||||||
if (this.isInfinite) {
|
|
||||||
// If a search reduces the number of things to show, we want to limit
|
|
||||||
// the number of songs shown as well. This is to prevent you scrolling
|
|
||||||
// to load your entire library, searching for one song, and then having
|
|
||||||
// th re-render the entire library
|
|
||||||
if (this.currentPage > this.numPages) {
|
|
||||||
this.currentPage = this.numPages;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'prefs.scroll': function () {
|
|
||||||
// When changing modes, set the page to 1. This is primarily to
|
|
||||||
// prevent going to a high page (e.g., 50) and then switching to infinite
|
|
||||||
// and showing 12.5k songs
|
|
||||||
this.currentPage = 1;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
computed: {
|
||||||
isInfinite: function () {
|
|
||||||
return this.prefs.scroll === "infinite"
|
|
||||||
},
|
|
||||||
currentSlice: function () {
|
currentSlice: function () {
|
||||||
if (this.isInfinite) {
|
return this.library.songs.displayListing.slice(this.start, this.end);
|
||||||
return this.library.songs.displayListing.slice(
|
|
||||||
0, this.currentPage * this.pageSize
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
const startingPage = Math.min(this.numPages, this.currentPage);
|
|
||||||
|
|
||||||
return this.library.songs.displayListing.slice(
|
|
||||||
(startingPage - 1) * this.pageSize,
|
|
||||||
startingPage * this.pageSize
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
numPages: function () {
|
|
||||||
return Math.ceil(this.library.songs.displayListing.length / this.pageSize) || 1;
|
|
||||||
},
|
|
||||||
pagesToShow: function () {
|
|
||||||
let start = this.currentPage - 2;
|
|
||||||
let end = this.currentPage + 2;
|
|
||||||
|
|
||||||
if (start < 1) {
|
|
||||||
end += (1 - start);
|
|
||||||
start = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
const endDifference = end - this.numPages;
|
|
||||||
if (endDifference > 0) {
|
|
||||||
end = this.numPages;
|
|
||||||
start = Math.max(1, start - endDifference);
|
|
||||||
}
|
|
||||||
|
|
||||||
const array = [];
|
|
||||||
for (let idx = start; idx <= end; idx++) {
|
|
||||||
array.push(idx);
|
|
||||||
}
|
|
||||||
return array;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
// Infinite Scrolling
|
onRangeChange: function (newRange) {
|
||||||
handleScroll: function (event) {
|
this.start = newRange[0];
|
||||||
if (this.isInfinite &&
|
this.end = newRange[1];
|
||||||
this.currentPage < this.numPages &&
|
|
||||||
event.target.scrollTop >= event.target.scrollHeight - event.target.clientHeight) {
|
|
||||||
this.currentPage += 1;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
// Pagination
|
|
||||||
isCurrentPage: function (idx) {
|
|
||||||
return idx === this.currentPage ||
|
|
||||||
(idx === this.numPages && this.currentPage > this.numPages);
|
|
||||||
},
|
|
||||||
changePage: function (event) {
|
|
||||||
const value = event.target.valueAsNumber;
|
|
||||||
|
|
||||||
if (!isNaN(value) && value >= 1 && value <= this.numPages) {
|
|
||||||
this.currentPage = value;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
goToPage: function (page) {
|
|
||||||
this.currentPage = page;
|
|
||||||
},
|
|
||||||
goToPrevious: function () {
|
|
||||||
if (this.currentPage > 1) {
|
|
||||||
this.currentPage -= 1;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
goToNext: function () {
|
|
||||||
if (this.currentPage < this.numPages) {
|
|
||||||
this.currentPage += 1;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
goToEnd: function () {
|
|
||||||
this.currentPage = this.numPages;
|
|
||||||
},
|
|
||||||
// Miscellaneous
|
|
||||||
play: function () {
|
play: function () {
|
||||||
function shuffleArray(array) {
|
function shuffleArray(array) {
|
||||||
for (var i = array.length - 1; i > 0; i--) {
|
for (var i = array.length - 1; i > 0; i--) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue