diff --git a/package.json b/package.json index 826613eb..00eb051b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "impro", - "version": "0.18.51", + "version": "0.18.52", "type": "module", "scripts": { "start": "rm -rf \"${BUILD_DIR:-build}\" && NODE_ENV=development eleventy --serve", diff --git a/src/css/style.css b/src/css/style.css index e9ffb90c..a97e603e 100644 --- a/src/css/style.css +++ b/src/css/style.css @@ -6893,6 +6893,32 @@ context-menu-item-group:not(:last-child) .context-menu-item-group { flex-direction: column-reverse; } +.modal-dialog-button.confirm-button.is-pending { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 8px; + cursor: default; +} + +.modal-dialog-button.confirm-button .loading-spinner { + width: 14px; + height: 14px; + border-width: 2px; + border-left-color: transparent; + border-right-color: transparent; + border-bottom-color: transparent; +} + +.modal-dialog-button.primary-button .loading-spinner, +.modal-dialog-button.danger-button .loading-spinner { + border-top-color: var(--primary-button-text-color); +} + +.modal-dialog-button.danger-subtle-button .loading-spinner { + border-top-color: var(--error-color); +} + @media (min-width: 800px) { .image-alt-text-dialog.bottom-sheet { max-width: 600px; diff --git a/src/js/listInteractionHandler.js b/src/js/listInteractionHandler.js index a4020529..fff33e21 100644 --- a/src/js/listInteractionHandler.js +++ b/src/js/listInteractionHandler.js @@ -88,24 +88,24 @@ export class ListInteractionHandler { } async handleDeleteList(list) { - const confirmed = await confirmModal( + return await confirmModal( "This list will be permanently deleted. This action cannot be undone.", { title: "Delete this list?", confirmButtonText: "Delete", + pendingText: "Deleting…", confirmButtonStyle: "danger", + onConfirm: async () => { + try { + await this.dataLayer.mutations.deleteList(list); + showToast("List deleted"); + } catch (error) { + console.error(error); + showToast("Failed to delete list", { style: "error" }); + throw error; + } + }, }, ); - if (!confirmed) return false; - try { - hapticsImpactMedium(); - await this.dataLayer.mutations.deleteList(list); - showToast("List deleted"); - return true; - } catch (error) { - console.error(error); - showToast("Failed to delete list", { style: "error" }); - return false; - } } } diff --git a/src/js/modals/confirm.modal.js b/src/js/modals/confirm.modal.js index 94fabfb3..b601aff7 100644 --- a/src/js/modals/confirm.modal.js +++ b/src/js/modals/confirm.modal.js @@ -2,6 +2,8 @@ import { html } from "/js/lib/lit-html.js"; import { Modal } from "/js/modals/modal.js"; class ConfirmModal extends Modal { + #isPending = false; + get className() { return "bottom-sheet text-modal confirm-modal compact"; } @@ -10,17 +12,40 @@ class ConfirmModal extends Modal { return { "data-testid": "confirm-modal" }; } + canDismiss() { + return !this.#isPending; + } + render({ dismiss, + update, props: { message, title, confirmButtonStyle = "primary", confirmButtonText = "Confirm", + pendingText = null, + onConfirm = null, }, }) { + const isPending = this.#isPending; + const handleConfirm = async () => { + if (!onConfirm) { + dismiss(true); + return; + } + this.#isPending = true; + update(); + try { + await onConfirm(); + dismiss(true); + } catch { + this.#isPending = false; + update(); + } + }; return html` -