refactor: signup component as composition api (#8957)

This commit is contained in:
Johann150 2022-07-15 10:14:05 +02:00 committed by GitHub
parent a4b5a0072d
commit 7cb5b5c8c2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -1,7 +1,6 @@
<template> <template>
<form class="qlvuhzng _formRoot" autocomplete="new-password" @submit.prevent="onSubmit"> <form class="qlvuhzng _formRoot" autocomplete="new-password" @submit.prevent="onSubmit">
<template v-if="meta"> <MkInput v-if="instance.disableRegistration" v-model="invitationCode" class="_formBlock" type="text" :spellcheck="false" required>
<MkInput v-if="meta.disableRegistration" v-model="invitationCode" class="_formBlock" type="text" :spellcheck="false" required>
<template #label>{{ $ts.invitationCode }}</template> <template #label>{{ $ts.invitationCode }}</template>
<template #prefix><i class="fas fa-key"></i></template> <template #prefix><i class="fas fa-key"></i></template>
</MkInput> </MkInput>
@ -19,7 +18,7 @@
<span v-else-if="usernameState === 'max-range'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.tooLong }}</span> <span v-else-if="usernameState === 'max-range'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.tooLong }}</span>
</template> </template>
</MkInput> </MkInput>
<MkInput v-if="meta.emailRequiredForSignup" v-model="email" class="_formBlock" :debounce="true" type="email" :spellcheck="false" required data-cy-signup-email @update:modelValue="onChangeEmail"> <MkInput v-if="instance.emailRequiredForSignup" v-model="email" class="_formBlock" :debounce="true" type="email" :spellcheck="false" required data-cy-signup-email @update:modelValue="onChangeEmail">
<template #label>{{ $ts.emailAddress }} <div v-tooltip:dialog="$ts._signup.emailAddressInfo" class="_button _help"><i class="far fa-question-circle"></i></div></template> <template #label>{{ $ts.emailAddress }} <div v-tooltip:dialog="$ts._signup.emailAddressInfo" class="_button _help"><i class="far fa-question-circle"></i></div></template>
<template #prefix><i class="fas fa-envelope"></i></template> <template #prefix><i class="fas fa-envelope"></i></template>
<template #caption> <template #caption>
@ -51,205 +50,185 @@
<span v-if="passwordRetypeState == 'not-match'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.passwordNotMatched }}</span> <span v-if="passwordRetypeState == 'not-match'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.passwordNotMatched }}</span>
</template> </template>
</MkInput> </MkInput>
<MkSwitch v-if="meta.tosUrl" v-model="ToSAgreement" class="_formBlock tou"> <MkSwitch v-if="instance.tosUrl" v-model="ToSAgreement" class="_formBlock tou">
<I18n :src="$ts.agreeTo"> <I18n :src="$ts.agreeTo">
<template #0> <template #0>
<a :href="meta.tosUrl" class="_link" target="_blank">{{ $ts.tos }}</a> <a :href="instance.tosUrl" class="_link" target="_blank">{{ $ts.tos }}</a>
</template> </template>
</I18n> </I18n>
</MkSwitch> </MkSwitch>
<MkCaptcha v-if="meta.enableHcaptcha" ref="hcaptcha" v-model="hCaptchaResponse" class="_formBlock captcha" provider="hcaptcha" :sitekey="meta.hcaptchaSiteKey"/> <MkCaptcha v-if="instance.enableHcaptcha" ref="hcaptcha" v-model="hCaptchaResponse" class="_formBlock captcha" provider="hcaptcha" :sitekey="instance.hcaptchaSiteKey"/>
<MkCaptcha v-if="meta.enableRecaptcha" ref="recaptcha" v-model="reCaptchaResponse" class="_formBlock captcha" provider="recaptcha" :sitekey="meta.recaptchaSiteKey"/> <MkCaptcha v-if="instance.enableRecaptcha" ref="recaptcha" v-model="reCaptchaResponse" class="_formBlock captcha" provider="recaptcha" :sitekey="instance.recaptchaSiteKey"/>
<MkButton class="_formBlock" type="submit" :disabled="shouldDisableSubmitting" gradate data-cy-signup-submit>{{ $ts.start }}</MkButton> <MkButton class="_formBlock" type="submit" :disabled="shouldDisableSubmitting" gradate data-cy-signup-submit>{{ $ts.start }}</MkButton>
</template>
</form> </form>
</template> </template>
<script lang="ts"> <script lang="ts" setup>
import { defineComponent, defineAsyncComponent } from 'vue'; import { } from 'vue';
import getPasswordStrength from 'syuilo-password-strength'; import getPasswordStrength from 'syuilo-password-strength';
import { toUnicode } from 'punycode/'; import { toUnicode } from 'punycode/';
import MkButton from './ui/button.vue'; import MkButton from './ui/button.vue';
import MkCaptcha from './captcha.vue';
import MkInput from './form/input.vue'; import MkInput from './form/input.vue';
import MkSwitch from './form/switch.vue'; import MkSwitch from './form/switch.vue';
import { host, url } from '@/config'; import * as config from '@/config';
import * as os from '@/os'; import * as os from '@/os';
import { login } from '@/account'; import { login } from '@/account';
import { instance } from '@/instance';
import { i18n } from '@/i18n';
export default defineComponent({ const props = withDefaults(defineProps<{
components: { autoSet?: boolean;
MkButton, }>(), {
MkInput, autoSet: false,
MkSwitch, });
MkCaptcha: defineAsyncComponent(() => import('./captcha.vue')),
},
props: { const emit = defineEmits<{
autoSet: { (ev: 'signup', user: Record<string, any>): void;
type: Boolean, (ev: 'signupEmailPending'): void;
required: false, }>();
default: false,
},
},
emits: ['signup'], const host = toUnicode(config.host);
data() { let hcaptcha = $ref();
return { let recaptcha = $ref();
host: toUnicode(host),
username: '',
password: '',
retypedPassword: '',
invitationCode: '',
email: '',
url,
usernameState: null,
emailState: null,
passwordStrength: '',
passwordRetypeState: null,
submitting: false,
ToSAgreement: false,
hCaptchaResponse: null,
reCaptchaResponse: null,
};
},
computed: { let username: string = $ref('');
meta() { let password: string = $ref('');
return this.$instance; let retypedPassword: string = $ref('');
}, let invitationCode: string = $ref('');
let email = $ref('');
let usernameState: null | 'wait' | 'ok' | 'unavailable' | 'error' | 'invalid-format' | 'min-range' | 'max-range' = $ref(null);
let emailState: null | 'wait' | 'ok' | 'unavailable:used' | 'unavailable:format' | 'unavailable:disposable' | 'unavailable:mx' | 'unavailable:smtp' | 'unavailable' | 'error' = $ref(null);
let passwordStrength: '' | 'low' | 'medium' | 'high' = $ref('');
let passwordRetypeState: null | 'match' | 'not-match' = $ref(null);
let submitting: boolean = $ref(false);
let ToSAgreement: boolean = $ref(false);
let hCaptchaResponse = $ref(null);
let reCaptchaResponse = $ref(null);
shouldDisableSubmitting(): boolean { const shouldDisableSubmitting = $computed((): boolean => {
return this.submitting || return submitting ||
this.meta.tosUrl && !this.ToSAgreement || instance.tosUrl && !ToSAgreement ||
this.meta.enableHcaptcha && !this.hCaptchaResponse || instance.enableHcaptcha && !hCaptchaResponse ||
this.meta.enableRecaptcha && !this.reCaptchaResponse || instance.enableRecaptcha && !reCaptchaResponse ||
this.passwordRetypeState === 'not-match'; passwordRetypeState === 'not-match';
}, });
shouldShowProfileUrl(): boolean { function onChangeUsername(): void {
return (this.username !== '' && if (username === '') {
this.usernameState !== 'invalid-format' && usernameState = null;
this.usernameState !== 'min-range' &&
this.usernameState !== 'max-range');
},
},
methods: {
onChangeUsername() {
if (this.username === '') {
this.usernameState = null;
return; return;
} }
{
const err = const err =
!this.username.match(/^[a-zA-Z0-9_]+$/) ? 'invalid-format' : !username.match(/^[a-zA-Z0-9_]+$/) ? 'invalid-format' :
this.username.length < 1 ? 'min-range' : username.length < 1 ? 'min-range' :
this.username.length > 20 ? 'max-range' : username.length > 20 ? 'max-range' :
null; null;
if (err) { if (err) {
this.usernameState = err; usernameState = err;
return; return;
} }
}
this.usernameState = 'wait'; usernameState = 'wait';
os.api('username/available', { os.api('username/available', {
username: this.username, username,
}).then(result => { }).then(result => {
this.usernameState = result.available ? 'ok' : 'unavailable'; usernameState = result.available ? 'ok' : 'unavailable';
}).catch(err => { }).catch(() => {
this.usernameState = 'error'; usernameState = 'error';
}); });
}, }
onChangeEmail() { function onChangeEmail(): void {
if (this.email === '') { if (email === '') {
this.emailState = null; emailState = null;
return; return;
} }
this.emailState = 'wait'; emailState = 'wait';
os.api('email-address/available', { os.api('email-address/available', {
emailAddress: this.email, emailAddress: email,
}).then(result => { }).then(result => {
this.emailState = result.available ? 'ok' : emailState = result.available ? 'ok' :
result.reason === 'used' ? 'unavailable:used' : result.reason === 'used' ? 'unavailable:used' :
result.reason === 'format' ? 'unavailable:format' : result.reason === 'format' ? 'unavailable:format' :
result.reason === 'disposable' ? 'unavailable:disposable' : result.reason === 'disposable' ? 'unavailable:disposable' :
result.reason === 'mx' ? 'unavailable:mx' : result.reason === 'mx' ? 'unavailable:mx' :
result.reason === 'smtp' ? 'unavailable:smtp' : result.reason === 'smtp' ? 'unavailable:smtp' :
'unavailable'; 'unavailable';
}).catch(err => { }).catch(() => {
this.emailState = 'error'; emailState = 'error';
}); });
}, }
onChangePassword() { function onChangePassword(): void {
if (this.password === '') { if (password === '') {
this.passwordStrength = ''; passwordStrength = '';
return; return;
} }
const strength = getPasswordStrength(this.password); const strength = getPasswordStrength(password);
this.passwordStrength = strength > 0.7 ? 'high' : strength > 0.3 ? 'medium' : 'low'; passwordStrength = strength > 0.7 ? 'high' : strength > 0.3 ? 'medium' : 'low';
}, }
onChangePasswordRetype() { function onChangePasswordRetype(): void {
if (this.retypedPassword === '') { if (retypedPassword === '') {
this.passwordRetypeState = null; passwordRetypeState = null;
return; return;
} }
this.passwordRetypeState = this.password === this.retypedPassword ? 'match' : 'not-match'; passwordRetypeState = password === retypedPassword ? 'match' : 'not-match';
}, }
onSubmit() { function onSubmit(): void {
if (this.submitting) return; if (submitting) return;
this.submitting = true; submitting = true;
os.api('signup', { os.api('signup', {
username: this.username, username,
password: this.password, password,
emailAddress: this.email, emailAddress: email,
invitationCode: this.invitationCode, invitationCode,
'hcaptcha-response': this.hCaptchaResponse, 'hcaptcha-response': hCaptchaResponse,
'g-recaptcha-response': this.reCaptchaResponse, 'g-recaptcha-response': reCaptchaResponse,
}).then(() => { }).then(() => {
if (this.meta.emailRequiredForSignup) { if (instance.emailRequiredForSignup) {
os.alert({ os.alert({
type: 'success', type: 'success',
title: this.$ts._signup.almostThere, title: i18n.ts._signup.almostThere,
text: this.$t('_signup.emailSent', { email: this.email }), text: i18n.t('_signup.emailSent', { email }),
}); });
this.$emit('signupEmailPending'); emit('signupEmailPending');
} else { } else {
os.api('signin', { os.api('signin', {
username: this.username, username,
password: this.password, password,
}).then(res => { }).then(res => {
this.$emit('signup', res); emit('signup', res);
if (this.autoSet) { if (props.autoSet) {
login(res.i); login(res.i);
} }
}); });
} }
}).catch(() => { }).catch(() => {
this.submitting = false; submitting = false;
this.$refs.hcaptcha?.reset?.(); hcaptcha.reset?.();
this.$refs.recaptcha?.reset?.(); recaptcha.reset?.();
os.alert({ os.alert({
type: 'error', type: 'error',
text: this.$ts.somethingHappened, text: i18n.ts.somethingHappened,
}); });
}); });
}, }
},
});
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>