feat: add support for the Web Share Target API (#1100)

Co-authored-by: userquin <userquin@gmail.com>
This commit is contained in:
Horváth Bálint 2023-01-14 21:58:52 +01:00 committed by GitHub
parent a6a825e553
commit bede92404b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 221 additions and 7 deletions

View file

@ -0,0 +1,64 @@
/// <reference lib="WebWorker" />
declare const self: ServiceWorkerGlobalScope
const clientResolves: { [key: string]: Function } = {}
self.addEventListener('message', (event) => {
if (event.data.action !== 'ready-to-receive')
return
const id: string | undefined = (event.source as any)?.id ?? undefined
if (id && clientResolves[id] !== undefined)
clientResolves[id]()
})
export const onShareTarget = (event: FetchEvent) => {
if (!event.request.url.endsWith('/web-share-target') || event.request.method !== 'POST')
return
event.waitUntil(handleSharedTarget(event))
}
async function handleSharedTarget(event: FetchEvent) {
event.respondWith(Response.redirect('/home?share-target=true'))
await waitForClientToGetReady(event.resultingClientId)
const [client, formData] = await getClientAndFormData(event)
if (client === undefined)
return
await sendShareTargetMessage(client, formData)
}
async function sendShareTargetMessage(client: Client, data: FormData) {
const sharedData: { text?: string; files?: File[] } = {}
const text = data.get('text')
if (text !== null)
sharedData.text = text.toString()
const files: File[] = []
for (const [name, file] of data.entries()) {
if (name === 'files' && file instanceof File)
files.push(file)
}
if (files.length !== 0)
sharedData.files = files
client.postMessage({ data: sharedData, action: 'compose-with-shared-data' })
}
function waitForClientToGetReady(clientId: string) {
return new Promise<void>((resolve) => {
clientResolves[clientId] = resolve
})
}
function getClientAndFormData(event: FetchEvent): Promise<[client: Client | undefined, formData: FormData]> {
return Promise.all([
self.clients.get(event.resultingClientId),
event.request.formData(),
])
}

View file

@ -7,6 +7,7 @@ import { StaleWhileRevalidate } from 'workbox-strategies'
import { ExpirationPlugin } from 'workbox-expiration'
import { onNotificationClick, onPush } from './web-push-notifications'
import { onShareTarget } from './share-target'
declare const self: ServiceWorkerGlobalScope
@ -32,7 +33,7 @@ if (import.meta.env.DEV)
// deny api and server page calls
let denylist: undefined | RegExp[]
if (import.meta.env.PROD)
denylist = [/^\/api\//, /^\/login\//, /^\/oauth\//, /^\/signin\//]
denylist = [/^\/api\//, /^\/login\//, /^\/oauth\//, /^\/signin\//, /^\/web-share-target\//]
// only cache pages and external assets on local build + start or in production
if (import.meta.env.PROD) {
@ -90,3 +91,4 @@ registerRoute(new NavigationRoute(
self.addEventListener('push', onPush)
self.addEventListener('notificationclick', onNotificationClick)
self.addEventListener('fetch', onShareTarget)