|
@@ -1,13 +1,16 @@
|
|
<script setup lang="ts">
|
|
<script setup lang="ts">
|
|
-import { defineAsyncComponent, ref, watch, reactive, onMounted } from "vue";
|
|
|
|
|
|
+import { defineAsyncComponent, onMounted } from "vue";
|
|
import { useRoute } from "vue-router";
|
|
import { useRoute } from "vue-router";
|
|
import Toast from "toasters";
|
|
import Toast from "toasters";
|
|
import { storeToRefs } from "pinia";
|
|
import { storeToRefs } from "pinia";
|
|
-import { useSettingsStore } from "@/stores/settings";
|
|
|
|
import { useWebsocketsStore } from "@/stores/websockets";
|
|
import { useWebsocketsStore } from "@/stores/websockets";
|
|
import { useUserAuthStore } from "@/stores/userAuth";
|
|
import { useUserAuthStore } from "@/stores/userAuth";
|
|
import { useModalsStore } from "@/stores/modals";
|
|
import { useModalsStore } from "@/stores/modals";
|
|
-import _validation from "@/validation";
|
|
|
|
|
|
+import validation from "@/validation";
|
|
|
|
+import { useEvents } from "@/composables/useEvents";
|
|
|
|
+import { useModels } from "@/composables/useModels";
|
|
|
|
+import { useWebsocketStore } from "@/stores/websocket";
|
|
|
|
+import { useForm } from "@/composables/useForm";
|
|
|
|
|
|
const InputHelpBox = defineAsyncComponent(
|
|
const InputHelpBox = defineAsyncComponent(
|
|
() => import("@/components/InputHelpBox.vue")
|
|
() => import("@/components/InputHelpBox.vue")
|
|
@@ -19,127 +22,66 @@ const QuickConfirm = defineAsyncComponent(
|
|
() => import("@/components/QuickConfirm.vue")
|
|
() => import("@/components/QuickConfirm.vue")
|
|
);
|
|
);
|
|
|
|
|
|
-const settingsStore = useSettingsStore();
|
|
|
|
-const userAuthStore = useUserAuthStore();
|
|
|
|
const route = useRoute();
|
|
const route = useRoute();
|
|
|
|
|
|
const { socket } = useWebsocketsStore();
|
|
const { socket } = useWebsocketsStore();
|
|
|
|
|
|
-const saveButton = ref();
|
|
|
|
-
|
|
|
|
-const { currentUser } = storeToRefs(userAuthStore);
|
|
|
|
-const { originalUser, modifiedUser } = settingsStore;
|
|
|
|
-
|
|
|
|
-const validation = reactive({
|
|
|
|
- username: {
|
|
|
|
- entered: false,
|
|
|
|
- valid: false,
|
|
|
|
- message: "Please enter a valid username."
|
|
|
|
- },
|
|
|
|
- email: {
|
|
|
|
- entered: false,
|
|
|
|
- valid: false,
|
|
|
|
- message: "Please enter a valid email address."
|
|
|
|
- }
|
|
|
|
-});
|
|
|
|
-
|
|
|
|
-const { updateOriginalUser } = settingsStore;
|
|
|
|
-
|
|
|
|
const { openModal } = useModalsStore();
|
|
const { openModal } = useModalsStore();
|
|
|
|
|
|
-const onInput = inputName => {
|
|
|
|
- validation[inputName].entered = true;
|
|
|
|
-};
|
|
|
|
-
|
|
|
|
-const changeEmail = () => {
|
|
|
|
- const email = modifiedUser.email.address;
|
|
|
|
- if (!_validation.isLength(email, 3, 254))
|
|
|
|
- return new Toast("Email must have between 3 and 254 characters.");
|
|
|
|
- if (
|
|
|
|
- email.indexOf("@") !== email.lastIndexOf("@") ||
|
|
|
|
- !_validation.regex.emailSimple.test(email)
|
|
|
|
- )
|
|
|
|
- return new Toast("Invalid email format.");
|
|
|
|
-
|
|
|
|
- saveButton.value.saveStatus = "disabled";
|
|
|
|
|
|
+const { runJob } = useWebsocketStore();
|
|
|
|
+const { onReady } = useEvents();
|
|
|
|
+const { registerModel } = useModels();
|
|
|
|
|
|
- return socket.dispatch(
|
|
|
|
- "users.updateEmail",
|
|
|
|
- currentUser.value?._id,
|
|
|
|
- email,
|
|
|
|
- res => {
|
|
|
|
- if (res.status !== "success") {
|
|
|
|
- new Toast(res.message);
|
|
|
|
- saveButton.value.handleFailedSave();
|
|
|
|
- } else {
|
|
|
|
- new Toast("Successfully changed email address");
|
|
|
|
|
|
+const userAuthStore = useUserAuthStore();
|
|
|
|
|
|
- updateOriginalUser({
|
|
|
|
- property: "email.address",
|
|
|
|
- value: email
|
|
|
|
- });
|
|
|
|
|
|
+const { currentUser } = storeToRefs(userAuthStore);
|
|
|
|
|
|
- saveButton.value.handleSuccessfulSave();
|
|
|
|
|
|
+const { inputs, saveButton, validate, save, setModelValues } = useForm(
|
|
|
|
+ {
|
|
|
|
+ username: {
|
|
|
|
+ value: null,
|
|
|
|
+ validate: (value: string) => {
|
|
|
|
+ if (!validation.isLength(value, 2, 32))
|
|
|
|
+ return "Username must have between 2 and 32 characters.";
|
|
|
|
+ if (!validation.regex.azAZ09_.test(value))
|
|
|
|
+ return "Invalid username format. Allowed characters: a-z, A-Z, 0-9 and _.";
|
|
|
|
+ if (value.replaceAll(/[_]/g, "").length === 0)
|
|
|
|
+ return "Invalid username format. Allowed characters: a-z, A-Z, 0-9 and _, and there has to be at least one letter or number.";
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ emailAddress: {
|
|
|
|
+ value: null,
|
|
|
|
+ validate: (value: string) => {
|
|
|
|
+ if (!validation.isLength(value, 3, 254))
|
|
|
|
+ return "Email address must have between 3 and 254 characters.";
|
|
|
|
+ if (
|
|
|
|
+ value.indexOf("@") !== value.lastIndexOf("@") ||
|
|
|
|
+ !validation.regex.emailSimple.test(value)
|
|
|
|
+ )
|
|
|
|
+ return "Invalid email address format.";
|
|
|
|
+ return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
- );
|
|
|
|
-};
|
|
|
|
-
|
|
|
|
-const changeUsername = () => {
|
|
|
|
- const { username } = modifiedUser;
|
|
|
|
-
|
|
|
|
- if (!_validation.isLength(username, 2, 32))
|
|
|
|
- return new Toast("Username must have between 2 and 32 characters.");
|
|
|
|
-
|
|
|
|
- if (!_validation.regex.azAZ09_.test(username))
|
|
|
|
- return new Toast(
|
|
|
|
- "Invalid username format. Allowed characters: a-z, A-Z, 0-9 and _."
|
|
|
|
- );
|
|
|
|
-
|
|
|
|
- if (username.replaceAll(/[_]/g, "").length === 0)
|
|
|
|
- return new Toast(
|
|
|
|
- "Invalid username format. Allowed characters: a-z, A-Z, 0-9 and _, and there has to be at least one letter or number."
|
|
|
|
- );
|
|
|
|
-
|
|
|
|
- saveButton.value.saveStatus = "disabled";
|
|
|
|
-
|
|
|
|
- return socket.dispatch(
|
|
|
|
- "users.updateUsername",
|
|
|
|
- currentUser.value?._id,
|
|
|
|
- username,
|
|
|
|
- res => {
|
|
|
|
- if (res.status !== "success") {
|
|
|
|
- new Toast(res.message);
|
|
|
|
- saveButton.value.handleFailedSave();
|
|
|
|
- } else {
|
|
|
|
- new Toast("Successfully changed username");
|
|
|
|
-
|
|
|
|
- updateOriginalUser({
|
|
|
|
- property: "username",
|
|
|
|
- value: username
|
|
|
|
|
|
+ },
|
|
|
|
+ ({ status, messages, values }, resolve, reject) => {
|
|
|
|
+ if (status === "success") {
|
|
|
|
+ runJob(`data.users.updateById`, {
|
|
|
|
+ _id: currentUser.value._id,
|
|
|
|
+ query: values
|
|
|
|
+ })
|
|
|
|
+ .then(resolve)
|
|
|
|
+ .catch(reject);
|
|
|
|
+ } else {
|
|
|
|
+ if (status === "unchanged") new Toast(messages.unchanged);
|
|
|
|
+ else if (status === "error")
|
|
|
|
+ Object.values(messages).forEach(message => {
|
|
|
|
+ new Toast({ content: message, timeout: 8000 });
|
|
});
|
|
});
|
|
-
|
|
|
|
- saveButton.value.handleSuccessfulSave();
|
|
|
|
- }
|
|
|
|
|
|
+ resolve();
|
|
}
|
|
}
|
|
- );
|
|
|
|
-};
|
|
|
|
-
|
|
|
|
-const saveChanges = () => {
|
|
|
|
- const usernameChanged = modifiedUser.username !== originalUser.username;
|
|
|
|
- const emailAddressChanged =
|
|
|
|
- modifiedUser.email.address !== originalUser.email.address;
|
|
|
|
-
|
|
|
|
- if (usernameChanged) changeUsername();
|
|
|
|
-
|
|
|
|
- if (emailAddressChanged) changeEmail();
|
|
|
|
-
|
|
|
|
- if (!usernameChanged && !emailAddressChanged) {
|
|
|
|
- saveButton.value.handleFailedSave();
|
|
|
|
-
|
|
|
|
- new Toast("Please make a change before saving.");
|
|
|
|
}
|
|
}
|
|
-};
|
|
|
|
|
|
+);
|
|
|
|
|
|
const removeActivities = () => {
|
|
const removeActivities = () => {
|
|
socket.dispatch("activities.removeAllForUser", res => {
|
|
socket.dispatch("activities.removeAllForUser", res => {
|
|
@@ -147,7 +89,14 @@ const removeActivities = () => {
|
|
});
|
|
});
|
|
};
|
|
};
|
|
|
|
|
|
-onMounted(() => {
|
|
|
|
|
|
+onMounted(async () => {
|
|
|
|
+ await onReady(async () => {
|
|
|
|
+ setModelValues(await registerModel(currentUser.value), [
|
|
|
|
+ "username",
|
|
|
|
+ "emailAddress"
|
|
|
|
+ ]);
|
|
|
|
+ });
|
|
|
|
+
|
|
if (
|
|
if (
|
|
route.query.removeAccount === "relinked-github" &&
|
|
route.query.removeAccount === "relinked-github" &&
|
|
!localStorage.getItem("github_redirect")
|
|
!localStorage.getItem("github_redirect")
|
|
@@ -158,55 +107,6 @@ onMounted(() => {
|
|
});
|
|
});
|
|
}
|
|
}
|
|
});
|
|
});
|
|
-
|
|
|
|
-watch(
|
|
|
|
- () => modifiedUser.username,
|
|
|
|
- value => {
|
|
|
|
- // const value = newModifiedUser.username;
|
|
|
|
-
|
|
|
|
- if (!_validation.isLength(value, 2, 32)) {
|
|
|
|
- validation.username.message =
|
|
|
|
- "Username must have between 2 and 32 characters.";
|
|
|
|
- validation.username.valid = false;
|
|
|
|
- } else if (
|
|
|
|
- !_validation.regex.azAZ09_.test(value) &&
|
|
|
|
- value !== originalUser.username // Sometimes a username pulled from GitHub won't succeed validation
|
|
|
|
- ) {
|
|
|
|
- validation.username.message =
|
|
|
|
- "Invalid format. Allowed characters: a-z, A-Z, 0-9 and _.";
|
|
|
|
- validation.username.valid = false;
|
|
|
|
- } else if (value.replaceAll(/[_]/g, "").length === 0) {
|
|
|
|
- validation.username.message =
|
|
|
|
- "Invalid format. Allowed characters: a-z, A-Z, 0-9 and _, and there has to be at least one letter or number.";
|
|
|
|
- validation.username.valid = false;
|
|
|
|
- } else {
|
|
|
|
- validation.username.message = "Everything looks great!";
|
|
|
|
- validation.username.valid = true;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-);
|
|
|
|
-
|
|
|
|
-watch(
|
|
|
|
- () => modifiedUser.email.address,
|
|
|
|
- value => {
|
|
|
|
- // const value = newModifiedUser.email.address;
|
|
|
|
-
|
|
|
|
- if (!_validation.isLength(value, 3, 254)) {
|
|
|
|
- validation.email.message =
|
|
|
|
- "Email must have between 3 and 254 characters.";
|
|
|
|
- validation.email.valid = false;
|
|
|
|
- } else if (
|
|
|
|
- value.indexOf("@") !== value.lastIndexOf("@") ||
|
|
|
|
- !_validation.regex.emailSimple.test(value)
|
|
|
|
- ) {
|
|
|
|
- validation.email.message = "Invalid format.";
|
|
|
|
- validation.email.valid = false;
|
|
|
|
- } else {
|
|
|
|
- validation.email.message = "Everything looks great!";
|
|
|
|
- validation.email.valid = true;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-);
|
|
|
|
</script>
|
|
</script>
|
|
|
|
|
|
<template>
|
|
<template>
|
|
@@ -224,21 +124,22 @@ watch(
|
|
id="username"
|
|
id="username"
|
|
type="text"
|
|
type="text"
|
|
placeholder="Enter username here..."
|
|
placeholder="Enter username here..."
|
|
- v-model="modifiedUser.username"
|
|
|
|
|
|
+ v-model="inputs.username.value"
|
|
|
|
+ @input="validate('username')"
|
|
maxlength="32"
|
|
maxlength="32"
|
|
autocomplete="off"
|
|
autocomplete="off"
|
|
- @keypress="onInput('username')"
|
|
|
|
- @paste="onInput('username')"
|
|
|
|
/>
|
|
/>
|
|
- <span v-if="modifiedUser.username" class="character-counter"
|
|
|
|
- >{{ modifiedUser.username.length }}/32</span
|
|
|
|
- >
|
|
|
|
|
|
+ <span v-if="inputs.username.value" class="character-counter">
|
|
|
|
+ {{ inputs.username.value.length }}/32
|
|
|
|
+ </span>
|
|
</p>
|
|
</p>
|
|
<transition name="fadein-helpbox">
|
|
<transition name="fadein-helpbox">
|
|
<input-help-box
|
|
<input-help-box
|
|
- :entered="validation.username.entered"
|
|
|
|
- :valid="validation.username.valid"
|
|
|
|
- :message="validation.username.message"
|
|
|
|
|
|
+ :entered="inputs.username.value?.length > 1"
|
|
|
|
+ :valid="inputs.username.errors.length === 0"
|
|
|
|
+ :message="
|
|
|
|
+ inputs.username.errors[0] ?? 'Everything looks great!'
|
|
|
|
+ "
|
|
/>
|
|
/>
|
|
</transition>
|
|
</transition>
|
|
|
|
|
|
@@ -249,22 +150,22 @@ watch(
|
|
id="email"
|
|
id="email"
|
|
type="text"
|
|
type="text"
|
|
placeholder="Enter email address here..."
|
|
placeholder="Enter email address here..."
|
|
- v-if="modifiedUser.email"
|
|
|
|
- v-model="modifiedUser.email.address"
|
|
|
|
- @keypress="onInput('email')"
|
|
|
|
- @paste="onInput('email')"
|
|
|
|
|
|
+ v-model="inputs.emailAddress.value"
|
|
|
|
+ @input="validate('emailAddress')"
|
|
autocomplete="off"
|
|
autocomplete="off"
|
|
/>
|
|
/>
|
|
</p>
|
|
</p>
|
|
<transition name="fadein-helpbox">
|
|
<transition name="fadein-helpbox">
|
|
<input-help-box
|
|
<input-help-box
|
|
- :entered="validation.email.entered"
|
|
|
|
- :valid="validation.email.valid"
|
|
|
|
- :message="validation.email.message"
|
|
|
|
|
|
+ :entered="inputs.emailAddress.value?.length > 1"
|
|
|
|
+ :valid="inputs.emailAddress.errors.length === 0"
|
|
|
|
+ :message="
|
|
|
|
+ inputs.emailAddress.errors[0] ?? 'Everything looks great!'
|
|
|
|
+ "
|
|
/>
|
|
/>
|
|
</transition>
|
|
</transition>
|
|
|
|
|
|
- <SaveButton ref="saveButton" @clicked="saveChanges()" />
|
|
|
|
|
|
+ <SaveButton ref="saveButton" @clicked="save()" />
|
|
|
|
|
|
<div class="section-margin-bottom" />
|
|
<div class="section-margin-bottom" />
|
|
|
|
|