Quellcode durchsuchen

refactor(socket.io -> WS): emitting and listening for events (along with callbacks)

Signed-off-by: Jonathan <theflametrooper@gmail.com>
Jonathan vor 4 Jahren
Ursprung
Commit
78ac4585d7
50 geänderte Dateien mit 745 neuen und 585 gelöschten Zeilen
  1. 2 1
      backend/logic/app.js
  2. 104 78
      backend/logic/io.js
  3. 28 27
      backend/package-lock.json
  4. 2 1
      backend/package.json
  5. 1 0
      docker-compose.yml
  6. 1 1
      frontend/src/App.vue
  7. 1 1
      frontend/src/api/admin/reports.js
  8. 3 3
      frontend/src/api/auth.js
  9. 4 4
      frontend/src/components/modals/AddSongToQueue.vue
  10. 1 1
      frontend/src/components/modals/CreateCommunityStation.vue
  11. 17 13
      frontend/src/components/modals/CreatePlaylist.vue
  12. 15 10
      frontend/src/components/modals/EditNews.vue
  13. 21 17
      frontend/src/components/modals/EditPlaylist/index.vue
  14. 3 3
      frontend/src/components/modals/EditSong.vue
  15. 58 50
      frontend/src/components/modals/EditStation.vue
  16. 6 6
      frontend/src/components/modals/EditUser.vue
  17. 1 1
      frontend/src/components/modals/Report.vue
  18. 1 1
      frontend/src/components/modals/ViewPunishment.vue
  19. 1 1
      frontend/src/components/modals/ViewReport.vue
  20. 1 1
      frontend/src/components/modals/WhatIsNew.vue
  21. 3 3
      frontend/src/components/ui/AddToPlaylistDropdown.vue
  22. 73 26
      frontend/src/io.js
  23. 15 2
      frontend/src/main.js
  24. 2 2
      frontend/src/mixins/SearchYoutube.vue
  25. 1 1
      frontend/src/mixins/SortablePlaylists.vue
  26. 2 2
      frontend/src/pages/Admin/tabs/NewStatistics.vue
  27. 19 15
      frontend/src/pages/Admin/tabs/News.vue
  28. 2 2
      frontend/src/pages/Admin/tabs/Playlists.vue
  29. 3 3
      frontend/src/pages/Admin/tabs/Punishments.vue
  30. 5 5
      frontend/src/pages/Admin/tabs/QueueSongs.vue
  31. 14 10
      frontend/src/pages/Admin/tabs/Reports.vue
  32. 5 5
      frontend/src/pages/Admin/tabs/Songs.vue
  33. 4 4
      frontend/src/pages/Admin/tabs/Stations.vue
  34. 1 1
      frontend/src/pages/Admin/tabs/Statistics.vue
  35. 2 2
      frontend/src/pages/Admin/tabs/Users.vue
  36. 26 18
      frontend/src/pages/Home.vue
  37. 1 1
      frontend/src/pages/News.vue
  38. 3 2
      frontend/src/pages/Profile/index.vue
  39. 2 2
      frontend/src/pages/Profile/tabs/Playlists.vue
  40. 4 4
      frontend/src/pages/Profile/tabs/RecentActivity.vue
  41. 4 4
      frontend/src/pages/ResetPassword.vue
  42. 1 1
      frontend/src/pages/Settings/index.vue
  43. 5 5
      frontend/src/pages/Settings/tabs/Account.vue
  44. 2 2
      frontend/src/pages/Settings/tabs/Preferences.vue
  45. 4 4
      frontend/src/pages/Settings/tabs/Profile.vue
  46. 4 4
      frontend/src/pages/Settings/tabs/Security.vue
  47. 3 3
      frontend/src/pages/Station/components/Sidebar/MyPlaylists.vue
  48. 1 1
      frontend/src/pages/Station/components/Sidebar/Queue/index.vue
  49. 262 230
      frontend/src/pages/Station/index.vue
  50. 1 1
      frontend/src/store/modules/user.js

+ 2 - 1
backend/logic/app.js

@@ -6,6 +6,7 @@ import cookieParser from "cookie-parser";
 import bodyParser from "body-parser";
 import express from "express";
 import oauth from "oauth";
+import http from "http";
 import CoreClass from "../core";
 
 const { OAuth2 } = oauth;
@@ -42,7 +43,7 @@ class _AppModule extends CoreClass {
 
 			const app = (this.app = express());
 			const SIDname = config.get("cookie.SIDname");
-			this.server = app.listen(config.get("serverPort"));
+			this.server = http.createServer(app).listen(config.get("serverPort"));
 
 			app.use(cookieParser());
 

+ 104 - 78
backend/logic/io.js

@@ -4,7 +4,8 @@
 
 import config from "config";
 import async from "async";
-import socketio from "socket.io";
+import WebSocket from "ws";
+import { EventEmitter } from "events";
 
 import CoreClass from "../core";
 
@@ -48,20 +49,34 @@ class _IOModule extends CoreClass {
 		// TODO: Check every 30s/, for all sockets, if they are still allowed to be in the rooms they are in, and on socket at all (permission changing/banning)
 		const server = await AppModule.runJob("SERVER");
 
-		this._io = socketio(server);
 		// this._io.origins(config.get("cors.origin"));
 
+		// this._io = socketio(server);
+
+		this._io = new WebSocket.Server({ server, path: "/ws" });
+
 		return new Promise(resolve => {
 			this.setStage(3);
 
-			this._io.use(async (socket, cb) => {
-				IOModule.runJob("HANDLE_IO_USE", { socket, cb });
-			});
+			// this._io.use(async (socket, cb) => {
+			// 	IOModule.runJob("HANDLE_IO_USE", { socket, cb });
+			// });
 
 			this.setStage(4);
 
-			this._io.on("connection", async socket => {
-				IOModule.runJob("HANDLE_IO_CONNECTION", { socket });
+			this._io.on("connection", async (socket, req) => {
+				socket.dispatch = (...args) => socket.send(JSON.stringify(args));
+
+				console.log(socket.actions);
+
+				socket.actions = new EventEmitter();
+				socket.actions.setMaxListeners(0);
+				socket.listen = (target, cb) => socket.actions.addListener(target, args => cb(args));
+
+				socket.dispatch("test2", { color: "red" }, 9);
+				IOModule.runJob("HANDLE_IO_USE", { socket, req }).then(socket =>
+					IOModule.runJob("HANDLE_IO_CONNECTION", { socket })
+				);
 			});
 
 			this.setStage(5);
@@ -370,17 +385,17 @@ class _IOModule extends CoreClass {
 	 * @returns {Promise} - returns promise (reject, resolve)
 	 */
 	async EMIT_TO_ROOM(payload) {
-		return new Promise(resolve => {
-			const { sockets } = IOModule._io.sockets;
-			Object.keys(sockets).forEach(socketKey => {
-				const socket = sockets[socketKey];
-				if (socket.rooms[payload.room]) {
-					socket.emit(...payload.args);
-				}
-			});
-
-			return resolve();
-		});
+		return new Promise(resolve =>
+			// const { sockets } = IOModule._io.sockets;
+			// Object.keys(sockets).forEach(socketKey => {
+			// 	const socket = sockets[socketKey];
+			// 	if (socket.rooms[payload.room]) {
+			// 		socket.dispatch(...payload.args);
+			// 	}
+			// });
+
+			resolve()
+		);
 	}
 
 	/**
@@ -410,27 +425,25 @@ class _IOModule extends CoreClass {
 	 * @returns {Promise} - returns promise (reject, resolve)
 	 */
 	async HANDLE_IO_USE(payload) {
+		console.log("io use");
+
 		return new Promise(resolve => {
-			const { socket, cb } = payload;
+			const { socket, req } = payload;
 
 			let SID;
 
-			socket.ip = socket.request.headers["x-forwarded-for"] || "0.0.0.0";
+			socket.ip = req.headers["x-forwarded-for"] || "0.0.0.0";
 
 			return async.waterfall(
 				[
 					next => {
-						if (!socket.request.headers.cookie) return next("No cookie exists yet.");
-						return UtilsModule.runJob(
-							"PARSE_COOKIES",
-							{
-								cookieString: socket.request.headers.cookie
-							},
-							this
-						).then(res => {
-							SID = res[IOModule.SIDname];
-							next(null);
-						});
+						if (!req.headers.cookie) return next("No cookie exists yet.");
+						return UtilsModule.runJob("PARSE_COOKIES", { cookieString: req.headers.cookie }, this).then(
+							res => {
+								SID = res[IOModule.SIDname];
+								next(null);
+							}
+						);
 					},
 
 					next => {
@@ -440,9 +453,7 @@ class _IOModule extends CoreClass {
 
 					next => {
 						CacheModule.runJob("HGET", { table: "sessions", key: SID }, this)
-							.then(session => {
-								next(null, session);
-							})
+							.then(session => next(null, session))
 							.catch(next);
 					},
 
@@ -455,15 +466,9 @@ class _IOModule extends CoreClass {
 
 						return CacheModule.runJob(
 							"HSET",
-							{
-								table: "sessions",
-								key: SID,
-								value: session
-							},
+							{ table: "sessions", key: SID, value: session },
 							this
-						).then(session => {
-							next(null, session);
-						});
+						).then(session => next(null, session));
 					},
 
 					(res, next) => {
@@ -496,11 +501,13 @@ class _IOModule extends CoreClass {
 					}
 				],
 				() => {
-					if (!socket.session) socket.session = { socketId: socket.id };
-					else socket.session.socketId = socket.id;
+					if (!socket.session) socket.session = { socketId: req.headers["sec-websocket-key"] };
+					else socket.session.socketId = req.headers["sec-websocket-key"];
+
+					console.log("session", socket.session);
 
-					cb();
-					resolve();
+					// cb();
+					resolve(socket);
 				}
 			);
 		});
@@ -513,6 +520,8 @@ class _IOModule extends CoreClass {
 	 * @returns {Promise} - returns promise (reject, resolve)
 	 */
 	async HANDLE_IO_CONNECTION(payload) {
+		console.log("handle io connection");
+
 		return new Promise(resolve => {
 			const { socket } = payload;
 
@@ -528,7 +537,7 @@ class _IOModule extends CoreClass {
 					`A user tried to connect, but is currently banned. IP: ${socket.ip}.${sessionInfo}`
 				);
 
-				socket.emit("keep.event:banned", socket.banishment.ban);
+				socket.dispatch("keep.event:banned", socket.banishment.ban);
 
 				return socket.disconnect(true);
 			}
@@ -536,35 +545,35 @@ class _IOModule extends CoreClass {
 			IOModule.log("INFO", "IO_CONNECTION", `User connected. IP: ${socket.ip}.${sessionInfo}`);
 
 			// catch when the socket has been disconnected
-			socket.on("disconnect", () => {
+			socket.onclose = () => {
 				if (socket.session.sessionId) sessionInfo = ` UserID: ${socket.session.userId}.`;
 				IOModule.log("INFO", "IO_DISCONNECTION", `User disconnected. IP: ${socket.ip}.${sessionInfo}`);
-			});
-
-			socket.use((data, next) => {
-				if (data.length === 0) return next(new Error("Not enough arguments specified."));
-				if (typeof data[0] !== "string") return next(new Error("First argument must be a string."));
-
-				const namespaceAction = data[0];
-				if (
-					!namespaceAction ||
-					namespaceAction.indexOf(".") === -1 ||
-					namespaceAction.indexOf(".") !== namespaceAction.lastIndexOf(".")
-				)
-					return next(new Error("Invalid first argument"));
-				const namespace = data[0].split(".")[0];
-				const action = data[0].split(".")[1];
-
-				if (!namespace) return next(new Error("Invalid namespace."));
-				if (!action) return next(new Error("Invalid action."));
-				if (!IOModule.actions[namespace]) return next(new Error("Namespace not found."));
-				if (!IOModule.actions[namespace][action]) return next(new Error("Action not found."));
-
-				return next();
-			});
+			};
+
+			// socket.use((data, next) => {
+			// 	if (data.length === 0) return next(new Error("Not enough arguments specified."));
+			// 	if (typeof data[0] !== "string") return next(new Error("First argument must be a string."));
+
+			// 	const namespaceAction = data[0];
+			// 	if (
+			// 		!namespaceAction ||
+			// 		namespaceAction.indexOf(".") === -1 ||
+			// 		namespaceAction.indexOf(".") !== namespaceAction.lastIndexOf(".")
+			// 	)
+			// 		return next(new Error("Invalid first argument"));
+			// 	const namespace = data[0].split(".")[0];
+			// 	const action = data[0].split(".")[1];
+
+			// 	if (!namespace) return next(new Error("Invalid namespace."));
+			// 	if (!action) return next(new Error("Invalid action."));
+			// 	if (!IOModule.actions[namespace]) return next(new Error("Namespace not found."));
+			// 	if (!IOModule.actions[namespace][action]) return next(new Error("Action not found."));
+
+			// 	return next();
+			// });
 
 			// catch errors on the socket (internal to socket.io)
-			socket.on("error", console.error);
+			socket.onerror = console.error;
 
 			if (socket.session.sessionId) {
 				CacheModule.runJob("HGET", {
@@ -574,7 +583,7 @@ class _IOModule extends CoreClass {
 					.then(session => {
 						if (session && session.userId) {
 							IOModule.userModel.findOne({ _id: session.userId }, (err, user) => {
-								if (err || !user) return socket.emit("ready", false);
+								if (err || !user) return socket.dispatch("ready", false);
 
 								let role = "";
 								let username = "";
@@ -585,14 +594,14 @@ class _IOModule extends CoreClass {
 									userId = session.userId;
 								}
 
-								return socket.emit("ready", true, role, username, userId);
+								return socket.dispatch("ready", true, role, username, userId);
 							});
-						} else socket.emit("ready", false);
+						} else socket.dispatch("ready", false);
 					})
 					.catch(() => {
-						socket.emit("ready", false);
+						socket.dispatch("ready", false);
 					});
-			} else socket.emit("ready", false);
+			} else socket.dispatch("ready", false);
 
 			// have the socket listen for each action
 			Object.keys(IOModule.actions).forEach(namespace => {
@@ -600,10 +609,27 @@ class _IOModule extends CoreClass {
 					// the full name of the action
 					const name = `${namespace}.${action}`;
 
+					socket.onmessage = message => {
+						const data = JSON.parse(message.data);
+
+						if (data[data.length - 1].callbackRef) {
+							const { callbackRef } = data[data.length - 1];
+							data.pop();
+							return socket.actions.emit(data.shift(0), [
+								...data,
+								res => socket.dispatch("callbackRef", callbackRef, res)
+							]);
+						}
+
+						return socket.actions.emit(data.shift(0), data);
+					};
+
 					// listen for this action to be called
-					socket.on(name, async (...args) => {
+					socket.listen(name, async args => {
 						IOModule.runJob("RUN_ACTION", { socket, namespace, action, args });
 
+						console.log(name, args);
+
 						/* let cb = args[args.length - 1];
 
 						if (typeof cb !== "function")

+ 28 - 27
backend/package-lock.json

@@ -25,7 +25,8 @@
         "redis": "^2.8.0",
         "sha256": "^0.2.0",
         "socket.io": "2.4.1",
-        "underscore": "^1.10.2"
+        "underscore": "^1.10.2",
+        "ws": "^7.4.3"
       },
       "devDependencies": {
         "eslint": "^7.16.0",
@@ -848,14 +849,6 @@
       "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
       "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
     },
-    "node_modules/engine.io-client/node_modules/ws": {
-      "version": "7.4.2",
-      "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.2.tgz",
-      "integrity": "sha512-T4tewALS3+qsrpGI/8dqNMLIVdq/g/85U98HPMa6F0m6xTbvhXU6RCQLqPH3+SlomNV/LdY6RXEbBpMH6EOJnA==",
-      "engines": {
-        "node": ">=8.3.0"
-      }
-    },
     "node_modules/engine.io-parser": {
       "version": "2.2.1",
       "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.1.tgz",
@@ -876,14 +869,6 @@
         "ms": "^2.1.1"
       }
     },
-    "node_modules/engine.io/node_modules/ws": {
-      "version": "7.4.2",
-      "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.2.tgz",
-      "integrity": "sha512-T4tewALS3+qsrpGI/8dqNMLIVdq/g/85U98HPMa6F0m6xTbvhXU6RCQLqPH3+SlomNV/LdY6RXEbBpMH6EOJnA==",
-      "engines": {
-        "node": ">=8.3.0"
-      }
-    },
     "node_modules/enquirer": {
       "version": "2.3.6",
       "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz",
@@ -3737,6 +3722,26 @@
       "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
       "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
     },
+    "node_modules/ws": {
+      "version": "7.4.3",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.3.tgz",
+      "integrity": "sha512-hr6vCR76GsossIRsr8OLR9acVVm1jyfEWvhbNjtgPOrfvAlKzvyeg/P6r8RuDjRyrcQoPQT7K0DGEPc7Ae6jzA==",
+      "engines": {
+        "node": ">=8.3.0"
+      },
+      "peerDependencies": {
+        "bufferutil": "^4.0.1",
+        "utf-8-validate": "^5.0.2"
+      },
+      "peerDependenciesMeta": {
+        "bufferutil": {
+          "optional": true
+        },
+        "utf-8-validate": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/xmlhttprequest-ssl": {
       "version": "1.5.5",
       "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz",
@@ -4415,11 +4420,6 @@
           "requires": {
             "ms": "^2.1.1"
           }
-        },
-        "ws": {
-          "version": "7.4.2",
-          "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.2.tgz",
-          "integrity": "sha512-T4tewALS3+qsrpGI/8dqNMLIVdq/g/85U98HPMa6F0m6xTbvhXU6RCQLqPH3+SlomNV/LdY6RXEbBpMH6EOJnA=="
         }
       }
     },
@@ -4453,11 +4453,6 @@
           "version": "2.0.0",
           "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
           "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
-        },
-        "ws": {
-          "version": "7.4.2",
-          "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.2.tgz",
-          "integrity": "sha512-T4tewALS3+qsrpGI/8dqNMLIVdq/g/85U98HPMa6F0m6xTbvhXU6RCQLqPH3+SlomNV/LdY6RXEbBpMH6EOJnA=="
         }
       }
     },
@@ -6830,6 +6825,12 @@
       "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
       "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
     },
+    "ws": {
+      "version": "7.4.3",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.3.tgz",
+      "integrity": "sha512-hr6vCR76GsossIRsr8OLR9acVVm1jyfEWvhbNjtgPOrfvAlKzvyeg/P6r8RuDjRyrcQoPQT7K0DGEPc7Ae6jzA==",
+      "requires": {}
+    },
     "xmlhttprequest-ssl": {
       "version": "1.5.5",
       "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz",

+ 2 - 1
backend/package.json

@@ -31,7 +31,8 @@
     "redis": "^2.8.0",
     "sha256": "^0.2.0",
     "socket.io": "2.4.1",
-    "underscore": "^1.10.2"
+    "underscore": "^1.10.2",
+    "ws": "^7.4.3"
   },
   "devDependencies": {
     "eslint": "^7.16.0",

+ 1 - 0
docker-compose.yml

@@ -5,6 +5,7 @@ services:
     build: ./backend
     ports:
       - "${BACKEND_PORT}:8080"
+      - "3030:3030"
     volumes:
       - ./backend:/opt/app
       - ./log:/opt/log

+ 1 - 1
frontend/src/App.vue

@@ -141,7 +141,7 @@ export default {
 		io.getSocket(true, socket => {
 			this.socket = socket;
 
-			this.socket.emit("users.getPreferences", res => {
+			this.socket.dispatch("users.getPreferences", res => {
 				if (res.status === "success") {
 					this.changeAutoSkipDisliked(res.data.autoSkipDisliked);
 					this.changeNightmode(res.data.nightmode);

+ 1 - 1
frontend/src/api/admin/reports.js

@@ -5,7 +5,7 @@ export default {
 	resolve(reportId) {
 		return new Promise((resolve, reject) => {
 			io.getSocket(socket => {
-				socket.emit("reports.resolve", reportId, res => {
+				socket.dispatch("reports.resolve", reportId, res => {
 					new Toast({ content: res.message, timeout: 3000 });
 					if (res.status === "success")
 						return resolve({ status: "success" });

+ 3 - 3
frontend/src/api/auth.js

@@ -9,7 +9,7 @@ export default {
 			const { username, email, password, recaptchaToken } = user;
 
 			io.getSocket(socket => {
-				socket.emit(
+				socket.dispatch(
 					"users.register",
 					username,
 					email,
@@ -53,7 +53,7 @@ export default {
 			const { email, password } = user;
 
 			io.getSocket(socket => {
-				socket.emit("users.login", email, password, res => {
+				socket.dispatch("users.login", email, password, res => {
 					console.log(123, res);
 					if (res.status === "success") {
 						return lofig.get("cookie").then(cookie => {
@@ -81,7 +81,7 @@ export default {
 	logout() {
 		return new Promise((resolve, reject) => {
 			io.getSocket(socket => {
-				socket.emit("users.logout", res => {
+				socket.dispatch("users.logout", res => {
 					if (res.status === "success") {
 						return lofig.get("cookie").then(cookie => {
 							document.cookie = `${cookie.SIDname}=;expires=Thu, 01 Jan 1970 00:00:01 GMT;`;

+ 4 - 4
frontend/src/components/modals/AddSongToQueue.vue

@@ -235,7 +235,7 @@ export default {
 	mounted() {
 		io.getSocket(socket => {
 			this.socket = socket;
-			this.socket.emit("playlists.indexMyPlaylists", true, res => {
+			this.socket.dispatch("playlists.indexMyPlaylists", true, res => {
 				if (res.status === "success") this.playlists = res.data;
 			});
 		});
@@ -257,7 +257,7 @@ export default {
 		},
 		addSongToQueue(songId, index) {
 			if (this.station.type === "community") {
-				this.socket.emit(
+				this.socket.dispatch(
 					"stations.addToQueue",
 					this.station._id,
 					songId,
@@ -280,7 +280,7 @@ export default {
 					}
 				);
 			} else {
-				this.socket.emit("queueSongs.add", songId, data => {
+				this.socket.dispatch("queueSongs.add", songId, data => {
 					if (data.status !== "success")
 						new Toast({
 							content: `Error: ${data.message}`,
@@ -318,7 +318,7 @@ export default {
 				}
 			}, 750);
 
-			return this.socket.emit(
+			return this.socket.dispatch(
 				"queueSongs.addSetToQueue",
 				this.search.playlist.query,
 				this.search.playlist.isImportingOnlyMusic,

+ 1 - 1
frontend/src/components/modals/CreateCommunityStation.vue

@@ -118,7 +118,7 @@ export default {
 					timeout: 8000
 				});
 
-			return this.socket.emit(
+			return this.socket.dispatch(
 				"stations.create",
 				{
 					name,

+ 17 - 13
frontend/src/components/modals/CreatePlaylist.vue

@@ -73,21 +73,25 @@ export default {
 					timeout: 8000
 				});
 
-			return this.socket.emit("playlists.create", this.playlist, res => {
-				new Toast({ content: res.message, timeout: 3000 });
+			return this.socket.dispatch(
+				"playlists.create",
+				this.playlist,
+				res => {
+					new Toast({ content: res.message, timeout: 3000 });
 
-				if (res.status === "success") {
-					this.closeModal({
-						sector: "station",
-						modal: "createPlaylist"
-					});
-					this.editPlaylist(res.data._id);
-					this.openModal({
-						sector: "station",
-						modal: "editPlaylist"
-					});
+					if (res.status === "success") {
+						this.closeModal({
+							sector: "station",
+							modal: "createPlaylist"
+						});
+						this.editPlaylist(res.data._id);
+						this.openModal({
+							sector: "station",
+							modal: "editPlaylist"
+						});
+					}
 				}
-			});
+			);
 		},
 		...mapActions("modalVisibility", ["closeModal", "openModal"]),
 		...mapActions("user/playlists", ["editPlaylist"])

+ 15 - 10
frontend/src/components/modals/EditNews.vue

@@ -186,7 +186,7 @@ export default {
 		io.getSocket(socket => {
 			this.socket = socket;
 
-			this.socket.emit(`news.getNewsFromId`, this.newsId, res => {
+			this.socket.dispatch(`news.getNewsFromId`, this.newsId, res => {
 				if (res.status === "success") {
 					const news = res.data;
 					this.editNews(news);
@@ -227,16 +227,21 @@ export default {
 			this.removeChange({ type, index });
 		},
 		updateNews(close) {
-			this.socket.emit("news.update", this.news._id, this.news, res => {
-				new Toast({ content: res.message, timeout: 4000 });
-				if (res.status === "success") {
-					if (close)
-						this.closeModal({
-							sector: this.sector,
-							modal: "editNews"
-						});
+			this.socket.dispatch(
+				"news.update",
+				this.news._id,
+				this.news,
+				res => {
+					new Toast({ content: res.message, timeout: 4000 });
+					if (res.status === "success") {
+						if (close)
+							this.closeModal({
+								sector: this.sector,
+								modal: "editNews"
+							});
+					}
 				}
-			});
+			);
 		},
 		...mapActions("modalVisibility", ["closeModal"]),
 		...mapActions("modals/editNews", [

+ 21 - 17
frontend/src/components/modals/EditPlaylist/index.vue

@@ -414,7 +414,7 @@ export default {
 		io.getSocket(socket => {
 			this.socket = socket;
 
-			this.socket.emit("playlists.getPlaylist", this.editing, res => {
+			this.socket.dispatch("playlists.getPlaylist", this.editing, res => {
 				if (res.status === "success") {
 					this.playlist = res.data;
 					this.playlist.songs.sort((a, b) => a.position - b.position);
@@ -507,7 +507,7 @@ export default {
 				}
 			}, 750);
 
-			return this.socket.emit(
+			return this.socket.dispatch(
 				"playlists.addSetToPlaylist",
 				this.search.playlist.query,
 				this.playlist._id,
@@ -545,7 +545,7 @@ export default {
 					});
 			});
 
-			this.socket.emit(
+			this.socket.dispatch(
 				"playlists.repositionSongs",
 				this.playlist._id,
 				songsBeingChanged,
@@ -562,17 +562,21 @@ export default {
 			return this.utils.formatTimeLong(length);
 		},
 		shuffle() {
-			this.socket.emit("playlists.shuffle", this.playlist._id, res => {
-				new Toast({ content: res.message, timeout: 4000 });
-				if (res.status === "success") {
-					this.playlist.songs = res.data.songs.sort(
-						(a, b) => a.position - b.position
-					);
+			this.socket.dispatch(
+				"playlists.shuffle",
+				this.playlist._id,
+				res => {
+					new Toast({ content: res.message, timeout: 4000 });
+					if (res.status === "success") {
+						this.playlist.songs = res.data.songs.sort(
+							(a, b) => a.position - b.position
+						);
+					}
 				}
-			});
+			);
 		},
 		addSongToPlaylist(id, index) {
-			this.socket.emit(
+			this.socket.dispatch(
 				"playlists.addSongToPlaylist",
 				false,
 				id,
@@ -586,16 +590,16 @@ export default {
 		},
 		removeSongFromPlaylist(id) {
 			if (this.playlist.displayName === "Liked Songs") {
-				this.socket.emit("songs.unlike", id, res => {
+				this.socket.dispatch("songs.unlike", id, res => {
 					new Toast({ content: res.message, timeout: 4000 });
 				});
 			}
 			if (this.playlist.displayName === "Disliked Songs") {
-				this.socket.emit("songs.undislike", id, res => {
+				this.socket.dispatch("songs.undislike", id, res => {
 					new Toast({ content: res.message, timeout: 4000 });
 				});
 			} else {
-				this.socket.emit(
+				this.socket.dispatch(
 					"playlists.removeSongFromPlaylist",
 					id,
 					this.playlist._id,
@@ -620,7 +624,7 @@ export default {
 					timeout: 8000
 				});
 
-			return this.socket.emit(
+			return this.socket.dispatch(
 				"playlists.updateDisplayName",
 				this.playlist._id,
 				this.playlist.displayName,
@@ -630,7 +634,7 @@ export default {
 			);
 		},
 		removePlaylist() {
-			this.socket.emit("playlists.remove", this.playlist._id, res => {
+			this.socket.dispatch("playlists.remove", this.playlist._id, res => {
 				new Toast({ content: res.message, timeout: 3000 });
 				if (res.status === "success") {
 					this.closeModal({
@@ -698,7 +702,7 @@ export default {
 		updatePrivacy() {
 			const { privacy } = this.playlist;
 			if (privacy === "public" || privacy === "private") {
-				this.socket.emit(
+				this.socket.dispatch(
 					"playlists.updatePrivacy",
 					this.playlist._id,
 					privacy,

+ 3 - 3
frontend/src/components/modals/EditSong.vue

@@ -615,7 +615,7 @@ export default {
 		io.getSocket(socket => {
 			this.socket = socket;
 
-			this.socket.emit(
+			this.socket.dispatch(
 				`${this.songType}.getSongFromMusareId`,
 				this.songId,
 				res => {
@@ -1092,7 +1092,7 @@ export default {
 
 			saveButtonRef.status = "disabled";
 
-			return this.socket.emit(
+			return this.socket.dispatch(
 				`${this.songType}.update`,
 				song._id,
 				song,
@@ -1177,7 +1177,7 @@ export default {
 		searchDiscogsForPage(page) {
 			const query = this.discogsQuery;
 
-			this.socket.emit("apis.searchDiscogs", query, page, res => {
+			this.socket.dispatch("apis.searchDiscogs", query, page, res => {
 				if (res.status === "success") {
 					if (page === 1)
 						new Toast({

+ 58 - 50
frontend/src/components/modals/EditStation.vue

@@ -513,33 +513,37 @@ export default {
 		io.getSocket(socket => {
 			this.socket = socket;
 
-			this.socket.emit(`stations.getStationById`, this.stationId, res => {
-				if (res.status === "success") {
-					const { station } = res;
-					// this.song = { ...song };
-					// if (this.song.discogs === undefined)
-					// 	this.song.discogs = null;
-					this.editStation(station);
-
-					// this.songDataLoaded = true;
-
-					this.station.genres = JSON.parse(
-						JSON.stringify(this.station.genres)
-					);
-					this.station.blacklistedGenres = JSON.parse(
-						JSON.stringify(this.station.blacklistedGenres)
-					);
-				} else {
-					new Toast({
-						content: "Station with that ID not found",
-						timeout: 3000
-					});
-					this.closeModal({
-						sector: this.sector,
-						modal: "editStation"
-					});
+			this.socket.dispatch(
+				`stations.getStationById`,
+				this.stationId,
+				res => {
+					if (res.status === "success") {
+						const { station } = res;
+						// this.song = { ...song };
+						// if (this.song.discogs === undefined)
+						// 	this.song.discogs = null;
+						this.editStation(station);
+
+						// this.songDataLoaded = true;
+
+						this.station.genres = JSON.parse(
+							JSON.stringify(this.station.genres)
+						);
+						this.station.blacklistedGenres = JSON.parse(
+							JSON.stringify(this.station.blacklistedGenres)
+						);
+					} else {
+						new Toast({
+							content: "Station with that ID not found",
+							timeout: 3000
+						});
+						this.closeModal({
+							sector: this.sector,
+							modal: "editStation"
+						});
+					}
 				}
-			});
+			);
 
 			return socket;
 		});
@@ -614,7 +618,7 @@ export default {
 
 			this.$refs.saveButton.status = "disabled";
 
-			return this.socket.emit(
+			return this.socket.dispatch(
 				"stations.updateName",
 				this.station._id,
 				name,
@@ -649,7 +653,7 @@ export default {
 
 			this.$refs.saveButton.status = "disabled";
 
-			return this.socket.emit(
+			return this.socket.dispatch(
 				"stations.updateDisplayName",
 				this.station._id,
 				displayName,
@@ -686,7 +690,7 @@ export default {
 
 			this.$refs.saveButton.status = "disabled";
 
-			return this.socket.emit(
+			return this.socket.dispatch(
 				"stations.updateDescription",
 				this.station._id,
 				description,
@@ -710,7 +714,7 @@ export default {
 		updatePrivacy() {
 			this.$refs.saveButton.status = "disabled";
 
-			this.socket.emit(
+			this.socket.dispatch(
 				"stations.updatePrivacy",
 				this.station._id,
 				this.station.privacy,
@@ -729,7 +733,7 @@ export default {
 		updateGenres() {
 			this.$refs.saveButton.status = "disabled";
 
-			this.socket.emit(
+			this.socket.dispatch(
 				"stations.updateGenres",
 				this.station._id,
 				this.station.genres,
@@ -754,7 +758,7 @@ export default {
 		updateBlacklistedGenres() {
 			this.$refs.saveButton.status = "disabled";
 
-			this.socket.emit(
+			this.socket.dispatch(
 				"stations.updateBlacklistedGenres",
 				this.station._id,
 				this.station.blacklistedGenres,
@@ -783,7 +787,7 @@ export default {
 		updatePartyMode() {
 			this.$refs.saveButton.status = "disabled";
 
-			this.socket.emit(
+			this.socket.dispatch(
 				"stations.updatePartyMode",
 				this.station._id,
 				this.station.partyMode,
@@ -807,26 +811,30 @@ export default {
 		updateQueueLock() {
 			this.$refs.saveButton.status = "disabled";
 
-			this.socket.emit("stations.toggleLock", this.station._id, res => {
-				if (res.status === "success") {
-					if (this.originalStation)
-						this.originalStation.locked = res.data;
+			this.socket.dispatch(
+				"stations.toggleLock",
+				this.station._id,
+				res => {
+					if (res.status === "success") {
+						if (this.originalStation)
+							this.originalStation.locked = res.data;
+
+						new Toast({
+							content: `Toggled queue lock successfully to ${res.data}`,
+							timeout: 4000
+						});
+
+						return this.$refs.saveButton.handleSuccessfulSave();
+					}
 
 					new Toast({
-						content: `Toggled queue lock successfully to ${res.data}`,
-						timeout: 4000
+						content: "Failed to toggle queue lock.",
+						timeout: 8000
 					});
 
-					return this.$refs.saveButton.handleSuccessfulSave();
+					return this.$refs.saveButton.handleFailedSave();
 				}
-
-				new Toast({
-					content: "Failed to toggle queue lock.",
-					timeout: 8000
-				});
-
-				return this.$refs.saveButton.handleFailedSave();
-			});
+			);
 		},
 		updateThemeLocal(theme) {
 			if (this.station.theme === theme) return;
@@ -836,7 +844,7 @@ export default {
 		updateTheme() {
 			this.$refs.saveButton.status = "disabled";
 
-			this.socket.emit(
+			this.socket.dispatch(
 				"stations.updateTheme",
 				this.station._id,
 				this.station.theme,
@@ -853,7 +861,7 @@ export default {
 			);
 		},
 		deleteStation() {
-			this.socket.emit("stations.remove", this.station._id, res => {
+			this.socket.dispatch("stations.remove", this.station._id, res => {
 				if (res.status === "success")
 					this.closeModal({
 						sector: "station",

+ 6 - 6
frontend/src/components/modals/EditUser.vue

@@ -117,7 +117,7 @@ export default {
 		io.getSocket(socket => {
 			this.socket = socket;
 
-			this.socket.emit(`users.getUserFromId`, this.userId, res => {
+			this.socket.dispatch(`users.getUserFromId`, this.userId, res => {
 				if (res.status === "success") {
 					const user = res.data;
 					this.editUser(user);
@@ -151,7 +151,7 @@ export default {
 					timeout: 8000
 				});
 
-			return this.socket.emit(
+			return this.socket.dispatch(
 				`users.updateUsername`,
 				this.user._id,
 				username,
@@ -177,7 +177,7 @@ export default {
 					timeout: 8000
 				});
 
-			return this.socket.emit(
+			return this.socket.dispatch(
 				`users.updateEmail`,
 				this.user._id,
 				email,
@@ -187,7 +187,7 @@ export default {
 			);
 		},
 		updateRole() {
-			this.socket.emit(
+			this.socket.dispatch(
 				`users.updateRole`,
 				this.user._id,
 				this.user.role,
@@ -210,7 +210,7 @@ export default {
 					timeout: 8000
 				});
 
-			return this.socket.emit(
+			return this.socket.dispatch(
 				`users.banUserById`,
 				this.user._id,
 				this.ban.reason,
@@ -221,7 +221,7 @@ export default {
 			);
 		},
 		removeSessions() {
-			this.socket.emit(`users.removeSessions`, this.user._id, res => {
+			this.socket.dispatch(`users.removeSessions`, this.user._id, res => {
 				new Toast({ content: res.message, timeout: 4000 });
 			});
 		},

+ 1 - 1
frontend/src/components/modals/Report.vue

@@ -267,7 +267,7 @@ export default {
 	methods: {
 		create() {
 			console.log(this.report);
-			this.socket.emit("reports.create", this.report, res => {
+			this.socket.dispatch("reports.create", this.report, res => {
 				new Toast({ content: res.message, timeout: 4000 });
 				if (res.status === "success")
 					this.closeModal({

+ 1 - 1
frontend/src/components/modals/ViewPunishment.vue

@@ -101,7 +101,7 @@ export default {
 		io.getSocket(socket => {
 			this.socket = socket;
 
-			this.socket.emit(
+			this.socket.dispatch(
 				`punishments.getPunishmentById`,
 				this.punishmentId,
 				res => {

+ 1 - 1
frontend/src/components/modals/ViewReport.vue

@@ -117,7 +117,7 @@ export default {
 		io.getSocket(socket => {
 			this.socket = socket;
 
-			this.socket.emit(`reports.findOne`, this.reportId, res => {
+			this.socket.dispatch(`reports.findOne`, this.reportId, res => {
 				if (res.status === "success") {
 					const report = res.data;
 					this.viewReport(report);

+ 1 - 1
frontend/src/components/modals/WhatIsNew.vue

@@ -82,7 +82,7 @@ export default {
 	mounted() {
 		io.getSocket(true, socket => {
 			this.socket = socket;
-			this.socket.emit("news.newest", res => {
+			this.socket.dispatch("news.newest", res => {
 				this.news = res.data;
 				if (this.news && localStorage.getItem("firstVisited")) {
 					if (localStorage.getItem("whatIsNew")) {

+ 3 - 3
frontend/src/components/ui/AddToPlaylistDropdown.vue

@@ -60,7 +60,7 @@ export default {
 		io.getSocket(socket => {
 			this.socket = socket;
 
-			this.socket.emit("playlists.indexMyPlaylists", false, res => {
+			this.socket.dispatch("playlists.indexMyPlaylists", false, res => {
 				if (res.status === "success") {
 					this.playlists = res.data;
 					this.checkIfPlaylistsHaveSong();
@@ -95,7 +95,7 @@ export default {
 	methods: {
 		toggleSongInPlaylist(index, playlistId) {
 			if (!this.playlists[index].hasSong) {
-				this.socket.emit(
+				this.socket.dispatch(
 					"playlists.addSongToPlaylist",
 					false,
 					this.song.songId,
@@ -110,7 +110,7 @@ export default {
 					}
 				);
 			} else {
-				this.socket.emit(
+				this.socket.dispatch(
 					"playlists.removeSongFromPlaylist",
 					this.song.songId,
 					playlistId,

+ 73 - 26
frontend/src/io.js

@@ -19,17 +19,22 @@ const callbacks = {
 	}
 };
 
+const callbackss = {};
+let callbackRef = 0;
+
 export default {
 	ready: false,
-
 	socket: null,
+	dispatcher: null,
 
 	getSocket(...args) {
 		if (args[0] === true) {
-			if (this.ready) args[1](this.socket);
-			else callbacks.general.persist.push(args[1]);
-		} else if (this.ready) args[0](this.socket);
-		else callbacks.general.temp.push(args[0]);
+			if (this.ready) {
+				args[1](this.socket);
+			} else callbacks.general.persist.push(args[1]);
+		} else if (this.ready) {
+			args[0](this.socket);
+		} else callbacks.general.temp.push(args[0]);
 	},
 
 	onConnect(...args) {
@@ -64,40 +69,82 @@ export default {
 	},
 
 	init(url) {
-		/* eslint-disable-next-line no-undef */
-		this.socket = window.socket = io(url);
+		class CustomWebSocket extends WebSocket {
+			constructor() {
+				super(url);
+				this.dispatcher = new EventTarget();
+			}
+
+			on(target, cb) {
+				this.dispatcher.addEventListener(target, event =>
+					cb(...event.detail)
+				);
+			}
+
+			dispatch(...args) {
+				callbackRef += 1;
+
+				const cb = args[args.length - 1];
+				if (typeof cb === "function") callbackss[callbackRef] = cb;
 
-		this.socket.on("connect", () => {
+				this.send(
+					JSON.stringify([...args.slice(0, -1), { callbackRef }])
+				);
+			}
+		}
+
+		this.socket = window.socket = new CustomWebSocket(url);
+
+		this.socket.onopen = () => {
 			callbacks.onConnect.temp.forEach(cb => cb());
 			callbacks.onConnect.persist.forEach(cb => cb());
-		});
 
-		this.socket.on("disconnect", () => {
+			this.ready = true;
+
+			callbacks.general.temp.forEach(callback => callback(this.socket));
+			callbacks.general.persist.forEach(callback =>
+				callback(this.socket)
+			);
+
+			callbacks.general.temp = [];
+			callbacks.general.persist = [];
+		};
+
+		this.socket.onmessage = message => {
+			const data = JSON.parse(message.data);
+			const name = data.shift(0);
+
+			if (name === "callbackRef") {
+				const callbackRef = data.shift(0);
+				callbackss[callbackRef](...data);
+				return delete callbackss[callbackRef];
+			}
+
+			return this.socket.dispatcher.dispatchEvent(
+				new CustomEvent(name, {
+					detail: data
+				})
+			);
+		};
+
+		this.socket.onclose = () => {
 			console.log("IO: SOCKET DISCONNECTED");
 			callbacks.onDisconnect.temp.forEach(cb => cb());
 			callbacks.onDisconnect.persist.forEach(cb => cb());
-		});
+		};
 
-		this.socket.on("connect_error", () => {
-			console.log("IO: SOCKET CONNECT ERROR");
-			callbacks.onConnectError.temp.forEach(cb => cb());
-			callbacks.onConnectError.persist.forEach(cb => cb());
-		});
+		// this.socket.on("connect_error", () => {
+		// 	console.log("IO: SOCKET CONNECT ERROR");
+		// 	callbacks.onConnectError.temp.forEach(cb => cb());
+		// 	callbacks.onConnectError.persist.forEach(cb => cb());
+		// });
 
-		this.socket.on("error", err => {
+		this.socket.onerror = err => {
 			console.log("IO: SOCKET ERROR", err);
 			new Toast({
 				content: err,
 				timeout: 8000
 			});
-		});
-
-		this.ready = true;
-
-		callbacks.general.temp.forEach(callback => callback(this.socket));
-		callbacks.general.persist.forEach(callback => callback(this.socket));
-
-		callbacks.general.temp = [];
-		callbacks.general.persist = [];
+		};
 	}
 };

+ 15 - 2
frontend/src/main.js

@@ -148,10 +148,12 @@ lofig.fetchConfig().then(config => {
 		window.stop();
 	}
 
-	const { serverDomain } = config;
-	io.init(serverDomain);
+	// const { serverDomain } = config;
+	io.init("ws://localhost:8080/ws");
 	io.getSocket(socket => {
 		socket.on("ready", (loggedIn, role, username, userId) => {
+			console.log("READY", loggedIn, role, username, userId);
+
 			store.dispatch("user/auth/authData", {
 				loggedIn,
 				role,
@@ -244,3 +246,14 @@ new Vue({
 	el: "#root",
 	render: wrapper => wrapper(App)
 });
+
+// const ws = new WebSocket("ws://localhost:8080/ws");
+
+// ws.onopen = () => {
+// 	// eslint-disable-next-line no-alert
+// 	alert("Now connected");
+// };
+
+// ws.onmessage = e => {
+// 	console.log("Message from server:", e.data);
+// };

+ 2 - 2
frontend/src/mixins/SearchYoutube.vue

@@ -33,7 +33,7 @@ export default {
 				query = query.join("");
 			}
 
-			this.socket.emit("apis.searchYoutube", query, res => {
+			this.socket.dispatch("apis.searchYoutube", query, res => {
 				if (res.status === "success") {
 					this.search.songs.nextPageToken = res.data.nextPageToken;
 					this.search.songs.results = [];
@@ -52,7 +52,7 @@ export default {
 			});
 		},
 		loadMoreSongs() {
-			this.socket.emit(
+			this.socket.dispatch(
 				"apis.searchYoutubeForPage",
 				this.search.songs.query,
 				this.search.songs.nextPageToken,

+ 1 - 1
frontend/src/mixins/SortablePlaylists.vue

@@ -41,7 +41,7 @@ export default {
 			)
 				return; // nothing has changed
 
-			this.socket.emit(
+			this.socket.dispatch(
 				"users.updateOrderOfPlaylists",
 				recalculatedOrder,
 				res => {

+ 2 - 2
frontend/src/pages/Admin/tabs/NewStatistics.vue

@@ -210,7 +210,7 @@ export default {
 	},
 	methods: {
 		init() {
-			this.socket.emit("utils.getModules", data => {
+			this.socket.dispatch("utils.getModules", data => {
 				console.log(data);
 				if (data.status === "success") {
 					this.modules = data.modules;
@@ -218,7 +218,7 @@ export default {
 			});
 
 			if (this.$route.query.moduleName) {
-				this.socket.emit(
+				this.socket.dispatch(
 					"utils.getModule",
 					this.$route.query.moduleName,
 					data => {

+ 19 - 15
frontend/src/pages/Admin/tabs/News.vue

@@ -248,7 +248,7 @@ export default {
 	mounted() {
 		io.getSocket(socket => {
 			this.socket = socket;
-			this.socket.emit("news.index", res => {
+			this.socket.dispatch("news.index", res => {
 				res.data.forEach(news => {
 					this.addNews(news);
 				});
@@ -293,21 +293,25 @@ export default {
 					timeout: 3000
 				});
 
-			return this.socket.emit("news.create", this.creating, result => {
-				new Toast(result.message, 4000);
-				if (result.status === "success")
-					this.creating = {
-						title: "",
-						description: "",
-						bugs: [],
-						features: [],
-						improvements: [],
-						upcoming: []
-					};
-			});
+			return this.socket.dispatch(
+				"news.create",
+				this.creating,
+				result => {
+					new Toast(result.message, 4000);
+					if (result.status === "success")
+						this.creating = {
+							title: "",
+							description: "",
+							bugs: [],
+							features: [],
+							improvements: [],
+							upcoming: []
+						};
+				}
+			);
 		},
 		remove(news) {
-			this.socket.emit(
+			this.socket.dispatch(
 				"news.remove",
 				news,
 				res => new Toast({ content: res.message, timeout: 8000 })
@@ -340,7 +344,7 @@ export default {
 			this.creating[type].splice(index, 1);
 		},
 		init() {
-			this.socket.emit("apis.joinAdminRoom", "news", () => {});
+			this.socket.dispatch("apis.joinAdminRoom", "news", () => {});
 		},
 		...mapActions("modalVisibility", ["openModal", "closeModal"]),
 		...mapActions("admin/news", [

+ 2 - 2
frontend/src/pages/Admin/tabs/Playlists.vue

@@ -92,7 +92,7 @@ export default {
 		// 	this.openModal({ sector: "admin", modal: "editPlaylist" });
 		// },
 		init() {
-			this.socket.emit("playlists.index", res => {
+			this.socket.dispatch("playlists.index", res => {
 				console.log(res);
 				if (res.status === "success") {
 					this.playlists = res.data;
@@ -104,7 +104,7 @@ export default {
 					// }
 				}
 			});
-			this.socket.emit("apis.joinAdminRoom", "playlists", () => {});
+			this.socket.dispatch("apis.joinAdminRoom", "playlists", () => {});
 		},
 		getDateFormatted(createdAt) {
 			const date = new Date(createdAt);

+ 3 - 3
frontend/src/pages/Admin/tabs/Punishments.vue

@@ -137,7 +137,7 @@ export default {
 			this.openModal({ sector: "admin", modal: "viewPunishment" });
 		},
 		banIP() {
-			this.socket.emit(
+			this.socket.dispatch(
 				"punishments.banIP",
 				this.ipBan.ip,
 				this.ipBan.reason,
@@ -148,10 +148,10 @@ export default {
 			);
 		},
 		init() {
-			this.socket.emit("punishments.index", res => {
+			this.socket.dispatch("punishments.index", res => {
 				if (res.status === "success") this.punishments = res.data;
 			});
-			this.socket.emit("apis.joinAdminRoom", "punishments", () => {});
+			this.socket.dispatch("apis.joinAdminRoom", "punishments", () => {});
 		},
 		...mapActions("modalVisibility", ["openModal"]),
 		...mapActions("admin/punishments", ["viewPunishment"])

+ 5 - 5
frontend/src/pages/Admin/tabs/QueueSongs.vue

@@ -261,7 +261,7 @@ export default {
 			this.openModal({ sector: "admin", modal: "editSong" });
 		},
 		add(song) {
-			this.socket.emit("songs.add", song, res => {
+			this.socket.dispatch("songs.add", song, res => {
 				if (res.status === "success")
 					new Toast({ content: res.message, timeout: 2000 });
 				else new Toast({ content: res.message, timeout: 4000 });
@@ -273,7 +273,7 @@ export default {
 				"Are you sure you want to delete this song?"
 			);
 			if (dialogResult !== true) return;
-			this.socket.emit("queueSongs.remove", id, res => {
+			this.socket.dispatch("queueSongs.remove", id, res => {
 				if (res.status === "success")
 					new Toast({ content: res.message, timeout: 2000 });
 				else new Toast({ content: res.message, timeout: 4000 });
@@ -284,7 +284,7 @@ export default {
 			if (this.position >= this.maxPosition) return;
 			this.isGettingSet = true;
 
-			this.socket.emit("queueSongs.getSet", this.position, data => {
+			this.socket.dispatch("queueSongs.getSet", this.position, data => {
 				data.forEach(song => this.songs.push(song));
 
 				this.position += 1;
@@ -309,13 +309,13 @@ export default {
 			if (this.songs.length > 0)
 				this.position = Math.ceil(this.songs.length / 15) + 1;
 
-			this.socket.emit("queueSongs.length", length => {
+			this.socket.dispatch("queueSongs.length", length => {
 				this.maxPosition = Math.ceil(length / 15) + 1;
 
 				this.getSet();
 			});
 
-			this.socket.emit("apis.joinAdminRoom", "queue", () => {});
+			this.socket.dispatch("apis.joinAdminRoom", "queue", () => {});
 		},
 		// ...mapActions("admin/songs", ["editSong"]),
 		...mapActions("modals/editSong", ["stopVideo"]),

+ 14 - 10
frontend/src/pages/Admin/tabs/Reports.vue

@@ -94,7 +94,7 @@ export default {
 			this.socket = socket;
 			if (this.socket.connected) this.init();
 
-			this.socket.emit("reports.index", res => {
+			this.socket.dispatch("reports.index", res => {
 				this.reports = res.data;
 			});
 
@@ -114,20 +114,24 @@ export default {
 		});
 
 		if (this.$route.query.id) {
-			this.socket.emit("reports.findOne", this.$route.query.id, res => {
-				if (res.status === "success") this.view(res.data);
-				else
-					new Toast({
-						content: "Report with that ID not found",
-						timeout: 3000
-					});
-			});
+			this.socket.dispatch(
+				"reports.findOne",
+				this.$route.query.id,
+				res => {
+					if (res.status === "success") this.view(res.data);
+					else
+						new Toast({
+							content: "Report with that ID not found",
+							timeout: 3000
+						});
+				}
+			);
 		}
 	},
 	methods: {
 		formatDistance,
 		init() {
-			this.socket.emit("apis.joinAdminRoom", "reports", () => {});
+			this.socket.dispatch("apis.joinAdminRoom", "reports", () => {});
 		},
 		view(report) {
 			// this.viewReport(report);

+ 5 - 5
frontend/src/pages/Admin/tabs/Songs.vue

@@ -329,7 +329,7 @@ export default {
 		});
 
 		if (this.$route.query.songId) {
-			this.socket.emit(
+			this.socket.dispatch(
 				"songs.getSongFromMusareId",
 				this.$route.query.songId,
 				res => {
@@ -356,7 +356,7 @@ export default {
 				"Are you sure you want to delete this song?"
 			);
 			if (dialogResult !== true) return;
-			this.socket.emit("songs.remove", id, res => {
+			this.socket.dispatch("songs.remove", id, res => {
 				if (res.status === "success")
 					new Toast({ content: res.message, timeout: 4000 });
 				else new Toast({ content: res.message, timeout: 8000 });
@@ -367,7 +367,7 @@ export default {
 			if (this.position >= this.maxPosition) return;
 			this.isGettingSet = true;
 
-			this.socket.emit("songs.getSet", this.position, data => {
+			this.socket.dispatch("songs.getSet", this.position, data => {
 				data.forEach(song => {
 					this.addSong(song);
 				});
@@ -404,13 +404,13 @@ export default {
 			if (this.songs.length > 0)
 				this.position = Math.ceil(this.songs.length / 15) + 1;
 
-			this.socket.emit("songs.length", length => {
+			this.socket.dispatch("songs.length", length => {
 				this.maxPosition = Math.ceil(length / 15) + 1;
 
 				this.getSet();
 			});
 
-			this.socket.emit("apis.joinAdminRoom", "songs", () => {});
+			this.socket.dispatch("apis.joinAdminRoom", "songs", () => {});
 		},
 		...mapActions("admin/songs", [
 			// "stopVideo",

+ 4 - 4
frontend/src/pages/Admin/tabs/Stations.vue

@@ -255,7 +255,7 @@ export default {
 					timeout: 3000
 				});
 
-			return this.socket.emit(
+			return this.socket.dispatch(
 				"stations.create",
 				{
 					name,
@@ -276,7 +276,7 @@ export default {
 			);
 		},
 		removeStation(index) {
-			this.socket.emit(
+			this.socket.dispatch(
 				"stations.remove",
 				this.stations[index]._id,
 				res => {
@@ -339,10 +339,10 @@ export default {
 			this.newStation.blacklistedGenres.splice(index, 1);
 		},
 		init() {
-			this.socket.emit("stations.index", data => {
+			this.socket.dispatch("stations.index", data => {
 				this.loadStations(data.stations);
 			});
-			this.socket.emit("apis.joinAdminRoom", "stations", () => {});
+			this.socket.dispatch("apis.joinAdminRoom", "stations", () => {});
 		},
 		...mapActions("modalVisibility", ["openModal"]),
 		...mapActions("admin/stations", [

+ 1 - 1
frontend/src/pages/Admin/tabs/Statistics.vue

@@ -271,7 +271,7 @@ export default {
 	},
 	methods: {
 		init() {
-			this.socket.emit("apis.joinAdminRoom", "statistics", () => {});
+			this.socket.dispatch("apis.joinAdminRoom", "statistics", () => {});
 			this.socket.on(
 				"event:admin.statistics.success.units.minute",
 				units => {

+ 2 - 2
frontend/src/pages/Admin/tabs/Users.vue

@@ -98,7 +98,7 @@ export default {
 			this.openModal({ sector: "admin", modal: "editUser" });
 		},
 		init() {
-			this.socket.emit("users.index", res => {
+			this.socket.dispatch("users.index", res => {
 				console.log(res);
 				if (res.status === "success") {
 					this.users = res.data;
@@ -110,7 +110,7 @@ export default {
 					}
 				}
 			});
-			this.socket.emit("apis.joinAdminRoom", "users", () => {});
+			this.socket.dispatch("apis.joinAdminRoom", "users", () => {});
 		},
 		...mapActions("modalVisibility", ["openModal"])
 	}

+ 26 - 18
frontend/src/pages/Home.vue

@@ -656,7 +656,7 @@ export default {
 	},
 	methods: {
 		init() {
-			this.socket.emit("stations.index", data => {
+			this.socket.dispatch("stations.index", data => {
 				this.stations = [];
 
 				if (data.status === "success")
@@ -678,7 +678,7 @@ export default {
 					});
 			});
 
-			this.socket.emit("apis.joinRoom", "home", () => {});
+			this.socket.dispatch("apis.joinRoom", "home", () => {});
 		},
 		isOwner(station) {
 			return station.owner === this.userId;
@@ -687,24 +687,32 @@ export default {
 			return typeof station.currentSong.title !== "undefined";
 		},
 		favoriteStation(station) {
-			this.socket.emit("stations.favoriteStation", station._id, res => {
-				if (res.status === "success") {
-					new Toast({
-						content: "Successfully favorited station.",
-						timeout: 4000
-					});
-				} else new Toast({ content: res.message, timeout: 8000 });
-			});
+			this.socket.dispatch(
+				"stations.favoriteStation",
+				station._id,
+				res => {
+					if (res.status === "success") {
+						new Toast({
+							content: "Successfully favorited station.",
+							timeout: 4000
+						});
+					} else new Toast({ content: res.message, timeout: 8000 });
+				}
+			);
 		},
 		unfavoriteStation(station) {
-			this.socket.emit("stations.unfavoriteStation", station._id, res => {
-				if (res.status === "success") {
-					new Toast({
-						content: "Successfully unfavorited station.",
-						timeout: 4000
-					});
-				} else new Toast({ content: res.message, timeout: 8000 });
-			});
+			this.socket.dispatch(
+				"stations.unfavoriteStation",
+				station._id,
+				res => {
+					if (res.status === "success") {
+						new Toast({
+							content: "Successfully unfavorited station.",
+							timeout: 4000
+						});
+					} else new Toast({ content: res.message, timeout: 8000 });
+				}
+			);
 		},
 		...mapActions("modalVisibility", ["openModal"]),
 		...mapActions("station", ["updateIfStationIsFavorited"])

+ 1 - 1
frontend/src/pages/News.vue

@@ -101,7 +101,7 @@ export default {
 	mounted() {
 		io.getSocket(socket => {
 			this.socket = socket;
-			this.socket.emit("news.index", res => {
+			this.socket.dispatch("news.index", res => {
 				this.news = res.data;
 				if (this.news.length === 0) this.noFound = true;
 			});

+ 3 - 2
frontend/src/pages/Profile/index.vue

@@ -136,11 +136,12 @@ export default {
 		io.getSocket(socket => {
 			this.socket = socket;
 
-			this.socket.emit(
+			this.socket.dispatch(
 				"users.findByUsername",
 				this.$route.params.username,
 				res => {
-					if (res.status === "error") this.$router.go("/404");
+					if (res.status === "error" || res.status === "failure")
+						this.$router.push("/404");
 					else {
 						this.user = res.data;
 

+ 2 - 2
frontend/src/pages/Profile/tabs/Playlists.vue

@@ -136,14 +136,14 @@ export default {
 			this.socket = socket;
 
 			if (this.myUserId !== this.userId) {
-				this.socket.emit(
+				this.socket.dispatch(
 					"apis.joinRoom",
 					`profile-${this.userId}-playlists`,
 					() => {}
 				);
 			}
 
-			this.socket.emit("playlists.indexForUser", this.userId, res => {
+			this.socket.dispatch("playlists.indexForUser", this.userId, res => {
 				if (res.status === "success") this.setPlaylists(res.data);
 				this.orderOfPlaylists = this.calculatePlaylistOrder(); // order in regards to the database
 			});

+ 4 - 4
frontend/src/pages/Profile/tabs/RecentActivity.vue

@@ -77,14 +77,14 @@ export default {
 			this.socket = socket;
 
 			if (this.myUserId !== this.userId) {
-				this.socket.emit(
+				this.socket.dispatch(
 					"apis.joinRoom",
 					`profile-${this.userId}-activities`,
 					() => {}
 				);
 			}
 
-			this.socket.emit("activities.length", this.userId, length => {
+			this.socket.dispatch("activities.length", this.userId, length => {
 				this.maxPosition = Math.ceil(length / 15) + 1;
 				this.getSet();
 			});
@@ -112,7 +112,7 @@ export default {
 	},
 	methods: {
 		hideActivity(activityId) {
-			this.socket.emit("activities.hideActivity", activityId, res => {
+			this.socket.dispatch("activities.hideActivity", activityId, res => {
 				if (res.status !== "success")
 					new Toast({ content: res.message, timeout: 3000 });
 			});
@@ -123,7 +123,7 @@ export default {
 
 			this.isGettingSet = true;
 
-			this.socket.emit(
+			this.socket.dispatch(
 				"activities.getSet",
 				this.userId,
 				this.position,

+ 4 - 4
frontend/src/pages/ResetPassword.vue

@@ -345,7 +345,7 @@ export default {
 			this.hasEmailBeenSentAlready = false;
 
 			if (this.mode === "set") {
-				return this.socket.emit("users.requestPassword", res => {
+				return this.socket.dispatch("users.requestPassword", res => {
 					new Toast({ content: res.message, timeout: 8000 });
 					if (res.status === "success") {
 						this.step = 2;
@@ -353,7 +353,7 @@ export default {
 				});
 			}
 
-			return this.socket.emit(
+			return this.socket.dispatch(
 				"users.requestPasswordReset",
 				this.email,
 				res => {
@@ -372,7 +372,7 @@ export default {
 					timeout: 8000
 				});
 
-			return this.socket.emit(
+			return this.socket.dispatch(
 				this.mode === "set"
 					? "users.verifyPasswordCode"
 					: "users.verifyPasswordResetCode",
@@ -401,7 +401,7 @@ export default {
 					timeout: 8000
 				});
 
-			return this.socket.emit(
+			return this.socket.dispatch(
 				this.mode === "set"
 					? "users.changePasswordWithCode"
 					: "users.changePasswordWithResetCode",

+ 1 - 1
frontend/src/pages/Settings/index.vue

@@ -90,7 +90,7 @@ export default {
 		io.getSocket(socket => {
 			this.socket = socket;
 
-			this.socket.emit("users.findBySession", res => {
+			this.socket.dispatch("users.findBySession", res => {
 				if (res.status === "success") {
 					this.setUser(res.data);
 				} else {

+ 5 - 5
frontend/src/pages/Settings/tabs/Account.vue

@@ -214,7 +214,7 @@ export default {
 
 			this.$refs.saveButton.saveStatus = "disabled";
 
-			return this.socket.emit(
+			return this.socket.dispatch(
 				"users.updateEmail",
 				this.userId,
 				email,
@@ -256,7 +256,7 @@ export default {
 
 			this.$refs.saveButton.saveStatus = "disabled";
 
-			return this.socket.emit(
+			return this.socket.dispatch(
 				"users.updateUsername",
 				this.userId,
 				username,
@@ -281,9 +281,9 @@ export default {
 			);
 		},
 		removeAccount() {
-			return this.socket.emit("users.remove", res => {
+			return this.socket.dispatch("users.remove", res => {
 				if (res.status === "success") {
-					return this.socket.emit("users.logout", () => {
+					return this.socket.dispatch("users.logout", () => {
 						return lofig.get("cookie").then(cookie => {
 							document.cookie = `${cookie.SIDname}=;expires=Thu, 01 Jan 1970 00:00:01 GMT;`;
 							return window.location.reload();
@@ -295,7 +295,7 @@ export default {
 			});
 		},
 		removeActivities() {
-			this.socket.emit("activities.removeAllForUser", res => {
+			this.socket.dispatch("activities.removeAllForUser", res => {
 				new Toast({ content: res.message, timeout: 4000 });
 			});
 		},

+ 2 - 2
frontend/src/pages/Settings/tabs/Preferences.vue

@@ -65,7 +65,7 @@ export default {
 		io.getSocket(socket => {
 			this.socket = socket;
 
-			this.socket.emit("users.getPreferences", res => {
+			this.socket.dispatch("users.getPreferences", res => {
 				if (res.status === "success") {
 					this.localNightmode = res.data.nightmode;
 					this.localAutoSkipDisliked = res.data.autoSkipDisliked;
@@ -97,7 +97,7 @@ export default {
 
 			this.$refs.saveButton.status = "disabled";
 
-			return this.socket.emit(
+			return this.socket.dispatch(
 				"users.updatePreferences",
 				{
 					nightmode: this.localNightmode,

+ 4 - 4
frontend/src/pages/Settings/tabs/Profile.vue

@@ -149,7 +149,7 @@ export default {
 
 			this.$refs.saveButton.status = "disabled";
 
-			return this.socket.emit(
+			return this.socket.dispatch(
 				"users.updateName",
 				this.userId,
 				name,
@@ -184,7 +184,7 @@ export default {
 
 			this.$refs.saveButton.status = "disabled";
 
-			return this.socket.emit(
+			return this.socket.dispatch(
 				"users.updateLocation",
 				this.userId,
 				location,
@@ -219,7 +219,7 @@ export default {
 
 			this.$refs.saveButton.status = "disabled";
 
-			return this.socket.emit(
+			return this.socket.dispatch(
 				"users.updateBio",
 				this.userId,
 				bio,
@@ -248,7 +248,7 @@ export default {
 
 			this.$refs.saveButton.status = "disabled";
 
-			return this.socket.emit(
+			return this.socket.dispatch(
 				"users.updateAvatarType",
 				this.userId,
 				avatar,

+ 4 - 4
frontend/src/pages/Settings/tabs/Security.vue

@@ -220,7 +220,7 @@ export default {
 					timeout: 8000
 				});
 
-			return this.socket.emit(
+			return this.socket.dispatch(
 				"users.updatePassword",
 				this.previousPassword,
 				newPassword,
@@ -240,17 +240,17 @@ export default {
 			);
 		},
 		unlinkPassword() {
-			this.socket.emit("users.unlinkPassword", res => {
+			this.socket.dispatch("users.unlinkPassword", res => {
 				new Toast({ content: res.message, timeout: 8000 });
 			});
 		},
 		unlinkGitHub() {
-			this.socket.emit("users.unlinkGitHub", res => {
+			this.socket.dispatch("users.unlinkGitHub", res => {
 				new Toast({ content: res.message, timeout: 8000 });
 			});
 		},
 		removeSessions() {
-			this.socket.emit(`users.removeSessions`, this.userId, res => {
+			this.socket.dispatch(`users.removeSessions`, this.userId, res => {
 				new Toast({ content: res.message, timeout: 4000 });
 			});
 		}

+ 3 - 3
frontend/src/pages/Station/components/Sidebar/MyPlaylists.vue

@@ -88,7 +88,7 @@ export default {
 			this.socket = socket;
 
 			/** Get playlists for user */
-			this.socket.emit("playlists.indexMyPlaylists", true, res => {
+			this.socket.dispatch("playlists.indexMyPlaylists", true, res => {
 				if (res.status === "success") this.playlists = res.data;
 				this.orderOfPlaylists = this.calculatePlaylistOrder(); // order in regards to the database
 			});
@@ -164,7 +164,7 @@ export default {
 			this.openModal({ sector: "station", modal: "editPlaylist" });
 		},
 		selectPlaylist(id) {
-			this.socket.emit(
+			this.socket.dispatch(
 				"stations.selectPrivatePlaylist",
 				this.station._id,
 				id,
@@ -179,7 +179,7 @@ export default {
 			);
 		},
 		deselectPlaylist() {
-			this.socket.emit(
+			this.socket.dispatch(
 				"stations.deselectPrivatePlaylist",
 				this.station._id,
 				res => {

+ 1 - 1
frontend/src/pages/Station/components/Sidebar/Queue/index.vue

@@ -118,7 +118,7 @@ export default {
 			return this.loggedIn && this.userRole === "admin";
 		},
 		removeFromQueue(songId) {
-			window.socket.emit(
+			window.socket.dispatch(
 				"stations.removeFromQueue",
 				this.station._id,
 				songId,

+ 262 - 230
frontend/src/pages/Station/index.vue

@@ -601,13 +601,13 @@ export default {
 			if (this.socket.connected) this.join();
 			io.onConnect(this.join);
 
-			this.socket.emit(
+			this.socket.dispatch(
 				"stations.existsByName",
 				this.stationIdentifier,
 				res => {
 					if (res.status === "failure" || !res.exists) {
 						// station identifier may be using stationid instead
-						this.socket.emit(
+						this.socket.dispatch(
 							"stations.existsById",
 							this.stationIdentifier,
 							res => {
@@ -650,7 +650,7 @@ export default {
 					if (!this.playerReady) this.youtubeReady();
 					else this.playVideo();
 
-					this.socket.emit(
+					this.socket.dispatch(
 						"songs.getOwnSongRatings",
 						data.currentSong.songId,
 						song => {
@@ -882,7 +882,7 @@ export default {
 			return this.isOwnerOnly() || this.isAdminOnly();
 		},
 		removeFromQueue(songId) {
-			window.socket.emit(
+			window.socket.dispatch(
 				"stations.removeFromQueue",
 				this.station._id,
 				songId,
@@ -1176,14 +1176,18 @@ export default {
 				this.timeElapsed = utils.formatTime(duration);
 		},
 		toggleLock() {
-			window.socket.emit("stations.toggleLock", this.station._id, res => {
-				if (res.status === "success") {
-					new Toast({
-						content: "Successfully toggled the queue lock.",
-						timeout: 4000
-					});
-				} else new Toast({ content: res.message, timeout: 8000 });
-			});
+			window.socket.dispatch(
+				"stations.toggleLock",
+				this.station._id,
+				res => {
+					if (res.status === "success") {
+						new Toast({
+							content: "Successfully toggled the queue lock.",
+							timeout: 4000
+						});
+					} else new Toast({ content: res.message, timeout: 8000 });
+				}
+			);
 		},
 		changeVolume() {
 			const volume = this.volumeSliderValue;
@@ -1223,36 +1227,45 @@ export default {
 			}
 		},
 		skipStation() {
-			this.socket.emit("stations.forceSkip", this.station._id, data => {
-				if (data.status !== "success")
-					new Toast({
-						content: `Error: ${data.message}`,
-						timeout: 8000
-					});
-				else
-					new Toast({
-						content:
-							"Successfully skipped the station's current song.",
-						timeout: 4000
-					});
-			});
+			this.socket.dispatch(
+				"stations.forceSkip",
+				this.station._id,
+				data => {
+					if (data.status !== "success")
+						new Toast({
+							content: `Error: ${data.message}`,
+							timeout: 8000
+						});
+					else
+						new Toast({
+							content:
+								"Successfully skipped the station's current song.",
+							timeout: 4000
+						});
+				}
+			);
 		},
 		voteSkipStation() {
-			this.socket.emit("stations.voteSkip", this.station._id, data => {
-				if (data.status !== "success")
-					new Toast({
-						content: `Error: ${data.message}`,
-						timeout: 8000
-					});
-				else
-					new Toast({
-						content: "Successfully voted to skip the current song.",
-						timeout: 4000
-					});
-			});
+			this.socket.dispatch(
+				"stations.voteSkip",
+				this.station._id,
+				data => {
+					if (data.status !== "success")
+						new Toast({
+							content: `Error: ${data.message}`,
+							timeout: 8000
+						});
+					else
+						new Toast({
+							content:
+								"Successfully voted to skip the current song.",
+							timeout: 4000
+						});
+				}
+			);
 		},
 		resumeStation() {
-			this.socket.emit("stations.resume", this.station._id, data => {
+			this.socket.dispatch("stations.resume", this.station._id, data => {
 				if (data.status !== "success")
 					new Toast({
 						content: `Error: ${data.message}`,
@@ -1266,7 +1279,7 @@ export default {
 			});
 		},
 		pauseStation() {
-			this.socket.emit("stations.pause", this.station._id, data => {
+			this.socket.dispatch("stations.pause", this.station._id, data => {
 				if (data.status !== "success")
 					new Toast({
 						content: `Error: ${data.message}`,
@@ -1309,7 +1322,7 @@ export default {
 		},
 		toggleLike() {
 			if (this.liked)
-				this.socket.emit(
+				this.socket.dispatch(
 					"songs.unlike",
 					this.currentSong.songId,
 					data => {
@@ -1321,7 +1334,7 @@ export default {
 					}
 				);
 			else
-				this.socket.emit(
+				this.socket.dispatch(
 					"songs.like",
 					this.currentSong.songId,
 					data => {
@@ -1335,7 +1348,7 @@ export default {
 		},
 		toggleDislike() {
 			if (this.disliked)
-				return this.socket.emit(
+				return this.socket.dispatch(
 					"songs.undislike",
 					this.currentSong.songId,
 					data => {
@@ -1347,7 +1360,7 @@ export default {
 					}
 				);
 
-			return this.socket.emit(
+			return this.socket.dispatch(
 				"songs.dislike",
 				this.currentSong.songId,
 				data => {
@@ -1369,7 +1382,7 @@ export default {
 					if (queueSong.requestedBy === this.userId) isInQueue = true;
 				});
 				if (!isInQueue && this.privatePlaylistQueueSelected) {
-					this.socket.emit(
+					this.socket.dispatch(
 						"playlists.getFirstSong",
 						this.privatePlaylistQueueSelected,
 						data => {
@@ -1378,13 +1391,13 @@ export default {
 									if (data.song.duration < 15 * 60) {
 										this.automaticallyRequestedSongId =
 											data.song.songId;
-										this.socket.emit(
+										this.socket.dispatch(
 											"stations.addToQueue",
 											this.station._id,
 											data.song.songId,
 											data2 => {
 												if (data2.status === "success")
-													this.socket.emit(
+													this.socket.dispatch(
 														"playlists.moveSongToBottom",
 														this
 															.privatePlaylistQueueSelected,
@@ -1398,7 +1411,7 @@ export default {
 											timeout: 3000
 										});
 
-										this.socket.emit(
+										this.socket.dispatch(
 											"playlists.moveSongToBottom",
 											this.privatePlaylistQueueSelected,
 											data.song.songId,
@@ -1430,223 +1443,242 @@ export default {
 			this.$refs.playerDebugBox.resetBox();
 		},
 		join() {
-			this.socket.emit("stations.join", this.stationIdentifier, res => {
-				if (res.status === "success") {
-					setTimeout(() => {
-						this.loading = false;
-					}, 1000); // prevents popping in of youtube embed etc.
-
-					const {
-						_id,
-						displayName,
-						name,
-						description,
-						privacy,
-						locked,
-						partyMode,
-						owner,
-						privatePlaylist,
-						type,
-						genres,
-						blacklistedGenres,
-						isFavorited,
-						theme
-					} = res.data;
-
-					// change url to use station name instead of station id
-					if (name !== this.stationIdentifier) {
-						// eslint-disable-next-line no-restricted-globals
-						history.pushState({}, null, name);
-					}
+			this.socket.dispatch(
+				"stations.join",
+				this.stationIdentifier,
+				res => {
+					if (res.status === "success") {
+						setTimeout(() => {
+							this.loading = false;
+						}, 1000); // prevents popping in of youtube embed etc.
+
+						const {
+							_id,
+							displayName,
+							name,
+							description,
+							privacy,
+							locked,
+							partyMode,
+							owner,
+							privatePlaylist,
+							type,
+							genres,
+							blacklistedGenres,
+							isFavorited,
+							theme
+						} = res.data;
+
+						// change url to use station name instead of station id
+						if (name !== this.stationIdentifier) {
+							// eslint-disable-next-line no-restricted-globals
+							history.pushState({}, null, name);
+						}
 
-					this.joinStation({
-						_id,
-						name,
-						displayName,
-						description,
-						privacy,
-						locked,
-						partyMode,
-						owner,
-						privatePlaylist,
-						type,
-						genres,
-						blacklistedGenres,
-						isFavorited,
-						theme
-					});
+						this.joinStation({
+							_id,
+							name,
+							displayName,
+							description,
+							privacy,
+							locked,
+							partyMode,
+							owner,
+							privatePlaylist,
+							type,
+							genres,
+							blacklistedGenres,
+							isFavorited,
+							theme
+						});
+
+						const currentSong = res.data.currentSong
+							? res.data.currentSong
+							: {};
+
+						if (currentSong.artists)
+							currentSong.artists = currentSong.artists.join(
+								", "
+							);
+
+						if (currentSong && !currentSong.thumbnail)
+							currentSong.ytThumbnail = `https://img.youtube.com/vi/${currentSong.songId}/mqdefault.jpg`;
+
+						this.updateCurrentSong(currentSong);
+
+						this.startedAt = res.data.startedAt;
+						this.updateStationPaused(res.data.paused);
+						this.timePaused = res.data.timePaused;
+						this.updateUserCount(res.data.userCount);
+						this.updateUsers(res.data.users);
+						this.pausedAt = res.data.pausedAt;
 
-					const currentSong = res.data.currentSong
-						? res.data.currentSong
-						: {};
-
-					if (currentSong.artists)
-						currentSong.artists = currentSong.artists.join(", ");
-
-					if (currentSong && !currentSong.thumbnail)
-						currentSong.ytThumbnail = `https://img.youtube.com/vi/${currentSong.songId}/mqdefault.jpg`;
-
-					this.updateCurrentSong(currentSong);
-
-					this.startedAt = res.data.startedAt;
-					this.updateStationPaused(res.data.paused);
-					this.timePaused = res.data.timePaused;
-					this.updateUserCount(res.data.userCount);
-					this.updateUsers(res.data.users);
-					this.pausedAt = res.data.pausedAt;
-
-					if (res.data.currentSong) {
-						this.updateNoSong(false);
-						this.youtubeReady();
-						this.playVideo();
-						this.socket.emit(
-							"songs.getOwnSongRatings",
-							res.data.currentSong.songId,
-							data => {
-								if (this.currentSong.songId === data.songId) {
-									this.liked = data.liked;
-									this.disliked = data.disliked;
+						if (res.data.currentSong) {
+							this.updateNoSong(false);
+							this.youtubeReady();
+							this.playVideo();
+							this.socket.dispatch(
+								"songs.getOwnSongRatings",
+								res.data.currentSong.songId,
+								data => {
+									if (
+										this.currentSong.songId === data.songId
+									) {
+										this.liked = data.liked;
+										this.disliked = data.disliked;
+									}
 								}
-							}
-						);
-					} else {
-						if (this.playerReady) this.player.pauseVideo();
-						this.updateNoSong(true);
-					}
+							);
+						} else {
+							if (this.playerReady) this.player.pauseVideo();
+							this.updateNoSong(true);
+						}
 
-					if (type === "community" && partyMode === true) {
-						this.socket.emit("stations.getQueue", _id, res => {
-							if (res.status === "success") {
-								this.updateSongsList(res.queue);
-							}
-						});
-					}
+						if (type === "community" && partyMode === true) {
+							this.socket.dispatch(
+								"stations.getQueue",
+								_id,
+								res => {
+									if (res.status === "success") {
+										this.updateSongsList(res.queue);
+									}
+								}
+							);
+						}
+
+						if (this.isOwnerOrAdmin()) {
+							keyboardShortcuts.registerShortcut(
+								"station.pauseResume",
+								{
+									keyCode: 32,
+									shift: false,
+									ctrl: true,
+									preventDefault: true,
+									handler: () => {
+										if (this.stationPaused)
+											this.resumeStation();
+										else this.pauseStation();
+									}
+								}
+							);
+
+							keyboardShortcuts.registerShortcut(
+								"station.skipStation",
+								{
+									keyCode: 39,
+									shift: false,
+									ctrl: true,
+									preventDefault: true,
+									handler: () => {
+										this.skipStation();
+									}
+								}
+							);
+						}
 
-					if (this.isOwnerOrAdmin()) {
 						keyboardShortcuts.registerShortcut(
-							"station.pauseResume",
+							"station.lowerVolumeLarge",
 							{
-								keyCode: 32,
+								keyCode: 40,
 								shift: false,
 								ctrl: true,
 								preventDefault: true,
 								handler: () => {
-									if (this.stationPaused)
-										this.resumeStation();
-									else this.pauseStation();
+									this.volumeSliderValue -= 1000;
+									this.changeVolume();
 								}
 							}
 						);
 
 						keyboardShortcuts.registerShortcut(
-							"station.skipStation",
+							"station.lowerVolumeSmall",
 							{
-								keyCode: 39,
-								shift: false,
+								keyCode: 40,
+								shift: true,
 								ctrl: true,
 								preventDefault: true,
 								handler: () => {
-									this.skipStation();
+									this.volumeSliderValue -= 100;
+									this.changeVolume();
 								}
 							}
 						);
-					}
-
-					keyboardShortcuts.registerShortcut(
-						"station.lowerVolumeLarge",
-						{
-							keyCode: 40,
-							shift: false,
-							ctrl: true,
-							preventDefault: true,
-							handler: () => {
-								this.volumeSliderValue -= 1000;
-								this.changeVolume();
-							}
-						}
-					);
 
-					keyboardShortcuts.registerShortcut(
-						"station.lowerVolumeSmall",
-						{
-							keyCode: 40,
-							shift: true,
-							ctrl: true,
-							preventDefault: true,
-							handler: () => {
-								this.volumeSliderValue -= 100;
-								this.changeVolume();
+						keyboardShortcuts.registerShortcut(
+							"station.increaseVolumeLarge",
+							{
+								keyCode: 38,
+								shift: false,
+								ctrl: true,
+								preventDefault: true,
+								handler: () => {
+									this.volumeSliderValue += 1000;
+									this.changeVolume();
+								}
 							}
-						}
-					);
+						);
 
-					keyboardShortcuts.registerShortcut(
-						"station.increaseVolumeLarge",
-						{
-							keyCode: 38,
-							shift: false,
-							ctrl: true,
-							preventDefault: true,
-							handler: () => {
-								this.volumeSliderValue += 1000;
-								this.changeVolume();
+						keyboardShortcuts.registerShortcut(
+							"station.increaseVolumeSmall",
+							{
+								keyCode: 38,
+								shift: true,
+								ctrl: true,
+								preventDefault: true,
+								handler: () => {
+									this.volumeSliderValue += 100;
+									this.changeVolume();
+								}
 							}
-						}
-					);
+						);
 
-					keyboardShortcuts.registerShortcut(
-						"station.increaseVolumeSmall",
-						{
-							keyCode: 38,
-							shift: true,
-							ctrl: true,
-							preventDefault: true,
-							handler: () => {
-								this.volumeSliderValue += 100;
-								this.changeVolume();
+						keyboardShortcuts.registerShortcut(
+							"station.toggleDebug",
+							{
+								keyCode: 68,
+								shift: false,
+								ctrl: true,
+								preventDefault: true,
+								handler: () => {
+									this.togglePlayerDebugBox();
+								}
 							}
-						}
-					);
-
-					keyboardShortcuts.registerShortcut("station.toggleDebug", {
-						keyCode: 68,
-						shift: false,
-						ctrl: true,
-						preventDefault: true,
-						handler: () => {
-							this.togglePlayerDebugBox();
-						}
-					});
+						);
 
-					// UNIX client time before ping
-					const beforePing = Date.now();
-					this.socket.emit("apis.ping", pong => {
-						// UNIX client time after ping
-						const afterPing = Date.now();
-						// Average time in MS it took between the server responding and the client receiving
-						const connectionLatency = (afterPing - beforePing) / 2;
-						console.log(connectionLatency, beforePing - afterPing);
-						// UNIX server time
-						const serverDate = pong.date;
-						// Difference between the server UNIX time and the client UNIX time after ping, with the connectionLatency added to the server UNIX time
-						const difference =
-							serverDate + connectionLatency - afterPing;
-						console.log("Difference: ", difference);
-						if (difference > 3000 || difference < -3000) {
+						// UNIX client time before ping
+						const beforePing = Date.now();
+						this.socket.dispatch("apis.ping", pong => {
+							// UNIX client time after ping
+							const afterPing = Date.now();
+							// Average time in MS it took between the server responding and the client receiving
+							const connectionLatency =
+								(afterPing - beforePing) / 2;
 							console.log(
-								"System time difference is bigger than 3 seconds."
+								connectionLatency,
+								beforePing - afterPing
 							);
-						}
-						this.systemDifference = difference;
-					});
-				} else {
-					this.loading = false;
-					this.exists = false;
+							// UNIX server time
+							const serverDate = pong.date;
+							// Difference between the server UNIX time and the client UNIX time after ping, with the connectionLatency added to the server UNIX time
+							const difference =
+								serverDate + connectionLatency - afterPing;
+							console.log("Difference: ", difference);
+							if (difference > 3000 || difference < -3000) {
+								console.log(
+									"System time difference is bigger than 3 seconds."
+								);
+							}
+							this.systemDifference = difference;
+						});
+					} else {
+						this.loading = false;
+						this.exists = false;
+					}
 				}
-			});
+			);
 		},
 		favoriteStation() {
-			this.socket.emit(
+			this.socket.dispatch(
 				"stations.favoriteStation",
 				this.station._id,
 				res => {
@@ -1660,7 +1692,7 @@ export default {
 			);
 		},
 		unfavoriteStation() {
-			this.socket.emit(
+			this.socket.dispatch(
 				"stations.unfavoriteStation",
 				this.station._id,
 				res => {

+ 1 - 1
frontend/src/store/modules/user.js

@@ -117,7 +117,7 @@ const modules = {
 						if (state.userIdRequested[`Z${userId}`] !== true) {
 							commit("requestingUserId", userId);
 							io.getSocket(socket => {
-								socket.emit(
+								socket.dispatch(
 									"users.getUsernameFromId",
 									userId,
 									res => {