mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-21 21:22:11 -03:00
feat: replace window.confirm with modal for version deletion
Replace the native browser confirmation dialog with a custom modal when deleting model versions. This provides better UX with consistent styling, allows displaying version information (name, preview, metadata), and gives users more context before confirming deletion. Key changes: - Added modalManager import - Created showDeleteVersionModal function to display deletion confirmation modal - Enhanced performDeleteVersion function with better error handling and button state management - Modal shows version preview, name, base model, and metadata - Improved accessibility with proper modal interactions - Maintains existing deletion functionality with enhanced user experience
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { getModelApiClient } from '../../api/modelApiFactory.js';
|
||||
import { downloadManager } from '../../managers/DownloadManager.js';
|
||||
import { modalManager } from '../../managers/ModalManager.js';
|
||||
import { showToast } from '../../utils/uiHelpers.js';
|
||||
import { translate } from '../../utils/i18nHelpers.js';
|
||||
import { state } from '../../state/index.js';
|
||||
@@ -566,14 +567,189 @@ export function initVersionsTab({
|
||||
}
|
||||
}
|
||||
|
||||
async function performDeleteVersion({
|
||||
triggerButton,
|
||||
confirmButton,
|
||||
closeModal,
|
||||
version,
|
||||
}) {
|
||||
if (!version?.filePath) {
|
||||
console.warn('Missing file path for deletion.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (triggerButton) {
|
||||
triggerButton.disabled = true;
|
||||
}
|
||||
|
||||
let confirmOriginalText = '';
|
||||
if (confirmButton) {
|
||||
confirmOriginalText = confirmButton.textContent;
|
||||
confirmButton.disabled = true;
|
||||
}
|
||||
|
||||
let deletionSucceeded = false;
|
||||
|
||||
try {
|
||||
const client = ensureClient();
|
||||
await client.deleteModel(version.filePath);
|
||||
deletionSucceeded = true;
|
||||
showToast(
|
||||
translate('modals.model.versions.toast.versionDeleted', {}, 'Version deleted'),
|
||||
{},
|
||||
'success'
|
||||
);
|
||||
} catch (error) {
|
||||
console.error('Failed to delete version:', error);
|
||||
showToast(error?.message || 'Failed to delete version', {}, 'error');
|
||||
} finally {
|
||||
if (triggerButton && document.body.contains(triggerButton)) {
|
||||
triggerButton.disabled = false;
|
||||
}
|
||||
|
||||
if (
|
||||
confirmButton &&
|
||||
document.body.contains(confirmButton) &&
|
||||
!deletionSucceeded
|
||||
) {
|
||||
confirmButton.disabled = false;
|
||||
if (confirmOriginalText) {
|
||||
confirmButton.textContent = confirmOriginalText;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!deletionSucceeded) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof closeModal === 'function') {
|
||||
closeModal();
|
||||
}
|
||||
|
||||
await refresh();
|
||||
}
|
||||
|
||||
function showDeleteVersionModal(version, triggerButton) {
|
||||
const modalRecord = modalManager?.getModal?.('deleteModal');
|
||||
if (!modalRecord?.element) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const deleteLabel = translate('modals.model.versions.actions.delete', {}, 'Delete');
|
||||
const cancelLabel = translate('common.actions.cancel', {}, 'Cancel');
|
||||
const title = translate('modals.model.versions.actions.delete', {}, 'Delete');
|
||||
const confirmMessage = translate(
|
||||
'modals.model.versions.confirm.delete',
|
||||
{},
|
||||
'Delete this version from your library?'
|
||||
);
|
||||
const versionName =
|
||||
version.name ||
|
||||
translate('modals.model.versions.labels.unnamed', {}, 'Untitled Version');
|
||||
const previewUrl =
|
||||
version.previewUrl || '/loras_static/images/no-preview.png';
|
||||
const metaMarkup = buildMetaMarkup(version);
|
||||
|
||||
const modalElement = modalRecord.element;
|
||||
const originalMarkup = modalElement.innerHTML;
|
||||
|
||||
const content = `
|
||||
<div class="modal-content delete-modal-content version-delete-modal">
|
||||
<h2>${escapeHtml(title)}</h2>
|
||||
<p class="delete-message">${escapeHtml(confirmMessage)}</p>
|
||||
<div class="delete-model-info">
|
||||
<div class="delete-preview">
|
||||
<img src="${escapeHtml(previewUrl)}" alt="${escapeHtml(versionName)}" onerror="this.src='/loras_static/images/no-preview.png'">
|
||||
</div>
|
||||
<div class="delete-info">
|
||||
<h3>${escapeHtml(versionName)}</h3>
|
||||
${
|
||||
version.baseModel
|
||||
? `<p class="version-base-model">${escapeHtml(version.baseModel)}</p>`
|
||||
: ''
|
||||
}
|
||||
${metaMarkup ? `<div class="version-meta">${metaMarkup}</div>` : ''}
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-actions">
|
||||
<button class="cancel-btn">${escapeHtml(cancelLabel)}</button>
|
||||
<button class="delete-btn">${escapeHtml(deleteLabel)}</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
const cleanupHandlers = [];
|
||||
|
||||
modalManager.showModal(
|
||||
'deleteModal',
|
||||
content,
|
||||
null,
|
||||
() => {
|
||||
cleanupHandlers.forEach(handler => {
|
||||
try {
|
||||
handler();
|
||||
} catch (error) {
|
||||
console.error('Failed to cleanup delete modal handler:', error);
|
||||
}
|
||||
});
|
||||
cleanupHandlers.length = 0;
|
||||
modalElement.innerHTML = originalMarkup;
|
||||
delete modalElement.dataset.versionId;
|
||||
}
|
||||
);
|
||||
|
||||
modalElement.dataset.versionId = String(version.versionId ?? '');
|
||||
|
||||
const cancelButton = modalElement.querySelector('.cancel-btn');
|
||||
const confirmButton = modalElement.querySelector('.delete-btn');
|
||||
|
||||
const closeModal = () => modalManager.closeModal('deleteModal');
|
||||
|
||||
if (cancelButton) {
|
||||
const handleCancel = event => {
|
||||
event.preventDefault();
|
||||
closeModal();
|
||||
};
|
||||
cancelButton.addEventListener('click', handleCancel);
|
||||
cleanupHandlers.push(() => {
|
||||
cancelButton.removeEventListener('click', handleCancel);
|
||||
});
|
||||
}
|
||||
|
||||
if (confirmButton) {
|
||||
const handleConfirm = async event => {
|
||||
event.preventDefault();
|
||||
await performDeleteVersion({
|
||||
triggerButton,
|
||||
confirmButton,
|
||||
closeModal,
|
||||
version,
|
||||
});
|
||||
};
|
||||
confirmButton.addEventListener('click', handleConfirm);
|
||||
cleanupHandlers.push(() => {
|
||||
confirmButton.removeEventListener('click', handleConfirm);
|
||||
});
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async function handleDeleteVersion(button, versionId) {
|
||||
if (!controller.record) {
|
||||
return;
|
||||
}
|
||||
const version = controller.record.versions.find(item => item.versionId === versionId);
|
||||
if (!version?.filePath) {
|
||||
if (!version) {
|
||||
console.warn('Target version missing from record for delete:', versionId);
|
||||
return;
|
||||
}
|
||||
|
||||
if (showDeleteVersionModal(version, button)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const confirmText = translate(
|
||||
'modals.model.versions.confirm.delete',
|
||||
{},
|
||||
@@ -582,22 +758,11 @@ export function initVersionsTab({
|
||||
if (!window.confirm(confirmText)) {
|
||||
return;
|
||||
}
|
||||
button.disabled = true;
|
||||
try {
|
||||
const client = ensureClient();
|
||||
await client.deleteModel(version.filePath);
|
||||
showToast(
|
||||
translate('modals.model.versions.toast.versionDeleted', {}, 'Version deleted'),
|
||||
{},
|
||||
'success'
|
||||
);
|
||||
await refresh();
|
||||
} catch (error) {
|
||||
console.error('Failed to delete version:', error);
|
||||
showToast(error?.message || 'Failed to delete version', {}, 'error');
|
||||
} finally {
|
||||
button.disabled = false;
|
||||
}
|
||||
|
||||
await performDeleteVersion({
|
||||
triggerButton: button,
|
||||
version,
|
||||
});
|
||||
}
|
||||
|
||||
async function handleDownloadVersion(button, versionId) {
|
||||
|
||||
Reference in New Issue
Block a user