diff --git a/public/scripts/extensions.js b/public/scripts/extensions.js index 9fb231264..c795af34c 100644 --- a/public/scripts/extensions.js +++ b/public/scripts/extensions.js @@ -641,14 +641,14 @@ async function showExtensionsDetails() { action: async () => { requiresReload = true; await autoUpdateExtensions(true); - popup.complete(POPUP_RESULT.AFFIRMATIVE); + await popup.complete(POPUP_RESULT.AFFIRMATIVE); }, }; // If we are updating an extension, the "old" popup is still active. We should close that. const oldPopup = Popup.util.popups.find(popup => popup.content.querySelector('.extensions_info')); if (oldPopup) { - oldPopup.complete(POPUP_RESULT.CANCELLED); + await oldPopup.complete(POPUP_RESULT.CANCELLED); } const popup = new Popup(`
${html}
`, POPUP_TYPE.TEXT, '', { okButton: 'Close', wide: true, large: true, customButtons: [updateAllButton], allowVerticalScrolling: true }); diff --git a/public/scripts/loader.js b/public/scripts/loader.js index 466d78162..d2d6d11b2 100644 --- a/public/scripts/loader.js +++ b/public/scripts/loader.js @@ -6,7 +6,7 @@ const ELEMENT_ID = 'loader'; let loaderPopup; export function showLoader() { - // Two loaders don't make sense + // Two loaders don't make sense. Don't await, we can overlay the old loader while it closes if (loaderPopup) loaderPopup.complete(POPUP_RESULT.CANCELLED); loaderPopup = new Popup(` @@ -23,24 +23,28 @@ export function showLoader() { export async function hideLoader() { if (!loaderPopup) { console.warn('There is no loader showing to hide'); - return; + return Promise.resolve(); } - //Sets up a 2-step animation. Spinner blurs/fades out, and then the loader shadow does the same. - $('#load-spinner').on('transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd', function () { - $(`#${ELEMENT_ID}`) - //only fade out the spinner and replace with login screen - .animate({ opacity: 0 }, 300, function () { - $(`#${ELEMENT_ID}`).remove(); - loaderPopup.complete(POPUP_RESULT.AFFIRMATIVE); - loaderPopup = null; - }); - }); - - $('#load-spinner') - .css({ - 'filter': 'blur(15px)', - 'opacity': '0', + return new Promise((resolve) => { + //Sets up a 2-step animation. Spinner blurs/fades out, and then the loader shadow does the same. + $('#load-spinner').on('transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd', function () { + $(`#${ELEMENT_ID}`) + //only fade out the spinner and replace with login screen + .animate({ opacity: 0 }, 300, function () { + $(`#${ELEMENT_ID}`).remove(); + loaderPopup.complete(POPUP_RESULT.AFFIRMATIVE).then(() => { + loaderPopup = null; + resolve(); + }); + }); }); + $('#load-spinner') + .css({ + 'filter': 'blur(15px)', + 'opacity': '0', + }); + }); } + diff --git a/public/scripts/popup.js b/public/scripts/popup.js index a34b9f1f0..41d11ec94 100644 --- a/public/scripts/popup.js +++ b/public/scripts/popup.js @@ -98,7 +98,7 @@ const showPopupHelper = { const result = await popup.show(); if (typeof result === 'string' || typeof result === 'boolean') throw new Error(`Invalid popup result. CONFIRM popups only support numbers, or null. Result: ${result}`); return result; - } + }, }; export class Popup { @@ -210,7 +210,7 @@ export class Popup { this.customInputs = customInputs; this.customInputs?.forEach(input => { if (!input.id || !(typeof input.id === 'string')) { - console.warn('Given custom input does not have a valid id set') + console.warn('Given custom input does not have a valid id set'); return; } @@ -318,20 +318,20 @@ export class Popup { if (String(undefined) === String(resultControl.dataset.result)) return; if (isNaN(result)) throw new Error('Invalid result control. Result must be a number. ' + resultControl.dataset.result); const type = resultControl.dataset.resultEvent || 'click'; - resultControl.addEventListener(type, () => this.complete(result)); + resultControl.addEventListener(type, async () => await this.complete(result)); }); // Bind dialog listeners manually, so we can be sure context is preserved - const cancelListener = (evt) => { - this.complete(POPUP_RESULT.CANCELLED); + const cancelListener = async (evt) => { evt.preventDefault(); evt.stopPropagation(); + await this.complete(POPUP_RESULT.CANCELLED); window.removeEventListener('cancel', cancelListenerBound); }; const cancelListenerBound = cancelListener.bind(this); this.dlg.addEventListener('cancel', cancelListenerBound); - const keyListener = (evt) => { + const keyListener = async (evt) => { switch (evt.key) { case 'Enter': { // CTRL+Enter counts as a closing action, but all other modifiers (ALT, SHIFT) should not trigger this @@ -347,10 +347,10 @@ export class Popup { if (!resultControl) return; - const result = Number(document.activeElement.getAttribute('data-result') ?? this.defaultResult); - this.complete(result); evt.preventDefault(); evt.stopPropagation(); + const result = Number(document.activeElement.getAttribute('data-result') ?? this.defaultResult); + await this.complete(result); window.removeEventListener('keydown', keyListenerBound); break; @@ -430,8 +430,10 @@ export class Popup { * - All other will return the result value as provided as `POPUP_RESULT` or a custom number value * * @param {POPUP_RESULT|number} result - The result of the popup (either an existing `POPUP_RESULT` or a custom result value) + * + * @returns {Promise} A promise that resolves with the value of the popup when it is completed. */ - complete(result) { + async complete(result) { // In all cases besides INPUT the popup value should be the result /** @type {POPUP_RESULT|number|boolean|string?} */ let value = result; @@ -468,6 +470,8 @@ export class Popup { Popup.util.lastResult = { value, result, inputResults: this.inputResults }; this.#hide(); + + return this.#promise; } /** diff --git a/public/scripts/slash-commands.js b/public/scripts/slash-commands.js index 9a30a669d..9d730b1db 100644 --- a/public/scripts/slash-commands.js +++ b/public/scripts/slash-commands.js @@ -1653,8 +1653,8 @@ async function buttonsCallback(args, text) { const buttonElement = document.createElement('div'); buttonElement.classList.add('menu_button', 'result-control', 'wide100p'); buttonElement.dataset.result = String(result); - buttonElement.addEventListener('click', () => { - popup?.complete(result); + buttonElement.addEventListener('click', async () => { + await popup.complete(result); }); buttonElement.innerText = button; buttonContainer.appendChild(buttonElement);