From d6e85ffb596632892347b93661b7ddb9f66db6c6 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sat, 18 Dec 2021 14:55:53 +0900
Subject: [PATCH] feat(client): improve toast component and show welcome
 message

---
 locales/ja-JP.yml                             |  1 +
 .../src/components/notification-toast.vue     | 74 +++++++++++++++++++
 packages/client/src/components/toast.vue      | 71 +++++++++---------
 packages/client/src/init.ts                   | 14 +++-
 packages/client/src/os.ts                     |  4 +-
 packages/client/src/ui/_common_/common.vue    |  2 +-
 6 files changed, 126 insertions(+), 40 deletions(-)
 create mode 100644 packages/client/src/components/notification-toast.vue

diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index daac05c66c..084fd6448e 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -816,6 +816,7 @@ hide: "隠す"
 leaveGroup: "グループから抜ける"
 leaveGroupConfirm: "「{name}」から抜けますか?"
 useDrawerReactionPickerForMobile: "モバイルデバイスのときドロワーで表示"
+welcomeBackWithName: "おかえりなさい、{name}さん"
 
 _emailUnavailable:
   used: "既に使用されています"
diff --git a/packages/client/src/components/notification-toast.vue b/packages/client/src/components/notification-toast.vue
new file mode 100644
index 0000000000..5449409ccc
--- /dev/null
+++ b/packages/client/src/components/notification-toast.vue
@@ -0,0 +1,74 @@
+<template>
+<div class="mk-notification-toast" :style="{ zIndex }">
+	<transition name="notification-toast" appear @after-leave="$emit('closed')">
+		<XNotification v-if="showing" :notification="notification" class="notification _acrylic"/>
+	</transition>
+</div>
+</template>
+
+<script lang="ts">
+import { defineComponent } from 'vue';
+import XNotification from './notification.vue';
+import * as os from '@/os';
+
+export default defineComponent({
+	components: {
+		XNotification
+	},
+	props: {
+		notification: {
+			type: Object,
+			required: true
+		}
+	},
+	emits: ['closed'],
+	data() {
+		return {
+			showing: true,
+			zIndex: os.claimZIndex('high'),
+		};
+	},
+	mounted() {
+		setTimeout(() => {
+			this.showing = false;
+		}, 6000);
+	}
+});
+</script>
+
+<style lang="scss" scoped>
+.notification-toast-enter-active, .notification-toast-leave-active {
+	transition: opacity 0.3s, transform 0.3s !important;
+}
+.notification-toast-enter-from, .notification-toast-leave-to {
+	opacity: 0;
+	transform: translateX(-250px);
+}
+
+.mk-notification-toast {
+	position: fixed;
+	left: 0;
+	width: 250px;
+	top: 32px;
+	padding: 0 32px;
+	pointer-events: none;
+
+	@media (max-width: 700px) {
+		top: initial;
+		bottom: 112px;
+		padding: 0 16px;
+	}
+
+	@media (max-width: 500px) {
+		bottom: 92px;
+		padding: 0 8px;
+	}
+
+	> .notification {
+		height: 100%;
+		box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
+		border-radius: 8px;
+		overflow: hidden;
+	}
+}
+</style>
diff --git a/packages/client/src/components/toast.vue b/packages/client/src/components/toast.vue
index 02b61e5bef..d9f0aa5eaa 100644
--- a/packages/client/src/components/toast.vue
+++ b/packages/client/src/components/toast.vue
@@ -1,25 +1,25 @@
 <template>
-<div class="mk-toast" :style="{ zIndex }">
-	<transition name="notification-slide" appear @after-leave="$emit('closed')">
-		<XNotification v-if="showing" :notification="notification" class="notification _acrylic"/>
+<div class="mk-toast">
+	<transition name="toast" appear @after-leave="$emit('closed')">
+		<div v-if="showing" class="body _acrylic" :style="{ zIndex }">
+			<div class="message">
+				{{ message }}
+			</div>
+		</div>
 	</transition>
 </div>
 </template>
 
 <script lang="ts">
 import { defineComponent } from 'vue';
-import XNotification from './notification.vue';
 import * as os from '@/os';
 
 export default defineComponent({
-	components: {
-		XNotification
-	},
 	props: {
-		notification: {
-			type: Object,
-			required: true
-		}
+		message: {
+			type: String,
+			required: true,
+		},
 	},
 	emits: ['closed'],
 	data() {
@@ -31,44 +31,41 @@ export default defineComponent({
 	mounted() {
 		setTimeout(() => {
 			this.showing = false;
-		}, 6000);
+		}, 4000);
 	}
 });
 </script>
 
 <style lang="scss" scoped>
-.notification-slide-enter-active, .notification-slide-leave-active {
+.toast-enter-active, .toast-leave-active {
 	transition: opacity 0.3s, transform 0.3s !important;
 }
-.notification-slide-enter-from, .notification-slide-leave-to {
+.toast-enter-from, .toast-leave-to {
 	opacity: 0;
-	transform: translateX(-250px);
+	transform: translateY(calc(0px - (100% - 32px)));
 }
 
 .mk-toast {
-	position: fixed;
-	left: 0;
-	width: 250px;
-	top: 32px;
-	padding: 0 32px;
-	pointer-events: none;
-
-	@media (max-width: 700px) {
-		top: initial;
-		bottom: 112px;
-		padding: 0 16px;
-	}
-
-	@media (max-width: 500px) {
-		bottom: 92px;
-		padding: 0 8px;
-	}
-
-	> .notification {
-		height: 100%;
+	> .body {
+		position: fixed;
+		left: 0;
+		right: 0;
+		top: 0;
+		margin: 0 auto;
+		padding-top: 32px;
+		margin-top: -32px;
+		min-width: 300px;
+		max-width: calc(100% - 32px);
+		width: min-content;
 		box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
-		border-radius: 8px;
-		overflow: hidden;
+		border-radius: 0 0 8px 8px;
+		overflow: clip;
+		text-align: center;
+		pointer-events: none;
+
+		> .message {
+			padding: 16px 24px;
+		}
 	}
 }
 </style>
diff --git a/packages/client/src/init.ts b/packages/client/src/init.ts
index f0368cc1d7..82a1e169ce 100644
--- a/packages/client/src/init.ts
+++ b/packages/client/src/init.ts
@@ -26,7 +26,7 @@ import { router } from '@/router';
 import { applyTheme } from '@/scripts/theme';
 import { isDeviceDarkmode } from '@/scripts/is-device-darkmode';
 import { i18n } from '@/i18n';
-import { stream, confirm, alert, post, popup } from '@/os';
+import { stream, confirm, alert, post, popup, toast } from '@/os';
 import * as sound from '@/scripts/sound';
 import { $i, refreshAccount, login, updateAccount, signout } from '@/account';
 import { defaultStore, ColdDeviceStorage } from '@/store';
@@ -342,6 +342,18 @@ if ($i) {
 		});
 	}
 
+	const lastUsed = localStorage.getItem('lastUsed');
+	if (lastUsed) {
+		const lastUsedDate = parseInt(lastUsed, 10);
+		// 二時間以上前なら
+		if (Date.now() - lastUsedDate > 1000 * 60 * 60 * 2) {
+			toast(i18n.t('welcomeBackWithName', {
+				name: $i.name || $i.username,
+			}));
+		}
+	}
+	localStorage.setItem('lastUsed', Date.now().toString());
+
 	if ('Notification' in window) {
 		// 許可を得ていなかったらリクエスト
 		if (Notification.permission === 'default') {
diff --git a/packages/client/src/os.ts b/packages/client/src/os.ts
index 665151b017..4ed69e0ec0 100644
--- a/packages/client/src/os.ts
+++ b/packages/client/src/os.ts
@@ -221,7 +221,9 @@ export function modalPageWindow(path: string) {
 }
 
 export function toast(message: string) {
-	// TODO
+	popup(import('@/components/toast.vue'), {
+		message
+	}, {}, 'closed');
 }
 
 export function alert(props: {
diff --git a/packages/client/src/ui/_common_/common.vue b/packages/client/src/ui/_common_/common.vue
index 68f5f779ff..956ec556c1 100644
--- a/packages/client/src/ui/_common_/common.vue
+++ b/packages/client/src/ui/_common_/common.vue
@@ -34,7 +34,7 @@ export default defineComponent({
 					id: notification.id
 				});
 
-				popup(import('@/components/toast.vue'), {
+				popup(import('@/components/notification-toast.vue'), {
 					notification
 				}, {}, 'closed');
 			}