summaryrefslogtreecommitdiff
path: root/packages/frontend/src/components/MkSignin.password.vue
diff options
context:
space:
mode:
authorかっこかり <67428053+kakkokari-gtyih@users.noreply.github.com>2024-10-04 15:23:33 +0900
committerGitHub <noreply@github.com>2024-10-04 15:23:33 +0900
commit975c2e7bc567618c3f8b0082afcba6530d679dae (patch)
tree2268801e42af4285f851b08077bbe9d569755156 /packages/frontend/src/components/MkSignin.password.vue
parentUpdate generate.tsx (diff)
downloadmisskey-975c2e7bc567618c3f8b0082afcba6530d679dae.tar.gz
misskey-975c2e7bc567618c3f8b0082afcba6530d679dae.tar.bz2
misskey-975c2e7bc567618c3f8b0082afcba6530d679dae.zip
enhance(frontend): サインイン画面の改善 (#14658)
* wip * Update MkSignin.vue * Update MkSignin.vue * wip * Update CHANGELOG.md * enhance(frontend): サインイン画面の改善 * Update Changelog * 14655の変更取り込み * spdx * fix * fix * fix * :art: * :art: * :art: * :art: * Captchaがリセットされない問題を修正 * 次の処理をsignin apiから読み取るように * Add Comments * fix * fix test * attempt to fix test * fix test * fix test * fix test * fix * fix test * fix: 一部のエラーがちゃんと出るように * Update Changelog * :art: * :art: * remove border --------- Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
Diffstat (limited to 'packages/frontend/src/components/MkSignin.password.vue')
-rw-r--r--packages/frontend/src/components/MkSignin.password.vue181
1 files changed, 181 insertions, 0 deletions
diff --git a/packages/frontend/src/components/MkSignin.password.vue b/packages/frontend/src/components/MkSignin.password.vue
new file mode 100644
index 0000000000..2d79e2aeb1
--- /dev/null
+++ b/packages/frontend/src/components/MkSignin.password.vue
@@ -0,0 +1,181 @@
+<!--
+SPDX-FileCopyrightText: syuilo and misskey-project
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<div :class="$style.wrapper" data-cy-signin-page-password>
+ <div class="_gaps" :class="$style.root">
+ <div :class="$style.avatar" :style="{ backgroundImage: user ? `url('${user.avatarUrl}')` : undefined }"></div>
+ <div :class="$style.welcomeBackMessage">
+ <I18n :src="i18n.ts.welcomeBackWithName" tag="span">
+ <template #name><Mfm :text="user.name ?? user.username" :plain="true"/></template>
+ </I18n>
+ </div>
+
+ <!-- password入力 -->
+ <form class="_gaps_s" @submit.prevent="onSubmit">
+ <!-- ブラウザ オートコンプリート用 -->
+ <input type="hidden" name="username" autocomplete="username" :value="user.username">
+
+ <MkInput v-model="password" :placeholder="i18n.ts.password" type="password" autocomplete="current-password webauthn" :withPasswordToggle="true" required autofocus data-cy-signin-password>
+ <template #prefix><i class="ti ti-lock"></i></template>
+ <template #caption><button class="_textButton" type="button" @click="resetPassword">{{ i18n.ts.forgotPassword }}</button></template>
+ </MkInput>
+
+ <div v-if="needCaptcha">
+ <MkCaptcha v-if="instance.enableHcaptcha" ref="hcaptcha" v-model="hCaptchaResponse" :class="$style.captcha" provider="hcaptcha" :sitekey="instance.hcaptchaSiteKey"/>
+ <MkCaptcha v-if="instance.enableMcaptcha" ref="mcaptcha" v-model="mCaptchaResponse" :class="$style.captcha" provider="mcaptcha" :sitekey="instance.mcaptchaSiteKey" :instanceUrl="instance.mcaptchaInstanceUrl"/>
+ <MkCaptcha v-if="instance.enableRecaptcha" ref="recaptcha" v-model="reCaptchaResponse" :class="$style.captcha" provider="recaptcha" :sitekey="instance.recaptchaSiteKey"/>
+ <MkCaptcha v-if="instance.enableTurnstile" ref="turnstile" v-model="turnstileResponse" :class="$style.captcha" provider="turnstile" :sitekey="instance.turnstileSiteKey"/>
+ </div>
+
+ <MkButton type="submit" :disabled="needCaptcha && captchaFailed" large primary rounded style="margin: 0 auto;" data-cy-signin-page-password-continue>{{ i18n.ts.continue }} <i class="ti ti-arrow-right"></i></MkButton>
+ </form>
+ </div>
+</div>
+</template>
+
+<script lang="ts">
+export type PwResponse = {
+ password: string;
+ captcha: {
+ hCaptchaResponse: string | null;
+ mCaptchaResponse: string | null;
+ reCaptchaResponse: string | null;
+ turnstileResponse: string | null;
+ };
+};
+</script>
+
+<script setup lang="ts">
+import { ref, computed, useTemplateRef, defineAsyncComponent } from 'vue';
+import * as Misskey from 'misskey-js';
+
+import { instance } from '@/instance.js';
+import { i18n } from '@/i18n.js';
+import * as os from '@/os.js';
+
+import MkButton from '@/components/MkButton.vue';
+import MkInput from '@/components/MkInput.vue';
+import MkCaptcha from '@/components/MkCaptcha.vue';
+
+const props = defineProps<{
+ user: Misskey.entities.UserDetailed;
+ needCaptcha: boolean;
+}>();
+
+const emit = defineEmits<{
+ (ev: 'passwordSubmitted', v: PwResponse): void;
+}>();
+
+const password = ref('');
+
+const hCaptcha = useTemplateRef('hcaptcha');
+const mCaptcha = useTemplateRef('mcaptcha');
+const reCaptcha = useTemplateRef('recaptcha');
+const turnstile = useTemplateRef('turnstile');
+
+const hCaptchaResponse = ref<string | null>(null);
+const mCaptchaResponse = ref<string | null>(null);
+const reCaptchaResponse = ref<string | null>(null);
+const turnstileResponse = ref<string | null>(null);
+
+const captchaFailed = computed((): boolean => {
+ return (
+ (instance.enableHcaptcha && !hCaptchaResponse.value) ||
+ (instance.enableMcaptcha && !mCaptchaResponse.value) ||
+ (instance.enableRecaptcha && !reCaptchaResponse.value) ||
+ (instance.enableTurnstile && !turnstileResponse.value)
+ );
+});
+
+function resetPassword(): void {
+ const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkForgotPassword.vue')), {}, {
+ closed: () => dispose(),
+ });
+}
+
+function onSubmit() {
+ emit('passwordSubmitted', {
+ password: password.value,
+ captcha: {
+ hCaptchaResponse: hCaptchaResponse.value,
+ mCaptchaResponse: mCaptchaResponse.value,
+ reCaptchaResponse: reCaptchaResponse.value,
+ turnstileResponse: turnstileResponse.value,
+ },
+ });
+}
+
+function resetCaptcha() {
+ hCaptcha.value?.reset();
+ mCaptcha.value?.reset();
+ reCaptcha.value?.reset();
+ turnstile.value?.reset();
+}
+
+defineExpose({
+ resetCaptcha,
+});
+</script>
+
+<style lang="scss" module>
+.wrapper {
+ display: flex;
+ align-items: center;
+ width: 100%;
+ min-height: 336px;
+
+ > .root {
+ width: 100%;
+ }
+}
+
+.avatar {
+ margin: 0 auto 0 auto;
+ width: 64px;
+ height: 64px;
+ background: #ddd;
+ background-position: center;
+ background-size: cover;
+ border-radius: 100%;
+}
+
+.welcomeBackMessage {
+ text-align: center;
+ font-size: 1.1em;
+}
+
+.instanceManualSelectButton {
+ display: block;
+ text-align: center;
+ opacity: .7;
+ font-size: .8em;
+
+ &:hover {
+ text-decoration: underline;
+ }
+}
+
+.orHr {
+ position: relative;
+ margin: .4em auto;
+ width: 100%;
+ height: 1px;
+ background: var(--divider);
+}
+
+.orMsg {
+ position: absolute;
+ top: -.6em;
+ display: inline-block;
+ padding: 0 1em;
+ background: var(--panel);
+ font-size: 0.8em;
+ color: var(--fgOnPanel);
+ margin: 0;
+ left: 50%;
+ transform: translateX(-50%);
+}
+</style>