Răsfoiți Sursa

Merge tag 'v3.6.0' into staging

Owen Diffey 3 ani în urmă
părinte
comite
fd52a44f5d

+ 7 - 0
CHANGELOG.md

@@ -1,5 +1,12 @@
 # Changelog
 
+## [v3.6.0] - 2022-06-12
+
+This release includes all changes from v3.6.0-rc1, in addition to the following. Upgrade instructions can be found at [.wiki/Upgrading](.wiki/Upgrading.md).
+
+### Fixed
+- fix: Removed tag="transition-group" from draggable components
+
 ## [v3.6.0-rc1] - 2022-06-05
 
 Upgrade instructions can be found at [.wiki/Upgrading](.wiki/Upgrading.md).

+ 2 - 2
backend/package-lock.json

@@ -1,12 +1,12 @@
 {
   "name": "musare-backend",
-  "version": "3.6.0-dev",
+  "version": "3.6.0",
   "lockfileVersion": 2,
   "requires": true,
   "packages": {
     "": {
       "name": "musare-backend",
-      "version": "3.6.0-dev",
+      "version": "3.6.0",
       "license": "GPL-3.0",
       "dependencies": {
         "async": "^3.2.3",

+ 1 - 1
backend/package.json

@@ -1,7 +1,7 @@
 {
   "name": "musare-backend",
   "private": true,
-  "version": "3.6.0-dev",
+  "version": "3.6.0",
   "type": "module",
   "description": "An open-source collaborative music listening and catalogue curation application. Currently supporting YouTube based content.",
   "main": "index.js",

+ 1 - 0
frontend/Dockerfile

@@ -2,6 +2,7 @@ FROM node:16.15 AS musare_frontend
 
 ARG FRONTEND_MODE=prod
 ENV FRONTEND_MODE=${FRONTEND_MODE}
+ENV SUPPRESS_NO_CONFIG_WARNING=1
 
 RUN apt-get update
 RUN apt-get install nginx -y

+ 2 - 2
frontend/package-lock.json

@@ -1,12 +1,12 @@
 {
   "name": "musare-frontend",
-  "version": "3.6.0-dev",
+  "version": "3.6.0",
   "lockfileVersion": 2,
   "requires": true,
   "packages": {
     "": {
       "name": "musare-frontend",
-      "version": "3.6.0-dev",
+      "version": "3.6.0",
       "license": "GPL-3.0",
       "dependencies": {
         "@babel/runtime": "^7.18.3",

+ 1 - 1
frontend/package.json

@@ -5,7 +5,7 @@
     "*.vue"
   ],
   "private": true,
-  "version": "3.6.0-dev",
+  "version": "3.6.0",
   "description": "An open-source collaborative music listening and catalogue curation application. Currently supporting YouTube based content.",
   "main": "main.js",
   "author": "Musare Team",

+ 0 - 1
frontend/src/components/PlaylistTabBase.vue

@@ -507,7 +507,6 @@
 					v-if="playlists.length > 0"
 				>
 					<draggable
-						tag="transition-group"
 						:component-data="{
 							name: !drag ? 'draggable-list-transition' : null
 						}"

+ 0 - 1
frontend/src/components/Queue.vue

@@ -8,7 +8,6 @@
 			}"
 		>
 			<draggable
-				tag="transition-group"
 				:component-data="{
 					name: !drag ? 'draggable-list-transition' : null
 				}"

+ 0 - 1
frontend/src/components/modals/EditPlaylist/index.vue

@@ -97,7 +97,6 @@
 
 					<aside class="menu">
 						<draggable
-							tag="transition-group"
 							:component-data="{
 								name: !drag ? 'draggable-list-transition' : null
 							}"

+ 31 - 11
frontend/src/components/modals/EditSong/index.vue

@@ -231,7 +231,11 @@
 							@loadError="onThumbnailLoadError"
 						/>
 						<img
-							v-if="!isYoutubeThumbnail && !songDeleted"
+							v-if="
+								!isYoutubeThumbnail &&
+								songDataLoaded &&
+								!songDeleted
+							"
 							class="thumbnail-dummy"
 							:src="song.thumbnail"
 							ref="thumbnailElement"
@@ -866,8 +870,11 @@ export default {
 			this.loadSong(youtubeId);
 		}
 	},
+	beforeMount() {
+		console.log("EDITSONG BEFOREMOUNT");
+	},
 	async mounted() {
-		console.log("MOUNTED");
+		console.log("EDITSONG MOUNTED");
 		this.activityWatchVideoDataInterval = setInterval(() => {
 			this.sendActivityWatchVideoData();
 		}, 1000);
@@ -1061,7 +1068,7 @@ export default {
 		*/
 	},
 	beforeUnmount() {
-		console.log("UNMOUNT");
+		console.log("EDITSONG BEFOREUNMOUNT");
 		this.unloadSong(this.youtubeId, this.song._id);
 
 		this.playerReady = false;
@@ -1096,8 +1103,19 @@ export default {
 				"editSong",
 				this.modalUuid
 			]);
+		} else {
+			console.log("UNREGISTER EDITSONG");
+			this.$store.unregisterModule([
+				"modals",
+				"editSongs",
+				this.modalUuid,
+				"editSong"
+			]);
 		}
 	},
+	unmounted() {
+		console.log("EDITSONG UNMOUNTED");
+	},
 	methods: {
 		onThumbnailLoad() {
 			if (this.$refs.thumbnailElement) {
@@ -1118,7 +1136,7 @@ export default {
 			this.thumbnailLoadError = error !== 0;
 		},
 		init() {
-			if (this.newSong && !this.youtubeId) {
+			if (this.newSong && !this.youtubeId && !this.bulk) {
 				this.setSong({
 					youtubeId: "",
 					title: "",
@@ -1368,7 +1386,8 @@ export default {
 			this.youtubeVideoCurrentTime = "0.000";
 			this.youtubeVideoDuration = "0.000";
 			this.youtubeVideoNote = "";
-			this.socket.dispatch("apis.leaveRoom", `edit-song.${songId}`);
+			if (songId)
+				this.socket.dispatch("apis.leaveRoom", `edit-song.${songId}`);
 			if (this.$refs.saveButton) this.$refs.saveButton.status = "default";
 		},
 		loadSong(youtubeId) {
@@ -1387,17 +1406,18 @@ export default {
 
 						this.songDataLoaded = true;
 
-						this.socket.dispatch(
-							"apis.joinRoom",
-							`edit-song.${this.song._id}`
-						);
+						if (song._id)
+							this.socket.dispatch(
+								"apis.joinRoom",
+								`edit-song.${song._id}`
+							);
 
 						if (
 							this.video.player &&
 							this.video.player.cueVideoById
 						) {
 							this.video.player.cueVideoById(
-								this.youtubeId,
+								youtubeId,
 								song.skipDuration
 							);
 						}
@@ -1857,9 +1877,9 @@ export default {
 				this.song.tags.splice(this.song.tags.indexOf(value), 1);
 		},
 		drawCanvas() {
-			if (!this.songDataLoaded) return;
 			const canvasElement =
 				this.$refs[`durationCanvas-${this.modalUuid}`];
+			if (!this.songDataLoaded || !canvasElement) return;
 			const ctx = canvasElement.getContext("2d");
 
 			const videoDuration = Number(this.youtubeVideoDuration);

+ 174 - 153
frontend/src/components/modals/EditSongs.vue

@@ -40,106 +40,114 @@
 					</header>
 					<section class="sidebar-body">
 						<div
-							class="item"
-							v-for="(
-								{ status, flagged, song }, index
-							) in filteredItems"
-							:key="song.youtubeId"
-							:ref="`edit-songs-item-${song.youtubeId}`"
+							v-show="filteredItems.length > 0"
+							class="edit-songs-items"
 						>
-							<song-item
-								:song="song"
-								:thumbnail="false"
-								:duration="false"
-								:disabled-actions="
-									song.removed ? ['all'] : ['report', 'edit']
-								"
-								:class="{
-									updated: song.updated,
-									removed: song.removed
-								}"
+							<div
+								class="item"
+								v-for="(
+									{ status, flagged, song }, index
+								) in filteredItems"
+								:key="`edit-songs-item-${index}`"
+								:ref="`edit-songs-item-${song.youtubeId}`"
 							>
-								<template #leftIcon>
-									<i
-										v-if="
-											currentSong.youtubeId ===
-												song.youtubeId && !song.removed
-										"
-										class="material-icons item-icon editing-icon"
-										content="Currently editing song"
-										v-tippy="{ theme: 'info' }"
-										@click="toggleDone(index)"
-										>edit</i
-									>
-									<i
-										v-else-if="song.removed"
-										class="material-icons item-icon removed-icon"
-										content="Song removed"
-										v-tippy="{ theme: 'info' }"
-										>delete_forever</i
-									>
-									<i
-										v-else-if="status === 'error'"
-										class="material-icons item-icon error-icon"
-										content="Error saving song"
-										v-tippy="{ theme: 'info' }"
-										@click="toggleDone(index)"
-										>error</i
-									>
-									<i
-										v-else-if="status === 'saving'"
-										class="material-icons item-icon saving-icon"
-										content="Currently saving song"
-										v-tippy="{ theme: 'info' }"
-										>pending</i
-									>
-									<i
-										v-else-if="flagged"
-										class="material-icons item-icon flag-icon"
-										content="Song flagged"
-										v-tippy="{ theme: 'info' }"
-										@click="toggleDone(index)"
-										>flag_circle</i
-									>
-									<i
-										v-else-if="status === 'done'"
-										class="material-icons item-icon done-icon"
-										content="Song marked complete"
-										v-tippy="{ theme: 'info' }"
-										@click="toggleDone(index)"
-										>check_circle</i
-									>
-									<i
-										v-else-if="status === 'todo'"
-										class="material-icons item-icon todo-icon"
-										content="Song marked todo"
-										v-tippy="{ theme: 'info' }"
-										@click="toggleDone(index)"
-										>cancel</i
-									>
-								</template>
-								<template v-if="!song.removed" #actions>
-									<i
-										class="material-icons edit-icon"
-										content="Edit Song"
-										v-tippy
-										@click="pickSong(song)"
-									>
-										edit
-									</i>
-								</template>
-								<template #tippyActions>
-									<i
-										class="material-icons flag-icon"
-										:class="{ flagged }"
-										content="Toggle Flag"
-										v-tippy
-										@click="toggleFlag(index)"
-									>
-										flag_circle
-									</i>
-								</template>
-							</song-item>
+								<song-item
+									:song="song"
+									:thumbnail="false"
+									:duration="false"
+									:disabled-actions="
+										song.removed
+											? ['all']
+											: ['report', 'edit']
+									"
+									:class="{
+										updated: song.updated,
+										removed: song.removed
+									}"
+								>
+									<template #leftIcon>
+										<i
+											v-if="
+												currentSong.youtubeId ===
+													song.youtubeId &&
+												!song.removed
+											"
+											class="material-icons item-icon editing-icon"
+											content="Currently editing song"
+											v-tippy="{ theme: 'info' }"
+											@click="toggleDone(index)"
+											>edit</i
+										>
+										<i
+											v-else-if="song.removed"
+											class="material-icons item-icon removed-icon"
+											content="Song removed"
+											v-tippy="{ theme: 'info' }"
+											>delete_forever</i
+										>
+										<i
+											v-else-if="status === 'error'"
+											class="material-icons item-icon error-icon"
+											content="Error saving song"
+											v-tippy="{ theme: 'info' }"
+											@click="toggleDone(index)"
+											>error</i
+										>
+										<i
+											v-else-if="status === 'saving'"
+											class="material-icons item-icon saving-icon"
+											content="Currently saving song"
+											v-tippy="{ theme: 'info' }"
+											>pending</i
+										>
+										<i
+											v-else-if="flagged"
+											class="material-icons item-icon flag-icon"
+											content="Song flagged"
+											v-tippy="{ theme: 'info' }"
+											@click="toggleDone(index)"
+											>flag_circle</i
+										>
+										<i
+											v-else-if="status === 'done'"
+											class="material-icons item-icon done-icon"
+											content="Song marked complete"
+											v-tippy="{ theme: 'info' }"
+											@click="toggleDone(index)"
+											>check_circle</i
+										>
+										<i
+											v-else-if="status === 'todo'"
+											class="material-icons item-icon todo-icon"
+											content="Song marked todo"
+											v-tippy="{ theme: 'info' }"
+											@click="toggleDone(index)"
+											>cancel</i
+										>
+									</template>
+									<template v-if="!song.removed" #actions>
+										<i
+											class="material-icons edit-icon"
+											content="Edit Song"
+											v-tippy
+											@click="pickSong(song)"
+										>
+											edit
+										</i>
+									</template>
+									<template #tippyActions>
+										<i
+											class="material-icons flag-icon"
+											:class="{ flagged }"
+											content="Toggle Flag"
+											v-tippy
+											@click="toggleFlag(index)"
+										>
+											flag_circle
+										</i>
+									</template>
+								</song-item>
+							</div>
 						</div>
 						<p v-if="filteredItems.length === 0" class="no-items">
 							{{
@@ -238,30 +246,33 @@ export default {
 			socket: "websockets/getSocket"
 		})
 	},
-	async mounted() {
-		this.socket.dispatch("apis.joinRoom", "edit-songs");
-
+	beforeMount() {
+		console.log("EDITSONGS BEFOREMOUNT");
 		this.$store.registerModule(
 			["modals", "editSongs", this.modalUuid, "editSong"],
 			editSong
 		);
+	},
+	async mounted() {
+		console.log("EDITSONGS MOUNTED");
+
+		this.socket.dispatch("apis.joinRoom", "edit-songs");
 
 		this.socket.dispatch(
 			"songs.getSongsFromYoutubeIds",
 			this.youtubeIds,
 			res => {
-				res.data.songs.forEach(song => {
-					this.items.push({
+				if (res.data.songs.length === 0) {
+					this.closeThisModal();
+					new Toast("You can't edit 0 songs.");
+				} else {
+					this.items = res.data.songs.map(song => ({
 						status: "todo",
 						flagged: false,
 						song
-					});
-				});
-
-				if (this.items.length === 0) {
-					this.closeThisModal();
-					new Toast("You can't edit 0 songs.");
-				} else this.editNextSong();
+					}));
+					this.editNextSong();
+				}
 			}
 		);
 
@@ -271,11 +282,12 @@ export default {
 				const index = this.items
 					.map(item => item.song.youtubeId)
 					.indexOf(res.data.song.youtubeId);
-				this.items[index].song = {
-					...this.items[index].song,
-					...res.data.song,
-					created: true
-				};
+				if (index >= 0)
+					this.items[index].song = {
+						...this.items[index].song,
+						...res.data.song,
+						created: true
+					};
 			},
 			{ modalUuid: this.modalUuid }
 		);
@@ -286,11 +298,12 @@ export default {
 				const index = this.items
 					.map(item => item.song.youtubeId)
 					.indexOf(res.data.song.youtubeId);
-				this.items[index].song = {
-					...this.items[index].song,
-					...res.data.song,
-					updated: true
-				};
+				if (index >= 0)
+					this.items[index].song = {
+						...this.items[index].song,
+						...res.data.song,
+						updated: true
+					};
 			},
 			{ modalUuid: this.modalUuid }
 		);
@@ -301,7 +314,7 @@ export default {
 				const index = this.items
 					.map(item => item.song._id)
 					.indexOf(res.data.songId);
-				this.items[index].song.removed = true;
+				if (index >= 0) this.items[index].song.removed = true;
 			},
 			{ modalUuid: this.modalUuid }
 		);
@@ -312,15 +325,17 @@ export default {
 				const index = this.items
 					.map(item => item.song.youtubeVideoId)
 					.indexOf(res.videoId);
-				if (index !== -1) this.items[index].song.removed = true;
+				if (index >= 0) this.items[index].song.removed = true;
 			},
 			{ modalUuid: this.modalUuid }
 		);
 	},
 	beforeUnmount() {
+		console.log("EDITSONGS BEFORE UNMOUNT");
 		this.socket.dispatch("apis.leaveRoom", "edit-songs");
 	},
 	unmounted() {
+		console.log("EDITSONGS UNMOUNTED");
 		// Delete the VueX module that was created for this modal, after all other cleanup tasks are performed
 		this.$store.unregisterModule(["modals", "editSongs", this.modalUuid]);
 	},
@@ -555,45 +570,51 @@ export default {
 		overflow: auto;
 		padding: 10px;
 
-		.item {
+		.edit-songs-items {
 			display: flex;
-			flex-direction: row;
-			align-items: center;
-			column-gap: 8px;
-
-			:deep(.song-item) {
-				.item-icon {
-					margin-right: 10px;
-					cursor: pointer;
-				}
+			flex-direction: column;
+			row-gap: 8px;
 
-				.removed-icon,
-				.error-icon {
-					color: var(--red);
-				}
+			.item {
+				display: flex;
+				flex-direction: row;
+				align-items: center;
+				column-gap: 8px;
+
+				:deep(.song-item) {
+					.item-icon {
+						margin-right: 10px;
+						cursor: pointer;
+					}
 
-				.saving-icon,
-				.todo-icon,
-				.editing-icon {
-					color: var(--primary-color);
-				}
+					.removed-icon,
+					.error-icon {
+						color: var(--red);
+					}
 
-				.done-icon {
-					color: var(--green);
-				}
+					.saving-icon,
+					.todo-icon,
+					.editing-icon {
+						color: var(--primary-color);
+					}
+
+					.done-icon {
+						color: var(--green);
+					}
 
-				.flag-icon {
-					color: var(--orange);
+					.flag-icon {
+						color: var(--orange);
 
-					&.flagged {
-						color: var(--grey);
+						&.flagged {
+							color: var(--grey);
+						}
 					}
-				}
 
-				&.removed {
-					filter: grayscale(100%);
-					cursor: not-allowed;
-					user-select: none;
+					&.removed {
+						filter: grayscale(100%);
+						cursor: not-allowed;
+						user-select: none;
+					}
 				}
 			}
 		}

+ 2 - 4
frontend/src/pages/Admin/Songs/Import.vue

@@ -265,8 +265,7 @@ export default {
 			createImport: {
 				stage: 2,
 				importMethod: "youtube",
-				youtubeUrl:
-					"https://www.youtube.com/playlist?list=PL3-sRm8xAzY9gpXTMGVHJWy_FMD67NBed",
+				youtubeUrl: "",
 				isImportingOnlyMusic: false
 			},
 			columnDefault: {
@@ -539,8 +538,7 @@ export default {
 			this.createImport = {
 				stage: 2,
 				importMethod: "youtube",
-				youtubeUrl:
-					"https://www.youtube.com/channel/UCio_FVgKVgqcHrRiXDpnqbw",
+				youtubeUrl: "",
 				isImportingOnlyMusic: false
 			};
 		},

+ 1 - 1
frontend/src/pages/Admin/YouTube/Videos.vue

@@ -18,7 +18,7 @@
 			data-action="youtube.getVideos"
 			name="admin-youtube-videos"
 			:max-width="1140"
-			:bulk-actions="{ width: 150 }"
+			:bulk-actions="{ width: 200 }"
 		>
 			<template #column-options="slotProps">
 				<div class="row-options">

+ 0 - 1
frontend/src/pages/Profile/Tabs/Playlists.vue

@@ -19,7 +19,6 @@
 			<hr class="section-horizontal-rule" />
 
 			<draggable
-				tag="transition-group"
 				:component-data="{
 					name: !drag ? 'draggable-list-transition' : null
 				}"

+ 0 - 1
frontend/src/pages/Station/Sidebar/Playlists.vue

@@ -2,7 +2,6 @@
 	<div id="my-playlists">
 		<div class="menu-list scrollable-list" v-if="playlists.length > 0">
 			<draggable
-				tag="transition-group"
 				:component-data="{
 					name: !drag ? 'draggable-list-transition' : null
 				}"

+ 2 - 2
frontend/src/store/modules/modals/editSong.js

@@ -59,13 +59,13 @@ export default {
 		},
 		editSong(state, song) {
 			state.newSong = !!song.newSong || !song._id;
-			state.youtubeId = song.newSong ? null : song.youtubeId;
+			state.youtubeId = song.youtubeId || null;
 			state.prefillData = song.prefill ? song.prefill : {};
 		},
 		setSong(state, song) {
 			if (song.discogs === undefined) song.discogs = null;
 			state.originalSong = JSON.parse(JSON.stringify(song));
-			state.song = { ...song };
+			state.song = JSON.parse(JSON.stringify(song));
 			state.newSong = !song._id;
 			state.youtubeId = song.youtubeId;
 		},

+ 33 - 12
frontend/src/vuex_helpers.js

@@ -2,20 +2,41 @@ import { defineAsyncComponent } from "vue";
 
 const mapModalState = (namespace, map) => {
 	const modalState = {};
+	// console.log("MAP MODAL STATE", namespace);
+
 	Object.entries(map).forEach(([mapKey, mapValue]) => {
 		modalState[mapKey] = function func() {
-			return mapValue(
-				namespace
-					.replace(
-						"MODAL_MODULE_PATH",
-						namespace.indexOf("MODAL_MODULE_PATH") !== -1
-							? this.modalModulePath
-							: null
-					)
-					.replace("MODAL_UUID", this.modalUuid)
-					.split("/")
-					.reduce((a, b) => a[b], this.$store.state)
-			);
+			// console.log(
+			// 	321,
+			// 	namespace
+			// 		.replace(
+			// 			"MODAL_MODULE_PATH",
+			// 			namespace.indexOf("MODAL_MODULE_PATH") !== -1
+			// 				? this.modalModulePath
+			// 				: null
+			// 		)
+			// 		.replace("MODAL_UUID", this.modalUuid)
+			// 		.split("/")
+			// );
+			// console.log(3211, mapKey);
+
+			const state = namespace
+				.replace(
+					"MODAL_MODULE_PATH",
+					namespace.indexOf("MODAL_MODULE_PATH") !== -1
+						? this.modalModulePath
+						: null
+				)
+				.replace("MODAL_UUID", this.modalUuid)
+				.split("/")
+				.reduce((a, b) => a[b], this.$store.state);
+
+			// console.log(32111, state);
+			// if (state) console.log(321111, mapValue(state));
+			// else console.log(321111, "NADA");
+
+			if (state) return mapValue(state);
+			return mapValue({});
 		};
 	});
 	return modalState;