Browse Source

refactor: moved station info box to its own component

Kristian Vos 2 years ago
parent
commit
88367f4db5

+ 23 - 1
backend/logic/actions/stations.js

@@ -946,6 +946,27 @@ export default {
 						.catch(err => next(err));
 				},
 
+				(station, next) => {
+					// only relevant if user logged in
+					if (session.userId) {
+						return StationsModule.runJob(
+							"HAS_USER_FAVORITED_STATION",
+							{
+								userId: session.userId,
+								stationId
+							},
+							this
+						)
+							.then(isStationFavorited => {
+								station.isFavorited = isStationFavorited;
+								return next(null, station);
+							})
+							.catch(err => next(err));
+					}
+
+					return next(null, station);
+				},
+
 				(station, next) => {
 					const data = {
 						_id: station._id,
@@ -959,7 +980,8 @@ export default {
 						owner: station.owner,
 						theme: station.theme,
 						paused: station.paused,
-						currentSong: station.currentSong
+						currentSong: station.currentSong,
+						isFavorited: station.isFavorited
 					};
 
 					next(null, data);

+ 245 - 0
frontend/src/components/StationInfoBox.vue

@@ -0,0 +1,245 @@
+<template>
+	<div class="about-station-container">
+		<div class="station-info">
+			<div class="row station-name">
+				<h1>{{ station.displayName }}</h1>
+				<i
+					v-if="station.type === 'official'"
+					class="material-icons verified-station"
+					content="Verified Station"
+					v-tippy
+				>
+					check_circle
+				</i>
+				<a>
+					<!-- Favorite Station Button -->
+					<i
+						v-if="loggedIn && station.isFavorited"
+						@click.prevent="unfavoriteStation()"
+						content="Unfavorite Station"
+						v-tippy
+						class="material-icons"
+						>star</i
+					>
+					<i
+						v-if="loggedIn && !station.isFavorited"
+						@click.prevent="favoriteStation()"
+						class="material-icons"
+						content="Favorite Station"
+						v-tippy
+						>star_border</i
+					>
+				</a>
+			</div>
+			<p>{{ station.description }}</p>
+		</div>
+
+		<div class="admin-buttons" v-if="isOwnerOrAdmin()">
+			<!-- (Admin) Pause/Resume Button -->
+			<button
+				class="button is-danger"
+				v-if="stationPaused"
+				@click="resumeStation()"
+			>
+				<i class="material-icons icon-with-button">play_arrow</i>
+				<span> Resume Station </span>
+			</button>
+			<button class="button is-danger" @click="pauseStation()" v-else>
+				<i class="material-icons icon-with-button">pause</i>
+				<span> Pause Station </span>
+			</button>
+
+			<!-- (Admin) Skip Button -->
+			<button class="button is-danger" @click="skipStation()">
+				<i class="material-icons icon-with-button">skip_next</i>
+				<span> Force Skip </span>
+			</button>
+
+			<!-- (Admin) Station Settings Button -->
+			<button
+				class="button is-primary"
+				@click="
+					openModal({
+						modal: 'manageStation',
+						data: {
+							stationId: station._id,
+							sector: 'station'
+						}
+					})
+				"
+				v-if="showManageStation"
+			>
+				<i class="material-icons icon-with-button">settings</i>
+				<span> Manage Station </span>
+			</button>
+			<router-link
+				v-if="showGoToStation"
+				:to="{
+					name: 'station',
+					params: { id: station.name }
+				}"
+				class="button is-primary"
+			>
+				Go To Station
+			</router-link>
+		</div>
+	</div>
+</template>
+
+<script>
+import { mapGetters, mapState, mapActions } from "vuex";
+import Toast from "toasters";
+
+export default {
+	props: {
+		station: { type: Object, default: null },
+		stationPaused: { type: Boolean, default: null },
+		showManageStation: { type: Boolean, default: false },
+		showGoToStation: { type: Boolean, default: false }
+	},
+	data() {
+		return {};
+	},
+	computed: {
+		...mapState({
+			loggedIn: state => state.user.auth.loggedIn,
+			userId: state => state.user.auth.userId,
+			role: state => state.user.auth.role
+		}),
+		...mapGetters({
+			socket: "websockets/getSocket"
+		})
+	},
+	mounted() {},
+	methods: {
+		isOwnerOnly() {
+			return this.loggedIn && this.userId === this.station.owner;
+		},
+		isAdminOnly() {
+			return this.loggedIn && this.role === "admin";
+		},
+		isOwnerOrAdmin() {
+			return this.isOwnerOnly() || this.isAdminOnly();
+		},
+		resumeStation() {
+			this.socket.dispatch("stations.resume", this.station._id, data => {
+				if (data.status !== "success")
+					new Toast(`Error: ${data.message}`);
+				else new Toast("Successfully resumed the station.");
+			});
+		},
+		pauseStation() {
+			this.socket.dispatch("stations.pause", this.station._id, data => {
+				if (data.status !== "success")
+					new Toast(`Error: ${data.message}`);
+				else new Toast("Successfully paused the station.");
+			});
+		},
+		skipStation() {
+			this.socket.dispatch(
+				"stations.forceSkip",
+				this.station._id,
+				data => {
+					if (data.status !== "success")
+						new Toast(`Error: ${data.message}`);
+					else
+						new Toast(
+							"Successfully skipped the station's current song."
+						);
+				}
+			);
+		},
+		favoriteStation() {
+			this.socket.dispatch(
+				"stations.favoriteStation",
+				this.station._id,
+				res => {
+					if (res.status === "success") {
+						new Toast("Successfully favorited station.");
+					} else new Toast(res.message);
+				}
+			);
+		},
+		unfavoriteStation() {
+			this.socket.dispatch(
+				"stations.unfavoriteStation",
+				this.station._id,
+				res => {
+					if (res.status === "success") {
+						new Toast("Successfully unfavorited station.");
+					} else new Toast(res.message);
+				}
+			);
+		},
+		...mapActions("modalVisibility", ["openModal"])
+	}
+};
+</script>
+
+<style lang="less">
+.night-mode {
+	.about-station-container {
+		background-color: var(--dark-grey-3) !important;
+	}
+}
+
+.about-station-container {
+	padding: 20px;
+	display: flex;
+	flex-direction: column;
+	flex-grow: unset;
+
+	.row {
+		display: flex;
+		flex-direction: row;
+		max-width: 100%;
+	}
+
+	.station-info {
+		.station-name {
+			flex-direction: row !important;
+
+			h1 {
+				margin: 0;
+				font-size: 36px;
+				line-height: 0.8;
+				text-overflow: ellipsis;
+				overflow: hidden;
+			}
+
+			i {
+				margin-left: 10px;
+				font-size: 30px;
+				color: var(--yellow);
+				&.stationMode {
+					padding-left: 10px;
+					margin-left: auto;
+					color: var(--primary-color);
+				}
+			}
+
+			.verified-station {
+				color: var(--primary-color);
+			}
+		}
+
+		p {
+			display: -webkit-box;
+			max-width: 700px;
+			margin-bottom: 10px;
+			overflow: hidden;
+			text-overflow: ellipsis;
+			-webkit-box-orient: vertical;
+			-webkit-line-clamp: 3;
+		}
+	}
+
+	.admin-buttons {
+		display: flex;
+
+		.button {
+			margin: 3px;
+		}
+	}
+}
+</style>

+ 35 - 122
frontend/src/components/modals/ManageStation/index.vue

@@ -16,68 +16,12 @@
 		<template #body v-if="station && station._id">
 			<div class="left-section">
 				<div class="section">
-					<div id="about-station-container">
-						<div id="station-info">
-							<div id="station-name">
-								<h1>{{ station.displayName }}</h1>
-								<i
-									v-if="station.type === 'official'"
-									class="material-icons verified-station"
-									content="Verified Station"
-									v-tippy
-								>
-									check_circle
-								</i>
-							</div>
-							<p>{{ station.description }}</p>
-						</div>
-
-						<div id="admin-buttons">
-							<!-- (Admin) Pause/Resume Button -->
-							<button
-								v-if="isOwnerOrAdmin() && stationPaused"
-								class="button is-danger"
-								@click="resumeStation()"
-							>
-								<i class="material-icons icon-with-button"
-									>play_arrow</i
-								>
-								<span> Resume Station </span>
-							</button>
-							<button
-								v-if="isOwnerOrAdmin() && !stationPaused"
-								class="button is-danger"
-								@click="pauseStation()"
-							>
-								<i class="material-icons icon-with-button"
-									>pause</i
-								>
-								<span> Pause Station </span>
-							</button>
-
-							<!-- (Admin) Skip Button -->
-							<button
-								v-if="isOwnerOrAdmin()"
-								class="button is-danger"
-								@click="skipStation()"
-							>
-								<i class="material-icons icon-with-button"
-									>skip_next</i
-								>
-								<span> Force Skip </span>
-							</button>
-
-							<router-link
-								v-if="sector !== 'station' && station.name"
-								:to="{
-									name: 'station',
-									params: { id: station.name }
-								}"
-								class="button is-primary"
-							>
-								Go To Station
-							</router-link>
-						</div>
+					<div class="station-info-box-wrapper">
+						<station-info-box
+							:station="station"
+							:station-paused="stationPaused"
+							:show-go-to-station="true"
+						/>
 					</div>
 					<div v-if="isOwnerOrAdmin() || sector !== 'home'">
 						<div class="tab-selection">
@@ -204,6 +148,7 @@ import { mapModalState, mapModalActions } from "@/vuex_helpers";
 
 import Queue from "@/components/Queue.vue";
 import SongItem from "@/components/SongItem.vue";
+import StationInfoBox from "@/components/StationInfoBox.vue";
 
 import Settings from "./Settings.vue";
 import PlaylistTabBase from "@/components/PlaylistTabBase.vue";
@@ -213,6 +158,7 @@ export default {
 	components: {
 		Queue,
 		SongItem,
+		StationInfoBox,
 		Settings,
 		PlaylistTabBase,
 		Request
@@ -384,6 +330,24 @@ export default {
 					},
 					{ modalUuid: this.modalUuid }
 				);
+
+				this.socket.on(
+					"event:user.station.favorited",
+					res => {
+						if (res.data.stationId === this.stationId)
+							this.updateIsFavorited(true);
+					},
+					{ modalUuid: this.modalUuid }
+				);
+
+				this.socket.on(
+					"event:user.station.unfavorited",
+					res => {
+						if (res.data.stationId === this.stationId)
+							this.updateIsFavorited(false);
+					},
+					{ modalUuid: this.modalUuid }
+				);
 			} else {
 				new Toast(`Station with that ID not found`);
 				this.closeModal("manageStation");
@@ -601,7 +565,8 @@ export default {
 			"repositionSongInList",
 			"updateStationPaused",
 			"updateCurrentSong",
-			"updateStation"
+			"updateStation",
+			"updateIsFavorited"
 		]),
 		...mapActions({
 			showTab(dispatch, payload) {
@@ -640,8 +605,7 @@ export default {
 .night-mode {
 	.manage-station-modal.modal .modal-card-body {
 		.left-section {
-			#about-station-container {
-				background-color: var(--dark-grey-3) !important;
+			.station-info-box-wrapper {
 				border: 0;
 			}
 			.section {
@@ -670,65 +634,14 @@ export default {
 	height: 100%;
 
 	.left-section {
-		#about-station-container {
-			padding: 20px;
-			display: flex;
-			flex-direction: column;
-			flex-grow: unset;
+		.section {
+			row-gap: 20px;
+		}
+
+		.station-info-box-wrapper {
 			border-radius: @border-radius;
-			margin: 0 0 20px 0;
-			background-color: var(--white);
 			border: 1px solid var(--light-grey-3);
-
-			#station-info {
-				#station-name {
-					flex-direction: row !important;
-					display: flex;
-					flex-direction: row;
-					max-width: 100%;
-
-					h1 {
-						margin: 0;
-						font-size: 36px;
-						line-height: 0.8;
-						text-overflow: ellipsis;
-						overflow: hidden;
-					}
-
-					i {
-						margin-left: 10px;
-						font-size: 30px;
-						color: var(--yellow);
-						&.stationMode {
-							padding-left: 10px;
-							margin-left: auto;
-							color: var(--primary-color);
-						}
-					}
-
-					.verified-station {
-						color: var(--primary-color);
-					}
-				}
-
-				p {
-					display: -webkit-box;
-					max-width: 700px;
-					margin-bottom: 10px;
-					overflow: hidden;
-					text-overflow: ellipsis;
-					-webkit-box-orient: vertical;
-					-webkit-line-clamp: 3;
-				}
-			}
-
-			#admin-buttons {
-				display: flex;
-
-				.button {
-					margin: 3px;
-				}
-			}
+			overflow: hidden;
 		}
 
 		.tab-selection {

+ 10 - 147
frontend/src/pages/Station/index.vue

@@ -68,98 +68,13 @@
 					:class="{ 'nothing-here': noSong }"
 				>
 					<div id="station-left-column" class="column">
-						<div id="about-station-container" class="quadrant">
-							<div id="station-info">
-								<div class="row" id="station-name">
-									<h1>{{ station.displayName }}</h1>
-									<i
-										v-if="station.type === 'official'"
-										class="material-icons verified-station"
-										content="Verified Station"
-										v-tippy
-									>
-										check_circle
-									</i>
-									<a>
-										<!-- Favorite Station Button -->
-										<i
-											v-if="
-												loggedIn && station.isFavorited
-											"
-											@click.prevent="unfavoriteStation()"
-											content="Unfavorite Station"
-											v-tippy
-											class="material-icons"
-											>star</i
-										>
-										<i
-											v-if="
-												loggedIn && !station.isFavorited
-											"
-											@click.prevent="favoriteStation()"
-											class="material-icons"
-											content="Favorite Station"
-											v-tippy
-											>star_border</i
-										>
-									</a>
-								</div>
-								<p>{{ station.description }}</p>
-							</div>
-
-							<div id="admin-buttons" v-if="isOwnerOrAdmin()">
-								<!-- (Admin) Pause/Resume Button -->
-								<button
-									class="button is-danger"
-									v-if="stationPaused"
-									@click="resumeStation()"
-								>
-									<i class="material-icons icon-with-button"
-										>play_arrow</i
-									>
-									<span> Resume Station </span>
-								</button>
-								<button
-									class="button is-danger"
-									@click="pauseStation()"
-									v-else
-								>
-									<i class="material-icons icon-with-button"
-										>pause</i
-									>
-									<span> Pause Station </span>
-								</button>
-
-								<!-- (Admin) Skip Button -->
-								<button
-									class="button is-danger"
-									@click="skipStation()"
-								>
-									<i class="material-icons icon-with-button"
-										>skip_next</i
-									>
-									<span> Force Skip </span>
-								</button>
-
-								<!-- (Admin) Station Settings Button -->
-								<button
-									class="button is-primary"
-									@click="
-										openModal({
-											modal: 'manageStation',
-											data: {
-												stationId: station._id,
-												sector: 'station'
-											}
-										})
-									"
-								>
-									<i class="material-icons icon-with-button"
-										>settings</i
-									>
-									<span> Manage Station </span>
-								</button>
-							</div>
+						<!-- div with quadrant class -->
+						<div class="quadrant">
+							<station-info-box
+								:station="station"
+								:station-paused="stationPaused"
+								:show-manage-station="true"
+							/>
 						</div>
 						<div id="sidebar-container" class="quadrant">
 							<station-sidebar />
@@ -782,6 +697,7 @@ import ws from "@/ws";
 import keyboardShortcuts from "@/keyboardShortcuts";
 
 import FloatingBox from "@/components/FloatingBox.vue";
+import StationInfoBox from "@/components/StationInfoBox.vue";
 import AddToPlaylistDropdown from "@/components/AddToPlaylistDropdown.vue";
 import SongItem from "@/components/SongItem.vue";
 import Z404 from "../404.vue";
@@ -795,6 +711,7 @@ export default {
 		ContentLoader,
 		Z404,
 		FloatingBox,
+		StationInfoBox,
 		StationSidebar,
 		AddToPlaylistDropdown,
 		SongItem
@@ -2301,7 +2218,6 @@ export default {
 .night-mode {
 	#currently-playing-container,
 	#next-up-container,
-	#about-station-container,
 	#control-bar-container,
 	.player-container {
 		background-color: var(--dark-grey-3) !important;
@@ -2357,6 +2273,7 @@ export default {
 		.quadrant {
 			border-radius: @border-radius;
 			margin: 10px;
+			overflow: hidden;
 		}
 
 		.quadrant:not(#sidebar-container) {
@@ -2369,60 +2286,6 @@ export default {
 			padding: 0;
 		}
 
-		#about-station-container {
-			padding: 20px;
-			display: flex;
-			flex-direction: column;
-			flex-grow: unset;
-
-			#station-info {
-				#station-name {
-					flex-direction: row !important;
-
-					h1 {
-						margin: 0;
-						font-size: 36px;
-						line-height: 0.8;
-						text-overflow: ellipsis;
-						overflow: hidden;
-					}
-
-					i {
-						margin-left: 10px;
-						font-size: 30px;
-						color: var(--yellow);
-						&.stationMode {
-							padding-left: 10px;
-							margin-left: auto;
-							color: var(--primary-color);
-						}
-					}
-
-					.verified-station {
-						color: var(--primary-color);
-					}
-				}
-
-				p {
-					display: -webkit-box;
-					max-width: 700px;
-					margin-bottom: 10px;
-					overflow: hidden;
-					text-overflow: ellipsis;
-					-webkit-box-orient: vertical;
-					-webkit-line-clamp: 3;
-				}
-			}
-
-			#admin-buttons {
-				display: flex;
-
-				.button {
-					margin: 3px;
-				}
-			}
-		}
-
 		#current-next-row {
 			display: flex;
 			flex-direction: row;

+ 7 - 1
frontend/src/store/modules/modals/manageStation.js

@@ -34,7 +34,10 @@ export default {
 			commit("updateStationPaused", stationPaused),
 		updateCurrentSong: ({ commit }, currentSong) =>
 			commit("updateCurrentSong", currentSong),
-		updateStation: ({ commit }, station) => commit("updateStation", station)
+		updateStation: ({ commit }, station) =>
+			commit("updateStation", station),
+		updateIsFavorited: ({ commit }, isFavorited) =>
+			commit("updateIsFavorited", isFavorited)
 	},
 	mutations: {
 		init(state, { stationId, sector }) {
@@ -93,6 +96,9 @@ export default {
 		},
 		updateStation(state, station) {
 			state.station = { ...state.station, ...station };
+		},
+		updateIsFavorited(state, isFavorited) {
+			state.station.isFavorited = isFavorited;
 		}
 	}
 };