123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387 |
- <script setup lang="ts">
- import { defineAsyncComponent, watch } from "vue";
- import Toast from "toasters";
- import { storeToRefs } from "pinia";
- import validation from "@/validation";
- import { useWebsocketsStore } from "@/stores/websockets";
- import { useManageStationStore } from "@/stores/manageStation";
- import { useForm } from "@/composables/useForm";
- const InfoIcon = defineAsyncComponent(
- () => import("@/components/InfoIcon.vue")
- );
- const props = defineProps({
- modalUuid: { type: String, required: true }
- });
- const { socket } = useWebsocketsStore();
- const manageStationStore = useManageStationStore({
- modalUuid: props.modalUuid
- });
- const { station } = storeToRefs(manageStationStore);
- const { editStation } = manageStationStore;
- const { inputs, save, setOriginalValue } = useForm(
- {
- name: {
- value: station.value.name,
- validate: value => {
- if (!validation.isLength(value, 2, 16))
- return "Name must have between 2 and 16 characters.";
- if (!validation.regex.az09_.test(value))
- return "Invalid name format. Allowed characters: a-z, 0-9 and _.";
- return true;
- }
- },
- displayName: {
- value: station.value.displayName,
- validate: value => {
- if (!validation.isLength(value, 2, 32))
- return "Display name must have between 2 and 32 characters.";
- if (!validation.regex.ascii.test(value))
- return "Invalid display name format. Only ASCII characters are allowed.";
- return true;
- }
- },
- description: {
- value: station.value.description,
- validate: value => {
- if (
- value
- .split("")
- .filter(character => character.charCodeAt(0) === 21328)
- .length !== 0
- )
- return "Invalid description format.";
- return true;
- }
- },
- theme: station.value.theme,
- privacy: station.value.privacy,
- requestsEnabled: station.value.requests.enabled,
- requestsAccess: station.value.requests.access,
- requestsLimit: station.value.requests.limit,
- autofillEnabled: station.value.autofill.enabled,
- autofillLimit: station.value.autofill.limit,
- autofillMode: station.value.autofill.mode
- },
- ({ status, messages, values }, resolve, reject) => {
- if (status === "success") {
- const oldStation = JSON.parse(JSON.stringify(station.value));
- const updatedStation = {
- ...oldStation,
- name: values.name,
- displayName: values.displayName,
- description: values.description,
- theme: values.theme,
- privacy: values.privacy,
- requests: {
- ...oldStation.requests,
- enabled: values.requestsEnabled,
- access: values.requestsAccess,
- limit: values.requestsLimit
- },
- autofill: {
- ...oldStation.autofill,
- enabled: values.autofillEnabled,
- limit: values.autofillLimit,
- mode: values.autofillMode
- }
- };
- socket.dispatch(
- "stations.update",
- station.value._id,
- updatedStation,
- res => {
- new Toast(res.message);
- if (res.status === "success") {
- editStation(updatedStation);
- resolve();
- } else reject(new Error(res.message));
- }
- );
- } else {
- Object.values(messages).forEach(message => {
- new Toast({ content: message, timeout: 8000 });
- });
- resolve();
- }
- },
- {
- modalUuid: props.modalUuid
- }
- );
- watch(station, value => {
- setOriginalValue({
- name: value.name,
- displayName: value.displayName,
- description: value.description,
- theme: value.theme,
- privacy: value.privacy,
- requestsEnabled: value.requests.enabled,
- requestsAccess: value.requests.access,
- requestsLimit: value.requests.limit,
- autofillEnabled: value.autofill.enabled,
- autofillLimit: value.autofill.limit,
- autofillMode: value.autofill.mode
- });
- });
- </script>
- <template>
- <div class="station-settings">
- <label class="label">Name</label>
- <div class="control is-expanded">
- <input class="input" type="text" v-model="inputs['name'].value" />
- </div>
- <label class="label">Display Name</label>
- <div class="control is-expanded">
- <input
- class="input"
- type="text"
- v-model="inputs['displayName'].value"
- />
- </div>
- <label class="label">Description</label>
- <div class="control is-expanded">
- <input
- class="input"
- type="text"
- v-model="inputs['description'].value"
- />
- </div>
- <div class="settings-buttons">
- <div class="small-section">
- <label class="label">Theme</label>
- <div class="control is-expanded select">
- <select v-model="inputs['theme'].value">
- <option value="blue" selected>Blue</option>
- <option value="purple">Purple</option>
- <option value="teal">Teal</option>
- <option value="orange">Orange</option>
- <option value="red">Red</option>
- </select>
- </div>
- </div>
- <div class="small-section">
- <label class="label">Privacy</label>
- <div class="control is-expanded select">
- <select v-model="inputs['privacy'].value">
- <option value="public">Public</option>
- <option value="unlisted">Unlisted</option>
- <option value="private" selected>Private</option>
- </select>
- </div>
- </div>
- <div
- class="requests-settings"
- :class="{ enabled: inputs['requestsEnabled'].value }"
- >
- <div class="toggle-row">
- <label class="label">
- Requests
- <info-icon
- tooltip="Allow users to add songs to the queue"
- />
- </label>
- <p class="is-expanded checkbox-control">
- <label class="switch">
- <input
- type="checkbox"
- id="toggle-requests"
- v-model="inputs['requestsEnabled'].value"
- />
- <span class="slider round"></span>
- </label>
- <label for="toggle-requests">
- <p>
- {{
- inputs["requestsEnabled"].value
- ? "Enabled"
- : "Disabled"
- }}
- </p>
- </label>
- </p>
- </div>
- <div
- v-if="inputs['requestsEnabled'].value"
- class="small-section"
- >
- <label class="label">Minimum access</label>
- <div class="control is-expanded select">
- <select v-model="inputs['requestsAccess'].value">
- <option value="owner" selected>Owner</option>
- <option value="user">User</option>
- </select>
- </div>
- </div>
- <div
- v-if="inputs['requestsEnabled'].value"
- class="small-section"
- >
- <label class="label">Per user request limit</label>
- <div class="control is-expanded">
- <input
- class="input"
- type="number"
- min="1"
- max="50"
- v-model="inputs['requestsLimit'].value"
- />
- </div>
- </div>
- </div>
- <div
- class="autofill-settings"
- :class="{ enabled: inputs['autofillEnabled'].value }"
- >
- <div class="toggle-row">
- <label class="label">
- Autofill
- <info-icon
- tooltip="Automatically fill the queue with songs"
- />
- </label>
- <p class="is-expanded checkbox-control">
- <label class="switch">
- <input
- type="checkbox"
- id="toggle-autofill"
- v-model="inputs['autofillEnabled'].value"
- />
- <span class="slider round"></span>
- </label>
- <label for="toggle-autofill">
- <p>
- {{
- inputs["autofillEnabled"].value
- ? "Enabled"
- : "Disabled"
- }}
- </p>
- </label>
- </p>
- </div>
- <div
- v-if="inputs['autofillEnabled'].value"
- class="small-section"
- >
- <label class="label">Song limit</label>
- <div class="control is-expanded">
- <input
- class="input"
- type="number"
- min="1"
- max="50"
- v-model="inputs['autofillLimit'].value"
- />
- </div>
- </div>
- <div
- v-if="inputs['autofillEnabled'].value"
- class="small-section"
- >
- <label class="label">Play mode</label>
- <div class="control is-expanded select">
- <select v-model="inputs['autofillMode'].value">
- <option value="random" selected>Random</option>
- <option value="sequential">Sequential</option>
- </select>
- </div>
- </div>
- </div>
- </div>
- <button class="control is-expanded button is-primary" @click="save()">
- Save Changes
- </button>
- </div>
- </template>
- <style lang="less" scoped>
- .night-mode {
- .requests-settings,
- .autofill-settings {
- background-color: var(--dark-grey-2) !important;
- }
- }
- .station-settings {
- .settings-buttons {
- display: flex;
- justify-content: center;
- flex-wrap: wrap;
- .small-section {
- width: calc(50% - 10px);
- min-width: 150px;
- margin: 5px auto;
- &:nth-child(odd) {
- margin-left: 0;
- }
- &:nth-child(even) {
- margin-right: 0;
- }
- }
- }
- .requests-settings,
- .autofill-settings {
- display: flex;
- flex-wrap: wrap;
- width: 100%;
- margin: 10px 0;
- padding: 10px;
- border-radius: @border-radius;
- box-shadow: @box-shadow;
- .toggle-row {
- display: flex;
- width: 100%;
- line-height: 36px;
- .label {
- font-size: 18px;
- margin: 0;
- }
- }
- .label {
- display: flex;
- flex-grow: 1;
- }
- .checkbox-control {
- justify-content: end;
- }
- .small-section {
- &:nth-child(even) {
- margin-left: 0;
- margin-right: auto;
- }
- &:nth-child(odd) {
- margin-left: auto;
- margin-right: 0;
- }
- }
- }
- }
- </style>
|