diff options
| author | Acid Chicken (硫酸鶏) <root@acid-chicken.com> | 2020-04-29 09:15:18 +0900 |
|---|---|---|
| committer | Acid Chicken (硫酸鶏) <root@acid-chicken.com> | 2020-04-29 09:15:18 +0900 |
| commit | 77adf26236855b4b43a1072ad2dc6a904bef7d39 (patch) | |
| tree | cf2255dfaa64d8d12ae1d369235738a1d3f58061 /src/client/components | |
| parent | Refactor code (diff) | |
| download | misskey-77adf26236855b4b43a1072ad2dc6a904bef7d39.tar.gz misskey-77adf26236855b4b43a1072ad2dc6a904bef7d39.tar.bz2 misskey-77adf26236855b4b43a1072ad2dc6a904bef7d39.zip | |
Factorize *captcha component
Diffstat (limited to 'src/client/components')
| -rw-r--r-- | src/client/components/captcha.vue | 119 | ||||
| -rw-r--r-- | src/client/components/hcaptcha.vue | 77 | ||||
| -rw-r--r-- | src/client/components/signup-dialog.vue | 2 | ||||
| -rw-r--r-- | src/client/components/signup.vue | 33 |
4 files changed, 137 insertions, 94 deletions
diff --git a/src/client/components/captcha.vue b/src/client/components/captcha.vue new file mode 100644 index 0000000000..6b1ee6f0b2 --- /dev/null +++ b/src/client/components/captcha.vue @@ -0,0 +1,119 @@ +<template> +<div> + <span v-if="!available">{{ $t('waiting') }}<mk-ellipsis/></span> + <div ref="captcha"></div> +</div> +</template> + +<script lang="ts"> +import Vue from 'vue'; +import i18n from '../i18n'; + +type Captcha = { + render(container: string | Node, options: { + readonly [_ in 'sitekey' | 'theme' | 'type' | 'size' | 'tabindex' | 'callback' | 'expired' | 'expired-callback' | 'error-callback' | 'endpoint']?: unknown; + }): string; + remove(id: string): void; + execute(id: string): void; + reset(id: string): void; + getResponse(id: string): string; +}; + +type CaptchaProvider = 'hcaptcha' | 'grecaptcha'; + +type CaptchaContainer = { + readonly [_ in CaptchaProvider]?: Captcha; +}; + +declare global { + interface Window extends CaptchaContainer { + } +} + +export default Vue.extend({ + i18n, + props: { + provider: { + type: String, + required: true, + }, + sitekey: { + type: String, + required: true, + }, + value: { + type: String, + }, + }, + + data() { + return { + available: false, + }; + }, + + computed: { + loaded() { + return !!window[this.provider as CaptchaProvider]; + }, + src() { + const endpoint = ({ + hcaptcha: 'https://hcaptcha.com/1', + grecaptcha: 'https://www.google.com/recaptcha', + } as Record<PropertyKey, unknown>)[this.provider]; + + return `${typeof endpoint == 'string' ? endpoint : 'about:invalid'}/api.js?render=explicit`; + }, + captcha() { + return window[this.provider as CaptchaProvider] || {} as unknown as Captcha; + }, + }, + + created() { + if (this.loaded) { + this.available = true; + } else { + (document.getElementById(this.provider) || document.head.appendChild(Object.assign(document.createElement('script'), { + async: true, + id: this.provider, + src: this.src, + }))) + .addEventListener('load', () => this.available = true); + } + }, + + mounted() { + if (this.available) { + this.requestRender(); + } else { + this.$watch('available', this.requestRender); + } + }, + + beforeDestroy() { + this.reset(); + }, + + methods: { + reset() { + this.captcha?.reset(); + }, + requestRender() { + if (this.captcha.render && this.$refs.captcha instanceof Element) { + this.captcha.render(this.$refs.captcha, { + sitekey: this.sitekey, + theme: this.$store.state.device.darkMode ? 'dark' : 'light', + callback: this.callback, + 'expired-callback': this.callback, + 'error-callback': this.callback, + }); + } else { + setTimeout(this.requestRender.bind(this), 1); + } + }, + callback(response?: string) { + this.$emit('input', typeof response == 'string' ? response : null); + }, + }, +}); +</script> diff --git a/src/client/components/hcaptcha.vue b/src/client/components/hcaptcha.vue deleted file mode 100644 index 4bc03342fe..0000000000 --- a/src/client/components/hcaptcha.vue +++ /dev/null @@ -1,77 +0,0 @@ -<template> - <div ref="hCaptcha"></div> -</template> - -<script lang="ts"> -import Vue from 'vue'; - -declare global { - interface Window { - hcaptcha?: { - render(container: string | Node, options: { - readonly [_ in 'sitekey' | 'theme' | 'type' | 'size' | 'tabindex' | 'callback' | 'expired' | 'expired-callback' | 'error-callback' | 'endpoint']?: unknown; - }): string; - remove(id: string): void; - execute(id: string): void; - reset(id: string): void; - getResponse(id: string): string; - }; - } -} - -export default Vue.extend({ - props: { - sitekey: { - type: String, - required: true, - }, - value: { - type: String, - }, - }, - - data() { - return { - available: false, - }; - }, - - created() { - if (window.hcaptcha) { // loaded - this.available = true; - } else { - (document.getElementById('hcaptcha') || document.head.appendChild(Object.assign(document.createElement('script'), { - async: true, - id: 'hcaptcha', - src: 'https://hcaptcha.com/1/api.js?render=explicit', - }))) - .addEventListener('load', () => this.available = true); - } - }, - - mounted() { - if (this.available) { - this.render(); - } else { - this.$watch('available', this.render); - } - }, - - methods: { - render() { - if (this.$refs.hCaptcha instanceof Element) { - window.hcaptcha!.render(this.$refs.hCaptcha, { - sitekey: this.sitekey, - theme: this.$store.state.device.darkMode ? 'dark' : 'light', - callback: this.callback, - 'expired-callback': this.callback, - 'error-callback': this.callback, - }); - } - }, - callback(response?: string) { - this.$emit('input', typeof response == 'string' ? response : null); - }, - }, -}); -</script> diff --git a/src/client/components/signup-dialog.vue b/src/client/components/signup-dialog.vue index 10cdf3a567..4db79af512 100644 --- a/src/client/components/signup-dialog.vue +++ b/src/client/components/signup-dialog.vue @@ -1,5 +1,5 @@ <template> -<x-window ref="window" @closed="() => { $emit('closed'); destroyDom(); }"> +<x-window ref="window" :width="366" :height="506" @closed="() => { $emit('closed'); destroyDom(); }"> <template #header>{{ $t('signup') }}</template> <x-signup :auto-set="autoSet" @signup="onSignup"/> </x-window> diff --git a/src/client/components/signup.vue b/src/client/components/signup.vue index 5d16a82bac..7e8bac7760 100644 --- a/src/client/components/signup.vue +++ b/src/client/components/signup.vue @@ -41,8 +41,8 @@ <a :href="meta.tosUrl" class="_link" target="_blank">{{ $t('tos') }}</a> </i18n> </mk-switch> - <div v-if="meta.enableRecaptcha" class="g-recaptcha" :data-sitekey="meta.recaptchaSiteKey" style="margin: 16px 0;"></div> - <h-captcha v-if="meta.enableHcaptcha" v-model="hCaptchaResponse" :sitekey="meta.hcaptchaSiteKey"/> + <captcha v-if="meta.enableHcaptcha" class="captcha" provider="hcaptcha" ref="hcaptcha" v-model="hCaptchaResponse" :sitekey="meta.hcaptchaSiteKey"/> + <captcha v-if="meta.enableRecaptcha" class="captcha" provider="grecaptcha" ref="recaptcha" v-model="reCaptchaResponse" :sitekey="meta.recaptchaSiteKey"/> <mk-button type="submit" :disabled="shouldDisableSubmitting" primary>{{ $t('start') }}</mk-button> </template> </form> @@ -66,7 +66,7 @@ export default Vue.extend({ MkButton, MkInput, MkSwitch, - hCaptcha: () => import('./hcaptcha.vue').then(x => x.default), + captcha: () => import('./captcha.vue').then(x => x.default), }, data() { @@ -83,6 +83,7 @@ export default Vue.extend({ submitting: false, ToSAgreement: false, hCaptchaResponse: null, + reCaptchaResponse: null, faLock, faExclamationTriangle, faSpinner, faCheck, faKey } }, @@ -124,14 +125,6 @@ export default Vue.extend({ } }, - mounted() { - if (this.meta.enableRecaptcha) { - const script = document.createElement('script'); - script.setAttribute('src', 'https://www.google.com/recaptcha/api.js'); - document.head.appendChild(script); - } - }, - methods: { onChangeUsername() { if (this.username == '') { @@ -189,7 +182,7 @@ export default Vue.extend({ password: this.password, invitationCode: this.invitationCode, 'hcaptcha-response': this.hCaptchaResponse, - 'g-recaptcha-response': this.meta.enableRecaptcha ? (window as any).grecaptcha.getResponse() : null + 'g-recaptcha-response': this.meta.reCaptchaResponse, }).then(() => { this.$root.api('signin', { username: this.username, @@ -199,17 +192,25 @@ export default Vue.extend({ }); }).catch(() => { this.submitting = false; + this.$refs.hcaptcha?.reset?.(); + this.$refs.recaptcha?.reset?.(); this.$root.dialog({ type: 'error', text: this.$t('error') }); - - if (this.meta.enableRecaptcha) { - (window as any).grecaptcha.reset(); - } }); } } }); </script> + +<style lang="scss" scoped> +.mk-signup { + padding: 32px 0 0; + + .captcha { + margin: 16px 0; + } +} +</style> |