Ver Fonte

feat(SortablePlaylists): added vue mixin to allow for sortable playlists in multiple places

Signed-off-by: Jonathan <theflametrooper@gmail.com>
Jonathan há 4 anos atrás
pai
commit
251a9c0111

+ 23 - 8
backend/logic/actions/playlists.js

@@ -229,18 +229,33 @@ export default {
 	 * @param {Function} cb - gets called with the result
 	 */
 	indexForUser: async function indexForUser(session, userId, cb) {
-		const playlistModel = await DBModule.runJob(
-			"GET_MODEL",
-			{
-				modelName: "playlist"
-			},
-			this
-		);
+		const playlistModel = await DBModule.runJob("GET_MODEL", { modelName: "playlist" }, this);
+		const userModel = await DBModule.runJob("GET_MODEL", { modelName: "user" }, this);
 
 		async.waterfall(
 			[
 				next => {
-					playlistModel.find({ createdBy: userId }, next);
+					userModel.findById(userId).select({ "preferences.orderOfPlaylists": -1 }).exec(next);
+				},
+
+				({ preferences }, next) => {
+					const { orderOfPlaylists } = preferences;
+
+					const match = {
+						createdBy: userId
+					};
+
+					// if a playlist order exists
+					if (orderOfPlaylists > 0) match._id = { $in: orderOfPlaylists };
+
+					playlistModel
+						.aggregate()
+						.match(match)
+						.addFields({
+							weight: { $indexOfArray: [orderOfPlaylists, "$_id"] }
+						})
+						.sort({ weight: 1 })
+						.exec(next);
 				},
 
 				(playlists, next) => {

+ 15 - 6
frontend/src/components/ui/PlaylistItem.vue

@@ -99,13 +99,22 @@ export default {
 		display: flex;
 		align-items: center;
 		padding: 12px;
-	}
 
-	button,
-	.button {
-		width: 100%;
-		font-size: 17px;
-		height: 36px;
+		div {
+			display: flex;
+			align-items: center;
+
+			button,
+			.button {
+				width: 100%;
+				font-size: 17px;
+				height: 36px;
+
+				&:not(:last-of-type) {
+					margin-right: 5px;
+				}
+			}
+		}
 	}
 }
 </style>

+ 66 - 0
frontend/src/mixins/SortablePlaylists.vue

@@ -0,0 +1,66 @@
+<script>
+import { mapState } from "vuex";
+import Toast from "toasters";
+import draggable from "vuedraggable";
+
+export default {
+	components: { draggable },
+	data() {
+		return {
+			orderOfPlaylists: [],
+			interval: null,
+			drag: false
+		};
+	},
+	computed: {
+		...mapState({
+			station: state => state.station.station
+		}),
+		dragOptions() {
+			return {
+				animation: 200,
+				group: "description",
+				disabled: false,
+				ghostClass: "draggable-list-ghost"
+			};
+		}
+	},
+	mounted() {
+		// checks if playlist order has changed every 1/2 second
+		this.interval = setInterval(() => this.savePlaylistOrder(), 500);
+	},
+	methods: {
+		calculatePlaylistOrder() {
+			const calculatedOrder = [];
+			this.playlists.forEach(playlist =>
+				calculatedOrder.push(playlist._id)
+			);
+
+			return calculatedOrder;
+		},
+		savePlaylistOrder() {
+			const recalculatedOrder = this.calculatePlaylistOrder();
+			if (
+				JSON.stringify(this.orderOfPlaylists) ===
+				JSON.stringify(recalculatedOrder)
+			)
+				return; // nothing has changed
+
+			this.socket.emit(
+				"users.updateOrderOfPlaylists",
+				recalculatedOrder,
+				res => {
+					if (res.status === "failure")
+						return new Toast({
+							content: res.message,
+							timeout: 8000
+						});
+
+					this.orderOfPlaylists = this.calculatePlaylistOrder(); // new order in regards to the database
+					return new Toast({ content: res.message, timeout: 4000 });
+				}
+			);
+		}
+	}
+};
+</script>

+ 58 - 40
frontend/src/pages/Profile.vue

@@ -161,32 +161,58 @@
 
 						<hr class="section-horizontal-rule" />
 
-						<div
-							class="item"
-							v-for="playlist in playlists"
-							:key="playlist._id"
+						<draggable
+							class="menu-list scrollable-list"
+							v-if="playlists.length > 0"
+							v-model="playlists"
+							v-bind="dragOptions"
+							@start="drag = true"
+							@end="drag = false"
 						>
-							<playlist-item
-								v-if="
-									playlist.privacy === 'public' ||
-										(playlist.privacy === 'private' &&
-											playlist.createdBy === userId)
+							<transition-group
+								type="transition"
+								:name="
+									!drag ? 'draggable-list-transition' : null
 								"
-								:playlist="playlist"
 							>
-								<div v-if="user._id === userId" slot="actions">
-									<button
-										class="button is-primary"
-										@click="editPlaylistClick(playlist._id)"
+								<div
+									class="item"
+									v-for="playlist in playlists"
+									:key="playlist._id"
+								>
+									<playlist-item
+										v-if="
+											playlist.privacy === 'public' ||
+												(playlist.privacy ===
+													'private' &&
+													playlist.createdBy ===
+														userId)
+										"
+										:playlist="playlist"
 									>
-										<i
-											class="material-icons icon-with-button"
-											>create</i
-										>Edit
-									</button>
+										<div
+											v-if="user._id === userId"
+											slot="actions"
+										>
+											<button
+												class="button is-primary"
+												@click="
+													editPlaylistClick(
+														playlist._id
+													)
+												"
+											>
+												<i
+													class="material-icons icon-with-button"
+													>create</i
+												>Edit
+											</button>
+										</div>
+									</playlist-item>
 								</div>
-							</playlist-item>
-						</div>
+							</transition-group>
+						</draggable>
+
 						<button
 							v-if="user._id === userId"
 							class="button is-primary"
@@ -215,8 +241,10 @@
 import { mapState, mapActions } from "vuex";
 import { format, formatDistance, parseISO } from "date-fns";
 import Toast from "toasters";
+import draggable from "vuedraggable";
 
 import PlaylistItem from "../components/ui/PlaylistItem.vue";
+import SortablePlaylists from "../mixins/SortablePlaylists.vue";
 import MainHeader from "../components/layout/MainHeader.vue";
 import MainFooter from "../components/layout/MainFooter.vue";
 
@@ -228,8 +256,10 @@ export default {
 		MainFooter,
 		PlaylistItem,
 		CreatePlaylist: () => import("../components/modals/CreatePlaylist.vue"),
-		EditPlaylist: () => import("../components/modals/EditPlaylist.vue")
+		EditPlaylist: () => import("../components/modals/EditPlaylist.vue"),
+		draggable
 	},
+	mixins: [SortablePlaylists],
 	data() {
 		return {
 			user: {},
@@ -291,6 +321,7 @@ export default {
 							res => {
 								if (res.status === "success")
 									this.playlists = res.data;
+								this.orderOfPlaylists = this.calculatePlaylistOrder(); // order in regards to the database
 							}
 						);
 
@@ -544,23 +575,6 @@ export default {
 @import "../styles/global.scss";
 
 @media only screen and (max-width: 750px) {
-	// #page-title {
-	// 	margin: 0;
-	// 	font-size: 40px;
-	// }
-
-	// #sidebar-with-content {
-	// 	width: 962px;
-	// 	margin: 0 auto;
-	// 	margin-top: 30px;
-	// 	flex-direction: row;
-
-	// 	.content {
-	// 		width: 600px;
-	// 		margin-top: 0px !important;
-	// 	}
-	// }
-
 	.info-section {
 		margin-top: 0 !important;
 
@@ -572,7 +586,7 @@ export default {
 			margin-top: 24px;
 		}
 
-		.buttons .button:not(last-of-type) {
+		.buttons .button:not(:last-of-type) {
 			margin-bottom: 10px;
 			margin-right: 5px;
 		}
@@ -768,6 +782,10 @@ export default {
 		.item {
 			overflow: hidden;
 
+			.playlist {
+				cursor: move;
+			}
+
 			&:not(:last-of-type) {
 				margin-bottom: 10px;
 			}

+ 27 - 76
frontend/src/pages/Station/components/Sidebar/MyPlaylists.vue

@@ -67,36 +67,21 @@ import { mapState, mapActions } from "vuex";
 import Toast from "toasters";
 import draggable from "vuedraggable";
 
-import PlaylistItem from "../../../../components/ui/PlaylistItem.vue";
 import io from "../../../../io";
+import PlaylistItem from "../../../../components/ui/PlaylistItem.vue";
+import SortablePlaylists from "../../../../mixins/SortablePlaylists.vue";
 
 export default {
 	components: { PlaylistItem, draggable },
+	mixins: [SortablePlaylists],
 	data() {
 		return {
-			orderOfPlaylists: [],
-			interval: null,
-			playlists: [],
-			drag: false
+			playlists: []
 		};
 	},
-	computed: {
-		...mapState("modalVisibility", {
-			modals: state => state.modals.station
-		}),
-		...mapState({
-			station: state => state.station.station,
-			userId: state => state.user.auth.userId
-		}),
-		dragOptions() {
-			return {
-				animation: 200,
-				group: "description",
-				disabled: false,
-				ghostClass: "draggable-list-ghost"
-			};
-		}
-	},
+	computed: mapState({
+		station: state => state.station.station
+	}),
 	mounted() {
 		io.getSocket(socket => {
 			this.socket = socket;
@@ -154,9 +139,6 @@ export default {
 					}
 				});
 			});
-
-			// checks if playlist order has changed every 1/2 second
-			this.interval = setInterval(() => this.savePlaylistOrder(), 500);
 		});
 	},
 	beforeDestroy() {
@@ -203,37 +185,6 @@ export default {
 				return false;
 			return true;
 		},
-		calculatePlaylistOrder() {
-			const calculatedOrder = [];
-			this.playlists.forEach(playlist =>
-				calculatedOrder.push(playlist._id)
-			);
-
-			return calculatedOrder;
-		},
-		savePlaylistOrder() {
-			const recalculatedOrder = this.calculatePlaylistOrder();
-			if (
-				JSON.stringify(this.orderOfPlaylists) ===
-				JSON.stringify(recalculatedOrder)
-			)
-				return; // nothing has changed
-
-			this.socket.emit(
-				"users.updateOrderOfPlaylists",
-				recalculatedOrder,
-				res => {
-					if (res.status === "failure")
-						return new Toast({
-							content: res.message,
-							timeout: 8000
-						});
-
-					this.orderOfPlaylists = this.calculatePlaylistOrder(); // new order in regards to the database
-					return new Toast({ content: res.message, timeout: 4000 });
-				}
-			);
-		},
 		...mapActions("modalVisibility", ["openModal"]),
 		...mapActions("user/playlists", ["editPlaylist"])
 	}
@@ -248,26 +199,6 @@ export default {
 	margin-bottom: 20px;
 	border-radius: 0 0 5px 5px;
 	max-height: 100%;
-
-	.nothing-here-text {
-		margin-bottom: 10px;
-	}
-
-	.icons-group {
-		display: flex;
-		align-items: center;
-
-		button {
-			background-color: var(--station-theme) !important;
-			&:not(:first-of-type) {
-				margin-left: 5px;
-			}
-			&:hover,
-			&:focus {
-				filter: brightness(90%);
-			}
-		}
-	}
 }
 
 .night-mode {
@@ -277,6 +208,26 @@ export default {
 	}
 }
 
+.nothing-here-text {
+	margin-bottom: 10px;
+}
+
+.icons-group {
+	display: flex;
+	align-items: center;
+
+	button {
+		background-color: var(--station-theme) !important;
+		&:not(:first-of-type) {
+			margin-left: 5px;
+		}
+		&:hover,
+		&:focus {
+			filter: brightness(90%);
+		}
+	}
+}
+
 .menu-list .playlist {
 	align-items: center;
 	cursor: move;