ソースを参照

Merge branch 'polishing' of https://github.com/Musare/MusareNode into polishing

Kristian Vos 4 年 前
コミット
8b0757a5e4

+ 21 - 7
frontend/src/components/InputHelpBox.vue

@@ -1,5 +1,14 @@
 <template>
-	<p class="help" :class="valid ? 'is-success' : 'is-danger'">
+	<p
+		class="help"
+		:class="
+			entered || entered === undefined
+				? valid
+					? 'is-success'
+					: 'is-danger'
+				: 'is-grey'
+		"
+	>
 		{{ message }}
 	</p>
 </template>
@@ -14,19 +23,24 @@ export default {
 		valid: {
 			type: Boolean,
 			required: true
+		},
+		entered: {
+			type: Boolean,
+			default: undefined,
+			required: false
 		}
-	},
-	data() {
-		return {};
-	},
-	methods: {}
+	}
 };
 </script>
 
 <style lang="scss" scoped>
 .help {
 	margin-top: 0 !important;
-	margin-bottom: 5px !important;
+	margin-bottom: 10px !important;
 	font-size: 12px;
 }
+
+.is-grey {
+	color: var(--dark-grey-1);
+}
 </style>

+ 168 - 86
frontend/src/components/modals/Login.vue

@@ -1,79 +1,118 @@
 <template>
-	<div class="modal is-active">
-		<div
-			class="modal-background"
-			@click="
-				closeModal({
-					sector: 'header',
-					modal: 'login'
-				})
-			"
-		/>
-		<div class="modal-card">
-			<header class="modal-card-head">
-				<p class="modal-card-title">Login</p>
-				<button
-					class="delete"
-					@click="
-						closeModal({
-							sector: 'header',
-							modal: 'login'
-						})
-					"
-				/>
-			</header>
-			<section class="modal-card-body">
-				<!-- validation to check if exists http://bulma.io/documentation/elements/form/ -->
-				<form>
-					<p class="control">
-						<label class="label">Email</label>
-						<input
-							v-model="email"
-							class="input"
-							type="email"
-							placeholder="Email..."
-						/>
-					</p>
-					<p class="control">
-						<label class="label">Password</label>
-						<input
-							v-model="password"
-							class="input"
-							type="password"
-							placeholder="Password..."
-							@keypress="
-								$parent.submitOnEnter(submitModal, $event)
-							"
-						/>
-					</p>
-					<br />
-					<p>
-						By logging in/registering you agree to our
-						<router-link to="/terms"> Terms of Service </router-link
-						>&nbsp;and
-						<router-link to="/privacy"> Privacy Policy </router-link
-						>.
-					</p>
-				</form>
-			</section>
-			<footer class="modal-card-foot">
-				<a class="button is-primary" href="#" @click="submitModal()"
-					>Submit</a
-				>
-				<a
-					class="button is-github"
-					:href="apiDomain + '/auth/github/authorize'"
-					@click="githubRedirect()"
-				>
-					<div class="icon">
-						<img class="invert" src="/assets/social/github.svg" />
+	<div>
+		<metadata title="Login" v-if="isPage" />
+		<div class="modal is-active">
+			<div class="modal-background" @click="closeLoginModal()" />
+			<div class="modal-card">
+				<header class="modal-card-head">
+					<p class="modal-card-title">Login</p>
+					<button
+						v-if="!isPage"
+						class="delete"
+						@click="closeLoginModal()"
+					/>
+				</header>
+
+				<section class="modal-card-body">
+					<form>
+						<!-- email address -->
+						<p class="control">
+							<label class="label">Email</label>
+							<input
+								v-model="email"
+								class="input"
+								type="email"
+								placeholder="Email..."
+							/>
+						</p>
+
+						<!-- password -->
+						<p class="control">
+							<label class="label">Password</label>
+						</p>
+
+						<div id="password-container">
+							<input
+								v-model="password.value"
+								class="input"
+								type="password"
+								ref="password"
+								placeholder="Password..."
+								@keypress="
+									$parent.submitOnEnter(submitModal, $event)
+								"
+							/>
+							<a @click="togglePasswordVisibility()">
+								<i class="material-icons">
+									{{
+										!password.visible
+											? "visibility"
+											: "visibility_off"
+									}}
+								</i>
+							</a>
+						</div>
+
+						<router-link
+							id="forgot-password"
+							href="#"
+							to="/reset_password"
+							@click.native="closeLoginModal()"
+						>
+							Forgot password?
+						</router-link>
+
+						<br />
+						<p>
+							By logging in you agree to our
+							<router-link
+								to="/terms"
+								@click.native="closeLoginModal()"
+							>
+								Terms of Service
+							</router-link>
+							&nbsp;and
+							<router-link
+								to="/privacy"
+								@click.native="closeLoginModal()"
+							>
+								Privacy Policy </router-link
+							>.
+						</p>
+					</form>
+				</section>
+
+				<footer class="modal-card-foot">
+					<router-link to="/register" v-if="isPage">
+						Don't have an account?
+					</router-link>
+					<a v-else href="#" @click="changeToRegisterModal()">
+						Don't have an account?
+					</a>
+
+					<div id="actions">
+						<a
+							class="button is-github"
+							:href="apiDomain + '/auth/github/authorize'"
+							@click="githubRedirect()"
+						>
+							<div class="icon">
+								<img
+									class="invert"
+									src="/assets/social/github.svg"
+								/>
+							</div>
+							&nbsp;&nbsp;Login with GitHub
+						</a>
+						<a
+							class="button is-primary"
+							href="#"
+							@click="submitModal()"
+							>Login</a
+						>
 					</div>
-					&nbsp;&nbsp;Login with GitHub
-				</a>
-				<a href="/reset_password" @click="resetPassword()"
-					>Forgot password?</a
-				>
-			</footer>
+				</footer>
+			</div>
 		</div>
 	</div>
 </template>
@@ -87,37 +126,53 @@ export default {
 	data() {
 		return {
 			email: "",
-			password: "",
-			apiDomain: ""
+			password: {
+				value: "",
+				visible: false
+			},
+			apiDomain: "",
+			isPage: false
 		};
 	},
 	async mounted() {
 		this.apiDomain = await lofig.get("apiDomain");
+
+		if (this.$route.path === "/login") this.isPage = true;
 	},
 	methods: {
 		submitModal() {
 			this.login({
 				email: this.email,
-				password: this.password
+				password: this.password.value
 			})
 				.then(res => {
 					if (res.status === "success") window.location.reload();
 				})
 				.catch(err => new Toast(err.message));
 		},
-		resetPassword() {
-			this.closeModal({ sector: "header", modal: "login" });
-			this.$router.go("/reset_password");
+		togglePasswordVisibility() {
+			if (this.$refs.password.type === "password") {
+				this.$refs.password.type = "text";
+				this.password.visible = true;
+			} else {
+				this.$refs.password.type = "password";
+				this.password.visible = false;
+			}
+		},
+		changeToRegisterModal() {
+			if (!this.isPage) {
+				this.closeLoginModal();
+				this.openModal({ sector: "header", modal: "register" });
+			}
+		},
+		closeLoginModal() {
+			if (!this.isPage)
+				this.closeModal({ sector: "header", modal: "login" });
 		},
 		githubRedirect() {
 			localStorage.setItem("github_redirect", this.$route.path);
-			console.log(
-				"Yes",
-				this.$route.path,
-				localStorage.getItem("github_redirect")
-			);
 		},
-		...mapActions("modalVisibility", ["closeModal"]),
+		...mapActions("modalVisibility", ["closeModal", "openModal"]),
 		...mapActions("user/auth", ["login"])
 	}
 };
@@ -138,6 +193,33 @@ export default {
 	}
 }
 
+#password-container {
+	display: flex;
+	align-items: center;
+
+	a {
+		width: 0;
+		margin-left: -30px;
+		z-index: 0;
+		top: 2px;
+		position: relative;
+		color: var(--light-grey-1);
+	}
+}
+
+#forgot-password {
+	display: flex;
+	justify-content: flex-end;
+	height: 0;
+	margin-top: 5px;
+}
+
+.modal-card-foot {
+	display: flex;
+	justify-content: space-between;
+	flex-wrap: wrap;
+}
+
 .button.is-github {
 	background-color: var(--dark-grey-2);
 	color: var(--white) !important;

+ 189 - 106
frontend/src/components/modals/Register.vue

@@ -1,108 +1,143 @@
 <template>
-	<div class="modal is-active">
-		<div
-			class="modal-background"
-			@click="
-				closeModal({
-					sector: 'header',
-					modal: 'register'
-				})
-			"
-		/>
-		<div class="modal-card">
-			<header class="modal-card-head">
-				<p class="modal-card-title">Register</p>
-				<button
-					class="delete"
-					@click="
-						closeModal({
-							sector: 'header',
-							modal: 'register'
-						})
-					"
-				/>
-			</header>
-			<section class="modal-card-body">
-				<p class="control">
-					<label class="label">Email</label>
-					<input
-						v-model="email.value"
-						class="input"
-						type="email"
-						placeholder="Email..."
-						@blur="onInputBlur('email')"
-						autofocus
+	<div>
+		<metadata title="Register" v-if="isPage" />
+		<div class="modal is-active">
+			<div class="modal-background" @click="closeRegisterModal()" />
+			<div class="modal-card">
+				<header class="modal-card-head">
+					<p class="modal-card-title">Register</p>
+					<button
+						v-if="!isPage"
+						class="delete"
+						@click="closeRegisterModal()"
 					/>
-				</p>
-				<transition name="fadein-helpbox">
-					<input-help-box
-						v-if="email.entered"
-						:valid="email.valid"
-						:message="email.message"
-					></input-help-box>
-				</transition>
+				</header>
+				<section class="modal-card-body">
+					<!-- email address -->
+					<p class="control">
+						<label class="label">Email</label>
+						<input
+							v-model="email.value"
+							class="input"
+							type="email"
+							placeholder="Email..."
+							@keypress="onInput('email')"
+							autofocus
+						/>
+					</p>
+					<transition name="fadein-helpbox">
+						<input-help-box
+							:entered="email.entered"
+							:valid="email.valid"
+							:message="email.message"
+						></input-help-box>
+					</transition>
 
-				<p class="control">
-					<label class="label">Username</label>
-					<input
-						v-model="username.value"
-						class="input"
-						type="text"
-						placeholder="Username..."
-						@blur="onInputBlur('username')"
-					/>
-				</p>
-				<transition name="fadein-helpbox">
-					<input-help-box
-						v-if="username.entered"
-						:valid="username.valid"
-						:message="username.message"
-					></input-help-box>
-				</transition>
+					<!-- username -->
+					<p class="control">
+						<label class="label">Username</label>
+						<input
+							v-model="username.value"
+							class="input"
+							type="text"
+							placeholder="Username..."
+							@keypress="onInput('username')"
+						/>
+					</p>
+					<transition name="fadein-helpbox">
+						<input-help-box
+							:entered="username.entered"
+							:valid="username.valid"
+							:message="username.message"
+						></input-help-box>
+					</transition>
 
-				<p class="control">
-					<label class="label">Password</label>
-					<input
-						v-model="password.value"
-						class="input"
-						type="password"
-						placeholder="Password..."
-						@blur="onInputBlur('password')"
-						@keypress="$parent.submitOnEnter(submitModal, $event)"
-					/>
-				</p>
-				<transition name="fadein-helpbox">
-					<input-help-box
-						v-if="password.entered"
-						:valid="password.valid"
-						:message="password.message"
-					></input-help-box>
-				</transition>
+					<!-- password -->
+					<p class="control">
+						<label class="label">Password</label>
+					</p>
+
+					<div id="password-container">
+						<input
+							v-model="password.value"
+							class="input"
+							type="password"
+							ref="password"
+							placeholder="Password..."
+							@keypress="
+								onInput('password') &&
+									$parent.submitOnEnter(submitModal, $event)
+							"
+						/>
+						<a @click="togglePasswordVisibility()">
+							<i class="material-icons">
+								{{
+									!password.visible
+										? "visibility"
+										: "visibility_off"
+								}}
+							</i>
+						</a>
+					</div>
+
+					<transition name="fadein-helpbox">
+						<input-help-box
+							:valid="password.valid"
+							:entered="password.entered"
+							:message="password.message"
+						></input-help-box>
+					</transition>
 
-				<br />
+					<br />
 
-				<p>
-					By logging in/registering you agree to our
-					<router-link to="/terms"> Terms of Service </router-link
-					>&nbsp;and
-					<router-link to="/privacy"> Privacy Policy </router-link>.
-				</p>
-			</section>
-			<footer class="modal-card-foot">
-				<a class="button is-primary" href="#" @click="submitModal()"
-					>Submit</a
-				>
-				<a
-					class="button is-github"
-					:href="apiDomain + '/auth/github/authorize'"
-					@click="githubRedirect()"
-				>
-					<div class="icon">
-						<img class="invert" src="/assets/social/github.svg" />
+					<p>
+						By registering you agree to our
+						<router-link
+							to="/terms"
+							@click.native="closeRegisterModal()"
+						>
+							Terms of Service
+						</router-link>
+						&nbsp;and
+						<router-link
+							to="/privacy"
+							@click.native="closeRegisterModal()"
+						>
+							Privacy Policy </router-link
+						>.
+					</p>
+				</section>
+				<footer class="modal-card-foot">
+					<router-link to="/login" v-if="isPage">
+						Already have an account?
+					</router-link>
+					<a v-else href="#" @click="changeToLoginModal()">
+						Already have an account?
+					</a>
+
+					<div id="actions">
+						<a
+							class="button is-github"
+							:href="apiDomain + '/auth/github/authorize'"
+							@click="githubRedirect()"
+						>
+							<div class="icon">
+								<img
+									class="invert"
+									src="/assets/social/github.svg"
+								/>
+							</div>
+							&nbsp;&nbsp;Register with GitHub
+						</a>
+						<a
+							class="button is-primary"
+							href="#"
+							@click="submitModal()"
+							>Register</a
+						>
 					</div>
-					&nbsp;&nbsp;Register with GitHub
-				</a>
-			</footer>
+				</footer>
+			</div>
 		</div>
 	</div>
 </template>
@@ -122,7 +157,7 @@ export default {
 				value: "",
 				valid: false,
 				entered: false,
-				message: "Please enter a valid username."
+				message: "Only letters, numbers and underscores are allowed."
 			},
 			email: {
 				value: "",
@@ -134,14 +169,17 @@ export default {
 				value: "",
 				valid: false,
 				entered: false,
-				message: "Please enter a valid password."
+				visible: false,
+				message:
+					"Include at least one lowercase letter, one uppercase letter, one number and one special character."
 			},
 			recaptcha: {
 				key: "",
 				token: "",
 				enabled: false
 			},
-			apiDomain: ""
+			apiDomain: "",
+			isPage: false
 		};
 	},
 	watch: {
@@ -153,7 +191,7 @@ export default {
 				this.username.valid = false;
 			} else if (!validation.regex.azAZ09_.test(value)) {
 				this.username.message =
-					"Invalid username format. Allowed characters: a-z, A-Z, 0-9 and _.";
+					"Invalid format. Allowed characters: a-z, A-Z, 0-9 and _.";
 				this.username.valid = false;
 			} else {
 				this.username.message = "Everything looks great!";
@@ -170,7 +208,7 @@ export default {
 				value.indexOf("@") !== value.lastIndexOf("@") ||
 				!validation.regex.emailSimple.test(value)
 			) {
-				this.email.message = "Invalid Email format.";
+				this.email.message = "Invalid format.";
 				this.email.valid = false;
 			} else {
 				this.email.message = "Everything looks great!";
@@ -185,7 +223,7 @@ export default {
 				this.password.valid = false;
 			} else if (!validation.regex.password.test(value)) {
 				this.password.message =
-					"Invalid password format. Must have one lowercase letter, one uppercase letter, one number and one special character.";
+					"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!";
@@ -194,6 +232,8 @@ export default {
 		}
 	},
 	async mounted() {
+		if (this.$route.path === "/register") this.isPage = true;
+
 		this.apiDomain = await lofig.get("apiDomain");
 
 		lofig.get("recaptcha").then(obj => {
@@ -221,6 +261,25 @@ export default {
 		});
 	},
 	methods: {
+		togglePasswordVisibility() {
+			if (this.$refs.password.type === "password") {
+				this.$refs.password.type = "text";
+				this.password.visible = true;
+			} else {
+				this.$refs.password.type = "password";
+				this.password.visible = false;
+			}
+		},
+		changeToLoginModal() {
+			if (!this.isPage) {
+				this.closeRegisterModal();
+				this.openModal({ sector: "header", modal: "login" });
+			}
+		},
+		closeRegisterModal() {
+			if (!this.isPage)
+				this.closeModal({ sector: "header", modal: "login" });
+		},
 		submitModal() {
 			if (
 				!this.username.valid ||
@@ -240,13 +299,13 @@ export default {
 				})
 				.catch(err => new Toast(err.message));
 		},
-		onInputBlur(inputName) {
+		onInput(inputName) {
 			this[inputName].entered = true;
 		},
 		githubRedirect() {
 			localStorage.setItem("github_redirect", this.$route.path);
 		},
-		...mapActions("modalVisibility", ["closeModal"]),
+		...mapActions("modalVisibility", ["closeModal", "openModal"]),
 		...mapActions("user/auth", ["register"])
 	}
 };
@@ -267,6 +326,30 @@ export default {
 	}
 }
 
+#password-container {
+	display: flex;
+	align-items: center;
+
+	a {
+		width: 0;
+		margin-left: -30px;
+		z-index: 0;
+		top: 2px;
+		position: relative;
+		color: var(--light-grey-1);
+	}
+}
+
+.control {
+	margin-bottom: 2px !important;
+}
+
+.modal-card-foot {
+	display: flex;
+	justify-content: space-between;
+	flex-wrap: wrap;
+}
+
 .button.is-github {
 	background-color: var(--dark-grey-2);
 	color: var(--white) !important;

+ 15 - 3
frontend/src/main.js

@@ -129,11 +129,17 @@ const router = new VueRouter({
 		},
 		{
 			path: "/login",
-			component: () => import("@/components/modals/Login.vue")
+			component: () => import("@/components/modals/Login.vue"),
+			meta: {
+				guestsOnly: true
+			}
 		},
 		{
 			path: "/register",
-			component: () => import("@/components/modals/Register.vue")
+			component: () => import("@/components/modals/Register.vue"),
+			meta: {
+				guestsOnly: true
+			}
 		},
 		{
 			path: "/admin",
@@ -226,7 +232,11 @@ lofig.folder = "../config/default.json";
 
 		ws.clear();
 
-		if (to.meta.loginRequired || to.meta.adminRequired) {
+		if (
+			to.meta.loginRequired ||
+			to.meta.adminRequired ||
+			to.meta.guestsOnly
+		) {
 			const gotData = () => {
 				if (to.meta.loginRequired && !store.state.user.auth.loggedIn)
 					next({ path: "/login" });
@@ -235,6 +245,8 @@ lofig.folder = "../config/default.json";
 					store.state.user.auth.role !== "admin"
 				)
 					next({ path: "/" });
+				else if (to.meta.guestsOnly && store.state.user.auth.loggedIn)
+					next({ path: "/" });
 				else next();
 			};
 

+ 19 - 12
frontend/src/pages/ResetPassword.vue

@@ -45,7 +45,7 @@
 										autofocus
 										v-model="email"
 										@keyup.enter="submitEmail()"
-										@blur="onInputBlur('email')"
+										@keypress="onInput('email')"
 									/>
 								</p>
 								<p class="control">
@@ -62,10 +62,10 @@
 							</div>
 							<transition name="fadein-helpbox">
 								<input-help-box
-									v-if="validation.email.entered"
+									:entered="validation.email.entered"
 									:valid="validation.email.valid"
 									:message="validation.email.message"
-								></input-help-box>
+								/>
 							</transition>
 						</div>
 					</div>
@@ -134,16 +134,16 @@
 									type="password"
 									placeholder="Enter password here..."
 									v-model="newPassword"
-									@blur="onInputBlur('newPassword')"
+									@keypress="onInput('newPassword')"
 								/>
 							</p>
 
 							<transition name="fadein-helpbox">
 								<input-help-box
-									v-if="validation.newPassword.entered"
+									:entered="validation.newPassword.entered"
 									:valid="validation.newPassword.valid"
 									:message="validation.newPassword.message"
-								></input-help-box>
+								/>
 							</transition>
 
 							<p
@@ -160,18 +160,20 @@
 									placeholder="Enter password here..."
 									v-model="newPasswordAgain"
 									@keyup.enter="changePassword()"
-									@blur="onInputBlur('newPasswordAgain')"
+									@keypress="onInput('newPasswordAgain')"
 								/>
 							</p>
 
 							<transition name="fadein-helpbox">
 								<input-help-box
-									v-if="validation.newPasswordAgain.entered"
+									:entered="
+										validation.newPasswordAgain.entered
+									"
 									:valid="validation.newPasswordAgain.valid"
 									:message="
 										validation.newPasswordAgain.message
 									"
-								></input-help-box>
+								/>
 							</transition>
 
 							<a
@@ -260,7 +262,8 @@ export default {
 				newPassword: {
 					entered: false,
 					valid: false,
-					message: "Please enter a valid password."
+					message:
+						"Include at least one lowercase letter, one uppercase letter, one number and one special character."
 				},
 				newPasswordAgain: {
 					entered: false,
@@ -297,7 +300,7 @@ export default {
 				this.validation.newPassword.valid = false;
 			} else if (!validation.regex.password.test(value)) {
 				this.validation.newPassword.message =
-					"Invalid password format. Must have one lowercase letter, one uppercase letter, one number and one special character.";
+					"Include at least one lowercase letter, one uppercase letter, one number and one special character.";
 				this.validation.newPassword.valid = false;
 			} else {
 				this.validation.newPassword.message = "Everything looks great!";
@@ -320,7 +323,7 @@ export default {
 				this.validation.newPasswordAgain.valid = true;
 			}
 		},
-		onInputBlur(inputName) {
+		onInput(inputName) {
 			this.validation[inputName].entered = true;
 		},
 		submitEmail() {
@@ -570,4 +573,8 @@ p {
 	background-color: var(--grey-3);
 	color: var(--white);
 }
+
+.control {
+	margin-bottom: 2px !important;
+}
 </style>

+ 15 - 7
frontend/src/pages/Settings/tabs/Account.vue

@@ -18,7 +18,7 @@
 				v-model="modifiedUser.username"
 				maxlength="32"
 				autocomplete="off"
-				@blur="onInputBlur('username')"
+				@keypress="onInput('username')"
 			/>
 			<span v-if="modifiedUser.username" class="character-counter"
 				>{{ modifiedUser.username.length }}/32</span
@@ -26,9 +26,10 @@
 		</p>
 		<transition name="fadein-helpbox">
 			<input-help-box
+				:entered="validation.username.entered"
 				:valid="validation.username.valid"
 				:message="validation.username.message"
-			></input-help-box>
+			/>
 		</transition>
 
 		<p class="control is-expanded">
@@ -40,15 +41,16 @@
 				placeholder="Enter email address here..."
 				v-if="modifiedUser.email"
 				v-model="modifiedUser.email.address"
-				@blur="onInputBlur('email')"
+				@keypress="onInput('email')"
 				autocomplete="off"
 			/>
 		</p>
 		<transition name="fadein-helpbox">
 			<input-help-box
+				:entered="validation.email.entered"
 				:valid="validation.email.valid"
 				:message="validation.email.message"
-			></input-help-box>
+			/>
 		</transition>
 
 		<save-button ref="saveButton" @clicked="saveChanges()" />
@@ -144,7 +146,7 @@ export default {
 			value !== this.originalUser.username // Sometimes a username pulled from GitHub won't succeed validation
 		) {
 				this.validation.username.message =
-					"Invalid username format. Allowed characters: a-z, A-Z, 0-9 and _.";
+					"Invalid format. Allowed characters: a-z, A-Z, 0-9 and _.";
 				this.validation.username.valid = false;
 			} else {
 				this.validation.username.message = "Everything looks great!";
@@ -162,7 +164,7 @@ export default {
 				value.indexOf("@") !== value.lastIndexOf("@") ||
 				!validation.regex.emailSimple.test(value)
 			) {
-				this.validation.email.message = "Invalid Email format.";
+				this.validation.email.message = "Invalid format.";
 				this.validation.email.valid = false;
 			} else {
 				this.validation.email.message = "Everything looks great!";
@@ -171,7 +173,7 @@ export default {
 		}
 	},
 	methods: {
-		onInputBlur(inputName) {
+		onInput(inputName) {
 			this.validation[inputName].entered = true;
 		},
 		saveChanges() {
@@ -285,3 +287,9 @@ export default {
 	}
 };
 </script>
+
+<style lang="scss" scoped>
+.control {
+	margin-bottom: 2px !important;
+}
+</style>

+ 11 - 6
frontend/src/pages/Settings/tabs/Security.vue

@@ -29,16 +29,16 @@
 					placeholder="Enter new password here..."
 					v-model="validation.newPassword.value"
 					@keyup.enter="changePassword()"
-					@blur="onInputBlur('newPassword')"
+					@keypress="onInput('newPassword')"
 				/>
 			</p>
 
 			<transition name="fadein-helpbox">
 				<input-help-box
-					v-if="validation.newPassword.entered"
+					:entered="validation.newPassword.entered"
 					:valid="validation.newPassword.valid"
 					:message="validation.newPassword.message"
-				></input-help-box>
+				/>
 			</transition>
 
 			<p class="control">
@@ -158,7 +158,8 @@ export default {
 					value: "",
 					valid: false,
 					entered: false,
-					message: "Please enter a valid password."
+					message:
+						"Include at least one lowercase letter, one uppercase letter, one number and one special character."
 				}
 			}
 		};
@@ -182,7 +183,7 @@ export default {
 				this.validation.newPassword.valid = false;
 			} else if (!validation.regex.password.test(value)) {
 				this.validation.newPassword.message =
-					"Invalid password format. Must have one lowercase letter, one uppercase letter, one number and one special character.";
+					"Include at least one lowercase letter, one uppercase letter, one number and one special character.";
 				this.validation.newPassword.valid = false;
 			} else {
 				this.validation.newPassword.message = "Everything looks great!";
@@ -194,7 +195,7 @@ export default {
 		this.apiDomain = await lofig.get("apiDomain");
 	},
 	methods: {
-		onInputBlur(inputName) {
+		onInput(inputName) {
 			this.validation[inputName].entered = true;
 		},
 		changePassword() {
@@ -244,4 +245,8 @@ export default {
 #change-password-button {
 	margin-top: 10px;
 }
+
+.control {
+	margin-bottom: 2px !important;
+}
 </style>

+ 5 - 5
frontend/src/store/modules/modalVisibility.js

@@ -32,11 +32,11 @@ const state = {
 const getters = {};
 
 const actions = {
-	closeModal: async ({ commit }, data) => {
-		if (data.modal === "register") {
-			const recaptchaEnabled = await lofig.get("recaptcha.enabled");
-			if (recaptchaEnabled) window.location.reload();
-		}
+	closeModal: ({ commit }, data) => {
+		if (data.modal === "register")
+			lofig.get("recaptcha.enabled").then(enabled => {
+				if (enabled) window.location.reload();
+			});
 
 		commit("closeModal", data);
 		commit("closeCurrentModal");

+ 1 - 1
frontend/src/validation.js

@@ -4,7 +4,7 @@ export default {
 		az09_: /^[a-z0-9_]+$/,
 		emailSimple: /^[\x00-\x7F]+@[a-z0-9]+\.[a-z0-9]+(\.[a-z0-9]+)?$/,
 		ascii: /^[\x00-\x7F]+$/,
-		password: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[$@$!%*?&])[A-Za-z\d$@$!%*?&]/,
+		password: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~])[A-Za-z\d!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~]/,
 		custom: regex => {
 			return new RegExp(`^[${regex}]+$`);
 		}