فهرست منبع

Added info and success messages.

KrisVos130 7 سال پیش
والد
کامیت
a03062a0e4

+ 0 - 70
frontend/app/js/components/CustomErrors.jsx

@@ -1,70 +0,0 @@
-import React, { Component } from "react";
-import PropTypes from "prop-types";
-
-export default class CustomErrors extends Component {
-	static propTypes = {
-		onRef: PropTypes.func,
-	};
-
-	static defaultProps = {
-		onRef: () => {},
-	};
-
-	constructor() {
-		super();
-
-		this.state = {
-			errors: [],
-		};
-	}
-
-	componentDidMount() {
-		this.props.onRef(this);
-	}
-
-	componentWillUnmount() {
-		this.props.onRef(null);
-	}
-
-	clearErrors = (cb = () => {}) => {
-		this.setState({
-			errors: [],
-		}, cb);
-	};
-
-	addError = (error) => {
-		// TODO add error parsing, e.g. for arrays/objects
-		this.setState({
-			errors: this.state.errors.concat([error]),
-		});
-	};
-
-	clearAddError = (error) => {
-		this.setState({
-			errors: [error],
-		});
-	};
-
-	listErrors = () => {
-		let errors = this.state.errors;
-		let key = 0;
-		if (errors.length > 0) {
-			errors = errors.map((error) => {
-				key++;
-				return (<li key={ key }>{ error }</li>);
-			});
-			return (
-				<div className="errors">
-					<p>Something went wrong</p>
-					<ul>
-						{ errors }
-					</ul>
-				</div>
-			);
-		} return null;
-	};
-
-	render() {
-		return this.listErrors();
-	}
-}

+ 134 - 0
frontend/app/js/components/CustomMessages.jsx

@@ -0,0 +1,134 @@
+import React, { Component } from "react";
+import PropTypes from "prop-types";
+
+export default class CustomMessages extends Component {
+	static propTypes = {
+		onRef: PropTypes.func,
+	};
+
+	static defaultProps = {
+		onRef: () => {},
+	};
+
+	constructor() {
+		super();
+
+		this.state = {
+			error: [],
+			info: [],
+			success: [],
+		};
+	}
+
+	componentDidMount() {
+		this.props.onRef(this);
+	}
+
+	componentWillUnmount() {
+		this.props.onRef(null);
+	}
+
+	clearError = (cb = () => {}) => {
+		this.clear("error", cb);
+	};
+
+	addError = (error) => {
+		this.add("errors", error);
+	};
+
+	clearAddError = (error) => {
+		this.clearAdd("error", error);
+	};
+
+	clearInfo = (cb = () => {}) => {
+		this.clear("info", cb);
+	};
+
+	addInfo = (info) => {
+		this.add("info", info);
+	};
+
+	clearAddInfo = (info) => {
+		this.clearAdd("info", info);
+	};
+
+	clearSuccess = (cb = () => {}) => {
+		this.clear("success", cb);
+	};
+
+	addSuccess = (success) => {
+		this.add("success", success);
+	};
+
+	clearAddSuccess = (success) => {
+		this.clearAdd("success", success);
+	};
+
+	clearErrorSuccess = (cb) => {
+		this.setState({
+			error: [],
+			success: [],
+		}, cb);
+	};
+
+	clearAll = (cb) => {
+		this.setState({
+			error: [],
+			success: [],
+			info: [],
+		}, cb);
+	};
+
+	clear = (type, cb) => {
+		this.setState({
+			[type]: [],
+		}, cb);
+	};
+
+	add = (type, message) => {
+		// TODO add error parsing, e.g. for arrays/objects
+		this.setState({
+			[type]: this.state[type].concat([message]),
+		});
+	};
+
+	clearAdd = (type, message) => {
+		this.setState({
+			[type]: [message],
+		});
+	};
+
+	list = (type) => {
+		let messages = this.state[type];
+		let key = 0;
+		if (messages.length > 0) {
+			messages = messages.map((message) => {
+				key++;
+				return (<li key={ key }>{ message }</li>);
+			});
+			let text = "";
+			if (type === "error") text = "Something went wrong";
+			else if (type === "info") text = "Info";
+			else if (type === "success") text = "Success";
+
+			return (
+				<div key={ type } className={ type }>
+					<p>{ text }</p>
+					<ul>
+						{ messages }
+					</ul>
+				</div>
+			);
+		} return null;
+	};
+
+	render() {
+		return (
+			<div>
+				{ this.list("error") }
+				{ this.list("info") }
+				{ this.list("success") }
+			</div>
+		);
+	}
+}

+ 17 - 15
frontend/app/js/views/Auth/ForgotPassword/index.jsx

@@ -3,7 +3,7 @@ import PropTypes from "prop-types";
 import { connect } from "react-redux";
 
 import CustomInput from "components/CustomInput.jsx";
-import CustomErrors from "components/CustomErrors.jsx";
+import CustomErrors from "components/CustomMessages.jsx";
 
 import io from "io";
 
@@ -64,18 +64,19 @@ export default class Settings extends Component {
 
 	requestResetCode = () => {
 		if (CustomInput.hasInvalidInput(this.input, ["email"])) {
-			this.errors.clearAddError("Some fields are incorrect. Please fix them before continuing.");
+			this.messages.clearAddError("Some fields are incorrect. Please fix them before continuing.");
 		} else {
-			this.errors.clearErrors();
+			this.messages.clearAll();
 			io.getSocket(socket => {
 				socket.emit("users.requestPasswordReset", this.input.email.getValue(), res => {
 					if (res.status === "success") {
-						alert("Success!");
+						this.messages.clearAddSuccess("Successfully requested reset code.");
+						this.messages.clearAddInfo("We have sent a unique reset code to your email address.");
 						this.setState({
 							step: 2,
 						});
 					} else {
-						this.errors.addError(res.message);
+						this.messages.addError(res.message);
 					}
 				});
 			});
@@ -84,19 +85,19 @@ export default class Settings extends Component {
 
 	verifyResetCode = () => {
 		if (CustomInput.hasInvalidInput(this.input, ["resetCode"])) {
-			this.errors.clearAddError("Some fields are incorrect. Please fix them before continuing.");
+			this.messages.clearAddError("Some fields are incorrect. Please fix them before continuing.");
 		} else {
-			this.errors.clearErrors();
+			this.messages.clearErrors();
 			io.getSocket(socket => {
 				socket.emit("users.verifyPasswordResetCode", this.input.resetCode.getValue(), res => {
 					if (res.status === "success") {
-						alert("Success!");
+						this.messages.clearAddSuccess("Successfully verified reset code.");
 						this.setState({
 							step: 3,
 							resetCode: this.input.resetCode.getValue(),
 						});
 					} else {
-						this.errors.addError(res.message);
+						this.messages.addError(res.message);
 					}
 				});
 			});
@@ -104,19 +105,20 @@ export default class Settings extends Component {
 	};
 
 	changePassword = () => {
+		this.messages.clearErrorSuccess();
 		if (CustomInput.hasInvalidInput(this.input, ["newPassword", "newPasswordAgain"])) {
-			this.errors.clearAddError("Some fields are incorrect. Please fix them before continuing.");
+			this.messages.clearAddError("Some fields are incorrect. Please fix them before continuing.");
 		} else if (CustomInput.isTheSame(this.input, ["newPassword", "newPasswordAgain"])) {
-			this.errors.clearAddError("New password and new password again need to be the same.");
+			this.messages.clearAddError("New password and new password again need to be the same.");
 		} else {
-			this.errors.clearErrors();
 			io.getSocket(socket => {
 				socket.emit("users.changePasswordWithResetCode", this.state.resetCode, this.input.newPassword.getValue(), res => {
 					if (res.status === "success") {
-						alert("Success!");
+						this.messages.clearAddSuccess("Successfully changed password. Redirecting you to the login page.");
+						// TODO Maybe add 5s delay and replace location.href everywhere
 						location.href = "/login";
 					} else {
-						this.errors.addError(res.message);
+						this.messages.addError(res.message);
 					}
 				});
 			});
@@ -140,7 +142,7 @@ export default class Settings extends Component {
 					<span className="step-line-2" />
 					<span className={ `step-circle-3 ${ this.state.step === 3 ? "step-circle-active" : "" }` }>3</span>
 				</div>
-				<CustomErrors onRef={ ref => (this.errors = ref) } />
+				<CustomErrors onRef={ ref => (this.messages = ref) } />
 				{ this.getActions() }
 			</div>
 		);

+ 5 - 5
frontend/app/js/views/Auth/Login/index.jsx

@@ -1,7 +1,7 @@
 import React, { Component } from "react";
 
 import CustomInput from "components/CustomInput.jsx";
-import CustomErrors from "components/CustomErrors.jsx";
+import CustomMessages from "components/CustomMessages.jsx";
 
 import io from "io";
 import config from "config";
@@ -14,10 +14,10 @@ export default class Login extends Component {
 	}
 
 	login = () => {
+		this.messages.clearErrorSuccess();
 		if (CustomInput.hasInvalidInput(this.input)) {
-			this.errors.clearAddError("Some fields are incorrect. Please fix them before continuing.");
+			this.messages.clearAddError("Some fields are incorrect. Please fix them before continuing.");
 		} else {
-			this.errors.clearErrors();
 			io.getSocket(socket => {
 				socket.emit("users.login", this.input.email.getValue(), this.input.password.getValue(), res => {
 					if (res.status === "success") {
@@ -29,7 +29,7 @@ export default class Login extends Component {
 						document.cookie = `${ config.cookie.sidName }=${ res.SID }; expires=${ date.toGMTString() }; ${ domain }${ secure }path=/`;
 						location.reload(); // if we could avoid this, then that would be better
 					} else {
-						this.errors.addError(res.message);
+						this.messages.addError(res.message);
 					}
 				});
 			});
@@ -43,7 +43,7 @@ export default class Login extends Component {
 	render() {
 		return (
 			<div>
-				<CustomErrors onRef={ ref => (this.errors = ref) } />
+				<CustomMessages onRef={ ref => (this.messages = ref) } />
 				<CustomInput type="email" name="email" label="Email" placeholder="Email" onRef={ ref => (this.input.email = ref) } />
 				<CustomInput type="password" name="password" label="Password" placeholder="Password" onRef={ ref => (this.input.password = ref) } />
 				<p>By logging in/registering you agree to our <a href="/terms">Terms of Service</a> and <a href="/privacy">Privacy Policy</a>.</p>

+ 1 - 1
frontend/app/js/views/Auth/Logout/index.jsx

@@ -18,7 +18,7 @@ export default class Login extends Component {
 					document.cookie = "SID=;expires=Thu, 01 Jan 1970 00:00:01 GMT;";
 					location.reload(); // if we could avoid this, then that would be better
 				} else {
-					// return res.message, temporarily:
+					// TODO change this
 					alert(res.message); // eslint-disable-line no-alert
 				}
 			});

+ 5 - 5
frontend/app/js/views/Auth/Register/index.jsx

@@ -1,7 +1,7 @@
 import React, { Component } from "react";
 
 import CustomInput from "components/CustomInput.jsx";
-import CustomErrors from "components/CustomErrors.jsx";
+import CustomMessages from "components/CustomMessages.jsx";
 
 import io from "io";
 import config from "config";
@@ -24,10 +24,10 @@ export default class Register extends Component {
 	}
 
 	register = () => {
+		this.messages.clearErrorSuccess();
 		if (CustomInput.hasInvalidInput(this.input)) {
-			this.errors.clearAddError("Some fields are incorrect. Please fix them before continuing.");
+			this.messages.clearAddError("Some fields are incorrect. Please fix them before continuing.");
 		} else {
-			this.errors.clearErrors();
 			io.getSocket(socket => {
 				socket.emit("users.register", this.input.username.getValue(), this.input.email.getValue(), this.input.password.getValue(), grecaptcha.getResponse(this.state.recaptcha), res => {
 					if (res.status === "success") {
@@ -41,7 +41,7 @@ export default class Register extends Component {
 							// redirect to login
 						}
 					} else {
-						this.errors.addError(res.message);
+						this.messages.addError(res.message);
 						grecaptcha.reset();
 					}
 				});
@@ -56,7 +56,7 @@ export default class Register extends Component {
 	render() {
 		return (
 			<div>
-				<CustomErrors onRef={ ref => (this.errors = ref) } />
+				<CustomMessages onRef={ ref => (this.messages = ref) } />
 				<CustomInput type="email" name="email" label="Email" placeholder="Email" onRef={ ref => (this.input.email = ref) } />
 				<CustomInput type="username" name="username" label="Username" placeholder="Username" onRef={ ref => (this.input.username = ref) } />
 				<CustomInput type="password" name="password" label="Password" placeholder="Password" onRef={ ref => (this.input.password = ref) } />

+ 17 - 16
frontend/app/js/views/Auth/Settings/SetPassword/index.jsx

@@ -3,7 +3,7 @@ import PropTypes from "prop-types";
 import { connect } from "react-redux";
 
 import CustomInput from "components/CustomInput.jsx";
-import CustomErrors from "components/CustomErrors.jsx";
+import CustomMessages from "components/CustomMessages.jsx";
 
 import io from "io";
 
@@ -41,12 +41,12 @@ export default class Settings extends Component {
 						passwordLinked: res.data.password,
 					});
 				} else {
-					this.errors.addError("You are currently not logged in.");
+					this.messages.addError("You are currently not logged in.");
 				}
 			});
 
 			socket.on("event:user.linkPassword", () => {
-				alert("A password for your account has been set. We will now redirect you to the settings page.");
+				this.messages.clearAddInfo("A password for your account has been set. Redirecting you to the settings page.");
 				location.href = "/settings";
 			});
 		});
@@ -76,19 +76,19 @@ export default class Settings extends Component {
 	};
 
 	setPassword = () => {
+		this.messages.clearErrorSuccess();
 		if (CustomInput.hasInvalidInput(this.input, ["newPassword", "newPasswordAgain"])) {
-			this.errors.clearAddError("Some fields are incorrect. Please fix them before continuing.");
+			this.messages.clearAddError("Some fields are incorrect. Please fix them before continuing.");
 		} else if (CustomInput.isTheSame(this.input, ["newPassword", "newPasswordAgain"])) {
-			this.errors.clearAddError("New password and new password again need to be the same.");
+			this.messages.clearAddError("New password and new password again need to be the same.");
 		} else {
-			this.errors.clearErrors();
 			io.getSocket(socket => {
 				socket.emit("users.changePasswordWithCode", this.state.code, this.input.newPassword.getValue(), res => {
 					if (res.status === "success") {
-						alert("Success!");
+						this.messages.clearAddSuccess("Successfully set password. Redirecting you to the settings page.");
 						location.href = "/settings";
 					} else {
-						this.errors.addError(res.message);
+						this.messages.addError(res.message);
 					}
 				});
 			});
@@ -96,36 +96,37 @@ export default class Settings extends Component {
 	};
 
 	requestCode = () => {
-		this.errors.clearErrors();
+		this.messages.clearErrorSuccess();
 		io.getSocket(socket => {
 			socket.emit("users.requestPassword", res => {
 				if (res.status === "success") {
-					alert("Success!");
+					this.messages.clearAddSuccess("Successfully requested code.");
+					this.messages.clearAddInfo("We have sent a unique code to your email address.");
 					this.setState({
 						step: 2,
 					});
 				} else {
-					this.errors.addError(res.message);
+					this.messages.addError(res.message);
 				}
 			});
 		});
 	};
 
 	verifyCode = () => {
+		this.messages.clearErrorSuccess();
 		if (CustomInput.hasInvalidInput(this.input, ["code"])) {
-			this.errors.clearAddError("Some fields are incorrect. Please fix them before continuing.");
+			this.messages.clearAddError("Some fields are incorrect. Please fix them before continuing.");
 		} else {
-			this.errors.clearErrors();
 			io.getSocket(socket => {
 				socket.emit("users.verifyPasswordCode", this.input.code.getValue(), res => {
 					if (res.status === "success") {
-						alert("Success!");
+						this.messages.clearAddSuccess("Successfully verified code.");
 						this.setState({
 							step: 3,
 							code: this.input.code.getValue(),
 						});
 					} else {
-						this.errors.addError(res.message);
+						this.messages.addError(res.message);
 					}
 				});
 			});
@@ -136,7 +137,7 @@ export default class Settings extends Component {
 		return (
 			<div>
 				<h1>Set Password</h1>
-				<CustomErrors onRef={ ref => (this.errors = ref) } />
+				<CustomMessages onRef={ ref => (this.messages = ref) } />
 				{ this.getActions() }
 			</div>
 		);

+ 24 - 24
frontend/app/js/views/Auth/Settings/index.jsx

@@ -7,7 +7,7 @@ import { NavLink } from "react-router-dom";
 import config from "config";
 
 import CustomInput from "components/CustomInput.jsx";
-import CustomErrors from "components/CustomErrors.jsx";
+import CustomMessages from "components/CustomMessages.jsx";
 
 import io from "io";
 
@@ -48,7 +48,7 @@ export default class Settings extends Component {
 						gitHubLinked: res.data.github,
 					});
 				} else {
-					this.errors.addError("You are currently not logged in.");
+					this.messages.addError("You are currently not logged in.");
 				}
 			});
 
@@ -89,6 +89,7 @@ export default class Settings extends Component {
 	} */
 
 	saveChanges = () => {
+		this.messages.clearErrorSuccess();
 		async.waterfall([
 			(next) => {
 				if (this.input.username.isPristine()) this.input.username.validate(next);
@@ -100,20 +101,19 @@ export default class Settings extends Component {
 			},
 		], () => {
 			if (CustomInput.hasInvalidInput(this.input, ["username", "email"])) {
-				this.errors.clearAddError("Some fields are incorrect. Please fix them before continuing.");
+				this.messages.clearAddError("Some fields are incorrect. Please fix them before continuing.");
 			} else if (this.input.username.isOriginal() && this.input.email.isOriginal()) {
-				this.errors.clearAddError("Username or email hasn't changed.");
+				this.messages.clearAddError("Username or email hasn't changed.");
 			} else {
-				this.errors.clearErrors();
 				const email = this.input.email.getValue();
 				const username = this.input.username.getValue();
 				io.getSocket(socket => {
 					if (!this.input.email.isOriginal()) {
 						socket.emit("users.updateEmail", this.props.user.userId, email, res => {
 							if (res.status === "success") {
-								alert("Success!");
+								this.messages.clearAddSuccess("Successfully updated email.");
 							} else {
-								this.errors.addError(res.message);
+								this.messages.addError(res.message);
 							}
 						});
 					}
@@ -121,9 +121,9 @@ export default class Settings extends Component {
 					if (!this.input.username.isOriginal()) {
 						socket.emit("users.updateUsername", this.props.user.userId, username, res => {
 							if (res.status === "success") {
-								alert("Success!");
+								this.messages.clearAddSuccess("Successfully updated username.");
 							} else {
-								this.errors.addError(res.message);
+								this.messages.addError(res.message);
 							}
 						});
 					}
@@ -133,18 +133,18 @@ export default class Settings extends Component {
 	};
 
 	changePassword = () => {
+		this.messages.clearErrorSuccess();
 		if (CustomInput.hasInvalidInput(this.input, ["newPassword"])) {
-			this.errors.clearAddError("Some fields are incorrect. Please fix them before continuing.");
+			this.messages.clearAddError("Some fields are incorrect. Please fix them before continuing.");
 		} else if (!this.state.passwordLinked) {
-			this.errors.clearAddError("You don't have a password set.");
+			this.messages.clearAddError("You don't have a password set.");
 		} else {
-			this.errors.clearErrors();
 			io.getSocket(socket => {
 				socket.emit("users.updatePassword", this.input.newPassword.getValue(), res => {
 					if (res.status === "success") {
-						alert("Success!");
+						this.messages.clearAddSuccess("Successfully changed password.");
 					} else {
-						this.errors.addError(res.message);
+						this.messages.addError(res.message);
 					}
 				});
 			});
@@ -152,39 +152,39 @@ export default class Settings extends Component {
 	};
 
 	logOutEverywhere = () => {
-		this.errors.clearErrors();
+		this.messages.clearErrorSuccess();
 		io.getSocket(socket => {
 			socket.emit("users.removeSessions", this.props.user.userId, res => {
 				if (res.status === "success") {
-					alert("Success!");
+					this.messages.clearAddSuccess("Successfully logged out everywhere.");
 				} else {
-					this.errors.addError(res.message);
+					this.messages.addError(res.message);
 				}
 			});
 		});
 	};
 
 	unlinkGitHub = () => {
-		this.errors.clearErrors();
+		this.messages.clearErrorSuccess();
 		io.getSocket(socket => {
 			socket.emit("users.unlinkGitHub", res => {
 				if (res.status === "success") {
-					alert("Success!");
+					this.messages.clearAddSuccess("Successfully unlinked GitHub.");
 				} else {
-					this.errors.addError(res.message);
+					this.messages.addError(res.message);
 				}
 			});
 		});
 	};
 
 	unlinkPassword = () => {
-		this.errors.clearErrors();
+		this.messages.clearErrorSuccess();
 		io.getSocket(socket => {
 			socket.emit("users.unlinkPassword", res => {
 				if (res.status === "success") {
-					alert("Success!");
+					this.messages.clearAddSuccess("Successfully unlinked password.");
 				} else {
-					this.errors.addError(res.message);
+					this.messages.addError(res.message);
 				}
 			});
 		});
@@ -219,7 +219,7 @@ export default class Settings extends Component {
 	render() {
 		return (
 			<div>
-				<CustomErrors onRef={ ref => (this.errors = ref) } />
+				<CustomMessages onRef={ ref => (this.messages = ref) } />
 				<div>
 					<h2>General</h2>
 					<CustomInput type="email" name="email" label="Email" placeholder="Email" onRef={ ref => (this.input.email = ref) } />