浏览代码

Merge remote-tracking branch 'origin/master' into owen

Owen Diffey 3 年之前
父节点
当前提交
58bf081ffe
共有 46 个文件被更改,包括 217 次插入266 次删除
  1. 5 5
      backend/logic/actions/playlists.js
  2. 23 4
      backend/logic/api.js
  3. 4 3
      backend/package-lock.json
  4. 3 3
      backend/package.json
  5. 1 1
      frontend/package-lock.json
  6. 4 4
      frontend/package.json
  7. 9 0
      frontend/src/App.vue
  8. 1 1
      frontend/src/components/ActivityItem.vue
  9. 14 2
      frontend/src/components/AddToPlaylistDropdown.vue
  10. 12 2
      frontend/src/components/ReportInfoItem.vue
  11. 2 2
      frontend/src/components/SearchQueryItem.vue
  12. 2 6
      frontend/src/components/SongItem.vue
  13. 10 18
      frontend/src/components/layout/MainFooter.vue
  14. 5 15
      frontend/src/components/layout/MainHeader.vue
  15. 6 1
      frontend/src/components/modals/EditNews.vue
  16. 6 13
      frontend/src/components/modals/EditPlaylist/Tabs/AddSongs.vue
  17. 3 4
      frontend/src/components/modals/EditPlaylist/Tabs/ImportPlaylists.vue
  18. 6 6
      frontend/src/components/modals/EditPlaylist/Tabs/Settings.vue
  19. 8 13
      frontend/src/components/modals/EditPlaylist/index.vue
  20. 3 7
      frontend/src/components/modals/EditSong/Tabs/Discogs.vue
  21. 0 4
      frontend/src/components/modals/EditSong/Tabs/Songs.vue
  22. 5 11
      frontend/src/components/modals/EditSong/Tabs/Youtube.vue
  23. 1 1
      frontend/src/components/modals/EditUser.vue
  24. 4 5
      frontend/src/components/modals/Login.vue
  25. 0 3
      frontend/src/components/modals/ManageStation/index.vue
  26. 4 4
      frontend/src/components/modals/Register.vue
  27. 1 1
      frontend/src/components/modals/RemoveAccount.vue
  28. 3 7
      frontend/src/components/modals/Report.vue
  29. 11 13
      frontend/src/components/modals/RequestSong.vue
  30. 3 2
      frontend/src/components/modals/ViewReport.vue
  31. 5 13
      frontend/src/components/modals/WhatIsNew.vue
  32. 18 13
      frontend/src/pages/Admin/tabs/News.vue
  33. 0 4
      frontend/src/pages/Admin/tabs/Playlists.vue
  34. 1 7
      frontend/src/pages/Admin/tabs/Punishments.vue
  35. 5 7
      frontend/src/pages/Admin/tabs/Reports.vue
  36. 7 10
      frontend/src/pages/Admin/tabs/Stations.vue
  37. 0 4
      frontend/src/pages/Admin/tabs/Statistics.vue
  38. 0 4
      frontend/src/pages/Admin/tabs/Users.vue
  39. 0 4
      frontend/src/pages/Admin/tabs/VerifiedSongs.vue
  40. 0 1
      frontend/src/pages/Home.vue
  41. 1 3
      frontend/src/pages/News.vue
  42. 16 26
      frontend/src/pages/ResetPassword.vue
  43. 1 1
      frontend/src/pages/Settings/Tabs/Security.vue
  44. 2 3
      frontend/src/pages/Station/Sidebar/Playlists.vue
  45. 1 4
      frontend/src/pages/Station/index.vue
  46. 1 1
      frontend/src/pages/Team.vue

+ 5 - 5
backend/logic/actions/playlists.js

@@ -1460,17 +1460,17 @@ export default {
 					userModel.updateOne(
 						{ _id: playlist.createdBy },
 						{ $pull: { "preferences.orderOfPlaylists": playlist._id } },
-						err => next(err, playlist)
+						err => next(err, playlist, playlist.createdBy)
 					);
 				},
 
-				(playlist, next) => {
+				(playlist, playlistCreator, next) => {
 					PlaylistsModule.runJob("DELETE_PLAYLIST", { playlistId }, this)
-						.then(() => next(null, playlist))
+						.then(() => next(null, playlistCreator))
 						.catch(next);
 				}
 			],
-			async err => {
+			async (err, playlistCreator) => {
 				if (err) {
 					err = await UtilsModule.runJob("GET_ERROR", { error: err }, this);
 					this.log(
@@ -1490,7 +1490,7 @@ export default {
 				CacheModule.runJob("PUB", {
 					channel: "playlist.delete",
 					value: {
-						userId: session.userId,
+						userId: playlistCreator,
 						playlistId
 					}
 				});

+ 23 - 4
backend/logic/api.js

@@ -124,13 +124,32 @@ class _APIModule extends CoreClass {
 						});
 					});
 
-					response.app.get("/export/privatePlaylist/:playlistId", isLoggedIn, (req, res) => {
+					response.app.get("/export/playlist/:playlistId", async (req, res) => {
 						const { playlistId } = req.params;
+
+						const userModel = await DBModule.runJob("GET_MODEL", { modelName: "user" });
+
 						PlaylistsModule.runJob("GET_PLAYLIST", { playlistId })
 							.then(playlist => {
-								if (playlist.createdBy === req.session.userId)
-									res.json({ status: "success", playlist });
-								else res.json({ status: "error", message: "You're not the owner." });
+								if (playlist.privacy === "public") res.json({ status: "success", playlist });
+								else {
+									isLoggedIn(req, res, () => {
+										if (playlist.createdBy === req.session.userId)
+											res.json({ status: "success", playlist });
+										else {
+											userModel.findOne({ _id: req.session.userId }, (err, user) => {
+												if (err) res.json({ status: "error", message: err.message });
+												else if (user.role === "admin")
+													res.json({ status: "success", playlist });
+												else
+													res.json({
+														status: "error",
+														message: "You're not allowed to download this playlist."
+													});
+											});
+										}
+									});
+								}
 							})
 							.catch(err => {
 								res.json({ status: "error", message: err.message });

+ 4 - 3
backend/package-lock.json

@@ -1,12 +1,12 @@
 {
   "name": "musare-backend",
-  "version": "2.1.0",
+  "version": "3.0.0",
   "lockfileVersion": 2,
   "requires": true,
   "packages": {
     "": {
       "name": "musare-backend",
-      "version": "2.1.0",
+      "version": "3.0.0",
       "license": "GPL-3.0",
       "dependencies": {
         "async": "^3.2.1",
@@ -2443,7 +2443,8 @@
       "dependencies": {
         "bson": "^4.5.2",
         "denque": "^2.0.1",
-        "mongodb-connection-string-url": "^2.0.0"
+        "mongodb-connection-string-url": "^2.0.0",
+        "saslprep": "^1.0.3"
       },
       "engines": {
         "node": ">=12.9.0"

+ 3 - 3
backend/package.json

@@ -1,13 +1,13 @@
 {
   "name": "musare-backend",
   "private": true,
-  "version": "2.1.0",
+  "version": "3.0.0",
   "type": "module",
-  "description": "A modern, open-source, collaborative music app https://musare.com",
+  "description": "An open-source collaborative music listening and catalogue curation application. Currently supporting YouTube based content.",
   "main": "index.js",
   "author": "Musare Team",
   "license": "GPL-3.0",
-  "repository": "https://github.com/Musare/MusareNode",
+  "repository": "https://github.com/Musare/Musare",
   "scripts": {
     "dev": "nodemon --es-module-specifier-resolution=node",
     "docker:dev": "nodemon --es-module-specifier-resolution=node -L /opt/app",

+ 1 - 1
frontend/package-lock.json

@@ -1,6 +1,6 @@
 {
   "name": "musare-frontend",
-  "version": "2.1.0",
+  "version": "3.0.0",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {

+ 4 - 4
frontend/package.json

@@ -5,12 +5,12 @@
     "*.vue"
   ],
   "private": true,
-  "version": "2.1.0",
-  "description": "A modern, open-source, collaborative music app https://musare.com",
+  "version": "3.0.0",
+  "description": "An open-source collaborative music listening and catalogue curation application. Currently supporting YouTube based content.",
   "main": "main.js",
   "author": "Musare Team",
   "license": "GPL-3.0",
-  "repository": "https://github.com/Musare/MusareNode",
+  "repository": "https://github.com/Musare/Musare",
   "scripts": {
     "lint": "npx eslint src --ext .js,.vue",
     "dev": "npx webpack serve --config webpack.dev.js",
@@ -60,4 +60,4 @@
     "webpack-bundle-analyzer": "^4.4.2",
     "webpack-merge": "^5.8.0"
   }
-}
+}

+ 9 - 0
frontend/src/App.vue

@@ -338,6 +338,15 @@ export default {
 	.tippy-box[data-theme~="songActions"] {
 		background-color: var(--dark-grey);
 	}
+
+	code {
+		background-color: var(--dark-grey-2) !important;
+	}
+}
+
+code {
+	background-color: var(--light-grey) !important;
+	color: var(--red) !important;
 }
 
 body.night-mode {

+ 1 - 1
frontend/src/components/ActivityItem.vue

@@ -259,7 +259,7 @@ export default {
 	.left-part {
 		flex: 1;
 		padding: 12px;
-		width: calc(100% - 150px);
+		min-width: 0;
 
 		.item-title {
 			margin: 0;

+ 14 - 2
frontend/src/components/AddToPlaylistDropdown.vue

@@ -25,7 +25,6 @@
 			<div class="nav-dropdown-items" v-if="playlists.length > 0">
 				<button
 					class="nav-item"
-					href="#"
 					v-for="(playlist, index) in playlists"
 					:key="playlist._id"
 					@click.prevent="toggleSongInPlaylist(index)"
@@ -48,7 +47,9 @@
 					</p>
 				</button>
 			</div>
-			<p v-else>You haven't created any playlists.</p>
+			<p v-else class="no-playlists">
+				You haven't created any playlists.
+			</p>
 
 			<button
 				id="create-playlist"
@@ -177,3 +178,14 @@ export default {
 	}
 };
 </script>
+
+<style lang="scss" scoped>
+.no-playlists {
+	text-align: center;
+	margin-top: 10px;
+}
+
+#create-playlist .material-icons {
+	color: var(--white);
+}
+</style>

+ 12 - 2
frontend/src/components/ReportInfoItem.vue

@@ -10,7 +10,12 @@
 		</div>
 
 		<div class="item-title-description">
-			<h2 class="item-title">
+			<h2
+				class="item-title"
+				:title="`Reported by ${
+					createdBy.username ? createdBy.username : 'Deleted User'
+				}`"
+			>
 				Reported by
 				<router-link
 					v-if="createdBy.username"
@@ -61,7 +66,7 @@ export default {
 <style lang="scss" scoped>
 .night-mode {
 	.report-info-item {
-		background-color: var(--dark-grey-2) !important;
+		background-color: var(--dark-grey) !important;
 		border: 0 !important;
 	}
 }
@@ -93,6 +98,10 @@ export default {
 		}
 	}
 
+	.item-title-description {
+		min-width: 0;
+	}
+
 	.item-title {
 		font-size: 14px;
 		margin: 0;
@@ -100,6 +109,7 @@ export default {
 
 	.item-description {
 		font-size: 12px;
+		line-height: 14px;
 		text-transform: capitalize;
 		margin: 0;
 	}

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

@@ -108,7 +108,7 @@ export default {
 	}
 
 	.thumbnail-and-info {
-		width: calc(100% - 160px);
+		min-width: 0;
 	}
 
 	.song-info {
@@ -116,7 +116,7 @@ export default {
 		flex-direction: column;
 		justify-content: center;
 		margin-left: 20px;
-		width: calc(100% - 65px);
+		min-width: 0;
 
 		.item-title {
 			font-size: 20px;

+ 2 - 6
frontend/src/components/SongItem.vue

@@ -318,7 +318,7 @@ export default {
 	}
 
 	.thumbnail-and-info {
-		width: calc(100% - 90px);
+		min-width: 0;
 	}
 
 	.thumbnail {
@@ -333,7 +333,7 @@ export default {
 		flex-direction: column;
 		justify-content: center;
 		margin-left: 20px;
-		width: calc(100% - 10px);
+		min-width: 0;
 
 		*:not(i) {
 			margin: 0;
@@ -369,9 +369,5 @@ export default {
 	.edit-icon {
 		color: var(--primary-color);
 	}
-
-	&.with-duration .song-info {
-		width: calc(100% - 80px);
-	}
 }
 </style>

+ 10 - 18
frontend/src/components/layout/MainFooter.vue

@@ -53,29 +53,30 @@
 import Toast from "toasters";
 import { mapState, mapGetters, mapActions } from "vuex";
 
-import ws from "@/ws";
-
 export default {
 	data() {
 		return {
 			siteSettings: {
 				logo: "",
 				sitename: "Musare",
-				github: "#"
+				github: ""
 			},
 			localNightmode: null
 		};
 	},
 	computed: {
 		...mapState({
-			loggedIn: state => state.user.auth.loggedIn
+			loggedIn: state => state.user.auth.loggedIn,
+			nightmode: state => state.user.preferences.nightmode
 		}),
 		...mapGetters({
 			socket: "websockets/getSocket"
 		})
 	},
 	watch: {
-		localNightmode() {
+		localNightmode(newValue, oldValue) {
+			if (oldValue === null) return;
+
 			localStorage.setItem("nightmode", this.localNightmode);
 
 			if (this.loggedIn) {
@@ -89,28 +90,19 @@ export default {
 			}
 
 			this.changeNightmode(this.localNightmode);
+		},
+		nightmode(nightmode) {
+			if (this.localNightmode !== nightmode)
+				this.localNightmode = nightmode;
 		}
 	},
 	async mounted() {
 		this.localNightmode = JSON.parse(localStorage.getItem("nightmode"));
 
-		ws.onConnect(this.init);
-
-		this.socket.on("keep.event:user.preferences.updated", res => {
-			if (res.data.preferences.nightmode !== undefined)
-				this.localNightmode = res.data.preferences.nightmode;
-		});
-
 		this.frontendDomain = await lofig.get("frontendDomain");
 		this.siteSettings = await lofig.get("siteSettings");
 	},
 	methods: {
-		init() {
-			this.socket.dispatch("users.getPreferences", res => {
-				if (res.status === "success")
-					this.localNightmode = res.data.preferences.nightmode;
-			});
-		},
 		...mapActions("user/preferences", ["changeNightmode"])
 	}
 };

+ 5 - 15
frontend/src/components/layout/MainHeader.vue

@@ -45,15 +45,11 @@
 				<router-link class="nav-item" to="/settings"
 					>Settings</router-link
 				>
-				<a class="nav-item" href="#" @click="logout()">Logout</a>
+				<a class="nav-item" @click="logout()">Logout</a>
 			</span>
 			<span v-if="!loggedIn && !hideLoggedOut" class="grouped">
-				<a class="nav-item" href="#" @click="openModal('login')"
-					>Login</a
-				>
-				<a class="nav-item" href="#" @click="openModal('register')"
-					>Register</a
-				>
+				<a class="nav-item" @click="openModal('login')">Login</a>
+				<a class="nav-item" @click="openModal('register')">Register</a>
 			</span>
 		</div>
 	</nav>
@@ -83,7 +79,8 @@ export default {
 			modals: state => state.modalVisibility.modals.header,
 			role: state => state.user.auth.role,
 			loggedIn: state => state.user.auth.loggedIn,
-			username: state => state.user.auth.username
+			username: state => state.user.auth.username,
+			nightmode: state => state.user.preferences.nightmode
 		}),
 		...mapGetters({
 			socket: "websockets/getSocket"
@@ -93,14 +90,7 @@ export default {
 		this.frontendDomain = await lofig.get("frontendDomain");
 		this.siteSettings = await lofig.get("siteSettings");
 	},
-
 	methods: {
-		init() {
-			this.socket.dispatch("users.getPreferences", res => {
-				if (res.status === "success")
-					this.localNightmode = res.data.preferences.nightmode;
-			});
-		},
 		...mapActions("modalVisibility", ["openModal"]),
 		...mapActions("user/auth", ["logout"])
 	}

+ 6 - 1
frontend/src/components/modals/EditNews.vue

@@ -229,7 +229,12 @@ export default {
 .night-mode {
 	#markdown-editor-and-preview textarea,
 	#markdown-editor-and-preview #preview {
-		border-color: #fff;
+		border-color: var(--grey-3);
+	}
+
+	#markdown-editor-and-preview textarea {
+		background-color: var(--dark-grey);
+		color: var(--white);
 	}
 }
 

+ 6 - 13
frontend/src/components/modals/EditPlaylist/Tabs/AddSongs.vue

@@ -83,13 +83,13 @@
 					/>
 				</p>
 				<p class="control">
-					<a
+					<button
 						class="button is-info"
 						@click.prevent="searchForSongs()"
-						href="#"
-						><i class="material-icons icon-with-button">search</i
-						>Search</a
 					>
+						<i class="material-icons icon-with-button">search</i
+						>Search
+					</button>
 				</p>
 			</div>
 
@@ -128,13 +128,12 @@
 					</template>
 				</search-query-item>
 
-				<a
+				<button
 					class="button is-primary load-more-button"
 					@click.prevent="loadMoreSongs()"
-					href="#"
 				>
 					Load more...
-				</a>
+				</button>
 			</div>
 		</div>
 	</div>
@@ -212,12 +211,6 @@ export default {
 };
 </script>
 
-<style lang="scss">
-.youtube-tab .song-query-results .song-item .thumbnail-and-info {
-	width: calc(100% - 110px);
-}
-</style>
-
 <style lang="scss" scoped>
 .youtube-tab {
 	.song-query-results {

+ 3 - 4
frontend/src/components/modals/EditPlaylist/Tabs/ImportPlaylists.vue

@@ -20,13 +20,12 @@
 						<option :value="true">Import only music</option>
 					</select>
 				</span>
-				<a
+				<button
 					class="button is-info"
 					@click.prevent="importPlaylist()"
-					href="#"
-					><i class="material-icons icon-with-button">publish</i
-					>Import</a
 				>
+					<i class="material-icons icon-with-button">publish</i>Import
+				</button>
 			</p>
 		</div>
 	</div>

+ 6 - 6
frontend/src/components/modals/EditPlaylist/Tabs/Settings.vue

@@ -13,12 +13,12 @@
 					/>
 				</p>
 				<p class="control">
-					<a
+					<button
 						class="button is-info"
 						@click.prevent="renamePlaylist()"
-						href="#"
-						>Rename</a
 					>
+						Rename
+					</button>
 				</p>
 			</div>
 		</div>
@@ -39,12 +39,12 @@
 					</select>
 				</div>
 				<p class="control">
-					<a
+					<button
 						class="button is-info"
 						@click.prevent="updatePrivacy()"
-						href="#"
-						>Update Privacy</a
 					>
+						Update Privacy
+					</button>
 				</p>
 			</div>
 		</div>

+ 8 - 13
frontend/src/components/modals/EditPlaylist/index.vue

@@ -214,18 +214,13 @@
 			</div>
 		</template>
 		<template #footer>
-			<a
+			<button
 				class="button is-default"
-				v-if="
-					userId === playlist.createdBy ||
-					isEditable() ||
-					playlist.privacy === 'public'
-				"
+				v-if="isOwner() || isAdmin() || playlist.privacy === 'public'"
 				@click="downloadPlaylist()"
-				href="#"
 			>
 				Download Playlist
-			</a>
+			</button>
 			<div class="right">
 				<confirm
 					v-if="playlist.type === 'station'"
@@ -316,6 +311,7 @@ export default {
 			}
 		},
 		...mapState({
+			loggedIn: state => state.user.auth.loggedIn,
 			userId: state => state.user.auth.userId,
 			userRole: state => state.user.auth.role
 		}),
@@ -521,10 +517,9 @@ export default {
 			if (this.apiDomain === "")
 				this.apiDomain = await lofig.get("backend.apiDomain");
 
-			fetch(
-				`${this.apiDomain}/export/privatePlaylist/${this.playlist._id}`,
-				{ credentials: "include" }
-			)
+			fetch(`${this.apiDomain}/export/playlist/${this.playlist._id}`, {
+				credentials: "include"
+			})
 				.then(res => res.blob())
 				.then(blob => {
 					const url = window.URL.createObjectURL(blob);
@@ -533,7 +528,7 @@ export default {
 					a.style.display = "none";
 					a.href = url;
 
-					a.download = `musare-privateplaylist-${
+					a.download = `musare-playlist-${
 						this.playlist._id
 					}-${new Date().toISOString()}.json`;
 

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

@@ -57,13 +57,9 @@
 				/>
 			</p>
 			<p class="control">
-				<a
-					class="button is-info"
-					@click="searchDiscogsForPage(1)"
-					href="#"
-					><i class="material-icons icon-with-button">search</i
-					>Search</a
-				>
+				<button class="button is-info" @click="searchDiscogsForPage(1)">
+					<i class="material-icons icon-with-button">search</i>Search
+				</button>
 			</p>
 		</div>
 

+ 0 - 4
frontend/src/components/modals/EditSong/Tabs/Songs.vue

@@ -75,10 +75,6 @@ export default {
 		overflow: auto;
 
 		.search-query-item {
-			/deep/ .thumbnail-and-info {
-				width: calc(100% - 57px);
-			}
-
 			.icon-selected {
 				color: var(--green) !important;
 			}

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

@@ -13,13 +13,12 @@
 				/>
 			</p>
 			<p class="control">
-				<a
+				<button
 					class="button is-info"
 					@click.prevent="searchForSongs()"
-					href="#"
-					><i class="material-icons icon-with-button">search</i
-					>Search</a
 				>
+					<i class="material-icons icon-with-button">search</i>Search
+				</button>
 			</p>
 		</div>
 
@@ -49,13 +48,12 @@
 				</template>
 			</search-query-item>
 
-			<a
+			<button
 				class="button is-primary load-more-button"
 				@click.prevent="loadMoreSongs()"
-				href="#"
 			>
 				Load more...
-			</a>
+			</button>
 		</div>
 	</div>
 </template>
@@ -96,10 +94,6 @@ export default {
 		overflow: auto;
 
 		.search-query-item {
-			/deep/ .thumbnail-and-info {
-				width: calc(100% - 59px);
-			}
-
 			.icon-selected {
 				color: var(--green) !important;
 			}

+ 1 - 1
frontend/src/components/modals/EditUser.vue

@@ -250,7 +250,7 @@ export default {
 
 <style lang="scss" scoped>
 .night-mode .section {
-	background-color: unset !important;
+	background-color: transparent !important;
 }
 
 .section {

+ 4 - 5
frontend/src/components/modals/Login.vue

@@ -56,7 +56,6 @@
 						<p class="content-box-optional-helper">
 							<router-link
 								id="forgot-password"
-								href="#"
 								to="/reset_password"
 								@click="closeLoginModal()"
 							>
@@ -83,12 +82,12 @@
 
 				<footer class="modal-card-foot">
 					<div id="actions">
-						<a
+						<button
 							class="button is-primary"
-							href="#"
 							@click="submitModal()"
-							>Login</a
 						>
+							Login
+						</button>
 						<a
 							class="button is-github"
 							:href="apiDomain + '/auth/github/authorize'"
@@ -108,7 +107,7 @@
 						<router-link to="/register" v-if="isPage">
 							Don't have an account?
 						</router-link>
-						<a v-else href="#" @click="changeToRegisterModal()">
+						<a v-else @click="changeToRegisterModal()">
 							Don't have an account?
 						</a>
 					</p>

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

@@ -600,9 +600,6 @@ export default {
 			margin-bottom: 10px;
 		}
 		.currently-playing.song-item {
-			.song-info {
-				width: calc(100% - 150px);
-			}
 			.thumbnail {
 				min-width: 130px;
 				width: 130px;

+ 4 - 4
frontend/src/components/modals/Register.vue

@@ -115,12 +115,12 @@
 				</section>
 				<footer class="modal-card-foot">
 					<div id="actions">
-						<a
+						<button
 							class="button is-primary"
-							href="#"
 							@click="submitModal()"
-							>Register</a
 						>
+							Register
+						</button>
 						<a
 							class="button is-github"
 							:href="apiDomain + '/auth/github/authorize'"
@@ -140,7 +140,7 @@
 						<router-link to="/login" v-if="isPage">
 							Already have an account?
 						</router-link>
-						<a v-else href="#" @click="changeToLoginModal()">
+						<a v-else @click="changeToLoginModal()">
 							Already have an account?
 						</a>
 					</p>

+ 1 - 1
frontend/src/components/modals/RemoveAccount.vue

@@ -148,7 +148,7 @@
 			</div>
 
 			<div v-if="step === 'export-data'">
-				DOWNLOAD A BACKUP OF YOUR DATa BEFORE ITS PERMENATNELY DELETED
+				DOWNLOAD A BACKUP OF YOUR DATA BEFORE ITS PERMENATNELY DELETED
 			</div>
 
 			<div

+ 3 - 7
frontend/src/components/modals/Report.vue

@@ -187,15 +187,11 @@
 				</div>
 			</template>
 			<template #footer>
-				<a class="button is-success" @click="create()" href="#">
+				<button class="button is-success" @click="create()">
 					<i class="material-icons save-changes">done</i>
 					<span>&nbsp;Create</span>
-				</a>
-				<a
-					class="button is-danger"
-					href="#"
-					@click="closeModal('report')"
-				>
+				</button>
+				<a class="button is-danger" @click="closeModal('report')">
 					<span>&nbsp;Cancel</span>
 				</a>
 				<div class="right">

+ 11 - 13
frontend/src/components/modals/RequestSong.vue

@@ -23,14 +23,13 @@
 						/>
 					</p>
 					<p class="control">
-						<a
+						<button
 							class="button is-info"
 							@click.prevent="searchForSongs()"
-							href="#"
-							><i class="material-icons icon-with-button"
-								>search</i
-							>Search</a
 						>
+							<i class="material-icons icon-with-button">search</i
+							>Search
+						</button>
 					</p>
 				</div>
 
@@ -73,13 +72,12 @@
 						</template>
 					</search-query-item>
 
-					<a
+					<button
 						class="button is-default load-more-button"
 						@click.prevent="loadMoreSongs()"
-						href="#"
 					>
 						Load more...
-					</a>
+					</button>
 				</div>
 
 				<!-- Import a playlist from youtube -->
@@ -118,14 +116,14 @@
 									</option>
 								</select>
 							</span>
-							<a
+							<button
 								class="button is-info"
 								@click.prevent="importPlaylist()"
-								href="#"
-								><i class="material-icons icon-with-button"
-									>publish</i
-								>Import</a
 							>
+								<i class="material-icons icon-with-button"
+									>publish</i
+								>Import
+							</button>
 						</p>
 					</div>
 				</div>

+ 3 - 2
frontend/src/components/modals/ViewReport.vue

@@ -90,7 +90,7 @@
 				</i>
 				Edit Song
 			</a>
-			<a class="button is-success" href="#" @click="resolve()">
+			<button class="button is-success" @click="resolve()">
 				<i
 					class="material-icons icon-with-button"
 					content="Resolve"
@@ -99,7 +99,7 @@
 					done_all
 				</i>
 				Resolve
-			</a>
+			</button>
 		</template>
 	</modal>
 </template>
@@ -267,6 +267,7 @@ export default {
 
 			.item-description {
 				font-size: 14px;
+				line-height: 15px;
 				font-family: Karla, Arial, sans-serif;
 			}
 		}

+ 5 - 13
frontend/src/components/modals/WhatIsNew.vue

@@ -14,7 +14,7 @@
 						:user-id="news.createdBy"
 						:alt="news.createdBy"
 						:link="true" /></span
-				><span :title="new Date(news.createdAt)">
+				>&nbsp;<span :title="new Date(news.createdAt)">
 					{{
 						formatDistance(news.createdAt, new Date(), {
 							addSuffix: true
@@ -101,18 +101,6 @@ export default {
 };
 </script>
 
-<style lang="scss">
-.what-is-news-modal {
-	.modal-card {
-		.modal-card-foot {
-			span:not(:last-child) {
-				margin-right: 5px !important;
-			}
-		}
-	}
-}
-</style>
-
 <style lang="scss" scoped>
 .night-mode {
 	.modal-card,
@@ -125,6 +113,10 @@ export default {
 	p {
 		color: var(--light-grey-2);
 	}
+
+	.section {
+		background-color: transparent !important;
+	}
 }
 
 .modal-card-head {

+ 18 - 13
frontend/src/pages/Admin/tabs/News.vue

@@ -32,15 +32,19 @@
 						</td>
 						<td class="news-item-markdown">{{ news.markdown }}</td>
 						<td id="options-column">
-							<button
-								class="button is-primary"
-								@click="edit(news._id)"
-							>
-								Edit
-							</button>
-							<confirm @confirm="remove(news._id)">
-								<button class="button is-danger">Remove</button>
-							</confirm>
+							<div>
+								<button
+									class="button is-primary"
+									@click="edit(news._id)"
+								>
+									Edit
+								</button>
+								<confirm @confirm="remove(news._id)">
+									<button class="button is-danger">
+										Remove
+									</button>
+								</confirm>
+							</div>
 						</td>
 					</tr>
 				</tbody>
@@ -208,10 +212,11 @@ td {
 }
 
 #options-column {
-	display: flex;
-
-	button {
-		margin-right: 5px;
+	> div {
+		display: flex;
+		button {
+			margin-right: 5px;
+		}
 	}
 }
 </style>

+ 0 - 4
frontend/src/pages/Admin/tabs/Playlists.vue

@@ -336,10 +336,6 @@ export default {
 	}
 }
 
-body {
-	font-family: "Hind", sans-serif;
-}
-
 td {
 	vertical-align: middle;
 }

+ 1 - 7
frontend/src/pages/Admin/tabs/Punishments.vue

@@ -95,9 +95,7 @@
 					</div>
 				</div>
 				<footer class="card-footer">
-					<a class="card-footer-item" @click="banIP()" href="#"
-						>Ban IP</a
-					>
+					<a class="card-footer-item" @click="banIP()">Ban IP</a>
 				</footer>
 			</div>
 		</div>
@@ -222,10 +220,6 @@ export default {
 	}
 }
 
-body {
-	font-family: "Hind", sans-serif;
-}
-
 td {
 	vertical-align: middle;
 }

+ 5 - 7
frontend/src/pages/Admin/tabs/Reports.vue

@@ -48,9 +48,8 @@
 							</ul>
 						</td>
 						<td id="options-column">
-							<a
+							<button
 								class="button is-primary"
-								href="#"
 								@click="view(report._id)"
 								content="Expand"
 								v-tippy
@@ -59,10 +58,9 @@
 									open_in_full
 								</i>
 								Expand
-							</a>
-							<a
+							</button>
+							<button
 								class="button is-success"
-								href="#"
 								@click="resolve(report._id)"
 								content="Resolve"
 								v-tippy
@@ -71,7 +69,7 @@
 									done_all
 								</i>
 								Resolve
-							</a>
+							</button>
 						</td>
 					</tr>
 				</tbody>
@@ -196,7 +194,7 @@ export default {
 }
 
 #options-column {
-	a:not(:last-of-type) {
+	button:not(:last-of-type) {
 		margin-right: 5px;
 	}
 }

+ 7 - 10
frontend/src/pages/Admin/tabs/Stations.vue

@@ -118,12 +118,12 @@
 										placeholder="Genre"
 										@keyup.enter="addGenre()"
 									/>
-									<a
+									<button
 										class="button is-info"
-										href="#"
 										@click="addGenre()"
-										>Add genre</a
 									>
+										Add genre
+									</button>
 								</p>
 								<span
 									v-for="(genre, index) in newStation.genres"
@@ -146,12 +146,12 @@
 										placeholder="Blacklisted Genre"
 										@keyup.enter="addBlacklistedGenre()"
 									/>
-									<a
+									<button
 										class="button is-info"
-										href="#"
 										@click="addBlacklistedGenre()"
-										>Add blacklisted genre</a
 									>
+										Add blacklisted genre
+									</button>
 								</p>
 								<span
 									v-for="(
@@ -171,10 +171,7 @@
 					</div>
 				</div>
 				<footer class="card-footer">
-					<a
-						class="card-footer-item"
-						href="#"
-						@click="createStation()"
+					<a class="card-footer-item" @click="createStation()"
 						>Create</a
 					>
 				</footer>

+ 0 - 4
frontend/src/pages/Admin/tabs/Statistics.vue

@@ -292,10 +292,6 @@ export default {
 	}
 }
 
-body {
-	font-family: "Hind", sans-serif;
-}
-
 .user-avatar {
 	display: block;
 	max-width: 50px;

+ 0 - 4
frontend/src/pages/Admin/tabs/Users.vue

@@ -206,10 +206,6 @@ export default {
 	}
 }
 
-body {
-	font-family: "Hind", sans-serif;
-}
-
 h2 {
 	font-size: 30px;
 	text-align: center;

+ 0 - 4
frontend/src/pages/Admin/tabs/VerifiedSongs.vue

@@ -614,10 +614,6 @@ export default {
 	}
 }
 
-body {
-	font-family: "Hind", sans-serif;
-}
-
 .box {
 	background-color: var(--light-grey);
 	border-radius: 5px;

+ 0 - 1
frontend/src/pages/Home.vue

@@ -1041,7 +1041,6 @@ html {
 	flex-wrap: wrap;
 	border-radius: 5px;
 	box-shadow: 0 2px 3px rgba(10, 10, 10, 0.1), 0 0 0 1px rgba(10, 10, 10, 0.1);
-	transition: all ease-in-out 0.2s;
 
 	.card-content {
 		padding: 10px 10px 10px 15px;

+ 1 - 3
frontend/src/pages/News.vue

@@ -18,9 +18,7 @@
 							:user-id="item.createdBy"
 							:alt="item.createdBy"
 							:link="true"
-						/>
-
-						<span :title="new Date(item.createdAt)">
+						/>&nbsp;<span :title="new Date(item.createdAt)">
 							{{
 								formatDistance(item.createdAt, new Date(), {
 									addSuffix: true

+ 16 - 26
frontend/src/pages/ResetPassword.vue

@@ -31,9 +31,7 @@
 							</p>
 
 							<p class="content-box-optional-helper">
-								<a href="#" @click="step = 2"
-									>Already have a code?</a
-								>
+								<a @click="step = 2">Already have a code?</a>
 							</p>
 
 							<div class="content-box-inputs">
@@ -53,18 +51,18 @@
 										/>
 									</p>
 									<p class="control">
-										<a
+										<button
 											class="button is-info"
-											href="#"
 											@click="submitEmail()"
-											><i
+										>
+											<i
 												class="
 													material-icons
 													icon-with-button
 												"
 												>mail</i
-											>Request</a
-										>
+											>Request
+										</button>
 									</p>
 								</div>
 								<transition name="fadein-helpbox">
@@ -92,7 +90,6 @@
 
 							<p class="content-box-optional-helper">
 								<a
-									href="#"
 									@click="
 										email.value ? submitEmail() : (step = 1)
 									"
@@ -115,18 +112,18 @@
 										/>
 									</p>
 									<p class="control">
-										<a
+										<button
 											class="button is-info"
-											href="#"
 											@click="verifyCode()"
-											><i
+										>
+											<i
 												class="
 													material-icons
 													icon-with-button
 												"
 												>vpn_key</i
-											>Verify</a
-										>
+											>Verify
+										</button>
 									</p>
 								</div>
 							</div>
@@ -228,14 +225,13 @@
 									/>
 								</transition>
 
-								<a
+								<button
 									id="change-password-button"
 									class="button is-success"
-									href="#"
 									@click="changePassword()"
 								>
-									Change password</a
-								>
+									Change password
+								</button>
 							</div>
 						</div>
 
@@ -248,10 +244,7 @@
 								>check_circle</i
 							>
 							<h2>Password successfully {{ mode }}</h2>
-							<router-link
-								class="button is-dark"
-								href="#"
-								to="/settings"
+							<router-link class="button is-dark" to="/settings"
 								><i class="material-icons icon-with-button"
 									>undo</i
 								>Return to Settings</router-link
@@ -268,10 +261,7 @@
 								Password {{ mode }} failed, please try again
 								later
 							</h2>
-							<router-link
-								class="button is-dark"
-								href="#"
-								to="/settings"
+							<router-link class="button is-dark" to="/settings"
 								><i class="material-icons icon-with-button"
 									>undo</i
 								>Return to Settings</router-link

+ 1 - 1
frontend/src/pages/Settings/Tabs/Security.vue

@@ -90,7 +90,7 @@
 
 			<hr class="section-horizontal-rule" />
 
-			<router-link to="/set_password" class="button is-default" href="#"
+			<router-link to="/set_password" class="button is-default"
 				><i class="material-icons icon-with-button">create</i>Set
 				Password
 			</router-link>

+ 2 - 3
frontend/src/pages/Station/Sidebar/Playlists.vue

@@ -88,14 +88,13 @@
 		<p v-else class="nothing-here-text scrollable-list">
 			No Playlists found
 		</p>
-		<a
+		<button
 			class="button create-playlist tab-actionable-button"
-			href="#"
 			@click="openModal('createPlaylist')"
 		>
 			<i class="material-icons icon-with-button">create</i>
 			<span> Create Playlist </span>
-		</a>
+		</button>
 	</div>
 </template>
 

+ 1 - 4
frontend/src/pages/Station/index.vue

@@ -80,7 +80,7 @@
 									>
 										check_circle
 									</i>
-									<a href="#">
+									<a>
 										<!-- Favorite Station Button -->
 										<i
 											v-if="
@@ -2322,9 +2322,6 @@ export default {
 #currently-playing-container,
 #next-up-container {
 	.song-item {
-		.song-info {
-			width: calc(100% - 80px);
-		}
 		.thumbnail {
 			min-width: 130px;
 			width: 130px;

+ 1 - 1
frontend/src/pages/Team.vue

@@ -177,7 +177,7 @@ export default {
 				{
 					name: "Jonathan Graham",
 					bio: `
-						<em>Gap-year student based in the UK hoping to study Computer Science at university in September.</em>
+						<em>Student based in the UK studying Computer Science at university.</em>
 						<br /><br />
 						Lead Developer, Designer and QA Tester.
 						`,