Procházet zdrojové kódy

refactor: Moved useForm callback promise to composable and other tweaks

Owen Diffey před 1 rokem
rodič
revize
188660611e

+ 8 - 7
frontend/src/components/UserLink.vue

@@ -1,5 +1,6 @@
 <script setup lang="ts">
 import { ref, onMounted } from "vue";
+import { useWebsocketsStore } from "@/stores/websockets";
 import { useUserAuthStore } from "@/stores/userAuth";
 
 const props = defineProps({
@@ -7,16 +8,16 @@ const props = defineProps({
 	link: { type: Boolean, default: true }
 });
 
-const user = ref({
-	name: "Unknown",
-	username: null
+const user = ref(<{ name: string; username?: string }>{
+	name: "Unknown"
 });
 
+const { socket } = useWebsocketsStore();
 const { getBasicUser } = useUserAuthStore();
 
 onMounted(() => {
-	getBasicUser(props.userId).then(
-		(basicUser: { name: string; username: string } | null) => {
+	socket.onConnect(() => {
+		getBasicUser(props.userId).then(basicUser => {
 			if (basicUser) {
 				const { name, username } = basicUser;
 				user.value = {
@@ -24,8 +25,8 @@ onMounted(() => {
 					username
 				};
 			}
-		}
-	);
+		});
+	});
 });
 </script>
 

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

@@ -68,28 +68,27 @@ const { inputs, save, setOriginalValue } = useForm(
 		status: "published",
 		showToNewUsers: false
 	},
-	(status, messages, values) =>
-		new Promise((resolve, reject) => {
-			if (status === "success") {
-				const data = {
-					title: getTitle(),
-					markdown: values.markdown,
-					status: values.status,
-					showToNewUsers: values.showToNewUsers
-				};
-				const cb = res => {
-					new Toast(res.message);
-					if (res.status === "success") resolve();
-					else reject(new Error(res.message));
-				};
-				if (createNews.value) socket.dispatch("news.create", data, cb);
-				else socket.dispatch("news.update", newsId.value, data, cb);
-			} else if (status === "unchanged") new Toast(messages.unchanged);
-			else if (status === "error")
-				Object.values(messages).forEach(message => {
-					new Toast({ content: message, timeout: 8000 });
-				});
-		}),
+	({ status, messages, values }, resolve, reject) => {
+		if (status === "success") {
+			const data = {
+				title: getTitle(),
+				markdown: values.markdown,
+				status: values.status,
+				showToNewUsers: values.showToNewUsers
+			};
+			const cb = res => {
+				new Toast(res.message);
+				if (res.status === "success") resolve();
+				else reject(new Error(res.message));
+			};
+			if (createNews.value) socket.dispatch("news.create", data, cb);
+			else socket.dispatch("news.update", newsId.value, data, cb);
+		} else if (status === "unchanged") new Toast(messages.unchanged);
+		else if (status === "error")
+			Object.values(messages).forEach(message => {
+				new Toast({ content: message, timeout: 8000 });
+			});
+	},
 	{
 		modalUuid: props.modalUuid
 	}

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

@@ -54,26 +54,25 @@ const {
 			}
 		}
 	},
-	(status, messages, values) =>
-		new Promise((resolve, reject) => {
-			if (status === "success")
-				socket.dispatch(
-					"playlists.updateDisplayName",
-					playlist.value._id,
-					values.displayName,
-					res => {
-						playlist.value.displayName = values.displayName;
-						if (res.status === "success") {
-							resolve();
-							new Toast(res.message);
-						} else reject(new Error(res.message));
-					}
-				);
-			else
-				Object.values(messages).forEach(message => {
-					new Toast({ content: message, timeout: 8000 });
-				});
-		}),
+	({ status, messages, values }, resolve, reject) => {
+		if (status === "success")
+			socket.dispatch(
+				"playlists.updateDisplayName",
+				playlist.value._id,
+				values.displayName,
+				res => {
+					playlist.value.displayName = values.displayName;
+					if (res.status === "success") {
+						resolve();
+						new Toast(res.message);
+					} else reject(new Error(res.message));
+				}
+			);
+		else
+			Object.values(messages).forEach(message => {
+				new Toast({ content: message, timeout: 8000 });
+			});
+	},
 	{
 		modalUuid: props.modalUuid,
 		preventCloseUnsaved: false
@@ -87,25 +86,24 @@ const {
 	setOriginalValue: setPrivacy
 } = useForm(
 	{ privacy: playlist.value.privacy },
-	(status, messages, values) =>
-		new Promise((resolve, reject) => {
-			if (status === "success")
-				socket.dispatch(
-					playlist.value.type === "genre"
-						? "playlists.updatePrivacyAdmin"
-						: "playlists.updatePrivacy",
-					playlist.value._id,
-					values.privacy,
-					res => {
-						playlist.value.privacy = values.privacy;
-						if (res.status === "success") {
-							resolve();
-							new Toast(res.message);
-						} else reject(new Error(res.message));
-					}
-				);
-			else if (messages[status]) new Toast(messages[status]);
-		}),
+	({ status, messages, values }, resolve, reject) => {
+		if (status === "success")
+			socket.dispatch(
+				playlist.value.type === "genre"
+					? "playlists.updatePrivacyAdmin"
+					: "playlists.updatePrivacy",
+				playlist.value._id,
+				values.privacy,
+				res => {
+					playlist.value.privacy = values.privacy;
+					if (res.status === "success") {
+						resolve();
+						new Toast(res.message);
+					} else reject(new Error(res.message));
+				}
+			);
+		else if (messages[status]) new Toast(messages[status]);
+	},
 	{
 		modalUuid: props.modalUuid,
 		preventCloseUnsaved: false

+ 37 - 41
frontend/src/components/modals/EditSong/index.vue

@@ -386,50 +386,46 @@ const { inputs, unsavedChanges, save, setValue, setOriginalValue } = useForm(
 			required: false
 		}
 	},
-	(status, messages, values) =>
-		new Promise((resolve, reject) => {
-			const saveButtonRef = saveButtonRefs.value[saveButtonRefName.value];
-			if (
-				status === "success" ||
-				(status === "unchanged" && newSong.value)
-			) {
-				const mergedValues = Object.assign(song.value, values);
-				const cb = res => {
-					if (res.status === "error") {
-						reject(new Error(res.message));
-						return;
-					}
-					new Toast(res.message);
-					saveButtonRef.handleSuccessfulSave();
-					onSavedSuccess(values.youtubeId);
-					if (newSong.value) loadSong(values.youtubeId, true);
-					else setSong(mergedValues);
-					resolve();
-				};
-				if (newSong.value)
-					socket.dispatch("songs.create", mergedValues, cb);
-				else
-					socket.dispatch(
-						"songs.update",
-						song.value._id,
-						mergedValues,
-						cb
-					);
-			} else {
-				if (status === "unchanged") {
-					new Toast(messages.unchanged);
-					saveButtonRef.handleSuccessfulSave();
-					onSavedSuccess(values.youtubeId);
-				} else {
-					Object.values(messages).forEach(message => {
-						new Toast({ content: message, timeout: 8000 });
-					});
-					saveButtonRef.handleFailedSave();
-					onSavedError(values.youtubeId);
+	({ status, messages, values }, resolve, reject) => {
+		const saveButtonRef = saveButtonRefs.value[saveButtonRefName.value];
+		if (status === "success" || (status === "unchanged" && newSong.value)) {
+			const mergedValues = Object.assign(song.value, values);
+			const cb = res => {
+				if (res.status === "error") {
+					reject(new Error(res.message));
+					return;
 				}
+				new Toast(res.message);
+				saveButtonRef.handleSuccessfulSave();
+				onSavedSuccess(values.youtubeId);
+				if (newSong.value) loadSong(values.youtubeId, true);
+				else setSong(mergedValues);
 				resolve();
+			};
+			if (newSong.value)
+				socket.dispatch("songs.create", mergedValues, cb);
+			else
+				socket.dispatch(
+					"songs.update",
+					song.value._id,
+					mergedValues,
+					cb
+				);
+		} else {
+			if (status === "unchanged") {
+				new Toast(messages.unchanged);
+				saveButtonRef.handleSuccessfulSave();
+				onSavedSuccess(values.youtubeId);
+			} else {
+				Object.values(messages).forEach(message => {
+					new Toast({ content: message, timeout: 8000 });
+				});
+				saveButtonRef.handleFailedSave();
+				onSavedError(values.youtubeId);
 			}
-		}),
+			resolve();
+		}
+	},
 	{ modalUuid: props.modalUuid, preventCloseUnsaved: false }
 );
 

+ 75 - 79
frontend/src/components/modals/EditUser.vue

@@ -47,26 +47,25 @@ const {
 			}
 		}
 	},
-	(status, messages, values) =>
-		new Promise((resolve, reject) => {
-			if (status === "success")
-				socket.dispatch(
-					"users.updateUsername",
-					user.value._id,
-					values.username,
-					res => {
-						user.value.username = values.username;
-						if (res.status === "success") {
-							resolve();
-							new Toast(res.message);
-						} else reject(new Error(res.message));
-					}
-				);
-			else
-				Object.values(messages).forEach(message => {
-					new Toast({ content: message, timeout: 8000 });
-				});
-		}),
+	({ status, messages, values }, resolve, reject) => {
+		if (status === "success")
+			socket.dispatch(
+				"users.updateUsername",
+				user.value._id,
+				values.username,
+				res => {
+					user.value.username = values.username;
+					if (res.status === "success") {
+						resolve();
+						new Toast(res.message);
+					} else reject(new Error(res.message));
+				}
+			);
+		else
+			Object.values(messages).forEach(message => {
+				new Toast({ content: message, timeout: 8000 });
+			});
+	},
 	{
 		modalUuid: props.modalUuid,
 		preventCloseUnsaved: false
@@ -95,26 +94,25 @@ const {
 			}
 		}
 	},
-	(status, messages, values) =>
-		new Promise((resolve, reject) => {
-			if (status === "success")
-				socket.dispatch(
-					"users.updateEmail",
-					user.value._id,
-					values.email,
-					res => {
-						user.value.email.address = values.email;
-						if (res.status === "success") {
-							resolve();
-							new Toast(res.message);
-						} else reject(new Error(res.message));
-					}
-				);
-			else
-				Object.values(messages).forEach(message => {
-					new Toast({ content: message, timeout: 8000 });
-				});
-		}),
+	({ status, messages, values }, resolve, reject) => {
+		if (status === "success")
+			socket.dispatch(
+				"users.updateEmail",
+				user.value._id,
+				values.email,
+				res => {
+					user.value.email.address = values.email;
+					if (res.status === "success") {
+						resolve();
+						new Toast(res.message);
+					} else reject(new Error(res.message));
+				}
+			);
+		else
+			Object.values(messages).forEach(message => {
+				new Toast({ content: message, timeout: 8000 });
+			});
+	},
 	{
 		modalUuid: props.modalUuid,
 		preventCloseUnsaved: false
@@ -128,26 +126,25 @@ const {
 	setOriginalValue: setRole
 } = useForm(
 	{ role: user.value.role },
-	(status, messages, values) =>
-		new Promise((resolve, reject) => {
-			if (status === "success")
-				socket.dispatch(
-					"users.updateRole",
-					user.value._id,
-					values.role,
-					res => {
-						user.value.role = values.role;
-						if (res.status === "success") {
-							resolve();
-							new Toast(res.message);
-						} else reject(new Error(res.message));
-					}
-				);
-			else
-				Object.values(messages).forEach(message => {
-					new Toast({ content: message, timeout: 8000 });
-				});
-		}),
+	({ status, messages, values }, resolve, reject) => {
+		if (status === "success")
+			socket.dispatch(
+				"users.updateRole",
+				user.value._id,
+				values.role,
+				res => {
+					user.value.role = values.role;
+					if (res.status === "success") {
+						resolve();
+						new Toast(res.message);
+					} else reject(new Error(res.message));
+				}
+			);
+		else
+			Object.values(messages).forEach(message => {
+				new Toast({ content: message, timeout: 8000 });
+			});
+	},
 	{
 		modalUuid: props.modalUuid,
 		preventCloseUnsaved: false
@@ -172,25 +169,24 @@ const {
 		},
 		expiresAt: "1h"
 	},
-	(status, messages, values) =>
-		new Promise((resolve, reject) => {
-			if (status === "success")
-				socket.dispatch(
-					"users.banUserById",
-					user.value._id,
-					values.reason,
-					values.expiresAt,
-					res => {
-						new Toast(res.message);
-						if (res.status === "success") resolve();
-						else reject(new Error(res.message));
-					}
-				);
-			else
-				Object.values(messages).forEach(message => {
-					new Toast({ content: message, timeout: 8000 });
-				});
-		}),
+	({ status, messages, values }, resolve, reject) => {
+		if (status === "success")
+			socket.dispatch(
+				"users.banUserById",
+				user.value._id,
+				values.reason,
+				values.expiresAt,
+				res => {
+					new Toast(res.message);
+					if (res.status === "success") resolve();
+					else reject(new Error(res.message));
+				}
+			);
+		else
+			Object.values(messages).forEach(message => {
+				new Toast({ content: message, timeout: 8000 });
+			});
+	},
 	{
 		modalUuid: props.modalUuid,
 		preventCloseUnsaved: false

+ 40 - 41
frontend/src/components/modals/ManageStation/Settings.vue

@@ -65,47 +65,46 @@ const { inputs, save, setOriginalValue } = useForm(
 		autofillLimit: station.value.autofill.limit,
 		autofillMode: station.value.autofill.mode
 	},
-	(status, messages, values) =>
-		new Promise((resolve, reject) => {
-			if (status === "success") {
-				const oldStation = JSON.parse(JSON.stringify(station.value));
-				const updatedStation = {
-					...oldStation,
-					name: values.name,
-					displayName: values.displayName,
-					description: values.description,
-					theme: values.theme,
-					privacy: values.privacy,
-					requests: {
-						...oldStation.requests,
-						enabled: values.requestsEnabled,
-						access: values.requestsAccess,
-						limit: values.requestsLimit
-					},
-					autofill: {
-						...oldStation.autofill,
-						enabled: values.autofillEnabled,
-						limit: values.autofillLimit,
-						mode: values.autofillMode
-					}
-				};
-				socket.dispatch(
-					"stations.update",
-					station.value._id,
-					updatedStation,
-					res => {
-						new Toast(res.message);
-						if (res.status === "success") {
-							editStation(updatedStation);
-							resolve();
-						} else reject(new Error(res.message));
-					}
-				);
-			} else
-				Object.values(messages).forEach(message => {
-					new Toast({ content: message, timeout: 8000 });
-				});
-		}),
+	({ status, messages, values }, resolve, reject) => {
+		if (status === "success") {
+			const oldStation = JSON.parse(JSON.stringify(station.value));
+			const updatedStation = {
+				...oldStation,
+				name: values.name,
+				displayName: values.displayName,
+				description: values.description,
+				theme: values.theme,
+				privacy: values.privacy,
+				requests: {
+					...oldStation.requests,
+					enabled: values.requestsEnabled,
+					access: values.requestsAccess,
+					limit: values.requestsLimit
+				},
+				autofill: {
+					...oldStation.autofill,
+					enabled: values.autofillEnabled,
+					limit: values.autofillLimit,
+					mode: values.autofillMode
+				}
+			};
+			socket.dispatch(
+				"stations.update",
+				station.value._id,
+				updatedStation,
+				res => {
+					new Toast(res.message);
+					if (res.status === "success") {
+						editStation(updatedStation);
+						resolve();
+					} else reject(new Error(res.message));
+				}
+			);
+		} else
+			Object.values(messages).forEach(message => {
+				new Toast({ content: message, timeout: 8000 });
+			});
+	},
 	{
 		modalUuid: props.modalUuid
 	}

+ 24 - 14
frontend/src/composables/useForm.ts

@@ -11,10 +11,14 @@ export const useForm = (
 			| any;
 	},
 	cb: (
-		status: string,
-		messages: { [key: string]: string },
-		values: { [key: string]: any }
-	) => Promise<void>,
+		response: {
+			status: string;
+			messages: { [key: string]: string };
+			values: { [key: string]: any };
+		},
+		resolve: (value?: undefined) => void,
+		reject: (value: Error) => void
+	) => void,
 	options?: {
 		modalUuid?: string;
 		preventCloseUnsaved?: boolean;
@@ -72,16 +76,22 @@ export const useForm = (
 		status: string,
 		messages?: { [key: string]: string }
 	) =>
-		cb(
-			status,
-			{ ...messages },
-			Object.fromEntries(
-				Object.entries(inputs.value).map(([name, input]) => [
-					name,
-					input.value
-				])
-			)
-		);
+		new Promise((resolve, reject: (reason: Error) => void) => {
+			cb(
+				{
+					status,
+					messages: { ...messages },
+					values: Object.fromEntries(
+						Object.entries(inputs.value).map(([name, input]) => [
+							name,
+							input.value
+						])
+					)
+				},
+				resolve,
+				reject
+			);
+		});
 
 	const resetOriginalValues = () => {
 		inputs.value = Object.fromEntries(

+ 1 - 1
frontend/src/pages/Profile/Tabs/RecentActivity.vue

@@ -74,7 +74,7 @@ onMounted(() => {
 
 	socket.onConnect(() => {
 		if (myUserId.value !== props.userId)
-			getBasicUser(props.userId).then((user: any) => {
+			getBasicUser(props.userId).then(user => {
 				if (user && user.username) username.value = user.username;
 			});
 

+ 54 - 37
frontend/src/stores/userAuth.ts

@@ -5,9 +5,16 @@ import { useWebsocketsStore } from "@/stores/websockets";
 
 export const useUserAuthStore = defineStore("userAuth", {
 	state: () => ({
-		userIdMap: {},
-		userIdRequested: {},
-		pendingUserIdCallbacks: {},
+		userIdMap: <{ [key: string]: { name: string; username: string } }>{},
+		userIdRequested: <{ [key: string]: boolean }>{},
+		pendingUserIdCallbacks: <
+			{
+				[key: string]: ((basicUser: {
+					name: string;
+					username: string;
+				}) => void)[];
+			}
+		>{},
 		loggedIn: false,
 		role: "",
 		username: "",
@@ -174,44 +181,51 @@ export const useUserAuthStore = defineStore("userAuth", {
 				});
 			});
 		},
-		getBasicUser(userId) {
-			return new Promise(resolve => {
-				if (typeof this.userIdMap[`Z${userId}`] !== "string") {
-					if (this.userIdRequested[`Z${userId}`] !== true) {
-						this.requestingUserId(userId);
-						const { socket } = useWebsocketsStore();
-						socket.dispatch("users.getBasicUser", userId, res => {
-							if (res.status === "success") {
-								const user = res.data;
+		getBasicUser(userId: string) {
+			return new Promise(
+				(
+					resolve: (
+						basicUser: { name: string; username: string } | null
+					) => void
+				) => {
+					if (typeof this.userIdMap[`Z${userId}`] !== "string") {
+						if (this.userIdRequested[`Z${userId}`] !== true) {
+							this.requestingUserId(userId);
+							const { socket } = useWebsocketsStore();
+							socket.dispatch(
+								"users.getBasicUser",
+								userId,
+								res => {
+									if (res.status === "success") {
+										const user = res.data;
 
-								this.mapUserId({
-									userId,
-									user: {
-										name: user.name,
-										username: user.username
-									}
-								});
+										this.mapUserId({
+											userId,
+											user: {
+												name: user.name,
+												username: user.username
+											}
+										});
 
-								this.pendingUserIdCallbacks[
-									`Z${userId}`
-								].forEach(cb => cb(user));
+										this.pendingUserIdCallbacks[
+											`Z${userId}`
+										].forEach(cb => cb(user));
 
-								this.clearPendingCallbacks(userId);
+										this.clearPendingCallbacks(userId);
 
-								return resolve(user);
-							}
-							return resolve(null);
-						});
+										return resolve(user);
+									}
+									return resolve(null);
+								}
+							);
+						} else {
+							this.pendingUser(userId, user => resolve(user));
+						}
 					} else {
-						this.pendingUser({
-							userId,
-							callback: user => resolve(user)
-						});
+						resolve(this.userIdMap[`Z${userId}`]);
 					}
-				} else {
-					resolve(this.userIdMap[`Z${userId}`]);
 				}
-			});
+			);
 		},
 		mapUserId(data) {
 			this.userIdMap[`Z${data.userId}`] = data.user;
@@ -222,10 +236,13 @@ export const useUserAuthStore = defineStore("userAuth", {
 			if (!this.pendingUserIdCallbacks[`Z${userId}`])
 				this.pendingUserIdCallbacks[`Z${userId}`] = [];
 		},
-		pendingUser(data) {
-			this.pendingUserIdCallbacks[`Z${data.userId}`].push(data.callback);
+		pendingUser(
+			userId: string,
+			callback: (basicUser: { name: string; username: string }) => void
+		) {
+			this.pendingUserIdCallbacks[`Z${userId}`].push(callback);
 		},
-		clearPendingCallbacks(userId) {
+		clearPendingCallbacks(userId: string) {
 			this.pendingUserIdCallbacks[`Z${userId}`] = [];
 		},
 		authData(data) {

+ 1 - 1
frontend/vite.config.js

@@ -154,7 +154,7 @@ export default {
 		]
 	},
 	define: {
-		__VUE_PROD_DEVTOOLS__: false,
+		__VUE_PROD_DEVTOOLS__: config.get("mode") === "development",
 		MUSARE_VERSION: JSON.stringify(debug.version),
 		MUSARE_GIT_REMOTE: JSON.stringify(debug.git.remote),
 		MUSARE_GIT_REMOTE_URL: JSON.stringify(debug.git.remoteUrl),