From 85a4c8dbb1cd455ac538d8004dafc47d4d6d400f Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Mon, 8 May 2023 17:29:19 +0900
Subject: feat(frontend): アカウント初期設定ウィザード (#10799)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* wip

* :art:

* :art:

* wip

* wip

* :art:

* Update CHANGELOG.md

* wip

* Update MkUserSetupDialog.vue

* add stories

Co-authored-by: Acid Chicken (硫酸鶏) <root@acid-chicken.com>

* update stories

* Update MkUserSetupDialog.Follow.stories.impl.ts

* test: load mock user account

* :v:

* :v:

* test: reset on each render

* test: use id to identify

---------

Co-authored-by: Acid Chicken (硫酸鶏) <root@acid-chicken.com>
---
 .../frontend/src/components/MkUserSetupDialog.vue  | 135 +++++++++++++++++++++
 1 file changed, 135 insertions(+)
 create mode 100644 packages/frontend/src/components/MkUserSetupDialog.vue

(limited to 'packages/frontend/src/components/MkUserSetupDialog.vue')

diff --git a/packages/frontend/src/components/MkUserSetupDialog.vue b/packages/frontend/src/components/MkUserSetupDialog.vue
new file mode 100644
index 0000000000..58afe09b61
--- /dev/null
+++ b/packages/frontend/src/components/MkUserSetupDialog.vue
@@ -0,0 +1,135 @@
+<template>
+<MkModalWindow
+	ref="dialog"
+	:width="500"
+	:height="550"
+	@close="close"
+	@closed="emit('closed')"
+>
+	<template #header>{{ i18n.ts.initialAccountSetting }}</template>
+
+	<div style="overflow-x: clip;">
+		<Transition
+			mode="out-in"
+			:enter-active-class="$style.transition_x_enterActive"
+			:leave-active-class="$style.transition_x_leaveActive"
+			:enter-from-class="$style.transition_x_enterFrom"
+			:leave-to-class="$style.transition_x_leaveTo"
+		>
+			<template v-if="page === 0">
+				<div :class="$style.centerPage">
+					<MkSpacer :margin-min="20" :margin-max="28">
+						<div class="_gaps" style="text-align: center;">
+							<i class="ti ti-confetti" style="display: block; margin: auto; font-size: 3em; color: var(--accent);"></i>
+							<div style="font-size: 120%;">{{ i18n.ts._initialAccountSetting.accountCreated }}</div>
+							<div>{{ i18n.ts._initialAccountSetting.letsFillYourProfile }}</div>
+							<MkButton primary rounded gradate style="margin: 16px auto 0 auto;" @click="page++">{{ i18n.ts._initialAccountSetting.profileSetting }} <i class="ti ti-arrow-right"></i></MkButton>
+						</div>
+					</MkSpacer>
+				</div>
+			</template>
+			<template v-else-if="page === 1">
+				<div style="height: 100cqh; overflow: auto;">
+					<MkSpacer :margin-min="20" :margin-max="28">
+						<XProfile/>
+						<MkButton primary rounded gradate style="margin: 16px auto 0 auto;" @click="page++">{{ i18n.ts.continue }} <i class="ti ti-arrow-right"></i></MkButton>
+					</MkSpacer>
+				</div>
+			</template>
+			<template v-else-if="page === 2">
+				<div style="height: 100cqh; overflow: auto;">
+					<MkSpacer :margin-min="20" :margin-max="28">
+						<XFollow/>
+						<MkButton primary rounded gradate style="margin: 16px auto 0 auto;" @click="page++">{{ i18n.ts.continue }} <i class="ti ti-arrow-right"></i></MkButton>
+					</MkSpacer>
+				</div>
+			</template>
+			<template v-else-if="page === 3">
+				<div :class="$style.centerPage">
+					<MkSpacer :margin-min="20" :margin-max="28">
+						<div class="_gaps" style="text-align: center;">
+							<i class="ti ti-bell-ringing-2" style="display: block; margin: auto; font-size: 3em; color: var(--accent);"></i>
+							<div style="font-size: 120%;">{{ i18n.ts.pushNotification }}</div>
+							<div style="padding: 0 16px;">{{ i18n.t('_initialAccountSetting.pushNotificationDescription', { name: instance.name ?? host }) }}</div>
+							<MkPushNotificationAllowButton primary show-only-to-register style="margin: 0 auto;"/>
+							<MkButton primary rounded gradate style="margin: 16px auto 0 auto;" @click="page++">{{ i18n.ts.continue }} <i class="ti ti-arrow-right"></i></MkButton>
+						</div>
+					</MkSpacer>
+				</div>
+			</template>
+			<template v-else-if="page === 4">
+				<div :class="$style.centerPage">
+					<MkSpacer :margin-min="20" :margin-max="28">
+						<div class="_gaps" style="text-align: center;">
+							<i class="ti ti-check" style="display: block; margin: auto; font-size: 3em; color: var(--accent);"></i>
+							<div style="font-size: 120%;">{{ i18n.ts._initialAccountSetting.initialAccountSettingCompleted }}</div>
+							<I18n :src="i18n.ts._initialAccountSetting.ifYouNeedLearnMore" tag="div" style="padding: 0 16px;">
+								<template #name>{{ instance.name ?? host }}</template>
+								<template #link>
+									<a href="https://misskey-hub.net/help.html" target="_blank" class="_link">{{ i18n.ts.help }}</a>
+								</template>
+							</I18n>
+							<div>{{ i18n.t('_initialAccountSetting.haveFun', { name: instance.name ?? host }) }}</div>
+							<MkButton primary rounded gradate style="margin: 16px auto 0 auto;" @click="close">{{ i18n.ts.close }}</MkButton>
+						</div>
+					</MkSpacer>
+				</div>
+			</template>
+		</Transition>
+	</div>
+</MkModalWindow>
+</template>
+
+<script lang="ts" setup>
+import { ref, shallowRef, watch } from 'vue';
+import MkModalWindow from '@/components/MkModalWindow.vue';
+import MkButton from '@/components/MkButton.vue';
+import XProfile from '@/components/MkUserSetupDialog.Profile.vue';
+import XFollow from '@/components/MkUserSetupDialog.Follow.vue';
+import { i18n } from '@/i18n';
+import { instance } from '@/instance';
+import { host } from '@/config';
+import MkPushNotificationAllowButton from '@/components/MkPushNotificationAllowButton.vue';
+import { defaultStore } from '@/store';
+
+const emit = defineEmits<{
+	(ev: 'closed'): void;
+}>();
+
+const dialog = shallowRef<InstanceType<typeof MkModalWindow>>();
+
+const page = ref(defaultStore.state.accountSetupWizard);
+
+watch(page, () => {
+	defaultStore.set('accountSetupWizard', page.value);
+});
+
+function close() {
+	dialog.value.close();
+	defaultStore.set('accountSetupWizard', -1);
+}
+</script>
+
+<style lang="scss" module>
+.transition_x_enterActive,
+.transition_x_leaveActive {
+	transition: opacity 0.3s cubic-bezier(0,0,.35,1), transform 0.3s cubic-bezier(0,0,.35,1);
+}
+.transition_x_enterFrom {
+	opacity: 0;
+	transform: translateX(50px);
+}
+.transition_x_leaveTo {
+	opacity: 0;
+	transform: translateX(-50px);
+}
+
+.centerPage {
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	height: 100cqh;
+	padding-bottom: 30px;
+	box-sizing: border-box;
+}
+</style>
-- 
cgit v1.2.3-freya


From 6fd63c1fb65432872455fc8b268edd7843fdb45e Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Mon, 8 May 2023 17:49:19 +0900
Subject: tweak of #10799

---
 locales/ja-JP.yml                                      |  1 +
 packages/frontend/src/components/MkUserSetupDialog.vue | 15 ++++++++++++---
 packages/frontend/src/init.ts                          |  3 +++
 3 files changed, 16 insertions(+), 3 deletions(-)

(limited to 'packages/frontend/src/components/MkUserSetupDialog.vue')

diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index c3dc2172c3..b886cc7bfd 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1050,6 +1050,7 @@ _initialAccountSetting:
   initialAccountSettingCompleted: "初期設定が完了しました！"
   haveFun: "{name}をお楽しみください！"
   ifYouNeedLearnMore: "{name}(Misskey)の使い方などを詳しく知るには{link}をご覧ください。"
+  skipAreYouSure: "初期設定をスキップしますか？"
 
 _serverRules:
   description: "新規登録前に表示する、サーバーの簡潔なルールを設定します。内容は利用規約の要約とすることを推奨します。"
diff --git a/packages/frontend/src/components/MkUserSetupDialog.vue b/packages/frontend/src/components/MkUserSetupDialog.vue
index 58afe09b61..a9d117e073 100644
--- a/packages/frontend/src/components/MkUserSetupDialog.vue
+++ b/packages/frontend/src/components/MkUserSetupDialog.vue
@@ -3,7 +3,7 @@
 	ref="dialog"
 	:width="500"
 	:height="550"
-	@close="close"
+	@close="close(true)"
 	@closed="emit('closed')"
 >
 	<template #header>{{ i18n.ts.initialAccountSetting }}</template>
@@ -70,7 +70,7 @@
 								</template>
 							</I18n>
 							<div>{{ i18n.t('_initialAccountSetting.haveFun', { name: instance.name ?? host }) }}</div>
-							<MkButton primary rounded gradate style="margin: 16px auto 0 auto;" @click="close">{{ i18n.ts.close }}</MkButton>
+							<MkButton primary rounded gradate style="margin: 16px auto 0 auto;" @click="close(false)">{{ i18n.ts.close }}</MkButton>
 						</div>
 					</MkSpacer>
 				</div>
@@ -91,6 +91,7 @@ import { instance } from '@/instance';
 import { host } from '@/config';
 import MkPushNotificationAllowButton from '@/components/MkPushNotificationAllowButton.vue';
 import { defaultStore } from '@/store';
+import * as os from '@/os';
 
 const emit = defineEmits<{
 	(ev: 'closed'): void;
@@ -104,7 +105,15 @@ watch(page, () => {
 	defaultStore.set('accountSetupWizard', page.value);
 });
 
-function close() {
+async function close(skip: boolean) {
+	if (skip) {
+		const { canceled } = await os.confirm({
+			type: 'warning',
+			text: i18n.ts._initialAccountSetting.skipAreYouSure,
+		});
+		if (canceled) return;
+	}
+
 	dialog.value.close();
 	defaultStore.set('accountSetupWizard', -1);
 }
diff --git a/packages/frontend/src/init.ts b/packages/frontend/src/init.ts
index eb67803240..49e7bb4008 100644
--- a/packages/frontend/src/init.ts
+++ b/packages/frontend/src/init.ts
@@ -345,8 +345,11 @@ if ($i) {
 
 	if (defaultStore.state.accountSetupWizard !== -1) {
 		// このウィザードが実装される前に登録したユーザーには表示させないため
+		// TODO: そのうち消す
 		if (Date.now() - new Date($i.createdAt).getTime() < 1000 * 60 * 60 * 24) {
 			popup(defineAsyncComponent(() => import('@/components/MkUserSetupDialog.vue')), {}, {}, 'closed');
+		} else {
+			defaultStore.set('accountSetupWizard', -1);
 		}
 	}
 
-- 
cgit v1.2.3-freya


From 8f36a807751f018d9422b05848ce44480e10e721 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Tue, 9 May 2023 08:01:54 +0900
Subject: fix e2e test

---
 cypress/e2e/basic.cy.js                            | 23 +++++++++++++++++++++-
 cypress/e2e/widgets.cy.js                          |  4 ++++
 packages/frontend/src/components/MkDialog.vue      |  4 ++--
 packages/frontend/src/components/MkModalWindow.vue |  2 +-
 .../frontend/src/components/MkUserSetupDialog.vue  | 11 ++++++-----
 5 files changed, 35 insertions(+), 9 deletions(-)

(limited to 'packages/frontend/src/components/MkUserSetupDialog.vue')

diff --git a/cypress/e2e/basic.cy.js b/cypress/e2e/basic.cy.js
index e271894ec1..e5e3a466ac 100644
--- a/cypress/e2e/basic.cy.js
+++ b/cypress/e2e/basic.cy.js
@@ -159,10 +159,31 @@ describe('After user signed in', () => {
 	});
 
   it('successfully loads', () => {
-		cy.get('[data-cy-open-post-form]').should('be.visible');
+		cy.get('[data-cy-user-setup-continue]').should('be.visible');
+  });
+
+	it('account setup wizard', () => {
+		cy.get('[data-cy-user-setup-continue]').click();
+
+		cy.get('[data-cy-user-setup-user-name] input').type('ありす');
+		cy.get('[data-cy-user-setup-user-description] textarea').type('ほげ');
+		// TODO: アイコン設定テスト
+
+		cy.get('[data-cy-user-setup-continue]').click();
+
+		// フォローはスキップ
+
+		cy.get('[data-cy-user-setup-continue]').click();
+
+		// プッシュ通知設定はスキップ
+
+		cy.get('[data-cy-user-setup-continue]').click();
+
+		cy.get('[data-cy-user-setup-continue]').click();
   });
 
 	it('note', () => {
+		cy.get('[data-cy-open-post-form]').should('be.visible');
 		cy.get('[data-cy-open-post-form]').click();
 		cy.get('[data-cy-post-form-text]').type('Hello, Misskey!');
 		cy.get('[data-cy-open-post-form-submit]').click();
diff --git a/cypress/e2e/widgets.cy.js b/cypress/e2e/widgets.cy.js
index 88e19030c1..f5a982eb0a 100644
--- a/cypress/e2e/widgets.cy.js
+++ b/cypress/e2e/widgets.cy.js
@@ -10,6 +10,10 @@ describe('After user signed in', () => {
 		cy.registerUser('alice', 'alice1234');
 
 		cy.login('alice', 'alice1234');
+
+		// アカウント初期設定ウィザード
+		cy.get('[data-cy-user-setup] [data-cy-modal-window-close]').click();
+		cy.get('[data-cy-modal-dialog-ok]').click();
 	});
 
 	afterEach(() => {
diff --git a/packages/frontend/src/components/MkDialog.vue b/packages/frontend/src/components/MkDialog.vue
index 7b9aa862ef..9f5404ce15 100644
--- a/packages/frontend/src/components/MkDialog.vue
+++ b/packages/frontend/src/components/MkDialog.vue
@@ -32,8 +32,8 @@
 			</template>
 		</MkSelect>
 		<div v-if="(showOkButton || showCancelButton) && !actions" :class="$style.buttons">
-			<MkButton v-if="showOkButton" inline primary rounded :autofocus="!input && !select" :disabled="okButtonDisabled" @click="ok">{{ okText ?? ((showCancelButton || input || select) ? i18n.ts.ok : i18n.ts.gotIt) }}</MkButton>
-			<MkButton v-if="showCancelButton || input || select" inline rounded @click="cancel">{{ cancelText ?? i18n.ts.cancel }}</MkButton>
+			<MkButton v-if="showOkButton" data-cy-modal-dialog-ok inline primary rounded :autofocus="!input && !select" :disabled="okButtonDisabled" @click="ok">{{ okText ?? ((showCancelButton || input || select) ? i18n.ts.ok : i18n.ts.gotIt) }}</MkButton>
+			<MkButton v-if="showCancelButton || input || select" data-cy-modal-dialog-cancel inline rounded @click="cancel">{{ cancelText ?? i18n.ts.cancel }}</MkButton>
 		</div>
 		<div v-if="actions" :class="$style.buttons">
 			<MkButton v-for="action in actions" :key="action.text" inline rounded :primary="action.primary" :danger="action.danger" @click="() => { action.callback(); modal?.close(); }">{{ action.text }}</MkButton>
diff --git a/packages/frontend/src/components/MkModalWindow.vue b/packages/frontend/src/components/MkModalWindow.vue
index f983f77750..1c942cfd0d 100644
--- a/packages/frontend/src/components/MkModalWindow.vue
+++ b/packages/frontend/src/components/MkModalWindow.vue
@@ -6,7 +6,7 @@
 			<span class="title">
 				<slot name="header"></slot>
 			</span>
-			<button v-if="!withOkButton" class="_button" @click="$emit('close')"><i class="ti ti-x"></i></button>
+			<button v-if="!withOkButton" class="_button" data-cy-modal-window-close @click="$emit('close')"><i class="ti ti-x"></i></button>
 			<button v-if="withOkButton" class="_button" :disabled="okButtonDisabled" @click="$emit('ok')"><i class="ti ti-check"></i></button>
 		</div>
 		<div class="body">
diff --git a/packages/frontend/src/components/MkUserSetupDialog.vue b/packages/frontend/src/components/MkUserSetupDialog.vue
index a9d117e073..c5e75276f0 100644
--- a/packages/frontend/src/components/MkUserSetupDialog.vue
+++ b/packages/frontend/src/components/MkUserSetupDialog.vue
@@ -3,6 +3,7 @@
 	ref="dialog"
 	:width="500"
 	:height="550"
+	data-cy-user-setup
 	@close="close(true)"
 	@closed="emit('closed')"
 >
@@ -23,7 +24,7 @@
 							<i class="ti ti-confetti" style="display: block; margin: auto; font-size: 3em; color: var(--accent);"></i>
 							<div style="font-size: 120%;">{{ i18n.ts._initialAccountSetting.accountCreated }}</div>
 							<div>{{ i18n.ts._initialAccountSetting.letsFillYourProfile }}</div>
-							<MkButton primary rounded gradate style="margin: 16px auto 0 auto;" @click="page++">{{ i18n.ts._initialAccountSetting.profileSetting }} <i class="ti ti-arrow-right"></i></MkButton>
+							<MkButton primary rounded gradate style="margin: 16px auto 0 auto;" data-cy-user-setup-continue @click="page++">{{ i18n.ts._initialAccountSetting.profileSetting }} <i class="ti ti-arrow-right"></i></MkButton>
 						</div>
 					</MkSpacer>
 				</div>
@@ -32,7 +33,7 @@
 				<div style="height: 100cqh; overflow: auto;">
 					<MkSpacer :margin-min="20" :margin-max="28">
 						<XProfile/>
-						<MkButton primary rounded gradate style="margin: 16px auto 0 auto;" @click="page++">{{ i18n.ts.continue }} <i class="ti ti-arrow-right"></i></MkButton>
+						<MkButton primary rounded gradate style="margin: 16px auto 0 auto;" data-cy-user-setup-continue @click="page++">{{ i18n.ts.continue }} <i class="ti ti-arrow-right"></i></MkButton>
 					</MkSpacer>
 				</div>
 			</template>
@@ -40,7 +41,7 @@
 				<div style="height: 100cqh; overflow: auto;">
 					<MkSpacer :margin-min="20" :margin-max="28">
 						<XFollow/>
-						<MkButton primary rounded gradate style="margin: 16px auto 0 auto;" @click="page++">{{ i18n.ts.continue }} <i class="ti ti-arrow-right"></i></MkButton>
+						<MkButton primary rounded gradate style="margin: 16px auto 0 auto;" data-cy-user-setup-continue @click="page++">{{ i18n.ts.continue }} <i class="ti ti-arrow-right"></i></MkButton>
 					</MkSpacer>
 				</div>
 			</template>
@@ -52,7 +53,7 @@
 							<div style="font-size: 120%;">{{ i18n.ts.pushNotification }}</div>
 							<div style="padding: 0 16px;">{{ i18n.t('_initialAccountSetting.pushNotificationDescription', { name: instance.name ?? host }) }}</div>
 							<MkPushNotificationAllowButton primary show-only-to-register style="margin: 0 auto;"/>
-							<MkButton primary rounded gradate style="margin: 16px auto 0 auto;" @click="page++">{{ i18n.ts.continue }} <i class="ti ti-arrow-right"></i></MkButton>
+							<MkButton primary rounded gradate style="margin: 16px auto 0 auto;" data-cy-user-setup-continue @click="page++">{{ i18n.ts.continue }} <i class="ti ti-arrow-right"></i></MkButton>
 						</div>
 					</MkSpacer>
 				</div>
@@ -70,7 +71,7 @@
 								</template>
 							</I18n>
 							<div>{{ i18n.t('_initialAccountSetting.haveFun', { name: instance.name ?? host }) }}</div>
-							<MkButton primary rounded gradate style="margin: 16px auto 0 auto;" @click="close(false)">{{ i18n.ts.close }}</MkButton>
+							<MkButton primary rounded gradate style="margin: 16px auto 0 auto;" data-cy-user-setup-continue @click="close(false)">{{ i18n.ts.close }}</MkButton>
 						</div>
 					</MkSpacer>
 				</div>
-- 
cgit v1.2.3-freya


From b16d7cc6c4b8d670e35e0b32e1527149e2c575ab Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Tue, 9 May 2023 08:09:16 +0900
Subject: chore(frontend): より柔軟な文言に変更
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 locales/ja-JP.yml                                      | 1 +
 packages/frontend/src/components/MkUserSetupDialog.vue | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

(limited to 'packages/frontend/src/components/MkUserSetupDialog.vue')

diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index b886cc7bfd..8bbf9459f5 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1041,6 +1041,7 @@ youFollowing: "フォロー中"
 
 _initialAccountSetting:
   accountCreated: "アカウントの作成が完了しました！"
+  letsStartAccountSetup: "アカウントの初期設定を行いましょう。"
   letsFillYourProfile: "まずはあなたのプロフィールを設定しましょう。"
   profileSetting: "プロフィール設定"
   theseSettingsCanEditLater: "これらの設定は後から変更できます。"
diff --git a/packages/frontend/src/components/MkUserSetupDialog.vue b/packages/frontend/src/components/MkUserSetupDialog.vue
index c5e75276f0..096b88c309 100644
--- a/packages/frontend/src/components/MkUserSetupDialog.vue
+++ b/packages/frontend/src/components/MkUserSetupDialog.vue
@@ -23,7 +23,7 @@
 						<div class="_gaps" style="text-align: center;">
 							<i class="ti ti-confetti" style="display: block; margin: auto; font-size: 3em; color: var(--accent);"></i>
 							<div style="font-size: 120%;">{{ i18n.ts._initialAccountSetting.accountCreated }}</div>
-							<div>{{ i18n.ts._initialAccountSetting.letsFillYourProfile }}</div>
+							<div>{{ i18n.ts._initialAccountSetting.letsStartAccountSetup }}</div>
 							<MkButton primary rounded gradate style="margin: 16px auto 0 auto;" data-cy-user-setup-continue @click="page++">{{ i18n.ts._initialAccountSetting.profileSetting }} <i class="ti ti-arrow-right"></i></MkButton>
 						</div>
 					</MkSpacer>
-- 
cgit v1.2.3-freya

