forked from Mirrors/elk
214 lines
6.6 KiB
TypeScript
214 lines
6.6 KiB
TypeScript
import type { mastodon } from 'masto'
|
|
|
|
import type {
|
|
CreatePushNotification,
|
|
PushNotificationPolicy,
|
|
PushNotificationRequest,
|
|
SubscriptionResult,
|
|
} from '~/composables/push-notifications/types'
|
|
import { STORAGE_KEY_NOTIFICATION, STORAGE_KEY_NOTIFICATION_POLICY } from '~/constants'
|
|
|
|
const supportsPushNotifications = typeof window !== 'undefined'
|
|
&& 'serviceWorker' in navigator
|
|
&& 'PushManager' in window
|
|
&& 'getKey' in PushSubscription.prototype
|
|
|
|
export function usePushManager() {
|
|
const { client } = useMasto()
|
|
const isSubscribed = ref(false)
|
|
const notificationPermission = ref<PermissionState | undefined>(
|
|
Notification.permission === 'denied'
|
|
? 'denied'
|
|
: Notification.permission === 'granted'
|
|
? 'granted'
|
|
: Notification.permission === 'default'
|
|
? 'prompt'
|
|
: undefined,
|
|
)
|
|
const isSupported = computed(() => supportsPushNotifications)
|
|
const hiddenNotification = useLocalStorage<PushNotificationRequest>(STORAGE_KEY_NOTIFICATION, {})
|
|
const configuredPolicy = useLocalStorage<PushNotificationPolicy>(STORAGE_KEY_NOTIFICATION_POLICY, {})
|
|
const pushNotificationData = ref(createRawSettings(
|
|
currentUser.value?.pushSubscription,
|
|
configuredPolicy.value[currentUser.value?.account?.acct ?? ''],
|
|
))
|
|
const oldPushNotificationData = ref(createRawSettings(
|
|
currentUser.value?.pushSubscription,
|
|
configuredPolicy.value[currentUser.value?.account?.acct ?? ''],
|
|
))
|
|
const saveEnabled = computed(() => {
|
|
const current = pushNotificationData.value
|
|
const previous = oldPushNotificationData.value
|
|
return current.favourite !== previous.favourite
|
|
|| current.reblog !== previous.reblog
|
|
|| current.mention !== previous.mention
|
|
|| current.follow !== previous.follow
|
|
|| current.poll !== previous.poll
|
|
|| current.policy !== previous.policy
|
|
})
|
|
|
|
watch(() => currentUser.value?.pushSubscription, (subscription) => {
|
|
isSubscribed.value = !!subscription
|
|
pushNotificationData.value = createRawSettings(
|
|
subscription,
|
|
configuredPolicy.value[currentUser.value?.account?.acct ?? ''],
|
|
)
|
|
oldPushNotificationData.value = createRawSettings(
|
|
subscription,
|
|
configuredPolicy.value[currentUser.value?.account?.acct ?? ''],
|
|
)
|
|
}, { immediate: true, flush: 'post' })
|
|
|
|
const subscribe = async (
|
|
notificationData?: CreatePushNotification,
|
|
policy?: mastodon.v1.WebPushSubscriptionPolicy,
|
|
force?: boolean,
|
|
): Promise<SubscriptionResult> => {
|
|
if (!isSupported.value)
|
|
return 'not-supported'
|
|
|
|
if (!currentUser.value)
|
|
return 'no-user'
|
|
|
|
const { pushSubscription, server, token, vapidKey, account: { acct } } = currentUser.value
|
|
|
|
if (!token || !server || !vapidKey)
|
|
return 'invalid-vapid-key'
|
|
|
|
// always request permission, browsers should remember user decision
|
|
const permission = await Promise.resolve(Notification.requestPermission()).then((p) => {
|
|
return p === 'default' ? 'prompt' : p
|
|
})
|
|
|
|
if (permission === 'denied') {
|
|
notificationPermission.value = permission
|
|
return 'notification-denied'
|
|
}
|
|
|
|
currentUser.value.pushSubscription = await createPushSubscription(
|
|
{
|
|
pushSubscription,
|
|
server,
|
|
token,
|
|
vapidKey,
|
|
},
|
|
notificationData ?? {
|
|
alerts: {
|
|
follow: true,
|
|
favourite: true,
|
|
reblog: true,
|
|
mention: true,
|
|
poll: true,
|
|
},
|
|
},
|
|
policy ?? 'all',
|
|
force,
|
|
)
|
|
await nextTick()
|
|
notificationPermission.value = permission
|
|
hiddenNotification.value[acct] = true
|
|
|
|
return 'subscribed'
|
|
}
|
|
|
|
const unsubscribe = async () => {
|
|
if (!isSupported.value || !isSubscribed.value || !currentUser.value)
|
|
return false
|
|
|
|
await removePushNotifications(currentUser.value)
|
|
await removePushNotificationData(currentUser.value)
|
|
}
|
|
|
|
const saveSettings = async (policy?: mastodon.v1.WebPushSubscriptionPolicy) => {
|
|
if (policy)
|
|
pushNotificationData.value.policy = policy
|
|
|
|
const current = pushNotificationData.value
|
|
oldPushNotificationData.value = {
|
|
favourite: current.favourite,
|
|
reblog: current.reblog,
|
|
mention: current.mention,
|
|
follow: current.follow,
|
|
poll: current.poll,
|
|
policy: current.policy,
|
|
}
|
|
|
|
if (policy)
|
|
configuredPolicy.value[currentUser.value!.account.acct ?? ''] = policy
|
|
else
|
|
configuredPolicy.value[currentUser.value!.account.acct ?? ''] = pushNotificationData.value.policy
|
|
|
|
await nextTick()
|
|
}
|
|
|
|
const undoChanges = () => {
|
|
const previous = oldPushNotificationData.value
|
|
pushNotificationData.value = {
|
|
favourite: previous.favourite,
|
|
reblog: previous.reblog,
|
|
mention: previous.mention,
|
|
follow: previous.follow,
|
|
poll: previous.poll,
|
|
policy: previous.policy,
|
|
}
|
|
configuredPolicy.value[currentUser.value!.account.acct ?? ''] = previous.policy
|
|
}
|
|
|
|
const updateSubscription = async () => {
|
|
if (currentUser.value) {
|
|
const previous = oldPushNotificationData.value
|
|
// const previous = history.value[0].snapshot
|
|
const data = {
|
|
alerts: {
|
|
follow: pushNotificationData.value.follow,
|
|
favourite: pushNotificationData.value.favourite,
|
|
reblog: pushNotificationData.value.reblog,
|
|
mention: pushNotificationData.value.mention,
|
|
poll: pushNotificationData.value.poll,
|
|
},
|
|
}
|
|
|
|
const policy = pushNotificationData.value.policy
|
|
|
|
const policyChanged = previous.policy !== policy
|
|
|
|
// to change policy we need to resubscribe
|
|
if (policyChanged)
|
|
await subscribe(data, policy, true)
|
|
else
|
|
currentUser.value.pushSubscription = await client.value.v1.push.subscription.update({ data })
|
|
|
|
policyChanged && await nextTick()
|
|
|
|
// force change policy when changed: watch is resetting it on push subscription update
|
|
await saveSettings(policyChanged ? policy : undefined)
|
|
}
|
|
}
|
|
|
|
return {
|
|
pushNotificationData,
|
|
saveEnabled,
|
|
undoChanges,
|
|
hiddenNotification,
|
|
isSupported,
|
|
isSubscribed,
|
|
notificationPermission,
|
|
updateSubscription,
|
|
subscribe,
|
|
unsubscribe,
|
|
}
|
|
}
|
|
|
|
function createRawSettings(
|
|
pushSubscription?: mastodon.v1.WebPushSubscription,
|
|
subscriptionPolicy?: mastodon.v1.WebPushSubscriptionPolicy,
|
|
) {
|
|
return {
|
|
follow: pushSubscription?.alerts.follow ?? true,
|
|
favourite: pushSubscription?.alerts.favourite ?? true,
|
|
reblog: pushSubscription?.alerts.reblog ?? true,
|
|
mention: pushSubscription?.alerts.mention ?? true,
|
|
poll: pushSubscription?.alerts.poll ?? true,
|
|
policy: subscriptionPolicy ?? 'all',
|
|
}
|
|
}
|