From bae4ad7d4a7d12594dc54f6aa38074799c45d6fc Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Thu, 22 Dec 2022 13:48:20 +0000 Subject: [PATCH] chore: update masto (#506) Co-authored-by: userquin --- .../createPushSubscription.ts | 33 ++++++-- .../push-notifications/usePushManager.ts | 78 ++++++++++++------- composables/users.ts | 27 ++++--- config/pwa.ts | 2 +- package.json | 2 +- pnpm-lock.yaml | 15 ++-- 6 files changed, 105 insertions(+), 52 deletions(-) diff --git a/composables/push-notifications/createPushSubscription.ts b/composables/push-notifications/createPushSubscription.ts index 69c1ed2a..e159f894 100644 --- a/composables/push-notifications/createPushSubscription.ts +++ b/composables/push-notifications/createPushSubscription.ts @@ -1,6 +1,7 @@ import type { CreatePushSubscriptionParams, PushSubscription as MastoPushSubscription, + SubscriptionPolicy, } from 'masto' import type { CreatePushNotification, @@ -8,11 +9,13 @@ import type { RequiredUserLogin, } from '~/composables/push-notifications/types' import { useMasto } from '~/composables/masto' -import { currentUser, removePushNotifications } from '~/composables/users' +import { currentUser, removePushNotificationData, removePushNotifications } from '~/composables/users' export const createPushSubscription = async ( user: RequiredUserLogin, notificationData: CreatePushNotification, + policy: SubscriptionPolicy = 'all', + force = false, ): Promise => { 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 // 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 (subscriptionServerKey === currentServerKey && subscription.endpoint === serverEndpoint && user.pushSubscription) { + if (subscriptionServerKey === currentServerKey && subscription.endpoint === serverEndpoint && (!force && user.pushSubscription)) { return Promise.resolve(user.pushSubscription) } else if (user.pushSubscription) { - // if we have a subscription, but it is not valid, we need to remove it - return unsubscribeFromBackend(false) + // if we have a subscription, but it is not valid or forcing renew, we need to remove it + // we need to prevent removing push notification data + return unsubscribeFromBackend(false, false) + .catch(removePushNotificationDataOnError) .then(() => subscribe(registration, vapidKey)) - .then(subscription => sendSubscriptionToBackend(subscription, notificationData)) + .then(subscription => sendSubscriptionToBackend(subscription, notificationData, policy)) } } return subscribe(registration, vapidKey).then( - subscription => sendSubscriptionToBackend(subscription, notificationData), + subscription => sendSubscriptionToBackend(subscription, notificationData, policy), ) }) .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 if (cu) - await removePushNotifications(cu, fromSWPushManager) + await removePushNotificationData(cu, true) + + throw e } async function sendSubscriptionToBackend( subscription: PushSubscription, data: CreatePushNotification, + policy: SubscriptionPolicy, ): Promise { const { endpoint, keys } = subscription.toJSON() const params: CreatePushSubscriptionParams = { + policy, subscription: { endpoint: endpoint!, keys: { diff --git a/composables/push-notifications/usePushManager.ts b/composables/push-notifications/usePushManager.ts index 057a31c3..72e1e480 100644 --- a/composables/push-notifications/usePushManager.ts +++ b/composables/push-notifications/usePushManager.ts @@ -1,3 +1,4 @@ +import type { SubscriptionPolicy } from 'masto' import type { CreatePushNotification, PushNotificationPolicy, @@ -14,6 +15,7 @@ const supportsPushNotifications = typeof window !== 'undefined' && 'getKey' in PushSubscription.prototype export const usePushManager = () => { + const masto = useMasto() const isSubscribed = ref(false) const notificationPermission = ref( Notification.permission === 'denied' @@ -59,7 +61,7 @@ export const usePushManager = () => { } }, { immediate: true, flush: 'post' }) - const subscribe = async (notificationData?: CreatePushNotification): Promise => { + const subscribe = async (notificationData?: CreatePushNotification, policy?: SubscriptionPolicy, force?: boolean): Promise => { if (!isSupported || !currentUser.value) return 'invalid-state' @@ -90,18 +92,22 @@ export const usePushManager = () => { return 'notification-denied' } - currentUser.value.pushSubscription = await createPushSubscription({ - pushSubscription, server, token, vapidKey, - }, notificationData ?? { - alerts: { - follow: true, - favourite: true, - reblog: true, - mention: true, - poll: true, + currentUser.value.pushSubscription = await createPushSubscription( + { + pushSubscription, server, token, vapidKey, }, - policy: 'all', - }) + notificationData ?? { + alerts: { + follow: true, + favourite: true, + reblog: true, + mention: true, + poll: true, + }, + }, + policy ?? 'all', + force, + ) await nextTick() notificationPermission.value = permission hiddenNotification.value[acct] = true @@ -116,9 +122,17 @@ export const usePushManager = () => { await removePushNotifications(currentUser.value) } - const saveSettings = async () => { + const saveSettings = async (policy?: SubscriptionPolicy) => { + if (policy) + pushNotificationData.value.policy = policy + 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() clear() await nextTick() @@ -140,19 +154,31 @@ export const usePushManager = () => { const updateSubscription = async () => { if (currentUser.value) { - currentUser.value.pushSubscription = await useMasto().pushSubscriptions.update({ - data: { - alerts: { - follow: pushNotificationData.value.follow, - favourite: pushNotificationData.value.favourite, - reblog: pushNotificationData.value.reblog, - mention: pushNotificationData.value.mention, - poll: pushNotificationData.value.poll, - }, - policy: pushNotificationData.value.policy, + 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, }, - }) - 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) } } diff --git a/composables/users.ts b/composables/users.ts index 77b3cb56..a7aa0ce5 100644 --- a/composables/users.ts +++ b/composables/users.ts @@ -96,17 +96,7 @@ async function loginTo(user?: Omit & { account?: AccountCr return masto } -export async function removePushNotifications(user: UserLogin, fromSWPushManager = true) { - if (!useRuntimeConfig().public.pwaEnabled || !user.pushSubscription) - return - - // unsubscribe push notifications - try { - await useMasto().pushSubscriptions.remove() - } - catch { - // ignore - } +export async function removePushNotificationData(user: UserLogin, fromSWPushManager = true) { // clear push subscription user.pushSubscription = undefined 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() { // TODO: confirm if (!currentUser.value) @@ -149,6 +152,8 @@ export async function signout() { await removePushNotifications(currentUser.value) + await removePushNotificationData(currentUser.value) + currentUserId.value = '' // Remove the current user from the users users.value.splice(index, 1) diff --git a/config/pwa.ts b/config/pwa.ts index d28c7204..63ffc356 100644 --- a/config/pwa.ts +++ b/config/pwa.ts @@ -6,7 +6,7 @@ const isPreview = process.env.PULL_REQUEST === 'true' const pwa: VitePWANuxtOptions = { mode: isCI ? 'production' : 'development', // 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: '/', srcDir: './service-worker', filename: 'sw.ts', diff --git a/package.json b/package.json index 032abb04..4c1e0607 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "fuse.js": "^6.6.2", "js-yaml": "^4.1.0", "lru-cache": "^7.14.1", - "masto": "^4.7.5", + "masto": "^4.11.1", "pinia": "^2.0.27", "shiki": "^0.11.1", "shiki-es": "^0.1.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bc52e5a5..aa0fc4fe 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -45,7 +45,7 @@ specifiers: jsdom: ^20.0.3 lint-staged: ^13.0.4 lru-cache: ^7.14.1 - masto: ^4.7.5 + masto: ^4.11.1 nuxt: ^3.0.0 pinia: ^2.0.27 postcss-nested: ^6.0.0 @@ -91,7 +91,7 @@ dependencies: fuse.js: 6.6.2 js-yaml: 4.1.0 lru-cache: 7.14.1 - masto: 4.7.5 + masto: 4.11.1 pinia: 2.0.27_typescript@4.9.3 shiki: 0.11.1 shiki-es: 0.1.2 @@ -5474,6 +5474,11 @@ packages: /eventemitter3/4.0.7: resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} + dev: true + + /eventemitter3/5.0.0: + resolution: {integrity: sha512-riuVbElZZNXLeLEoprfNYoDSwTBRR44X3mnhdI1YcnENpWTCsTTVZ2zFuqQcpoyqPQIUXdiPEU0ECAq0KQRaHg==} + dev: false /execa/5.1.1: resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} @@ -6900,12 +6905,12 @@ packages: semver: 6.3.0 dev: true - /masto/4.7.5: - resolution: {integrity: sha512-FRqr5yAAJm6PVCPqxrQt7uIwCft+FmPZgcjvLpzo4kzhgvBPGax70rFiGrULYZ34ehnrbcGOp4J+VcM1FxDU2w==} + /masto/4.11.1: + resolution: {integrity: sha512-siTQNhfLV1JjOERCGgjagMvD6q0K0hLuhOXrbXNcYzHAwpbPeSeAM6CSpIRrZ8zFDepOR62Djs/GtJdTR21Rfw==} dependencies: axios: 1.1.3 change-case: 4.1.2 - eventemitter3: 4.0.7 + eventemitter3: 5.0.0 isomorphic-form-data: 2.0.0 isomorphic-ws: 5.0.0_ws@8.11.0 semver: 7.3.8