Преглед на файлове

refactor: implemented all auth data into Vuex and fixed a few small bugs

Kristian Vos преди 5 години
родител
ревизия
dfb6effdb5

+ 5 - 28
frontend/App.vue

@@ -26,38 +26,26 @@ import WhatIsNew from "./components/Modals/WhatIsNew.vue";
 import MobileAlert from "./components/Modals/MobileAlert.vue";
 import LoginModal from "./components/Modals/Login.vue";
 import RegisterModal from "./components/Modals/Register.vue";
-import auth from "./auth";
 import io from "./io";
 
 export default {
 	replace: false,
 	data() {
 		return {
-			banned: false,
-			ban: {},
-			loggedIn: false,
-			role: "",
-			username: "",
-			userId: "",
 			serverDomain: "",
 			socketConnected: true
 		};
 	},
 	computed: mapState({
+		loggedIn: state => state.user.auth.loggedIn,
+		role: state => state.user.auth.role,
+		username: state => state.user.auth.username,
+		userId: state => state.user.auth.userId,
+		banned: state => state.user.auth.banned,
 		modals: state => state.modals.modals,
 		currentlyActive: state => state.modals.currentlyActive
 	}),
 	methods: {
-		logout() {
-			const _this = this;
-			_this.socket.emit("users.logout", result => {
-				if (result.status === "success") {
-					document.cookie =
-						"SID=;expires=Thu, 01 Jan 1970 00:00:01 GMT;";
-					window.location.reload();
-				} else Toast.methods.addToast(result.message, 4000);
-			});
-		},
 		submitOnEnter: (cb, event) => {
 			if (event.which === 13) cb();
 		},
@@ -78,17 +66,6 @@ export default {
 			this.$router.go(localStorage.getItem("github_redirect"));
 			localStorage.removeItem("github_redirect");
 		}
-		auth.isBanned((banned, ban) => {
-			_this.ban = ban;
-			_this.banned = banned;
-		});
-		auth.getStatus((authenticated, role, username, userId) => {
-			_this.socket = window.socket;
-			_this.loggedIn = authenticated;
-			_this.role = role;
-			_this.username = username;
-			_this.userId = userId;
-		});
 		io.onConnect(true, () => {
 			_this.socketConnected = true;
 		});

+ 16 - 0
frontend/api/auth.js

@@ -1,3 +1,4 @@
+import { Toast } from "vue-roaster";
 import io from "../io";
 
 // when Vuex needs to interact with socket.io
@@ -72,5 +73,20 @@ export default {
 				});
 			});
 		});
+	},
+	logout() {
+		return new Promise((resolve, reject) => {
+			io.getSocket(socket => {
+				socket.emit("users.logout", result => {
+					if (result.status === "success") {
+						document.cookie =
+							"SID=;expires=Thu, 01 Jan 1970 00:00:01 GMT;";
+						return window.location.reload();
+					}
+					Toast.methods.addToast(result.message, 4000);
+					return reject(new Error(result.message));
+				});
+			});
+		});
 	}
 };

+ 0 - 3
frontend/components/Admin/Users.vue

@@ -91,9 +91,6 @@ export default {
 				if (result.status === "success") _this.users = result.data;
 			});
 			_this.socket.emit("apis.joinAdminRoom", "users", () => {});
-			_this.socket.on("event:user.username.changed", username => {
-				_this.$parent.$parent.username = username;
-			});
 		},
 		...mapActions("admin/users", ["editUser"]),
 		...mapActions("modals", ["openModal"])

+ 11 - 12
frontend/components/MainHeader.vue

@@ -21,18 +21,18 @@
 
 		<div class="nav-right nav-menu" :class="{ 'is-active': isMobile }">
 			<router-link
-				v-if="$parent.$parent.role === 'admin'"
+				v-if="role === 'admin'"
 				class="nav-item is-tab admin"
 				to="/admin"
 			>
 				<strong>Admin</strong>
 			</router-link>
-			<span v-if="$parent.$parent.loggedIn" class="grouped">
+			<span v-if="loggedIn" class="grouped">
 				<router-link
 					class="nav-item is-tab"
 					:to="{
 						name: 'profile',
-						params: { username: $parent.$parent.username }
+						params: { username }
 					}"
 				>
 					Profile
@@ -40,12 +40,7 @@
 				<router-link class="nav-item is-tab" to="/settings"
 					>Settings</router-link
 				>
-				<a
-					class="nav-item is-tab"
-					href="#"
-					@click="$parent.$parent.logout()"
-					>Logout</a
-				>
+				<a class="nav-item is-tab" href="#" @click="logout()">Logout</a>
 			</span>
 			<span v-else class="grouped">
 				<a
@@ -99,11 +94,15 @@ export default {
 			return res;
 		});
 	},
-	computed: mapState("modals", {
-		modals: state => state.modals.header
+	computed: mapState({
+		modals: state => state.modals.modals.header,
+		role: state => state.user.auth.role,
+		loggedIn: state => state.user.auth.loggedIn,
+		username: state => state.user.auth.username
 	}),
 	methods: {
-		...mapActions("modals", ["openModal"])
+		...mapActions("modals", ["openModal"]),
+		...mapActions("user/auth", ["logout"])
 	}
 };
 </script>

+ 6 - 4
frontend/components/Modals/AddSongToQueue.vue

@@ -1,10 +1,7 @@
 <template>
 	<modal title="Add Song To Queue">
 		<div slot="body">
-			<aside
-				class="menu"
-				v-if="$parent.$parent.loggedIn && $parent.type === 'community'"
-			>
+			<aside class="menu" v-if="loggedIn && $parent.type === 'community'">
 				<ul class="menu-list">
 					<li v-for="(playlist, index) in playlists" :key="index">
 						<a
@@ -95,6 +92,8 @@
 </template>
 
 <script>
+import { mapState } from "vuex";
+
 import { Toast } from "vue-roaster";
 import Modal from "./Modal.vue";
 import io from "../../io";
@@ -109,6 +108,9 @@ export default {
 			importQuery: ""
 		};
 	},
+	computed: mapState({
+		loggedIn: state => state.user.auth.loggedIn
+	}),
 	methods: {
 		isPlaylistSelected(playlistId) {
 			return this.privatePlaylistQueueSelected === playlistId;

+ 4 - 1
frontend/components/Modals/EditUser.vue

@@ -109,6 +109,9 @@ export default {
 	computed: {
 		...mapState("admin/users", {
 			editing: state => state.editing
+		}),
+		...mapState({
+			userId: state => state.user.auth.userId
 		})
 	},
 	methods: {
@@ -166,7 +169,7 @@ export default {
 					if (
 						res.status === "success" &&
 						this.editing.role === "default" &&
-						this.editing._id === this.$parent.$parent.$parent.userId
+						this.editing._id === this.userId
 					)
 						window.location.reload();
 				}

+ 1 - 3
frontend/components/Modals/Playlists/Create.vue

@@ -34,9 +34,7 @@ export default {
 		return {
 			playlist: {
 				displayName: null,
-				songs: [],
-				createdBy: this.$parent.$parent.username,
-				createdAt: Date.now()
+				songs: []
 			}
 		};
 	},

+ 9 - 10
frontend/components/Sidebars/SongsList.vue

@@ -79,7 +79,7 @@
 			<div
 				v-if="
 					$parent.type === 'community' &&
-						$parent.$parent.loggedIn &&
+						loggedIn &&
 						$parent.station.partyMode === true
 				"
 			>
@@ -129,7 +129,7 @@
 </template>
 
 <script>
-import { mapActions } from "vuex";
+import { mapState, mapActions } from "vuex";
 
 import { Toast } from "vue-roaster";
 
@@ -141,18 +141,17 @@ export default {
 			dismissedWarning: false
 		};
 	},
+	computed: mapState({
+		loggedIn: state => state.user.auth.loggedIn,
+		userId: state => state.user.auth.userId,
+		role: state => state.user.auth.role
+	}),
 	methods: {
 		isOwnerOnly() {
-			return (
-				this.$parent.$parent.loggedIn &&
-				this.$parent.$parent.userId === this.$parent.station.owner
-			);
+			return this.loggedIn && this.userId === this.$parent.station.owner;
 		},
 		isAdminOnly() {
-			return (
-				this.$parent.$parent.loggedIn &&
-				this.$parent.$parent.role === "admin"
-			);
+			return this.loggedIn && this.role === "admin";
 		},
 		removeFromQueue(songId) {
 			window.socket.emit(

+ 19 - 20
frontend/components/Station/CommunityHeader.vue

@@ -26,26 +26,23 @@
 
 			<div class="nav-right nav-menu" :class="{ 'is-active': isMobile }">
 				<router-link
-					v-if="$parent.$parent.role === 'admin'"
+					v-if="role === 'admin'"
 					class="nav-item is-tab admin"
 					href="#"
 					:to="{ path: '/admin' }"
 				>
 					<strong>Admin</strong>
 				</router-link>
-				<span v-if="$parent.$parent.loggedIn" class="grouped">
+				<span v-if="loggedIn" class="grouped">
 					<router-link
 						class="nav-item is-tab"
-						:to="{ path: '/u/' + $parent.$parent.username }"
+						:to="{ path: '/u/' + username }"
 						>Profile</router-link
 					>
 					<router-link class="nav-item is-tab" to="/settings"
 						>Settings</router-link
 					>
-					<a
-						class="nav-item is-tab"
-						href="#"
-						@click="$parent.$parent.logout()"
+					<a class="nav-item is-tab" href="#" @click="logout()"
 						>Logout</a
 					>
 				</span>
@@ -116,13 +113,9 @@
 					</a>
 					<hr />
 				</div>
-				<div v-if="$parent.$parent.loggedIn && !$parent.noSong">
+				<div v-if="loggedIn && !$parent.noSong">
 					<a
-						v-if="
-							!isOwner() &&
-								$parent.$parent.loggedIn &&
-								!$parent.noSong
-						"
+						v-if="!isOwner() && loggedIn && !$parent.noSong"
 						class="sidebar-item"
 						href="#"
 						@click="$parent.voteSkipStation()"
@@ -136,7 +129,7 @@
 						<span class="icon-purpose">Skip current song</span>
 					</a>
 					<a
-						v-if="$parent.$parent.loggedIn && !$parent.noSong"
+						v-if="loggedIn && !$parent.noSong"
 						class="sidebar-item"
 						href="#"
 						@click="
@@ -179,7 +172,7 @@
 					>
 				</a>
 				<a
-					v-if="$parent.$parent.loggedIn"
+					v-if="loggedIn"
 					class="sidebar-item"
 					href="#"
 					@click="$parent.toggleSidebar('playlist')"
@@ -195,7 +188,7 @@
 </template>
 
 <script>
-import { mapActions } from "vuex";
+import { mapState, mapActions } from "vuex";
 
 export default {
 	data() {
@@ -210,6 +203,11 @@ export default {
 			}
 		};
 	},
+	computed: mapState({
+		loggedIn: state => state.user.auth.loggedIn,
+		userId: state => state.user.auth.userId,
+		role: state => state.user.auth.role
+	}),
 	mounted() {
 		lofig.get("frontendDomain", res => {
 			this.frontendDomain = res;
@@ -223,9 +221,9 @@ export default {
 	methods: {
 		isOwner() {
 			return (
-				this.$parent.$parent.loggedIn &&
-				(this.$parent.$parent.role === "admin" ||
-					this.$parent.$parent.userId === this.$parent.station.owner)
+				this.loggedIn &&
+				(this.role === "admin" ||
+					this.userId === this.$parent.station.owner)
 			);
 		},
 		settings() {
@@ -244,7 +242,8 @@ export default {
 			});
 		},
 		...mapActions("modals", ["openModal"]),
-		...mapActions("station", ["editStation"])
+		...mapActions("station", ["editStation"]),
+		...mapActions("user/auth", ["logout"])
 	}
 };
 </script>

+ 18 - 26
frontend/components/Station/OfficialHeader.vue

@@ -22,26 +22,24 @@
 
 			<div class="nav-right nav-menu" :class="{ 'is-active': isMobile }">
 				<router-link
-					v-if="$parent.$parent.role === 'admin'"
+					v-if="role === 'admin'"
 					class="nav-item is-tab admin"
 					href="#"
 					:to="{ path: '/admin' }"
 				>
 					<strong>Admin</strong>
 				</router-link>
-				<span v-if="$parent.$parent.loggedIn" class="grouped">
+				<span v-if="loggedIn" class="grouped">
 					<router-link
 						class="nav-item is-tab"
 						href="#"
-						:to="{ path: '/u/' + $parent.$parent.username }"
+						:to="{ path: '/u/' + username }"
 						>Profile</router-link
 					>
 					<router-link class="nav-item is-tab" to="/settings"
 						>Settings</router-link
 					>
-					<a class="nav-item is-tab" @click="$parent.$parent.logout()"
-						>Logout</a
-					>
+					<a class="nav-item is-tab" @click="logout()">Logout</a>
 				</span>
 				<span v-else class="grouped">
 					<a
@@ -110,12 +108,9 @@
 					</a>
 					<hr />
 				</div>
-				<div v-if="$parent.$parent.loggedIn">
+				<div v-if="loggedIn">
 					<a
-						v-if="
-							$parent.type === 'official' &&
-								$parent.$parent.loggedIn
-						"
+						v-if="$parent.type === 'official' && loggedIn"
 						class="sidebar-item"
 						href="#"
 						@click="
@@ -131,11 +126,7 @@
 						<span class="icon-purpose">Add song to queue</span>
 					</a>
 					<a
-						v-if="
-							!isOwner() &&
-								$parent.$parent.loggedIn &&
-								!$parent.noSong
-						"
+						v-if="!isOwner() && loggedIn && !$parent.noSong"
 						class="sidebar-item"
 						href="#"
 						@click="$parent.voteSkipStation()"
@@ -150,9 +141,7 @@
 					</a>
 					<a
 						v-if="
-							$parent.$parent.loggedIn &&
-								!$parent.noSong &&
-								!$parent.simpleSong
+							loggedIn && !$parent.noSong && !$parent.simpleSong
 						"
 						class="sidebar-item"
 						href="#"
@@ -169,7 +158,7 @@
 						<span class="icon-purpose">Report a song</span>
 					</a>
 					<a
-						v-if="$parent.$parent.loggedIn && !$parent.noSong"
+						v-if="loggedIn && !$parent.noSong"
 						class="sidebar-item"
 						href="#"
 						@click="
@@ -216,7 +205,7 @@
 </template>
 
 <script>
-import { mapActions } from "vuex";
+import { mapState, mapActions } from "vuex";
 
 export default {
 	data() {
@@ -231,6 +220,11 @@ export default {
 			}
 		};
 	},
+	computed: mapState({
+		role: state => state.user.auth.role,
+		username: state => state.user.auth.username,
+		loggedIn: state => state.user.auth.loggedIn
+	}),
 	mounted() {
 		lofig.get("frontendDomain", res => {
 			this.frontendDomain = res;
@@ -243,10 +237,7 @@ export default {
 	},
 	methods: {
 		isOwner() {
-			return (
-				this.$parent.$parent.loggedIn &&
-				this.$parent.$parent.role === "admin"
-			);
+			return this.loggedIn && this.role === "admin";
 		},
 		settings() {
 			this.editStation({
@@ -264,7 +255,8 @@ export default {
 			});
 		},
 		...mapActions("modals", ["openModal"]),
-		...mapActions("station", ["editStation"])
+		...mapActions("station", ["editStation"]),
+		...mapActions("user/auth", ["logout"])
 	}
 };
 </script>

+ 15 - 15
frontend/components/Station/Station.vue

@@ -28,10 +28,10 @@
 					v-if="
 						type === 'community' &&
 							station.partyMode &&
+							this.loggedIn &&
 							(!station.locked ||
 								(station.locked &&
-									$parent.loggedIn &&
-									$parent.userId === station.owner))
+									this.userId === station.owner))
 					"
 				>
 					<a
@@ -50,7 +50,7 @@
 					v-if="
 						type === 'community' &&
 							!station.partyMode &&
-							$parent.userId === station.owner &&
+							this.userId === station.owner &&
 							!station.privatePlaylist
 					"
 				>
@@ -65,7 +65,7 @@
 					v-if="
 						type === 'community' &&
 							!station.partyMode &&
-							$parent.userId === station.owner &&
+							this.userId === station.owner &&
 							station.privatePlaylist
 					"
 				>
@@ -172,7 +172,7 @@
 						</div>
 					</article>
 					<a
-						v-if="type === 'community' && $parent.loggedIn"
+						v-if="type === 'community' && loggedIn"
 						class="button add-to-queue"
 						href="#"
 						@click="
@@ -181,7 +181,7 @@
 								modal: 'addSongToQueue'
 							})
 						"
-						>Add Song to Queue</a
+						>Add a song to the queue</a
 					>
 				</div>
 			</div>
@@ -482,17 +482,19 @@ export default {
 		}),
 		...mapState("station", {
 			station: state => state.station
+		}),
+		...mapState({
+			loggedIn: state => state.user.auth.loggedIn,
+			userId: state => state.user.auth.userId,
+			role: state => state.user.auth.role
 		})
 	},
 	methods: {
 		isOwnerOnly() {
-			return (
-				this.$parent.loggedIn &&
-				this.$parent.userId === this.station.owner
-			);
+			return this.loggedIn && this.userId === this.station.owner;
 		},
 		isAdminOnly() {
-			return this.$parent.loggedIn && this.$parent.role === "admin";
+			return this.loggedIn && this.role === "admin";
 		},
 		removeFromQueue(songId) {
 			window.socket.emit(
@@ -896,10 +898,9 @@ export default {
 		addFirstPrivatePlaylistSongToQueue() {
 			const _this = this;
 			let isInQueue = false;
-			const { userId } = _this.$parent;
 			if (_this.type === "community") {
 				_this.songsList.forEach(queueSong => {
-					if (queueSong.requestedBy === userId) isInQueue = true;
+					if (queueSong.requestedBy === this.userId) isInQueue = true;
 				});
 				if (!isInQueue && _this.privatePlaylistQueueSelected) {
 					_this.socket.emit(
@@ -1110,9 +1111,8 @@ export default {
 				}
 
 				let isInQueue = false;
-				const { userId } = _this.$parent;
 				_this.songsList.forEach(queueSong => {
-					if (queueSong.requestedBy === userId) isInQueue = true;
+					if (queueSong.requestedBy === this.userId) isInQueue = true;
 				});
 				if (
 					!isInQueue &&

+ 10 - 12
frontend/components/User/Settings.vue

@@ -146,6 +146,8 @@
 </template>
 
 <script>
+import { mapState } from "vuex";
+
 import { Toast } from "vue-roaster";
 
 import MainHeader from "../MainHeader.vue";
@@ -167,6 +169,9 @@ export default {
 			passwordCode: ""
 		};
 	},
+	computed: mapState({
+		userId: state => state.user.auth.userId
+	}),
 	mounted() {
 		const _this = this;
 		io.getSocket(socket => {
@@ -184,9 +189,6 @@ export default {
 					);
 				}
 			});
-			_this.socket.on("event:user.username.changed", username => {
-				_this.$parent.username = username;
-			});
 			_this.socket.on("event:user.linkPassword", () => {
 				_this.password = true;
 			});
@@ -217,7 +219,7 @@ export default {
 
 			return this.socket.emit(
 				"users.updateEmail",
-				this.$parent.userId,
+				this.userId,
 				email,
 				res => {
 					if (res.status !== "success")
@@ -245,7 +247,7 @@ export default {
 
 			return this.socket.emit(
 				"users.updateUsername",
-				this.$parent.userId,
+				this.userId,
 				username,
 				res => {
 					if (res.status !== "success")
@@ -340,13 +342,9 @@ export default {
 			});
 		},
 		removeSessions() {
-			this.socket.emit(
-				`users.removeSessions`,
-				this.$parent.userId,
-				res => {
-					Toast.methods.addToast(res.message, 4000);
-				}
-			);
+			this.socket.emit(`users.removeSessions`, this.userId, res => {
+				Toast.methods.addToast(res.message, 4000);
+			});
 		}
 	}
 };

+ 7 - 3
frontend/components/User/Show.vue

@@ -6,9 +6,7 @@
 			<h2 class="has-text-centered username">@{{ user.username }}</h2>
 			<h5>A member since {{ user.createdAt }}</h5>
 			<div
-				v-if="
-					$parent.role === 'admin' && !($parent.userId === user._id)
-				"
+				v-if="role === 'admin' && userId !== user._id"
 				class="admin-functionality"
 			>
 				<a
@@ -64,6 +62,8 @@
 </template>
 
 <script>
+import { mapState } from "vuex";
+
 import { Toast } from "vue-roaster";
 
 import MainHeader from "../MainHeader.vue";
@@ -78,6 +78,10 @@ export default {
 			isUser: false
 		};
 	},
+	computed: mapState({
+		role: state => state.user.auth.role,
+		userId: state => state.user.auth.userId
+	}),
 	mounted() {
 		const _this = this;
 		io.getSocket(socket => {

+ 8 - 3
frontend/components/pages/Banned.vue

@@ -3,21 +3,26 @@
 		<i class="material-icons">not_interested</i>
 		<h4>
 			You are banned for
-			<strong>{{ moment($parent.ban.expiresAt).fromNow(true) }}</strong>
+			<strong>{{ moment(ban.expiresAt).fromNow(true) }}</strong>
 		</h4>
 		<h5 class="reason">
 			<strong>Reason: </strong>
-			{{ $parent.ban.reason }}
+			{{ ban.reason }}
 		</h5>
 	</div>
 </template>
 <script>
+import { mapState } from "vuex";
+
 export default {
 	data() {
 		return {
 			moment
 		};
-	}
+	},
+	computed: mapState({
+		ban: state => state.user.auth.ban
+	})
 };
 </script>
 

+ 78 - 91
frontend/components/pages/Home.vue

@@ -61,7 +61,7 @@
 					<div class="group-title">
 						Community Stations&nbsp;
 						<a
-							v-if="$parent.loggedIn"
+							v-if="loggedIn"
 							href="#"
 							@click="
 								openModal({
@@ -155,7 +155,6 @@ import MainFooter from "../MainFooter.vue";
 import CreateCommunityStation from "../Modals/CreateCommunityStation.vue";
 import UserIdToUsername from "../UserIdToUsername.vue";
 
-import auth from "../../auth";
 import io from "../../io";
 
 export default {
@@ -170,77 +169,75 @@ export default {
 			}
 		};
 	},
-	computed: mapState("modals", {
-		modals: state => state.modals.home
+	computed: mapState({
+		modals: state => state.modals.modals.home,
+		loggedIn: state => state.user.auth.loggedIn,
+		userId: state => state.user.auth.userId
 	}),
 	mounted() {
 		const _this = this;
-		auth.getStatus(() => {
-			io.getSocket(socket => {
-				_this.socket = socket;
-				if (_this.socket.connected) _this.init();
-				io.onConnect(() => {
-					_this.init();
-				});
-				_this.socket.on("event:stations.created", res => {
-					const station = res;
-
-					if (!station.currentSong)
-						station.currentSong = {
-							thumbnail: "/assets/notes-transparent.png"
-						};
-					if (station.currentSong && !station.currentSong.thumbnail)
-						station.currentSong.thumbnail =
-							"/assets/notes-transparent.png";
-					_this.stations[station.type].push(station);
-				});
-				_this.socket.on(
-					"event:userCount.updated",
-					(stationId, userCount) => {
-						_this.stations.official.forEach(s => {
-							const station = s;
-							if (station._id === stationId) {
-								station.userCount = userCount;
-							}
-						});
-
-						_this.stations.community.forEach(s => {
-							const station = s;
-							if (station._id === stationId) {
-								station.userCount = userCount;
-							}
-						});
-					}
-				);
-				_this.socket.on("event:station.nextSong", (stationId, song) => {
-					let newSong = song;
+		io.getSocket(socket => {
+			_this.socket = socket;
+			if (_this.socket.connected) _this.init();
+			io.onConnect(() => {
+				_this.init();
+			});
+			_this.socket.on("event:stations.created", res => {
+				const station = res;
+
+				if (!station.currentSong)
+					station.currentSong = {
+						thumbnail: "/assets/notes-transparent.png"
+					};
+				if (station.currentSong && !station.currentSong.thumbnail)
+					station.currentSong.thumbnail =
+						"/assets/notes-transparent.png";
+				_this.stations[station.type].push(station);
+			});
+			_this.socket.on(
+				"event:userCount.updated",
+				(stationId, userCount) => {
 					_this.stations.official.forEach(s => {
 						const station = s;
 						if (station._id === stationId) {
-							if (!newSong)
-								newSong = {
-									thumbnail: "/assets/notes-transparent.png"
-								};
-							if (newSong && !newSong.thumbnail)
-								newSong.thumbnail =
-									"/assets/notes-transparent.png";
-							station.currentSong = newSong;
+							station.userCount = userCount;
 						}
 					});
 
 					_this.stations.community.forEach(s => {
 						const station = s;
 						if (station._id === stationId) {
-							if (!newSong)
-								newSong = {
-									thumbnail: "/assets/notes-transparent.png"
-								};
-							if (newSong && !newSong.thumbnail)
-								newSong.thumbnail =
-									"/assets/notes-transparent.png";
-							station.currentSong = newSong;
+							station.userCount = userCount;
 						}
 					});
+				}
+			);
+			_this.socket.on("event:station.nextSong", (stationId, song) => {
+				let newSong = song;
+				_this.stations.official.forEach(s => {
+					const station = s;
+					if (station._id === stationId) {
+						if (!newSong)
+							newSong = {
+								thumbnail: "/assets/notes-transparent.png"
+							};
+						if (newSong && !newSong.thumbnail)
+							newSong.thumbnail = "/assets/notes-transparent.png";
+						station.currentSong = newSong;
+					}
+				});
+
+				_this.stations.community.forEach(s => {
+					const station = s;
+					if (station._id === stationId) {
+						if (!newSong)
+							newSong = {
+								thumbnail: "/assets/notes-transparent.png"
+							};
+						if (newSong && !newSong.thumbnail)
+							newSong.thumbnail = "/assets/notes-transparent.png";
+						station.currentSong = newSong;
+					}
 				});
 			});
 		});
@@ -248,43 +245,33 @@ export default {
 	methods: {
 		init() {
 			const _this = this;
-			auth.getStatus((authenticated, role, username, userId) => {
-				_this.socket.emit("stations.index", data => {
-					_this.stations.community = [];
-					_this.stations.official = [];
-					if (data.status === "success")
-						data.stations.forEach(s => {
-							const station = s;
-							if (!station.currentSong)
-								station.currentSong = {
-									thumbnail: "/assets/notes-transparent.png"
-								};
-							if (
-								station.currentSong &&
-								!station.currentSong.thumbnail
-							)
-								station.currentSong.thumbnail =
-									"/assets/notes-transparent.png";
-							if (station.privacy !== "public")
-								station.class = { "station-red": true };
-							else if (
-								station.type === "community" &&
-								station.owner === userId
-							)
-								station.class = { "station-blue": true };
-							if (station.type === "official")
-								_this.stations.official.push(station);
-							else _this.stations.community.push(station);
-						});
-				});
-				_this.socket.emit("apis.joinRoom", "home", () => {});
+			_this.socket.emit("stations.index", data => {
+				_this.stations.community = [];
+				_this.stations.official = [];
+				if (data.status === "success")
+					data.stations.forEach(s => {
+						const station = s;
+						if (!station.currentSong)
+							station.currentSong = {
+								thumbnail: "/assets/notes-transparent.png"
+							};
+						if (
+							station.currentSong &&
+							!station.currentSong.thumbnail
+						)
+							station.currentSong.thumbnail =
+								"/assets/notes-transparent.png";
+						if (station.type === "official")
+							_this.stations.official.push(station);
+						else _this.stations.community.push(station);
+					});
 			});
+			_this.socket.emit("apis.joinRoom", "home", () => {});
 		},
 		isOwner(station) {
 			const _this = this;
 			return (
-				station.owner === _this.$parent.userId &&
-				station.privacy === "public"
+				station.owner === _this.userId && station.privacy === "public"
 			);
 		},
 		...mapActions("modals", ["openModal"])

+ 46 - 16
frontend/main.js

@@ -4,7 +4,6 @@ import VueRouter from "vue-router";
 import store from "./store";
 
 import App from "./App.vue";
-import auth from "./auth";
 import io from "./io";
 
 Vue.use(VueRouter);
@@ -52,7 +51,9 @@ const router = new VueRouter({
 		{
 			path: "/settings",
 			component: () => import("./components/User/Settings.vue"),
-			loginRequired: true
+			meta: {
+				loginRequired: true
+			}
 		},
 		{
 			path: "/reset_password",
@@ -69,25 +70,33 @@ const router = new VueRouter({
 		{
 			path: "/admin",
 			component: () => import("./components/pages/Admin.vue"),
-			adminRequired: true
+			meta: {
+				adminRequired: true
+			}
 		},
 		{
 			path: "/admin/:page",
 			component: () => import("./components/pages/Admin.vue"),
-			adminRequired: true
+			meta: {
+				adminRequired: true
+			}
 		},
 		{
 			name: "official",
 			path: "/official/:id",
 			alias: "/:id",
 			component: () => import("./components/Station/Station.vue"),
-			officialRequired: true
+			meta: {
+				officialStation: true
+			}
 		},
 		{
 			name: "community",
 			path: "/community/:id",
 			component: () => import("./components/Station/Station.vue"),
-			communityRequired: true
+			meta: {
+				communityStation: true
+			}
 		}
 	]
 });
@@ -96,11 +105,19 @@ lofig.folder = "../config/default.json";
 lofig.get("serverDomain", res => {
 	io.init(res);
 	io.getSocket(socket => {
-		socket.on("ready", (status, role, username, userId) => {
-			auth.data(status, role, username, userId);
+		socket.on("ready", (loggedIn, role, username, userId) => {
+			store.dispatch("user/auth/authData", {
+				loggedIn,
+				role,
+				username,
+				userId
+			});
 		});
 		socket.on("keep.event:banned", ban => {
-			auth.setBanned(ban);
+			store.dispatch("user/auth/banned", ban);
+		});
+		socket.on("event:user.username.changed", username => {
+			store.dispatch("user/auth/updateUsername", username);
 		});
 	});
 });
@@ -112,19 +129,32 @@ router.beforeEach((to, from, next) => {
 	}
 	if (window.socket) io.removeAllListeners();
 	io.clear();
-	if (to.loginRequired || to.adminRequired) {
-		auth.getStatus((authenticated, role) => {
-			if (to.loginRequired && !authenticated) next({ path: "/login" });
-			else if (to.adminRequired && role !== "admin") next({ path: "/" });
+	if (to.meta.loginRequired || to.meta.adminRequired) {
+		const gotData = () => {
+			if (to.loginRequired && !store.state.user.auth.loggedIn)
+				next({ path: "/login" });
+			else if (to.adminRequired && store.state.user.auth.role !== "admin")
+				next({ path: "/" });
 			else next();
-		});
+		};
+
+		if (store.state.user.auth.gotData) gotData();
+		else {
+			const watcher = store.watch(
+				state => state.user.auth.gotData,
+				() => {
+					watcher();
+					gotData();
+				}
+			);
+		}
 	} else next();
 
 	if (from.name === "community" || from.name === "official") {
 		document.title = "Musare";
 	}
 
-	if (to.officialRequired) {
+	if (to.meta.officialStation) {
 		io.getSocket(socket => {
 			socket.emit("stations.findByName", to.params.id, res => {
 				if (res.status === "success") {
@@ -136,7 +166,7 @@ router.beforeEach((to, from, next) => {
 		});
 	}
 
-	if (to.communityRequired) {
+	if (to.meta.communityStation) {
 		io.getSocket(socket => {
 			socket.emit("stations.findByName", to.params.id, res => {
 				if (res.status === "success") {

+ 43 - 2
frontend/store/modules/user.js

@@ -15,9 +15,15 @@ const modules = {
 		state: {
 			userIdMap: {},
 			userIdRequested: {},
-			pendingUserIdCallbacks: {}
+			pendingUserIdCallbacks: {},
+			loggedIn: false,
+			role: "",
+			username: "",
+			userId: "",
+			banned: false,
+			ban: {},
+			gotData: false
 		},
-		getters: {},
 		actions: {
 			/* eslint-disable-next-line no-unused-vars */
 			register: ({ commit }, user) => {
@@ -96,6 +102,18 @@ const modules = {
 						});
 				});
 			},
+			logout: () => {
+				return new Promise((resolve, reject) => {
+					return auth
+						.logout()
+						.then(() => {
+							return resolve();
+						})
+						.catch(() => {
+							return reject();
+						});
+				});
+			},
 			getUsernameFromId: ({ commit, state }, userId) => {
 				return new Promise(resolve => {
 					if (typeof state.userIdMap[`Z${userId}`] !== "string") {
@@ -139,6 +157,15 @@ const modules = {
 						resolve(state.userIdMap[`Z${userId}`]);
 					}
 				});
+			},
+			authData: ({ commit }, data) => {
+				commit("authData", data);
+			},
+			banned: ({ commit }, ban) => {
+				commit("banned", ban);
+			},
+			updateUsername: ({ commit }, username) => {
+				commit("updateUsername", username);
 			}
 		},
 		mutations: {
@@ -158,6 +185,20 @@ const modules = {
 			},
 			clearPendingCallbacks(state, userId) {
 				state.pendingUserIdCallbacks[`Z${userId}`] = [];
+			},
+			authData(state, data) {
+				state.loggedIn = data.loggedIn;
+				state.role = data.role;
+				state.username = data.username;
+				state.userId = data.userId;
+				state.gotData = true;
+			},
+			banned(state, ban) {
+				state.banned = true;
+				state.ban = ban;
+			},
+			updateUsername(state, username) {
+				state.username = username;
 			}
 		}
 	},