From c1485ccbdb44290199b0754e0686de017635f289 Mon Sep 17 00:00:00 2001 From: root Date: Sat, 27 Dec 2025 22:01:36 +0000 Subject: [PATCH] Add 10-second error suppression grace period after SPA navigation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- app/RSpade/Core/Js/Ajax.js | 8 ++++++++ app/RSpade/Core/Js/Exception_Handler.js | 8 ++++++++ app/RSpade/Core/SPA/Spa.js | 26 +++++++++++++++++++++++++ 3 files changed, 42 insertions(+) diff --git a/app/RSpade/Core/Js/Ajax.js b/app/RSpade/Core/Js/Ajax.js index 5e3257781..26896a855 100755 --- a/app/RSpade/Core/Js/Ajax.js +++ b/app/RSpade/Core/Js/Ajax.js @@ -50,6 +50,14 @@ class Ajax { const error = event.reason; console.error('Uncaught Ajax error:', error); + // Suppress errors during navigation grace period + // After page navigation, pending requests from the old page may error out + // These errors are not relevant to the new page + if (typeof Spa !== 'undefined' && Spa.is_within_navigation_grace_period()) { + console.log('[Ajax] Suppressing error modal during navigation grace period'); + return; + } + // Show error modal for uncaught Ajax errors // In debug mode, use fatal_error() for detailed file/line info if (typeof Modal !== 'undefined') { diff --git a/app/RSpade/Core/Js/Exception_Handler.js b/app/RSpade/Core/Js/Exception_Handler.js index 4c960a524..078f0f490 100755 --- a/app/RSpade/Core/Js/Exception_Handler.js +++ b/app/RSpade/Core/Js/Exception_Handler.js @@ -160,6 +160,14 @@ class Exception_Handler { return; } + // Suppress errors during navigation grace period + // After page navigation, pending requests from the old page may error out + // These errors are not relevant to the new page + if (typeof Spa !== 'undefined' && Spa.is_within_navigation_grace_period()) { + console.log('[Exception_Handler] Suppressing error flash during navigation grace period'); + return; + } + // Extract message from Error object or string let error_text; if (exception instanceof Error) { diff --git a/app/RSpade/Core/SPA/Spa.js b/app/RSpade/Core/SPA/Spa.js index bf329b859..5e1e64aa5 100755 --- a/app/RSpade/Core/SPA/Spa.js +++ b/app/RSpade/Core/SPA/Spa.js @@ -43,6 +43,28 @@ class Spa { // Flag to track if initial load is complete (for session validation) static _initial_load_complete = false; + // Timestamp of last navigation start - used for error suppression grace period + static _navigation_timestamp = 0; + + // Grace period in milliseconds for suppressing errors after navigation + static NAVIGATION_GRACE_PERIOD_MS = 10000; + + /** + * Check if we're within the navigation grace period + * + * During the 10 seconds after navigation starts, pending AJAX requests from the + * previous page may error out (because their context is gone). These errors should + * not be shown to the user as they're not relevant to the new page. + * + * @returns {boolean} True if within grace period, false otherwise + */ + static is_within_navigation_grace_period() { + if (Spa._navigation_timestamp === 0) { + return false; + } + return (Date.now() - Spa._navigation_timestamp) < Spa.NAVIGATION_GRACE_PERIOD_MS; + } + /** * Disable SPA navigation - all navigation becomes full page loads * Call this when errors occur or forms are dirty @@ -521,6 +543,10 @@ class Spa { Spa.is_dispatching = true; + // Record navigation timestamp for error suppression grace period + // Errors from previous page's pending requests should be ignored for 10 seconds + Spa._navigation_timestamp = Date.now(); + try { const opts = { history: options.history || 'auto',