From 249d7c7a0a4484d127d0ab171cfcd678dc56a8b7 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sat, 21 Aug 2021 12:48:50 +0900
Subject: [PATCH] enhance: Improve account deletion experience

---
 locales/ja-JP.yml                            |  8 +++
 src/client/pages/settings/delete-account.vue | 67 ++++++++++++++++++++
 src/client/pages/settings/index.vue          |  1 +
 src/client/pages/settings/other.vue          | 19 +-----
 src/queue/processors/db/delete-account.ts    | 12 +++-
 5 files changed, 88 insertions(+), 19 deletions(-)
 create mode 100644 src/client/pages/settings/delete-account.vue

diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index f27fc0abe..35739d276 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -779,6 +779,14 @@ translate: "翻訳"
 translatedFrom: "{x}から翻訳"
 accountDeletionInProgress: "アカウントの削除が進行中です"
 
+_accountDelete:
+  accountDelete: "アカウントの削除"
+  mayTakeTime: "アカウントの削除は負荷のかかる処理であるため、作成したコンテンツの数やアップロードしたファイルの数が多いと完了までに時間がかかることがあります。"
+  sendEmail: "アカウントの削除が完了する際は、登録してあったメールアドレス宛に通知を送信します。"
+  requestAccountDelete: "アカウント削除をリクエスト"
+  started: "削除処理が開始されました。"
+  inProgress: "削除が進行中"
+
 _docs: 
   continueReading: "続きを読む"
   features: "機能"
diff --git a/src/client/pages/settings/delete-account.vue b/src/client/pages/settings/delete-account.vue
new file mode 100644
index 000000000..3af187985
--- /dev/null
+++ b/src/client/pages/settings/delete-account.vue
@@ -0,0 +1,67 @@
+<template>
+<FormBase>
+	<FormInfo warn>{{ $ts._accountDelete.mayTakeTime }}</FormInfo>
+	<FormInfo>{{ $ts._accountDelete.sendEmail }}</FormInfo>
+	<FormButton @click="deleteAccount" danger v-if="!$i.isDeleted">{{ $ts._accountDelete.requestAccountDelete }}</FormButton>
+	<FormButton disabled v-else>{{ $ts._accountDelete.inProgress }}</FormButton>
+</FormBase>
+</template>
+
+<script lang="ts">
+import { defineAsyncComponent, defineComponent } from 'vue';
+import FormInfo from '@client/components/form/info.vue';
+import FormBase from '@client/components/form/base.vue';
+import FormGroup from '@client/components/form/group.vue';
+import FormButton from '@client/components/form/button.vue';
+import * as os from '@client/os';
+import { debug } from '@client/config';
+import { signout } from '@client/account';
+import * as symbols from '@client/symbols';
+
+export default defineComponent({
+	components: {
+		FormBase,
+		FormButton,
+		FormGroup,
+		FormInfo,
+	},
+
+	emits: ['info'],
+	
+	data() {
+		return {
+			[symbols.PAGE_INFO]: {
+				title: this.$ts._accountDelete.accountDelete,
+				icon: 'fas fa-exclamation-triangle'
+			},
+			debug,
+		}
+	},
+
+	mounted() {
+		this.$emit('info', this[symbols.PAGE_INFO]);
+	},
+
+	methods: {
+		async deleteAccount() {
+			const { canceled, result: password } = await os.dialog({
+				title: this.$ts.password,
+				input: {
+					type: 'password'
+				}
+			});
+			if (canceled) return;
+
+			await os.apiWithDialog('i/delete-account', {
+				password: password
+			});
+
+			await os.dialog({
+				title: this.$ts._accountDelete.started,
+			});
+
+			signout();
+		}
+	}
+});
+</script>
diff --git a/src/client/pages/settings/index.vue b/src/client/pages/settings/index.vue
index 17b373fcd..e7e250602 100644
--- a/src/client/pages/settings/index.vue
+++ b/src/client/pages/settings/index.vue
@@ -132,6 +132,7 @@ export default defineComponent({
 				case 'account-info': return defineAsyncComponent(() => import('./account-info.vue'));
 				case 'update': return defineAsyncComponent(() => import('./update.vue'));
 				case 'registry': return defineAsyncComponent(() => import('./registry.vue'));
+				case 'delete-account': return defineAsyncComponent(() => import('./delete-account.vue'));
 				case 'experimental-features': return defineAsyncComponent(() => import('./experimental-features.vue'));
 			}
 			if (page.value.startsWith('registry/keys/system/')) {
diff --git a/src/client/pages/settings/other.vue b/src/client/pages/settings/other.vue
index f73ff9cb2..685795035 100644
--- a/src/client/pages/settings/other.vue
+++ b/src/client/pages/settings/other.vue
@@ -26,7 +26,7 @@
 	<FormLink to="/bios" behavior="browser"><template #icon><i class="fas fa-door-open"></i></template>BIOS</FormLink>
 	<FormLink to="/cli" behavior="browser"><template #icon><i class="fas fa-door-open"></i></template>CLI</FormLink>
 
-	<FormButton @click="closeAccount" danger>{{ $ts.closeAccount }}</FormButton>
+	<FormLink to="./delete-account"><template #icon><i class="fas fa-exclamation-triangle"></i></template>{{ $ts.closeAccount }}</FormLink>
 </FormBase>
 </template>
 
@@ -41,7 +41,6 @@ import FormButton from '@client/components/form/button.vue';
 import * as os from '@client/os';
 import { debug } from '@client/config';
 import { defaultStore } from '@client/store';
-import { signout } from '@client/account';
 import { unisonReload } from '@client/scripts/unison-reload';
 import * as symbols from '@client/symbols';
 
@@ -92,22 +91,6 @@ export default defineComponent({
 			os.popup(import('@client/components/taskmanager.vue'), {
 			}, {}, 'closed');
 		},
-
-		closeAccount() {
-			os.dialog({
-				title: this.$ts.password,
-				input: {
-					type: 'password'
-				}
-			}).then(({ canceled, result: password }) => {
-				if (canceled) return;
-				os.api('i/delete-account', {
-					password: password
-				}).then(() => {
-					signout();
-				});
-			});
-		}
 	}
 });
 </script>
diff --git a/src/queue/processors/db/delete-account.ts b/src/queue/processors/db/delete-account.ts
index 95614b61a..65327754c 100644
--- a/src/queue/processors/db/delete-account.ts
+++ b/src/queue/processors/db/delete-account.ts
@@ -1,11 +1,12 @@
 import * as Bull from 'bull';
 import { queueLogger } from '../../logger';
-import { DriveFiles, Notes, Users } from '@/models/index';
+import { DriveFiles, Notes, UserProfiles, Users } from '@/models/index';
 import { DbUserJobData } from '@/queue/types';
 import { Note } from '@/models/entities/note';
 import { DriveFile } from '@/models/entities/drive-file';
 import { MoreThan } from 'typeorm';
 import { deleteFileSync } from '@/services/drive/delete-file';
+import { sendEmail } from '@/services/send-email';
 
 const logger = queueLogger.createSubLogger('delete-account');
 
@@ -73,6 +74,15 @@ export async function deleteAccount(job: Bull.Job<DbUserJobData>): Promise<strin
 		logger.succ(`All of files deleted`);
 	}
 
+	{ // Send email notification
+		const profile = await UserProfiles.findOneOrFail(user.id);
+		if (profile.email && profile.emailVerified) {
+			sendEmail(profile.email, 'Account deleted',
+				`Your account has been deleted.`,
+				`Your account has been deleted.`);
+		}
+	}
+
 	await Users.delete(job.data.user.id);
 
 	return 'Account deleted';