Explorar el Código

feat(Settings/SaveButton): added SaveButton mixin, along with failed/saving.... messages

Signed-off-by: Jonathan <theflametrooper@gmail.com>
Jonathan hace 4 años
padre
commit
4a5da4490f

+ 14 - 0
frontend/src/App.vue

@@ -193,6 +193,20 @@ export default {
 			background-color: $light-grey !important;
 		}
 	}
+
+	h1,
+	h2,
+	h3,
+	h4,
+	h5,
+	h6 {
+		color: #fff !important;
+	}
+
+	p:not(.help),
+	label {
+		color: #ddd !important;
+	}
 }
 
 body.night-mode {

+ 10 - 19
frontend/src/pages/Settings/index.vue

@@ -136,20 +136,6 @@ export default {
 @import "../../styles/global.scss";
 
 .night-mode {
-	h1,
-	h2,
-	h3,
-	h4,
-	h5,
-	h6 {
-		color: #fff !important;
-	}
-
-	p:not(.help),
-	label {
-		color: #ddd !important;
-	}
-
 	.content {
 		background-color: #222 !important;
 	}
@@ -167,7 +153,7 @@ export default {
 
 	.content {
 		background-color: #fff;
-		padding: 50px;
+		padding: 30px 50px;
 		border-radius: 3px;
 	}
 
@@ -231,7 +217,12 @@ export default {
 		height: fit-content;
 
 		.save-changes {
-			margin-top: 15px;
+			margin-top: 20px;
+
+			&:disabled {
+				background-color: $light-grey !important;
+				color: #000;
+			}
 		}
 
 		label {
@@ -249,11 +240,11 @@ export default {
 	}
 }
 
-/deep/ .saved-changes-transition-enter-active {
-	transition: all 0.2s ease;
+/deep/ .saving-changes-transition-enter-active {
+	transition: all 0.1s ease;
 }
 
-/deep/ .saved-changes-transition-enter {
+/deep/ .saving-changes-transition-enter {
 	transform: translateX(20px);
 	opacity: 0;
 }

+ 73 - 0
frontend/src/pages/Settings/mixins/SaveButton.vue

@@ -0,0 +1,73 @@
+<script>
+export default {
+	data() {
+		return {
+			saveStatus: "default" // enum: ["default", "disabled", "save-failure", "save-success"],
+		};
+	},
+	computed: {
+		saveButtonMessage() {
+			switch (this.saveStatus) {
+				case "save-success":
+					return `<i class="material-icons icon-with-button">done</i>Saved Changes`;
+				case "save-failure":
+					return `<i class="material-icons icon-with-button">error_outline</i>Failed to save`;
+				case "disabled":
+					return "Saving...";
+				default:
+					return "Save changes";
+			}
+		},
+		saveButtonStyle() {
+			switch (this.saveStatus) {
+				case "save-success":
+					return "is-success";
+				case "save-failure":
+					return `is-danger`;
+				default:
+					return "is-primary";
+			}
+		}
+	},
+	methods: {
+		successfulSave() {
+			if (this.saveStatus !== "save-success") {
+				this.saveStatus = "save-success";
+				setTimeout(() => {
+					this.saveStatus = "default";
+				}, 2000);
+			}
+		},
+		failedSave() {
+			if (this.saveStatus !== "save-failure") {
+				this.saveStatus = "save-failure";
+				setTimeout(() => {
+					this.saveStatus = "default";
+				}, 2000);
+			}
+		}
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+@import "../../../styles/global.scss";
+
+.save-changes {
+	margin-top: 20px;
+
+	&:disabled {
+		background-color: $light-grey !important;
+		color: #000;
+	}
+}
+
+.saving-changes-transition-enter-active {
+	transition: all 0.1s ease;
+}
+
+.saving-changes-transition-enter {
+	transform: translateX(20px);
+	opacity: 0;
+}
+</style>

+ 41 - 33
frontend/src/pages/Settings/tabs/Account.vue

@@ -54,18 +54,15 @@
 				{{ validation.email.message }}
 			</p>
 		</transition>
-		<transition name="saved-changes-transition" mode="out-in">
+		<transition name="saving-changes-transition" mode="out-in">
 			<button
-				class="button is-primary save-changes"
-				v-if="!savedChanges"
+				class="button save-changes"
+				:class="saveButtonStyle"
 				@click="saveChanges()"
-				key="save"
-			>
-				Save changes
-			</button>
-			<button class="button is-success save-changes" key="saved" v-else>
-				<i class="material-icons icon-with-button">done</i>Saved Changes
-			</button>
+				:key="saveStatus"
+				:disabled="saveStatus === 'disabled'"
+				v-html="saveButtonMessage"
+			/>
 		</transition>
 	</div>
 </template>
@@ -77,6 +74,8 @@ import Toast from "toasters";
 import validation from "../../../validation";
 import io from "../../../io";
 
+import SaveButton from "../mixins/SaveButton.vue";
+
 export default {
 	data() {
 		return {
@@ -91,8 +90,7 @@ export default {
 					valid: false,
 					message: "Please enter a valid email address."
 				}
-			},
-			savedChanges: false
+			}
 		};
 	},
 	computed: mapState({
@@ -145,23 +143,28 @@ export default {
 		});
 	},
 	methods: {
-		showSavedAnimation() {
-			this.savedChanges = true;
-			setTimeout(() => {
-				this.savedChanges = false;
-			}, 2000);
-		},
 		onInputBlur(inputName) {
 			this.validation[inputName].entered = true;
 		},
 		saveChanges() {
-			if (this.modifiedUser.username !== this.originalUser.username)
-				this.changeUsername();
-			if (
+			const usernameChanged =
+				this.modifiedUser.username !== this.originalUser.username;
+			const emailAddressChanged =
 				this.modifiedUser.email.address !==
-				this.originalUser.email.address
-			)
-				this.changeEmail();
+				this.originalUser.email.address;
+
+			if (usernameChanged) this.changeUsername();
+
+			if (emailAddressChanged) this.changeEmail();
+
+			if (!usernameChanged && !emailAddressChanged) {
+				this.failedSave();
+
+				new Toast({
+					content: "Please make a change before saving.",
+					timeout: 8000
+				});
+			}
 		},
 		changeEmail() {
 			const email = this.modifiedUser.email.address;
@@ -179,16 +182,17 @@ export default {
 					timeout: 8000
 				});
 
+			this.saveStatus = "disabled";
+
 			return this.socket.emit(
 				"users.updateEmail",
 				this.userId,
 				email,
 				res => {
-					console.log(res);
-
-					if (res.status !== "success")
+					if (res.status !== "success") {
 						new Toast({ content: res.message, timeout: 8000 });
-					else {
+						this.failedSave();
+					} else {
 						new Toast({
 							content: "Successfully changed email address",
 							timeout: 4000
@@ -199,7 +203,7 @@ export default {
 							value: email
 						});
 
-						if (!this.savedChanges) this.showSavedAnimation();
+						this.successfulSave();
 					}
 				}
 			);
@@ -220,14 +224,17 @@ export default {
 					timeout: 8000
 				});
 
+			this.saveStatus = "disabled";
+
 			return this.socket.emit(
 				"users.updateUsername",
 				this.userId,
 				username,
 				res => {
-					if (res.status !== "success")
+					if (res.status !== "success") {
 						new Toast({ content: res.message, timeout: 8000 });
-					else {
+						this.failedSave();
+					} else {
 						new Toast({
 							content: "Successfully changed username",
 							timeout: 4000
@@ -238,12 +245,13 @@ export default {
 							value: username
 						});
 
-						if (!this.savedChanges) this.showSavedAnimation();
+						this.successfulSave();
 					}
 				}
 			);
 		},
 		...mapActions("settings", ["updateOriginalUser"])
-	}
+	},
+	mixins: [SaveButton]
 };
 </script>

+ 62 - 39
frontend/src/pages/Settings/tabs/Profile.vue

@@ -79,18 +79,15 @@
 				>{{ modifiedUser.bio.length }}/200</span
 			>
 		</p>
-		<transition name="saved-changes-transition" mode="out-in">
+		<transition name="saving-changes-transition" mode="out-in">
 			<button
-				class="button is-primary save-changes"
-				v-if="!savedChanges"
+				class="button save-changes"
+				:class="saveButtonStyle"
 				@click="saveChanges()"
-				key="save"
-			>
-				Save changes
-			</button>
-			<button class="button is-success save-changes" key="saved" v-else>
-				<i class="material-icons icon-with-button">done</i>Saved Changes
-			</button>
+				:key="saveStatus"
+				:disabled="saveStatus === 'disabled'"
+				v-html="saveButtonMessage"
+			/>
 		</transition>
 	</div>
 </template>
@@ -102,11 +99,12 @@ import Toast from "toasters";
 import validation from "../../../validation";
 import io from "../../../io";
 
+import SaveButton from "../mixins/SaveButton.vue";
+
 export default {
 	data() {
 		return {
-			notesUri: "",
-			savedChanges: false
+			notesUri: ""
 		};
 	},
 	computed: mapState({
@@ -128,20 +126,32 @@ export default {
 	},
 	methods: {
 		saveChanges() {
-			if (this.modifiedUser.name !== this.originalUser.name)
-				this.changeName();
-			if (this.modifiedUser.location !== this.originalUser.location)
-				this.changeLocation();
-			if (this.modifiedUser.bio !== this.originalUser.bio)
-				this.changeBio();
-			if (this.modifiedUser.avatar.type !== this.originalUser.avatar.type)
-				this.changeAvatarType();
-		},
-		showSavedAnimation() {
-			this.savedChanges = true;
-			setTimeout(() => {
-				this.savedChanges = false;
-			}, 2000);
+			const nameChanged =
+				this.modifiedUser.name !== this.originalUser.name;
+			const locationChanged =
+				this.modifiedUser.location !== this.originalUser.location;
+			const bioChanged = this.modifiedUser.bio !== this.originalUser.bio;
+			const avatarChanged =
+				this.modifiedUser.avatar.type !== this.originalUser.avatar.type;
+
+			if (nameChanged) this.changeName();
+			if (locationChanged) this.changeLocation();
+			if (bioChanged) this.changeBio();
+			if (avatarChanged) this.changeAvatarType();
+
+			if (
+				!avatarChanged &&
+				!bioChanged &&
+				!locationChanged &&
+				!nameChanged
+			) {
+				this.failedSave();
+
+				new Toast({
+					content: "Please make a change before saving.",
+					timeout: 8000
+				});
+			}
 		},
 		changeName() {
 			const { name } = this.modifiedUser;
@@ -152,14 +162,17 @@ export default {
 					timeout: 8000
 				});
 
+			this.saveStatus = "disabled";
+
 			return this.socket.emit(
 				"users.updateName",
 				this.userId,
 				name,
 				res => {
-					if (res.status !== "success")
+					if (res.status !== "success") {
 						new Toast({ content: res.message, timeout: 8000 });
-					else {
+						this.failedSave();
+					} else {
 						new Toast({
 							content: "Successfully changed name",
 							timeout: 4000
@@ -170,7 +183,7 @@ export default {
 							value: name
 						});
 
-						if (!this.savedChanges) this.showSavedAnimation();
+						this.successfulSave();
 					}
 				}
 			);
@@ -184,14 +197,17 @@ export default {
 					timeout: 8000
 				});
 
+			this.saveStatus = "disabled";
+
 			return this.socket.emit(
 				"users.updateLocation",
 				this.userId,
 				location,
 				res => {
-					if (res.status !== "success")
+					if (res.status !== "success") {
 						new Toast({ content: res.message, timeout: 8000 });
-					else {
+						this.failedSave();
+					} else {
 						new Toast({
 							content: "Successfully changed location",
 							timeout: 4000
@@ -202,7 +218,7 @@ export default {
 							value: location
 						});
 
-						if (!this.savedChanges) this.showSavedAnimation();
+						this.successfulSave();
 					}
 				}
 			);
@@ -216,14 +232,17 @@ export default {
 					timeout: 8000
 				});
 
+			this.saveStatus = "disabled";
+
 			return this.socket.emit(
 				"users.updateBio",
 				this.userId,
 				bio,
 				res => {
-					if (res.status !== "success")
+					if (res.status !== "success") {
 						new Toast({ content: res.message, timeout: 8000 });
-					else {
+						this.failedSave();
+					} else {
 						new Toast({
 							content: "Successfully changed bio",
 							timeout: 4000
@@ -234,7 +253,7 @@ export default {
 							value: bio
 						});
 
-						if (!this.savedChanges) this.showSavedAnimation();
+						this.successfulSave();
 					}
 				}
 			);
@@ -242,14 +261,17 @@ export default {
 		changeAvatarType() {
 			const { avatar } = this.modifiedUser;
 
+			this.saveStatus = "disabled";
+
 			return this.socket.emit(
 				"users.updateAvatarType",
 				this.userId,
 				avatar.type,
 				res => {
-					if (res.status !== "success")
+					if (res.status !== "success") {
 						new Toast({ content: res.message, timeout: 8000 });
-					else {
+						this.failedSave();
+					} else {
 						new Toast({
 							content: "Successfully updated avatar type",
 							timeout: 4000
@@ -260,13 +282,14 @@ export default {
 							value: avatar
 						});
 
-						if (!this.savedChanges) this.showSavedAnimation();
+						this.successfulSave();
 					}
 				}
 			);
 		},
 		...mapActions("settings", ["updateOriginalUser"])
-	}
+	},
+	mixins: [SaveButton]
 };
 </script>
 

+ 11 - 1
frontend/src/store/modules/settings.js

@@ -22,7 +22,17 @@ const actions = {
 const mutations = {
 	updateOriginalUser(state, payload) {
 		const { property, value } = payload;
-		state.originalUser[property] = JSON.parse(JSON.stringify(value));
+
+		property.split(".").reduce(
+			// eslint-disable-next-line no-return-assign
+			(o, p, i) =>
+				(o[p] =
+					// eslint-disable-next-line no-plusplus
+					property.split(".").length === ++i
+						? JSON.parse(JSON.stringify(value))
+						: o[p] || {}),
+			state.originalUser
+		);
 	},
 	setUser(state, user) {
 		state.originalUser = user;