From 9384f5399da39e53855beb8e7f8ded1aa56bf72e Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Tue, 27 Dec 2022 14:36:33 +0900
Subject: rename: client -> frontend

---
 .../components/MkPushNotificationAllowButton.vue   | 167 +++++++++++++++++++++
 1 file changed, 167 insertions(+)
 create mode 100644 packages/frontend/src/components/MkPushNotificationAllowButton.vue

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

diff --git a/packages/frontend/src/components/MkPushNotificationAllowButton.vue b/packages/frontend/src/components/MkPushNotificationAllowButton.vue
new file mode 100644
index 0000000000..b356fd3b89
--- /dev/null
+++ b/packages/frontend/src/components/MkPushNotificationAllowButton.vue
@@ -0,0 +1,167 @@
+<template>
+<MkButton
+	v-if="supported && !pushRegistrationInServer"
+	type="button"
+	primary
+	:gradate="gradate"
+	:rounded="rounded"
+	:inline="inline"
+	:autofocus="autofocus"
+	:wait="wait"
+	:full="full"
+	@click="subscribe"
+>
+	{{ i18n.ts.subscribePushNotification }}
+</MkButton>
+<MkButton
+	v-else-if="!showOnlyToRegister && ($i ? pushRegistrationInServer : pushSubscription)"
+	type="button"
+	:primary="false"
+	:gradate="gradate"
+	:rounded="rounded"
+	:inline="inline"
+	:autofocus="autofocus"
+	:wait="wait"
+	:full="full"
+	@click="unsubscribe"
+>
+	{{ i18n.ts.unsubscribePushNotification }}
+</MkButton>
+<MkButton v-else-if="$i && pushRegistrationInServer" disabled :rounded="rounded" :inline="inline" :wait="wait" :full="full">
+	{{ i18n.ts.pushNotificationAlreadySubscribed }}
+</MkButton>
+<MkButton v-else-if="!supported" disabled :rounded="rounded" :inline="inline" :wait="wait" :full="full">
+	{{ i18n.ts.pushNotificationNotSupported }}
+</MkButton>
+</template>
+
+<script setup lang="ts">
+import { $i, getAccounts } from '@/account';
+import MkButton from '@/components/MkButton.vue';
+import { instance } from '@/instance';
+import { api, apiWithDialog, promiseDialog } from '@/os';
+import { i18n } from '@/i18n';
+
+defineProps<{
+	primary?: boolean;
+	gradate?: boolean;
+	rounded?: boolean;
+	inline?: boolean;
+	link?: boolean;
+	to?: string;
+	autofocus?: boolean;
+	wait?: boolean;
+	danger?: boolean;
+	full?: boolean;
+	showOnlyToRegister?: boolean;
+}>();
+
+// ServiceWorker registration
+let registration = $ref<ServiceWorkerRegistration | undefined>();
+// If this browser supports push notification
+let supported = $ref(false);
+// If this browser has already subscribed to push notification
+let pushSubscription = $ref<PushSubscription | null>(null);
+let pushRegistrationInServer = $ref<{ state?: string; key?: string; userId: string; endpoint: string; sendReadMessage: boolean; } | undefined>();
+
+function subscribe() {
+	if (!registration || !supported || !instance.swPublickey) return;
+
+	// SEE: https://developer.mozilla.org/en-US/docs/Web/API/PushManager/subscribe#Parameters
+	return promiseDialog(registration.pushManager.subscribe({
+		userVisibleOnly: true,
+		applicationServerKey: urlBase64ToUint8Array(instance.swPublickey),
+	})
+	.then(async subscription => {
+		pushSubscription = subscription;
+
+		// Register
+		pushRegistrationInServer = await api('sw/register', {
+			endpoint: subscription.endpoint,
+			auth: encode(subscription.getKey('auth')),
+			publickey: encode(subscription.getKey('p256dh')),
+		});
+	}, async err => { // When subscribe failed
+		// 通知が許可されていなかったとき
+		if (err?.name === 'NotAllowedError') {
+			console.info('User denied the notification permission request.');
+			return;
+		}
+
+		// 違うapplicationServerKey (または gcm_sender_id)のサブスクリプションが
+		// 既に存在していることが原因でエラーになった可能性があるので、
+		// そのサブスクリプションを解除しておく
+		// （これは実行されなさそうだけど、おまじない的に古い実装から残してある）
+		await unsubscribe();
+	}), null, null);
+}
+
+async function unsubscribe() {
+	if (!pushSubscription) return;
+
+	const endpoint = pushSubscription.endpoint;
+	const accounts = await getAccounts();
+
+	pushRegistrationInServer = undefined;
+
+	if ($i && accounts.length >= 2) {
+		apiWithDialog('sw/unregister', {
+			i: $i.token,
+			endpoint,
+		});
+	} else {
+		pushSubscription.unsubscribe();
+		apiWithDialog('sw/unregister', {
+			endpoint,
+		});
+		pushSubscription = null;
+	}
+}
+
+function encode(buffer: ArrayBuffer | null) {
+	return btoa(String.fromCharCode.apply(null, new Uint8Array(buffer)));
+}
+
+/**
+ * Convert the URL safe base64 string to a Uint8Array
+ * @param base64String base64 string
+ */
+ function urlBase64ToUint8Array(base64String: string): Uint8Array {
+	const padding = '='.repeat((4 - base64String.length % 4) % 4);
+	const base64 = (base64String + padding)
+		.replace(/-/g, '+')
+		.replace(/_/g, '/');
+
+	const rawData = window.atob(base64);
+	const outputArray = new Uint8Array(rawData.length);
+
+	for (let i = 0; i < rawData.length; ++i) {
+		outputArray[i] = rawData.charCodeAt(i);
+	}
+	return outputArray;
+}
+
+navigator.serviceWorker.ready.then(async swr => {
+	registration = swr;
+
+	pushSubscription = await registration.pushManager.getSubscription();
+
+	if (instance.swPublickey && ('PushManager' in window) && $i && $i.token) {
+		supported = true;
+
+		if (pushSubscription) {
+			const res = await api('sw/show-registration', {
+				endpoint: pushSubscription.endpoint,
+			});
+
+			if (res) {
+				pushRegistrationInServer = res;
+			}
+		}
+	}
+});
+
+defineExpose({
+	pushRegistrationInServer: $$(pushRegistrationInServer),
+});
+</script>
-- 
cgit v1.2.3-freya


From e6112506df73a8a38bb8b5a1fee5dd3149e989aa Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Thu, 29 Dec 2022 17:20:24 +0900
Subject: 通知設定が開けないのを修正
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../components/MkPushNotificationAllowButton.vue   | 32 ++++++++++++----------
 1 file changed, 18 insertions(+), 14 deletions(-)

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

diff --git a/packages/frontend/src/components/MkPushNotificationAllowButton.vue b/packages/frontend/src/components/MkPushNotificationAllowButton.vue
index b356fd3b89..b98c814f24 100644
--- a/packages/frontend/src/components/MkPushNotificationAllowButton.vue
+++ b/packages/frontend/src/components/MkPushNotificationAllowButton.vue
@@ -126,7 +126,7 @@ function encode(buffer: ArrayBuffer | null) {
  * Convert the URL safe base64 string to a Uint8Array
  * @param base64String base64 string
  */
- function urlBase64ToUint8Array(base64String: string): Uint8Array {
+function urlBase64ToUint8Array(base64String: string): Uint8Array {
 	const padding = '='.repeat((4 - base64String.length % 4) % 4);
 	const base64 = (base64String + padding)
 		.replace(/-/g, '+')
@@ -141,25 +141,29 @@ function encode(buffer: ArrayBuffer | null) {
 	return outputArray;
 }
 
-navigator.serviceWorker.ready.then(async swr => {
-	registration = swr;
+if (navigator.serviceWorker == null) {
+	// TODO: よしなに？
+} else {
+	navigator.serviceWorker.ready.then(async swr => {
+		registration = swr;
 
-	pushSubscription = await registration.pushManager.getSubscription();
+		pushSubscription = await registration.pushManager.getSubscription();
 
-	if (instance.swPublickey && ('PushManager' in window) && $i && $i.token) {
-		supported = true;
+		if (instance.swPublickey && ('PushManager' in window) && $i && $i.token) {
+			supported = true;
 
-		if (pushSubscription) {
-			const res = await api('sw/show-registration', {
-				endpoint: pushSubscription.endpoint,
-			});
+			if (pushSubscription) {
+				const res = await api('sw/show-registration', {
+					endpoint: pushSubscription.endpoint,
+				});
 
-			if (res) {
-				pushRegistrationInServer = res;
+				if (res) {
+					pushRegistrationInServer = res;
+				}
 			}
 		}
-	}
-});
+	});
+}
 
 defineExpose({
 	pushRegistrationInServer: $$(pushRegistrationInServer),
-- 
cgit v1.2.3-freya

