Add dirty check to all remaining editor modals

Subclass Modal with snapshotValues() for: value source editor, audio
source editor, add device, profile editor, capture template, stream
editor, and PP template modals. Close/cancel now triggers discard
confirmation when form has unsaved changes. Document the convention
in CLAUDE.md.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-24 18:12:30 +03:00
parent 053a56eed3
commit e4c4301a7b
6 changed files with 201 additions and 32 deletions

View File

@@ -45,11 +45,72 @@ document.addEventListener('languageChanged', () => { if (apiKey) loadPictureSour
// ===== Modal instances =====
const templateModal = new Modal('template-modal');
class CaptureTemplateModal extends Modal {
constructor() { super('template-modal'); }
snapshotValues() {
const vals = {
name: document.getElementById('template-name').value,
description: document.getElementById('template-description').value,
engine: document.getElementById('template-engine').value,
};
document.querySelectorAll('[data-config-key]').forEach(field => {
vals['cfg_' + field.dataset.configKey] = field.value;
});
return vals;
}
onForceClose() {
setCurrentEditingTemplateId(null);
set_templateNameManuallyEdited(false);
}
}
class StreamEditorModal extends Modal {
constructor() { super('stream-modal'); }
snapshotValues() {
return {
name: document.getElementById('stream-name').value,
description: document.getElementById('stream-description').value,
type: document.getElementById('stream-type').value,
displayIndex: document.getElementById('stream-display-index').value,
captureTemplate: document.getElementById('stream-capture-template').value,
targetFps: document.getElementById('stream-target-fps').value,
source: document.getElementById('stream-source').value,
ppTemplate: document.getElementById('stream-pp-template').value,
imageSource: document.getElementById('stream-image-source').value,
};
}
onForceClose() {
document.getElementById('stream-type').disabled = false;
set_streamNameManuallyEdited(false);
}
}
class PPTemplateEditorModal extends Modal {
constructor() { super('pp-template-modal'); }
snapshotValues() {
return {
name: document.getElementById('pp-template-name').value,
description: document.getElementById('pp-template-description').value,
filters: JSON.stringify(_modalFilters.map(fi => ({ filter_id: fi.filter_id, options: fi.options }))),
};
}
onForceClose() {
set_modalFilters([]);
set_ppTemplateNameManuallyEdited(false);
}
}
const templateModal = new CaptureTemplateModal();
const testTemplateModal = new Modal('test-template-modal');
const streamModal = new Modal('stream-modal');
const streamModal = new StreamEditorModal();
const testStreamModal = new Modal('test-stream-modal');
const ppTemplateModal = new Modal('pp-template-modal');
const ppTemplateModal = new PPTemplateEditorModal();
const testPPTemplateModal = new Modal('test-pp-template-modal');
// ===== Capture Templates =====
@@ -94,6 +155,7 @@ export async function showAddTemplateModal(cloneData = null) {
}
templateModal.open();
templateModal.snapshot();
}
export async function editTemplate(templateId) {
@@ -120,16 +182,15 @@ export async function editTemplate(templateId) {
document.getElementById('template-error').style.display = 'none';
templateModal.open();
templateModal.snapshot();
} catch (error) {
console.error('Error loading template:', error);
showToast(t('templates.error.load') + ': ' + error.message, 'error');
}
}
export function closeTemplateModal() {
templateModal.forceClose();
setCurrentEditingTemplateId(null);
set_templateNameManuallyEdited(false);
export async function closeTemplateModal() {
await templateModal.close();
}
function updateCaptureDuration(value) {
@@ -419,7 +480,7 @@ export async function saveTemplate() {
}
showToast(templateId ? t('templates.updated') : t('templates.created'), 'success');
closeTemplateModal();
templateModal.forceClose();
await loadCaptureTemplates();
} catch (error) {
console.error('Error saving template:', error);
@@ -793,6 +854,7 @@ export async function showAddStreamModal(presetType, cloneData = null) {
}
streamModal.open();
streamModal.snapshot();
}
export async function editStream(streamId) {
@@ -837,6 +899,7 @@ export async function editStream(streamId) {
}
streamModal.open();
streamModal.snapshot();
} catch (error) {
console.error('Error loading stream:', error);
showToast(t('streams.error.load') + ': ' + error.message, 'error');
@@ -996,7 +1059,7 @@ export async function saveStream() {
}
showToast(streamId ? t('streams.updated') : t('streams.created'), 'success');
closeStreamModal();
streamModal.forceClose();
await loadPictureSources();
} catch (error) {
console.error('Error saving stream:', error);
@@ -1023,10 +1086,8 @@ export async function deleteStream(streamId) {
}
}
export function closeStreamModal() {
streamModal.forceClose();
document.getElementById('stream-type').disabled = false;
set_streamNameManuallyEdited(false);
export async function closeStreamModal() {
await streamModal.close();
}
async function validateStaticImage() {
@@ -1448,6 +1509,7 @@ export async function showAddPPTemplateModal(cloneData = null) {
}
ppTemplateModal.open();
ppTemplateModal.snapshot();
}
export async function editPPTemplate(templateId) {
@@ -1473,6 +1535,7 @@ export async function editPPTemplate(templateId) {
renderModalFilterList();
ppTemplateModal.open();
ppTemplateModal.snapshot();
} catch (error) {
console.error('Error loading PP template:', error);
showToast(t('postprocessing.error.load') + ': ' + error.message, 'error');
@@ -1503,7 +1566,7 @@ export async function savePPTemplate() {
}
showToast(templateId ? t('postprocessing.updated') : t('postprocessing.created'), 'success');
closePPTemplateModal();
ppTemplateModal.forceClose();
await loadPPTemplates();
} catch (error) {
console.error('Error saving PP template:', error);
@@ -1571,10 +1634,8 @@ export async function deletePPTemplate(templateId) {
}
}
export function closePPTemplateModal() {
ppTemplateModal.forceClose();
set_modalFilters([]);
set_ppTemplateNameManuallyEdited(false);
export async function closePPTemplateModal() {
await ppTemplateModal.close();
}
// Exported helpers used by other modules