Browse Source

Fix dispatch toast stuck after second print dispatch

  The print dispatch progress toast stayed visible forever after the
  second print in a session. lastDispatchSummaryRef was never reset
  between batches — every single-printer dispatch produced the same
  summary key ("first-complete:1:0"), so subsequent completions matched
  the stale ref and skipped creating the done toast.

  Reset the dedup guard when the dispatch toast is dismissed and when
  a new batch starts.
maziggy 2 months ago
parent
commit
e1e4ec224c
4 changed files with 10 additions and 1 deletions
  1. 3 0
      CHANGELOG.md
  2. 6 0
      frontend/src/contexts/ToastContext.tsx
  3. 0 0
      static/assets/index-p3ObkGK6.js
  4. 1 1
      static/index.html

+ 3 - 0
CHANGELOG.md

@@ -4,6 +4,9 @@ All notable changes to Bambuddy will be documented in this file.
 
 ## [0.2.2b3] - Unreleased
 
+### Fixed
+- **Dispatch Toast Stuck After Second Print** — The print dispatch progress toast ("Starting prints…") stayed visible forever after the second print dispatch in a session. The dedup guard (`lastDispatchSummaryRef`) that prevents duplicate completion toasts was never reset between batches, so every single-printer dispatch produced the same summary key (`"first-complete:1:0"`). The first print completed normally, but subsequent completions matched the stale ref and skipped creating the done toast — leaving the progress toast stuck in "Processing" state with no way to dismiss except a page reload. Now resets the dedup guard whenever the dispatch toast is dismissed (auto-dismiss timeout, cleanup events) and when a new batch starts.
+
 ## [0.2.2b2] - 2026-03-06
 
 ### New Features

+ 6 - 0
frontend/src/contexts/ToastContext.tsx

@@ -268,6 +268,7 @@ export function ToastProvider({ children }: { children: ReactNode }) {
           const timeout = setTimeout(() => {
             setToasts((prev) => prev.filter((t) => t.id !== dispatchToastId));
             timeoutRefs.current.delete(dispatchToastId);
+            lastDispatchSummaryRef.current = null;
           }, 3000);
           timeoutRefs.current.set(dispatchToastId, timeout);
         }
@@ -275,6 +276,8 @@ export function ToastProvider({ children }: { children: ReactNode }) {
       }
 
       if (hasActiveWork) {
+        // New batch starting — reset dedup guard so completion toast works
+        lastDispatchSummaryRef.current = null;
         setToasts((prev) => {
           const existing = prev.find((toastItem) => toastItem.id === dispatchToastId);
           const existingJobs = existing?.dispatchData?.jobs || [];
@@ -419,6 +422,7 @@ export function ToastProvider({ children }: { children: ReactNode }) {
         const timeout = setTimeout(() => {
           setToasts((prev) => prev.filter((t) => t.id !== dispatchToastId));
           timeoutRefs.current.delete(dispatchToastId);
+          lastDispatchSummaryRef.current = null;
         }, 3000);
         timeoutRefs.current.set(dispatchToastId, timeout);
         return;
@@ -426,10 +430,12 @@ export function ToastProvider({ children }: { children: ReactNode }) {
 
       if (!hasActiveWork && recentStatus && ['cancelled', 'failed', 'completed', 'idle'].includes(recentStatus)) {
         setToasts((prev) => prev.filter((t) => t.id !== dispatchToastId));
+        lastDispatchSummaryRef.current = null;
       }
 
       if (detail.recent_event?.status === 'idle' && !hasActiveWork) {
         setToasts((prev) => prev.filter((t) => t.id !== dispatchToastId));
+        lastDispatchSummaryRef.current = null;
       }
 
       if (!hasActiveWork) {

File diff suppressed because it is too large
+ 0 - 0
static/assets/index-p3ObkGK6.js


+ 1 - 1
static/index.html

@@ -23,7 +23,7 @@
 
     <!-- Splash screens for iOS -->
     <link rel="apple-touch-startup-image" href="/img/android-chrome-512x512.png" />
-    <script type="module" crossorigin src="/assets/index-ClxzXUHL.js"></script>
+    <script type="module" crossorigin src="/assets/index-p3ObkGK6.js"></script>
     <link rel="stylesheet" crossorigin href="/assets/index-DfcIVNpM.css">
   </head>
   <body>

Some files were not shown because too many files changed in this diff