diff --git a/public/scripts/extensions.js b/public/scripts/extensions.js index 330256ca0..ab7bb6582 100644 --- a/public/scripts/extensions.js +++ b/public/scripts/extensions.js @@ -21,6 +21,7 @@ const defaultUrl = 'http://localhost:5100'; let saveMetadataTimeout = null; let requiresReload = false; +let stateChanged = false; export function saveMetadataDebounced() { const context = getContext(); @@ -238,6 +239,7 @@ function onEnableExtensionClick() { async function enableExtension(name, reload = true) { extension_settings.disabledExtensions = extension_settings.disabledExtensions.filter(x => x !== name); + stateChanged = true; await saveSettings(); if (reload) { location.reload(); @@ -248,6 +250,7 @@ async function enableExtension(name, reload = true) { async function disableExtension(name, reload = true) { extension_settings.disabledExtensions.push(name); + stateChanged = true; await saveSettings(); if (reload) { location.reload(); @@ -657,7 +660,20 @@ async function showExtensionsDetails() { await oldPopup.complete(POPUP_RESULT.CANCELLED); } - const popup = new Popup(html, POPUP_TYPE.TEXT, '', { okButton: 'Close', wide: true, large: true, customButtons: [updateAllButton], allowVerticalScrolling: true }); + const popup = new Popup(html, POPUP_TYPE.TEXT, '', { + okButton: 'Close', + wide: true, + large: true, + customButtons: [updateAllButton], + allowVerticalScrolling: true, + onClosing: async () => { + if (stateChanged) { + toastr.info('The page will be reloaded shortly...', 'Extensions state changed'); + await saveSettings(); + } + return true; + }, + }); popupPromise = popup.show(); } catch (error) { toastr.error('Error loading extensions. See browser console for details.'); diff --git a/public/scripts/popup.js b/public/scripts/popup.js index 12790ebff..46b20dd8e 100644 --- a/public/scripts/popup.js +++ b/public/scripts/popup.js @@ -40,7 +40,7 @@ export const POPUP_RESULT = { * @property {POPUP_RESULT|number?} [defaultResult=POPUP_RESULT.AFFIRMATIVE] - The default result of this popup when Enter is pressed. Can be changed from `POPUP_RESULT.AFFIRMATIVE`. * @property {CustomPopupButton[]|string[]?} [customButtons=null] - Custom buttons to add to the popup. If only strings are provided, the buttons will be added with default options, and their result will be in order from `2` onward. * @property {CustomPopupInput[]?} [customInputs=null] - Custom inputs to add to the popup. The display below the content and the input box, one by one. - * @property {(popup: Popup) => boolean?} [onClosing=null] - Handler called before the popup closes, return `false` to cancel the close + * @property {(popup: Popup) => Promise|boolean?} [onClosing=null] - Handler called before the popup closes, return `false` to cancel the close * @property {(popup: Popup) => void?} [onClose=null] - Handler called after the popup closes, but before the DOM is cleaned up * @property {number?} [cropAspect=null] - Aspect ratio for the crop popup * @property {string?} [cropImage=null] - Image URL to display in the crop popup @@ -138,7 +138,7 @@ export class Popup { /** @readonly @type {CustomPopupButton[]|string[]?} */ customButtons; /** @readonly @type {CustomPopupInput[]} */ customInputs; - /** @type {(popup: Popup) => boolean?} */ onClosing; + /** @type {(popup: Popup) => Promise|boolean?} */ onClosing; /** @type {(popup: Popup) => void?} */ onClose; /** @type {POPUP_RESULT|number} */ result; @@ -509,7 +509,7 @@ export class Popup { this.result = result; if (this.onClosing) { - const shouldClose = this.onClosing(this); + const shouldClose = await this.onClosing(this); if (!shouldClose) { this.#isClosingPrevented = true; // Set values back if we cancel out of closing the popup