浏览代码

refactor(Station): changed the way likes/dislikes are loaded

Kristian Vos 3 年之前
父节点
当前提交
9b511dc974

+ 48 - 0
backend/logic/actions/songs.js

@@ -1428,6 +1428,54 @@ export default {
 		);
 	}),
 
+	/**
+	 * Gets song ratings
+	 *
+	 * @param session
+	 * @param songId - the Musare song id
+	 * @param cb
+	 */
+
+	getSongRatings: isLoginRequired(async function getSongRatings(session, songId, cb) {
+		async.waterfall(
+			[
+				next => {
+					SongsModule.runJob("GET_SONG", { songId }, this)
+						.then(res => next(null, res.song))
+						.catch(next);
+				},
+
+				(song, next) => {
+					next(null, {
+						likes: song.likes,
+						dislikes: song.dislikes
+					});
+				}
+			],
+			async (err, ratings) => {
+				if (err) {
+					err = await UtilsModule.runJob("GET_ERROR", { error: err }, this);
+					this.log(
+						"ERROR",
+						"SONGS_GET_RATINGS",
+						`User "${session.userId}" failed to get ratings for ${songId}. "${err}"`
+					);
+					return cb({ status: "error", message: err });
+				}
+
+				const { likes, dislikes } = ratings;
+
+				return cb({
+					status: "success",
+					data: {
+						likes,
+						dislikes
+					}
+				});
+			}
+		);
+	}),
+
 	/**
 	 * Gets user's own song ratings
 	 *

+ 1 - 1
backend/logic/db/index.js

@@ -13,7 +13,7 @@ const REQUIRED_DOCUMENT_VERSIONS = {
 	queueSong: 1,
 	report: 5,
 	song: 5,
-	station: 5,
+	station: 6,
 	user: 3
 };
 

+ 1 - 5
backend/logic/db/schemas/station.js

@@ -14,8 +14,6 @@ export default {
 		duration: { type: Number },
 		skipDuration: { type: Number },
 		thumbnail: { type: String },
-		likes: { type: Number, default: -1 },
-		dislikes: { type: Number, default: -1 },
 		skipVotes: [{ type: String }],
 		requestedBy: { type: String },
 		requestedAt: { type: Date },
@@ -36,8 +34,6 @@ export default {
 			duration: { type: Number },
 			skipDuration: { type: Number },
 			thumbnail: { type: String },
-			likes: { type: Number },
-			dislikes: { type: Number },
 			requestedBy: { type: String },
 			requestedAt: { type: Date },
 			status: { type: String }
@@ -49,5 +45,5 @@ export default {
 	theme: { type: String, enum: ["blue", "purple", "teal", "orange"], default: "blue" },
 	includedPlaylists: [{ type: String }],
 	excludedPlaylists: [{ type: String }],
-	documentVersion: { type: Number, default: 5, required: true }
+	documentVersion: { type: Number, default: 6, required: true }
 };

+ 65 - 0
backend/logic/migration/migrations/migration14.js

@@ -0,0 +1,65 @@
+import async from "async";
+
+/**
+ * Migration 14
+ *
+ * Migration for removing some data from stations
+ *
+ * @param {object} MigrationModule - the MigrationModule
+ * @returns {Promise} - returns promise
+ */
+export default async function migrate(MigrationModule) {
+	const stationModel = await MigrationModule.runJob("GET_MODEL", { modelName: "station" }, this);
+
+	return new Promise((resolve, reject) => {
+		async.waterfall(
+			[
+				next => {
+					this.log("INFO", `Migration 14. Finding stations with document version 5.`);
+					stationModel.find({ documentVersion: 5 }, (err, stations) => {
+						if (err) next(err);
+						else {
+							async.eachLimit(
+								stations.map(station => station._doc),
+								1,
+								(station, next) => {
+									const { queue, currentSong } = station;
+
+									if (currentSong && currentSong.likes) {
+										delete currentSong.likes;
+										delete currentSong.dislikes;
+									}
+
+									queue.forEach(song => {
+										delete song.likes;
+										delete song.dislikes;
+									});
+
+									stationModel.updateOne(
+										{ _id: station._id },
+										{
+											$set: {
+												documentVersion: 6,
+												queue,
+												currentSong
+											}
+										},
+										next
+									);
+								},
+								err => {
+									this.log("INFO", `Migration 14. Stations found: ${stations.length}.`);
+									next(err);
+								}
+							);
+						}
+					});
+				}
+			],
+			err => {
+				if (err) reject(new Error(err));
+				else resolve();
+			}
+		);
+	});
+}

+ 13 - 11
backend/logic/stations.js

@@ -554,9 +554,7 @@ class _StationsModule extends CoreClass {
 								"skipDuration",
 								"artists",
 								"thumbnail",
-								"status",
-								"likes",
-								"dislikes"
+								"status"
 							]
 						})
 							.then(response => {
@@ -924,8 +922,6 @@ class _StationsModule extends CoreClass {
 								title: song.title,
 								artists: song.artists,
 								duration: song.duration,
-								likes: song.likes,
-								dislikes: song.dislikes,
 								skipDuration: song.skipDuration,
 								thumbnail: song.thumbnail,
 								requestedAt: song.requestedAt,
@@ -938,10 +934,10 @@ class _StationsModule extends CoreClass {
 						$set.startedAt = Date.now();
 						$set.timePaused = 0;
 						if (station.paused) $set.pausedAt = Date.now();
-						next(null, $set, station);
+						next(null, $set, song, station);
 					},
 
-					($set, station, next) => {
+					($set, song, station, next) => {
 						StationsModule.stationModel.updateOne({ _id: station._id }, { $set }, err => {
 							if (err) return next(err);
 
@@ -953,10 +949,19 @@ class _StationsModule extends CoreClass {
 									})
 										.then()
 										.catch();
-									next(null, station);
+									next(null, station, song);
 								})
 								.catch(next);
 						});
+					},
+
+					(station, song, next) => {
+						if (station.currentSong !== null && station.currentSong.youtubeId !== undefined) {
+							station.currentSong.likes = song.likes;
+							station.currentSong.dislikes = song.dislikes;
+							station.currentSong.skipVotes = 0;
+						}
+						next(null, station);
 					}
 				],
 				async (err, station) => {
@@ -966,9 +971,6 @@ class _StationsModule extends CoreClass {
 						return reject(new Error(err));
 					}
 
-					if (station.currentSong !== null && station.currentSong.youtubeId !== undefined)
-						station.currentSong.skipVotes = 0;
-
 					// TODO Pub/Sub this
 
 					const { currentSong } = station;

+ 103 - 13
frontend/src/pages/Station/index.vue

@@ -319,7 +319,18 @@
 
 									<!-- Vote to Skip Button -->
 									<button
-										v-if="loggedIn"
+										v-if="!skipVotesLoaded"
+										class="button is-primary disabled"
+										content="Skip votes have not been loaded yet"
+										v-tippy
+									>
+										<i
+											class="material-icons icon-with-button"
+											>skip_next</i
+										>
+									</button>
+									<button
+										v-else-if="loggedIn"
 										class="button is-primary"
 										@click="voteSkipStation()"
 										content="Vote to Skip Song"
@@ -392,6 +403,7 @@
 									<!-- Ratings (Like/Dislike) Buttons -->
 									<div
 										id="ratings"
+										v-if="ratingsLoaded"
 										:class="{
 											liked: liked,
 											disliked: disliked
@@ -429,6 +441,33 @@
 											>{{ currentSong.dislikes }}
 										</button>
 									</div>
+									<div id="ratings" class="disabled" v-else>
+										<!-- Like Song Button -->
+										<button
+											class="button is-success like-song disabled"
+											id="like-song"
+											content="Ratings have not been loaded yet"
+											v-tippy
+										>
+											<i
+												class="material-icons icon-with-button"
+												>thumb_up_alt</i
+											>
+										</button>
+
+										<!-- Dislike Song Button -->
+										<button
+											class="button is-danger dislike-song disabled"
+											id="dislike-song"
+											content="Ratings have not been loaded yet"
+											v-tippy
+										>
+											<i
+												class="material-icons icon-with-button"
+												>thumb_down_alt</i
+											>
+										</button>
+									</div>
 
 									<!-- Add Song To Playlist Button & Dropdown -->
 									<add-to-playlist-dropdown
@@ -472,7 +511,7 @@
 								</div>
 								<div id="right-buttons" v-else>
 									<!-- Disabled Ratings (Like/Dislike) Buttons -->
-									<div id="ratings">
+									<div id="ratings" v-if="ratingsLoaded">
 										<!-- Disabled Like Song Button -->
 										<button
 											class="button is-success disabled"
@@ -499,6 +538,33 @@
 											>{{ currentSong.dislikes }}
 										</button>
 									</div>
+									<div id="ratings" v-else>
+										<!-- Disabled Like Song Button -->
+										<button
+											class="button is-success disabled"
+											id="like-song"
+											content="Ratings have not been loaded yet"
+											v-tippy="{ theme: 'info' }"
+										>
+											<i
+												class="material-icons icon-with-button"
+												>thumb_up_alt</i
+											>
+										</button>
+
+										<!-- Disabled Dislike Song Button -->
+										<button
+											class="button is-danger disabled"
+											id="dislike-song"
+											content="Ratings have not been loaded yet"
+											v-tippy="{ theme: 'info' }"
+										>
+											<i
+												class="material-icons icon-with-button"
+												>thumb_down_alt</i
+											>
+										</button>
+									</div>
 									<!-- Disabled Add Song To Playlist Button & Dropdown -->
 									<div id="add-song-to-playlist">
 										<div class="control has-addons">
@@ -681,7 +747,6 @@ export default {
 			liked: false,
 			disliked: false,
 			timeBeforePause: 0,
-			skipVotes: 0,
 			systemDifference: 0,
 			attemptsToPlayVideo: 0,
 			canAutoplay: true,
@@ -703,6 +768,22 @@ export default {
 		};
 	},
 	computed: {
+		skipVotesLoaded() {
+			return (
+				!this.noSong &&
+				Number.isInteger(this.currentSong.skipVotes) &&
+				this.currentSong.skipVotes >= 0
+			);
+		},
+		ratingsLoaded() {
+			return (
+				!this.noSong &&
+				Number.isInteger(this.currentSong.likes) &&
+				Number.isInteger(this.currentSong.dislikes) &&
+				this.currentSong.likes >= 0 &&
+				this.currentSong.dislikes >= 0
+			);
+		},
 		...mapState("modalVisibility", {
 			modals: state => state.modals
 		}),
@@ -853,8 +934,7 @@ export default {
 		this.socket.on("event:song.liked", res => {
 			if (!this.noSong) {
 				if (res.data.youtubeId === this.currentSong.youtubeId) {
-					this.currentSong.dislikes = res.data.dislikes;
-					this.currentSong.likes = res.data.likes;
+					this.updateCurrentSongRatings(res.data);
 				}
 			}
 		});
@@ -862,8 +942,7 @@ export default {
 		this.socket.on("event:song.disliked", res => {
 			if (!this.noSong) {
 				if (res.data.youtubeId === this.currentSong.youtubeId) {
-					this.currentSong.dislikes = res.data.dislikes;
-					this.currentSong.likes = res.data.likes;
+					this.updateCurrentSongRatings(res.data);
 				}
 			}
 		});
@@ -871,8 +950,7 @@ export default {
 		this.socket.on("event:song.unliked", res => {
 			if (!this.noSong) {
 				if (res.data.youtubeId === this.currentSong.youtubeId) {
-					this.currentSong.dislikes = res.data.dislikes;
-					this.currentSong.likes = res.data.likes;
+					this.updateCurrentSongRatings(res.data);
 				}
 			}
 		});
@@ -880,8 +958,7 @@ export default {
 		this.socket.on("event:song.undisliked", res => {
 			if (!this.noSong) {
 				if (res.data.youtubeId === this.currentSong.youtubeId) {
-					this.currentSong.dislikes = res.data.dislikes;
-					this.currentSong.likes = res.data.likes;
+					this.updateCurrentSongRatings(res.data);
 				}
 			}
 		});
@@ -945,7 +1022,7 @@ export default {
 		});
 
 		this.socket.on("event:station.voteSkipSong", () => {
-			if (this.currentSong) this.currentSong.skipVotes += 1;
+			if (this.currentSong) this.updateCurrentSongSkipVotes(this.currentSong.skipVotes + 1);
 		});
 
 		this.socket.on("event:privatePlaylist.selected", res => {
@@ -1191,6 +1268,17 @@ export default {
 					}, this.getTimeRemaining());
 				}
 
+				this.socket.dispatch(
+					"songs.getSongRatings",
+					currentSong._id,
+					res => {
+						if (currentSong._id === this.currentSong._id) {
+							const { likes, dislikes } = res.data;
+							this.updateCurrentSongRatings({ likes, dislikes });
+						}
+					}
+				);
+
 				this.socket.dispatch(
 					"songs.getOwnSongRatings",
 					currentSong.youtubeId,
@@ -2026,7 +2114,9 @@ export default {
 			"updateNoSong",
 			"updateIfStationIsFavorited",
 			"setIncludedPlaylists",
-			"setExcludedPlaylists"
+			"setExcludedPlaylists",
+			"updateCurrentSongRatings",
+			"updateCurrentSongSkipVotes"
 		]),
 		...mapActions("modals/editSong", ["stopVideo"])
 	}

+ 13 - 0
frontend/src/store/modules/station.js

@@ -69,6 +69,12 @@ const actions = {
 	},
 	setExcludedPlaylists: ({ commit }, excludedPlaylists) => {
 		commit("setExcludedPlaylists", excludedPlaylists);
+	},
+	updateCurrentSongRatings: ({ commit }, songRatings) => {
+		commit("updateCurrentSongRatings", songRatings);
+	},
+	updateCurrentSongSkipVotes: ({ commit }, skipVotes) => {
+		commit("updateCurrentSongSkipVotes", skipVotes);
 	}
 };
 
@@ -149,6 +155,13 @@ const mutations = {
 	},
 	setExcludedPlaylists(state, excludedPlaylists) {
 		state.excludedPlaylists = JSON.parse(JSON.stringify(excludedPlaylists));
+	},
+	updateCurrentSongRatings(state, songRatings) {
+		state.currentSong.likes = songRatings.likes;
+		state.currentSong.dislikes = songRatings.dislikes;
+	},
+	updateCurrentSongSkipVotes(state, skipVotes) {
+		state.currentSong.skipVotes = skipVotes;
 	}
 };