fix(03-04): replace CameraCapture with file input capture="environment"
- Remove CameraCapture modal which required getUserMedia (HTTPS only) - Use native file input with capture="environment" attribute - Opens camera directly via OS file picker on mobile - Works over HTTP during development - Simpler implementation with better browser compatibility
This commit is contained in:
@@ -5,8 +5,7 @@
|
|||||||
import { slide } from 'svelte/transition';
|
import { slide } from 'svelte/transition';
|
||||||
import ImageGallery from './ImageGallery.svelte';
|
import ImageGallery from './ImageGallery.svelte';
|
||||||
import ImageUpload from './ImageUpload.svelte';
|
import ImageUpload from './ImageUpload.svelte';
|
||||||
import CameraCapture from './CameraCapture.svelte';
|
|
||||||
|
|
||||||
interface EntryWithImages extends Entry {
|
interface EntryWithImages extends Entry {
|
||||||
images: Image[];
|
images: Image[];
|
||||||
}
|
}
|
||||||
@@ -21,8 +20,8 @@
|
|||||||
let expanded = $state(false);
|
let expanded = $state(false);
|
||||||
|
|
||||||
// Image management state
|
// Image management state
|
||||||
let showCamera = $state(false);
|
|
||||||
let editImagesMode = $state(false);
|
let editImagesMode = $state(false);
|
||||||
|
let cameraInput: HTMLInputElement;
|
||||||
|
|
||||||
// Edit state - use $derived to stay in sync with entry prop
|
// Edit state - use $derived to stay in sync with entry prop
|
||||||
let editTitle = $state(entry.title || '');
|
let editTitle = $state(entry.title || '');
|
||||||
@@ -49,6 +48,11 @@
|
|||||||
let isSwiping = $state(false);
|
let isSwiping = $state(false);
|
||||||
|
|
||||||
function handleTouchStart(e: TouchEvent) {
|
function handleTouchStart(e: TouchEvent) {
|
||||||
|
// Ignore if touch started on a button or interactive element
|
||||||
|
const target = e.target as HTMLElement;
|
||||||
|
if (target.closest('button, input, textarea, select, a, [role="button"]')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
touchStartX = e.touches[0].clientX;
|
touchStartX = e.touches[0].clientX;
|
||||||
isSwiping = true;
|
isSwiping = true;
|
||||||
}
|
}
|
||||||
@@ -151,9 +155,22 @@
|
|||||||
// invalidateAll is called by ImageUpload, so nothing extra needed here
|
// invalidateAll is called by ImageUpload, so nothing extra needed here
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleCameraCapture() {
|
async function handleCameraInput(e: Event) {
|
||||||
showCamera = false;
|
const input = e.target as HTMLInputElement;
|
||||||
// invalidateAll is called by CameraCapture
|
const file = input.files?.[0];
|
||||||
|
if (!file || !file.type.startsWith('image/')) return;
|
||||||
|
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('image', file);
|
||||||
|
formData.append('entryId', entry.id);
|
||||||
|
|
||||||
|
await fetch('?/uploadImage', {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData
|
||||||
|
});
|
||||||
|
|
||||||
|
await invalidateAll();
|
||||||
|
input.value = '';
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -357,7 +374,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onclick={() => (showCamera = true)}
|
onclick={() => cameraInput?.click()}
|
||||||
class="px-4 py-2 border border-gray-300 rounded-lg flex items-center gap-2 hover:bg-gray-50 transition-colors"
|
class="px-4 py-2 border border-gray-300 rounded-lg flex items-center gap-2 hover:bg-gray-50 transition-colors"
|
||||||
>
|
>
|
||||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
@@ -376,6 +393,14 @@
|
|||||||
</svg>
|
</svg>
|
||||||
Camera
|
Camera
|
||||||
</button>
|
</button>
|
||||||
|
<input
|
||||||
|
bind:this={cameraInput}
|
||||||
|
type="file"
|
||||||
|
accept="image/*"
|
||||||
|
capture="environment"
|
||||||
|
onchange={handleCameraInput}
|
||||||
|
class="hidden"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -436,10 +461,3 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if showCamera}
|
|
||||||
<CameraCapture
|
|
||||||
entryId={entry.id}
|
|
||||||
onClose={() => (showCamera = false)}
|
|
||||||
onCapture={handleCameraCapture}
|
|
||||||
/>
|
|
||||||
{/if}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user