Browse Source

refactor(ResetPassword): Converted to composition API

Owen Diffey 2 years ago
parent
commit
7d511bcaf8
1 changed files with 231 additions and 205 deletions
  1. 231 205
      frontend/src/pages/ResetPassword.vue

+ 231 - 205
frontend/src/pages/ResetPassword.vue

@@ -1,3 +1,195 @@
+<script setup lang="ts">
+import { useStore } from "vuex";
+import { defineAsyncComponent, ref, computed, watch, onMounted } from "vue";
+import Toast from "toasters";
+import validation from "@/validation";
+
+const InputHelpBox = defineAsyncComponent(
+	() => import("@/components/InputHelpBox.vue")
+);
+
+const props = defineProps({
+	mode: { type: String, enum: ["reset", "set"], default: "reset" }
+});
+
+const store = useStore();
+
+const accountEmail = computed(() => store.state.user.auth.email);
+
+const { socket } = store.state.websockets;
+
+const code = ref("");
+const inputs = ref({
+	email: {
+		value: "",
+		hasBeenSentAlready: true,
+		entered: false,
+		valid: false,
+		message: "Please enter a valid email address."
+	},
+	password: {
+		value: "",
+		visible: false,
+		entered: false,
+		valid: false,
+		message:
+			"Include at least one lowercase letter, one uppercase letter, one number and one special character."
+	},
+	passwordAgain: {
+		value: "",
+		visible: false,
+		entered: false,
+		valid: false,
+		message: "This password must match."
+	}
+});
+const step = ref(1);
+const inputElements = ref([]);
+
+const togglePasswordVisibility = input => {
+	if (inputElements.value[input].type === "password") {
+		inputElements.value[input].type = "text";
+		inputs.value[input].visible = true;
+	} else {
+		inputElements.value[input].type = "password";
+		inputs.value[input].visible = false;
+	}
+};
+
+const checkPasswordMatch = (pass, passAgain) => {
+	if (passAgain !== pass) {
+		inputs.value.passwordAgain.message = "This password must match.";
+		inputs.value.passwordAgain.valid = false;
+	} else {
+		inputs.value.passwordAgain.message = "Everything looks great!";
+		inputs.value.passwordAgain.valid = true;
+	}
+};
+
+const onInput = inputName => {
+	inputs.value[inputName].entered = true;
+};
+
+const submitEmail = () => {
+	if (
+		inputs.value.email.value.indexOf("@") !==
+			inputs.value.email.value.lastIndexOf("@") ||
+		!validation.regex.emailSimple.test(inputs.value.email.value)
+	)
+		return new Toast("Invalid email format.");
+
+	if (!inputs.value.email.value) return new Toast("Email cannot be empty");
+
+	inputs.value.email.hasBeenSentAlready = false;
+
+	if (props.mode === "set") {
+		return socket.dispatch("users.requestPassword", res => {
+			new Toast(res.message);
+			if (res.status === "success") step.value = 2;
+		});
+	}
+
+	return socket.dispatch(
+		"users.requestPasswordReset",
+		inputs.value.email.value,
+		res => {
+			new Toast(res.message);
+			if (res.status === "success") {
+				code.value = ""; // in case: already have a code -> request another code
+				step.value = 2;
+			} else step.value = 5;
+		}
+	);
+};
+
+const verifyCode = () => {
+	if (!code.value) return new Toast("Code cannot be empty");
+
+	return socket.dispatch(
+		props.mode === "set"
+			? "users.verifyPasswordCode"
+			: "users.verifyPasswordResetCode",
+		code.value,
+		res => {
+			new Toast(res.message);
+			if (res.status === "success") step.value = 3;
+		}
+	);
+};
+
+const changePassword = () => {
+	if (inputs.value.password.valid && !inputs.value.passwordAgain.valid)
+		return new Toast("Please ensure the passwords match.");
+
+	if (!inputs.value.password.valid)
+		return new Toast("Please enter a valid password.");
+
+	return socket.dispatch(
+		props.mode === "set"
+			? "users.changePasswordWithCode"
+			: "users.changePasswordWithResetCode",
+		code.value,
+		inputs.value.password.value,
+		res => {
+			new Toast(res.message);
+			if (res.status === "success") step.value = 4;
+			else step.value = 5;
+		}
+	);
+};
+
+watch(
+	() => inputs.value.email.value,
+	value => {
+		if (!value) return;
+
+		if (
+			value.indexOf("@") !== value.lastIndexOf("@") ||
+			!validation.regex.emailSimple.test(value)
+		) {
+			inputs.value.email.message = "Please enter a valid email address.";
+			inputs.value.email.valid = false;
+		} else {
+			inputs.value.email.message = "Everything looks great!";
+			inputs.value.email.valid = true;
+		}
+	}
+);
+watch(
+	() => inputs.value.password.value,
+	value => {
+		if (!value) return;
+
+		checkPasswordMatch(value, inputs.value.passwordAgain.value);
+
+		if (!validation.isLength(value, 6, 200)) {
+			inputs.value.password.message =
+				"Password must have between 6 and 200 characters.";
+			inputs.value.password.valid = false;
+		} else if (!validation.regex.password.test(value)) {
+			inputs.value.password.message =
+				"Include at least one lowercase letter, one uppercase letter, one number and one special character.";
+			inputs.value.password.valid = false;
+		} else {
+			inputs.value.password.message = "Everything looks great!";
+			inputs.value.password.valid = true;
+		}
+	}
+);
+watch(
+	() => inputs.value.passwordAgain.value,
+	value => {
+		if (!value) return;
+
+		checkPasswordMatch(inputs.value.password.value, value);
+	}
+);
+
+onMounted(() => {
+	inputs.value.email.value = accountEmail.value;
+});
+</script>
+
 <template>
 	<div>
 		<page-metadata
@@ -45,9 +237,15 @@
 											<input
 												class="input"
 												type="email"
+												:ref="
+													el =>
+														(inputElements[
+															'email'
+														] = el)
+												"
 												placeholder="Enter email address here..."
 												autofocus
-												v-model="email.value"
+												v-model="inputs.email.value"
 												@keyup.enter="submitEmail()"
 												@keypress="onInput('email')"
 												@paste="onInput('email')"
@@ -67,9 +265,9 @@
 									</div>
 									<transition name="fadein-helpbox">
 										<input-help-box
-											:entered="email.entered"
-											:valid="email.valid"
-											:message="email.message"
+											:entered="inputs.email.entered"
+											:valid="inputs.email.valid"
+											:message="inputs.email.message"
 										/>
 									</transition>
 								</div>
@@ -82,16 +280,16 @@
 								</h2>
 								<p
 									class="content-box-description"
-									v-if="!email.hasBeenSentAlready"
+									v-if="!inputs.email.hasBeenSentAlready"
 								>
 									A code has been sent to
-									<strong>{{ email.value }}.</strong>
+									<strong>{{ inputs.email.value }}.</strong>
 								</p>
 
 								<p class="content-box-optional-helper">
 									<a
 										@click="
-											email.value
+											inputs.email.value
 												? submitEmail()
 												: (step = 1)
 										"
@@ -149,9 +347,13 @@
 											class="input"
 											id="new-password"
 											type="password"
-											ref="password"
+											:ref="
+												el =>
+													(inputElements['password'] =
+														el)
+											"
 											placeholder="Enter password here..."
-											v-model="password.value"
+											v-model="inputs.password.value"
 											@keypress="onInput('password')"
 											@paste="onInput('password')"
 										/>
@@ -164,7 +366,7 @@
 										>
 											<i class="material-icons">
 												{{
-													!password.visible
+													!inputs.password.visible
 														? "visibility"
 														: "visibility_off"
 												}}
@@ -174,9 +376,9 @@
 
 									<transition name="fadein-helpbox">
 										<input-help-box
-											:entered="password.entered"
-											:valid="password.valid"
-											:message="password.message"
+											:entered="inputs.password.entered"
+											:valid="inputs.password.valid"
+											:message="inputs.password.message"
 										/>
 									</transition>
 
@@ -194,9 +396,14 @@
 											class="input"
 											id="new-password-again"
 											type="password"
-											ref="passwordAgain"
+											:ref="
+												el =>
+													(inputElements[
+														'passwordAgain'
+													] = el)
+											"
 											placeholder="Enter password here..."
-											v-model="passwordAgain.value"
+											v-model="inputs.passwordAgain.value"
 											@keyup.enter="changePassword()"
 											@keypress="onInput('passwordAgain')"
 											@paste="onInput('passwordAgain')"
@@ -210,7 +417,8 @@
 										>
 											<i class="material-icons">
 												{{
-													!passwordAgain.visible
+													!inputs.passwordAgain
+														.visible
 														? "visibility"
 														: "visibility_off"
 												}}
@@ -220,9 +428,13 @@
 
 									<transition name="fadein-helpbox">
 										<input-help-box
-											:entered="passwordAgain.entered"
-											:valid="passwordAgain.valid"
-											:message="passwordAgain.message"
+											:entered="
+												inputs.passwordAgain.entered
+											"
+											:valid="inputs.passwordAgain.valid"
+											:message="
+												inputs.passwordAgain.message
+											"
 										/>
 									</transition>
 
@@ -281,192 +493,6 @@
 	</div>
 </template>
 
-<script>
-import Toast from "toasters";
-import { mapGetters, mapState } from "vuex";
-
-import InputHelpBox from "@/components/InputHelpBox.vue";
-
-import validation from "@/validation";
-
-export default {
-	components: { InputHelpBox },
-	props: {
-		mode: {
-			default: "reset",
-			enum: ["reset", "set"],
-			type: String
-		}
-	},
-	data() {
-		return {
-			code: "",
-			email: {
-				value: "",
-				hasBeenSentAlready: true,
-				entered: false,
-				valid: false,
-				message: "Please enter a valid email address."
-			},
-			password: {
-				value: "",
-				visible: false,
-				entered: false,
-				valid: false,
-				message:
-					"Include at least one lowercase letter, one uppercase letter, one number and one special character."
-			},
-			passwordAgain: {
-				value: "",
-				visible: false,
-				entered: false,
-				valid: false,
-				message: "This password must match."
-			},
-			step: 1
-		};
-	},
-	computed: {
-		...mapGetters({
-			socket: "websockets/getSocket"
-		}),
-		...mapState({
-			accountEmail: state => state.user.auth.email
-		})
-	},
-	watch: {
-		"email.value": function watchEmail(value) {
-			if (!value) return;
-
-			if (
-				value.indexOf("@") !== value.lastIndexOf("@") ||
-				!validation.regex.emailSimple.test(value)
-			) {
-				this.email.message = "Please enter a valid email address.";
-				this.email.valid = false;
-			} else {
-				this.email.message = "Everything looks great!";
-				this.email.valid = true;
-			}
-		},
-		"password.value": function watchPassword(value) {
-			if (!value) return;
-
-			this.checkPasswordMatch(value, this.passwordAgain.value);
-
-			if (!validation.isLength(value, 6, 200)) {
-				this.password.message =
-					"Password must have between 6 and 200 characters.";
-				this.password.valid = false;
-			} else if (!validation.regex.password.test(value)) {
-				this.password.message =
-					"Include at least one lowercase letter, one uppercase letter, one number and one special character.";
-				this.password.valid = false;
-			} else {
-				this.password.message = "Everything looks great!";
-				this.password.valid = true;
-			}
-		},
-		"passwordAgain.value": function watchPasswordAgain(value) {
-			if (!value) return;
-
-			this.checkPasswordMatch(this.password.value, value);
-		}
-	},
-	mounted() {
-		this.email.value = this.accountEmail;
-	},
-	methods: {
-		togglePasswordVisibility(ref) {
-			if (this.$refs[ref].type === "password") {
-				this.$refs[ref].type = "text";
-				this[ref].visible = true;
-			} else {
-				this.$refs[ref].type = "password";
-				this[ref].visible = false;
-			}
-		},
-		checkPasswordMatch(password, passwordAgain) {
-			if (passwordAgain !== password) {
-				this.passwordAgain.message = "This password must match.";
-				this.passwordAgain.valid = false;
-			} else {
-				this.passwordAgain.message = "Everything looks great!";
-				this.passwordAgain.valid = true;
-			}
-		},
-		onInput(inputName) {
-			this[inputName].entered = true;
-		},
-		submitEmail() {
-			if (
-				this.email.value.indexOf("@") !==
-					this.email.value.lastIndexOf("@") ||
-				!validation.regex.emailSimple.test(this.email.value)
-			)
-				return new Toast("Invalid email format.");
-
-			if (!this.email.value) return new Toast("Email cannot be empty");
-
-			this.email.hasBeenSentAlready = false;
-
-			if (this.mode === "set") {
-				return this.socket.dispatch("users.requestPassword", res => {
-					new Toast(res.message);
-					if (res.status === "success") this.step = 2;
-				});
-			}
-
-			return this.socket.dispatch(
-				"users.requestPasswordReset",
-				this.email.value,
-				res => {
-					new Toast(res.message);
-					if (res.status === "success") {
-						this.code = ""; // in case: already have a code -> request another code
-						this.step = 2;
-					} else this.step = 5;
-				}
-			);
-		},
-		verifyCode() {
-			if (!this.code) return new Toast("Code cannot be empty");
-
-			return this.socket.dispatch(
-				this.mode === "set"
-					? "users.verifyPasswordCode"
-					: "users.verifyPasswordResetCode",
-				this.code,
-				res => {
-					new Toast(res.message);
-					if (res.status === "success") this.step = 3;
-				}
-			);
-		},
-		changePassword() {
-			if (this.password.valid && !this.passwordAgain.valid)
-				return new Toast("Please ensure the passwords match.");
-
-			if (!this.password.valid)
-				return new Toast("Please enter a valid password.");
-
-			return this.socket.dispatch(
-				this.mode === "set"
-					? "users.changePasswordWithCode"
-					: "users.changePasswordWithResetCode",
-				this.code,
-				this.password.value,
-				res => {
-					new Toast(res.message);
-					if (res.status === "success") this.step = 4;
-					else this.step = 5;
-				}
-			);
-		}
-	}
-};
-</script>
-
 <style lang="less" scoped>
 .night-mode {
 	.label {