Fix manual photo UI: both gallery picker + camera button visible

- Manual photo area now shows two clear choices:
  📁 Choose from Gallery — plain accept=image/* (EXIF preserved)
  📸 Take Photo — calls openManualCamera() for quick capture
- Each has a label explaining EXIF behavior
- openManualCamera() now appends input to DOM before click()
  (required by mobile Chrome for programmatic file input clicks)
- Dynamically created camera input is cleaned up after use
- camera-area overflow:visible to not clip the taller placeholder
This commit is contained in:
2026-05-21 23:12:43 -04:00
parent 90bd3f0e4b
commit f9f6d9d5ce
+19 -4
View File
@@ -991,10 +991,15 @@
<!-- Photo -->
<div class="form-section">
<div class="form-section-title">📸 Photo (optional)</div>
<div id="manPhotoArea" class="camera-area" style="aspect-ratio:4/3;margin-bottom:8px;">
<div id="manPhotoPlaceholder" class="camera-placeholder" onclick="document.getElementById('manPhotoInput').click()">
<span class="cam-icon">📸</span>
<div class="cam-hint" style="margin-top:4px;">Tap to choose photo</div>
<div id="manPhotoArea" class="camera-area" style="aspect-ratio:4/3;margin-bottom:8px;overflow:visible;">
<div id="manPhotoPlaceholder" class="camera-placeholder">
<div style="display:flex;flex-direction:column;gap:10px;align-items:center;">
<button class="btn btn-outline" onclick="document.getElementById('manPhotoInput').click()" style="width:100%;padding:14px;font-size:15px;">📁 Choose from Gallery</button>
<div style="font-size:11px;color:var(--text2);">(preserves EXIF/GPS)</div>
<div style="width:80%;height:1px;background:var(--border);margin:4px 0;"></div>
<button class="btn btn-outline" onclick="openManualCamera()" style="width:100%;padding:14px;font-size:15px;">📸 Take Photo</button>
<div style="font-size:11px;color:var(--text2);">(quick capture, no EXIF)</div>
</div>
</div>
<input type="file" id="manPhotoInput" accept="image/*" style="display:none;" onchange="handleManualPhotoFile(event)">
</div>
@@ -2614,13 +2619,23 @@
inp.type = 'file';
inp.accept = 'image/*';
inp.capture = 'environment';
inp.style.display = 'none';
inp.onchange = handleManualPhotoFile;
// Must be in DOM for mobile Chrome to allow programmatic click
document.body.appendChild(inp);
inp.click();
// Clean up after file is picked (or cancelled)
inp.addEventListener('cancel', () => inp.remove());
// Fallback: remove after a timeout in case 'cancel' doesn't fire
setTimeout(() => { if (inp.parentNode) inp.remove(); }, 60000);
}
async function handleManualPhotoFile(event) {
const file = event.target.files?.[0];
if (!file) return;
// Clean up any dynamically created camera input
const inp = event.target;
if (inp.id !== 'manPhotoInput' && inp.parentNode) inp.remove();
manualPhotoFile = file;
manualPhotoBlob = file; // Use original file (preserves EXIF)