Browse Source

fix(labels): single PDF per click (#1628)

  window.open(url, '_blank', 'noopener,noreferrer') returns null even on
  success per the WindowFeatures spec — `noopener` deliberately suppresses
  the return reference. The label-print modal treated null as "popup
  blocked → fall back to <a download> click", so the fallback fired on
  every click. Result: window.open opened the blob tab (downloading a
  random-named PDF on systems without an inline viewer) AND the fallback
  downloaded bambuddy-labels.pdf — two identical PDFs per click.

  Drop noopener,noreferrer. The blob is same-origin, the destination is a
  passive PDF preview tab with no script context, and noreferrer is no-op
  for blob URLs. window.open now returns a real window reference on
  success and the if (!win) fallback only fires on genuine popup-block.
maziggy 3 tháng trước cách đây
mục cha
commit
24ce250176

+ 3 - 0
CHANGELOG.md

@@ -4,6 +4,9 @@ All notable changes to Bambuddy will be documented in this file.
 
 ## [0.2.5b1] - Unreleased
 
+### Fixed
+- **Label printing produced two identical PDFs per click (#1628)** — `LabelTemplatePickerModal.tsx::openBlobInNewTab` called `window.open(url, '_blank', 'noopener,noreferrer')` and treated a `null` return as "popup blocked → fall back to `<a download>` click." Per the WindowFeatures spec, `noopener` deliberately forces `window.open` to return `null` even on success, so the `if (!win)` fallback fired on EVERY click. Path 1 (window.open) opened the blob tab — on Linux Chromium without an inline PDF viewer the OS saved a random-named copy (the `zo70GhSL.pdf` / `f7w0OcDi.pdf` files in the reporter's screenshot). Path 2 (fallback) downloaded a second copy named `bambuddy-labels.pdf`. Two identical PDFs per click. Fix: drop `noopener,noreferrer`. The blob is same-origin (created via `URL.createObjectURL` from our own fetch response), the destination is a passive PDF preview tab with no script context to abuse `window.opener`, and `noreferrer` is a no-op for blob URLs. After removal, `window.open` returns a real window reference on success → `if (!win)` only fires on genuine popup-block, single PDF per click. Existing 17 vitest cases in `LabelTemplatePickerModal.test.tsx` still pass; the change is comment + one parameter.
+
 ### Changed
 - **AMS drying now enabled for H2C starting at firmware 01.02.00.00** — H2C was previously in `_DRYING_UNSUPPORTED_MODELS` alongside the A1 family. Moved to `_DRYING_MIN_FIRMWARE` with the same `01.02.00.00` floor as H2S / P2S. Both SSDP model codes the H2C advertises (`O1C`, `O1C2` — single- and dual-nozzle variants) get the same firmware gate so the `supports_drying()` check fires correctly regardless of which form is in the printer record. Test coverage extended in `TestSupportsDrying`: H2C / O1C / O1C2 cases added to the with-firmware pass set, the old-firmware fail set, and removed from the unsupported-models loop.
 

+ 10 - 1
frontend/src/components/LabelTemplatePickerModal.tsx

@@ -73,7 +73,16 @@ const TEMPLATE_OPTIONS: TemplateOption[] = [
 
 function openBlobInNewTab(blob: Blob): void {
   const url = window.URL.createObjectURL(blob);
-  const win = window.open(url, '_blank', 'noopener,noreferrer');
+  // Do NOT pass `noopener,noreferrer`: per the WindowFeatures spec, `noopener`
+  // forces window.open to return `null` even on success, which made the
+  // `if (!win)` popup-block fallback below fire on EVERY click — so the blob
+  // tab opened (downloading a random-named PDF on systems without an inline
+  // viewer) AND the `<a download>` fallback fired (downloading a second copy
+  // named bambuddy-labels.pdf). Two identical PDFs per click — issue #1628.
+  // The blob is same-origin, the destination is a passive PDF tab with no
+  // script context, and `noreferrer` is a no-op for blob URLs, so dropping
+  // these flags has no security impact.
+  const win = window.open(url, '_blank');
   if (!win) {
     const a = document.createElement('a');
     a.href = url;

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
static/assets/index-CYiRVzBv.js


+ 1 - 1
static/index.html

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

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác