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);