Fix make `/profile` await the for an actual successful API connection (#4589) * fix: add connection status check after profile switch in connection manager Fixes #4262 * feat: add timeout parameter to connection manager await functionality & catch timout error * Add rejectOnTimeout option to waitUntilCondition --------- Co-authored-by: Cohee <18619528+Cohee1207@users.noreply.github.com>

df2dee84820f957c4a70182150cea4bd62d97fd7

Wolfsblvt <wolfsblvt@gmail.com>

Signed
2 files changed, +23 -4Showing whitespace changes
public/scripts/extensions/connection-manager/index.js+16 -2
@@ -1,6 +1,6 @@
11import { DOMPurify, Fuse } from '../../../lib.js';
22
33import { event_types, eventSource, main_api, online_status, saveSettingsDebounced } from '../../../script.js';
44import { extension_settings, renderExtensionTemplateAsync } from '../../extensions.js';
55import { callGenericPopup, Popup, POPUP_RESULT, POPUP_TYPE } from '../../popup.js';
66import { SlashCommand } from '../../slash-commands/SlashCommand.js';
@@ -11,7 +11,7 @@ import { SlashCommandDebugController } from '../../slash-commands/SlashCommandDe
1111import { enumTypes, SlashCommandEnumValue } from '../../slash-commands/SlashCommandEnumValue.js';
1212import { SlashCommandParser } from '../../slash-commands/SlashCommandParser.js';
1313import { SlashCommandScope } from '../../slash-commands/SlashCommandScope.js';
1414import { collapseSpaces, getUniqueName, isFalseBoolean, uuidv4, waitUntilCondition } from '../../utils.js';
1515import { t } from '../../i18n.js';
1616import { getSecretLabelById } from '../../secrets.js';
1717
@@ -684,6 +684,13 @@ async function renderDetailsContent(detailsContent) {
684684 defaultValue: 'true',
685685 enumList: commonEnumProviders.boolean('trueFalse')(),
686686 }),
687+ SlashCommandNamedArgument.fromProps({
688+ name: 'timeout',
689+ description: 'Maximum time to wait for the API connection to be established, in milliseconds. Set to 0 to disable. Only applies when await=true.',
690+ isRequired: false,
691+ typeList: [ARGUMENT_TYPE.NUMBER],
692+ defaultValue: '2000',
693+ }),
687694 ],
688695 callback: async (args, value) => {
689696 if (!value || typeof value !== 'string') {
@@ -715,6 +722,13 @@ async function renderDetailsContent(detailsContent) {
715722
716723 if (shouldAwait) {
717724 await awaitPromise;
725+
726+ // We should also await the connection to be established
727+ const parsedTimeout = parseInt(args?.timeout?.toString());
728+ const timeout = !isNaN(parsedTimeout) ? Math.max(0, parsedTimeout) : 2000;
729+ if (timeout > 0) {
730+ await waitUntilCondition(() => online_status !== 'no_connection', timeout, 100, { rejectOnTimeout: false });
731+ }
718732 }
719733
720734 return profile.name;
public/scripts/utils.js+7 -2
@@ -1637,13 +1637,18 @@ export function createThumbnail(dataUrl, maxWidth = null, maxHeight = null, type
16371637 * @param {{ (): boolean; }} condition The condition to wait for.
16381638 * @param {number} [timeout=1000] The timeout in milliseconds.
16391639 * @param {number} [interval=100] The interval in milliseconds.
1640+ * @param {object} [options] Options object
1641+ * @param {boolean} [options.rejectOnTimeout=true] Whether to reject the promise on timeout or resolve it.
16401642 * @returns {Promise<void>} A promise that resolves when the condition is true.
16411643 */
16421644export async function waitUntilCondition(condition, timeout = 1000, interval = 100, options = {}) {
1645+ const { rejectOnTimeout = true } = options;
1646+
16431647 return new Promise((resolve, reject) => {
16441648 const timeoutId = setTimeout(() => {
16451649 clearInterval(intervalId);
1646- reject(new Error('Timed out waiting for condition to be true'));
1650+ const timeoutFn = rejectOnTimeout ? reject : resolve;
1651+ timeoutFn(new Error('Timed out waiting for condition to be true'));
16471652 }, timeout);
16481653
16491654 const intervalId = setInterval(() => {