Fix photo upload on mobile - prevent page reload issue
- Use persistent file input instead of dynamically created one - Store pending upload info in sessionStorage for mobile camera flow - Use simpler accept="image/*" for better mobile compatibility - Handle case where page context is lost after camera returns Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
9fc984bea3
commit
cab561a20e
1 changed files with 78 additions and 46 deletions
|
|
@ -2804,7 +2804,10 @@
|
|||
}
|
||||
},
|
||||
|
||||
// Upload photo
|
||||
// Pending photo upload info (for mobile camera flow)
|
||||
_pendingPhotoUpload: null,
|
||||
|
||||
// Upload photo - mobile-friendly version
|
||||
uploadPhoto(itemId) {
|
||||
// Find the item and check it has a database ID
|
||||
let targetItem = null;
|
||||
|
|
@ -2817,52 +2820,81 @@
|
|||
return;
|
||||
}
|
||||
|
||||
const input = document.createElement('input');
|
||||
input.type = 'file';
|
||||
input.accept = 'image/jpeg,image/png,image/gif,image/webp,image/heic,image/heif,.heic,.heif';
|
||||
input.capture = 'environment'; // Prefer rear camera on mobile
|
||||
input.onchange = async (e) => {
|
||||
const file = e.target.files[0];
|
||||
if (!file) return;
|
||||
|
||||
// Be permissive with file types - server will validate
|
||||
// Mobile cameras may report inconsistent MIME types
|
||||
const validTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp', 'image/heic', 'image/heif', ''];
|
||||
if (file.type && !validTypes.includes(file.type) && !file.type.startsWith('image/')) {
|
||||
this.toast('Please select a valid image file', 'error');
|
||||
return;
|
||||
}
|
||||
if (file.size > 5 * 1024 * 1024) {
|
||||
this.toast('Image must be under 5MB', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
this.toast('Uploading photo...', 'info');
|
||||
|
||||
try {
|
||||
const formData = new FormData();
|
||||
formData.append('photo', file);
|
||||
formData.append('ItemID', targetItem.dbId);
|
||||
|
||||
const response = await fetch(`${this.config.apiBaseUrl}/menu/uploadItemPhoto.cfm`, {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
credentials: 'include'
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
if (data.OK) {
|
||||
targetItem.imageUrl = data.IMAGEURL + '?t=' + Date.now();
|
||||
this.render();
|
||||
this.toast('Photo uploaded!', 'success');
|
||||
} else {
|
||||
this.toast(data.MESSAGE || 'Failed to upload photo', 'error');
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Photo upload error:', err);
|
||||
this.toast('Failed to upload photo', 'error');
|
||||
}
|
||||
// Store pending upload info (survives page context issues on mobile)
|
||||
this._pendingPhotoUpload = {
|
||||
itemId: itemId,
|
||||
dbId: targetItem.dbId
|
||||
};
|
||||
sessionStorage.setItem('pendingPhotoUpload', JSON.stringify(this._pendingPhotoUpload));
|
||||
|
||||
// Get or create persistent file input (mobile browsers handle these better)
|
||||
let input = document.getElementById('photoUploadInput');
|
||||
if (!input) {
|
||||
input = document.createElement('input');
|
||||
input.type = 'file';
|
||||
input.id = 'photoUploadInput';
|
||||
input.accept = 'image/*'; // Simpler accept for better mobile compatibility
|
||||
input.style.display = 'none';
|
||||
document.body.appendChild(input);
|
||||
|
||||
input.addEventListener('change', async (e) => {
|
||||
const file = e.target.files[0];
|
||||
if (!file) return;
|
||||
|
||||
// Get pending upload info
|
||||
const pending = this._pendingPhotoUpload || JSON.parse(sessionStorage.getItem('pendingPhotoUpload') || 'null');
|
||||
if (!pending) {
|
||||
this.toast('Upload context lost - please try again', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
if (file.size > 5 * 1024 * 1024) {
|
||||
this.toast('Image must be under 5MB', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
this.toast('Uploading photo...', 'info');
|
||||
|
||||
try {
|
||||
const formData = new FormData();
|
||||
formData.append('photo', file);
|
||||
formData.append('ItemID', pending.dbId);
|
||||
|
||||
const response = await fetch(`${this.config.apiBaseUrl}/menu/uploadItemPhoto.cfm`, {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
credentials: 'include'
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
if (data.OK) {
|
||||
// Find and update the item
|
||||
for (const cat of this.menu.categories) {
|
||||
const item = cat.items.find(i => i.id === pending.itemId);
|
||||
if (item) {
|
||||
item.imageUrl = data.IMAGEURL + '?t=' + Date.now();
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.render();
|
||||
this.toast('Photo uploaded!', 'success');
|
||||
} else {
|
||||
this.toast(data.MESSAGE || 'Failed to upload photo', 'error');
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Photo upload error:', err);
|
||||
this.toast('Failed to upload photo', 'error');
|
||||
}
|
||||
|
||||
// Clean up
|
||||
sessionStorage.removeItem('pendingPhotoUpload');
|
||||
this._pendingPhotoUpload = null;
|
||||
input.value = ''; // Reset for next upload
|
||||
});
|
||||
}
|
||||
|
||||
// Reset and trigger
|
||||
input.value = '';
|
||||
input.click();
|
||||
},
|
||||
|
||||
|
|
|
|||
Reference in a new issue