| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472 | <template>	<div>		<metadata title="Settings" />		<main-header />		<div class="container">			<!--Implement Validation-->			<label class="label">Username</label>			<div class="control is-grouped">				<p class="control is-expanded has-icon has-icon-right">					<input						v-model="user.username"						class="input"						type="text"						placeholder="Change username"					/>					<!--Remove validation if it's their own without changing-->				</p>				<p class="control">					<button class="button is-success" @click="changeUsername()">						Save changes					</button>				</p>			</div>			<label class="label">Email</label>			<div v-if="user.email" class="control is-grouped">				<p class="control is-expanded has-icon has-icon-right">					<input						v-model="user.email.address"						class="input"						type="text"						placeholder="Change email address"					/>					<!--Remove validation if it's their own without changing-->				</p>				<p class="control is-expanded">					<button class="button is-success" @click="changeEmail()">						Save changes					</button>				</p>			</div>			<label v-if="password" class="label">Change Password</label>			<div v-if="password" class="control is-grouped">				<p class="control is-expanded has-icon has-icon-right">					<input						v-model="newPassword"						class="input"						type="password"						placeholder="Change password"					/>				</p>				<p class="control is-expanded">					<button class="button is-success" @click="changePassword()">						Change password					</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					:checked="nightmode"					type="checkbox"					@click="toggleNightmode()"				/>			</div>			<label v-if="!password" class="label">Add password</label>			<div v-if="!password" class="control is-grouped">				<button					v-if="passwordStep === 1"					class="button is-success"					@click="requestPassword()"				>					Request password email				</button>				<br />				<p					v-if="passwordStep === 2"					class="control is-expanded has-icon has-icon-right"				>					<input						v-model="passwordCode"						class="input"						type="text"						placeholder="Code"					/>				</p>				<p v-if="passwordStep === 2" class="control is-expanded">					<button class="button is-success" v-on:click="verifyCode()">						Verify code					</button>				</p>				<p					v-if="passwordStep === 3"					class="control is-expanded has-icon has-icon-right"				>					<input						v-model="setNewPassword"						class="input"						type="password"						placeholder="New password"					/>				</p>				<p v-if="passwordStep === 3" class="control is-expanded">					<button class="button is-success" @click="setPassword()">						Set password					</button>				</p>			</div>			<a				v-if="passwordStep === 1 && !password"				href="#"				@click="passwordStep = 2"				>Skip this step</a			>			<a				v-if="!github"				class="button is-github"				:href="`${serverDomain}/auth/github/link`"			>				<div class="icon">					<img class="invert" src="/assets/social/github.svg" />				</div>				  Link GitHub to account			</a>			<button				v-if="password && github"				class="button is-danger"				@click="unlinkPassword()"			>				Remove logging in with password			</button>			<button				v-if="password && github"				class="button is-danger"				@click="unlinkGitHub()"			>				Remove logging in with GitHub			</button>			<br />			<button				class="button is-warning"				style="margin-top: 30px;"				@click="removeSessions()"			>				Log out everywhere			</button>		</div>		<main-footer />	</div></template><script>import { mapState, mapActions } from "vuex";import Toast from "toasters";import MainHeader from "../MainHeader.vue";import MainFooter from "../MainFooter.vue";import io from "../../io";import validation from "../../validation";export default {	components: { MainHeader, MainFooter },	data() {		return {			user: {},			newPassword: "",			password: false,			github: false,			setNewPassword: "",			passwordStep: 1,			passwordCode: "",			serverDomain: ""		};	},	computed: mapState({		userId: state => state.user.auth.userId,		nightmode: state => state.user.preferences.nightmode	}),	mounted() {		lofig.get("serverDomain").then(serverDomain => {			this.serverDomain = serverDomain;		});		io.getSocket(socket => {			this.socket = socket;			this.socket.emit("users.findBySession", res => {				if (res.status === "success") {					this.user = res.data;					this.password = this.user.password;					this.github = this.user.github;				} else {					new Toast({						content: "Your are currently not signed in",						timeout: 3000					});				}			});			this.socket.on("event:user.linkPassword", () => {				this.password = true;			});			this.socket.on("event:user.linkGitHub", () => {				this.github = true;			});			this.socket.on("event:user.unlinkPassword", () => {				this.password = false;			});			this.socket.on("event:user.unlinkGitHub", () => {				this.github = false;			});		});	},	methods: {		changeEmail() {			const email = this.user.email.address;			if (!validation.isLength(email, 3, 254))				return new Toast({					content: "Email must have between 3 and 254 characters.",					timeout: 8000				});			if (				email.indexOf("@") !== email.lastIndexOf("@") ||				!validation.regex.emailSimple.test(email)			)				return new Toast({					content: "Invalid email format.",					timeout: 8000				});			return this.socket.emit(				"users.updateEmail",				this.userId,				email,				res => {					if (res.status !== "success")						new Toast({ content: res.message, timeout: 8000 });					else						new Toast({							content: "Successfully changed email address",							timeout: 4000						});				}			);		},		changeUsername() {			const { username } = this.user;			if (!validation.isLength(username, 2, 32))				return new Toast({					content: "Username must have between 2 and 32 characters.",					timeout: 8000				});			if (!validation.regex.azAZ09_.test(username))				return new Toast({					content:						"Invalid username format. Allowed characters: a-z, A-Z, 0-9 and _.",					timeout: 8000				});			return this.socket.emit(				"users.updateUsername",				this.userId,				username,				res => {					if (res.status !== "success")						new Toast({ content: res.message, timeout: 8000 });					else						new Toast({							content: "Successfully changed username",							timeout: 4000						});				}			);		},		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))				return new Toast({					content: "Password must have between 6 and 200 characters.",					timeout: 8000				});			if (!validation.regex.password.test(newPassword))				return new Toast({					content:						"Invalid password format. Must have one lowercase letter, one uppercase letter, one number and one special character.",					timeout: 8000				});			return this.socket.emit(				"users.updatePassword",				newPassword,				res => {					if (res.status !== "success")						new Toast({ content: res.message, timeout: 8000 });					else						new Toast({							content: "Successfully changed password",							timeout: 4000						});				}			);		},		requestPassword() {			return this.socket.emit("users.requestPassword", res => {				new Toast({ content: res.message, timeout: 8000 });				if (res.status === "success") {					this.passwordStep = 2;				}			});		},		verifyCode() {			if (!this.passwordCode)				return new Toast({					content: "Code cannot be empty",					timeout: 8000				});			return this.socket.emit(				"users.verifyPasswordCode",				this.passwordCode,				res => {					new Toast({ content: res.message, timeout: 8000 });					if (res.status === "success") {						this.passwordStep = 3;					}				}			);		},		setPassword() {			const newPassword = this.setNewPassword;			if (!validation.isLength(newPassword, 6, 200))				return new Toast({					content: "Password must have between 6 and 200 characters.",					timeout: 8000				});			if (!validation.regex.password.test(newPassword))				return new Toast({					content:						"Invalid password format. Must have one lowercase letter, one uppercase letter, one number and one special character.",					timeout: 8000				});			return this.socket.emit(				"users.changePasswordWithCode",				this.passwordCode,				newPassword,				res => {					new Toast({ content: res.message, timeout: 8000 });				}			);		},		unlinkPassword() {			this.socket.emit("users.unlinkPassword", res => {				new Toast({ content: res.message, timeout: 8000 });			});		},		unlinkGitHub() {			this.socket.emit("users.unlinkGitHub", res => {				new Toast({ content: res.message, timeout: 8000 });			});		},		removeSessions() {			this.socket.emit(`users.removeSessions`, this.userId, res => {				new Toast({ content: res.message, timeout: 4000 });			});		},		toggleNightmode() {			localStorage.setItem("nightmode", !this.nightmode);			this.changeNightmode(!this.nightmode);		},		...mapActions("user/preferences", ["changeNightmode"])	}};</script><style lang="scss" scoped>@import "styles/global.scss";.night-mode {	.label {		color: #ddd;	}}.container {	padding: 25px;}a {	color: $primary-color !important;}</style>
 |