Improve EXIF tool error handling and UX
- Add loading spinner feedback for Clear All and Save buttons - Show error alerts when requests fail instead of silent failure - Detect session expiration and redirect to login - Update UI to show empty state after clearing metadata - Fix download by properly appending anchor to DOM 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -309,6 +309,10 @@ document.getElementById('exifClearAll')?.addEventListener('click', async functio
|
|||||||
formData.append('image', exifCurrentFile);
|
formData.append('image', exifCurrentFile);
|
||||||
formData.append('format', 'PNG');
|
formData.append('format', 'PNG');
|
||||||
|
|
||||||
|
const btn = this;
|
||||||
|
btn.disabled = true;
|
||||||
|
btn.innerHTML = '<span class="spinner-border spinner-border-sm me-1"></span>Clearing...';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await fetch('/api/tools/exif/clear', { method: 'POST', body: formData });
|
const res = await fetch('/api/tools/exif/clear', { method: 'POST', body: formData });
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
@@ -317,11 +321,34 @@ document.getElementById('exifClearAll')?.addEventListener('click', async functio
|
|||||||
const a = document.createElement('a');
|
const a = document.createElement('a');
|
||||||
a.href = url;
|
a.href = url;
|
||||||
a.download = res.headers.get('Content-Disposition')?.split('filename=')[1]?.replace(/"/g, '') || 'clean.png';
|
a.download = res.headers.get('Content-Disposition')?.split('filename=')[1]?.replace(/"/g, '') || 'clean.png';
|
||||||
|
document.body.appendChild(a);
|
||||||
a.click();
|
a.click();
|
||||||
|
document.body.removeChild(a);
|
||||||
URL.revokeObjectURL(url);
|
URL.revokeObjectURL(url);
|
||||||
|
|
||||||
|
// Clear the current data to show empty state
|
||||||
|
exifCurrentData = {};
|
||||||
|
exifOriginalData = {};
|
||||||
|
renderExifTable();
|
||||||
|
} else {
|
||||||
|
// Try to parse error
|
||||||
|
const contentType = res.headers.get('content-type');
|
||||||
|
if (contentType && contentType.includes('application/json')) {
|
||||||
|
const err = await res.json();
|
||||||
|
alert(err.error || 'Failed to clear metadata');
|
||||||
|
} else if (res.status === 401 || res.status === 302) {
|
||||||
|
alert('Session expired. Please log in again.');
|
||||||
|
window.location.href = '/login';
|
||||||
|
} else {
|
||||||
|
alert(`Failed to clear metadata (${res.status})`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
|
alert('Failed to clear metadata: ' + err.message);
|
||||||
|
} finally {
|
||||||
|
btn.disabled = false;
|
||||||
|
btn.innerHTML = '<i class="bi bi-trash me-1"></i>Clear All';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -356,6 +383,11 @@ document.getElementById('exifSave')?.addEventListener('click', async function()
|
|||||||
formData.append('image', exifCurrentFile);
|
formData.append('image', exifCurrentFile);
|
||||||
formData.append('updates', JSON.stringify(updates));
|
formData.append('updates', JSON.stringify(updates));
|
||||||
|
|
||||||
|
const btn = this;
|
||||||
|
const originalHtml = btn.innerHTML;
|
||||||
|
btn.disabled = true;
|
||||||
|
btn.innerHTML = '<span class="spinner-border spinner-border-sm me-1"></span>Saving...';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await fetch('/api/tools/exif/update', { method: 'POST', body: formData });
|
const res = await fetch('/api/tools/exif/update', { method: 'POST', body: formData });
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
@@ -364,19 +396,33 @@ document.getElementById('exifSave')?.addEventListener('click', async function()
|
|||||||
const a = document.createElement('a');
|
const a = document.createElement('a');
|
||||||
a.href = url;
|
a.href = url;
|
||||||
a.download = res.headers.get('Content-Disposition')?.split('filename=')[1]?.replace(/"/g, '') || 'updated.jpg';
|
a.download = res.headers.get('Content-Disposition')?.split('filename=')[1]?.replace(/"/g, '') || 'updated.jpg';
|
||||||
|
document.body.appendChild(a);
|
||||||
a.click();
|
a.click();
|
||||||
|
document.body.removeChild(a);
|
||||||
URL.revokeObjectURL(url);
|
URL.revokeObjectURL(url);
|
||||||
|
|
||||||
// Update original to match current
|
// Update original to match current
|
||||||
exifOriginalData = JSON.parse(JSON.stringify(exifCurrentData));
|
exifOriginalData = JSON.parse(JSON.stringify(exifCurrentData));
|
||||||
updateSaveButton();
|
updateSaveButton();
|
||||||
} else {
|
} else {
|
||||||
|
const contentType = res.headers.get('content-type');
|
||||||
|
if (contentType && contentType.includes('application/json')) {
|
||||||
const err = await res.json();
|
const err = await res.json();
|
||||||
alert(err.error || 'Failed to save');
|
alert(err.error || 'Failed to save');
|
||||||
|
} else if (res.status === 401 || res.status === 302) {
|
||||||
|
alert('Session expired. Please log in again.');
|
||||||
|
window.location.href = '/login';
|
||||||
|
} else {
|
||||||
|
alert(`Failed to save (${res.status})`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
alert('Failed to save changes');
|
alert('Failed to save changes: ' + err.message);
|
||||||
|
} finally {
|
||||||
|
btn.disabled = false;
|
||||||
|
btn.innerHTML = originalHtml;
|
||||||
|
updateSaveButton();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user