Browse Source

feat: added ability to change your location/bio in settings

Kristian Vos 5 năm trước cách đây
mục cha
commit
ea577dad3f

+ 75 - 1
backend/logic/actions/users.js

@@ -502,7 +502,9 @@ module.exports = {
 					email: {
 						address: user.email.address
 					},
-					username: user.username
+					username: user.username,
+					location: user.location,
+					bio: user.bio
 				};
 				if (user.services.password && user.services.password.password) data.password = true;
 				if (user.services.github && user.services.github.id) data.github = true;
@@ -633,6 +635,78 @@ module.exports = {
 		});
 	}),
 
+	/**
+	 * Updates a user's location
+	 *
+	 * @param {Object} session - the session object automatically added by socket.io
+	 * @param {String} updatingUserId - the updating user's id
+	 * @param {String} newLocation - the new location
+	 * @param {Function} cb - gets called with the result
+	 */
+	updateLocation: hooks.loginRequired((session, updatingUserId, newLocation, cb) => {
+		async.waterfall([
+			(next) => {
+				if (updatingUserId === session.userId) return next(null, true);
+				db.models.user.findOne({_id: session.userId}, next);
+			},
+
+			(user, next) => {
+				if (user !== true && (!user || user.role !== 'admin')) return next('Invalid permissions.');
+				db.models.user.findOne({ _id: updatingUserId }, next);
+			},
+
+			(user, next) => {
+				if (!user) return next('User not found.');
+				db.models.user.updateOne({ _id: updatingUserId }, {$set: {location: newLocation}}, {runValidators: true}, next);
+			}
+		], async (err) => {
+			if (err && err !== true) {
+				err = await utils.getError(err);
+				logger.error("UPDATE_LOCATION", `Couldn't update location for user "${updatingUserId}" to location "${newLocation}". "${err}"`);
+				cb({status: 'failure', message: err});
+			} else {
+				logger.success("UPDATE_LOCATION", `Updated location for user "${updatingUserId}" to location "${newLocation}".`);
+				cb({ status: 'success', message: 'Location updated successfully' });
+			}
+		});
+	}),
+
+	/**
+	 * Updates a user's bio
+	 *
+	 * @param {Object} session - the session object automatically added by socket.io
+	 * @param {String} updatingUserId - the updating user's id
+	 * @param {String} newBio - the new bio
+	 * @param {Function} cb - gets called with the result
+	 */
+	updateBio: hooks.loginRequired((session, updatingUserId, newBio, cb) => {
+		async.waterfall([
+			(next) => {
+				if (updatingUserId === session.userId) return next(null, true);
+				db.models.user.findOne({_id: session.userId}, next);
+			},
+
+			(user, next) => {
+				if (user !== true && (!user || user.role !== 'admin')) return next('Invalid permissions.');
+				db.models.user.findOne({ _id: updatingUserId }, next);
+			},
+
+			(user, next) => {
+				if (!user) return next('User not found.');
+				db.models.user.updateOne({ _id: updatingUserId }, {$set: {bio: newBio}}, {runValidators: true}, next);
+			}
+		], async (err) => {
+			if (err && err !== true) {
+				err = await utils.getError(err);
+				logger.error("UPDATE_BIO", `Couldn't update bio for user "${updatingUserId}" to bio "${newBio}". "${err}"`);
+				cb({status: 'failure', message: err});
+			} else {
+				logger.success("UPDATE_BIO", `Updated bio for user "${updatingUserId}" to bio "${newBio}".`);
+				cb({ status: 'success', message: 'Bio updated successfully' });
+			}
+		});
+	}),
+
 	/**
 	 * Updates a user's role
 	 *

+ 2 - 0
backend/logic/db/schemas/user.js

@@ -29,5 +29,7 @@ module.exports = {
 	liked: [{ type: String }],
 	disliked: [{ type: String }],
 	favoriteStations: [{ type: String }],
+	location: { type: String, default: "" },
+	bio: { type: String, default: "" },
 	createdAt: { type: Date, default: Date.now() }
 };

+ 78 - 2
frontend/components/User/Settings.vue

@@ -54,6 +54,38 @@
 					</button>
 				</p>
 			</div>
+			<label class="label">Location</label>
+			<div v-if="user" class="control is-grouped">
+				<p class="control is-expanded has-icon has-icon-right">
+					<input
+						v-model="user.location"
+						class="input"
+						type="text"
+						placeholder="Change location"
+					/>
+				</p>
+				<p class="control is-expanded">
+					<button class="button is-success" @click="changeLocation()">
+						Save changes
+					</button>
+				</p>
+			</div>
+			<label class="label">Bio</label>
+			<div v-if="user" class="control is-grouped">
+				<p class="control is-expanded has-icon has-icon-right">
+					<textarea
+						v-model="user.bio"
+						class="textarea"
+						type="text"
+						placeholder="Change bio"
+					/>
+				</p>
+				<p class="control is-expanded">
+					<button class="button is-success" @click="changeBio()">
+						Save changes
+					</button>
+				</p>
+			</div>
 			<label v-if="password" class="label">Use nightmode</label>
 			<div v-if="password" class="control is-grouped">
 				<input
@@ -62,7 +94,6 @@
 					@click="toggleNightmode()"
 				/>
 			</div>
-
 			<label v-if="!password" class="label">Add password</label>
 			<div v-if="!password" class="control is-grouped">
 				<button
@@ -140,7 +171,6 @@
 			>
 				Remove logging in with GitHub
 			</button>
-
 			<br />
 			<button
 				class="button is-warning"
@@ -277,6 +307,52 @@ export default {
 				}
 			);
 		},
+		changeLocation() {
+			const { location } = this.user;
+			if (!validation.isLength(location, 0, 50))
+				return new Toast({
+					content: "Location must have between 0 and 50 characters.",
+					timeout: 8000
+				});
+
+			return this.socket.emit(
+				"users.updateLocation",
+				this.userId,
+				location,
+				res => {
+					if (res.status !== "success")
+						new Toast({ content: res.message, timeout: 8000 });
+					else
+						new Toast({
+							content: "Successfully changed location",
+							timeout: 4000
+						});
+				}
+			);
+		},
+		changeBio() {
+			const { bio } = this.user;
+			if (!validation.isLength(bio, 0, 200))
+				return new Toast({
+					content: "Bio must have between 0 and 200 characters.",
+					timeout: 8000
+				});
+
+			return this.socket.emit(
+				"users.updateBio",
+				this.userId,
+				bio,
+				res => {
+					if (res.status !== "success")
+						new Toast({ content: res.message, timeout: 8000 });
+					else
+						new Toast({
+							content: "Successfully changed bio",
+							timeout: 4000
+						});
+				}
+			);
+		},
 		changePassword() {
 			const { newPassword } = this;
 			if (!validation.isLength(newPassword, 6, 200))