Prechádzať zdrojové kódy

Removed my manage station modal

Owen Diffey 4 rokov pred
rodič
commit
76cce582da

+ 1 - 2
frontend/dist/config/template.json

@@ -22,7 +22,6 @@
 	"messages": {
 		"accountRemoval": "Your account will be deactivated instantly and your data will shortly be deleted by an admin."
 	},
-	"manageStationVersion": "kris",
 	"skipConfigVersionCheck": false,
-	"configVersion": 5
+	"configVersion": 6
 }

+ 0 - 0
frontend/src/components/modals/ManageStationKris/Tabs/Playlists.vue → frontend/src/components/modals/ManageStation/Tabs/Playlists.vue


+ 0 - 0
frontend/src/components/modals/ManageStationKris/Tabs/Settings.vue → frontend/src/components/modals/ManageStation/Tabs/Settings.vue


+ 0 - 0
frontend/src/components/modals/ManageStationKris/Tabs/Songs.vue → frontend/src/components/modals/ManageStation/Tabs/Songs.vue


+ 0 - 0
frontend/src/components/modals/ManageStationKris/index.vue → frontend/src/components/modals/ManageStation/index.vue


+ 0 - 165
frontend/src/components/modals/ManageStationOwen/Tabs/Blacklist.vue

@@ -1,165 +0,0 @@
-<template>
-	<div class="station-blacklist">
-		<p class="has-text-centered">
-			Blacklist a playlist to prevent all of its songs playing in this
-			station.
-		</p>
-		<div class="tabs-container">
-			<!-- <div class="tab-selection">
-				<button
-					class="button is-default"
-					:class="{ selected: tab === 'playlists' }"
-					@click="showTab('playlists')"
-				>
-					Playlists
-				</button>
-				<button
-					class="button is-default"
-					:class="{ selected: tab === 'songs' }"
-					@click="showTab('songs')"
-				>
-					Songs
-				</button>
-			</div> -->
-			<div class="tab" v-show="tab === 'playlists'">
-				<div v-if="excludedPlaylists.length > 0">
-					<playlist-item
-						:playlist="playlist"
-						v-for="playlist in excludedPlaylists"
-						:key="`key-${playlist._id}`"
-					>
-						<template #actions>
-							<div class="icons-group">
-								<confirm
-									@confirm="deselectPlaylist(playlist._id)"
-								>
-									<i
-										class="material-icons stop-icon"
-										content="Stop blacklisting songs from this playlist
-							"
-										v-tippy
-										>stop</i
-									>
-								</confirm>
-								<i
-									v-if="playlist.createdBy === userId"
-									@click="showPlaylist(playlist._id)"
-									class="material-icons edit-icon"
-									content="Edit Playlist"
-									v-tippy
-									>edit</i
-								>
-								<i
-									v-else
-									@click="showPlaylist(playlist._id)"
-									class="material-icons edit-icon"
-									content="View Playlist"
-									v-tippy
-									>visibility</i
-								>
-							</div>
-						</template>
-					</playlist-item>
-				</div>
-				<p v-else class="has-text-centered scrollable-list">
-					No playlists currently blacklisted.
-				</p>
-			</div>
-			<!-- <div class="tab" v-show="tab === 'songs'">
-				Blacklisting songs has yet to be added.
-			</div> -->
-		</div>
-	</div>
-</template>
-<script>
-import { mapActions, mapState, mapGetters } from "vuex";
-
-import Toast from "toasters";
-import PlaylistItem from "@/components/PlaylistItem.vue";
-import Confirm from "@/components/Confirm.vue";
-
-export default {
-	components: {
-		PlaylistItem,
-		Confirm
-	},
-	data() {
-		return {
-			tab: "playlists"
-		};
-	},
-	computed: {
-		...mapState({
-			userId: state => state.user.auth.userId
-		}),
-		...mapState("modals/manageStation", {
-			station: state => state.station,
-			originalStation: state => state.originalStation,
-			excludedPlaylists: state => state.excludedPlaylists
-		}),
-		...mapGetters({
-			socket: "websockets/getSocket"
-		})
-	},
-	methods: {
-		showTab(tab) {
-			this.tab = tab;
-		},
-		showPlaylist(playlistId) {
-			this.editPlaylist(playlistId);
-			this.openModal("editPlaylist");
-		},
-		deselectPlaylist(id) {
-			this.socket.dispatch(
-				"stations.removeExcludedPlaylist",
-				this.station._id,
-				id,
-				res => {
-					new Toast(res.message);
-				}
-			);
-		},
-		...mapActions("modalVisibility", ["openModal"]),
-		...mapActions("user/playlists", ["editPlaylist"])
-	}
-};
-</script>
-
-<style lang="scss" scoped>
-.station-blacklist {
-	.tabs-container {
-		margin-top: 10px;
-		.tab-selection {
-			display: flex;
-			overflow-x: auto;
-			.button {
-				border-radius: 0;
-				border: 0;
-				text-transform: uppercase;
-				font-size: 14px;
-				color: var(--dark-grey-3);
-				background-color: var(--light-grey-2);
-				flex-grow: 1;
-				height: 32px;
-
-				&:not(:first-of-type) {
-					margin-left: 5px;
-				}
-			}
-
-			.selected {
-				background-color: var(--primary-color) !important;
-				color: var(--white) !important;
-				font-weight: 600;
-			}
-		}
-		.tab {
-			padding: 15px 0;
-			border-radius: 0;
-			.playlist-item:not(:last-of-type) {
-				margin-bottom: 10px;
-			}
-		}
-	}
-}
-</style>

+ 0 - 646
frontend/src/components/modals/ManageStationOwen/Tabs/Playlists.vue

@@ -1,646 +0,0 @@
-<template>
-	<div class="station-playlists">
-		<div class="tabs-container">
-			<div class="tab-selection">
-				<button
-					class="button is-default"
-					ref="current-tab"
-					:class="{ selected: tab === 'current' }"
-					@click="showTab('current')"
-				>
-					Current
-				</button>
-				<button
-					class="button is-default"
-					ref="search-tab"
-					:class="{ selected: tab === 'search' }"
-					@click="showTab('search')"
-				>
-					Search
-				</button>
-				<button
-					v-if="station.type === 'community'"
-					class="button is-default"
-					ref="my-playlists-tab"
-					:class="{ selected: tab === 'my-playlists' }"
-					@click="showTab('my-playlists')"
-				>
-					My Playlists
-				</button>
-			</div>
-			<div class="tab" v-show="tab === 'current'">
-				<div v-if="currentPlaylists.length > 0">
-					<playlist-item
-						v-for="playlist in currentPlaylists"
-						:key="`key-${playlist._id}`"
-						:playlist="playlist"
-						:show-owner="true"
-					>
-						<template #actions>
-							<div class="icons-group">
-								<confirm
-									v-if="isOwnerOrAdmin()"
-									@confirm="deselectPlaylist(playlist._id)"
-								>
-									<i
-										class="material-icons stop-icon"
-										content="Stop playing songs from this playlist"
-										v-tippy
-									>
-										stop
-									</i>
-								</confirm>
-								<confirm
-									v-if="isOwnerOrAdmin()"
-									@confirm="blacklistPlaylist(playlist._id)"
-								>
-									<i
-										class="material-icons stop-icon"
-										content="Blacklist Playlist"
-										v-tippy
-										>block</i
-									>
-								</confirm>
-								<i
-									v-if="playlist.createdBy === myUserId"
-									@click="showPlaylist(playlist._id)"
-									class="material-icons edit-icon"
-									content="Edit Playlist"
-									v-tippy
-									>edit</i
-								>
-								<i
-									v-if="
-										playlist.createdBy !== myUserId &&
-											(playlist.privacy === 'public' ||
-												isAdmin())
-									"
-									@click="showPlaylist(playlist._id)"
-									class="material-icons edit-icon"
-									content="View Playlist"
-									v-tippy
-									>visibility</i
-								>
-							</div>
-						</template>
-					</playlist-item>
-				</div>
-				<p v-else class="has-text-centered scrollable-list">
-					No playlists currently selected.
-				</p>
-			</div>
-			<div class="tab" v-show="tab === 'search'">
-				<label class="label"> Search for a public playlist </label>
-				<div class="control is-grouped input-with-button">
-					<p class="control is-expanded">
-						<input
-							class="input"
-							type="text"
-							placeholder="Enter your playlist query here..."
-							v-model="search.query"
-							@keyup.enter="searchForPlaylists(1)"
-						/>
-					</p>
-					<p class="control">
-						<a class="button is-info" @click="searchForPlaylists(1)"
-							><i class="material-icons icon-with-button"
-								>search</i
-							>Search</a
-						>
-					</p>
-				</div>
-				<div v-if="search.results.length > 0">
-					<playlist-item
-						v-for="playlist in search.results"
-						:key="`searchKey-${playlist._id}`"
-						:playlist="playlist"
-						:show-owner="true"
-					>
-						<template #actions>
-							<div class="icons-group">
-								<i
-									v-if="isExcluded(playlist._id)"
-									class="material-icons stop-icon"
-									content="This playlist is blacklisted in this station"
-									v-tippy="{ theme: 'info' }"
-									>play_disabled</i
-								>
-								<confirm
-									v-if="
-										(isOwnerOrAdmin() ||
-											(station.type === 'community' &&
-												station.partyMode)) &&
-											isSelected(playlist._id)
-									"
-									@confirm="deselectPlaylist(playlist._id)"
-								>
-									<i
-										class="material-icons stop-icon"
-										content="Stop playing songs from this playlist"
-										v-tippy
-									>
-										stop
-									</i>
-								</confirm>
-								<i
-									v-if="
-										(isOwnerOrAdmin() ||
-											(station.type === 'community' &&
-												station.partyMode)) &&
-											!isSelected(playlist._id) &&
-											!isExcluded(playlist._id)
-									"
-									@click="selectPlaylist(playlist)"
-									class="material-icons play-icon"
-									:content="
-										station.partyMode
-											? 'Request songs from this playlist'
-											: 'Play songs from this playlist'
-									"
-									v-tippy
-									>play_arrow</i
-								>
-								<confirm
-									v-if="
-										isOwnerOrAdmin() &&
-											!isExcluded(playlist._id)
-									"
-									@confirm="blacklistPlaylist(playlist._id)"
-								>
-									<i
-										class="material-icons stop-icon"
-										content="Blacklist Playlist"
-										v-tippy
-										>block</i
-									>
-								</confirm>
-								<i
-									v-if="playlist.createdBy === myUserId"
-									@click="showPlaylist(playlist._id)"
-									class="material-icons edit-icon"
-									content="Edit Playlist"
-									v-tippy
-									>edit</i
-								>
-								<i
-									v-if="
-										playlist.createdBy !== myUserId &&
-											(playlist.privacy === 'public' ||
-												isAdmin())
-									"
-									@click="showPlaylist(playlist._id)"
-									class="material-icons edit-icon"
-									content="View Playlist"
-									v-tippy
-									>visibility</i
-								>
-							</div>
-						</template>
-					</playlist-item>
-					<button
-						v-if="resultsLeftCount > 0"
-						class="button is-primary load-more-button"
-						@click="searchForPlaylists(search.page + 1)"
-					>
-						Load {{ nextPageResultsCount }} more results
-					</button>
-				</div>
-			</div>
-			<div
-				v-if="station.type === 'community'"
-				class="tab"
-				v-show="tab === 'my-playlists'"
-			>
-				<button
-					class="button is-primary"
-					id="create-new-playlist-button"
-					@click="openModal('createPlaylist')"
-				>
-					Create new playlist
-				</button>
-				<div
-					class="menu-list scrollable-list"
-					v-if="playlists.length > 0"
-				>
-					<draggable
-						tag="transition-group"
-						:component-data="{
-							name: !drag ? 'draggable-list-transition' : null
-						}"
-						item-key="_id"
-						v-model="playlists"
-						v-bind="dragOptions"
-						@start="drag = true"
-						@end="drag = false"
-						@change="savePlaylistOrder"
-					>
-						<template #item="{element}">
-							<playlist-item
-								class="item-draggable"
-								:playlist="element"
-							>
-								<template #actions>
-									<i
-										v-if="isExcluded(element._id)"
-										class="material-icons stop-icon"
-										content="This playlist is blacklisted in this station"
-										v-tippy="{ theme: 'info' }"
-										>play_disabled</i
-									>
-									<i
-										v-if="
-											station.type === 'community' &&
-												(isOwnerOrAdmin() ||
-													station.partyMode) &&
-												!isSelected(element._id) &&
-												!isExcluded(element._id)
-										"
-										@click="selectPlaylist(element)"
-										class="material-icons play-icon"
-										:content="
-											station.partyMode
-												? 'Request songs from this playlist'
-												: 'Play songs from this playlist'
-										"
-										v-tippy
-										>play_arrow</i
-									>
-									<confirm
-										v-if="
-											station.type === 'community' &&
-												(isOwnerOrAdmin() ||
-													station.partyMode) &&
-												isSelected(element._id)
-										"
-										@confirm="deselectPlaylist(element._id)"
-									>
-										<i
-											class="material-icons stop-icon"
-											:content="
-												station.partyMode
-													? 'Stop requesting songs from this playlist'
-													: 'Stop playing songs from this playlist'
-											"
-											v-tippy
-											>stop</i
-										>
-									</confirm>
-									<confirm
-										v-if="
-											isOwnerOrAdmin() &&
-												!isExcluded(element._id)
-										"
-										@confirm="
-											blacklistPlaylist(element._id)
-										"
-									>
-										<i
-											class="material-icons stop-icon"
-											content="Blacklist Playlist"
-											v-tippy
-											>block</i
-										>
-									</confirm>
-									<i
-										@click="showPlaylist(element._id)"
-										class="material-icons edit-icon"
-										content="Edit Playlist"
-										v-tippy
-										>edit</i
-									>
-								</template>
-							</playlist-item>
-						</template>
-					</draggable>
-				</div>
-
-				<p v-else class="has-text-centered scrollable-list">
-					You don't have any playlists!
-				</p>
-			</div>
-		</div>
-	</div>
-</template>
-<script>
-import { mapActions, mapState, mapGetters } from "vuex";
-
-import Toast from "toasters";
-import PlaylistItem from "@/components/PlaylistItem.vue";
-import Confirm from "@/components/Confirm.vue";
-
-import SortablePlaylists from "@/mixins/SortablePlaylists.vue";
-
-export default {
-	components: {
-		PlaylistItem,
-		Confirm
-	},
-	mixins: [SortablePlaylists],
-	data() {
-		return {
-			tab: "current",
-			search: {
-				query: "",
-				searchedQuery: "",
-				page: 0,
-				count: 0,
-				resultsLeft: 0,
-				results: []
-			}
-		};
-	},
-	computed: {
-		currentPlaylists() {
-			if (this.station.type === "community" && this.station.partyMode) {
-				return this.partyPlaylists;
-			}
-			return this.includedPlaylists;
-		},
-		resultsLeftCount() {
-			return this.search.count - this.search.results.length;
-		},
-		nextPageResultsCount() {
-			return Math.min(this.search.pageSize, this.resultsLeftCount);
-		},
-		...mapState({
-			loggedIn: state => state.user.auth.loggedIn,
-			role: state => state.user.auth.role,
-			userId: state => state.user.auth.userId,
-			partyPlaylists: state => state.station.partyPlaylists
-		}),
-		...mapState("modals/manageStation", {
-			originalStation: state => state.originalStation,
-			station: state => state.station,
-			includedPlaylists: state => state.includedPlaylists,
-			excludedPlaylists: state => state.excludedPlaylists,
-			songsList: state => state.songsList
-		}),
-		...mapGetters({
-			socket: "websockets/getSocket"
-		})
-	},
-	mounted() {
-		this.socket.dispatch("playlists.indexMyPlaylists", true, res => {
-			if (res.status === "success") this.setPlaylists(res.data.playlists);
-			this.orderOfPlaylists = this.calculatePlaylistOrder(); // order in regards to the database
-		});
-
-		this.socket.dispatch(
-			`stations.getStationIncludedPlaylistsById`,
-			this.station._id,
-			res => {
-				if (res.status === "success") {
-					this.station.includedPlaylists = res.data.playlists;
-					this.originalStation.includedPlaylists = res.data.playlists;
-				}
-			}
-		);
-
-		this.socket.dispatch(
-			`stations.getStationExcludedPlaylistsById`,
-			this.station._id,
-			res => {
-				if (res.status === "success") {
-					this.station.excludedPlaylists = res.data.playlists;
-					this.originalStation.excludedPlaylists = res.data.playlists;
-				}
-			}
-		);
-	},
-	methods: {
-		showTab(tab) {
-			this.$refs[`${tab}-tab`].scrollIntoView();
-			this.tab = tab;
-		},
-		isOwner() {
-			return this.loggedIn && this.userId === this.station.owner;
-		},
-		isAdmin() {
-			return this.loggedIn && this.role === "admin";
-		},
-		isOwnerOrAdmin() {
-			return this.isOwner() || this.isAdmin();
-		},
-		showPlaylist(playlistId) {
-			this.editPlaylist(playlistId);
-			this.openModal("editPlaylist");
-		},
-		selectPlaylist(playlist) {
-			if (this.station.type === "community" && this.station.partyMode) {
-				if (!this.isSelected(playlist.id)) {
-					this.partyPlaylists.push(playlist);
-					this.addPartyPlaylistSongToQueue();
-					new Toast(
-						"Successfully selected playlist to auto request songs."
-					);
-				} else {
-					new Toast("Error: Playlist already selected.");
-				}
-			} else {
-				this.socket.dispatch(
-					"stations.includePlaylist",
-					this.station._id,
-					playlist._id,
-					res => {
-						new Toast(res.message);
-					}
-				);
-			}
-		},
-		deselectPlaylist(id) {
-			return new Promise(resolve => {
-				if (
-					this.station.type === "community" &&
-					this.station.partyMode
-				) {
-					let selected = false;
-					this.currentPlaylists.forEach((playlist, index) => {
-						if (playlist._id === id) {
-							selected = true;
-							this.partyPlaylists.splice(index, 1);
-						}
-					});
-					if (selected) {
-						new Toast("Successfully deselected playlist.");
-						resolve();
-					} else {
-						new Toast("Playlist not selected.");
-						resolve();
-					}
-				} else {
-					this.socket.dispatch(
-						"stations.removeIncludedPlaylist",
-						this.station._id,
-						id,
-						res => {
-							new Toast(res.message);
-							resolve();
-						}
-					);
-				}
-			});
-		},
-		isSelected(id) {
-			let selected = false;
-			this.currentPlaylists.forEach(playlist => {
-				if (playlist._id === id) selected = true;
-			});
-			return selected;
-		},
-		isExcluded(id) {
-			let selected = false;
-			this.excludedPlaylists.forEach(playlist => {
-				if (playlist._id === id) selected = true;
-			});
-			return selected;
-		},
-		searchForPlaylists(page) {
-			if (
-				this.search.page >= page ||
-				this.search.searchedQuery !== this.search.query
-			) {
-				this.search.results = [];
-				this.search.page = 0;
-				this.search.count = 0;
-				this.search.resultsLeft = 0;
-				this.search.pageSize = 0;
-			}
-
-			const { query } = this.search;
-			const action =
-				this.station.type === "official"
-					? "playlists.searchOfficial"
-					: "playlists.searchCommunity";
-
-			this.search.searchedQuery = this.search.query;
-			this.socket.dispatch(action, query, page, res => {
-				const { data } = res;
-				const { count, pageSize, playlists } = data;
-				if (res.status === "success") {
-					this.search.results = [
-						...this.search.results,
-						...playlists
-					];
-					this.search.page = page;
-					this.search.count = count;
-					this.search.resultsLeft =
-						count - this.search.results.length;
-					this.search.pageSize = pageSize;
-				} else if (res.status === "error") {
-					this.search.results = [];
-					this.search.page = 0;
-					this.search.count = 0;
-					this.search.resultsLeft = 0;
-					this.search.pageSize = 0;
-					new Toast(res.message);
-				}
-			});
-		},
-		async blacklistPlaylist(id) {
-			if (this.isSelected(id)) await this.deselectPlaylist(id);
-
-			this.socket.dispatch(
-				"stations.excludePlaylist",
-				this.station._id,
-				id,
-				res => {
-					new Toast(res.message);
-				}
-			);
-		},
-		addPartyPlaylistSongToQueue() {
-			let isInQueue = false;
-			if (
-				this.station.type === "community" &&
-				this.station.partyMode === true
-			) {
-				this.songsList.forEach(queueSong => {
-					if (queueSong.requestedBy === this.userId) isInQueue = true;
-				});
-				if (!isInQueue && this.partyPlaylists) {
-					const selectedPlaylist = this.partyPlaylists[
-						Math.floor(Math.random() * this.partyPlaylists.length)
-					];
-					if (
-						selectedPlaylist._id &&
-						selectedPlaylist.songs.length > 0
-					) {
-						const selectedSong =
-							selectedPlaylist.songs[
-								Math.floor(
-									Math.random() *
-										selectedPlaylist.songs.length
-								)
-							];
-						if (selectedSong.youtubeId) {
-							this.socket.dispatch(
-								"stations.addToQueue",
-								this.station._id,
-								selectedSong.youtubeId,
-								data => {
-									if (data.status !== "success")
-										new Toast("Error auto queueing song");
-								}
-							);
-						}
-					}
-				}
-			}
-		},
-		...mapActions("station", ["updatePartyPlaylists"]),
-		...mapActions("modalVisibility", ["openModal"]),
-		...mapActions("user/playlists", ["editPlaylist", "setPlaylists"])
-	}
-};
-</script>
-
-<style lang="scss" scoped>
-.station-playlists {
-	.tabs-container {
-		.tab-selection {
-			display: flex;
-			overflow-x: auto;
-			.button {
-				border-radius: 0;
-				border: 0;
-				text-transform: uppercase;
-				font-size: 14px;
-				color: var(--dark-grey-3);
-				background-color: var(--light-grey-2);
-				flex-grow: 1;
-				height: 32px;
-
-				&:not(:first-of-type) {
-					margin-left: 5px;
-				}
-			}
-
-			.selected {
-				background-color: var(--primary-color) !important;
-				color: var(--white) !important;
-				font-weight: 600;
-			}
-		}
-		.tab {
-			padding: 15px 0;
-			border-radius: 0;
-			.playlist-item:not(:last-of-type),
-			.item.item-draggable:not(:last-of-type) {
-				margin-bottom: 10px;
-			}
-			.load-more-button {
-				width: 100%;
-				margin-top: 10px;
-			}
-		}
-	}
-}
-.draggable-list-transition-move {
-	transition: transform 0.5s;
-}
-
-.draggable-list-ghost {
-	opacity: 0.5;
-	filter: brightness(95%);
-}
-</style>

+ 0 - 247
frontend/src/components/modals/ManageStationOwen/Tabs/Search.vue

@@ -1,247 +0,0 @@
-<template>
-	<div class="search">
-		<div class="musare-search">
-			<label class="label"> Search for a song on Musare </label>
-			<div class="control is-grouped input-with-button">
-				<p class="control is-expanded">
-					<input
-						class="input"
-						type="text"
-						placeholder="Enter your song query here..."
-						v-model="musareSearch.query"
-						@keyup.enter="searchForMusareSongs(1)"
-					/>
-				</p>
-				<p class="control">
-					<a class="button is-info" @click="searchForMusareSongs(1)"
-						><i class="material-icons icon-with-button">search</i
-						>Search</a
-					>
-				</p>
-			</div>
-			<div v-if="musareSearch.results.length > 0">
-				<song-item
-					v-for="song in musareSearch.results"
-					:key="song._id"
-					:song="song"
-				>
-					<template #actions>
-						<div class="icons-group">
-							<i
-								class="material-icons add-to-queue-icon"
-								v-if="station.partyMode && !station.locked"
-								@click="addSongToQueue(song.youtubeId)"
-								content="Add Song to Queue"
-								v-tippy
-								>queue</i
-							>
-						</div>
-					</template>
-				</song-item>
-				<button
-					v-if="resultsLeftCount > 0"
-					class="button is-primary load-more-button"
-					@click="searchForMusareSongs(musareSearch.page + 1)"
-				>
-					Load {{ nextPageResultsCount }} more results
-				</button>
-			</div>
-		</div>
-		<div class="youtube-search">
-			<label class="label"> Search for a song on YouTube </label>
-			<div class="control is-grouped input-with-button">
-				<p class="control is-expanded">
-					<input
-						class="input"
-						type="text"
-						placeholder="Enter your YouTube query here..."
-						v-model="search.songs.query"
-						autofocus
-						@keyup.enter="searchForSongs()"
-					/>
-				</p>
-				<p class="control">
-					<a class="button is-info" @click.prevent="searchForSongs()"
-						><i class="material-icons icon-with-button">search</i
-						>Search</a
-					>
-				</p>
-			</div>
-
-			<div v-if="search.songs.results.length > 0" id="song-query-results">
-				<search-query-item
-					v-for="(result, index) in search.songs.results"
-					:key="result.id"
-					:result="result"
-				>
-					<template #actions>
-						<transition name="search-query-actions" mode="out-in">
-							<a
-								class="button is-success"
-								v-if="result.isAddedToQueue"
-								key="added-to-queue"
-							>
-								<i class="material-icons icon-with-button"
-									>done</i
-								>
-								Added to queue
-							</a>
-							<a
-								class="button is-dark"
-								v-else
-								@click.prevent="
-									addSongToQueue(result.id, index)
-								"
-								key="add-to-queue"
-							>
-								<i class="material-icons icon-with-button"
-									>add</i
-								>
-								Add to queue
-							</a>
-						</transition>
-					</template>
-				</search-query-item>
-
-				<a
-					class="button is-primary load-more-button"
-					@click.prevent="loadMoreSongs()"
-				>
-					Load more...
-				</a>
-			</div>
-		</div>
-	</div>
-</template>
-
-<script>
-import { mapState, mapGetters } from "vuex";
-
-import Toast from "toasters";
-import SearchYoutube from "@/mixins/SearchYoutube.vue";
-
-import SongItem from "@/components/SongItem.vue";
-import SearchQueryItem from "../../../SearchQueryItem.vue";
-
-export default {
-	components: {
-		SongItem,
-		SearchQueryItem
-	},
-	mixins: [SearchYoutube],
-	data() {
-		return {
-			musareSearch: {
-				query: "",
-				searchedQuery: "",
-				page: 0,
-				count: 0,
-				resultsLeft: 0,
-				results: []
-			}
-		};
-	},
-	computed: {
-		resultsLeftCount() {
-			return this.musareSearch.count - this.musareSearch.results.length;
-		},
-		nextPageResultsCount() {
-			return Math.min(this.musareSearch.pageSize, this.resultsLeftCount);
-		},
-		...mapState("modals/manageStation", {
-			station: state => state.station,
-			originalStation: state => state.originalStation
-		}),
-		...mapGetters({
-			socket: "websockets/getSocket"
-		})
-	},
-	methods: {
-		addSongToQueue(youtubeId, index) {
-			if (this.station.type === "community") {
-				this.socket.dispatch(
-					"stations.addToQueue",
-					this.station._id,
-					youtubeId,
-					res => {
-						if (res.status !== "success")
-							new Toast(`Error: ${res.message}`);
-						else {
-							if (index)
-								this.search.songs.results[
-									index
-								].isAddedToQueue = true;
-
-							new Toast(res.message);
-						}
-					}
-				);
-			} else {
-				this.socket.dispatch("songs.request", youtubeId, res => {
-					if (res.status !== "success")
-						new Toast(`Error: ${res.message}`);
-					else {
-						this.search.songs.results[index].isAddedToQueue = true;
-
-						new Toast(res.message);
-					}
-				});
-			}
-		},
-		searchForMusareSongs(page) {
-			if (
-				this.musareSearch.page >= page ||
-				this.musareSearch.searchedQuery !== this.musareSearch.query
-			) {
-				this.musareSearch.results = [];
-				this.musareSearch.page = 0;
-				this.musareSearch.count = 0;
-				this.musareSearch.resultsLeft = 0;
-				this.musareSearch.pageSize = 0;
-			}
-
-			this.musareSearch.searchedQuery = this.musareSearch.query;
-			this.socket.dispatch(
-				"songs.searchOfficial",
-				this.musareSearch.query,
-				page,
-				res => {
-					const { data } = res;
-					const { count, pageSize, songs } = data;
-					if (res.status === "success") {
-						this.musareSearch.results = [
-							...this.musareSearch.results,
-							...songs
-						];
-						this.musareSearch.page = page;
-						this.musareSearch.count = count;
-						this.musareSearch.resultsLeft =
-							count - this.musareSearch.results.length;
-						this.musareSearch.pageSize = pageSize;
-					} else if (res.status === "error") {
-						this.musareSearch.results = [];
-						this.musareSearch.page = 0;
-						this.musareSearch.count = 0;
-						this.musareSearch.resultsLeft = 0;
-						this.musareSearch.pageSize = 0;
-						new Toast(res.message);
-					}
-				}
-			);
-		}
-	}
-};
-</script>
-
-<style lang="scss">
-.search {
-	.musare-search,
-	.universal-item:not(:last-of-type) {
-		margin-bottom: 10px;
-	}
-	.load-more-button {
-		width: 100%;
-		margin-top: 10px;
-	}
-}
-</style>

+ 0 - 608
frontend/src/components/modals/ManageStationOwen/Tabs/Settings.vue

@@ -1,608 +0,0 @@
-<template>
-	<div class="station-settings">
-		<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="updateName()">Save</a>
-			</p>
-		</div>
-		<label class="label">Display Name</label>
-		<div class="control is-grouped input-with-button">
-			<p class="control is-expanded">
-				<input
-					class="input"
-					type="text"
-					v-model="station.displayName"
-				/>
-			</p>
-			<p class="control">
-				<a class="button is-info" @click.prevent="updateDisplayName()"
-					>Save</a
-				>
-			</p>
-		</div>
-		<label class="label">Description</label>
-		<div class="control is-grouped input-with-button">
-			<p class="control is-expanded">
-				<input
-					class="input"
-					type="text"
-					v-model="station.description"
-				/>
-			</p>
-			<p class="control">
-				<a class="button is-info" @click.prevent="updateDescription()"
-					>Save</a
-				>
-			</p>
-		</div>
-		<div class="settings-buttons">
-			<div class="small-section">
-				<label class="label">Theme</label>
-				<div class="button-wrapper">
-					<tippy
-						theme="addToPlaylist"
-						:interactive="true"
-						:touch="true"
-						placement="bottom"
-						trigger="click"
-						append-to="parent"
-					>
-						<button :class="station.theme">
-							<i class="material-icons">palette</i>
-							{{ station.theme }}
-						</button>
-
-						<template #content>
-							<button
-								class="blue"
-								v-if="station.theme !== 'blue'"
-								@click="updateTheme('blue')"
-							>
-								<i class="material-icons">palette</i>
-								Blue
-							</button>
-							<button
-								class="purple"
-								v-if="station.theme !== 'purple'"
-								@click="updateTheme('purple')"
-							>
-								<i class="material-icons">palette</i>
-								Purple
-							</button>
-							<button
-								class="teal"
-								v-if="station.theme !== 'teal'"
-								@click="updateTheme('teal')"
-							>
-								<i class="material-icons">palette</i>
-								Teal
-							</button>
-							<button
-								class="orange"
-								v-if="station.theme !== 'orange'"
-								@click="updateTheme('orange')"
-							>
-								<i class="material-icons">palette</i>
-								Orange
-							</button>
-						</template>
-					</tippy>
-				</div>
-			</div>
-			<div class="small-section">
-				<label class="label">Privacy</label>
-				<div class="button-wrapper">
-					<tippy
-						theme="addToPlaylist"
-						:interactive="true"
-						:touch="true"
-						placement="bottom"
-						trigger="click"
-						append-to="parent"
-					>
-						<button :class="privacyButtons[station.privacy].style">
-							<i class="material-icons">{{
-								privacyButtons[station.privacy].iconName
-							}}</i>
-							{{ station.privacy }}
-						</button>
-
-						<template #content>
-							<button
-								class="green"
-								v-if="station.privacy !== 'public'"
-								@click="updatePrivacy('public')"
-							>
-								<i class="material-icons">{{
-									privacyButtons["public"].iconName
-								}}</i>
-								Public
-							</button>
-							<button
-								class="orange"
-								v-if="station.privacy !== 'unlisted'"
-								@click="updatePrivacy('unlisted')"
-							>
-								<i class="material-icons">{{
-									privacyButtons["unlisted"].iconName
-								}}</i>
-								Unlisted
-							</button>
-							<button
-								class="red"
-								v-if="station.privacy !== 'private'"
-								@click="updatePrivacy('private')"
-							>
-								<i class="material-icons">{{
-									privacyButtons["private"].iconName
-								}}</i>
-								Private
-							</button>
-						</template>
-					</tippy>
-				</div>
-			</div>
-			<div class="small-section">
-				<label class="label">Station Mode</label>
-				<div class="button-wrapper" v-if="station.type === 'community'">
-					<tippy
-						theme="addToPlaylist"
-						:interactive="true"
-						:touch="true"
-						placement="bottom"
-						trigger="click"
-						append-to="parent"
-					>
-						<button
-							:class="{
-								blue: !station.partyMode,
-								yellow: station.partyMode
-							}"
-						>
-							<i class="material-icons">{{
-								station.partyMode
-									? "emoji_people"
-									: "playlist_play"
-							}}</i>
-							{{ station.partyMode ? "Party" : "Playlist" }}
-						</button>
-
-						<template #content>
-							<button
-								class="blue"
-								v-if="station.partyMode"
-								@click="updatePartyMode(false)"
-							>
-								<i class="material-icons">playlist_play</i>
-								Playlist
-							</button>
-							<button
-								class="yellow"
-								v-if="!station.partyMode"
-								@click="updatePartyMode(true)"
-							>
-								<i class="material-icons">emoji_people</i>
-								Party
-							</button>
-						</template>
-					</tippy>
-				</div>
-				<div v-else class="button-wrapper">
-					<button
-						class="blue"
-						content="Can not be changed on official stations."
-						v-tippy="{ theme: 'info' }"
-					>
-						<i class="material-icons">playlist_play</i>
-						Playlist
-					</button>
-				</div>
-			</div>
-			<div v-if="!station.partyMode" class="small-section">
-				<label class="label">Play Mode</label>
-				<div class="button-wrapper" v-if="station.type === 'community'">
-					<tippy
-						theme="addToPlaylist"
-						:interactive="true"
-						:touch="true"
-						placement="bottom"
-						trigger="click"
-						append-to="parent"
-					>
-						<button class="blue">
-							<i class="material-icons">{{
-								station.playMode === "random"
-									? "shuffle"
-									: "format_list_numbered"
-							}}</i>
-							{{
-								station.playMode === "random"
-									? "Random"
-									: "Sequential"
-							}}
-						</button>
-
-						<template #content>
-							<div class="button-wrapper">
-								<button
-									class="blue"
-									v-if="station.playMode === 'sequential'"
-									@click="updatePlayMode('random')"
-								>
-									<i class="material-icons">shuffle</i>
-									Random
-								</button>
-								<button
-									class="blue"
-									v-if="station.playMode === 'random'"
-									@click="updatePlayMode('sequential')"
-								>
-									<i class="material-icons"
-										>format_list_numbered</i
-									>
-									Sequential
-								</button>
-							</div>
-						</template>
-					</tippy>
-				</div>
-
-				<div v-else class="button-wrapper">
-					<button
-						class="blue"
-						content="Can not be changed on official stations."
-						v-tippy="{ theme: 'info' }"
-					>
-						<i class="material-icons">shuffle</i>
-						Random
-					</button>
-				</div>
-			</div>
-			<div
-				v-if="
-					station.type === 'community' && station.partyMode === true
-				"
-				class="small-section"
-			>
-				<label class="label">Queue lock</label>
-				<div class="button-wrapper">
-					<tippy
-						theme="addToPlaylist"
-						:interactive="true"
-						:touch="true"
-						placement="bottom"
-						trigger="click"
-						append-to="parent"
-					>
-						<button
-							:class="{
-								green: station.locked,
-								red: !station.locked
-							}"
-						>
-							<i class="material-icons">{{
-								station.locked ? "lock" : "lock_open"
-							}}</i>
-							{{ station.locked ? "Locked" : "Unlocked" }}
-						</button>
-
-						<template #content>
-							<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>
-						</template>
-					</tippy>
-				</div>
-			</div>
-		</div>
-	</div>
-</template>
-
-<script>
-import { mapState, mapGetters } from "vuex";
-
-import Toast from "toasters";
-
-import validation from "@/validation";
-
-export default {
-	data() {
-		return {
-			privacyButtons: {
-				public: {
-					style: "green",
-					iconName: "public"
-				},
-				private: {
-					style: "red",
-					iconName: "lock"
-				},
-				unlisted: {
-					style: "orange",
-					iconName: "link"
-				}
-			}
-		};
-	},
-	computed: {
-		...mapState("modals/manageStation", {
-			station: state => state.station,
-			originalStation: state => state.originalStation
-		}),
-		...mapGetters({
-			socket: "websockets/getSocket"
-		})
-	},
-	methods: {
-		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.locked;
-								this.originalStation.locked = res.data.locked;
-							}
-
-							new Toast(
-								`Toggled queue lock successfully to ${res.data.locked}`
-							);
-						} else {
-							new Toast("Failed to toggle queue lock.");
-						}
-					}
-				);
-			}
-		}
-	}
-};
-</script>
-
-<style lang="scss" scoped>
-.station-settings {
-	.settings-buttons {
-		display: flex;
-		justify-content: center;
-		flex-wrap: wrap;
-		.small-section {
-			width: calc(50% - 10px);
-			min-width: 150px;
-			margin: 5px auto;
-		}
-	}
-	.button-wrapper {
-		display: flex;
-		flex-direction: column;
-
-		button {
-			width: 100%;
-			height: 36px;
-			border: 0;
-			border-radius: 3px;
-			font-size: 18px;
-			color: var(--white);
-			box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.25);
-			display: block;
-			text-align: center;
-			justify-content: center;
-			display: inline-flex;
-			-ms-flex-align: center;
-			align-items: center;
-			-moz-user-select: none;
-			user-select: none;
-			cursor: pointer;
-			padding: 0;
-			text-transform: capitalize;
-
-			&.red {
-				background-color: var(--red);
-			}
-
-			&.green {
-				background-color: var(--green);
-			}
-
-			&.blue {
-				background-color: var(--blue);
-			}
-
-			&.orange {
-				background-color: var(--orange);
-			}
-
-			&.yellow {
-				background-color: var(--yellow);
-			}
-
-			&.purple {
-				background-color: var(--purple);
-			}
-
-			&.teal {
-				background-color: var(--teal);
-			}
-
-			i {
-				font-size: 20px;
-				margin-right: 4px;
-			}
-		}
-	}
-}
-</style>

+ 0 - 681
frontend/src/components/modals/ManageStationOwen/index.vue

@@ -1,681 +0,0 @@
-<template>
-	<modal
-		v-if="station"
-		:title="
-			!isOwnerOrAdmin() && station.partyMode
-				? 'Add Song to Queue'
-				: 'Manage Station'
-		"
-		:style="`--primary-color: var(--${station.theme})`"
-		class="manage-station-modal"
-	>
-		<template #body>
-			<div class="custom-modal-body" v-if="station && station._id">
-				<div class="left-section">
-					<div class="section tabs-container">
-						<div class="tab-selection">
-							<button
-								v-if="isOwnerOrAdmin()"
-								class="button is-default"
-								:class="{ selected: tab === 'settings' }"
-								ref="settings-tab"
-								@click="showTab('settings')"
-							>
-								Settings
-							</button>
-							<button
-								v-if="
-									isOwnerOrAdmin() ||
-										(loggedIn &&
-											station.type === 'community' &&
-											station.partyMode &&
-											((station.locked &&
-												isOwnerOrAdmin()) ||
-												!station.locked))
-								"
-								class="button is-default"
-								:class="{ selected: tab === 'playlists' }"
-								ref="playlists-tab"
-								@click="showTab('playlists')"
-							>
-								Playlists
-							</button>
-							<button
-								v-if="
-									loggedIn &&
-										station.type === 'community' &&
-										station.partyMode &&
-										((station.locked && isOwnerOrAdmin()) ||
-											!station.locked)
-								"
-								class="button is-default"
-								:class="{ selected: tab === 'search' }"
-								ref="search-tab"
-								@click="showTab('search')"
-							>
-								Search
-							</button>
-							<button
-								v-if="isOwnerOrAdmin()"
-								class="button is-default"
-								:class="{ selected: tab === 'blacklist' }"
-								ref="blacklist-tab"
-								@click="showTab('blacklist')"
-							>
-								Blacklist
-							</button>
-						</div>
-						<settings
-							v-if="isOwnerOrAdmin()"
-							class="tab"
-							v-show="tab === 'settings'"
-						/>
-						<playlists
-							v-if="
-								isOwnerOrAdmin() ||
-									(loggedIn &&
-										station.type === 'community' &&
-										station.partyMode &&
-										((station.locked && isOwnerOrAdmin()) ||
-											!station.locked))
-							"
-							class="tab"
-							v-show="tab === 'playlists'"
-						/>
-						<search
-							v-if="
-								loggedIn &&
-									station.type === 'community' &&
-									station.partyMode &&
-									((station.locked && isOwnerOrAdmin()) ||
-										!station.locked)
-							"
-							class="tab"
-							v-show="tab === 'search'"
-						/>
-						<blacklist
-							v-if="isOwnerOrAdmin()"
-							class="tab"
-							v-show="tab === 'blacklist'"
-						/>
-					</div>
-				</div>
-				<div class="right-section">
-					<div class="section">
-						<div class="queue-title">
-							<h4 class="section-title">Queue</h4>
-							<i
-								v-if="isOwnerOrAdmin() && stationPaused"
-								@click="resumeStation()"
-								class="material-icons resume-station"
-								content="Resume Station"
-								v-tippy
-							>
-								play_arrow
-							</i>
-							<i
-								v-if="isOwnerOrAdmin() && !stationPaused"
-								@click="pauseStation()"
-								class="material-icons pause-station"
-								content="Pause Station"
-								v-tippy
-							>
-								pause
-							</i>
-							<confirm
-								v-if="isOwnerOrAdmin()"
-								@confirm="skipStation()"
-							>
-								<i
-									class="material-icons skip-station"
-									content="Force Skip Station"
-									v-tippy
-								>
-									skip_next
-								</i>
-							</confirm>
-						</div>
-						<hr class="section-horizontal-rule" />
-						<song-item
-							v-if="currentSong._id"
-							:song="currentSong"
-							:requested-by="
-								station.type === 'community' &&
-									station.partyMode === true
-							"
-							header="Currently Playing.."
-							class="currently-playing"
-						/>
-						<queue sector="manageStation" />
-					</div>
-				</div>
-			</div>
-		</template>
-		<template #footer>
-			<router-link
-				v-if="sector !== 'station' && station.name"
-				:to="{
-					name: 'station',
-					params: { id: station.name }
-				}"
-				class="button is-primary"
-			>
-				Go To Station
-			</router-link>
-			<a
-				class="button is-default"
-				v-if="isOwnerOrAdmin() && !station.partyMode"
-				@click="stationPlaylist()"
-			>
-				View Station Playlist
-			</a>
-			<button
-				class="button is-primary tab-actionable-button"
-				v-if="loggedIn && station.type === 'official'"
-				@click="openModal('requestSong')"
-			>
-				<i class="material-icons icon-with-button">queue</i>
-				<span class="optional-desktop-only-text"> Request Song </span>
-			</button>
-			<div v-if="isOwnerOrAdmin()" class="right">
-				<confirm @confirm="clearAndRefillStationQueue()">
-					<a class="button is-danger">
-						Clear and refill station queue
-					</a>
-				</confirm>
-				<confirm
-					v-if="station && station.type === 'community'"
-					@confirm="removeStation()"
-				>
-					<button class="button is-danger">Delete station</button>
-				</confirm>
-			</div>
-		</template>
-	</modal>
-</template>
-
-<script>
-import { mapState, mapGetters, mapActions } from "vuex";
-
-import Toast from "toasters";
-
-import Confirm from "@/components/Confirm.vue";
-import Queue from "@/components/Queue.vue";
-import SongItem from "@/components/SongItem.vue";
-import Modal from "../../Modal.vue";
-
-import Settings from "./Tabs/Settings.vue";
-import Playlists from "./Tabs/Playlists.vue";
-import Search from "./Tabs/Search.vue";
-import Blacklist from "./Tabs/Blacklist.vue";
-
-export default {
-	components: {
-		Modal,
-		Confirm,
-		Queue,
-		SongItem,
-		Settings,
-		Playlists,
-		Search,
-		Blacklist
-	},
-	props: {
-		stationId: { type: String, default: "" },
-		sector: { type: String, default: "admin" }
-	},
-	computed: {
-		...mapState({
-			loggedIn: state => state.user.auth.loggedIn,
-			userId: state => state.user.auth.userId,
-			role: state => state.user.auth.role
-		}),
-		...mapState("modals/manageStation", {
-			tab: state => state.tab,
-			station: state => state.station,
-			originalStation: state => state.originalStation,
-			songsList: state => state.songsList,
-			includedPlaylists: state => state.includedPlaylists,
-			excludedPlaylists: state => state.excludedPlaylists,
-			stationPaused: state => state.stationPaused,
-			currentSong: state => state.currentSong
-		}),
-		...mapGetters({
-			socket: "websockets/getSocket"
-		})
-	},
-	mounted() {
-		this.socket.dispatch(`stations.getStationById`, this.stationId, res => {
-			if (res.status === "success") {
-				const { station } = res.data;
-				this.editStation(station);
-
-				if (!this.isOwnerOrAdmin() && this.station.partyMode)
-					this.showTab("search");
-
-				const currentSong = res.data.station.currentSong
-					? res.data.station.currentSong
-					: {};
-
-				this.updateCurrentSong(currentSong);
-
-				this.updateStationPaused(res.data.station.paused);
-
-				this.socket.dispatch(
-					"stations.getStationIncludedPlaylistsById",
-					this.stationId,
-					res => {
-						if (res.status === "success")
-							this.setIncludedPlaylists(res.data.playlists);
-					}
-				);
-
-				this.socket.dispatch(
-					"stations.getStationExcludedPlaylistsById",
-					this.stationId,
-					res => {
-						if (res.status === "success")
-							this.setExcludedPlaylists(res.data.playlists);
-					}
-				);
-
-				this.socket.dispatch(
-					"stations.getQueue",
-					this.stationId,
-					res => {
-						if (res.status === "success")
-							this.updateSongsList(res.data.queue);
-					}
-				);
-
-				this.socket.dispatch(
-					"apis.joinRoom",
-					`manage-station.${this.stationId}`
-				);
-
-				this.socket.on(
-					"event:station.name.updated",
-					res => {
-						this.station.name = res.data.name;
-					},
-					{ modal: "manageStation" }
-				);
-
-				this.socket.on(
-					"event:station.displayName.updated",
-					res => {
-						this.station.displayName = res.data.displayName;
-					},
-					{ modal: "manageStation" }
-				);
-
-				this.socket.on(
-					"event:station.description.updated",
-					res => {
-						this.station.description = res.data.description;
-					},
-					{ modal: "manageStation" }
-				);
-
-				this.socket.on(
-					"event:station.partyMode.updated",
-					res => {
-						if (this.station.type === "community")
-							this.station.partyMode = res.data.partyMode;
-					},
-					{ modal: "manageStation" }
-				);
-
-				this.socket.on(
-					"event:station.playMode.updated",
-					res => {
-						this.station.playMode = res.data.playMode;
-					},
-					{ modal: "manageStation" }
-				);
-
-				this.socket.on(
-					"event:station.theme.updated",
-					res => {
-						const { theme } = res.data;
-						this.station.theme = theme;
-					},
-					{ modal: "manageStation" }
-				);
-
-				this.socket.on(
-					"event:station.privacy.updated",
-					res => {
-						this.station.privacy = res.data.privacy;
-					},
-					{ modal: "manageStation" }
-				);
-
-				this.socket.on(
-					"event:station.queue.lock.toggled",
-					res => {
-						this.station.locked = res.data.locked;
-					},
-					{ modal: "manageStation" }
-				);
-
-				this.socket.on(
-					"event:station.includedPlaylist",
-					res => {
-						const { playlist } = res.data;
-						const playlistIndex = this.includedPlaylists
-							.map(includedPlaylist => includedPlaylist._id)
-							.indexOf(playlist._id);
-						if (playlistIndex === -1)
-							this.includedPlaylists.push(playlist);
-					},
-					{ modal: "manageStation" }
-				);
-
-				this.socket.on(
-					"event:station.excludedPlaylist",
-					res => {
-						const { playlist } = res.data;
-						const playlistIndex = this.excludedPlaylists
-							.map(excludedPlaylist => excludedPlaylist._id)
-							.indexOf(playlist._id);
-						if (playlistIndex === -1)
-							this.excludedPlaylists.push(playlist);
-					},
-					{ modal: "manageStation" }
-				);
-
-				this.socket.on(
-					"event:station.removedIncludedPlaylist",
-					res => {
-						const { playlistId } = res.data;
-						const playlistIndex = this.includedPlaylists
-							.map(playlist => playlist._id)
-							.indexOf(playlistId);
-						if (playlistIndex >= 0)
-							this.includedPlaylists.splice(playlistIndex, 1);
-					},
-					{ modal: "manageStation" }
-				);
-
-				this.socket.on(
-					"event:station.removedExcludedPlaylist",
-					res => {
-						const { playlistId } = res.data;
-						const playlistIndex = this.excludedPlaylists
-							.map(playlist => playlist._id)
-							.indexOf(playlistId);
-						if (playlistIndex >= 0)
-							this.excludedPlaylists.splice(playlistIndex, 1);
-					},
-					{ modal: "manageStation" }
-				);
-			} else {
-				new Toast(`Station with that ID not found`);
-				this.closeModal("manageStation");
-			}
-		});
-
-		this.socket.on(
-			"event:station.queue.updated",
-			res => this.updateSongsList(res.data.queue),
-			{ modal: "manageStation" }
-		);
-
-		this.socket.on(
-			"event:station.queue.song.repositioned",
-			res => this.repositionSongInList(res.data.song),
-			{ modal: "manageStation" }
-		);
-
-		this.socket.on(
-			"event:station.pause",
-			() => this.updateStationPaused(true),
-			{ modal: "manageStation" }
-		);
-
-		this.socket.on(
-			"event:station.resume",
-			() => this.updateStationPaused(false),
-			{ modal: "manageStation" }
-		);
-
-		this.socket.on(
-			"event:station.nextSong",
-			res => {
-				const { currentSong } = res.data;
-				this.updateCurrentSong(currentSong || {});
-			},
-			{ modal: "manageStation" }
-		);
-	},
-	onBeforeUnmount() {
-		this.socket.dispatch(
-			"apis.leaveRoom",
-			`manage-station.${this.stationId}`,
-			() => {}
-		);
-
-		this.repositionSongInList([]);
-		this.clearStation();
-		this.showTab("settings");
-	},
-	methods: {
-		isOwner() {
-			return this.loggedIn && this.userId === this.station.owner;
-		},
-		isAdmin() {
-			return this.loggedIn && this.role === "admin";
-		},
-		isOwnerOrAdmin() {
-			return this.isOwner() || this.isAdmin();
-		},
-		removeStation() {
-			this.socket.dispatch("stations.remove", this.station._id, res => {
-				new Toast(res.message);
-				if (res.status === "success") {
-					this.closeModal("manageStation");
-				}
-			});
-		},
-		resumeStation() {
-			this.socket.dispatch("stations.resume", this.station._id, res => {
-				if (res.status !== "success")
-					new Toast(`Error: ${res.message}`);
-				else new Toast("Successfully resumed the station.");
-			});
-		},
-		pauseStation() {
-			this.socket.dispatch("stations.pause", this.station._id, res => {
-				if (res.status !== "success")
-					new Toast(`Error: ${res.message}`);
-				else new Toast("Successfully paused the station.");
-			});
-		},
-		skipStation() {
-			this.socket.dispatch(
-				"stations.forceSkip",
-				this.station._id,
-				res => {
-					if (res.status !== "success")
-						new Toast(`Error: ${res.message}`);
-					else
-						new Toast(
-							"Successfully skipped the station's current song."
-						);
-				}
-			);
-		},
-		clearAndRefillStationQueue() {
-			this.socket.dispatch(
-				"stations.clearAndRefillStationQueue",
-				this.station._id,
-				res => {
-					if (res.status !== "success")
-						new Toast({
-							content: `Error: ${res.message}`,
-							timeout: 8000
-						});
-					else new Toast({ content: res.message, timeout: 4000 });
-				}
-			);
-		},
-		stationPlaylist() {
-			this.socket.dispatch(
-				"playlists.getPlaylistForStation",
-				this.station._id,
-				false,
-				res => {
-					if (res.status === "success") {
-						this.editPlaylist(res.data.playlist._id);
-						this.openModal("editPlaylist");
-					} else {
-						new Toast(res.message);
-					}
-				}
-			);
-		},
-		...mapActions("modals/manageStation", [
-			"editStation",
-			"setIncludedPlaylists",
-			"setExcludedPlaylists",
-			"clearStation",
-			"updateSongsList",
-			"repositionSongInList",
-			"updateStationPaused",
-			"updateCurrentSong"
-		]),
-		...mapActions({
-			showTab(dispatch, payload) {
-				this.$refs[`${payload}-tab`].scrollIntoView();
-				return dispatch("modals/manageStation/showTab", payload);
-			}
-		}),
-		...mapActions("modalVisibility", ["openModal", "closeModal"]),
-		...mapActions("user/playlists", ["editPlaylist"])
-	}
-};
-</script>
-
-<style lang="scss">
-.manage-station-modal.modal {
-	z-index: 1800;
-	.modal-card {
-		width: 1300px;
-		height: 100%;
-		overflow: auto;
-		.tab > button {
-			width: 100%;
-			margin-bottom: 10px;
-		}
-		.currently-playing.song-item {
-			.song-info {
-				width: calc(100% - 150px);
-			}
-			.thumbnail {
-				min-width: 130px;
-				width: 130px;
-				height: 130px;
-			}
-		}
-	}
-}
-</style>
-
-<style lang="scss" scoped>
-.manage-station-modal.modal .modal-card-body .custom-modal-body {
-	display: flex;
-	flex-wrap: wrap;
-	height: 100%;
-
-	.section {
-		display: flex;
-		flex-direction: column;
-		flex-grow: 1;
-		width: auto;
-		padding: 15px !important;
-		margin: 0 10px;
-	}
-
-	.left-section {
-		flex-basis: 50%;
-		height: 100%;
-		overflow-y: auto;
-		flex-grow: 1;
-
-		.tabs-container {
-			.tab-selection {
-				display: flex;
-				overflow-x: auto;
-
-				.button {
-					border-radius: 5px 5px 0 0;
-					border: 0;
-					text-transform: uppercase;
-					font-size: 14px;
-					color: var(--dark-grey-3);
-					background-color: var(--light-grey-2);
-					flex-grow: 1;
-					height: 32px;
-
-					&:not(:first-of-type) {
-						margin-left: 5px;
-					}
-				}
-
-				.selected {
-					background-color: var(--primary-color) !important;
-					color: var(--white) !important;
-					font-weight: 600;
-				}
-			}
-			.tab {
-				border: 1px solid var(--light-grey-3);
-				padding: 15px;
-				border-radius: 0 0 5px 5px;
-			}
-		}
-	}
-	.right-section {
-		flex-basis: 50%;
-		height: 100%;
-		overflow-y: auto;
-		flex-grow: 1;
-		.section {
-			.queue-title {
-				display: flex;
-				line-height: 30px;
-				.material-icons {
-					margin-left: 5px;
-					margin-bottom: 5px;
-					font-size: 28px;
-					cursor: pointer;
-					&:first-of-type {
-						margin-left: auto;
-					}
-					&.skip-station {
-						color: var(--red);
-					}
-					&.resume-station,
-					&.pause-station {
-						color: var(--primary-color);
-					}
-				}
-			}
-			.currently-playing {
-				margin-bottom: 10px;
-			}
-		}
-	}
-}
-
-@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>

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

@@ -180,13 +180,8 @@
 		<request-song v-if="modals.requestSong" />
 		<edit-playlist v-if="modals.editPlaylist" />
 		<create-playlist v-if="modals.createPlaylist" />
-		<manage-station-owen
-			v-if="modals.manageStation && manageStationVersion === 'owen'"
-			:station-id="editingStationId"
-			sector="admin"
-		/>
-		<manage-station-kris
-			v-if="modals.manageStation && manageStationVersion === 'kris'"
+		<manage-station
+			v-if="modals.manageStation"
 			:station-id="editingStationId"
 			sector="admin"
 		/>
@@ -215,11 +210,8 @@ export default {
 		CreatePlaylist: defineAsyncComponent(() =>
 			import("@/components/modals/CreatePlaylist.vue")
 		),
-		ManageStationOwen: defineAsyncComponent(() =>
-			import("@/components/modals/ManageStationOwen/index.vue")
-		),
-		ManageStationKris: defineAsyncComponent(() =>
-			import("@/components/modals/ManageStationKris/index.vue")
+		ManageStation: defineAsyncComponent(() =>
+			import("@/components/modals/ManageStation/index.vue")
 		),
 		Report: defineAsyncComponent(() =>
 			import("@/components/modals/Report.vue")

+ 4 - 16
frontend/src/pages/Station/index.vue

@@ -562,17 +562,8 @@
 				<request-song v-if="modals.requestSong" />
 				<edit-playlist v-if="modals.editPlaylist" />
 				<create-playlist v-if="modals.createPlaylist" />
-				<manage-station-owen
-					v-if="
-						modals.manageStation && manageStationVersion === 'owen'
-					"
-					:station-id="station._id"
-					sector="station"
-				/>
-				<manage-station-kris
-					v-if="
-						modals.manageStation && manageStationVersion === 'kris'
-					"
+				<manage-station
+					v-if="modals.manageStation"
 					:station-id="station._id"
 					sector="station"
 				/>
@@ -659,11 +650,8 @@ export default {
 		CreatePlaylist: defineAsyncComponent(() =>
 			import("@/components/modals/CreatePlaylist.vue")
 		),
-		ManageStationOwen: defineAsyncComponent(() =>
-			import("@/components/modals/ManageStationOwen/index.vue")
-		),
-		ManageStationKris: defineAsyncComponent(() =>
-			import("@/components/modals/ManageStationKris/index.vue")
+		ManageStation: defineAsyncComponent(() =>
+			import("@/components/modals/ManageStation/index.vue")
 		),
 		Report: defineAsyncComponent(() =>
 			import("@/components/modals/Report.vue")