mirror of
				https://codeberg.org/yeentown/barkey.git
				synced 2025-10-31 13:34:12 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			178 lines
		
	
	
	
		
			5 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
			
		
		
	
	
			178 lines
		
	
	
	
		
			5 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
| <!--
 | |
| SPDX-FileCopyrightText: syuilo and misskey-project
 | |
| SPDX-License-Identifier: AGPL-3.0-only
 | |
| -->
 | |
| 
 | |
| <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 { ref } from 'vue';
 | |
| import { $i, getAccounts } from '@/account.js';
 | |
| import MkButton from '@/components/MkButton.vue';
 | |
| import { instance } from '@/instance.js';
 | |
| import { apiWithDialog, promiseDialog } from '@/os.js';
 | |
| import { misskeyApi } from '@/scripts/misskey-api.js';
 | |
| import { i18n } from '@/i18n.js';
 | |
| 
 | |
| 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
 | |
| const registration = ref<ServiceWorkerRegistration | undefined>();
 | |
| // If this browser supports push notification
 | |
| const supported = ref(false);
 | |
| // If this browser has already subscribed to push notification
 | |
| const pushSubscription = ref<PushSubscription | null>(null);
 | |
| const pushRegistrationInServer = ref<{ state?: string; key?: string; userId: string; endpoint: string; sendReadMessage: boolean; } | undefined>();
 | |
| 
 | |
| function subscribe() {
 | |
| 	if (!registration.value || !supported.value || !instance.swPublickey) return;
 | |
| 
 | |
| 	// SEE: https://developer.mozilla.org/en-US/docs/Web/API/PushManager/subscribe#Parameters
 | |
| 	return promiseDialog(registration.value.pushManager.subscribe({
 | |
| 		userVisibleOnly: true,
 | |
| 		applicationServerKey: urlBase64ToUint8Array(instance.swPublickey),
 | |
| 	})
 | |
| 		.then(async subscription => {
 | |
| 			pushSubscription.value = subscription;
 | |
| 
 | |
| 			// Register
 | |
| 			pushRegistrationInServer.value = await misskeyApi('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.value) return;
 | |
| 
 | |
| 	const endpoint = pushSubscription.value.endpoint;
 | |
| 	const accounts = await getAccounts();
 | |
| 
 | |
| 	pushRegistrationInServer.value = undefined;
 | |
| 
 | |
| 	if ($i && accounts.length >= 2) {
 | |
| 		apiWithDialog('sw/unregister', {
 | |
| 			i: $i.token,
 | |
| 			endpoint,
 | |
| 		});
 | |
| 	} else {
 | |
| 		pushSubscription.value.unsubscribe();
 | |
| 		apiWithDialog('sw/unregister', {
 | |
| 			endpoint,
 | |
| 		});
 | |
| 		pushSubscription.value = null;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| function encode(buffer: ArrayBuffer | null) {
 | |
| 	return btoa(String.fromCharCode.apply(null, buffer ? new Uint8Array(buffer) as any : []));
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * 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;
 | |
| }
 | |
| 
 | |
| if (navigator.serviceWorker == null) {
 | |
| 	// TODO: よしなに?
 | |
| } else {
 | |
| 	navigator.serviceWorker.ready.then(async swr => {
 | |
| 		registration.value = swr;
 | |
| 
 | |
| 		pushSubscription.value = await registration.value.pushManager.getSubscription();
 | |
| 
 | |
| 		if (instance.swPublickey && ('PushManager' in window) && $i && $i.token) {
 | |
| 			supported.value = true;
 | |
| 
 | |
| 			if (pushSubscription.value) {
 | |
| 				const res = await misskeyApi('sw/show-registration', {
 | |
| 					endpoint: pushSubscription.value.endpoint,
 | |
| 				});
 | |
| 
 | |
| 				if (res) {
 | |
| 					pushRegistrationInServer.value = res;
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	});
 | |
| }
 | |
| 
 | |
| defineExpose({
 | |
| 	pushRegistrationInServer: pushRegistrationInServer,
 | |
| });
 | |
| </script>
 |