Procházet zdrojové kódy

Merge branch 'polishing' of github.com:Musare/MusareNode into polishing

Jonathan Graham před 3 roky
rodič
revize
fc98e08f2b

+ 0 - 1
backend/logic/actions/users.js

@@ -537,7 +537,6 @@ export default {
 					next(null, {
 						_id,
 						username,
-						name: username,
 						email: {
 							address: email,
 							verificationToken

+ 10 - 1
frontend/dist/config/template.json

@@ -22,6 +22,15 @@
 	"messages": {
 		"accountRemoval": "Your account will be deactivated instantly and your data will shortly be deleted by an admin."
 	},
+	// "shortcutOverrides": {
+	// 	"editSong.useAllDiscogs": {
+	// 		"keyCode": 68,
+	// 		"ctrl": true,
+	// 		"alt": true,
+	// 		"shift": false,
+	// 		"preventDefault": true
+	// 	}
+	// },
 	"skipConfigVersionCheck": false,
-	"configVersion": 6
+	"configVersion": 7
 }

+ 6 - 15
frontend/js/utils.js

@@ -43,11 +43,8 @@ export default {
 		const hours = Math.floor(duration / (60 * 60));
 		const formatHours = () => {
 			if (hours > 0) {
-				if (hours > 1) {
-					if (hours < 10) return `0${hours} hours `;
-					return `${hours} hours `;
-				}
-				return `0${hours} hour `;
+				if (hours > 1) return `${hours} hours `;
+				return `${hours} hour `;
 			}
 			return "";
 		};
@@ -55,11 +52,8 @@ export default {
 		const minutes = Math.floor((duration - hours * 60 * 60) / 60);
 		const formatMinutes = () => {
 			if (minutes > 0) {
-				if (minutes > 1) {
-					if (minutes < 10) return `0${minutes} minutes `;
-					return `${minutes} minutes `;
-				}
-				return `0${minutes} minute `;
+				if (minutes > 1) return `${minutes} minutes `;
+				return `${minutes} minute `;
 			}
 			return "";
 		};
@@ -67,11 +61,8 @@ export default {
 		const seconds = Math.floor(duration - hours * 60 * 60 - minutes * 60);
 		const formatSeconds = () => {
 			if (seconds > 0) {
-				if (seconds > 1) {
-					if (seconds < 10) return `0${seconds} seconds `;
-					return `${seconds} seconds `;
-				}
-				return `0${seconds} second `;
+				if (seconds > 1) return `${seconds} seconds `;
+				return `${seconds} second `;
 			}
 			return "";
 		};

+ 3 - 3
frontend/package-lock.json

@@ -8607,9 +8607,9 @@
       }
     },
     "url-parse": {
-      "version": "1.5.1",
-      "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.1.tgz",
-      "integrity": "sha512-HOfCOUJt7iSYzEx/UqgtwKRMC6EU91NFhsCHMv9oM03VJcVo2Qrp8T8kI9D7amFf1cu+/3CEhgb3rF9zL7k85Q==",
+      "version": "1.5.3",
+      "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.3.tgz",
+      "integrity": "sha512-IIORyIQD9rvj0A4CLWsHkBBJuNqWpFQe224b6j9t/ABmquIS0qDU2pY6kl6AuOrL5OkCXHMCFNe1jBcuAggjvQ==",
       "dev": true,
       "requires": {
         "querystringify": "^2.1.1",

+ 22 - 3
frontend/src/App.vue

@@ -5,7 +5,7 @@
 			<router-view
 				:key="$route.fullPath"
 				class="main-container"
-				:class="{ 'main-container-modal-active': aModalIsOpen }"
+				:class="{ 'main-container-modal-active': aModalIsOpen2 }"
 			/>
 			<what-is-new v-show="modals.whatIsNew" />
 			<login-modal v-if="modals.login" />
@@ -45,7 +45,9 @@ export default {
 		return {
 			apiDomain: "",
 			socketConnected: true,
-			keyIsDown: false
+			keyIsDown: false,
+			scrollPosition: { y: 0, x: 0 },
+			aModalIsOpen2: false
 		};
 	},
 	computed: {
@@ -79,6 +81,23 @@ export default {
 		activityWatch(activityWatch) {
 			if (activityWatch) aw.enable();
 			else aw.disable();
+		},
+		aModalIsOpen(aModalIsOpen) {
+			if (aModalIsOpen) {
+				this.scrollPosition = {
+					x: window.scrollX,
+					y: window.scrollY
+				};
+				this.aModalIsOpen2 = true;
+			} else {
+				this.aModalIsOpen2 = false;
+				setTimeout(() => {
+					window.scrollTo(
+						this.scrollPosition.x,
+						this.scrollPosition.y
+					);
+				}, 10);
+			}
 		}
 	},
 	async mounted() {
@@ -411,7 +430,7 @@ textarea {
 
 .main-container.main-container-modal-active {
 	height: 100% !important;
-	overflow: hidden;
+	overflow: hidden !important;
 }
 
 a {

+ 14 - 6
frontend/src/components/FloatingBox.vue

@@ -13,10 +13,10 @@
 			top: top + 'px',
 			left: left + 'px'
 		}"
-		@mousedown="onResizeBox"
+		@mousedown.left="onResizeBox"
 	>
-		<div class="box-header item-draggable" @mousedown="onDragBox">
-			<slot name="header"></slot>
+		<div class="box-header item-draggable" @mousedown.left="onDragBox">
+			<button class="delete" @click.prevent="toggleBox()" />
 		</div>
 		<div class="box-body">
 			<slot name="body"></slot>
@@ -151,23 +151,31 @@ export default {
 	overflow: auto;
 	border: 1px solid var(--light-grey-2);
 	border-radius: 5px;
-	padding: 10px;
 	min-height: 50px !important;
 	min-width: 50px !important;
+	padding: 0;
 
 	.box-header {
 		z-index: 100000001;
 		background-color: var(--primary-color);
-		padding: 10px;
 		display: block;
-		height: 10px;
+		height: 24px;
 		width: 100%;
+
+		.delete {
+			position: absolute;
+			height: 20px;
+			width: 20px;
+			top: 2px;
+			right: 2px;
+		}
 	}
 
 	.box-body {
 		display: flex;
 		flex-wrap: wrap;
 		justify-content: space-evenly;
+		padding: 10px;
 
 		span {
 			padding: 3px 6px;

+ 2 - 2
frontend/src/components/PlaylistItem.vue

@@ -23,8 +23,8 @@
 						:user-id="playlist.createdBy"
 						:link="true"
 					/>
-					•</span
-				>
+					•
+				</span>
 				<span :title="playlistLength">
 					{{ playlistLength }}
 				</span>

+ 0 - 3
frontend/src/components/modals/EditSong/Tabs/Discogs.vue

@@ -401,9 +401,6 @@ export default {
 	}
 
 	.api-results-container {
-		max-height: 445px;
-		overflow: auto;
-
 		.api-result {
 			background-color: var(--white);
 			border: 0.5px solid var(--light-grey-3);

+ 2 - 2
frontend/src/components/modals/EditSong/Tabs/Youtube.vue

@@ -89,15 +89,15 @@ export default {
 
 <style lang="scss" scoped>
 .youtube-tab {
+	height: calc(100% - 32px);
+
 	#song-query-results {
 		height: calc(100% - 74px);
-		max-height: 740px;
 		overflow: auto;
 
 		.search-query-item {
 			/deep/ .thumbnail-and-info {
 				width: calc(100% - 29px);
-				max-width: 500px;
 			}
 
 			.icon-selected {

+ 30 - 16
frontend/src/components/modals/EditSong/index.vue

@@ -278,7 +278,7 @@
 									<span
 										class="autosuggest-item"
 										tabindex="0"
-										@click="selectArtistAutosuggest(item)"
+										@click="addTag('artists', item)"
 										v-for="item in artistAutosuggestItems"
 										:key="item"
 										>{{ item }}</span
@@ -359,7 +359,7 @@
 								>
 									<span
 										class="autosuggest-item"
-										@click="selectGenreAutosuggest(item)"
+										@click="addTag('genres', item)"
 										v-for="item in genreAutosuggestItems"
 										:key="item"
 										>{{ item }}</span
@@ -646,10 +646,10 @@ export default {
 	},
 	watch: {
 		/* eslint-disable */
-		"song.duration": function() {
+		"song.duration": function () {
 			this.drawCanvas();
 		},
-		"song.skipDuration": function() {
+		"song.skipDuration": function () {
 			this.drawCanvas();
 		}
 		/* eslint-enable */
@@ -1331,9 +1331,6 @@ export default {
 				// Do things here to query the artist
 			}, 1000);
 		},
-		selectArtistAutosuggest(value) {
-			this.artistInputValue = value;
-		},
 		blurGenreInput() {
 			this.genreInputFocussed = false;
 		},
@@ -1358,9 +1355,6 @@ export default {
 				} else this.genreAutosuggestItems = [];
 			}, 1000);
 		},
-		selectGenreAutosuggest(value) {
-			this.genreInputValue = value;
-		},
 		settings(type) {
 			switch (type) {
 				default:
@@ -1421,9 +1415,9 @@ export default {
 			this.video.player.setVolume(volume);
 			localStorage.setItem("volume", volume);
 		},
-		addTag(type) {
+		addTag(type, value) {
 			if (type === "genres") {
-				const genre = this.genreInputValue.trim();
+				const genre = value || this.genreInputValue.trim();
 
 				if (
 					this.song.genres
@@ -1434,18 +1428,20 @@ export default {
 				if (genre) {
 					this.song.genres.push(genre);
 					this.genreInputValue = "";
+					this.genreAutosuggestItems = [];
 					return false;
 				}
 
 				return new Toast("Genre cannot be empty");
 			}
 			if (type === "artists") {
-				const artist = this.artistInputValue;
+				const artist = value || this.artistInputValue;
 				if (this.song.artists.indexOf(artist) !== -1)
 					return new Toast("Artist already exists");
 				if (artist !== "") {
 					this.song.artists.push(artist);
 					this.artistInputValue = "";
+					this.artistAutosuggestItems = [];
 					return false;
 				}
 				return new Toast("Artist cannot be empty");
@@ -1619,6 +1615,21 @@ export default {
 		}
 	}
 
+	.autosuggest-container {
+		background-color: unset !important;
+	}
+
+	.autosuggest-item {
+		background-color: var(--dark-grey) !important;
+		color: white !important;
+		border-color: var(--dark-grey) !important;
+	}
+
+	.autosuggest-item:hover,
+	.autosuggest-item:focus {
+		background-color: var(--dark-grey-2) !important;
+	}
+
 	#tabs-container #tab-selection .button {
 		background: var(--dark-grey) !important;
 		color: var(--white) !important;
@@ -1629,17 +1640,22 @@ export default {
 	&::v-deep {
 		.modal-card {
 			width: 1300px;
+			height: 100%;
 
 			.modal-card-body {
 				display: flex;
-				flex-wrap: wrap;
 				column-gap: 16px;
 				row-gap: 16px;
 
+				@media screen and (max-width: 1000px) {
+					flex-wrap: wrap;
+				}
+
 				> div {
 					display: flex;
 					flex-grow: 1;
 					height: 100%;
+					overflow: auto;
 				}
 			}
 
@@ -1874,7 +1890,6 @@ export default {
 	.edit-section {
 		border: 1px solid var(--light-grey-3);
 		margin-top: 16px;
-		overflow: auto;
 		border-radius: 3px;
 
 		.album-get-button {
@@ -2084,7 +2099,6 @@ export default {
 
 	#tabs-container {
 		overflow: auto;
-		height: 100%;
 		display: flex;
 		flex-direction: column;
 		flex-grow: 1;

+ 3 - 2
frontend/src/components/modals/ManageStation/index.vue

@@ -545,8 +545,8 @@ export default {
 			() => {}
 		);
 
+		if (this.isOwnerOrAdmin()) this.showTab("settings");
 		this.clearStation();
-		this.showTab("settings");
 	},
 	methods: {
 		isOwner() {
@@ -643,7 +643,8 @@ export default {
 		]),
 		...mapActions({
 			showTab(dispatch, payload) {
-				this.$refs[`${payload}-tab`].scrollIntoView();
+				if (this.$refs[`${payload}-tab`])
+					this.$refs[`${payload}-tab`].scrollIntoView(); // Only works if the ref exists, which it doesn't always
 				return dispatch("modals/manageStation/showTab", payload);
 			}
 		}),

+ 7 - 0
frontend/src/keyboardShortcuts.js

@@ -32,6 +32,13 @@ const lib = {
 		shortcuts[name].shift = shortcuts[name].shift
 			? shortcuts[name].shift
 			: false;
+		lofig.get("shortcutOverrides").then(overrides => {
+			if (overrides && overrides[name])
+				shortcuts[name] = Object.assign(
+					shortcuts[name],
+					overrides[name]
+				);
+		});
 		lib.remakeShortcutsArray();
 	},
 

+ 1 - 1
frontend/src/main.js

@@ -9,7 +9,7 @@ import store from "./store";
 
 import AppComponent from "./App.vue";
 
-const REQUIRED_CONFIG_VERSION = 6;
+const REQUIRED_CONFIG_VERSION = 7;
 
 const handleMetadata = attrs => {
 	document.title = `Musare | ${attrs.title}`;

+ 93 - 8
frontend/src/pages/Station/index.vue

@@ -768,6 +768,46 @@
 				>
 			</template>
 		</floating-box>
+		<floating-box
+			id="keyboardShortcutsHelper"
+			ref="keyboardShortcutsHelper"
+		>
+			<template #body>
+				<div>
+					<div v-if="isOwnerOrAdmin()">
+						<span class="biggest"><b>Admin/owner</b></span>
+						<span><b>Ctrl + Space</b> - Pause/resume station</span>
+						<span><b>Ctrl + Numpad right</b> - Skip station</span>
+					</div>
+					<hr v-if="isOwnerOrAdmin()" />
+					<div>
+						<span class="biggest"><b>Volume</b></span>
+						<span
+							><b>Ctrl + Numpad up/down</b> - Volume up/down
+							10%</span
+						>
+						<span
+							><b>Ctrl + Shift + Numpad up/down</b> - Volume
+							up/down 10%</span
+						>
+					</div>
+					<hr />
+					<div>
+						<span class="biggest"><b>Misc</b></span>
+						<span><b>Ctrl + D</b> - Toggles debug box</span>
+						<span><b>Ctrl + Shift + D</b> - Resets debug box</span>
+						<span
+							><b>Ctrl + /</b> - Toggles keyboard shortcuts
+							box</span
+						>
+						<span
+							><b>Ctrl + Shift + /</b> - Resets keyboard shortcuts
+							box</span
+						>
+					</div>
+				</div>
+			</template>
+		</floating-box>
 
 		<Z404 v-if="!exists"></Z404>
 	</div>
@@ -886,8 +926,12 @@ export default {
 				typeof this.currentSong.disliked === "boolean"
 			);
 		},
+		aModalIsOpen() {
+			return Object.keys(this.currentlyActive).length > 0;
+		},
 		...mapState("modalVisibility", {
-			modals: state => state.modals
+			modals: state => state.modals,
+			currentlyActive: state => state.currentlyActive
 		}),
 		...mapState("modals/editSong", {
 			video: state => state.video
@@ -1597,6 +1641,7 @@ export default {
 		calculateTimeElapsed() {
 			if (
 				this.playerReady &&
+				!this.noSong &&
 				this.currentSong &&
 				this.player.getPlayerState() === -1
 			) {
@@ -1907,6 +1952,12 @@ export default {
 		resetPlayerDebugBox() {
 			this.$refs.playerDebugBox.resetBox();
 		},
+		toggleKeyboardShortcutsHelper() {
+			this.$refs.keyboardShortcutsHelper.toggleBox();
+		},
+		resetKeyboardShortcutsHelper() {
+			this.$refs.keyboardShortcutsHelper.resetBox();
+		},
 		join() {
 			this.socket.dispatch(
 				"stations.join",
@@ -2012,11 +2063,12 @@ export default {
 							keyboardShortcuts.registerShortcut(
 								"station.pauseResume",
 								{
-									keyCode: 32,
+									keyCode: 32, // Spacebar
 									shift: false,
 									ctrl: true,
 									preventDefault: true,
 									handler: () => {
+										if (this.aModalIsOpen) return;
 										if (this.stationPaused)
 											this.resumeStation();
 										else this.pauseStation();
@@ -2027,11 +2079,12 @@ export default {
 							keyboardShortcuts.registerShortcut(
 								"station.skipStation",
 								{
-									keyCode: 39,
+									keyCode: 39, // Right arrow key
 									shift: false,
 									ctrl: true,
 									preventDefault: true,
 									handler: () => {
+										if (this.aModalIsOpen) return;
 										this.skipStation();
 									}
 								}
@@ -2041,11 +2094,12 @@ export default {
 						keyboardShortcuts.registerShortcut(
 							"station.lowerVolumeLarge",
 							{
-								keyCode: 40,
+								keyCode: 40, // Down arrow key
 								shift: false,
 								ctrl: true,
 								preventDefault: true,
 								handler: () => {
+									if (this.aModalIsOpen) return;
 									this.volumeSliderValue -= 1000;
 									this.changeVolume();
 								}
@@ -2055,11 +2109,12 @@ export default {
 						keyboardShortcuts.registerShortcut(
 							"station.lowerVolumeSmall",
 							{
-								keyCode: 40,
+								keyCode: 40, // Down arrow key
 								shift: true,
 								ctrl: true,
 								preventDefault: true,
 								handler: () => {
+									if (this.aModalIsOpen) return;
 									this.volumeSliderValue -= 100;
 									this.changeVolume();
 								}
@@ -2069,11 +2124,12 @@ export default {
 						keyboardShortcuts.registerShortcut(
 							"station.increaseVolumeLarge",
 							{
-								keyCode: 38,
+								keyCode: 38, // Up arrow key
 								shift: false,
 								ctrl: true,
 								preventDefault: true,
 								handler: () => {
+									if (this.aModalIsOpen) return;
 									this.volumeSliderValue += 1000;
 									this.changeVolume();
 								}
@@ -2083,11 +2139,12 @@ export default {
 						keyboardShortcuts.registerShortcut(
 							"station.increaseVolumeSmall",
 							{
-								keyCode: 38,
+								keyCode: 38, // Up arrow key
 								shift: true,
 								ctrl: true,
 								preventDefault: true,
 								handler: () => {
+									if (this.aModalIsOpen) return;
 									this.volumeSliderValue += 100;
 									this.changeVolume();
 								}
@@ -2097,16 +2154,44 @@ export default {
 						keyboardShortcuts.registerShortcut(
 							"station.toggleDebug",
 							{
-								keyCode: 68,
+								keyCode: 68, // D key
 								shift: false,
 								ctrl: true,
 								preventDefault: true,
 								handler: () => {
+									if (this.aModalIsOpen) return;
 									this.togglePlayerDebugBox();
 								}
 							}
 						);
 
+						keyboardShortcuts.registerShortcut(
+							"station.toggleKeyboardShortcutsHelper",
+							{
+								keyCode: 191, // '/' key
+								ctrl: true,
+								preventDefault: true,
+								handler: () => {
+									if (this.aModalIsOpen) return;
+									this.toggleKeyboardShortcutsHelper();
+								}
+							}
+						);
+
+						keyboardShortcuts.registerShortcut(
+							"station.resetKeyboardShortcutsHelper",
+							{
+								keyCode: 191, // '/' key
+								ctrl: true,
+								shift: true,
+								preventDefault: true,
+								handler: () => {
+									if (this.aModalIsOpen) return;
+									this.resetKeyboardShortcutsHelper();
+								}
+							}
+						);
+
 						// UNIX client time before ping
 						const beforePing = Date.now();
 						this.socket.dispatch("apis.ping", res => {