elk/components/notification/NotificationPreferences.client.vue

228 lines
8.3 KiB
Vue

<script setup lang="ts">
import { PushSubscriptionError } from '~/composables/push-notifications/types'
defineProps<{ show?: boolean }>()
const {
pushNotificationData,
saveEnabled,
undoChanges,
hiddenNotification,
isSubscribed,
isSupported,
notificationPermission,
updateSubscription,
subscribe,
unsubscribe,
} = usePushManager()
const { t } = useI18n()
const pwaEnabled = useAppConfig().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 key = currentUser.value?.account?.acct
if (key)
hiddenNotification.value[key] = true
}
const showWarning = $computed(() => {
if (!pwaEnabled)
return false
return isSupported
&& (!isSubscribed.value || !notificationPermission.value || notificationPermission.value === 'prompt')
&& !(hiddenNotification.value[currentUser.value?.account?.acct ?? ''] === true)
})
const saveSettings = async () => {
if (busy)
return
busy = true
await nextTick()
animateSave = true
try {
const subscription = await updateSubscription()
}
catch (err) {
// todo: handle error
console.error(err)
}
finally {
busy = false
animateSave = false
}
}
const doSubscribe = async () => {
if (busy)
return
busy = true
await nextTick()
animateSubscription = true
try {
const result = await subscribe()
if (result !== 'subscribed') {
subscribeError = t(`settings.notifications.push_notifications.subscription_error.${result === 'notification-denied' ? 'permission_denied' : 'request_error'}`)
showSubscribeError = true
}
}
catch (err) {
if (err instanceof PushSubscriptionError) {
subscribeError = t(`settings.notifications.push_notifications.subscription_error.${err.code}`)
}
else {
console.error(err)
subscribeError = t('settings.notifications.push_notifications.subscription_error.request_error')
}
showSubscribeError = true
}
finally {
busy = false
animateSubscription = false
}
}
const removeSubscription = async () => {
if (busy)
return
busy = true
await nextTick()
animateRemoveSubscription = true
try {
await unsubscribe()
}
catch (err) {
console.error(err)
}
finally {
busy = false
animateRemoveSubscription = false
}
}
onActivated(() => (busy = false))
</script>
<template>
<section v-if="pwaEnabled && (showWarning || show)" aria-labelledby="pn-s">
<Transition name="slide-down">
<div v-if="show" flex="~ col" border="b base">
<h3 id="pn-settings" px6 py4 mt2 font-bold text-xl flex="~ gap-1" items-center>
{{ $t('settings.notifications.push_notifications.label') }}
</h3>
<template v-if="isSupported">
<div v-if="isSubscribed" flex="~ col">
<form flex="~ col" gap-y-2 px6 pb4 @submit.prevent="saveSettings">
<p id="pn-instructions" text-sm pb2 aria-hidden="true">
{{ $t('settings.notifications.push_notifications.instructions') }}
</p>
<fieldset flex="~ col" gap-y-1 py-1>
<legend>{{ $t('settings.notifications.push_notifications.alerts.title') }}</legend>
<CommonCheckbox v-model="pushNotificationData.follow" hover :label="$t('settings.notifications.push_notifications.alerts.follow')" />
<CommonCheckbox v-model="pushNotificationData.favourite" hover :label="$t('settings.notifications.push_notifications.alerts.favourite')" />
<CommonCheckbox v-model="pushNotificationData.reblog" hover :label="$t('settings.notifications.push_notifications.alerts.reblog')" />
<CommonCheckbox v-model="pushNotificationData.mention" hover :label="$t('settings.notifications.push_notifications.alerts.mention')" />
<CommonCheckbox v-model="pushNotificationData.poll" hover :label="$t('settings.notifications.push_notifications.alerts.poll')" />
</fieldset>
<fieldset flex="~ col" gap-y-1 py-1>
<legend>{{ $t('settings.notifications.push_notifications.policy.title') }}</legend>
<CommonRadio v-model="pushNotificationData.policy" hover value="all" :label="$t('settings.notifications.push_notifications.policy.all')" />
<CommonRadio v-model="pushNotificationData.policy" hover value="followed" :label="$t('settings.notifications.push_notifications.policy.followed')" />
<CommonRadio v-model="pushNotificationData.policy" hover value="follower" :label="$t('settings.notifications.push_notifications.policy.follower')" />
<CommonRadio v-model="pushNotificationData.policy" hover value="none" :label="$t('settings.notifications.push_notifications.policy.none')" />
</fieldset>
<div flex="~ col" gap-y-4 gap-x-2 py-1 sm="~ justify-between flex-row">
<button
btn-solid font-bold py2 full-w sm-wa flex="~ gap2 center"
:class="busy || !saveEnabled ? 'border-transparent' : null"
:disabled="busy || !saveEnabled"
>
<span v-if="busy && animateSave" aria-hidden="true" block animate-spin preserve-3d>
<span block i-ri:loader-2-fill aria-hidden="true" />
</span>
<span v-else block aria-hidden="true" i-ri:save-2-fill />
{{ $t('settings.notifications.push_notifications.save_settings') }}
</button>
<button
btn-outline font-bold py2 full-w sm-wa flex="~ gap2 center"
type="button"
:class="busy || !saveEnabled ? 'border-transparent' : null"
:disabled="busy || !saveEnabled"
@click="undoChanges"
>
<span aria-hidden="true" class="block i-material-symbols:undo-rounded" />
{{ $t('settings.notifications.push_notifications.undo_settings') }}
</button>
</div>
</form>
<form flex="~ col" mt-4 @submit.prevent="removeSubscription">
<span border="b base 2px" class="bg-$c-text-secondary" />
<button
btn-outline rounded-full font-bold py-4 flex="~ gap2 center" m5
:class="busy ? 'border-transparent' : null"
:disabled="busy"
>
<span v-if="busy && animateRemoveSubscription" aria-hidden="true" block animate-spin preserve-3d>
<span block i-ri:loader-2-fill aria-hidden="true" />
</span>
<span v-else block aria-hidden="true" i-material-symbols:cancel-rounded />
{{ $t('settings.notifications.push_notifications.unsubscribe') }}
</button>
</form>
</div>
<template v-else>
<NotificationEnablePushNotification
:animate="animateSubscription"
:busy="busy"
@hide="hideNotification"
@subscribe="doSubscribe"
>
<template #error>
<Transition name="slide-down">
<NotificationSubscribePushNotificationError
v-model="showSubscribeError"
:message="subscribeError"
/>
</transition>
</template>
</NotificationEnablePushNotification>
</template>
</template>
<div v-else px6 pb4 role="alert" aria-labelledby="n-unsupported">
<p id="n-unsupported">
{{ $t('settings.notifications.push_notifications.unsupported') }}
</p>
</div>
</div>
</Transition>
<NotificationEnablePushNotification
v-if="showWarning && !show"
closeable-header
px5
py4
:animate="animateSubscription"
:busy="busy"
@hide="hideNotification"
@subscribe="doSubscribe"
>
<template #error>
<Transition name="slide-down">
<NotificationSubscribePushNotificationError
v-model="showSubscribeError"
:message="subscribeError"
/>
</Transition>
</template>
</NotificationEnablePushNotification>
</section>
</template>