Browse Source

refactor: Added news actions and events types

Owen Diffey 1 year ago
parent
commit
a7fc34c021

+ 7 - 7
frontend/src/classes/SocketHandler.class.ts

@@ -10,27 +10,27 @@ export default class SocketHandler {
 	dispatcher: ListenerHandler;
 
 	onConnectCbs: {
-		temp: ((...args: any[]) => any)[];
-		persist: ((...args: any[]) => any)[];
+		temp: (() => void)[];
+		persist: (() => void)[];
 	};
 
 	ready: boolean;
 
 	firstInit: boolean;
 
-	pendingDispatches: ((...args: any[]) => any)[];
+	pendingDispatches: (() => void)[];
 
 	onDisconnectCbs: {
-		temp: ((...args: any[]) => any)[];
-		persist: ((...args: any[]) => any)[];
+		temp: (() => void)[];
+		persist: (() => void)[];
 	};
 
 	CB_REFS: {
-		[key: string]: (...args: any[]) => any;
+		[key: string]: (...args: any[]) => void;
 	};
 
 	PROGRESS_CB_REFS: {
-		[key: string]: (...args: any[]) => any;
+		[key: string]: (...args: any[]) => void;
 	};
 
 	data: {

+ 27 - 18
frontend/src/components/modals/EditNews.vue

@@ -5,6 +5,11 @@ import DOMPurify from "dompurify";
 import Toast from "toasters";
 import { formatDistance } from "date-fns";
 import { storeToRefs } from "pinia";
+import {
+	GetNewsResponse,
+	CreateNewsResponse,
+	UpdateNewsResponse
+} from "@musare_types/actions/NewsActions";
 import { useWebsocketsStore } from "@/stores/websockets";
 import { useEditNewsStore } from "@/stores/editNews";
 import { useModalsStore } from "@/stores/modals";
@@ -38,18 +43,18 @@ const getTitle = () => {
 
 	// validate existence of h1 for the page title
 
-	if (preview.childNodes.length === 0) return "";
+	if (!preview || preview.childNodes.length === 0) return "";
 
 	if (preview.childNodes[0].nodeName !== "H1") {
 		for (let node = 0; node < preview.childNodes.length; node += 1) {
 			if (preview.childNodes[node].nodeName) {
 				if (preview.childNodes[node].nodeName === "H1")
-					title = preview.childNodes[node].textContent;
+					title = preview.childNodes[node].textContent || "";
 
 				break;
 			}
 		}
-	} else title = preview.childNodes[0].textContent;
+	} else title = preview.childNodes[0].textContent || "";
 
 	return title;
 };
@@ -58,7 +63,7 @@ const { inputs, save, setOriginalValue } = useForm(
 	{
 		markdown: {
 			value: "# 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",
-			validate: value => {
+			validate: (value: string) => {
 				if (value === "") return "News item cannot be empty.";
 				if (!getTitle())
 					return "Please provide a title (heading level 1) at the top of the document.";
@@ -76,7 +81,7 @@ const { inputs, save, setOriginalValue } = useForm(
 				status: values.status,
 				showToNewUsers: values.showToNewUsers
 			};
-			const cb = res => {
+			const cb = (res: CreateNewsResponse | UpdateNewsResponse) => {
 				new Toast(res.message);
 				if (res.status === "success") resolve();
 				else reject(new Error(res.message));
@@ -113,20 +118,24 @@ onMounted(() => {
 
 	socket.onConnect(() => {
 		if (newsId.value && !createNews.value) {
-			socket.dispatch(`news.getNewsFromId`, newsId.value, res => {
-				if (res.status === "success") {
-					setOriginalValue({
-						markdown: res.data.news.markdown,
-						status: res.data.news.status,
-						showToNewUsers: res.data.news.showToNewUsers
-					});
-					createdBy.value = res.data.news.createdBy;
-					createdAt.value = res.data.news.createdAt;
-				} else {
-					new Toast("News with that ID not found.");
-					closeCurrentModal();
+			socket.dispatch(
+				`news.getNewsFromId`,
+				newsId.value,
+				(res: GetNewsResponse) => {
+					if (res.status === "success") {
+						setOriginalValue({
+							markdown: res.data.news.markdown,
+							status: res.data.news.status,
+							showToNewUsers: res.data.news.showToNewUsers
+						});
+						createdBy.value = res.data.news.createdBy;
+						createdAt.value = res.data.news.createdAt;
+					} else {
+						new Toast("News with that ID not found.");
+						closeCurrentModal();
+					}
 				}
-			});
+			);
 		}
 	});
 });

+ 7 - 2
frontend/src/pages/Admin/News.vue

@@ -1,6 +1,7 @@
 <script setup lang="ts">
 import { defineAsyncComponent, ref } from "vue";
 import Toast from "toasters";
+import { RemoveNewsResponse } from "@musare_types/actions/NewsActions";
 import { useWebsocketsStore } from "@/stores/websockets";
 import { useModalsStore } from "@/stores/modals";
 import { useUserAuthStore } from "@/stores/userAuth";
@@ -126,8 +127,12 @@ const { openModal } = useModalsStore();
 
 const { hasPermission } = useUserAuthStore();
 
-const remove = id => {
-	socket.dispatch("news.remove", id, res => new Toast(res.message));
+const remove = (id: string) => {
+	socket.dispatch(
+		"news.remove",
+		id,
+		(res: RemoveNewsResponse) => new Toast(res.message)
+	);
 };
 </script>
 

+ 17 - 7
frontend/src/pages/News.vue

@@ -4,6 +4,13 @@ import { defineAsyncComponent, ref, onMounted } from "vue";
 import { formatDistance } from "date-fns";
 import { marked } from "marked";
 import DOMPurify from "dompurify";
+import { NewsModel } from "@musare_types/models/News";
+import {
+	NewsCreatedResponse,
+	NewsUpdatedResponse,
+	NewsRemovedResponse
+} from "@musare_types/events/NewsEvents";
+import { GetPublishedNewsResponse } from "@musare_types/actions/NewsActions";
 import { useWebsocketsStore } from "@/stores/websockets";
 
 const MainHeader = defineAsyncComponent(
@@ -18,7 +25,7 @@ const UserLink = defineAsyncComponent(
 
 const { socket } = useWebsocketsStore();
 
-const news = ref([]);
+const news = ref<NewsModel[]>([]);
 
 const { sanitize } = DOMPurify;
 
@@ -35,17 +42,20 @@ onMounted(() => {
 	});
 
 	socket.onConnect(() => {
-		socket.dispatch("news.getPublished", res => {
-			if (res.status === "success") news.value = res.data.news;
-		});
+		socket.dispatch(
+			"news.getPublished",
+			(res: GetPublishedNewsResponse) => {
+				if (res.status === "success") news.value = res.data.news;
+			}
+		);
 
 		socket.dispatch("apis.joinRoom", "news");
 
-		socket.on("event:news.created", res =>
+		socket.on("event:news.created", (res: NewsCreatedResponse) =>
 			news.value.unshift(res.data.news)
 		);
 
-		socket.on("event:news.updated", res => {
+		socket.on("event:news.updated", (res: NewsUpdatedResponse) => {
 			if (res.data.news.status === "draft") {
 				news.value = news.value.filter(
 					item => item._id !== res.data.news._id
@@ -62,7 +72,7 @@ onMounted(() => {
 			}
 		});
 
-		socket.on("event:news.deleted", res => {
+		socket.on("event:news.deleted", (res: NewsRemovedResponse) => {
 			news.value = news.value.filter(
 				item => item._id !== res.data.newsId
 			);

+ 12 - 1
types/actions/NewsActions.ts

@@ -1,8 +1,19 @@
 import { BaseResponse } from "./BaseActions";
 import { NewsModel } from "../models/News";
 
-export type NewestResponse = BaseResponse & {
+type BaseNewsResponse = BaseResponse & {
 	data: {
 		news: NewsModel;
 	};
 };
+
+export type NewestResponse = BaseNewsResponse;
+export type GetNewsResponse = BaseNewsResponse;
+export type GetPublishedNewsResponse = BaseResponse & {
+	data: {
+		news: NewsModel[]
+	}
+};
+export type CreateNewsResponse = BaseResponse;
+export type UpdateNewsResponse = BaseResponse;
+export type RemoveNewsResponse = BaseResponse;

+ 15 - 0
types/events/NewsEvents.ts

@@ -0,0 +1,15 @@
+import { NewsModel } from "../models/News";
+
+type BaseNewsEventsResponse = {
+	data: {
+		news: NewsModel;
+	};
+};
+
+export type NewsCreatedResponse = BaseNewsEventsResponse;
+export type NewsUpdatedResponse = BaseNewsEventsResponse;
+export type NewsRemovedResponse = {
+	data: {
+		newsId: string
+	}
+};

+ 1 - 0
types/models/News.ts

@@ -1,4 +1,5 @@
 export type NewsModel = {
+	_id: string;
 	title: string;
 	markdown: string;
 	status: "draft" | "published" | "archived";