add guest subscribe push notification
This commit is contained in:
@@ -0,0 +1,226 @@
|
||||
<template>
|
||||
<div class="text-sm">
|
||||
<div class="flex items-center gap-2">
|
||||
<div v-if="isPending" class="w-5 h-5">
|
||||
<ProgressSpinner style="width: 100%; height: 100%" strokeWidth="8" fill="transparent" />
|
||||
</div>
|
||||
<Checkbox v-else v-model="enabled" inputId="webPush" name="delivery" binary />
|
||||
|
||||
<label for="webPush">Показать уведомление, когда придет ответ</label>
|
||||
</div>
|
||||
|
||||
<!-- Инструкции для iOS -->
|
||||
<div
|
||||
v-if="showRequireStandaloneInstructionsForIOS"
|
||||
class="bg-yellow-100 text-yellow-800 p-4 rounded my-4"
|
||||
>
|
||||
<p class="mb-2">Чтобы включить уведомления, добавьте это приложение на главный экран:</p>
|
||||
<ul class="list-disc pl-5">
|
||||
<li class="mb-1">
|
||||
Нажмите
|
||||
<strong class="font-semibold">
|
||||
"<img
|
||||
src="@/assets/icons/apple-share.svg?url"
|
||||
alt="Поделиться"
|
||||
class="h-7 -mt-2 inline-block"
|
||||
/>"
|
||||
</strong>
|
||||
внизу экрана
|
||||
</li>
|
||||
<li class="mb-1">
|
||||
Выберите
|
||||
<strong class="inline-flex items-baseline font-semibold">
|
||||
<span class="me-1">"На экран «Домой»</span>
|
||||
<img
|
||||
src="@/assets/icons/apple-system-plus.svg?url"
|
||||
alt=""
|
||||
class="h-4 inline-block self-center"
|
||||
/>"
|
||||
</strong>
|
||||
</li>
|
||||
<li>Откройте приложение с главного экрана и повторите попытку.</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Ошибка разрешений -->
|
||||
<p v-else-if="isError" class="text-red-500 mb-2">{{ error?.message || error }}<br /></p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import { useMutation } from '@tanstack/vue-query'
|
||||
import {
|
||||
getVapidPublicKey,
|
||||
getWebPushSubscriptionDocument,
|
||||
saveWebPushSubscription,
|
||||
} from '@/api/webPush'
|
||||
import { getUserChatSettingsDocument } from '@/api/userChatSettings'
|
||||
import type { UserChatSettingsDocument } from '@hereconnect/types'
|
||||
import { isNotFoundError } from '@/api/PouchDB'
|
||||
import { saveUserChatSettings } from '@/api/userChatSettings'
|
||||
|
||||
// Проверки iOS и запуска из главного экрана
|
||||
const isIOS = computed(
|
||||
() => /iPad|iPhone|iPod/.test(navigator.userAgent) && !('MSStream' in window && window.MSStream),
|
||||
)
|
||||
const isStandalone = computed(() => 'standalone' in window.navigator && window.navigator.standalone)
|
||||
|
||||
const { qr_code_uri, chat, user_chat_role } = defineProps<{
|
||||
qr_code_uri: UserChatSettingsDocument['qr_code_uri']
|
||||
chat: UserChatSettingsDocument['chat']
|
||||
user_chat_role: UserChatSettingsDocument['user_chat_role']
|
||||
}>()
|
||||
|
||||
const enabled = ref<boolean>(false)
|
||||
|
||||
const subscribeOptionsPromise = getVapidPublicKey().then((applicationServerKey) => ({
|
||||
userVisibleOnly: true,
|
||||
applicationServerKey,
|
||||
}))
|
||||
|
||||
// Функция запроса разрешений и подписки
|
||||
async function requestNotificationPermission() {
|
||||
if (!('Notification' in window)) {
|
||||
throw new Error(
|
||||
isIOS.value && !isStandalone.value
|
||||
? 'Добавьте приложение на главный экран, чтобы использовать уведомления.'
|
||||
: 'Уведомления не поддерживаются этим браузером.',
|
||||
)
|
||||
}
|
||||
|
||||
const permission = await Notification.requestPermission()
|
||||
if (permission !== 'granted') {
|
||||
throw new Error(`Разрешение на уведомления не предоставлено: ${permission}`)
|
||||
}
|
||||
|
||||
const registration = await navigator.serviceWorker.ready
|
||||
let subscription: PushSubscription | null = null
|
||||
|
||||
try {
|
||||
subscription = await registration.pushManager.subscribe(await subscribeOptionsPromise)
|
||||
} catch (error) {
|
||||
subscription = await registration.pushManager.getSubscription()
|
||||
if (!subscription) {
|
||||
throw error
|
||||
}
|
||||
await subscription.unsubscribe()
|
||||
subscription = await registration.pushManager.subscribe(await subscribeOptionsPromise)
|
||||
}
|
||||
|
||||
return saveWebPushSubscription(subscription)
|
||||
}
|
||||
|
||||
const getPushSubscription = async () => {
|
||||
try {
|
||||
if ('serviceWorker' in navigator && 'PushManager' in window) {
|
||||
const registration = await navigator.serviceWorker.ready
|
||||
|
||||
const permission = await registration.pushManager.permissionState(
|
||||
await subscribeOptionsPromise,
|
||||
)
|
||||
console.debug('permission', permission)
|
||||
if (permission === 'prompt') {
|
||||
return
|
||||
}
|
||||
if (permission !== 'granted') {
|
||||
throw new Error(`Разрешение на уведомления не предоставлено: ${permission}`)
|
||||
}
|
||||
|
||||
const subscription = await registration.pushManager.getSubscription()
|
||||
if (subscription) {
|
||||
console.debug('Уже есть активная подписка:', subscription)
|
||||
return subscription
|
||||
} else {
|
||||
console.debug('Нет активной подписки на Push-уведомления.')
|
||||
}
|
||||
} else {
|
||||
console.warn('Push уведомления или Service Worker не поддерживаются в этом браузере.')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Произошла ошибка при получении подписки:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
const stage = ref<'receivePushSubscription' | 'createPushSubscription' | 'removePushSubscription'>(
|
||||
'receivePushSubscription',
|
||||
)
|
||||
|
||||
// Хуки для состояния запроса
|
||||
const { isPending, isError, error, mutate } = useMutation({
|
||||
mutationKey: [stage],
|
||||
mutationFn: async () => {
|
||||
console.debug(stage.value)
|
||||
try {
|
||||
if (stage.value === 'receivePushSubscription') {
|
||||
const chatSettingsDocument = await getUserChatSettingsDocument({
|
||||
qr_code_uri,
|
||||
chat,
|
||||
user_chat_role,
|
||||
}).catch((error) => {
|
||||
if (!isNotFoundError(error)) throw error
|
||||
})
|
||||
if (chatSettingsDocument && chatSettingsDocument.is_push_notification_enabled) {
|
||||
const [subscription, subscriptionDocument] = await Promise.all([
|
||||
getPushSubscription(),
|
||||
getWebPushSubscriptionDocument().catch((error) => {
|
||||
if (!isNotFoundError(error)) throw error
|
||||
}),
|
||||
])
|
||||
if (subscription) {
|
||||
if (!subscriptionDocument) {
|
||||
await saveWebPushSubscription(subscription)
|
||||
}
|
||||
enabled.value = true
|
||||
stage.value = 'removePushSubscription'
|
||||
} else {
|
||||
stage.value = 'createPushSubscription'
|
||||
enabled.value = false
|
||||
}
|
||||
} else {
|
||||
stage.value = 'createPushSubscription'
|
||||
}
|
||||
} else if (stage.value === 'createPushSubscription') {
|
||||
await requestNotificationPermission()
|
||||
await saveUserChatSettings({
|
||||
qr_code_uri,
|
||||
chat,
|
||||
user_chat_role,
|
||||
is_push_notification_enabled: true,
|
||||
})
|
||||
} else if (stage.value === 'removePushSubscription') {
|
||||
await saveUserChatSettings({
|
||||
qr_code_uri,
|
||||
chat,
|
||||
user_chat_role,
|
||||
is_push_notification_enabled: false,
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
enabled.value = false
|
||||
throw error
|
||||
}
|
||||
return null
|
||||
},
|
||||
retry: false,
|
||||
})
|
||||
|
||||
watch(
|
||||
[() => stage.value, () => enabled.value],
|
||||
() => {
|
||||
if (stage.value === 'removePushSubscription') {
|
||||
if (!enabled.value) mutate()
|
||||
} else if (stage.value === 'createPushSubscription') {
|
||||
if (enabled.value) mutate()
|
||||
} else {
|
||||
mutate()
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
const showRequireStandaloneInstructionsForIOS = computed(
|
||||
() => isError.value && isIOS.value && !isStandalone.value && enabled.value,
|
||||
)
|
||||
</script>
|
||||
Reference in New Issue
Block a user