123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257 |
- <script setup lang="ts">
- import Toast from "toasters";
- import { storeToRefs } from "pinia";
- import { ref, onBeforeUnmount, onMounted, watch } from "vue";
- import validation from "@/validation";
- import { useWebsocketsStore } from "@/stores/websockets";
- import { useUserAuthStore } from "@/stores/userAuth";
- import { useEditPlaylistStore } from "@/stores/editPlaylist";
- import { useModalsStore } from "@/stores/modals";
- import { useForm } from "@/composables/useForm";
- const props = defineProps({
- modalUuid: { type: String, required: true }
- });
- const userAuthStore = useUserAuthStore();
- const { loggedIn, userId } = storeToRefs(userAuthStore);
- const { hasPermission } = userAuthStore;
- const { socket } = useWebsocketsStore();
- const editPlaylistStore = useEditPlaylistStore({ modalUuid: props.modalUuid });
- const { playlist } = storeToRefs(editPlaylistStore);
- const { preventCloseUnsaved } = useModalsStore();
- const featured = ref(playlist.value.featured);
- const isOwner = () =>
- loggedIn.value && userId.value === playlist.value.createdBy;
- const isEditable = permission => {
- if (permission === "playlists.update.featured")
- return playlist.value.type !== "station" && hasPermission(permission);
- if (
- ["user", "user-liked", "user-disliked", "admin"].includes(
- playlist.value.type
- )
- )
- return isOwner() || hasPermission(permission);
- if (
- playlist.value.type === "genre" &&
- permission === "playlists.update.privacy"
- )
- return hasPermission(permission);
- return false;
- };
- const {
- inputs: displayNameInputs,
- unsavedChanges: displayNameUnsaved,
- save: saveDisplayName,
- setOriginalValue: setDisplayName
- } = useForm(
- {
- displayName: {
- value: playlist.value.displayName,
- validate: value => {
- if (!validation.isLength(value, 1, 64))
- return "Display name must have between 1 and 64 characters.";
- if (!validation.regex.ascii.test(value))
- return "Invalid display name format. Only ASCII characters are allowed.";
- return true;
- }
- }
- },
- ({ status, messages, values }, resolve, reject) => {
- if (status === "success")
- socket.dispatch(
- "playlists.updateDisplayName",
- playlist.value._id,
- values.displayName,
- res => {
- playlist.value.displayName = values.displayName;
- if (res.status === "success") {
- resolve();
- new Toast(res.message);
- } else reject(new Error(res.message));
- }
- );
- else {
- Object.values(messages).forEach(message => {
- new Toast({ content: message, timeout: 8000 });
- });
- resolve();
- }
- },
- {
- modalUuid: props.modalUuid,
- preventCloseUnsaved: false
- }
- );
- const {
- inputs: privacyInputs,
- unsavedChanges: privacyUnsaved,
- save: savePrivacy,
- setOriginalValue: setPrivacy
- } = useForm(
- { privacy: playlist.value.privacy },
- ({ status, messages, values }, resolve, reject) => {
- if (status === "success")
- socket.dispatch(
- playlist.value.type === "genre" ||
- playlist.value.type === "admin"
- ? "playlists.updatePrivacyAdmin"
- : "playlists.updatePrivacy",
- playlist.value._id,
- values.privacy,
- res => {
- playlist.value.privacy = values.privacy;
- if (values.privacy !== "public") featured.value = false;
- if (res.status === "success") {
- resolve();
- new Toast(res.message);
- } else reject(new Error(res.message));
- }
- );
- else {
- if (messages[status]) new Toast(messages[status]);
- resolve();
- }
- },
- {
- modalUuid: props.modalUuid,
- preventCloseUnsaved: false
- }
- );
- const toggleFeatured = () => {
- if (playlist.value.privacy !== "public") return;
- featured.value = !featured.value;
- socket.dispatch(
- "playlists.updateFeatured",
- playlist.value._id,
- featured.value,
- res => {
- playlist.value.featured = featured.value;
- new Toast(res.message);
- }
- );
- };
- watch(playlist, (value, oldValue) => {
- if (value.displayName !== oldValue.displayName)
- setDisplayName({ displayName: value.displayName });
- if (value.privacy !== oldValue.privacy) {
- setPrivacy({ privacy: value.privacy });
- if (value.privacy !== "public") featured.value = false;
- }
- if (value.featured !== oldValue.featured) featured.value = value.featured;
- });
- onMounted(() => {
- preventCloseUnsaved[props.modalUuid] = () =>
- displayNameUnsaved.value.length + privacyUnsaved.value.length > 0;
- });
- onBeforeUnmount(() => {
- delete preventCloseUnsaved[props.modalUuid];
- });
- </script>
- <template>
- <div class="settings-tab section">
- <div
- v-if="
- isEditable('playlists.update.displayName') &&
- !(
- playlist.type === 'user-liked' ||
- playlist.type === 'user-disliked'
- )
- "
- >
- <label class="label"> Change display name </label>
- <div class="control is-grouped input-with-button">
- <p class="control is-expanded">
- <input
- v-model="displayNameInputs['displayName'].value"
- class="input"
- type="text"
- placeholder="Playlist Display Name"
- @keyup.enter="saveDisplayName()"
- />
- </p>
- <p class="control">
- <button
- class="button is-info"
- @click.prevent="saveDisplayName()"
- >
- Rename
- </button>
- </p>
- </div>
- </div>
- <div v-if="isEditable('playlists.update.privacy')">
- <label class="label"> Change privacy </label>
- <div class="control is-grouped input-with-button">
- <div class="control is-expanded select">
- <select v-model="privacyInputs['privacy'].value">
- <option value="private">Private</option>
- <option value="public">Public</option>
- </select>
- </div>
- <p class="control">
- <button
- class="button is-info"
- @click.prevent="savePrivacy()"
- >
- Update Privacy
- </button>
- </p>
- </div>
- </div>
- <div
- v-if="isEditable('playlists.update.featured')"
- class="control is-expanded checkbox-control"
- >
- <label class="switch">
- <input
- type="checkbox"
- id="featured"
- :checked="featured"
- @click="toggleFeatured"
- :disabled="playlist.privacy !== 'public'"
- />
- <span
- v-if="playlist.privacy === 'public'"
- class="slider round"
- ></span>
- <span
- v-else
- class="slider round disabled"
- content="Only public playlists can be featured"
- v-tippy
- ></span>
- </label>
- <label class="label" for="featured">Featured Playlist</label>
- </div>
- </div>
- </template>
- <style lang="less" scoped>
- .checkbox-control label.label {
- margin-left: 10px;
- }
- @media screen and (max-width: 1300px) {
- .section {
- max-width: 100% !important;
- }
- }
- </style>
|