Browse Source

feat(Station page): add song to playlist is now a dropdown

Signed-off-by: Jonathan <theflametrooper@gmail.com>
Jonathan 4 years ago
parent
commit
fb71b8c4b0

+ 3 - 5
frontend/src/pages/Settings/index.vue

@@ -119,14 +119,12 @@ export default {
 					})
 				);
 
-				this.socket.on("event:user.unlinkGithub", () => {
+				this.socket.on("event:user.unlinkGithub", () =>
 					this.updateOriginalUser({
 						property: "github",
 						value: false
-					});
-
-					console.log("unlink");
-				});
+					})
+				);
 			});
 		}
 	},

+ 0 - 157
frontend/src/pages/Station/AddSongToPlaylist.vue

@@ -1,157 +0,0 @@
-<template>
-	<modal title="Add Song To Playlist">
-		<template #body>
-			<h4 class="songTitle">
-				{{ currentSong.title }}
-			</h4>
-			<h5 class="songArtist">
-				{{ currentSong.artists }}
-			</h5>
-			<aside class="menu">
-				<p class="menu-label">Playlists</p>
-				<ul class="menu-list">
-					<li v-for="(playlist, index) in playlistsArr" :key="index">
-						<div class="playlist">
-							<span
-								v-if="playlists[playlist._id].hasSong"
-								class="icon is-small"
-								@click="removeSongFromPlaylist(playlist._id)"
-							>
-								<i class="material-icons">playlist_add_check</i>
-							</span>
-							<span
-								v-else
-								class="icon"
-								@click="addSongToPlaylist(playlist._id)"
-							>
-								<i class="material-icons">playlist_add</i>
-							</span>
-							{{ playlist.displayName }}
-						</div>
-					</li>
-				</ul>
-			</aside>
-		</template>
-	</modal>
-</template>
-
-<script>
-import { mapState } from "vuex";
-
-import Toast from "toasters";
-import Modal from "../../components/Modal.vue";
-import io from "../../io";
-
-export default {
-	components: { Modal },
-	data() {
-		return {
-			playlists: {},
-			playlistsArr: [],
-			songId: null,
-			song: null
-		};
-	},
-	computed: {
-		...mapState("station", {
-			currentSong: state => state.currentSong
-		})
-	},
-	mounted() {
-		this.songId = this.currentSong.songId;
-		this.song = this.currentSong;
-		io.getSocket(socket => {
-			this.socket = socket;
-			this.socket.emit("playlists.indexForUser", res => {
-				if (res.status === "success") {
-					res.data.forEach(playlist => {
-						this.playlists[playlist._id] = playlist;
-					});
-					this.recalculatePlaylists();
-				}
-			});
-		});
-	},
-	methods: {
-		addSongToPlaylist(playlistId) {
-			this.socket.emit(
-				"playlists.addSongToPlaylist",
-				false,
-				this.currentSong.songId,
-				playlistId,
-				res => {
-					new Toast({ content: res.message, timeout: 4000 });
-					if (res.status === "success") {
-						this.playlists[playlistId].songs.push(this.song);
-					}
-					this.recalculatePlaylists();
-				}
-			);
-		},
-		removeSongFromPlaylist(playlistId) {
-			this.socket.emit(
-				"playlists.removeSongFromPlaylist",
-				this.songId,
-				playlistId,
-				res => {
-					new Toast({ content: res.message, timeout: 4000 });
-					if (res.status === "success") {
-						this.playlists[playlistId].songs.forEach(
-							(song, index) => {
-								if (song.songId === this.songId)
-									this.playlists[playlistId].songs.splice(
-										index,
-										1
-									);
-							}
-						);
-					}
-					this.recalculatePlaylists();
-				}
-			);
-		},
-		recalculatePlaylists() {
-			this.playlistsArr = Object.values(this.playlists).map(playlist => {
-				let hasSong = false;
-				for (let i = 0; i < playlist.songs.length; i += 1) {
-					if (playlist.songs[i].songId === this.songId) {
-						hasSong = true;
-					}
-				}
-
-				playlist.hasSong = hasSong; // eslint-disable-line no-param-reassign
-				this.playlists[playlist._id] = playlist;
-				return playlist;
-			});
-		}
-	}
-};
-</script>
-
-<style lang="scss" scoped>
-@import "../../styles/global.scss";
-
-.modal-card-body {
-	padding: 16px;
-	display: flex;
-}
-
-.icon.is-small {
-	margin-right: 10px !important;
-}
-
-.songTitle {
-	font-size: 22px;
-	padding: 0 10px;
-}
-
-.songArtist {
-	font-size: 19px;
-	font-weight: 200;
-	padding: 0 10px;
-}
-
-.menu-label {
-	font-size: 16px;
-}
-</style>

+ 307 - 0
frontend/src/pages/Station/components/AddToPlaylistDropdown.vue

@@ -0,0 +1,307 @@
+<template>
+	<div id="nav-dropdown">
+		<div class="nav-dropdown-items" v-if="playlistsArr.length > 0">
+			<!-- <a class="nav-item" id="nightmode-toggle">
+				<span>Nightmode</span>
+				<label class="switch">
+					<input type="checkbox" checked />
+					<span class="slider round"></span>
+				</label>
+			</a> -->
+
+			<button
+				class="nav-item"
+				href="#"
+				v-for="(playlist, index) in playlistsArr"
+				:key="index"
+				@click.prevent="toggleSongInPlaylist(playlist._id)"
+				:title="playlist.displayName"
+			>
+				<p class="control is-expanded checkbox-control">
+					<input
+						type="checkbox"
+						:id="index"
+						v-model="playlists[playlist._id].hasSong"
+					/>
+					<label :for="index">
+						<span></span>
+						<p>{{ playlist.displayName }}</p>
+					</label>
+				</p>
+			</button>
+		</div>
+		<p class="nav-dropdown-items" id="no-playlists" v-else>
+			You haven't created any playlists
+		</p>
+	</div>
+</template>
+
+<script>
+import { mapState } from "vuex";
+
+import Toast from "toasters";
+import io from "../../../io";
+
+export default {
+	data() {
+		return {
+			playlists: {},
+			playlistsArr: [],
+			songId: null,
+			song: null
+		};
+	},
+	computed: {
+		...mapState("station", {
+			currentSong: state => state.currentSong
+		})
+	},
+	mounted() {
+		this.songId = this.currentSong.songId;
+		this.song = this.currentSong;
+		io.getSocket(socket => {
+			this.socket = socket;
+			this.socket.emit("playlists.indexForUser", res => {
+				if (res.status === "success") {
+					res.data.forEach(playlist => {
+						this.playlists[playlist._id] = playlist;
+					});
+
+					this.recalculatePlaylists();
+				}
+			});
+		});
+	},
+	methods: {
+		toggleSongInPlaylist(playlistId) {
+			if (!this.playlists[playlistId].hasSong) {
+				this.socket.emit(
+					"playlists.addSongToPlaylist",
+					false,
+					this.currentSong.songId,
+					playlistId,
+					res => {
+						new Toast({ content: res.message, timeout: 4000 });
+
+						if (res.status === "success") {
+							this.playlists[playlistId].songs.push(this.song);
+						}
+
+						this.recalculatePlaylists();
+					}
+				);
+			} else {
+				this.socket.emit(
+					"playlists.removeSongFromPlaylist",
+					this.songId,
+					playlistId,
+					res => {
+						new Toast({ content: res.message, timeout: 4000 });
+
+						if (res.status === "success") {
+							this.playlists[playlistId].songs.forEach(
+								(song, index) => {
+									if (song.songId === this.songId)
+										this.playlists[playlistId].songs.splice(
+											index,
+											1
+										);
+								}
+							);
+						}
+
+						this.recalculatePlaylists();
+					}
+				);
+			}
+		},
+		recalculatePlaylists() {
+			this.playlistsArr = Object.values(this.playlists).map(playlist => {
+				let hasSong = false;
+				for (let i = 0; i < playlist.songs.length; i += 1) {
+					if (playlist.songs[i].songId === this.songId) {
+						hasSong = true;
+					}
+				}
+
+				playlist.hasSong = hasSong; // eslint-disable-line no-param-reassign
+				this.playlists[playlist._id] = playlist;
+				return playlist;
+			});
+		}
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+@import "../../../styles/global.scss";
+
+.night-mode {
+	.nav-dropdown-items {
+		background-color: $dark-grey-2;
+
+		.nav-item {
+			background-color: $dark-grey;
+
+			&:focus {
+				outline-color: $dark-grey;
+			}
+
+			p {
+				color: #fff;
+			}
+		}
+	}
+}
+
+#nav-dropdown {
+	position: absolute;
+	margin-left: 4px;
+	z-index: 1;
+	margin-bottom: 36px;
+	width: inherit;
+}
+
+#nav-dropdown-triangle {
+	border-style: solid;
+	border-width: 15px 15px 0 15px;
+	border-color: $dark-grey-2 transparent transparent transparent;
+}
+
+.nav-dropdown-items {
+	border: 1px solid $light-grey-2;
+	border-bottom: 0;
+	background-color: #fff;
+	padding: 5px;
+	border-radius: 5px;
+
+	.nav-item {
+		width: 100%;
+		justify-content: flex-start;
+		border: 0;
+		padding: 10px;
+		font-size: 15.5px;
+		height: 36px;
+		background: #eee;
+		border-radius: 5px;
+		cursor: pointer;
+
+		.checkbox-control {
+			display: flex;
+			align-items: center;
+			margin-bottom: 0 !important;
+
+			input {
+				margin-right: 5px;
+			}
+
+			input[type="checkbox"] {
+				opacity: 0;
+				position: absolute;
+			}
+
+			label {
+				display: flex;
+				flex-direction: row;
+				align-items: center;
+
+				span {
+					cursor: pointer;
+					width: 24px;
+					height: 24px;
+					background-color: $white;
+					display: inline-block;
+					border: 1px solid $dark-grey-2;
+					position: relative;
+					border-radius: 3px;
+				}
+
+				p {
+					margin-left: 10px;
+					cursor: pointer;
+					color: #000;
+				}
+			}
+
+			input[type="checkbox"]:checked + label span::after {
+				content: "";
+				width: 18px;
+				height: 18px;
+				left: 2px;
+				top: 2px;
+				border-radius: 3px;
+				background-color: $musare-blue;
+				position: absolute;
+			}
+		}
+
+		&:focus {
+			outline-color: $light-grey-2;
+		}
+
+		&:not(:last-of-type) {
+			margin-bottom: 5px;
+		}
+	}
+}
+
+#nightmode-toggle {
+	display: flex;
+	justify-content: space-evenly;
+}
+
+/*  CSS - Toggle Switch */
+
+.switch {
+	position: relative;
+	display: inline-block;
+	width: 50px;
+	height: 24px;
+
+	input {
+		opacity: 0;
+		width: 0;
+		height: 0;
+	}
+}
+
+.slider {
+	position: absolute;
+	cursor: pointer;
+	top: 0;
+	left: 0;
+	right: 0;
+	bottom: 0;
+	background-color: #ccc;
+	transition: 0.4s;
+	border-radius: 34px;
+
+	&:before {
+		position: absolute;
+		content: "";
+		height: 16px;
+		width: 16px;
+		left: 4px;
+		bottom: 4px;
+		background-color: white;
+		transition: 0.4s;
+		border-radius: 50%;
+	}
+}
+
+input:checked + .slider {
+	background-color: $primary-color;
+}
+
+input:focus + .slider {
+	box-shadow: 0 0 1px $primary-color;
+}
+
+input:checked + .slider:before {
+	transform: translateX(26px);
+}
+
+#no-playlists {
+	padding: 10px;
+}
+</style>

+ 67 - 23
frontend/src/pages/Station/index.vue

@@ -245,22 +245,41 @@
 									</button>
 								</div>
 
-								<!-- Add Song To Playlist Button (will be dropdown soon) -->
-								<button
-									class="button is-primary"
-									id="add-song-to-playlist"
-									@click="
-										openModal({
-											sector: 'station',
-											modal: 'addSongToPlaylist'
-										})
-									"
-								>
-									<i class="material-icons">queue</i>
-									<span class="optional-desktop-only-text"
-										>Add Song To Playlist</span
-									>
-								</button>
+								<!-- Add Song To Playlist Button & Dropdown -->
+								<div id="add-song-to-playlist">
+									<div class="control has-addons">
+										<button
+											class="button is-primary"
+											@click="
+												showPlaylistDropdown = !showPlaylistDropdown
+											"
+										>
+											<i class="material-icons">queue</i>
+											<span
+												class="optional-desktop-only-text"
+												>Add Song To Playlist</span
+											>
+										</button>
+										<button
+											class="button"
+											id="dropdown-toggle"
+											@click="
+												showPlaylistDropdown = !showPlaylistDropdown
+											"
+										>
+											<i class="material-icons">
+												{{
+													showPlaylistDropdown
+														? "expand_more"
+														: "expand_less"
+												}}
+											</i>
+										</button>
+									</div>
+									<add-to-playlist-dropdown
+										v-if="showPlaylistDropdown"
+									/>
+								</div>
 							</div>
 						</div>
 					</div>
@@ -274,7 +293,6 @@
 			</div>
 
 			<song-queue v-if="modals.addSongToQueue" />
-			<add-to-playlist v-if="modals.addSongToPlaylist" />
 			<edit-playlist v-if="modals.editPlaylist" />
 			<create-playlist v-if="modals.createPlaylist" />
 			<edit-station v-if="modals.editStation" store="station" />
@@ -330,6 +348,7 @@ import MainHeader from "../../components/layout/MainHeader.vue";
 import Z404 from "../404.vue";
 
 import FloatingBox from "../../components/ui/FloatingBox.vue";
+import AddToPlaylistDropdown from "./components/AddToPlaylistDropdown.vue";
 
 import io from "../../io";
 import keyboardShortcuts from "../../keyboardShortcuts";
@@ -342,7 +361,6 @@ export default {
 	components: {
 		MainHeader,
 		SongQueue: () => import("./AddSongToQueue.vue"),
-		AddToPlaylist: () => import("./AddSongToPlaylist.vue"),
 		EditPlaylist: () => import("../../components/modals/EditPlaylist.vue"),
 		CreatePlaylist: () =>
 			import("../../components/modals/CreatePlaylist.vue"),
@@ -351,7 +369,8 @@ export default {
 		Z404,
 		FloatingBox,
 		CurrentlyPlaying,
-		StationSidebar
+		StationSidebar,
+		AddToPlaylistDropdown
 	},
 	data() {
 		return {
@@ -377,7 +396,8 @@ export default {
 			seeking: false,
 			playbackRate: 1,
 			volumeSliderValue: 0,
-			favoriteStation: false
+			favoriteStation: false,
+			showPlaylistDropdown: false
 		};
 	},
 	computed: {
@@ -1437,6 +1457,15 @@ export default {
 	#control-bar-container {
 		border: 0 !important;
 	}
+
+	#dropdown-toggle {
+		background-color: $dark-grey !important;
+		border: 0;
+
+		i {
+			color: #fff;
+		}
+	}
 }
 
 #station-outer-container {
@@ -1522,11 +1551,11 @@ export default {
 					display: none;
 				}
 
-				.button {
+				.button:not(#dropdown-toggle) {
 					width: 75px;
 				}
 
-				#add-song-to-playlist,
+				#add-song-to-playlist .button,
 				#local-resume,
 				#local-pause {
 					i {
@@ -1669,6 +1698,7 @@ export default {
 					justify-content: space-around;
 					padding: 10px 0;
 					width: 100%;
+					// height: 62px;
 					background: #fff;
 					border: 1px solid $light-grey-2;
 					border-radius: 0 0 5px 5px;
@@ -1704,6 +1734,9 @@ export default {
 
 						p {
 							font-size: 22px;
+							/** prevents duration width slightly varying and shifting other controls slightly */
+							width: 125px;
+							text-align: center;
 						}
 					}
 
@@ -1816,7 +1849,7 @@ export default {
 						display: flex;
 
 						#dislike-song,
-						#add-song-to-playlist {
+						#add-song-to-playlist .button:not(#dropdown-toggle) {
 							margin-left: 5px;
 						}
 
@@ -1833,6 +1866,17 @@ export default {
 								background-color: darken($red, 5%) !important;
 							}
 						}
+
+						#add-song-to-playlist {
+							display: flex;
+							flex-direction: column-reverse;
+							width: 224px;
+
+							.control {
+								width: fit-content;
+								margin-bottom: 0 !important;
+							}
+						}
 					}
 				}
 			}

+ 0 - 1
frontend/src/store/modules/modals.js

@@ -13,7 +13,6 @@ const state = {
 			addSongToQueue: false,
 			editPlaylist: false,
 			createPlaylist: false,
-			addSongToPlaylist: false,
 			editStation: false,
 			report: false
 		},