diff --git a/src/i18n/en_US.json b/src/i18n/en_US.json
index a4282abc..154d7120 100644
--- a/src/i18n/en_US.json
+++ b/src/i18n/en_US.json
@@ -90,6 +90,9 @@
"term.size": "Size",
"term.size.normal": "Normal",
"term.size.compact": "Compact",
+ "term.scroll": "Scroll Mode",
+ "term.scroll.infinite": "Infinite",
+ "term.scroll.paged": "${songsPerPage} per page",
"term.enable": "Enable",
"term.disable": "Disable",
"term.enabled": "Enabled",
diff --git a/src/main/base/store.ts b/src/main/base/store.ts
index a014ae45..27140c80 100644
--- a/src/main/base/store.ts
+++ b/src/main/base/store.ts
@@ -147,6 +147,7 @@ export class Store {
},
"libraryPrefs": {
"songs": {
+ "scroll": "infinite",
"sort": "name",
"sortOrder": "asc",
"size": "normal"
diff --git a/src/renderer/assets/angles-left.svg b/src/renderer/assets/angles-left.svg
new file mode 100644
index 00000000..cc38eb23
--- /dev/null
+++ b/src/renderer/assets/angles-left.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/renderer/assets/angles-right.svg b/src/renderer/assets/angles-right.svg
new file mode 100644
index 00000000..7bc040ed
--- /dev/null
+++ b/src/renderer/assets/angles-right.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/renderer/assets/chevron-right.svg b/src/renderer/assets/chevron-right.svg
new file mode 100644
index 00000000..538cc611
--- /dev/null
+++ b/src/renderer/assets/chevron-right.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/renderer/less/elements.less b/src/renderer/less/elements.less
index 9341a783..771bd0e4 100644
--- a/src/renderer/less/elements.less
+++ b/src/renderer/less/elements.less
@@ -162,6 +162,32 @@
align-self: center;
}
+.page-btn {
+ align-self: center;
+ height: 32px;
+}
+
+.page-btn img {
+ height: 100%;
+ align-self: center;
+}
+
+.md-ico-first {
+ content: url('./assets/angles-left.svg');
+}
+
+.md-ico-prev {
+ content: url('./assets/chevron-left.svg');
+}
+
+.md-ico-next {
+ content: url('./assets/chevron-right.svg');
+}
+
+.md-ico-last {
+ content: url('./assets/angles-right.svg');
+}
+
.reload-btn {
background: rgb(86 86 86 / 52%);
border-radius: 100%;
diff --git a/src/renderer/style.css b/src/renderer/style.css
index cbeee3c8..ea76326e 100644
--- a/src/renderer/style.css
+++ b/src/renderer/style.css
@@ -10652,6 +10652,26 @@ fieldset:disabled .btn {
margin-bottom: -1.5px;
align-self: center;
}
+.page-btn {
+ align-self: center;
+ height: 32px;
+}
+.page-btn img {
+ height: 100%;
+ align-self: center;
+}
+.md-ico-first {
+ content: url('./assets/angles-left.svg');
+}
+.md-ico-prev {
+ content: url('./assets/chevron-left.svg');
+}
+.md-ico-next {
+ content: url('./assets/chevron-right.svg');
+}
+.md-ico-last {
+ content: url('./assets/angles-right.svg');
+}
.reload-btn {
background: rgba(86, 86, 86, 0.52);
border-radius: 100%;
diff --git a/src/renderer/views/components/mediaitem-list-item.ejs b/src/renderer/views/components/mediaitem-list-item.ejs
index 56b0c409..8dcd4bc5 100644
--- a/src/renderer/views/components/mediaitem-list-item.ejs
+++ b/src/renderer/views/components/mediaitem-list-item.ejs
@@ -1,5 +1,5 @@
\ No newline at end of file
+
diff --git a/src/renderer/views/pages/library-songs.ejs b/src/renderer/views/pages/library-songs.ejs
index a96e1597..e4ef860c 100644
--- a/src/renderer/views/pages/library-songs.ejs
+++ b/src/renderer/views/pages/library-songs.ejs
@@ -1,5 +1,3 @@
-
-
@@ -75,21 +120,125 @@
template: '#cider-library-songs',
data: function () {
return {
+ // currentPage is oneIndexed
+ currentPage: 1,
library: this.$root.library,
mediaItemSize: "compact",
+ pageSize: 250,
prefs: this.$root.cfg.libraryPrefs.songs,
- app : this.$root
+ app: this.$root
}
},
mounted() {
+ document.querySelector("#app-content")
+ .addEventListener("scroll", this.handleScroll)
this.$root.getLibrarySongsFull()
},
- methods: {
- sayHello: function () {
- alert('Hello world!');
+ 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;
+ }
+ }
},
- play: function () {
+ '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: {
+ isInfinite: function () {
+ return this.prefs.scroll === "infinite"
+ },
+ currentSlice: function () {
+ if (this.isInfinite) {
+ 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: {
+ // Infinite Scrolling
+ handleScroll: function (event) {
+ if (this.isInfinite &&
+ 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 () {
function shuffleArray(array) {
for (var i = array.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
@@ -101,11 +250,11 @@
let query = this.app.library.songs.displayListing.map(item => new MusicKit.MediaItem(item));
if (!app.mk.queue.isEmpty)
- app.mk.queue.splice(0, app.mk.queue._itemIDs.length);
+ app.mk.queue.splice(0, app.mk.queue._itemIDs.length);
app.mk.stop().then(() => {
- if (app.mk.shuffleMode == 1) {
- shuffleArray(query)
- }
+ if (app.mk.shuffleMode == 1) {
+ shuffleArray(query)
+ }
app.mk.queue.append(query)
app.mk.changeToMediaAtIndex(0)
});