Browse Source

refactor: improved the loading of (queue)songs on admin pages

Kristian Vos 5 years ago
parent
commit
b64520a29e

+ 22 - 19
backend/logic/actions/queueSongs.js

@@ -32,30 +32,24 @@ cache.sub('queue.update', songId => {
 let lib = {
 
 	/**
-	 * Gets all queuesongs
+	 * Returns the length of the queue songs list
 	 *
-	 * @param {Object} session - the session object automatically added by socket.io
-	 * @param {Function} cb - gets called with the result
+	 * @param session
+	 * @param cb
 	 */
-	index: hooks.adminRequired((session, cb) => {
+	length: hooks.adminRequired((session, cb) => {
 		async.waterfall([
 			(next) => {
-				db.models.queueSong.find({}, next);
+				db.models.queueSong.countDocuments({}, next);
 			}
-		], async (err, songs) => {
+		], async (err, count) => {
 			if (err) {
 				err = await utils.getError(err);
-				logger.error("QUEUE_INDEX", `Indexing queuesongs failed. "${err}"`);
-				return cb({status: 'failure', message: err});
-			} else {
-				module.exports.getSet(session, 1, result => {
-					logger.success("QUEUE_INDEX", `Indexing queuesongs successful.`);
-					return cb({
-						songs: result,
-						maxLength: songs.length
-					});
-				});
+				logger.error("QUEUE_SONGS_LENGTH", `Failed to get length from queue songs. "${err}"`);
+				return cb({'status': 'failure', 'message': err});
 			}
+			logger.success("QUEUE_SONGS_LENGTH", `Got length from queue songs successfully.`);
+			cb(count);
 		});
 	}),
 
@@ -67,9 +61,18 @@ let lib = {
 	 * @param cb
 	 */
 	getSet: hooks.adminRequired((session, set, cb) => {
-		db.models.queueSong.find({}).limit(50 * set).exec((err, songs) => {
-			if (err) throw err;
-			cb(songs.splice(Math.max(songs.length - 50, 0)));
+		async.waterfall([
+			(next) => {
+				db.models.queueSong.find({}).skip(15 * (set - 1)).limit(15).exec(next);
+			},
+		], async (err, songs) => {
+			if (err) {
+				err = await utils.getError(err);
+				logger.error("QUEUE_SONGS_GET_SET", `Failed to get set from queue songs. "${err}"`);
+				return cb({'status': 'failure', 'message': err});
+			}
+			logger.success("QUEUE_SONGS_GET_SET", `Got set from queue songs successfully.`);
+			cb(songs);
 		});
 	}),
 

+ 3 - 5
backend/logic/actions/songs.js

@@ -99,8 +99,8 @@ module.exports = {
 	getSet: hooks.adminRequired((session, set, cb) => {
 		async.waterfall([
 			(next) => {
-				db.models.song.find({}).limit(15 * set).exec(next);
-			}
+				db.models.song.find({}).skip(15 * (set - 1)).limit(15).exec(next);
+			},
 		], async (err, songs) => {
 			if (err) {
 				err = await utils.getError(err);
@@ -108,9 +108,7 @@ module.exports = {
 				return cb({'status': 'failure', 'message': err});
 			}
 			logger.success("SONGS_GET_SET", `Got set from songs successfully.`);
-			logger.stationIssue(songs.length, true);
-			logger.stationIssue(Math.max(songs.length - 15, 0), true);
-			cb(songs.splice(Math.max(songs.length - 15, 0)));
+			cb(songs);
 		});
 	}),
 

+ 51 - 29
frontend/components/Admin/QueueSongs.vue

@@ -1,13 +1,25 @@
 <template>
 	<div>
 		<metadata title="Admin | Queue songs" />
-		<div class="container">
+		<div class="container" v-scroll="handleScroll">
+			<p>
+				<span>Sets loaded: {{ position - 1 }} / {{ maxPosition }}</span>
+				<br />
+				<span>Loaded songs: {{ this.songs.length }}</span>
+			</p>
 			<input
 				v-model="searchQuery"
 				type="text"
 				class="input"
 				placeholder="Search for Songs"
 			/>
+			<button
+				v-if="!getSetsAutomatically"
+				class="button is-primary"
+				@click="loadSongsAutomatically()"
+			>
+				Load songs automatically
+			</button>
 			<br />
 			<br />
 			<table class="table is-striped">
@@ -79,24 +91,6 @@
 				</tbody>
 			</table>
 		</div>
-		<nav class="pagination">
-			<a
-				v-if="position > 1"
-				class="button"
-				href="#"
-				@click="getSet(position - 1)"
-			>
-				<i class="material-icons">navigate_before</i>
-			</a>
-			<a
-				v-if="maxPosition > position"
-				class="button"
-				href="#"
-				@click="getSet(position + 1)"
-			>
-				<i class="material-icons">navigate_next</i>
-			</a>
-		</nav>
 		<edit-song v-if="modals.editSong" />
 	</div>
 </template>
@@ -118,7 +112,9 @@ export default {
 			position: 1,
 			maxPosition: 1,
 			searchQuery: "",
-			songs: []
+			songs: [],
+			gettingSet: false,
+			getSetsAutomatically: false
 		};
 	},
 	computed: {
@@ -141,12 +137,6 @@ export default {
 	//   }
 	// },
 	methods: {
-		getSet(position) {
-			this.socket.emit("queueSongs.getSet", position, data => {
-				this.songs = data;
-				this.position = position;
-			});
-		},
 		edit(song, index) {
 			const newSong = {};
 			Object.keys(song).forEach(n => {
@@ -170,10 +160,42 @@ export default {
 				else Toast.methods.addToast(res.message, 4000);
 			});
 		},
+		getSet() {
+			if (this.gettingSet) return;
+			if (this.position > this.maxPosition) return;
+			this.gettingSet = true;
+			this.socket.emit("queueSongs.getSet", this.position, data => {
+				data.forEach(song => {
+					this.songs.push(song);
+				});
+				this.position += 1;
+				this.gettingSet = false;
+				if (
+					this.getSetsAutomatically &&
+					this.maxPosition > this.position - 1
+				)
+					setTimeout(() => {
+						this.getSet();
+					}, 500);
+			});
+		},
+		handleScroll() {
+			if (this.getSetsAutomatically) return false;
+			if (window.scrollY + 50 >= window.scrollMaxY) this.getSet();
+
+			return this.maxPosition === this.position;
+		},
+		loadSongsAutomatically() {
+			this.getSetsAutomatically = true;
+			this.getSet();
+		},
 		init() {
-			this.socket.emit("queueSongs.index", data => {
-				this.songs = data.songs;
-				this.maxPosition = Math.round(data.maxLength / 50);
+			if (this.songs.length > 0)
+				this.position = Math.ceil(this.songs.length / 15) + 1;
+
+			this.socket.emit("queueSongs.length", length => {
+				this.maxPosition = Math.ceil(length / 15);
+				this.getSet();
 			});
 			this.socket.emit("apis.joinAdminRoom", "queue", () => {});
 		},

+ 40 - 3
frontend/components/Admin/Songs.vue

@@ -1,13 +1,25 @@
 <template>
 	<div>
 		<metadata title="Admin | Songs" />
-		<div class="container">
+		<div class="container" v-scroll="handleScroll">
+			<p>
+				<span>Sets loaded: {{ position - 1 }} / {{ maxPosition }}</span>
+				<br />
+				<span>Loaded songs: {{ this.songs.length }}</span>
+			</p>
 			<input
 				v-model="searchQuery"
 				type="text"
 				class="input"
 				placeholder="Search for Songs"
 			/>
+			<button
+				v-if="!getSetsAutomatically"
+				class="button is-primary"
+				@click="loadSongsAutomatically()"
+			>
+				Load songs automatically
+			</button>
 			<br />
 			<br />
 			<table class="table is-striped">
@@ -107,7 +119,9 @@ export default {
 			editing: {
 				index: 0,
 				song: {}
-			}
+			},
+			gettingSet: false,
+			getSetsAutomatically: false
 		};
 	},
 	computed: {
@@ -144,15 +158,38 @@ export default {
 			});
 		},
 		getSet() {
+			if (this.gettingSet) return;
+			if (this.position > this.maxPosition) return;
+			this.gettingSet = true;
 			this.socket.emit("songs.getSet", this.position, data => {
 				data.forEach(song => {
 					this.addSong(song);
 				});
 				this.position += 1;
-				if (this.maxPosition > this.position - 1) this.getSet();
+				this.gettingSet = false;
+				if (
+					this.getSetsAutomatically &&
+					this.maxPosition > this.position - 1
+				)
+					setTimeout(() => {
+						this.getSet();
+					}, 500);
 			});
 		},
+		handleScroll() {
+			if (this.getSetsAutomatically) return false;
+			if (window.scrollY + 50 >= window.scrollMaxY) this.getSet();
+
+			return this.maxPosition === this.position;
+		},
+		loadSongsAutomatically() {
+			this.getSetsAutomatically = true;
+			this.getSet();
+		},
 		init() {
+			if (this.songs.length > 0)
+				this.position = Math.ceil(this.songs.length / 15) + 1;
+
 			this.socket.emit("songs.length", length => {
 				this.maxPosition = Math.ceil(length / 15);
 				this.getSet();

+ 14 - 0
frontend/main.js

@@ -27,6 +27,20 @@ Vue.component("metadata", {
 
 Vue.use(VueRouter);
 
+Vue.directive("scroll", {
+	inserted(el, binding) {
+		const f = evt => {
+			clearTimeout(window.scrollDebounceId);
+			window.scrollDebounceId = setTimeout(() => {
+				if (binding.value(evt, el)) {
+					window.removeEventListener("scroll", f);
+				}
+			}, 200);
+		};
+		window.addEventListener("scroll", f);
+	}
+});
+
 const router = new VueRouter({
 	mode: "history",
 	routes: [