actually fix volume selection

This commit is contained in:
Raven Scott
2025-11-24 20:17:35 -05:00
parent 30fb4f584b
commit bca2555d5a
2 changed files with 162 additions and 29 deletions
+42
View File
@@ -3684,6 +3684,48 @@ function handlePeerData(data, topicId, peer) {
break;
}
// Intercept volumes responses and route them to active volume handlers
// This ensures volumes responses are never lost, even if handler was replaced
if (response && response.success === true && Array.isArray(response.volumes)) {
console.log('[DEBUG] ===== Volumes response intercepted in app.js =====');
console.log('[DEBUG] Response:', JSON.stringify(response).substring(0, 200));
// Check if there are active volume handlers
if (window.activeVolumeHandlers && window.activeVolumeHandlers.size > 0) {
console.log('[DEBUG] Found', window.activeVolumeHandlers.size, 'active volume handler(s)');
// Route to all active volume handlers
// Also check for handlers that timed out but should still process late responses
let routed = false;
for (const [volumeId, handlerInfo] of window.activeVolumeHandlers.entries()) {
const state = handlerInfo?.state;
// Process if not received yet, even if it timed out (late response)
if (state && !state.volumesReceived) {
console.log('[DEBUG] Routing volumes response to handler for volumeId:', volumeId);
console.log('[DEBUG] Handler timed out?', state.timedOut);
if (handlerInfo.protectedHandler && typeof handlerInfo.protectedHandler === 'function') {
handlerInfo.protectedHandler(response);
routed = true;
} else if (handlerInfo.handler && typeof handlerInfo.handler === 'function') {
handlerInfo.handler(response);
routed = true;
}
} else if (state && state.volumesReceived) {
console.log('[DEBUG] Handler for volumeId', volumeId, 'already received volumes, skipping');
}
}
if (routed) {
console.log('[DEBUG] Volumes response routed to volume handler(s), skipping window.handlePeerResponse');
return; // Don't pass to window.handlePeerResponse if we routed it
} else {
console.log('[DEBUG] No active handlers needed this response, passing to window.handlePeerResponse');
}
} else {
console.log('[DEBUG] No active volume handlers found, passing to window.handlePeerResponse');
}
}
// Handle peer response callback if defined
// This allows custom handlers (like directory browser) to process responses
if (typeof window.handlePeerResponse === 'function') {
+120 -29
View File
@@ -1083,6 +1083,8 @@ const volumeDataCache = new Map();
// Store active volume handlers to prevent them from being replaced
const activeVolumeHandlers = new Map();
// Expose to window for app.js to access
window.activeVolumeHandlers = activeVolumeHandlers;
// Load volumes for named volume select
async function loadVolumesForSelect(volumeId) {
@@ -1138,13 +1140,28 @@ async function loadVolumesForSelect(volumeId) {
return;
}
// Store original handler
const originalHandler = window.handlePeerResponse;
let volumesReceived = false;
// Store original handler - create no-op if undefined
let originalHandler = window.handlePeerResponse;
if (typeof originalHandler !== 'function') {
console.log('[DEBUG] Original handler is undefined/null, creating no-op function');
originalHandler = () => {
// No-op: just log that we received a response that wasn't handled
console.log('[DEBUG] No-op handler received response (no original handler was set)');
};
}
const requestId = `volumes_${Date.now()}_${Math.random()}`;
const startTime = Date.now();
let responseCount = 0;
// Create a shared state object that both handlers can access
const handlerState = {
volumesReceived: false,
volumeId: volumeId,
requestId: requestId,
startTime: startTime
};
console.log('[DEBUG] ===== Setting up volume handler =====');
console.log('[DEBUG] Request ID:', requestId);
console.log('[DEBUG] Volume ID:', volumeId);
@@ -1164,7 +1181,7 @@ async function loadVolumesForSelect(volumeId) {
console.log('[DEBUG] Response volumes:', response?.volumes);
console.log('[DEBUG] Response type:', response?.type);
console.log('[DEBUG] Is volumes array?', Array.isArray(response?.volumes));
console.log('[DEBUG] Volumes received flag:', volumesReceived);
console.log('[DEBUG] Volumes received flag:', handlerState.volumesReceived);
// Log ALL responses to help debug
if (elapsed < 100) {
@@ -1192,7 +1209,7 @@ async function loadVolumesForSelect(volumeId) {
return;
}
if (volumesReceived) {
if (handlerState.volumesReceived) {
// Already processed, pass to original handler
console.log('[DEBUG] Already processed volumes response, passing to original handler');
if (typeof originalHandler === 'function' && originalHandler !== volumeHandler) {
@@ -1202,6 +1219,13 @@ async function loadVolumesForSelect(volumeId) {
}
console.log('[DEBUG] ===== Processing volumes response =====');
console.log('[DEBUG] Handler state at processing time:');
console.log('[DEBUG] - volumesReceived:', handlerState.volumesReceived);
console.log('[DEBUG] - volumeId:', handlerState.volumeId);
console.log('[DEBUG] - requestId:', handlerState.requestId);
console.log('[DEBUG] - elapsed time:', Date.now() - handlerState.startTime, 'ms');
console.log('[DEBUG] Active handlers count:', activeVolumeHandlers.size);
console.log('[DEBUG] Handler still in activeVolumeHandlers:', activeVolumeHandlers.has(volumeId));
// Handle success response - check multiple formats
let volumesArray = null;
@@ -1227,7 +1251,7 @@ async function loadVolumesForSelect(volumeId) {
if (volumesArray !== null) {
console.log('[DEBUG] Successfully extracted volumes array, count:', volumesArray.length);
volumesReceived = true;
handlerState.volumesReceived = true;
// Remove this handler from active handlers
activeVolumeHandlers.delete(volumeId);
@@ -1382,7 +1406,7 @@ async function loadVolumesForSelect(volumeId) {
else if (response && response.error) {
console.error('[ERROR] ===== Volumes request returned error =====');
console.error('[ERROR] Error message:', response.error);
volumesReceived = true;
handlerState.volumesReceived = true;
activeVolumeHandlers.delete(volumeId);
if (window.handlePeerResponse === volumeHandler) {
@@ -1406,7 +1430,7 @@ async function loadVolumesForSelect(volumeId) {
console.warn('[WARN] ===== Unexpected volumes response format =====');
console.warn('[WARN] Response:', JSON.stringify(response));
if (response && response.volumes !== undefined) {
volumesReceived = true;
handlerState.volumesReceived = true;
activeVolumeHandlers.delete(volumeId);
const handlerInfo = activeVolumeHandlers.get(volumeId);
@@ -1446,14 +1470,69 @@ async function loadVolumesForSelect(volumeId) {
// Create a wrapper that checks if this handler is still active
const protectedHandler = (response) => {
// First check if this is a volumes response - use same detection logic as volumeHandler
const isVolumesResponse =
(response && response.success === true && Array.isArray(response.volumes)) ||
(response && response.success === true && response.volumes !== undefined) ||
(response && response.error && (response.error.includes('volume') || response.error.includes('Volume'))) ||
(response && !response.type && Array.isArray(response.volumes)) ||
(response && response.type === 'volumes' && response.data) ||
(response && response.volumes && typeof response.volumes === 'object' && !Array.isArray(response.volumes) && response.volumes.Volumes);
// Get handler info - but also use closure's handlerState as fallback
const handlerInfo = activeVolumeHandlers.get(volumeId);
// Use handlerState from closure if handlerInfo was removed
// Prefer handlerInfo.state if it exists, otherwise use closure's handlerState
const state = handlerInfo?.state || handlerState;
console.log('[DEBUG] Protected handler called');
console.log('[DEBUG] isVolumesResponse:', isVolumesResponse);
console.log('[DEBUG] volumesReceived (from state):', state?.volumesReceived);
console.log('[DEBUG] volumesReceived (from closure):', handlerState.volumesReceived);
console.log('[DEBUG] handlerInfo exists:', !!handlerInfo);
console.log('[DEBUG] volumeId:', volumeId);
console.log('[DEBUG] timedOut:', state?.timedOut);
// If it's a volumes response, ALWAYS try to process it if we haven't actually processed one yet
// This handles cases where handler info was removed but response arrives late
// Also handles cases where timeout occurred but response arrives late
// Also handles cases where volumesReceived flag is incorrectly set
if (isVolumesResponse) {
// Check if we should process it
// Process if: volumesReceived is false, OR handlerInfo was removed (late response)
const shouldProcess = !state?.volumesReceived || !handlerInfo;
if (shouldProcess) {
console.log('[DEBUG] Protected handler: Volumes response detected, processing with volumeHandler');
console.log('[DEBUG] Using handlerState from:', handlerInfo ? 'activeVolumeHandlers' : 'closure');
console.log('[DEBUG] Handler timed out?', state?.timedOut);
console.log('[DEBUG] Handler info exists:', !!handlerInfo);
console.log('[DEBUG] volumesReceived flag:', state?.volumesReceived);
console.log('[DEBUG] Processing because:', !handlerInfo ? 'handlerInfo was removed (late response)' : 'volumesReceived is false');
// Process it even if it timed out or handlerInfo was removed - the response is here now!
volumeHandler(response);
return; // Don't pass to original handler
} else {
console.log('[DEBUG] Protected handler: Volumes response already processed, passing to original handler');
console.log('[DEBUG] volumesReceived:', state?.volumesReceived);
console.log('[DEBUG] handlerInfo exists:', !!handlerInfo);
// Already processed, pass to original handler
if (typeof originalHandler === 'function' && originalHandler !== volumeHandler && originalHandler !== protectedHandler) {
originalHandler(response);
}
return;
}
}
// Not a volumes response - check if handler is still active
if (handlerInfo && handlerInfo.handler === volumeHandler) {
// Handler is still active, process the response
// Handler is still active, process it (for non-volumes responses)
console.log('[DEBUG] Protected handler: Handler still active, processing non-volumes response');
volumeHandler(response);
} else {
// Handler was replaced, pass to original handler
console.log('[DEBUG] Handler was replaced, passing response to original handler');
if (typeof originalHandler === 'function' && originalHandler !== volumeHandler) {
// Handler was replaced or not a volumes response, pass to original handler
console.log('[DEBUG] Protected handler: Handler was replaced or inactive, passing to original handler');
if (typeof originalHandler === 'function' && originalHandler !== volumeHandler && originalHandler !== protectedHandler) {
originalHandler(response);
}
}
@@ -1465,7 +1544,8 @@ async function loadVolumesForSelect(volumeId) {
protectedHandler: protectedHandler,
requestId: requestId,
startTime: startTime,
originalHandler: originalHandler
originalHandler: originalHandler,
state: handlerState
});
// Set the protected handler BEFORE sending command
@@ -1473,6 +1553,16 @@ async function loadVolumesForSelect(volumeId) {
console.log('[DEBUG] ===== Volume handler installed =====');
console.log('[DEBUG] Handler stored in activeVolumeHandlers:', activeVolumeHandlers.has(volumeId));
console.log('[DEBUG] window.handlePeerResponse === volumeHandler:', window.handlePeerResponse === volumeHandler);
console.log('[DEBUG] window.handlePeerResponse === protectedHandler:', window.handlePeerResponse === protectedHandler);
console.log('[DEBUG] window.handlePeerResponse type:', typeof window.handlePeerResponse);
// Verify handler is set synchronously
if (window.handlePeerResponse !== protectedHandler) {
console.error('[ERROR] Handler verification failed! window.handlePeerResponse !== protectedHandler');
console.error('[ERROR] This should never happen - handler was not set correctly');
} else {
console.log('[DEBUG] Handler verification passed - protectedHandler is correctly set');
}
// Send the command - always fetch fresh from server
console.log('[DEBUG] ===== Sending listVolumes command =====');
@@ -1480,6 +1570,15 @@ async function loadVolumesForSelect(volumeId) {
window.sendCommand('listVolumes');
console.log('[DEBUG] listVolumes command sent successfully');
console.log('[DEBUG] Waiting for response...');
// Verify handler is still set after sending command
if (window.handlePeerResponse !== protectedHandler) {
console.warn('[WARN] Handler was replaced immediately after sending command!');
console.warn('[WARN] Current handler:', typeof window.handlePeerResponse);
// Restore it
window.handlePeerResponse = protectedHandler;
console.log('[DEBUG] Handler restored');
}
} catch (error) {
console.error('[ERROR] Failed to send listVolumes command:', error);
activeVolumeHandlers.delete(volumeId);
@@ -1501,28 +1600,20 @@ async function loadVolumesForSelect(volumeId) {
console.warn('[WARN] ===== Volumes list request TIMED OUT =====');
console.warn('[WARN] Elapsed time:', elapsed, 'ms');
console.warn('[WARN] Request ID:', requestId);
console.warn('[WARN] Volumes received:', volumesReceived);
console.warn('[WARN] Volumes received:', handlerState.volumesReceived);
console.warn('[WARN] Handler still active:', window.handlePeerResponse === volumeHandler);
console.warn('[WARN] Current handler type:', typeof window.handlePeerResponse);
console.warn('[WARN] Response count received:', responseCount);
console.warn('[WARN] Active handlers:', Array.from(activeVolumeHandlers.keys()));
if (!volumesReceived) {
// Remove from active handlers
activeVolumeHandlers.delete(volumeId);
if (!handlerState.volumesReceived) {
// Don't remove from active handlers yet - keep it so late-arriving responses can be processed
// Just mark that we've timed out
handlerState.timedOut = true;
console.warn('[WARN] Marked handler as timed out, but keeping in activeVolumeHandlers for late responses');
// Only restore original handler if current handler is still our protected handler
const handlerInfo = activeVolumeHandlers.get(volumeId);
if (handlerInfo && window.handlePeerResponse === handlerInfo.protectedHandler) {
window.handlePeerResponse = originalHandler;
console.warn('[WARN] Restored original handler after timeout');
} else {
console.warn('[WARN] Handler was already replaced before timeout');
console.warn('[WARN] Current handler:', typeof window.handlePeerResponse);
console.warn('[WARN] Expected handler:', typeof handlerInfo?.protectedHandler);
}
volumesReceived = true; // Mark as received to prevent double handling
// Don't restore original handler yet - keep protected handler active to catch late responses
// Only restore if we're sure no response will arrive
volumeLoadingStates.delete(volumeId);
const currentSelect = document.querySelector(`[data-volume-named="${volumeId}"]`);