Add spinner to indicate profile application
| @@ -49,6 +49,48 @@ const FANCY_NAMES = { | ||
| 49 | 49 | 'tokenizer': 'Tokenizer', |
| 50 | 50 | }; |
| 51 | 51 | |
| 52 | +/** | |
| 53 | + * A wrapper for the connection manager spinner. | |
| 54 | + */ | |
| 55 | +class ConnectionManagerSpinner { | |
| 56 | + /** | |
| 57 | + * @type {AbortController[]} | |
| 58 | + */ | |
| 59 | + static abortControllers = []; | |
| 60 | + | |
| 61 | + /** @type {HTMLElement} */ | |
| 62 | + spinnerElement; | |
| 63 | + | |
| 64 | + /** @type {AbortController} */ | |
| 65 | + abortController = new AbortController(); | |
| 66 | + | |
| 67 | + constructor() { | |
| 68 | + // @ts-ignore | |
| 69 | + this.spinnerElement = document.getElementById('connection_profile_spinner'); | |
| 70 | + this.abortController = new AbortController(); | |
| 71 | + } | |
| 72 | + | |
| 73 | + start() { | |
| 74 | + ConnectionManagerSpinner.abortControllers.push(this.abortController); | |
| 75 | + this.spinnerElement.classList.remove('hidden'); | |
| 76 | + } | |
| 77 | + | |
| 78 | + stop() { | |
| 79 | + this.spinnerElement.classList.add('hidden'); | |
| 80 | + } | |
| 81 | + | |
| 82 | + isAborted() { | |
| 83 | + return this.abortController.signal.aborted; | |
| 84 | + } | |
| 85 | + | |
| 86 | + static abort() { | |
| 87 | + for (const controller of ConnectionManagerSpinner.abortControllers) { | |
| 88 | + controller.abort(); | |
| 89 | + } | |
| 90 | + ConnectionManagerSpinner.abortControllers = []; | |
| 91 | + } | |
| 92 | +} | |
| 93 | + | |
| 52 | 94 | /** @type {() => SlashCommandEnumValue[]} */ |
| 53 | 95 | const profilesProvider = () => [ |
| 54 | 96 | new SlashCommandEnumValue(NONE), |
| @@ -214,10 +256,19 @@ async function applyConnectionProfile(profile) { | ||
| 214 | 256 | return; |
| 215 | 257 | } |
| 216 | 258 | |
| 259 | + // Abort any ongoing profile application | |
| 260 | + ConnectionManagerSpinner.abort(); | |
| 261 | + | |
| 217 | 262 | const mode = profile.mode; |
| 218 | 263 | const commands = mode === 'cc' ? CC_COMMANDS : TC_COMMANDS; |
| 264 | + const spinner = new ConnectionManagerSpinner(); | |
| 265 | + spinner.start(); | |
| 219 | 266 | |
| 220 | 267 | for (const command of commands) { |
| 268 | + if (spinner.isAborted()) { | |
| 269 | + throw new Error('Profile application aborted'); | |
| 270 | + } | |
| 271 | + | |
| 221 | 272 | const argument = profile[command]; |
| 222 | 273 | if (!argument) { |
| 223 | 274 | continue; |
| @@ -226,9 +277,11 @@ async function applyConnectionProfile(profile) { | ||
| 226 | 277 | try { |
| 227 | 278 | await executeSlashCommandsWithOptions(commandText, { handleParserErrors: false, handleExecutionErrors: false }); |
| 228 | 279 | } catch (error) { |
| 229 | 280 | console.warnerror(`Failed to execute command: ${commandText}`, error); |
| 230 | 281 | } |
| 231 | 282 | } |
| 283 | + | |
| 284 | + spinner.stop(); | |
| 232 | 285 | } |
| 233 | 286 | |
| 234 | 287 | /** |
| @@ -13,8 +13,9 @@ | ||
| 13 | 13 | <i id="delete_connection_profile" class="menu_button fa-solid fa-trash-can" title="Delete a connection profile" data-i18n="[title]Delete a connection profile"></i> |
| 14 | 14 | </div> |
| 15 | 15 | <details id="connection_profile_details" class="marginBot10"> |
| 16 | - <summary data-i18n="Profile Details"> | |
| 16 | + <summary> | |
| 17 | - Profile Details | |
| 17 | + <span data-i18n="Profile Details">Profile Details</span> | |
| 18 | + <i id="connection_profile_spinner" class="fa-solid fa-spinner fa-spin hidden"></i> | |
| 18 | 19 | </summary> |
| 19 | 20 | <div id="connection_profile_details_content" class="marginTop5"></div> |
| 20 | 21 | </details> |
| @@ -7,3 +7,7 @@ | ||
| 7 | 7 | #connection_profile_details ul { |
| 8 | 8 | margin: 5px 0; |
| 9 | 9 | } |
| 10 | + | |
| 11 | +#connection_profile_spinner { | |
| 12 | + margin-lefT: 5px; | |
| 13 | +} | |