mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-04-10 13:02:59 +00:00
fix(web): resolve plugin settings tabs not loading (#301)
* fix(web): resolve plugin settings tabs not loading due to enhancement race Two co-occurring bugs prevented plugin setting tabs from loading: 1. Both stub-to-full app() enhancement paths (tryEnhance and requestAnimationFrame) could fire independently, with the second overwriting installedPlugins back to [] after init() already fetched them. Added a guard flag (_appEnhanced) and runtime state preservation to prevent this race. 2. Plugin config x-init only loaded content if window.htmx was available at that exact moment, with no retry or fallback. Added retry loop (up to 3s) and fetch() fallback for resilience. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(web): use runtime default tab and add Alpine.initTree to fetch fallback - Replace hard-coded 'overview' comparison with runtime defaultTab (isAPMode ? 'wifi' : 'overview') in both enhancement paths, so activeTab is preserved correctly in AP mode - Add Alpine.initTree(el) call in the plugin config fetch() fallback so Alpine directives in the injected HTML are initialized, matching the pattern used by loadOverviewDirect and loadWifiDirect Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -519,6 +519,9 @@
|
||||
}
|
||||
});
|
||||
|
||||
// Guard flag to prevent duplicate stub-to-full enhancement
|
||||
window._appEnhanced = false;
|
||||
|
||||
// Define app() function early so Alpine can find it when it initializes
|
||||
// This is a complete implementation that will work immediately
|
||||
(function() {
|
||||
@@ -534,15 +537,25 @@
|
||||
init() {
|
||||
// Try to enhance immediately with full implementation
|
||||
const tryEnhance = () => {
|
||||
if (window._appEnhanced) return true;
|
||||
if (typeof window.app === 'function') {
|
||||
const fullApp = window.app();
|
||||
// Check if this is the full implementation (has updatePluginTabs with proper implementation)
|
||||
if (fullApp && typeof fullApp.updatePluginTabs === 'function' && fullApp.updatePluginTabs.toString().includes('_doUpdatePluginTabs')) {
|
||||
// Full implementation is available, copy all methods
|
||||
// But preserve _initialized flag to prevent double init
|
||||
window._appEnhanced = true;
|
||||
// Preserve runtime state that should not be reset
|
||||
const preservedPlugins = this.installedPlugins;
|
||||
const preservedTab = this.activeTab;
|
||||
const defaultTab = isAPMode ? 'wifi' : 'overview';
|
||||
const wasInitialized = this._initialized;
|
||||
Object.assign(this, fullApp);
|
||||
// Restore _initialized flag if it was set
|
||||
// Restore runtime state if non-default
|
||||
if (preservedPlugins && preservedPlugins.length > 0) {
|
||||
this.installedPlugins = preservedPlugins;
|
||||
}
|
||||
if (preservedTab && preservedTab !== defaultTab) {
|
||||
this.activeTab = preservedTab;
|
||||
}
|
||||
if (wasInitialized) {
|
||||
this._initialized = wasInitialized;
|
||||
}
|
||||
@@ -1253,10 +1266,26 @@
|
||||
<div class="bg-white rounded-lg shadow p-6 plugin-config-tab"
|
||||
:id="'plugin-config-' + plugin.id"
|
||||
x-init="$nextTick(() => {
|
||||
if (window.htmx && !$el.dataset.htmxLoaded) {
|
||||
$el.dataset.htmxLoaded = 'true';
|
||||
htmx.ajax('GET', '/v3/partials/plugin-config/' + plugin.id, {target: $el, swap: 'innerHTML'});
|
||||
}
|
||||
const el = $el;
|
||||
const pid = plugin.id;
|
||||
const loadContent = (retries) => {
|
||||
if (window.htmx && !el.dataset.htmxLoaded) {
|
||||
el.dataset.htmxLoaded = 'true';
|
||||
htmx.ajax('GET', '/v3/partials/plugin-config/' + pid, {target: el, swap: 'innerHTML'});
|
||||
} else if (!window.htmx && retries < 15) {
|
||||
setTimeout(() => loadContent(retries + 1), 200);
|
||||
} else if (!window.htmx) {
|
||||
fetch('/v3/partials/plugin-config/' + pid)
|
||||
.then(r => r.text())
|
||||
.then(html => {
|
||||
el.innerHTML = html;
|
||||
if (window.Alpine) {
|
||||
window.Alpine.initTree(el);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
loadContent(0);
|
||||
})">
|
||||
<!-- Loading skeleton shown until HTMX loads server-rendered content -->
|
||||
<div class="animate-pulse space-y-6">
|
||||
@@ -3066,13 +3095,28 @@
|
||||
if (window.Alpine) {
|
||||
// Use requestAnimationFrame for immediate execution without blocking
|
||||
requestAnimationFrame(() => {
|
||||
if (window._appEnhanced) return;
|
||||
window._appEnhanced = true;
|
||||
const isAPMode = window.location.hostname === '192.168.4.1' ||
|
||||
window.location.hostname.startsWith('192.168.4.');
|
||||
const defaultTab = isAPMode ? 'wifi' : 'overview';
|
||||
const appElement = document.querySelector('[x-data]');
|
||||
if (appElement && appElement._x_dataStack && appElement._x_dataStack[0]) {
|
||||
const existingComponent = appElement._x_dataStack[0];
|
||||
// Preserve runtime state that should not be reset
|
||||
const preservedPlugins = existingComponent.installedPlugins;
|
||||
const preservedTab = existingComponent.activeTab;
|
||||
// Replace all properties and methods from full implementation
|
||||
Object.keys(fullImplementation).forEach(key => {
|
||||
existingComponent[key] = fullImplementation[key];
|
||||
});
|
||||
// Restore runtime state if non-default
|
||||
if (preservedPlugins && preservedPlugins.length > 0) {
|
||||
existingComponent.installedPlugins = preservedPlugins;
|
||||
}
|
||||
if (preservedTab && preservedTab !== defaultTab) {
|
||||
existingComponent.activeTab = preservedTab;
|
||||
}
|
||||
// Call init to load plugins and set up watchers (only if not already initialized)
|
||||
if (typeof existingComponent.init === 'function' && !existingComponent._initialized) {
|
||||
existingComponent.init();
|
||||
|
||||
Reference in New Issue
Block a user