Files
SealShare/resources/js/share-created.js
T
Andreas Reinhold / reiniandClaude Opus 5 1352533667 Offer a new share as a QR code and through the share sheet
The share created page gets "Show QR code", a dialog with the link as a
QR code (black on white, full screen on a phone) that downloads as a PNG
drawn in the browser, with a password reminder for protected shares; and
"Share…", which opens the device's share sheet where there is one. Both
carry only the link.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V9NnLxnPp8vaaurb3Z1MFy
2026-09-13 11:46:07 +02:00

77 lines
2.4 KiB
JavaScript

/**
* `shareActions`: the ways the share created page hands a share over besides copying its link —
* the device's share sheet, where the browser has one, and the QR code in its dialog saved as a
* PNG. Both carry only the link.
*
* The PNG is drawn in the browser from the dialog's SVG, which is 1024 pixels square, so the
* server needs no image extension and every browser rasterises it at full size.
*/
document.addEventListener('alpine:init', () => {
window.Alpine.data('shareActions', ({ url, title, filename, messages }) => ({
open: false,
canShare: typeof navigator.share === 'function',
async share() {
try {
await navigator.share({ title, url })
} catch (error) {
if (error?.name !== 'AbortError') {
window.materialToast(messages.shareFailed, { type: 'error' })
}
}
},
async downloadQrCode(svg) {
try {
const png = await rasterise(svg)
const link = document.createElement('a')
const href = URL.createObjectURL(png)
link.href = href
link.download = filename
link.click()
setTimeout(() => URL.revokeObjectURL(href), 0)
} catch {
window.materialToast(messages.downloadFailed, { type: 'error' })
}
},
}))
})
/**
* The SVG element as a PNG blob, at the SVG's own width and height, on white.
*/
async function rasterise(svg) {
const width = Number(svg.getAttribute('width'))
const height = Number(svg.getAttribute('height'))
const source = URL.createObjectURL(new Blob([new XMLSerializer().serializeToString(svg)], { type: 'image/svg+xml' }))
try {
const image = new Image()
image.src = source
await image.decode()
const canvas = document.createElement('canvas')
canvas.width = width
canvas.height = height
const context = canvas.getContext('2d')
context.imageSmoothingEnabled = false
context.fillStyle = '#ffffff'
context.fillRect(0, 0, width, height)
context.drawImage(image, 0, 0, width, height)
const png = await new Promise((resolve) => canvas.toBlob(resolve, 'image/png'))
if (!png) {
throw new Error('The canvas gave no image.')
}
return png
} finally {
URL.revokeObjectURL(source)
}
}