From 963c4d3b91c63cf09adc4a6f5ba1612ff1433fee Mon Sep 17 00:00:00 2001 From: Chuck <33324927+ChuckBuilds@users.noreply.github.com> Date: Sun, 15 Feb 2026 15:28:51 -0500 Subject: [PATCH] fix(web): use window.installedPlugins for bulk update button (#250) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous fix (#249) wired window.updateAllPlugins to PluginInstallManager.updateAll(), but that method reads from PluginStateManager.installedPlugins which is never populated on page load — only after individual install/update operations. Meanwhile, base.html already defined a working updateAllPlugins using window.installedPlugins (reliably populated by plugins_manager.js). The override from install_manager.js masked this working version. Fix: revert install_manager.js changes and rewrite runUpdateAllPlugins to iterate window.installedPlugins directly, calling the API endpoint without any middleman. Adds per-plugin progress in button text and a summary notification on completion. Co-authored-by: Chuck Co-authored-by: Claude Opus 4.6 --- .../static/v3/js/plugins/install_manager.js | 15 +-- web_interface/static/v3/plugins_manager.js | 100 +++++++++--------- 2 files changed, 55 insertions(+), 60 deletions(-) diff --git a/web_interface/static/v3/js/plugins/install_manager.js b/web_interface/static/v3/js/plugins/install_manager.js index c6cb5805..0b6fbacc 100644 --- a/web_interface/static/v3/js/plugins/install_manager.js +++ b/web_interface/static/v3/js/plugins/install_manager.js @@ -81,10 +81,9 @@ const PluginInstallManager = { /** * Update all plugins. * - * @param {Function} onProgress - Optional callback(index, total, pluginId) for progress updates * @returns {Promise} Update results */ - async updateAll(onProgress) { + async updateAll() { if (!window.PluginStateManager || !window.PluginStateManager.installedPlugins) { throw new Error('Installed plugins not loaded'); } @@ -92,22 +91,15 @@ const PluginInstallManager = { const plugins = window.PluginStateManager.installedPlugins; const results = []; - for (let i = 0; i < plugins.length; i++) { - const plugin = plugins[i]; - if (onProgress) onProgress(i + 1, plugins.length, plugin.id); + for (const plugin of plugins) { try { - const result = await window.PluginAPI.updatePlugin(plugin.id); + const result = await this.update(plugin.id); results.push({ pluginId: plugin.id, success: true, result }); } catch (error) { results.push({ pluginId: plugin.id, success: false, error }); } } - // Reload plugin list once at the end - if (window.PluginStateManager) { - await window.PluginStateManager.loadInstalledPlugins(); - } - return results; } }; @@ -117,6 +109,5 @@ if (typeof module !== 'undefined' && module.exports) { module.exports = PluginInstallManager; } else { window.PluginInstallManager = PluginInstallManager; - window.updateAllPlugins = (onProgress) => PluginInstallManager.updateAll(onProgress); } diff --git a/web_interface/static/v3/plugins_manager.js b/web_interface/static/v3/plugins_manager.js index e592076c..c5233fed 100644 --- a/web_interface/static/v3/plugins_manager.js +++ b/web_interface/static/v3/plugins_manager.js @@ -1682,16 +1682,12 @@ function startOnDemandStatusPolling() { window.loadOnDemandStatus = loadOnDemandStatus; -function runUpdateAllPlugins() { +async function runUpdateAllPlugins() { console.log('[runUpdateAllPlugins] Button clicked, checking for updates...'); const button = document.getElementById('update-all-plugins-btn'); if (!button) { - if (typeof showNotification === 'function') { - showNotification('Unable to locate bulk update controls. Refresh the Plugin Manager tab.', 'error'); - } else { - console.error('update-all-plugins-btn element not found'); - } + showNotification('Unable to locate bulk update controls. Refresh the Plugin Manager tab.', 'error'); return; } @@ -1699,12 +1695,9 @@ function runUpdateAllPlugins() { return; } - if (typeof window.updateAllPlugins !== 'function') { - if (typeof showNotification === 'function') { - showNotification('Bulk update handler unavailable. Refresh the Plugin Manager tab.', 'error'); - } else { - console.error('window.updateAllPlugins is not defined'); - } + const plugins = Array.isArray(window.installedPlugins) ? window.installedPlugins : []; + if (!plugins.length) { + showNotification('No installed plugins to update.', 'warning'); return; } @@ -1712,47 +1705,58 @@ function runUpdateAllPlugins() { button.dataset.running = 'true'; button.disabled = true; button.classList.add('opacity-60', 'cursor-wait'); - button.innerHTML = 'Checking...'; - const onProgress = (current, total, pluginId) => { - button.innerHTML = `Updating ${current}/${total}...`; - }; + let updated = 0, upToDate = 0, failed = 0; - Promise.resolve(window.updateAllPlugins(onProgress)) - .then(results => { - if (!results || !results.length) { - showNotification('No plugins to update.', 'info'); - return; - } - let updated = 0, upToDate = 0, failed = 0; - for (const r of results) { - if (!r.success) { - failed++; - } else if (r.result && r.result.message && r.result.message.includes('already up to date')) { - upToDate++; + try { + for (let i = 0; i < plugins.length; i++) { + const plugin = plugins[i]; + const pluginId = plugin.id; + button.innerHTML = `Updating ${i + 1}/${plugins.length}...`; + + try { + const response = await fetch('/api/v3/plugins/update', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ plugin_id: pluginId }) + }); + const data = await response.json(); + + if (data.status === 'success') { + if (data.message && data.message.includes('already up to date')) { + upToDate++; + } else { + updated++; + } } else { - updated++; + failed++; } + } catch (error) { + failed++; + console.error(`Error updating ${pluginId}:`, error); } - const parts = []; - if (updated > 0) parts.push(`${updated} updated`); - if (upToDate > 0) parts.push(`${upToDate} already up to date`); - if (failed > 0) parts.push(`${failed} failed`); - const type = failed > 0 ? (updated > 0 ? 'warning' : 'error') : 'success'; - showNotification(parts.join(', '), type); - }) - .catch(error => { - console.error('Error updating all plugins:', error); - if (typeof showNotification === 'function') { - showNotification('Error updating all plugins: ' + error.message, 'error'); - } - }) - .finally(() => { - button.innerHTML = originalContent; - button.disabled = false; - button.classList.remove('opacity-60', 'cursor-wait'); - button.dataset.running = 'false'; - }); + } + + // Refresh plugin list once at the end + if (updated > 0) { + loadInstalledPlugins(true); + } + + const parts = []; + if (updated > 0) parts.push(`${updated} updated`); + if (upToDate > 0) parts.push(`${upToDate} already up to date`); + if (failed > 0) parts.push(`${failed} failed`); + const type = failed > 0 ? (updated > 0 ? 'warning' : 'error') : 'success'; + showNotification(parts.join(', '), type); + } catch (error) { + console.error('Bulk plugin update failed:', error); + showNotification('Failed to update all plugins: ' + error.message, 'error'); + } finally { + button.innerHTML = originalContent; + button.disabled = false; + button.classList.remove('opacity-60', 'cursor-wait'); + button.dataset.running = 'false'; + } } // Initialize on-demand modal setup (runs unconditionally since modal is in base.html)