chore: update masto (#506)

Co-authored-by: userquin <userquin@gmail.com>
This commit is contained in:
Daniel Roe 2022-12-22 13:48:20 +00:00 committed by GitHub
parent 8d57cfc886
commit bae4ad7d4a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 105 additions and 52 deletions

View file

@ -1,6 +1,7 @@
import type { import type {
CreatePushSubscriptionParams, CreatePushSubscriptionParams,
PushSubscription as MastoPushSubscription, PushSubscription as MastoPushSubscription,
SubscriptionPolicy,
} from 'masto' } from 'masto'
import type { import type {
CreatePushNotification, CreatePushNotification,
@ -8,11 +9,13 @@ import type {
RequiredUserLogin, RequiredUserLogin,
} from '~/composables/push-notifications/types' } from '~/composables/push-notifications/types'
import { useMasto } from '~/composables/masto' import { useMasto } from '~/composables/masto'
import { currentUser, removePushNotifications } from '~/composables/users' import { currentUser, removePushNotificationData, removePushNotifications } from '~/composables/users'
export const createPushSubscription = async ( export const createPushSubscription = async (
user: RequiredUserLogin, user: RequiredUserLogin,
notificationData: CreatePushNotification, notificationData: CreatePushNotification,
policy: SubscriptionPolicy = 'all',
force = false,
): Promise<MastoPushSubscription | undefined> => { ): Promise<MastoPushSubscription | undefined> => {
const { server: serverEndpoint, vapidKey } = user const { server: serverEndpoint, vapidKey } = user
@ -26,19 +29,21 @@ export const createPushSubscription = async (
// If the VAPID public key did not change and the endpoint corresponds // If the VAPID public key did not change and the endpoint corresponds
// to the endpoint saved in the backend, the subscription is valid // to the endpoint saved in the backend, the subscription is valid
// If push subscription is not there, we need to create it: it is fetched on login // If push subscription is not there, we need to create it: it is fetched on login
if (subscriptionServerKey === currentServerKey && subscription.endpoint === serverEndpoint && user.pushSubscription) { if (subscriptionServerKey === currentServerKey && subscription.endpoint === serverEndpoint && (!force && user.pushSubscription)) {
return Promise.resolve(user.pushSubscription) return Promise.resolve(user.pushSubscription)
} }
else if (user.pushSubscription) { else if (user.pushSubscription) {
// if we have a subscription, but it is not valid, we need to remove it // if we have a subscription, but it is not valid or forcing renew, we need to remove it
return unsubscribeFromBackend(false) // we need to prevent removing push notification data
return unsubscribeFromBackend(false, false)
.catch(removePushNotificationDataOnError)
.then(() => subscribe(registration, vapidKey)) .then(() => subscribe(registration, vapidKey))
.then(subscription => sendSubscriptionToBackend(subscription, notificationData)) .then(subscription => sendSubscriptionToBackend(subscription, notificationData, policy))
} }
} }
return subscribe(registration, vapidKey).then( return subscribe(registration, vapidKey).then(
subscription => sendSubscriptionToBackend(subscription, notificationData), subscription => sendSubscriptionToBackend(subscription, notificationData, policy),
) )
}) })
.catch((error) => { .catch((error) => {
@ -92,18 +97,30 @@ async function subscribe(
}) })
} }
async function unsubscribeFromBackend(fromSWPushManager: boolean) { async function unsubscribeFromBackend(fromSWPushManager: boolean, removePushNotification = true) {
const cu = currentUser.value
if (cu) {
await removePushNotifications(cu)
removePushNotification && await removePushNotificationData(cu, fromSWPushManager)
}
}
async function removePushNotificationDataOnError(e: Error) {
const cu = currentUser.value const cu = currentUser.value
if (cu) if (cu)
await removePushNotifications(cu, fromSWPushManager) await removePushNotificationData(cu, true)
throw e
} }
async function sendSubscriptionToBackend( async function sendSubscriptionToBackend(
subscription: PushSubscription, subscription: PushSubscription,
data: CreatePushNotification, data: CreatePushNotification,
policy: SubscriptionPolicy,
): Promise<MastoPushSubscription> { ): Promise<MastoPushSubscription> {
const { endpoint, keys } = subscription.toJSON() const { endpoint, keys } = subscription.toJSON()
const params: CreatePushSubscriptionParams = { const params: CreatePushSubscriptionParams = {
policy,
subscription: { subscription: {
endpoint: endpoint!, endpoint: endpoint!,
keys: { keys: {

View file

@ -1,3 +1,4 @@
import type { SubscriptionPolicy } from 'masto'
import type { import type {
CreatePushNotification, CreatePushNotification,
PushNotificationPolicy, PushNotificationPolicy,
@ -14,6 +15,7 @@ const supportsPushNotifications = typeof window !== 'undefined'
&& 'getKey' in PushSubscription.prototype && 'getKey' in PushSubscription.prototype
export const usePushManager = () => { export const usePushManager = () => {
const masto = useMasto()
const isSubscribed = ref(false) const isSubscribed = ref(false)
const notificationPermission = ref<PermissionState | undefined>( const notificationPermission = ref<PermissionState | undefined>(
Notification.permission === 'denied' Notification.permission === 'denied'
@ -59,7 +61,7 @@ export const usePushManager = () => {
} }
}, { immediate: true, flush: 'post' }) }, { immediate: true, flush: 'post' })
const subscribe = async (notificationData?: CreatePushNotification): Promise<SubscriptionResult> => { const subscribe = async (notificationData?: CreatePushNotification, policy?: SubscriptionPolicy, force?: boolean): Promise<SubscriptionResult> => {
if (!isSupported || !currentUser.value) if (!isSupported || !currentUser.value)
return 'invalid-state' return 'invalid-state'
@ -90,18 +92,22 @@ export const usePushManager = () => {
return 'notification-denied' return 'notification-denied'
} }
currentUser.value.pushSubscription = await createPushSubscription({ currentUser.value.pushSubscription = await createPushSubscription(
pushSubscription, server, token, vapidKey, {
}, notificationData ?? { pushSubscription, server, token, vapidKey,
alerts: {
follow: true,
favourite: true,
reblog: true,
mention: true,
poll: true,
}, },
policy: 'all', notificationData ?? {
}) alerts: {
follow: true,
favourite: true,
reblog: true,
mention: true,
poll: true,
},
},
policy ?? 'all',
force,
)
await nextTick() await nextTick()
notificationPermission.value = permission notificationPermission.value = permission
hiddenNotification.value[acct] = true hiddenNotification.value[acct] = true
@ -116,9 +122,17 @@ export const usePushManager = () => {
await removePushNotifications(currentUser.value) await removePushNotifications(currentUser.value)
} }
const saveSettings = async () => { const saveSettings = async (policy?: SubscriptionPolicy) => {
if (policy)
pushNotificationData.value.policy = policy
commit() commit()
configuredPolicy.value[currentUser.value!.account.acct ?? ''] = pushNotificationData.value.policy
if (policy)
configuredPolicy.value[currentUser.value!.account.acct ?? ''] = policy
else
configuredPolicy.value[currentUser.value!.account.acct ?? ''] = pushNotificationData.value.policy
await nextTick() await nextTick()
clear() clear()
await nextTick() await nextTick()
@ -140,19 +154,31 @@ export const usePushManager = () => {
const updateSubscription = async () => { const updateSubscription = async () => {
if (currentUser.value) { if (currentUser.value) {
currentUser.value.pushSubscription = await useMasto().pushSubscriptions.update({ const previous = history.value[0].snapshot
data: { const data = {
alerts: { alerts: {
follow: pushNotificationData.value.follow, follow: pushNotificationData.value.follow,
favourite: pushNotificationData.value.favourite, favourite: pushNotificationData.value.favourite,
reblog: pushNotificationData.value.reblog, reblog: pushNotificationData.value.reblog,
mention: pushNotificationData.value.mention, mention: pushNotificationData.value.mention,
poll: pushNotificationData.value.poll, poll: pushNotificationData.value.poll,
},
policy: pushNotificationData.value.policy,
}, },
}) }
await saveSettings()
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 masto.pushSubscriptions.update({ data })
policyChanged && await nextTick()
// force change policy when changed: watch is resetting it on push subscription update
await saveSettings(policyChanged ? policy : undefined)
} }
} }

View file

@ -96,17 +96,7 @@ async function loginTo(user?: Omit<UserLogin, 'account'> & { account?: AccountCr
return masto return masto
} }
export async function removePushNotifications(user: UserLogin, fromSWPushManager = true) { export async function removePushNotificationData(user: UserLogin, fromSWPushManager = true) {
if (!useRuntimeConfig().public.pwaEnabled || !user.pushSubscription)
return
// unsubscribe push notifications
try {
await useMasto().pushSubscriptions.remove()
}
catch {
// ignore
}
// clear push subscription // clear push subscription
user.pushSubscription = undefined user.pushSubscription = undefined
const { acct } = user.account const { acct } = user.account
@ -130,6 +120,19 @@ export async function removePushNotifications(user: UserLogin, fromSWPushManager
} }
} }
export async function removePushNotifications(user: UserLogin) {
if (!useRuntimeConfig().public.pwaEnabled || !user.pushSubscription)
return
// unsubscribe push notifications
try {
await useMasto().pushSubscriptions.remove()
}
catch {
// ignore
}
}
export async function signout() { export async function signout() {
// TODO: confirm // TODO: confirm
if (!currentUser.value) if (!currentUser.value)
@ -149,6 +152,8 @@ export async function signout() {
await removePushNotifications(currentUser.value) await removePushNotifications(currentUser.value)
await removePushNotificationData(currentUser.value)
currentUserId.value = '' currentUserId.value = ''
// Remove the current user from the users // Remove the current user from the users
users.value.splice(index, 1) users.value.splice(index, 1)

View file

@ -6,7 +6,7 @@ const isPreview = process.env.PULL_REQUEST === 'true'
const pwa: VitePWANuxtOptions = { const pwa: VitePWANuxtOptions = {
mode: isCI ? 'production' : 'development', mode: isCI ? 'production' : 'development',
// disable PWA only when in preview mode // disable PWA only when in preview mode
disable: isPreview || (isDevelopment && process.env.VITE_DEV_PWA !== 'true'), disable: /* temporarily test in CI isPreview || */ (isDevelopment && process.env.VITE_DEV_PWA !== 'true'),
scope: '/', scope: '/',
srcDir: './service-worker', srcDir: './service-worker',
filename: 'sw.ts', filename: 'sw.ts',

View file

@ -43,7 +43,7 @@
"fuse.js": "^6.6.2", "fuse.js": "^6.6.2",
"js-yaml": "^4.1.0", "js-yaml": "^4.1.0",
"lru-cache": "^7.14.1", "lru-cache": "^7.14.1",
"masto": "^4.7.5", "masto": "^4.11.1",
"pinia": "^2.0.27", "pinia": "^2.0.27",
"shiki": "^0.11.1", "shiki": "^0.11.1",
"shiki-es": "^0.1.2", "shiki-es": "^0.1.2",

View file

@ -45,7 +45,7 @@ specifiers:
jsdom: ^20.0.3 jsdom: ^20.0.3
lint-staged: ^13.0.4 lint-staged: ^13.0.4
lru-cache: ^7.14.1 lru-cache: ^7.14.1
masto: ^4.7.5 masto: ^4.11.1
nuxt: ^3.0.0 nuxt: ^3.0.0
pinia: ^2.0.27 pinia: ^2.0.27
postcss-nested: ^6.0.0 postcss-nested: ^6.0.0
@ -91,7 +91,7 @@ dependencies:
fuse.js: 6.6.2 fuse.js: 6.6.2
js-yaml: 4.1.0 js-yaml: 4.1.0
lru-cache: 7.14.1 lru-cache: 7.14.1
masto: 4.7.5 masto: 4.11.1
pinia: 2.0.27_typescript@4.9.3 pinia: 2.0.27_typescript@4.9.3
shiki: 0.11.1 shiki: 0.11.1
shiki-es: 0.1.2 shiki-es: 0.1.2
@ -5474,6 +5474,11 @@ packages:
/eventemitter3/4.0.7: /eventemitter3/4.0.7:
resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==}
dev: true
/eventemitter3/5.0.0:
resolution: {integrity: sha512-riuVbElZZNXLeLEoprfNYoDSwTBRR44X3mnhdI1YcnENpWTCsTTVZ2zFuqQcpoyqPQIUXdiPEU0ECAq0KQRaHg==}
dev: false
/execa/5.1.1: /execa/5.1.1:
resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==}
@ -6900,12 +6905,12 @@ packages:
semver: 6.3.0 semver: 6.3.0
dev: true dev: true
/masto/4.7.5: /masto/4.11.1:
resolution: {integrity: sha512-FRqr5yAAJm6PVCPqxrQt7uIwCft+FmPZgcjvLpzo4kzhgvBPGax70rFiGrULYZ34ehnrbcGOp4J+VcM1FxDU2w==} resolution: {integrity: sha512-siTQNhfLV1JjOERCGgjagMvD6q0K0hLuhOXrbXNcYzHAwpbPeSeAM6CSpIRrZ8zFDepOR62Djs/GtJdTR21Rfw==}
dependencies: dependencies:
axios: 1.1.3 axios: 1.1.3
change-case: 4.1.2 change-case: 4.1.2
eventemitter3: 4.0.7 eventemitter3: 5.0.0
isomorphic-form-data: 2.0.0 isomorphic-form-data: 2.0.0
isomorphic-ws: 5.0.0_ws@8.11.0 isomorphic-ws: 5.0.0_ws@8.11.0
semver: 7.3.8 semver: 7.3.8