fix: push notification request permission (#677)

This commit is contained in:
Joaquín Sánchez 2023-01-01 20:24:22 +01:00 committed by GitHub
parent 6c38477bc2
commit d8abea75aa
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 123 additions and 35 deletions

View file

@ -7,11 +7,16 @@ defineProps<{
}>() }>()
defineEmits(['hide', 'subscribe']) defineEmits(['hide', 'subscribe'])
defineSlots<{
error: {}
}>()
const isLegacyAccount = computed(() => !currentUser.value?.vapidKey) const isLegacyAccount = computed(() => !currentUser.value?.vapidKey)
</script> </script>
<template> <template>
<div flex="~ col" role="alert" aria-labelledby="notifications-warning" :class="withHeader ? 'border-b border-base' : null"> <div flex="~ col" gap-y-2 role="alert" aria-labelledby="notifications-warning" :class="withHeader ? 'border-b border-base' : null">
<header v-if="withHeader" flex items-center pb-2> <header v-if="withHeader" flex items-center pb-2>
<h2 id="notifications-warning" text-md font-bold w-full> <h2 id="notifications-warning" text-md font-bold w-full>
{{ $t('notification.settings.warning.enable_title') }} {{ $t('notification.settings.warning.enable_title') }}
@ -43,5 +48,6 @@ const isLegacyAccount = computed(() => !currentUser.value?.vapidKey)
<span aria-hidden="true" :class="busy && animate ? 'i-ri:loader-2-fill animate-spin' : 'i-ri:check-line'" /> <span aria-hidden="true" :class="busy && animate ? 'i-ri:loader-2-fill animate-spin' : 'i-ri:check-line'" />
{{ $t('notification.settings.warning.enable_desktop') }} {{ $t('notification.settings.warning.enable_desktop') }}
</button> </button>
<slot v-if="showReAuthMessage" name="error" />
</div> </div>
</template> </template>

View file

@ -1,10 +1,8 @@
<script setup lang="ts"> <script setup lang="ts">
defineProps<{ show: boolean }>() import NotificationSubscribePushNotificationError
from '~/components/notification/NotificationSubscribePushNotificationError.vue'
let busy = $ref<boolean>(false) defineProps<{ show: boolean }>()
let animateSave = $ref<boolean>(false)
let animateSubscription = $ref<boolean>(false)
let animateRemoveSubscription = $ref<boolean>(false)
const { const {
pushNotificationData, pushNotificationData,
@ -18,9 +16,17 @@ const {
subscribe, subscribe,
unsubscribe, unsubscribe,
} = usePushManager() } = usePushManager()
const { t } = useI18n()
const pwaEnabled = useRuntimeConfig().public.pwaEnabled const pwaEnabled = useRuntimeConfig().public.pwaEnabled
let busy = $ref<boolean>(false)
let animateSave = $ref<boolean>(false)
let animateSubscription = $ref<boolean>(false)
let animateRemoveSubscription = $ref<boolean>(false)
let subscribeError = $ref<string>('')
let showSubscribeError = $ref<boolean>(false)
const hideNotification = () => { const hideNotification = () => {
const key = currentUser.value?.account?.acct const key = currentUser.value?.account?.acct
if (key) if (key)
@ -63,10 +69,15 @@ const doSubscribe = async () => {
animateSubscription = true animateSubscription = true
try { try {
const subscription = await subscribe() const result = await subscribe()
// todo: apply some logic based on the result: subscription === 'subscribed' if (result !== 'subscribed') {
// todo: maybe throwing an error instead just a literal to show a dialog with the error subscribeError = t(`notification.settings.subscription_error.${result === 'notification-denied' ? 'permission_denied' : 'request_error'}`)
// todo: handle error showSubscribeError = true
}
}
catch {
subscribeError = t('notification.settings.subscription_error.request_error')
showSubscribeError = true
} }
finally { finally {
busy = false busy = false
@ -162,7 +173,16 @@ onActivated(() => (busy = false))
:show-re-auth-message="!showWarning" :show-re-auth-message="!showWarning"
@hide="hideNotification" @hide="hideNotification"
@subscribe="doSubscribe" @subscribe="doSubscribe"
/> >
<template #error>
<Transition name="slide-down">
<NotificationSubscribePushNotificationError
v-model="showSubscribeError"
:message="subscribeError"
/>
</transition>
</template>
</NotificationEnablePushNotification>
</template> </template>
</template> </template>
<p v-else role="alert" aria-labelledby="notifications-unsupported"> <p v-else role="alert" aria-labelledby="notifications-unsupported">
@ -172,7 +192,7 @@ onActivated(() => (busy = false))
</Transition> </Transition>
<NotificationEnablePushNotification <NotificationEnablePushNotification
v-if="showWarning" v-if="showWarning"
:show-re-auth-message="true" show-re-auth-message
with-header with-header
px5 px5
py4 py4
@ -180,6 +200,15 @@ onActivated(() => (busy = false))
:busy="busy" :busy="busy"
@hide="hideNotification" @hide="hideNotification"
@subscribe="doSubscribe" @subscribe="doSubscribe"
/> >
<template #error>
<Transition name="slide-down">
<NotificationSubscribePushNotificationError
v-model="showSubscribeError"
:message="subscribeError"
/>
</Transition>
</template>
</NotificationEnablePushNotification>
</div> </div>
</template> </template>

View file

@ -0,0 +1,40 @@
<script setup lang="ts">
defineProps<{
title?: string
message: string
}>()
const { modelValue } = defineModel<{
modelValue: boolean
}>()
</script>
<template>
<div
v-if="modelValue"
role="alert"
aria-describedby="notification-failed"
flex="~ col"
gap-1 text-sm
pt-1 ps-2 pe-1 pb-2
text-red-600 dark:text-red-400
border="~ base rounded red-600 dark:red-400"
>
<head id="notification-failed" flex justify-between>
<div flex items-center gap-x-2 font-bold>
<div aria-hidden="true" i-ri:error-warning-fill />
<p>{{ title ?? $t('notification.settings.subscription_error.title') }}</p>
</div>
<CommonTooltip placement="bottom" :content="$t('notification.settings.subscription_error.clear_error')">
<button
flex rounded-4 p1
hover:bg-active cursor-pointer transition-100
:aria-label="$t('notification.settings.subscription_error.clear_error')"
@click="modelValue = false"
>
<span aria-hidden="true" w-1.75em h-1.75em i-ri:close-line />
</button>
</CommonTooltip>
</head>
<p>{{ message }}</p>
</div>
</template>

View file

@ -2,7 +2,7 @@ import type { Emoji, PushSubscription as MastoPushSubscription, PushSubscription
import type { UserLogin } from '~/types' import type { UserLogin } from '~/types'
export type SubscriptionResult = 'subscribed' | 'notification-denied' | 'invalid-state' export type SubscriptionResult = 'subscribed' | 'notification-denied' | 'not-supported' | 'invalid-vapid-key' | 'no-user'
export interface PushManagerSubscriptionInfo { export interface PushManagerSubscriptionInfo {
registration: ServiceWorkerRegistration registration: ServiceWorkerRegistration
subscription: PushSubscription | null subscription: PushSubscription | null

View file

@ -59,33 +59,28 @@ export const usePushManager = () => {
} }
}, { immediate: true, flush: 'post' }) }, { immediate: true, flush: 'post' })
const subscribe = async (notificationData?: CreatePushNotification, policy?: SubscriptionPolicy, force?: boolean): Promise<SubscriptionResult> => { const subscribe = async (
if (!isSupported || !currentUser.value) notificationData?: CreatePushNotification,
return 'invalid-state' policy?: SubscriptionPolicy,
force?: boolean,
): Promise<SubscriptionResult> => {
if (!isSupported)
return 'not-supported'
if (!currentUser.value)
return 'no-user'
const { pushSubscription, server, token, vapidKey, account: { acct } } = currentUser.value const { pushSubscription, server, token, vapidKey, account: { acct } } = currentUser.value
if (!token || !server || !vapidKey) if (!token || !server || !vapidKey)
return 'invalid-state' return 'invalid-vapid-key'
let permission: PermissionState | undefined // always request permission, browsers should remember user decision
const permission = await Promise.resolve(Notification.requestPermission()).then((p) => {
return p === 'default' ? 'prompt' : p
})
if (!notificationPermission.value || (notificationPermission.value === 'prompt' && !hiddenNotification.value[acct])) { if (permission === 'denied') {
// safari 16 does not support navigator.permissions.query for notifications
// try {
// permission = (await navigator.permissions?.query({ name: 'notifications' }))?.state
// }
// catch {
permission = await Promise.resolve(Notification.requestPermission()).then((p: NotificationPermission) => {
return p === 'default' ? 'prompt' : p
})
// }
}
else {
permission = notificationPermission.value
}
if (!permission || permission === 'denied') {
notificationPermission.value = permission notificationPermission.value = permission
return 'notification-denied' return 'notification-denied'
} }

View file

@ -179,6 +179,12 @@
}, },
"save_settings": "Save settings changes", "save_settings": "Save settings changes",
"show_btn": "Show push notifications settings", "show_btn": "Show push notifications settings",
"subscription_error": {
"clear_error": "Clear error",
"permission_denied": "Permission denied: enable notifications in your browser.",
"request_error": "An error occurred while requesting the subscription, try again and if the error persists, please report the issue to the Elk repository.",
"title": "Could not subscribe to push notifications"
},
"title": "Push notifications settings", "title": "Push notifications settings",
"undo_settings": "Undo settings changes", "undo_settings": "Undo settings changes",
"unsubscribe": "Disable push notifications", "unsubscribe": "Disable push notifications",

View file

@ -179,6 +179,12 @@
}, },
"save_settings": "Save settings changes", "save_settings": "Save settings changes",
"show_btn": "Show push notifications settings", "show_btn": "Show push notifications settings",
"subscription_error": {
"clear_error": "Clear error",
"permission_denied": "Permission denied: enable notifications in your browser.",
"request_error": "An error occurred while requesting the subscription, try again and if the error persists, please report the issue to the Elk repository.",
"title": "Could not subscribe to push notifications"
},
"title": "Push notifications settings", "title": "Push notifications settings",
"undo_settings": "Undo settings changes", "undo_settings": "Undo settings changes",
"unsubscribe": "Disable push notifications", "unsubscribe": "Disable push notifications",

View file

@ -174,6 +174,12 @@
}, },
"save_settings": "Guardar cambios", "save_settings": "Guardar cambios",
"show_btn": "Mostrar ajustes de las notificaciones push", "show_btn": "Mostrar ajustes de las notificaciones push",
"subscription_error": {
"clear_error": "Limpiar error",
"permission_denied": "Permiso denegado: habilite las notificaciones en su navegador.",
"request_error": "Se produjo un error al solicitar la suscripción, inténtalo de nuevo y si el error persiste, notifique la incidencia en el repositorio de Elk.",
"title": "No se pudo suscribir a las notificaciones push"
},
"title": "Ajustes de notificaciones push", "title": "Ajustes de notificaciones push",
"undo_settings": "Deshacer cambios", "undo_settings": "Deshacer cambios",
"unsubscribe": "Cancelar notificaciones push", "unsubscribe": "Cancelar notificaciones push",