Browse Source

chore(News): merge conflicts

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

+ 6 - 6
backend/logic/actions/songs.js

@@ -499,8 +499,8 @@ export default {
 								.filter((value, index, self) => self.indexOf(value) === index)
 								.forEach(genre => {
 									PlaylistsModule.runJob("AUTOFILL_GENRE_PLAYLIST", { genre })
-										.then(() => { })
-										.catch(() => { });
+										.then(() => {})
+										.catch(() => {});
 								});
 
 							next(null, song);
@@ -776,8 +776,8 @@ export default {
 				(song, next) => {
 					song.genres.forEach(genre => {
 						PlaylistsModule.runJob("AUTOFILL_GENRE_PLAYLIST", { genre })
-							.then(() => { })
-							.catch(() => { });
+							.then(() => {})
+							.catch(() => {});
 					});
 
 					SongsModule.runJob("UPDATE_SONG", { songId: song._id });
@@ -844,8 +844,8 @@ export default {
 				(song, next) => {
 					song.genres.forEach(genre => {
 						PlaylistsModule.runJob("AUTOFILL_GENRE_PLAYLIST", { genre })
-							.then(() => { })
-							.catch(() => { });
+							.then(() => {})
+							.catch(() => {});
 					});
 
 					SongsModule.runJob("UPDATE_SONG", { songId });

+ 86 - 0
backend/logic/migration/migrations/migration9.js

@@ -0,0 +1,86 @@
+import async from "async";
+
+/**
+ * Migration 9
+ *
+ * Migration for news
+ *
+ * @param {object} MigrationModule - the MigrationModule
+ * @returns {Promise} - returns promise
+ */
+export default async function migrate(MigrationModule) {
+	const newsModel = await MigrationModule.runJob("GET_MODEL", { modelName: "news" }, this);
+
+	return new Promise((resolve, reject) => {
+		async.waterfall(
+			[
+				next => {
+					// return next("BLOCKED");
+					this.log("INFO", `Migration 9. Finding news with document version 1.`);
+					newsModel.find({ documentVersion: 1 }, (err, news) => {
+						if (err) next(err);
+						else {
+							async.eachLimit(
+								news.map(newi => newi._doc),
+								1,
+								(newi, next) => {
+									newi.markdown = `# ${newi.title}\n\n`;
+									newi.markdown += `## ${newi.description}\n\n`;
+
+									if (newi.bugs) {
+										newi.markdown += `**Bugs:**\n\n${newi.bugs.join(", ")}\n\n`;
+									}
+
+									if (newi.features) {
+										newi.markdown += `**Features:**\n\n${newi.features.join(", ")}\n\n`;
+									}
+
+									if (newi.improvements) {
+										newi.markdown += `**Improvements:**\n\n${newi.improvements.join(", ")}\n\n`;
+									}
+
+									if (newi.upcoming) {
+										newi.markdown += `**Upcoming:**\n\n${newi.upcoming.join(", ")}\n`;
+									}
+
+									newsModel.updateOne(
+										{ _id: newi._id },
+										{
+											$set: {
+												markdown: newi.markdown,
+												status: "published",
+												documentVersion: 2
+											},
+											$unset: {
+												description: "",
+												bugs: "",
+												features: "",
+												improvements: "",
+												upcoming: ""
+											}
+										},
+										next
+									);
+								},
+								err => {
+									if (err) next(err);
+									else {
+										this.log("INFO", `Migration 9. News found: ${news.length}.`);
+										next();
+									}
+								}
+							);
+						}
+					});
+				}
+			],
+			err => {
+				if (err) {
+					reject(new Error(err));
+				} else {
+					resolve();
+				}
+			}
+		);
+	});
+}

+ 17 - 10
backend/logic/playlists.js

@@ -556,16 +556,23 @@ class _PlaylistsModule extends CoreClass {
 					(playlistId, next) => {
 						StationsModule.runJob("GET_STATIONS_THAT_INCLUDE_OR_EXCLUDE_PLAYLIST", { playlistId }, this)
 							.then(response => {
-								async.eachLimit(response.stationIds, 1, (stationId, next) => {
-									PlaylistsModule.runJob("AUTOFILL_STATION_PLAYLIST", { stationId }, this).then(() => {
-										next();
-									}).catch(err => {
-										next(err);
-									});
-								}, err => {
-									if (err) next(err);
-									else next();
-								});
+								async.eachLimit(
+									response.stationIds,
+									1,
+									(stationId, next) => {
+										PlaylistsModule.runJob("AUTOFILL_STATION_PLAYLIST", { stationId }, this)
+											.then(() => {
+												next();
+											})
+											.catch(err => {
+												next(err);
+											});
+									},
+									err => {
+										if (err) next(err);
+										else next();
+									}
+								);
 							})
 							.catch(err => {
 								next(err);

+ 96 - 71
backend/logic/songs.js

@@ -203,7 +203,7 @@ class _SongsModule extends CoreClass {
 						} else {
 							const status =
 								(!payload.userId && config.get("hideAnonymousSongs")) ||
-									(payload.automaticallyRequested && config.get("hideAutomaticallyRequestedSongs"))
+								(payload.automaticallyRequested && config.get("hideAutomaticallyRequestedSongs"))
 									? "hidden"
 									: "unverified";
 
@@ -302,40 +302,53 @@ class _SongsModule extends CoreClass {
 							status
 						};
 						this.log("INFO", `Going to update playlists now for song ${_id}`);
-						DBModule.runJob("GET_MODEL", { modelName: "playlist" }, this).then(playlistModel => {
-							playlistModel.updateMany(
-								{ "songs._id": song._id },
-								{ $set: { "songs.$": trimmedSong } },
-								err => {
-									if (err) next(err);
-									else
-										playlistModel.find({ "songs._id": song._id }, (err, playlists) => {
-											if (err) next(err);
-											else {
-												async.eachLimit(playlists, 1, (playlist, next) => {
-													PlaylistsModule.runJob("UPDATE_PLAYLIST", {
-														playlistId: playlist._id
-													}, this).then(() => {
-														next();
-													}).catch(err => {
-														next(err);
-													});
-												}, err => {
-													if (err) next(err);
-													else next(null, song)
-												});
-											}
-											// playlists.forEach(playlist => {
-											// 	PlaylistsModule.runJob("UPDATE_PLAYLIST", {
-											// 		playlistId: playlist._id
-											// 	});
-											// });
-										});
-								}
-							);
-						}).catch(err => {
-							next(err);
-						});
+						DBModule.runJob("GET_MODEL", { modelName: "playlist" }, this)
+							.then(playlistModel => {
+								playlistModel.updateMany(
+									{ "songs._id": song._id },
+									{ $set: { "songs.$": trimmedSong } },
+									err => {
+										if (err) next(err);
+										else
+											playlistModel.find({ "songs._id": song._id }, (err, playlists) => {
+												if (err) next(err);
+												else {
+													async.eachLimit(
+														playlists,
+														1,
+														(playlist, next) => {
+															PlaylistsModule.runJob(
+																"UPDATE_PLAYLIST",
+																{
+																	playlistId: playlist._id
+																},
+																this
+															)
+																.then(() => {
+																	next();
+																})
+																.catch(err => {
+																	next(err);
+																});
+														},
+														err => {
+															if (err) next(err);
+															else next(null, song);
+														}
+													);
+												}
+												// playlists.forEach(playlist => {
+												// 	PlaylistsModule.runJob("UPDATE_PLAYLIST", {
+												// 		playlistId: playlist._id
+												// 	});
+												// });
+											});
+									}
+								);
+							})
+							.catch(err => {
+								next(err);
+							});
 					},
 
 					(song, next) => {
@@ -369,42 +382,55 @@ class _SongsModule extends CoreClass {
 						// 	);
 						// });
 						this.log("INFO", `Going to update stations now for song ${_id}`);
-						DBModule.runJob("GET_MODEL", { modelName: "station" }, this).then(stationModel => {
-							stationModel.updateMany(
-								{ "queue._id": song._id },
-								{
-									$set: {
-										"queue.$.youtubeId": youtubeId,
-										"queue.$.title": title,
-										"queue.$.artists": artists,
-										"queue.$.thumbnail": thumbnail,
-										"queue.$.duration": duration,
-										"queue.$.status": status
+						DBModule.runJob("GET_MODEL", { modelName: "station" }, this)
+							.then(stationModel => {
+								stationModel.updateMany(
+									{ "queue._id": song._id },
+									{
+										$set: {
+											"queue.$.youtubeId": youtubeId,
+											"queue.$.title": title,
+											"queue.$.artists": artists,
+											"queue.$.thumbnail": thumbnail,
+											"queue.$.duration": duration,
+											"queue.$.status": status
+										}
+									},
+									err => {
+										if (err) this.log("ERROR", err);
+										else
+											stationModel.find({ "queue._id": song._id }, (err, stations) => {
+												if (err) next(err);
+												else {
+													async.eachLimit(
+														stations,
+														1,
+														(station, next) => {
+															StationsModule.runJob(
+																"UPDATE_STATION",
+																{ stationId: station._id },
+																this
+															)
+																.then(() => {
+																	next();
+																})
+																.catch(err => {
+																	next(err);
+																});
+														},
+														err => {
+															if (err) next(err);
+															else next(null, song);
+														}
+													);
+												}
+											});
 									}
-								},
-								err => {
-									if (err) this.log("ERROR", err);
-									else
-										stationModel.find({ "queue._id": song._id }, (err, stations) => {
-											if (err) next(err);
-											else {
-												async.eachLimit(stations, 1, (station, next) => {
-													StationsModule.runJob("UPDATE_STATION", { stationId: station._id }, this).then(() => {
-														next();
-													}).catch(err => {
-														next(err);
-													});
-												}, err => {
-													if (err) next(err);
-													else next(null, song);
-												});
-											}
-										});
-								}
-							);
-						}).catch(err => {
-							next(err);
-						});
+								);
+							})
+							.catch(err => {
+								next(err);
+							});
 					},
 
 					(song, next) => {
@@ -923,7 +949,6 @@ class _SongsModule extends CoreClass {
 					(song, next) => {
 						if (!song) return next("This song does not exist.");
 						if (song.status === "hidden") return next("This song is already hidden.");
-						if (song.status === "verified") return next("Verified songs cannot be hidden.");
 						// TODO Add err object as first param of callback
 						return next();
 					},

+ 7 - 7
backend/package-lock.json

@@ -25,7 +25,7 @@
         "redis": "^3.1.1",
         "retry-axios": "^2.4.0",
         "sha256": "^0.2.0",
-        "underscore": "^1.10.2",
+        "underscore": "^1.12.1",
         "ws": "^7.4.3"
       },
       "devDependencies": {
@@ -3384,9 +3384,9 @@
       }
     },
     "node_modules/underscore": {
-      "version": "1.10.2",
-      "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.10.2.tgz",
-      "integrity": "sha512-N4P+Q/BuyuEKFJ43B9gYuOj4TQUHXX+j2FqguVOpjkssLUUrnJofCcBccJSCoeturDoZU6GorDTHSvUDlSQbTg=="
+      "version": "1.12.1",
+      "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.12.1.tgz",
+      "integrity": "sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw=="
     },
     "node_modules/unpipe": {
       "version": "1.0.0",
@@ -6238,9 +6238,9 @@
       }
     },
     "underscore": {
-      "version": "1.10.2",
-      "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.10.2.tgz",
-      "integrity": "sha512-N4P+Q/BuyuEKFJ43B9gYuOj4TQUHXX+j2FqguVOpjkssLUUrnJofCcBccJSCoeturDoZU6GorDTHSvUDlSQbTg=="
+      "version": "1.12.1",
+      "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.12.1.tgz",
+      "integrity": "sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw=="
     },
     "unpipe": {
       "version": "1.0.0",

+ 1 - 1
backend/package.json

@@ -31,7 +31,7 @@
     "redis": "^3.1.1",
     "retry-axios": "^2.4.0",
     "sha256": "^0.2.0",
-    "underscore": "^1.10.2",
+    "underscore": "^1.12.1",
     "ws": "^7.4.3"
   },
   "devDependencies": {

+ 3 - 3
frontend/package-lock.json

@@ -7557,9 +7557,9 @@
       }
     },
     "lodash": {
-      "version": "4.17.20",
-      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
-      "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA=="
+      "version": "4.17.21",
+      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+      "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
     },
     "lodash.debounce": {
       "version": "4.0.8",

+ 17 - 10
frontend/src/App.vue

@@ -1088,6 +1088,10 @@ h4.section-title {
 
 .news-item {
 	font-family: "Karla";
+	border-radius: 5px;
+	padding: 20px;
+	border: unset !important;
+	box-shadow: 0 2px 3px rgba(10, 10, 10, 0.1), 0 0 0 1px rgba(10, 10, 10, 0.1);
 
 	* {
 		font-family: Karla, Arial, sans-serif;
@@ -1095,7 +1099,7 @@ h4.section-title {
 	}
 
 	h1 {
-		font-size: 55px;
+		font-size: 40px;
 
 		&:first-of-type {
 			margin-top: 0;
@@ -1103,23 +1107,26 @@ h4.section-title {
 	}
 
 	h2 {
-		font-size: 45px;
+		font-size: 30px;
 	}
 
 	h3 {
-		font-size: 40px;
-	}
-
-	h4 {
-		font-size: 35px;
+		font-size: 25px;
 	}
 
-	h5 {
-		font-size: 30px;
+	h4,
+	h5,
+	h6 {
+		font-size: 20px;
 	}
 
+	h1,
+	h2,
+	h3,
+	h4,
+	h5,
 	h6 {
-		font-size: 25px;
+		margin: 10px 0;
 	}
 
 	ul {

+ 39 - 4
frontend/src/components/modals/EditNews.vue

@@ -38,6 +38,21 @@
 				type="save-and-close"
 				@clicked="newsId ? update(true) : create(true)"
 			/>
+			<div class="right" v-if="createdAt > 0">
+				<span>
+					By
+					<user-id-to-username
+						:user-id="createdBy"
+						:alt="createdBy"
+						:link="true"/></span
+				><span :title="new Date(createdAt)">
+					{{
+						formatDistance(createdAt, new Date(), {
+							addSuffix: true
+						})
+					}}
+				</span>
+			</div>
 		</div>
 	</modal>
 </template>
@@ -46,20 +61,25 @@
 import { mapActions, mapGetters, mapState } from "vuex";
 import marked from "marked";
 import Toast from "toasters";
+import { formatDistance } from "date-fns";
 
+import UserIdToUsername from "@/components/UserIdToUsername.vue";
 import SaveButton from "../SaveButton.vue";
 import Modal from "../Modal.vue";
 
 export default {
-	components: { Modal, SaveButton },
+	components: { Modal, SaveButton, UserIdToUsername },
 	props: {
 		newsId: { type: String, default: "" },
 		sector: { type: String, default: "admin" }
 	},
 	data() {
 		return {
-			markdown: "# Example\n## Subheading goes here",
-			status: "published"
+			markdown:
+				"# Header\n ## Sub-Header\n- **So**\n- _Many_\n- ~Points~\n\nOther things you want to say and [link](https://example.com).\n\n### Sub-Sub-Header\n> Oh look, a quote!\n\n`lil code`\n\n```\nbig code\n```\n",
+			status: "published",
+			createdBy: null,
+			createdAt: 0
 		};
 	},
 	computed: {
@@ -81,9 +101,16 @@ export default {
 		if (this.newsId) {
 			this.socket.dispatch(`news.getNewsFromId`, this.newsId, res => {
 				if (res.status === "success") {
-					const { markdown, status } = res.data.news;
+					const {
+						markdown,
+						status,
+						createdBy,
+						createdAt
+					} = res.data.news;
 					this.markdown = markdown;
 					this.status = status;
+					this.createdBy = createdBy;
+					this.createdAt = createdAt;
 				} else {
 					new Toast("News with that ID not found.");
 					this.closeModal("editNews");
@@ -158,6 +185,7 @@ export default {
 				}
 			);
 		},
+		formatDistance,
 		...mapActions("modalVisibility", ["closeModal"]),
 		...mapActions("modals/editNews", [
 			"editNews",
@@ -172,6 +200,13 @@ export default {
 .edit-news-modal {
 	.modal-card {
 		width: 1300px;
+		.modal-card-foot .right {
+			margin: auto 0 auto auto !important;
+
+			span:not(:last-child) {
+				margin-right: 0 !important;
+			}
+		}
 	}
 }
 </style>

+ 76 - 22
frontend/src/components/modals/EditSong.vue

@@ -207,7 +207,9 @@
 									>
 										<div
 											class="list-item-circle"
-											@click="removeTag('artists', index)"
+											@click="
+												removeTag('artists', artist)
+											"
 										>
 											<i class="material-icons">close</i>
 										</div>
@@ -278,7 +280,7 @@
 									>
 										<div
 											class="list-item-circle"
-											@click="removeTag('genres', index)"
+											@click="removeTag('genres', genre)"
 										>
 											<i class="material-icons">close</i>
 										</div>
@@ -480,7 +482,7 @@
 				/>
 				<div class="right">
 					<button
-						v-if="song.status === 'unverified'"
+						v-if="song.status !== 'verified'"
 						class="button is-success"
 						@click="verify(song._id)"
 						content="Verify Song"
@@ -502,7 +504,7 @@
 						</button>
 					</confirm>
 					<confirm
-						v-if="song.status === 'unverified'"
+						v-if="song.status !== 'hidden'"
 						placement="left"
 						@confirm="hide(song._id)"
 					>
@@ -578,8 +580,8 @@ export default {
 			songDataLoaded: false,
 			focusedElementBefore: null,
 			discogsQuery: "",
-			youtubeVideoDuration: 0.0,
-			youtubeVideoCurrentTime: 0.0,
+			youtubeVideoDuration: "0.000",
+			youtubeVideoCurrentTime: 0,
 			youtubeVideoNote: "",
 			useHTTPS: false,
 			discogs: {
@@ -589,6 +591,7 @@ export default {
 				disableLoadMore: false
 			},
 			volumeSliderValue: 0,
+			skipToLast10SecsPressed: false,
 			artistInputValue: "",
 			genreInputValue: "",
 			artistInputFocussed: false,
@@ -737,9 +740,11 @@ export default {
 							this.video.player.seekTo(this.song.skipDuration);
 							this.video.player.setVolume(volume);
 							if (volume > 0) this.video.player.unMute();
-							this.youtubeVideoDuration = this.video.player
-								.getDuration()
-								.toFixed(3);
+
+							const duration = this.video.player.getDuration();
+
+							console.log(1111, duration.toFixed(3));
+							this.youtubeVideoDuration = duration.toFixed(3);
 							this.youtubeVideoNote = "(~)";
 							this.playerReady = true;
 
@@ -748,7 +753,13 @@ export default {
 						onStateChange: event => {
 							this.drawCanvas();
 
-							if (event.data === 1) {
+							let skipToLast10SecsPressed = false;
+							if (this.skipToLast10SecsPressed) {
+								this.skipToLast10SecsPressed = false;
+								skipToLast10SecsPressed = true;
+							}
+
+							if (event.data === 1 && !skipToLast10SecsPressed) {
 								if (!this.video.autoPlayed) {
 									this.video.autoPlayed = true;
 									return this.video.player.stopVideo();
@@ -756,9 +767,46 @@ export default {
 
 								this.video.paused = false;
 								let youtubeDuration = this.video.player.getDuration();
-								this.youtubeVideoDuration = youtubeDuration.toFixed(
+								const newYoutubeVideoDuration = youtubeDuration.toFixed(
+									3
+								);
+
+								const songDurationNumber = Number(
+									this.song.duration
+								);
+								const songDurationNumber2 =
+									Number(this.song.duration) + 1;
+								const songDurationNumber3 =
+									Number(this.song.duration) - 1;
+								const fixedSongDuration = songDurationNumber.toFixed(
 									3
 								);
+								const fixedSongDuration2 = songDurationNumber2.toFixed(
+									3
+								);
+								const fixedSongDuration3 = songDurationNumber3.toFixed(
+									3
+								);
+
+								if (
+									this.youtubeVideoDuration !==
+										newYoutubeVideoDuration &&
+									(fixedSongDuration ===
+										this.youtubeVideoDuration ||
+										fixedSongDuration2 ===
+											this.youtubeVideoDuration ||
+										fixedSongDuration3 ===
+											this.youtubeVideoDuration)
+								)
+									this.song.duration = newYoutubeVideoDuration;
+
+								console.log(
+									2222,
+									newYoutubeVideoDuration,
+									this.video.player.getDuration()
+								);
+
+								this.youtubeVideoDuration = newYoutubeVideoDuration;
 								this.youtubeVideoNote = "";
 
 								if (this.song.duration === -1)
@@ -1368,6 +1416,7 @@ export default {
 					this.pauseVideo(false);
 					break;
 				case "skipToLast10Secs":
+					this.skipToLast10SecsPressed = true;
 					if (this.video.paused) this.pauseVideo(false);
 					this.video.player.seekTo(
 						this.song.duration - 10 + this.song.skipDuration
@@ -1380,6 +1429,7 @@ export default {
 				this.video.player.getVideoData().video_id !==
 				this.song.youtubeId
 			) {
+				this.song.duration = -1;
 				this.loadVideoById(this.song.youtubeId, this.song.skipDuration);
 			}
 			this.settings("play");
@@ -1392,27 +1442,29 @@ export default {
 		},
 		addTag(type) {
 			if (type === "genres") {
-				const genre = this.$refs["new-genre"].value
-					.toLowerCase()
-					.trim();
+				const genre = this.genreInputValue.trim();
 
-				if (this.song.genres.indexOf(genre) !== -1)
+				if (
+					this.song.genres
+						.map(genre => genre.toLowerCase())
+						.indexOf(genre.toLowerCase()) !== -1
+				)
 					return new Toast("Genre already exists");
 				if (genre) {
 					this.song.genres.push(genre);
-					this.$refs["new-genre"].value = "";
+					this.genreInputValue = "";
 					return false;
 				}
 
 				return new Toast("Genre cannot be empty");
 			}
 			if (type === "artists") {
-				const artist = this.$refs["new-artist"].value;
+				const artist = this.artistInputValue;
 				if (this.song.artists.indexOf(artist) !== -1)
 					return new Toast("Artist already exists");
-				if (this.$refs["new-artist"].value !== "") {
+				if (artist !== "") {
 					this.song.artists.push(artist);
-					this.$refs["new-artist"].value = "";
+					this.artistInputValue = "";
 					return false;
 				}
 				return new Toast("Artist cannot be empty");
@@ -1420,9 +1472,11 @@ export default {
 
 			return false;
 		},
-		removeTag(type, index) {
-			if (type === "genres") this.song.genres.splice(index, 1);
-			else if (type === "artists") this.song.artists.splice(index, 1);
+		removeTag(type, value) {
+			if (type === "genres")
+				this.song.genres.splice(this.song.genres.indexOf(value), 1);
+			else if (type === "artists")
+				this.song.artists.splice(this.song.artists.indexOf(value), 1);
 		},
 		drawCanvas() {
 			const canvasElement = this.$refs.durationCanvas;

+ 2 - 1
frontend/src/components/modals/ManageStation/Tabs/Blacklist.vue

@@ -144,8 +144,9 @@ export default {
 			}
 
 			.selected {
-				background-color: var(--dark-grey-3) !important;
+				background-color: var(--primary-color) !important;
 				color: var(--white) !important;
+				font-weight: 600;
 			}
 		}
 		.tab {

+ 2 - 1
frontend/src/components/modals/ManageStation/Tabs/Playlists.vue

@@ -602,8 +602,9 @@ export default {
 			}
 
 			.selected {
-				background-color: var(--dark-grey-3) !important;
+				background-color: var(--primary-color) !important;
 				color: var(--white) !important;
+				font-weight: 600;
 			}
 		}
 		.tab {

+ 12 - 1
frontend/src/components/modals/ManageStation/index.vue

@@ -147,6 +147,16 @@
 			</div>
 		</template>
 		<template #footer>
+			<router-link
+				v-if="sector !== 'station'"
+				:to="{
+					name: 'station',
+					params: { id: station.name }
+				}"
+				class="button is-primary"
+			>
+				Go To Station
+			</router-link>
 			<a
 				class="button is-default"
 				v-if="isOwnerOrAdmin() && !station.partyMode"
@@ -605,8 +615,9 @@ export default {
 				}
 
 				.selected {
-					background-color: var(--dark-grey-3) !important;
+					background-color: var(--primary-color) !important;
 					color: var(--white) !important;
+					font-weight: 600;
 				}
 			}
 			.tab {

+ 36 - 8
frontend/src/components/modals/WhatIsNew.vue

@@ -1,12 +1,27 @@
 <template>
 	<div v-if="news !== null">
-		<modal :title="`News posted ${timeCreated}`">
+		<modal title="News" class="what-is-news-modal">
 			<div slot="body">
 				<div
 					class="section news-item"
 					v-html="marked(news.markdown)"
 				></div>
 			</div>
+			<div slot="footer">
+				<span v-if="news.createdBy">
+					By
+					<user-id-to-username
+						:user-id="news.createdBy"
+						:alt="news.createdBy"
+						:link="true"/></span
+				><span :title="new Date(news.createdAt)">
+					{{
+						formatDistance(news.createdAt, new Date(), {
+							addSuffix: true
+						})
+					}}
+				</span>
+			</div>
 		</modal>
 	</div>
 </template>
@@ -16,10 +31,11 @@ import { formatDistance } from "date-fns";
 import marked from "marked";
 import { mapGetters, mapActions } from "vuex";
 
+import UserIdToUsername from "@/components/UserIdToUsername.vue";
 import Modal from "../Modal.vue";
 
 export default {
-	components: { Modal },
+	components: { Modal, UserIdToUsername },
 	data() {
 		return {
 			isModalActive: false,
@@ -29,12 +45,7 @@ export default {
 	computed: {
 		...mapGetters({
 			socket: "websockets/getSocket"
-		}),
-		timeCreated() {
-			return formatDistance(this.news.createdAt, new Date(), {
-				addSuffix: true
-			});
-		}
+		})
 	},
 	mounted() {
 		marked.use({
@@ -77,11 +88,24 @@ export default {
 	},
 	methods: {
 		marked,
+		formatDistance,
 		...mapActions("modalVisibility", ["openModal"])
 	}
 };
 </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,
@@ -106,6 +130,10 @@ export default {
 	font-size: 14px;
 }
 
+.news-item {
+	box-shadow: unset !important;
+}
+
 .delete {
 	background: transparent;
 	&:hover {

+ 20 - 10
frontend/src/pages/News.vue

@@ -4,18 +4,27 @@
 		<main-header />
 		<div class="container">
 			<div class="content-wrapper">
-				<h3 class=" has-text-centered page-title">
-					{{
-						news.length === 0 ? "No news items were found." : "News"
-					}}
-				</h3>
-
+				<h1 class="has-text-centered page-title">News</h1>
 				<div
 					v-for="item in news"
 					:key="item._id"
 					class="section news-item"
-					v-html="marked(item.markdown)"
-				></div>
+				>
+					<div v-html="marked(item.markdown)"></div>
+					<div class="info">
+						<hr />
+						By
+						<user-id-to-username
+							:user-id="item.createdBy"
+							:alt="item.createdBy"
+							:link="true"
+						/>
+						@ {{ formatDate(item.createdAt) }}
+					</div>
+				</div>
+				<h3 v-if="news.length === 0" class="has-text-centered">
+					No news items were found.
+				</h3>
 			</div>
 		</div>
 		<main-footer />
@@ -29,9 +38,10 @@ import marked from "marked";
 
 import MainHeader from "@/components/layout/MainHeader.vue";
 import MainFooter from "@/components/layout/MainFooter.vue";
+import UserIdToUsername from "@/components/UserIdToUsername.vue";
 
 export default {
-	components: { MainHeader, MainFooter },
+	components: { MainHeader, MainFooter, UserIdToUsername },
 	data() {
 		return {
 			news: []
@@ -72,7 +82,7 @@ export default {
 	methods: {
 		marked,
 		formatDate: unix => {
-			return format(unix, "dd-MM-yyyy");
+			return format(unix, "HH:ii:ss dd-MM-yyyy");
 		}
 	}
 };

+ 2 - 1
frontend/src/pages/Station/Sidebar/index.vue

@@ -109,8 +109,9 @@ export default {
 	}
 
 	.selected {
-		background-color: var(--dark-grey-3) !important;
+		background-color: var(--primary-color) !important;
 		color: var(--white) !important;
+		font-weight: 600;
 	}
 }
 

+ 7 - 5
frontend/src/pages/Team.vue

@@ -222,9 +222,15 @@ export default {
 					bio: "Official instance Moderator.",
 					active: "Unknown"
 				},
+				{
+					name: "Johannes Andersen",
+					bio: "Official instance Moderator and QA Tester.",
+					active: "Unknown",
+					link: "https://github.com/Johannes-Andersen"
+				},
 				{
 					name: "Adryd",
-					bio: "Created Logo.",
+					bio: "Created Logo and Notes image.",
 					active: "May 2016",
 					link: "https://github.com/Adryd"
 				}
@@ -241,10 +247,6 @@ export default {
 				{
 					name: "Wesley McCann",
 					link: "https://github.com/Septimus"
-				},
-				{
-					name: "Johannes Andersen",
-					link: "https://github.com/Johannes-Andersen"
 				}
 			]
 		};