Browse Source

Keep dispatch toast visible for 3s after print upload completes (#615)

  The print dispatch notification disappeared instantly for small files or
  closed immediately when the progress bar reached 100%, giving no
  confirmation that the job was submitted. The dispatch toast now stays
  visible for 3 seconds showing the completion message before auto-
  dismissing. For very fast uploads where no progress toast was shown,
  a fresh confirmation toast is created instead.
maziggy 2 months ago
parent
commit
b631713fff
4 changed files with 34 additions and 11 deletions
  1. 1 0
      CHANGELOG.md
  2. 32 10
      frontend/src/contexts/ToastContext.tsx
  3. 0 0
      static/assets/index-D7-kOLKn.js
  4. 1 1
      static/index.html

+ 1 - 0
CHANGELOG.md

@@ -13,6 +13,7 @@ All notable changes to Bambuddy will be documented in this file.
 - **Print Queue Scheduler Diagnostics** ([#616](https://github.com/maziggy/bambuddy/issues/616)) — Added diagnostic logging to the print queue scheduler to help diagnose why queued prints aren't starting. After each queue check, the scheduler now logs a skip summary (how many items were skipped due to manual_start, scheduled_time, etc.) and for each busy printer, logs the exact state preventing it from being considered idle (connected status, printer state, plate_cleared flag). Previously the scheduler only logged "found N pending items" with no visibility into why items were skipped.
 
 ### Fixed
+- **Print Dispatch Toast Disappears Instantly on Fast Uploads** ([#615](https://github.com/maziggy/bambuddy/issues/615)) — When sending a print job, the notification popup disappeared instantly for small files or closed immediately when the progress bar reached 100% for larger files, giving no confirmation that the job was submitted. The dispatch toast now stays visible for 3 seconds after completion, showing a success message (e.g. "1 print started successfully") before auto-dismissing. For very fast uploads where the progress toast was never shown, a fresh confirmation toast is created instead. Reported by @aneopsy.
 - **Print Modal Shows Busy Printers as Selectable** ([#622](https://github.com/maziggy/bambuddy/issues/622)) — When printing a file from the file manager, the print modal listed all printers including busy ones. Selecting a busy printer resulted in a failed send notification. The printer selector now fetches each printer's live status and shows a state badge (Idle, Printing, Paused, Preparing, Finished, Failed, Offline). In reprint mode, busy printers are grayed out and not selectable. "Select all" also skips busy printers. In queue mode, busy printers remain selectable since the job will wait. Reported by contact@aito3d.fr.
 - **PWA Install Not Available in Chrome** ([#629](https://github.com/maziggy/bambuddy/issues/629)) — Chrome did not show the PWA install prompt because the manifest icons had incorrect dimensions (e.g. 190px wide declared as 192px) and the manifest was missing the `screenshots` entries required for Chrome's richer install UI. Resized all three icons (`android-chrome-192x192.png`, `android-chrome-512x512.png`, `apple-touch-icon.png`) to their declared sizes, split the discouraged `"any maskable"` purpose into a dedicated `"maskable"` entry, and added mobile and desktop screenshots to the manifest. Reported by @SebSeifert.
 - **Project Statistics Count Archived Files as Printed** ([#630](https://github.com/maziggy/bambuddy/issues/630)) — Files added to a project from the archive were counted in project statistics (completed prints, parts progress) as if they had already been printed. Only files with `status="completed"` (actually printed via a printer) now count toward completion stats. Files with `status="archived"` (stored but not yet printed) are no longer included. Reported by @SebSeifert.

+ 32 - 10
frontend/src/contexts/ToastContext.tsx

@@ -342,9 +342,6 @@ export function ToastProvider({ children }: { children: ReactNode }) {
       }
 
       const recentStatus = detail.recent_event?.status;
-      if (!hasActiveWork && recentStatus && ['cancelled', 'failed', 'completed', 'idle'].includes(recentStatus)) {
-        setToasts((prev) => prev.filter((t) => t.id !== dispatchToastId));
-      }
 
       if (allDone) {
         const summaryKey = `${completed}:${failed}`;
@@ -353,17 +350,42 @@ export function ToastProvider({ children }: { children: ReactNode }) {
         }
         lastDispatchSummaryRef.current = summaryKey;
 
-        setToasts((prev) => prev.filter((t) => t.id !== dispatchToastId));
         const doneMessage = failed > 0
           ? t('backgroundDispatch.toast.completeWithFailures', { completed, failed })
           : t('backgroundDispatch.toast.completeSuccess', { completed });
-        const id = Math.random().toString(36).substr(2, 9);
-        setToasts((prev) => [...prev, { id, message: doneMessage, type: failed > 0 ? 'warning' : 'success' }]);
+
+        // Show a brief "completed" state on the dispatch toast before replacing with summary
+        // This ensures the user sees confirmation even for fast uploads (#615)
+        setToasts((prev) => {
+          const doneToast: Toast = {
+            id: dispatchToastId,
+            message: doneMessage,
+            type: failed > 0 ? 'warning' : 'success',
+            persistent: true,
+            // Clear dispatchData so it renders as a simple text toast
+          };
+          const exists = prev.find((toastItem) => toastItem.id === dispatchToastId);
+          if (exists) {
+            return prev.map((toastItem) =>
+              toastItem.id === dispatchToastId ? doneToast : toastItem
+            );
+          }
+          return [...prev, doneToast];
+        });
+
+        // Auto-dismiss after 3 seconds
+        const existingTimeout = timeoutRefs.current.get(dispatchToastId);
+        if (existingTimeout) clearTimeout(existingTimeout);
         const timeout = setTimeout(() => {
-          setToasts((prev) => prev.filter((t) => t.id !== id));
-          timeoutRefs.current.delete(id);
-        }, 4000);
-        timeoutRefs.current.set(id, timeout);
+          setToasts((prev) => prev.filter((t) => t.id !== dispatchToastId));
+          timeoutRefs.current.delete(dispatchToastId);
+        }, 3000);
+        timeoutRefs.current.set(dispatchToastId, timeout);
+        return;
+      }
+
+      if (!hasActiveWork && recentStatus && ['cancelled', 'failed', 'completed', 'idle'].includes(recentStatus)) {
+        setToasts((prev) => prev.filter((t) => t.id !== dispatchToastId));
       }
 
       if (detail.recent_event?.status === 'idle' && !hasActiveWork) {

File diff suppressed because it is too large
+ 0 - 0
static/assets/index-D7-kOLKn.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-CMePull4.js"></script>
+    <script type="module" crossorigin src="/assets/index-D7-kOLKn.js"></script>
     <link rel="stylesheet" crossorigin href="/assets/index-DOJtH8DG.css">
   </head>
   <body>

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