Browse Source

Added Manage Station settings functionality

Owen Diffey 3 years ago
parent
commit
4ed3dab1cb

+ 237 - 61
frontend/src/components/modals/ManageStation/Tabs/Settings.vue

@@ -1,17 +1,15 @@
 <template>
 	<div class="station-settings">
-		<label class="label">Change Station Name</label>
+		<label class="label">Name</label>
 		<div class="control is-grouped input-with-button">
 			<p class="control is-expanded">
 				<input class="input" type="text" v-model="station.name" />
 			</p>
 			<p class="control">
-				<a class="button is-info" @click.prevent="saveChanges()"
-					>Save</a
-				>
+				<a class="button is-info" @click.prevent="updateName()">Save</a>
 			</p>
 		</div>
-		<label class="label">Change Station Display Name</label>
+		<label class="label">Display Name</label>
 		<div class="control is-grouped input-with-button">
 			<p class="control is-expanded">
 				<input
@@ -21,12 +19,12 @@
 				/>
 			</p>
 			<p class="control">
-				<a class="button is-info" @click.prevent="saveChanges()"
+				<a class="button is-info" @click.prevent="updateDisplayName()"
 					>Save</a
 				>
 			</p>
 		</div>
-		<label class="label">Change Description</label>
+		<label class="label">Description</label>
 		<div class="control is-grouped input-with-button">
 			<p class="control is-expanded">
 				<input
@@ -36,7 +34,7 @@
 				/>
 			</p>
 			<p class="control">
-				<a class="button is-info" @click.prevent="saveChanges()"
+				<a class="button is-info" @click.prevent="updateDescription()"
 					>Save</a
 				>
 			</p>
@@ -61,7 +59,7 @@
 					<button
 						class="blue"
 						v-if="station.theme !== 'blue'"
-						@click="updateThemeLocal('blue')"
+						@click="updateTheme('blue')"
 					>
 						<i class="material-icons">palette</i>
 						Blue
@@ -69,7 +67,7 @@
 					<button
 						class="purple"
 						v-if="station.theme !== 'purple'"
-						@click="updateThemeLocal('purple')"
+						@click="updateTheme('purple')"
 					>
 						<i class="material-icons">palette</i>
 						Purple
@@ -77,7 +75,7 @@
 					<button
 						class="teal"
 						v-if="station.theme !== 'teal'"
-						@click="updateThemeLocal('teal')"
+						@click="updateTheme('teal')"
 					>
 						<i class="material-icons">palette</i>
 						Teal
@@ -85,7 +83,7 @@
 					<button
 						class="orange"
 						v-if="station.theme !== 'orange'"
-						@click="updateThemeLocal('orange')"
+						@click="updateTheme('orange')"
 					>
 						<i class="material-icons">palette</i>
 						Orange
@@ -113,7 +111,7 @@
 					<button
 						class="green"
 						v-if="station.privacy !== 'public'"
-						@click="updatePrivacyLocal('public')"
+						@click="updatePrivacy('public')"
 					>
 						<i class="material-icons">{{
 							privacyButtons["public"].iconName
@@ -123,7 +121,7 @@
 					<button
 						class="orange"
 						v-if="station.privacy !== 'unlisted'"
-						@click="updatePrivacyLocal('unlisted')"
+						@click="updatePrivacy('unlisted')"
 					>
 						<i class="material-icons">{{
 							privacyButtons["unlisted"].iconName
@@ -133,7 +131,7 @@
 					<button
 						class="red"
 						v-if="station.privacy !== 'private'"
-						@click="updatePrivacyLocal('private')"
+						@click="updatePrivacy('private')"
 					>
 						<i class="material-icons">{{
 							privacyButtons["private"].iconName
@@ -170,7 +168,7 @@
 					<button
 						class="blue"
 						v-if="station.partyMode"
-						@click="updatePartyModeLocal(false)"
+						@click="updatePartyMode(false)"
 					>
 						<i class="material-icons">playlist_play</i>
 						Playlist
@@ -178,7 +176,7 @@
 					<button
 						class="yellow"
 						v-if="!station.partyMode"
-						@click="updatePartyModeLocal(true)"
+						@click="updatePartyMode(true)"
 					>
 						<i class="material-icons">emoji_people</i>
 						Party
@@ -212,7 +210,7 @@
 					<button
 						class="blue"
 						v-if="station.playMode === 'sequential'"
-						@click="updatePlayModeLocal('random')"
+						@click="updatePlayMode('random')"
 					>
 						<i class="material-icons">shuffle</i>
 						Random
@@ -220,7 +218,7 @@
 					<button
 						class="blue"
 						v-if="station.playMode === 'random'"
-						@click="updatePlayModeLocal('sequential')"
+						@click="updatePlayMode('sequential')"
 					>
 						<i class="material-icons">format_list_numbered</i>
 						Sequential
@@ -234,44 +232,44 @@
 				class="small-section"
 			>
 				<label class="label">Queue lock</label>
-				<div class="button-wrapper">
-					<button
-						:class="{
-							green: station.locked,
-							red: !station.locked
-						}"
-						@click="
-							station.locked
-								? updateQueueLockLocal(true)
-								: updateQueueLockLocal(false)
-						"
-					>
-						<i class="material-icons">{{
-							station.locked ? "lock" : "lock_open"
-						}}</i>
-						{{ station.locked ? "Locked" : "Unlocked" }}
-					</button>
-					<transition name="slide-down">
-						<button
-							class="green"
-							v-if="!station.locked"
-							@click="updateQueueLockLocal(true)"
-						>
-							<i class="material-icons">lock</i>
-							Locked
-						</button>
-					</transition>
-					<transition name="slide-down">
+				<tippy
+					class="button-wrapper"
+					theme="addToPlaylist"
+					interactive="true"
+					placement="bottom"
+					trigger="click"
+					append-to="parent"
+				>
+					<template #trigger>
 						<button
-							class="red"
-							v-if="station.locked"
-							@click="updateQueueLockLocal(false)"
+							:class="{
+								green: station.locked,
+								red: !station.locked
+							}"
 						>
-							<i class="material-icons">lock_open</i>
-							Unlocked
+							<i class="material-icons">{{
+								station.locked ? "lock" : "lock_open"
+							}}</i>
+							{{ station.locked ? "Locked" : "Unlocked" }}
 						</button>
-					</transition>
-				</div>
+					</template>
+					<button
+						class="green"
+						v-if="!station.locked"
+						@click="updateQueueLock(true)"
+					>
+						<i class="material-icons">lock</i>
+						Locked
+					</button>
+					<button
+						class="red"
+						v-if="station.locked"
+						@click="updateQueueLock(false)"
+					>
+						<i class="material-icons">lock_open</i>
+						Unlocked
+					</button>
+				</tippy>
 			</div>
 		</div>
 	</div>
@@ -280,6 +278,10 @@
 <script>
 import { mapState, mapGetters } from "vuex";
 
+import Toast from "toasters";
+
+import validation from "@/validation";
+
 export default {
 	data() {
 		return {
@@ -300,8 +302,8 @@ export default {
 		};
 	},
 	computed: {
-		...mapState({
-			station: state => state.station.station,
+		...mapState("modals/editStation", {
+			station: state => state.station,
 			originalStation: state => state.originalStation
 		}),
 		...mapGetters({
@@ -309,11 +311,185 @@ export default {
 		})
 	},
 	methods: {
-		updateThemeLocal() {},
-		updatePrivacyLocal() {},
-		updatePartyModeLocal() {},
-		updatePlayModeLocal() {},
-		updateQueueLockLocal() {}
+		updateName() {
+			if (this.originalStation.name !== this.station.name) {
+				const { name } = this.station;
+				if (!validation.isLength(name, 2, 16)) {
+					new Toast("Name must have between 2 and 16 characters.");
+				} else if (!validation.regex.az09_.test(name)) {
+					new Toast(
+						"Invalid name format. Allowed characters: a-z, 0-9 and _."
+					);
+				} else {
+					this.socket.dispatch(
+						"stations.updateName",
+						this.station._id,
+						name,
+						res => {
+							new Toast(res.message);
+
+							if (res.status === "success") {
+								this.station.name = name;
+								this.originalStation.name = name;
+							}
+						}
+					);
+				}
+			} else {
+				new Toast("Please make a change before saving.");
+			}
+		},
+		updateDisplayName() {
+			if (this.originalStation.displayName !== this.station.displayName) {
+				const { displayName } = this.station;
+				if (!validation.isLength(displayName, 2, 32)) {
+					new Toast(
+						"Display name must have between 2 and 32 characters."
+					);
+				} else if (!validation.regex.ascii.test(displayName)) {
+					new Toast(
+						"Invalid display name format. Only ASCII characters are allowed."
+					);
+				} else {
+					this.socket.dispatch(
+						"stations.updateDisplayName",
+						this.station._id,
+						displayName,
+						res => {
+							new Toast(res.message);
+
+							if (res.status === "success") {
+								this.station.displayName = displayName;
+								this.originalStation.displayName = displayName;
+							}
+						}
+					);
+				}
+			} else {
+				new Toast("Please make a change before saving.");
+			}
+		},
+		updateDescription() {
+			if (this.originalStation.description !== this.station.description) {
+				const { description } = this.station;
+				const characters = description.split("").filter(character => {
+					return character.charCodeAt(0) === 21328;
+				});
+				if (!validation.isLength(description, 2, 200)) {
+					new Toast(
+						"Description must have between 2 and 200 characters."
+					);
+				} else if (characters.length !== 0) {
+					new Toast("Invalid description format.");
+				} else {
+					this.socket.dispatch(
+						"stations.updateDescription",
+						this.station._id,
+						description,
+						res => {
+							new Toast(res.message);
+
+							if (res.status === "success") {
+								this.station.description = description;
+								this.originalStation.description = description;
+							}
+						}
+					);
+				}
+			} else {
+				new Toast("Please make a change before saving.");
+			}
+		},
+		updateTheme(theme) {
+			if (this.station.theme !== theme) {
+				this.socket.dispatch(
+					"stations.updateTheme",
+					this.station._id,
+					theme,
+					res => {
+						new Toast(res.message);
+
+						if (res.status === "success") {
+							this.station.theme = theme;
+							this.originalStation.theme = theme;
+						}
+					}
+				);
+			}
+		},
+		updatePrivacy(privacy) {
+			if (this.station.privacy !== privacy) {
+				this.socket.dispatch(
+					"stations.updatePrivacy",
+					this.station._id,
+					privacy,
+					res => {
+						new Toast(res.message);
+
+						if (res.status === "success") {
+							this.station.privacy = privacy;
+							this.originalStation.privacy = privacy;
+						}
+					}
+				);
+			}
+		},
+		updatePartyMode(partyMode) {
+			if (this.station.partyMode !== partyMode) {
+				this.socket.dispatch(
+					"stations.updatePartyMode",
+					this.station._id,
+					partyMode,
+					res => {
+						new Toast(res.message);
+
+						if (res.status === "success") {
+							this.station.partyMode = partyMode;
+							this.originalStation.partyMode = partyMode;
+						}
+					}
+				);
+			}
+		},
+		updatePlayMode(playMode) {
+			if (this.station.playMode !== playMode) {
+				this.socket.dispatch(
+					"stations.updatePlayMode",
+					this.station._id,
+					playMode,
+					res => {
+						new Toast(res.message);
+
+						if (res.status === "success") {
+							this.station.playMode = playMode;
+							this.originalStation.playMode = playMode;
+						}
+					}
+				);
+			}
+		},
+		updateQueueLock(locked) {
+			if (this.station.locked !== locked) {
+				this.socket.dispatch(
+					"stations.toggleLock",
+					this.station._id,
+					res => {
+						if (res.status === "success") {
+							if (this.originalStation) {
+								this.station.locked = res.data;
+								this.originalStation.locked = res.data;
+							}
+
+							new Toast(
+								`Toggled queue lock successfully to ${res.data}`
+							);
+						} else {
+							new Toast("Failed to toggle queue lock.");
+						}
+					}
+				);
+			}
+		}
 	}
 };
 </script>

+ 12 - 4
frontend/src/components/modals/ManageStation/index.vue

@@ -72,7 +72,6 @@ import { mapState, mapGetters, mapActions } from "vuex";
 import Toast from "toasters";
 import TabQueryHandler from "@/mixins/TabQueryHandler.vue";
 
-import validation from "@/validation";
 import Confirm from "@/components/Confirm.vue";
 import Modal from "../../Modal.vue";
 
@@ -158,13 +157,12 @@ export default {
 		flex-direction: column;
 		flex-grow: 1;
 		width: auto;
-		max-width: 600px;
 		padding: 15px !important;
 		margin: 0 10px;
 	}
 
 	.left-section {
-		max-width: 50%;
+		flex-basis: 50%;
 		height: 100%;
 		overflow-y: auto;
 		flex-grow: 1;
@@ -201,10 +199,20 @@ export default {
 		}
 	}
 	.right-section {
-		max-width: 50%;
+		flex-basis: 50%;
 		height: 100%;
 		overflow-y: auto;
 		flex-grow: 1;
 	}
 }
+
+@media screen and (max-width: 1100px) {
+	.manage-station-modal.modal .modal-card-body .custom-modal-body {
+		.left-section,
+		.right-section {
+			flex-basis: unset;
+			height: auto;
+		}
+	}
+}
 </style>