refactor: signup component as composition api (#8957)
This commit is contained in:
		
							parent
							
								
									a4b5a0072d
								
							
						
					
					
						commit
						7cb5b5c8c2
					
				
					 1 changed files with 216 additions and 237 deletions
				
			
		|  | @ -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> | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue