From eaeb5bd64f5534912c572e320e99cf7142bef9ed Mon Sep 17 00:00:00 2001 From: NguyenND Date: Tue, 19 May 2026 10:07:33 -0400 Subject: [PATCH] 05/19 Enhance codes 3 --- app/static/js/vault.js | 94 +++++++++++++++++++++++------------------- 1 file changed, 52 insertions(+), 42 deletions(-) diff --git a/app/static/js/vault.js b/app/static/js/vault.js index 3758d9f..eade3bd 100644 --- a/app/static/js/vault.js +++ b/app/static/js/vault.js @@ -480,8 +480,8 @@ const Vault = (() => { _sortOrder === "folder" ? Object.keys(groups).sort() : ["(No folder)", ..._folders.map((f) => f.name)].filter( - (k) => groups[k], - ); + (k) => groups[k], + ); Object.keys(groups).forEach((k) => { if (!keys.includes(k)) keys.push(k); }); @@ -549,7 +549,7 @@ const Vault = (() => { case "card": subText = item.plain.card_number ? "•••• " + - String(item.plain.card_number).replace(/\s/g, "").slice(-4) + String(item.plain.card_number).replace(/\s/g, "").slice(-4) : ""; break; case "bank": @@ -634,6 +634,8 @@ const Vault = (() => { // Wire the bulk-select checkbox. const checkbox = li.querySelector(".item-checkbox"); if (checkbox) { + // Stop the click from bubbling to the li (which would open the modal). + checkbox.addEventListener("click", (e) => e.stopPropagation()); checkbox.addEventListener("change", () => { if (checkbox.checked) { _selectedIds.add(item.id); @@ -714,7 +716,18 @@ const Vault = (() => { confirmDeleteItem(item); }, ); - li.addEventListener("click", () => openModal("edit", item)); + li.addEventListener("click", (e) => { + if (_selectMode) { + // Toggle the checkbox when the row itself is clicked (not the checkbox). + const cb = li.querySelector(".item-checkbox"); + if (cb && e.target !== cb) { + cb.checked = !cb.checked; + cb.dispatchEvent(new Event("change")); + } + return; + } + openModal("edit", item); + }); return li; } @@ -1112,9 +1125,9 @@ const Vault = (() => { 0, Math.round( 100 - - (weak.length / total) * 40 - - (reused.length / total) * 30 - - (old.length / total) * 15, + (weak.length / total) * 40 - + (reused.length / total) * 30 - + (old.length / total) * 15, ), ); const cls = @@ -1264,16 +1277,16 @@ const Vault = (() => { `; hibpSection.querySelectorAll("[data-sec-edit]").forEach((btn) => { btn.addEventListener("click", () => { @@ -1835,10 +1848,9 @@ const Vault = (() => { ${escHtml(s.item_name)} From ${escHtml(s.owner_email)}${expiryLabel} - ${ - !s.accepted - ? `` - : `` + : `` - } + } `; }, ) @@ -2389,10 +2401,9 @@ const Vault = (() => { `; @@ -2721,8 +2732,8 @@ const Vault = (() => { const typeLabel = transports.includes("internal") ? "📱 Device" : transports.some((t) => ["usb", "nfc", "ble", "smart-card"].includes(t)) - ? "🔑 Security key" - : "🔑 Passkey"; + ? "🔑 Security key" + : "🔑 Passkey"; return `
${escHtml(c.name)} @@ -2985,16 +2996,15 @@ const Vault = (() => {

${isFirstTime ? "🔐 MFA enabled — save your backup codes" : "🔐 New backup codes"}

- ${ - isFirstTime - ? `

+ ${isFirstTime + ? `

These codes let you sign in if you lose access to your authenticator app. Save them now — they will not be shown again.

` - : `

+ : `

Your previous codes have been invalidated. Save these new codes securely.

` - } + }
${codesHtml}
@@ -3503,14 +3513,14 @@ const Vault = (() => { const pool = !_activeFilter ? _items : _activeFilter.type === "itemType" - ? _items.filter((i) => i.item_type === _activeFilter.value) - : _activeFilter.type === "folder" - ? _items.filter((i) => i.folder_id === _activeFilter.value) - : _activeFilter.type === "tag" - ? _items.filter((i) => - (i.plain?.tags || []).includes(_activeFilter.value), - ) - : _items; + ? _items.filter((i) => i.item_type === _activeFilter.value) + : _activeFilter.type === "folder" + ? _items.filter((i) => i.folder_id === _activeFilter.value) + : _activeFilter.type === "tag" + ? _items.filter((i) => + (i.plain?.tags || []).includes(_activeFilter.value), + ) + : _items; renderItemList( pool.filter((item) => { const p = item.plain || {}; @@ -3719,7 +3729,7 @@ const Vault = (() => { if ( (mode === "add" && (document.getElementById("field-type").value || "password") === - "password") || + "password") || (mode === "edit" && (item?.item_type || "password") === "password") ) { initPasswordFieldEnhancements(); @@ -3984,7 +3994,7 @@ const Vault = (() => { "X-CSRFToken": csrfToken(), }, body: JSON.stringify({ refresh_token: refreshToken }), - }).catch(() => {}); + }).catch(() => { }); VaultSession.clear(); SharingSession.clear(); sessionStorage.removeItem("access_token"); @@ -4089,11 +4099,11 @@ const Vault = (() => { // Auto-clear clipboard after 30 seconds — industry-standard hygiene. if (_clipboardClearTimer) clearTimeout(_clipboardClearTimer); _clipboardClearTimer = setTimeout(() => { - navigator.clipboard.writeText("").catch(() => {}); + navigator.clipboard.writeText("").catch(() => { }); _clipboardClearTimer = null; }, 30_000); }) - .catch(() => {}); + .catch(() => { }); } function escHtml(str) { @@ -4713,4 +4723,4 @@ const Vault = (() => { } })(); -document.addEventListener("DOMContentLoaded", Vault.init); +document.addEventListener("DOMContentLoaded", Vault.init); \ No newline at end of file