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
This commit is contained in:
Andreas Reinhold / reini
2026-09-13 11:46:07 +02:00
co-authored by Claude Opus 5
parent fbe358b2e5
commit 1352533667
11 changed files with 416 additions and 10 deletions
@@ -11,14 +11,51 @@
</div>
<div class="grid gap-4">
<x-input
:label="__('Share Link')"
:value="route('share.download', $share)"
readonly
copyable
icon="link"
data-test="share-link"
/>
{{-- Besides the link: a QR code in a dialog, saved as a PNG in the browser, and the device's
share sheet where there is one (resources/js/share-created.js). Both carry the link only. --}}
<div
x-data="shareActions({
url: @js($shareUrl),
title: @js($siteTitle),
filename: @js('share-'.$share->token.'.png'),
messages: @js(['shareFailed' => __('The share sheet could not open.'), 'downloadFailed' => __('The QR code could not be saved.')]),
})"
class="grid gap-3"
data-test="share-actions"
>
<x-input
:label="__('Share Link')"
:value="$shareUrl"
readonly
copyable
icon="link"
data-test="share-link"
/>
<div class="flex flex-wrap gap-2">
<x-button :label="__('Show QR code')" icon="qr_code_2" variant="tonal" x-on:click="open = true" data-test="show-qr-code" />
<span x-show="canShare" x-cloak class="inline-flex">
<x-button :label="__('Share…')" icon="share" variant="tonal" x-on:click="share()" data-test="share-sheet" />
</span>
</div>
<x-modal fullscreen :title="__('Scan to open the share')" data-test="qr-code-dialog">
{{-- White in either theme: a scanner needs the contrast. The SVG is drawn from the app's own URL. --}}
<div data-qr-code class="mx-auto aspect-square w-full max-w-80 rounded-corner-lg bg-white p-2 [&>svg]:size-full">{!! $qrCodeSvg !!}</div>
@if ($share->isPasswordProtected())
<div class="mt-4">
<x-alert color="info" icon="lock" :title="__('Recipients also need the password.')" />
</div>
@endif
<x-slot:actions>
<x-button :label="__('Close')" x-on:click="close()" />
<x-button :label="__('Download')" icon="download" variant="tonal" x-on:click="downloadQrCode($el.closest('dialog').querySelector('[data-qr-code] svg'))" data-test="download-qr-code" />
</x-slot:actions>
</x-modal>
</div>
<div class="grid grid-cols-2 gap-3">
<x-stat :title="__('Files')" :value="$share->files->count()" icon="description" />