Release 2.3.0
A share can now hold a private text (a password, a key, a short note) with its files or on its own. The upload page's new "Private text" card takes up to 100 KB; the browser encrypts the text and sends it through the same chunk pipeline as a file, flagged is_text on share_files, so it gets the share's password, expiry, download limit and cleanup. The recipient sees the text only after pressing "Show text", which counts as their download, so a messenger link preview cannot use up a share limited to one download. The text is left out of the file list, the ZIP and the file counts; the admin dashboard marks shares that hold one with "Text". The website gains a Private text feature card and a fifth phone screenshot; every screenshot is retaken. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
202813a1e6
commit
f9a7839ad3
@@ -3,8 +3,9 @@
|
||||
@if ($isStorageFull && $pendingFiles->isEmpty())
|
||||
<x-alert color="warning" :title="__('Storage is full. Uploads are temporarily disabled.')" />
|
||||
@else
|
||||
{{-- Submitting sends the private text first (submit(), resources/js/share-uploader.js), then creates the share. --}}
|
||||
<x-form
|
||||
wire:submit="createShare"
|
||||
x-on:submit.prevent="submit()"
|
||||
x-data="shareUploader({
|
||||
csrfToken: {{ \Illuminate\Support\Js::from(csrf_token()) }},
|
||||
messages: {{ \Illuminate\Support\Js::from([
|
||||
@@ -12,6 +13,7 @@
|
||||
'uploaded' => __('Uploaded'),
|
||||
'failed' => __('Upload failed'),
|
||||
'sessionExpired' => __('Your session expired. Reload the page to upload again.'),
|
||||
'textFailed' => __('The text could not be sent. Try again.'),
|
||||
]) }},
|
||||
})"
|
||||
x-on:beforeunload.window="warnBeforeLeaving($event)"
|
||||
@@ -95,6 +97,30 @@
|
||||
</x-stack>
|
||||
@endif
|
||||
|
||||
{{-- Private text: bound in Alpine only, never to the component, so it never reaches the server
|
||||
unencrypted; it is sent with the share (submit()). The limit is in UTF-8 bytes, as the server counts. --}}
|
||||
<x-card :title="__('Private text')" heading="h2" variant="outlined">
|
||||
<x-stack gap="space100">
|
||||
<x-textarea
|
||||
full
|
||||
x-model="text"
|
||||
:label="__('Text')"
|
||||
:maxlength="$maxTextBytes"
|
||||
autocomplete="off"
|
||||
spellcheck="false"
|
||||
data-test="share-text"
|
||||
/>
|
||||
|
||||
<p class="md-type-body-sm md-tabular" x-bind:class="textBytes > {{ $maxTextBytes }} ? 'md-ink-error' : 'md-ink-variant'" data-test="share-text-size">
|
||||
<span x-text="textBytes"></span> {{ __('bytes') }} / {{ Number::fileSize($maxTextBytes) }}
|
||||
</p>
|
||||
|
||||
@error('text')
|
||||
<x-alert color="error">{{ $message }}</x-alert>
|
||||
@enderror
|
||||
</x-stack>
|
||||
</x-card>
|
||||
|
||||
{{-- Options --}}
|
||||
<x-card :title="__('Share Options')" heading="h2" variant="outlined">
|
||||
<x-stack gap="space200">
|
||||
@@ -148,7 +174,7 @@
|
||||
size="md"
|
||||
icon="link"
|
||||
spinner="createShare"
|
||||
x-bind:disabled="busy || {{ $allFilesUploaded ? 'false' : 'true' }}"
|
||||
x-bind:disabled="busy || {{ $allFilesUploaded ? 'false' : 'true' }} || ({{ $pendingFiles->isEmpty() ? 'true' : 'false' }} && text.trim() === '') || textBytes > {{ $maxTextBytes }}"
|
||||
data-test="create-share"
|
||||
/>
|
||||
</x-slot:actions>
|
||||
|
||||
Reference in New Issue
Block a user