| 
					
				 | 
			
			
				@@ -1,3 +1,111 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+<script setup lang="ts"> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import { useStore } from "vuex"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import { ref, computed, onMounted, onUnmounted } from "vue"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import { formatDistance, parseISO } from "date-fns"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import AddToPlaylistDropdown from "./AddToPlaylistDropdown.vue"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import utils from "@/utils"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const store = useStore(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const props = defineProps({ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	song: { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		type: Object, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		default: () => {} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	requestedBy: { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		type: Boolean, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		default: false 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	duration: { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		type: Boolean, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		default: true 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	thumbnail: { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		type: Boolean, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		default: true 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	disabledActions: { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		type: Array, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		default: () => [] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	header: { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		type: String, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		default: null 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+}); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const formatedRequestedAt = ref(null); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const formatRequestedAtInterval = ref(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const hoveredTippy = ref(false); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const songActions = ref(null); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const loggedIn = computed(() => store.state.user.auth.loggedIn); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const userRole = computed(() => store.state.user.auth.userRole); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+function formatRequestedAt() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	if (props.requestedBy && props.song.requestedAt) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		formatedRequestedAt.value = formatDistance( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			parseISO(props.song.requestedAt), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			new Date() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+function formatArtists() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	if (props.song.artists.length === 1) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		return props.song.artists[0]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	if (props.song.artists.length === 2) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		return props.song.artists.join(" & "); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	if (props.song.artists.length > 2) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		return `${props.song.artists 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			.slice(0, -1) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			.join(", ")} & ${props.song.artists.slice(-1)}`; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	return null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+function hideTippyElements() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	songActions.value.tippy.hide(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	setTimeout( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		() => 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			Array.from(document.querySelectorAll(".tippy-popper")).forEach( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				popper => popper._tippy.hide() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		500 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+function hoverTippy() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	hoveredTippy.value = true; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+function openModal(payload) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	store.dispatch("modalVisibility/openModal", payload); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+function report(song) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	hideTippyElements(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	openModal({ modal: "report", data: { song } }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+function edit(song) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	hideTippyElements(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	openModal({ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		modal: "editSong", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		data: { song } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	}); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+onMounted(() => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	if (props.requestedBy) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		formatRequestedAt(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		formatRequestedAtInterval.value = setInterval(() => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			formatRequestedAt(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		}, 30000); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+}); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+onUnmounted(() => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	clearInterval(formatRequestedAtInterval.value); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+}); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+</script> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 <template> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	<div 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		class="universal-item song-item" 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -166,120 +274,6 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	</div> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 </template> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-<script> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-import { mapActions, mapState } from "vuex"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-import { formatDistance, parseISO } from "date-fns"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-import AddToPlaylistDropdown from "./AddToPlaylistDropdown.vue"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-import utils from "@/utils"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-export default { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	components: { AddToPlaylistDropdown }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	props: { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		song: { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			type: Object, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			default: () => {} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		requestedBy: { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			type: Boolean, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			default: false 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		duration: { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			type: Boolean, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			default: true 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		thumbnail: { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			type: Boolean, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			default: true 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		disabledActions: { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			type: Array, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			default: () => [] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		header: { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			type: String, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			default: null 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	data() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		return { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			utils, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			formatedRequestedAt: null, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			formatRequestedAtInterval: null, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			hoveredTippy: false 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	computed: { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		...mapState({ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			loggedIn: state => state.user.auth.loggedIn, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			userRole: state => state.user.auth.role 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		}) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	mounted() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		if (this.requestedBy) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			this.formatRequestedAt(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			this.formatRequestedAtInterval = setInterval(() => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				this.formatRequestedAt(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			}, 30000); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	unmounted() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		clearInterval(this.formatRequestedAtInterval); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	methods: { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		formatRequestedAt() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			if (this.requestedBy && this.song.requestedAt) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				this.formatedRequestedAt = this.formatDistance( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					parseISO(this.song.requestedAt), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					new Date() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		formatArtists() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			if (this.song.artists.length === 1) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				return this.song.artists[0]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			if (this.song.artists.length === 2) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				return this.song.artists.join(" & "); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			if (this.song.artists.length > 2) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				return `${this.song.artists 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					.slice(0, -1) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					.join(", ")} & ${this.song.artists.slice(-1)}`; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			return null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		hideTippyElements() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			this.$refs.songActions.tippy.hide(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			setTimeout( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				() => 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					Array.from( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-						document.querySelectorAll(".tippy-popper") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					).forEach(popper => popper._tippy.hide()), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				500 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		hoverTippy() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			this.hoveredTippy = true; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		report(song) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			this.hideTippyElements(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			this.openModal({ modal: "report", data: { song } }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		edit(song) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			this.hideTippyElements(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			this.openModal({ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				modal: "editSong", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				data: { song } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			}); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		...mapActions("modalVisibility", ["openModal"]), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		formatDistance, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		parseISO 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-</script> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 <style lang="less" scoped> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 .night-mode { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	.song-item { 
			 |