<template> <modal title="Edit Station"> <template v-slot:body> <label class="label">Name</label> <p class="control"> <input v-model="editing.name" class="input" type="text" placeholder="Station Name" /> </p> <label class="label">Display name</label> <p class="control"> <input v-model="editing.displayName" class="input" type="text" placeholder="Station Display Name" /> </p> <label class="label">Description</label> <p class="control"> <input v-model="editing.description" class="input" type="text" placeholder="Station Description" /> </p> <label class="label">Privacy</label> <p class="control"> <span class="select"> <select v-model="editing.privacy"> <option value="public">Public</option> <option value="unlisted">Unlisted</option> <option value="private">Private</option> </select> </span> </p> <br /> <p class="control" v-if="station.type === 'community'"> <label class="checkbox party-mode-inner"> <input v-model="editing.partyMode" type="checkbox" /> Party mode </label> </p> <small v-if="station.type === 'community'" >With party mode enabled, people can add songs to a queue that plays. With party mode disabled you can play a private playlist on loop.</small > <br /> <div v-if="station.type === 'community' && station.partyMode"> <br /> <br /> <label class="label">Queue lock</label> <small v-if="station.partyMode" >With the queue locked, only owners (you) can add songs to the queue.</small > <br /> <button v-if="!station.locked" class="button is-danger" @click="$parent.toggleLock()" > Lock the queue </button> <button v-if="station.locked" class="button is-success" @click="$parent.toggleLock()" > Unlock the queue </button> </div> <div v-if="station.type === 'official'" class="control is-grouped genre-wrapper" > <div class="sector"> <p class="control has-addons"> <input id="new-genre-edit" class="input" type="text" placeholder="Genre" @keyup.enter="addGenre()" /> <a class="button is-info" href="#" @click="addGenre()" >Add genre</a > </p> <span v-for="(genre, index) in editing.genres" :key="index" class="tag is-info" > {{ genre }} <button class="delete is-info" @click="removeGenre(index)" /> </span> </div> <div class="sector"> <p class="control has-addons"> <input id="new-blacklisted-genre-edit" class="input" type="text" placeholder="Blacklisted Genre" @keyup.enter="addBlacklistedGenre()" /> <a class="button is-info" href="#" @click="addBlacklistedGenre()" >Add blacklisted genre</a > </p> <span v-for="(genre, index) in editing.blacklistedGenres" :key="index" class="tag is-info" > {{ genre }} <button class="delete is-info" @click="removeBlacklistedGenre(index)" /> </span> </div> </div> </template> <template v-slot:footer> <button class="button is-success" v-on:click="update()"> Update Settings </button> <button v-if="station.type === 'community'" class="button is-danger" @click="deleteStation()" > Delete station </button> </template> </modal> </template> <script> import { mapState } from "vuex"; import { Toast } from "vue-roaster"; import Modal from "../Modals/Modal.vue"; import io from "../../io"; import validation from "../../validation"; export default { computed: mapState("admin/stations", { station: state => state.station, editing: state => state.editing }), mounted() { const _this = this; io.getSocket(socket => { _this.socket = socket; return socket; }); }, methods: { update() { if (this.station.name !== this.editing.name) this.updateName(); if (this.station.displayName !== this.editing.displayName) this.updateDisplayName(); if (this.station.description !== this.editing.description) this.updateDescription(); if (this.station.privacy !== this.editing.privacy) this.updatePrivacy(); if (this.station.partyMode !== this.editing.partyMode) this.updatePartyMode(); if ( this.station.genres.toString() !== this.editing.genres.toString() ) this.updateGenres(); if ( this.station.blacklistedGenres.toString() !== this.editing.blacklistedGenres.toString() ) this.updateBlacklistedGenres(); }, updateName() { const { name } = this.editing; if (!validation.isLength(name, 2, 16)) return Toast.methods.addToast( "Name must have between 2 and 16 characters.", 8000 ); if (!validation.regex.az09_.test(name)) return Toast.methods.addToast( "Invalid name format. Allowed characters: a-z, 0-9 and _.", 8000 ); return this.socket.emit( "stations.updateName", this.editing._id, name, res => { if (res.status === "success") { if (this.station) this.station.name = name; this.$parent.stations.forEach((station, index) => { if (station._id === this.editing._id) { this.$parent.stations[index].name = name; return name; } return false; }); } Toast.methods.addToast(res.message, 8000); } ); }, updateDisplayName() { const { displayName } = this.editing; if (!validation.isLength(displayName, 2, 32)) return Toast.methods.addToast( "Display name must have between 2 and 32 characters.", 8000 ); if (!validation.regex.azAZ09_.test(displayName)) return Toast.methods.addToast( "Invalid display name format. Allowed characters: a-z, A-Z, 0-9 and _.", 8000 ); return this.socket.emit( "stations.updateDisplayName", this.editing._id, displayName, res => { if (res.status === "success") { if (this.station) { this.station.displayName = displayName; return displayName; } this.$parent.stations.forEach((station, index) => { if (station._id === this.editing._id) { this.$parent.stations[ index ].displayName = displayName; return displayName; } return false; }); } return Toast.methods.addToast(res.message, 8000); } ); }, updateDescription() { const _this = this; const { description } = this.editing; if (!validation.isLength(description, 2, 200)) return Toast.methods.addToast( "Description must have between 2 and 200 characters.", 8000 ); let characters = description.split(""); characters = characters.filter(character => { return character.charCodeAt(0) === 21328; }); if (characters.length !== 0) return Toast.methods.addToast( "Invalid description format. Swastika's are not allowed.", 8000 ); return this.socket.emit( "stations.updateDescription", this.editing._id, description, res => { if (res.status === "success") { if (_this.station) { _this.station.description = description; return description; } _this.$parent.stations.forEach((station, index) => { if (station._id === _this.editing._id) { _this.$parent.stations[ index ].description = description; return description; } return false; }); return Toast.methods.addToast(res.message, 4000); } return Toast.methods.addToast(res.message, 8000); } ); }, updatePrivacy() { const _this = this; this.socket.emit( "stations.updatePrivacy", this.editing._id, this.editing.privacy, res => { if (res.status === "success") { if (_this.station) _this.station.privacy = _this.editing.privacy; else { _this.$parent.stations.forEach((station, index) => { if (station._id === _this.editing._id) { _this.$parent.stations[index].privacy = _this.editing.privacy; return _this.editing.privacy; } return false; }); } return Toast.methods.addToast(res.message, 4000); } return Toast.methods.addToast(res.message, 8000); } ); }, updateGenres() { const _this = this; this.socket.emit( "stations.updateGenres", this.editing._id, this.editing.genres, res => { if (res.status === "success") { const genres = JSON.parse( JSON.stringify(_this.editing.genres) ); if (_this.station) _this.station.genres = genres; _this.$parent.stations.forEach((station, index) => { if (station._id === _this.editing._id) { _this.$parent.stations[index].genres = genres; return genres; } return false; }); return Toast.methods.addToast(res.message, 4000); } return Toast.methods.addToast(res.message, 8000); } ); }, updateBlacklistedGenres() { const _this = this; this.socket.emit( "stations.updateBlacklistedGenres", this.editing._id, this.editing.blacklistedGenres, res => { if (res.status === "success") { const blacklistedGenres = JSON.parse( JSON.stringify(_this.editing.blacklistedGenres) ); if (_this.station) _this.station.blacklistedGenres = blacklistedGenres; _this.$parent.stations.forEach((station, index) => { if (station._id === _this.editing._id) { _this.$parent.stations[ index ].blacklistedGenres = blacklistedGenres; return blacklistedGenres; } return false; }); return Toast.methods.addToast(res.message, 4000); } return Toast.methods.addToast(res.message, 8000); } ); }, updatePartyMode() { const _this = this; this.socket.emit( "stations.updatePartyMode", this.editing._id, this.editing.partyMode, res => { if (res.status === "success") { if (_this.station) _this.station.partyMode = _this.editing.partyMode; _this.$parent.stations.forEach((station, index) => { if (station._id === _this.editing._id) { _this.$parent.stations[index].partyMode = _this.editing.partyMode; return _this.editing.partyMode; } return false; }); return Toast.methods.addToast(res.message, 4000); } return Toast.methods.addToast(res.message, 8000); } ); }, addGenre() { const genre = document .getElementById(`new-genre-edit`) .value.toLowerCase() .trim(); if (this.editing.genres.indexOf(genre) !== -1) return Toast.methods.addToast("Genre already exists", 3000); if (genre) { this.editing.genres.push(genre); document.getElementById(`new-genre`).value = ""; return true; } return Toast.methods.addToast("Genre cannot be empty", 3000); }, removeGenre(index) { this.editing.genres.splice(index, 1); }, addBlacklistedGenre() { const genre = document .getElementById(`new-blacklisted-genre-edit`) .value.toLowerCase() .trim(); if (this.editing.blacklistedGenres.indexOf(genre) !== -1) return Toast.methods.addToast("Genre already exists", 3000); if (genre) { this.editing.blacklistedGenres.push(genre); document.getElementById(`new-blacklisted-genre`).value = ""; return true; } return Toast.methods.addToast("Genre cannot be empty", 3000); }, removeBlacklistedGenre(index) { this.editing.blacklistedGenres.splice(index, 1); }, deleteStation() { this.socket.emit("stations.remove", this.editing._id, res => { Toast.methods.addToast(res.message, 8000); }); } }, components: { Modal } }; </script> <style lang="scss" scoped> .controls { display: flex; a { display: flex; align-items: center; } } .table { margin-bottom: 0; } h5 { padding: 20px 0; } .party-mode-inner, .party-mode-outer { display: flex; align-items: center; } .select:after { border-color: #029ce3; } </style>