mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2026-01-20 17:19:34 +00:00
removed augmented checkbox and combined functionality into info mode checkbox. changed barcode scanner to use XHR callback for barcode decoding to avoid problems with form submission and camera caused by page reloaded when part not found.
This commit is contained in:
parent
03d1fc55ce
commit
052780c865
6 changed files with 137 additions and 136 deletions
|
|
@ -29,12 +29,20 @@ export default class extends Controller {
|
|||
_scanner = null;
|
||||
_submitting = false;
|
||||
_lastDecodedText = "";
|
||||
_onInfoChange = null;
|
||||
|
||||
connect() {
|
||||
// Prevent double init if connect fires twice
|
||||
if (this._scanner) return;
|
||||
|
||||
this.bindModeToggles();
|
||||
// clear last decoded barcode when state changes on info box
|
||||
const info = document.getElementById("scan_dialog_info_mode");
|
||||
if (info) {
|
||||
this._onInfoChange = () => {
|
||||
this._lastDecodedText = "";
|
||||
};
|
||||
info.addEventListener("change", this._onInfoChange);
|
||||
}
|
||||
|
||||
const isMobile = window.matchMedia("(max-width: 768px)").matches;
|
||||
|
||||
|
|
@ -74,7 +82,13 @@ export default class extends Controller {
|
|||
this._scanner = null;
|
||||
this._submitting = false;
|
||||
this._lastDecodedText = "";
|
||||
this.unbindModeToggles();
|
||||
|
||||
// Unbind info-mode change handler (always do this, even if scanner is null)
|
||||
const info = document.getElementById("scan_dialog_info_mode");
|
||||
if (info && this._onInfoChange) {
|
||||
info.removeEventListener("change", this._onInfoChange);
|
||||
}
|
||||
this._onInfoChange = null;
|
||||
|
||||
if (!scanner) return;
|
||||
|
||||
|
|
@ -86,49 +100,14 @@ export default class extends Controller {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add events to Mode checkboxes so they both can't be selected at the same time
|
||||
*/
|
||||
bindModeToggles() {
|
||||
const info = document.getElementById("scan_dialog_info_mode");
|
||||
const aug = document.getElementById("scan_dialog_augmented_mode");
|
||||
if (!info || !aug) return;
|
||||
|
||||
const onInfoChange = () => {
|
||||
if (info.checked) aug.checked = false;
|
||||
};
|
||||
const onAugChange = () => {
|
||||
if (aug.checked) info.checked = false;
|
||||
};
|
||||
|
||||
info.addEventListener("change", onInfoChange);
|
||||
aug.addEventListener("change", onAugChange);
|
||||
|
||||
// Save references so we can remove listeners on disconnect
|
||||
this._onInfoChange = onInfoChange;
|
||||
this._onAugChange = onAugChange;
|
||||
}
|
||||
|
||||
unbindModeToggles() {
|
||||
const info = document.getElementById("scan_dialog_info_mode");
|
||||
const aug = document.getElementById("scan_dialog_augmented_mode");
|
||||
if (!info || !aug) return;
|
||||
|
||||
if (this._onInfoChange) info.removeEventListener("change", this._onInfoChange);
|
||||
if (this._onAugChange) aug.removeEventListener("change", this._onAugChange);
|
||||
|
||||
this._onInfoChange = null;
|
||||
this._onAugChange = null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
async onScanSuccess(decodedText) {
|
||||
if (!decodedText) return;
|
||||
|
||||
const normalized = String(decodedText).trim();
|
||||
if (!normalized) return;
|
||||
|
||||
// If we already handled this exact barcode and it's still showing, ignore.
|
||||
// scan once per barcode
|
||||
if (normalized === this._lastDecodedText) return;
|
||||
|
||||
// If a request/submit is in-flight, ignore scans.
|
||||
|
|
@ -142,43 +121,42 @@ export default class extends Controller {
|
|||
const input = document.getElementById("scan_dialog_input");
|
||||
if (input) input.value = decodedText;
|
||||
|
||||
const augmented = !!document.getElementById("scan_dialog_augmented_mode")?.checked;
|
||||
const infoMode = !!document.getElementById("scan_dialog_info_mode")?.checked;
|
||||
|
||||
// If augmented mode: do NOT submit the form.
|
||||
if (augmented) {
|
||||
try {
|
||||
await this.lookupAndRender(decodedText);
|
||||
} catch (e) {
|
||||
console.warn("[barcode_scan] augmented lookup failed", e);
|
||||
// Allow retry on failure by clearing last decoded text
|
||||
this._lastDecodedText = "";
|
||||
} finally {
|
||||
// allow scanning again
|
||||
this._submitting = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Non-augmented: Stop scanner BEFORE submitting to avoid camera transition races
|
||||
try {
|
||||
if (this._scanner?.clear) {
|
||||
await this._scanner.clear();
|
||||
}
|
||||
} catch (_) {
|
||||
// ignore
|
||||
} finally {
|
||||
this._scanner = null;
|
||||
}
|
||||
const data = await this.lookup(normalized, infoMode);
|
||||
|
||||
//Submit form
|
||||
document.getElementById("scan_dialog_form")?.requestSubmit();
|
||||
// ok:false = transient junk decode; ignore without wiping UI
|
||||
if (!data || data.ok !== true) {
|
||||
this._lastDecodedText = ""; // allow retry
|
||||
return;
|
||||
}
|
||||
|
||||
// If info mode is OFF and part was found -> redirect
|
||||
if (!infoMode && data.found && data.redirectUrl) {
|
||||
window.location.assign(data.redirectUrl);
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise render returned fragment HTML
|
||||
if (typeof data.html === "string" && data.html !== "") {
|
||||
const el = document.getElementById("scan-augmented-result");
|
||||
if (el) el.innerHTML = data.html;
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn("[barcode_scan] lookup failed", e);
|
||||
// allow retry on failure
|
||||
this._lastDecodedText = "";
|
||||
} finally {
|
||||
this._submitting = false;
|
||||
}
|
||||
}
|
||||
|
||||
async lookupAndRender(decodedText) {
|
||||
const form = document.getElementById("scan_dialog_form");
|
||||
if (!form) return;
|
||||
|
||||
// Ensure the hidden csrf field has been converted from placeholder -> real token + cookie set
|
||||
async lookup(decodedText, infoMode) {
|
||||
const form = document.getElementById("scan_dialog_form");
|
||||
if (!form) return { ok: false };
|
||||
|
||||
generateCsrfToken(form);
|
||||
|
||||
const mode =
|
||||
|
|
@ -187,23 +165,28 @@ export default class extends Controller {
|
|||
const body = new URLSearchParams();
|
||||
body.set("input", decodedText);
|
||||
if (mode !== "") body.set("mode", mode);
|
||||
body.set("info_mode", infoMode ? "1" : "0");
|
||||
|
||||
const headers = {
|
||||
"Accept": "text/html",
|
||||
"Accept": "application/json",
|
||||
"Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
|
||||
...generateCsrfHeaders(form), // adds the special CSRF header Symfony expects (if enabled)
|
||||
...generateCsrfHeaders(form),
|
||||
};
|
||||
|
||||
const resp = await fetch(this.element.dataset.augmentedUrl, {
|
||||
const url = this.element.dataset.lookupUrl;
|
||||
if (!url) throw new Error("Missing data-lookup-url on #reader-box");
|
||||
|
||||
const resp = await fetch(url, {
|
||||
method: "POST",
|
||||
headers,
|
||||
body: body.toString(),
|
||||
credentials: "same-origin",
|
||||
});
|
||||
|
||||
const html = await resp.text();
|
||||
if (!resp.ok) {
|
||||
throw new Error(`lookup failed: HTTP ${resp.status}`);
|
||||
}
|
||||
|
||||
const el = document.getElementById("scan-augmented-result");
|
||||
if (el) el.innerHTML = html;
|
||||
return await resp.json();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue