Browse Source

feat: added ability to show a news item to new users when they first visit the site

Kristian Vos 3 years ago
parent
commit
5868eafcd9

+ 5 - 2
backend/logic/actions/news.js

@@ -255,10 +255,13 @@ export default {
 	 * @param {object} session - the session object automatically added by the websocket
 	 * @param {Function} cb - gets called with the result
 	 */
-	async newest(session, cb) {
+	async newest(session, newUser, cb) {
 		const newsModel = await DBModule.runJob("GET_MODEL", { modelName: "news" }, this);
+		const query = { status: "published" };
+		if (newUser)
+			query.showToNewUsers = true;
 		async.waterfall(
-			[next => newsModel.findOne({ status: "published" }).sort({ createdAt: "desc" }).exec(next)],
+			[next => newsModel.findOne(query).sort({ createdAt: "desc" }).exec(next)],
 			async (err, news) => {
 				if (err) {
 					err = await UtilsModule.runJob("GET_ERROR", { error: err }, this);

+ 1 - 1
backend/logic/db/index.js

@@ -7,7 +7,7 @@ import CoreClass from "../../core";
 
 const REQUIRED_DOCUMENT_VERSIONS = {
 	activity: 2,
-	news: 2,
+	news: 3,
 	playlist: 6,
 	punishment: 1,
 	queueSong: 1,

+ 2 - 1
backend/logic/db/schemas/news.js

@@ -2,7 +2,8 @@ export default {
 	title: { type: String, required: true },
 	markdown: { type: String, required: true },
 	status: { type: String, enum: ["draft", "published", "archived"], required: true, default: "published" },
+	showToNewUsers: { type: Boolean, required: true, default: false },
 	createdBy: { type: String, required: true },
 	createdAt: { type: Number, default: Date.now, required: true },
-	documentVersion: { type: Number, default: 2, required: true }
+	documentVersion: { type: Number, default: 3, required: true }
 };

+ 47 - 0
backend/logic/migration/migrations/migration19.js

@@ -0,0 +1,47 @@
+import async from "async";
+
+/**
+ * Migration 19
+ *
+ * Migration for news showToNewUsers property.
+ *
+ * @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 => {
+					this.log("INFO", `Migration 19. Finding news with version 2.`);
+					newsModel.updateMany(
+						{ documentVersion: 2 },
+						{
+							$set: { documentVersion: 3, showToNewUsers: false },
+						},
+						(err, res) => {
+							if (err) next(err);
+							else {
+								this.log(
+									"INFO",
+									`Migration 19. Matched: ${res.matchedCount}, modified: ${res.modifiedCount}, ok: ${res.ok}.`
+								);
+
+								next();
+							}
+						}
+					);
+				}
+			],
+			err => {
+				if (err) {
+					reject(new Error(err));
+				} else {
+					resolve();
+				}
+			}
+		);
+	});
+}

+ 22 - 3
frontend/src/components/modals/EditNews.vue

@@ -28,6 +28,21 @@
 					</select>
 				</p>
 
+				<p class="is-expanded checkbox-control">
+					<label class="switch">
+						<input
+							type="checkbox"
+							id="show-to-new-users"
+							v-model="showToNewUsers"
+						/>
+						<span class="slider round"></span>
+					</label>
+
+					<label for="show-to-new-users">
+						<p>Show to new users</p>
+					</label>
+				</p>
+
 				<save-button
 					ref="saveButton"
 					v-if="newsId"
@@ -83,6 +98,7 @@ export default {
 			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",
+			showToNewUsers: false,
 			createdBy: null,
 			createdAt: 0
 		};
@@ -110,10 +126,11 @@ export default {
 			if (this.newsId) {
 				this.socket.dispatch(`news.getNewsFromId`, this.newsId, res => {
 					if (res.status === "success") {
-						const { markdown, status, createdBy, createdAt } =
+						const { markdown, status, showToNewUsers, createdBy, createdAt } =
 							res.data.news;
 						this.markdown = markdown;
 						this.status = status;
+						this.showToNewUsers = showToNewUsers;
 						this.createdBy = createdBy;
 						this.createdAt = createdAt;
 					} else {
@@ -165,7 +182,8 @@ export default {
 				{
 					title,
 					markdown: this.markdown,
-					status: this.status
+					status: this.status,
+					showToNewUsers: this.showToNewUsers
 				},
 				res => {
 					new Toast(res.message);
@@ -190,7 +208,8 @@ export default {
 				{
 					title,
 					markdown: this.markdown,
-					status: this.status
+					status: this.status,
+					showToNewUsers: this.showToNewUsers
 				},
 				res => {
 					new Toast(res.message);

+ 24 - 17
frontend/src/components/modals/WhatIsNew.vue

@@ -66,31 +66,38 @@ export default {
 	},
 	methods: {
 		init() {
-			this.socket.dispatch("news.newest", res => {
+			const newUser = !localStorage.hasOwnProperty("firstVisited");
+			this.socket.dispatch("news.newest", newUser, res => {
 				if (res.status !== "success") return;
 
 				const { news } = res.data;
 
 				this.news = news;
-				if (this.news && localStorage.getItem("firstVisited")) {
-					if (localStorage.getItem("whatIsNew")) {
-						if (
-							parseInt(localStorage.getItem("whatIsNew")) <
-							news.createdAt
-						) {
-							this.openModal("whatIsNew");
+				if (this.news) {
+					if (newUser) {
+						this.openModal("whatIsNew");
+					} else if (localStorage.getItem("firstVisited")) {
+						if (localStorage.getItem("whatIsNew")) {
+							if (
+								parseInt(localStorage.getItem("whatIsNew")) <
+								news.createdAt
+							) {
+								this.openModal("whatIsNew");
+								localStorage.setItem("whatIsNew", news.createdAt);
+							}
+						} else {
+							if (
+								parseInt(localStorage.getItem("firstVisited")) <
+								news.createdAt
+							)
+								this.openModal("whatIsNew");
 							localStorage.setItem("whatIsNew", news.createdAt);
 						}
-					} else {
-						if (
-							parseInt(localStorage.getItem("firstVisited")) <
-							news.createdAt
-						)
-							this.openModal("whatIsNew");
-						localStorage.setItem("whatIsNew", news.createdAt);
 					}
-				} else if (!localStorage.getItem("firstVisited"))
-					localStorage.setItem("firstVisited", Date.now());
+				}
+
+				if (!localStorage.getItem("firstVisited"))
+						localStorage.setItem("firstVisited", Date.now());
 			});
 		},
 		marked,

+ 19 - 0
frontend/src/pages/Admin/News.vue

@@ -45,6 +45,11 @@
 						slotProps.item.status
 					}}</span>
 				</template>
+				<template #column-showToNewUsers="slotProps">
+					<span :title="slotProps.item.showToNewUsers">{{
+						slotProps.item.showToNewUsers
+					}}</span>
+				</template>
 				<template #column-title="slotProps">
 					<span :title="slotProps.item.title">{{
 						slotProps.item.title
@@ -121,6 +126,13 @@ export default {
 					sortProperty: "status",
 					defaultWidth: 150
 				},
+				{
+					name: "showToNewUsers",
+					displayName: "Show to new users",
+					properties: ["showToNewUsers"],
+					sortProperty: "showToNewUsers",
+					defaultWidth: 180
+				},
 				{
 					name: "title",
 					displayName: "Title",
@@ -149,6 +161,13 @@ export default {
 					filterTypes: ["contains", "exact", "regex"],
 					defaultFilterType: "contains"
 				},
+				{
+					name: "showToNewUsers",
+					displayName: "Show to new users",
+					property: "showToNewUsers",
+					filterTypes: ["boolean"],
+					defaultFilterType: "boolean"
+				},
 				{
 					name: "title",
 					displayName: "Title",