From 670715f57f5d2a4c255e51e28db78b3a63bd4de0 Mon Sep 17 00:00:00 2001 From: MarekWo Date: Fri, 20 Mar 2026 09:15:44 +0100 Subject: [PATCH] fix(contacts): disable ignore/block buttons for protected contacts - Existing Contacts: Ignore and Block buttons are now disabled when contact is protected, matching the existing Delete button behavior - updateProtectionUI: toggling protection now also enables/disables Ignore, Block, and Delete buttons dynamically - Chat: Ignore and Block buttons are hidden in message bubbles for protected contacts (loads protected pubkeys on init) Co-Authored-By: Claude Opus 4.6 --- app/static/js/app.js | 23 ++++++++++++++++++++++- app/static/js/contacts.js | 23 +++++++++++++++++------ 2 files changed, 39 insertions(+), 7 deletions(-) diff --git a/app/static/js/app.js b/app/static/js/app.js index 7f04e3e..591761b 100644 --- a/app/static/js/app.js +++ b/app/static/js/app.js @@ -23,6 +23,7 @@ let markersGroup = null; let contactsGeoCache = {}; // { 'contactName': { lat, lon }, ... } let contactsPubkeyMap = {}; // { 'contactName': 'full_pubkey', ... } let blockedContactNames = new Set(); // Names of blocked contacts +let protectedContactPubkeys = new Set(); // Pubkeys of protected contacts let allContactsWithGps = []; // Device contacts for map filtering let allCachedContactsWithGps = []; // Cache-only contacts for map let _selfInfo = null; // Own device info (for map marker) @@ -372,6 +373,23 @@ async function loadBlockedNames() { } } +async function loadProtectedPubkeys() { + try { + const resp = await fetch('/api/contacts/protected'); + const data = await resp.json(); + if (data.success) { + protectedContactPubkeys = new Set((data.protected_contacts || []).map(pk => pk.toLowerCase())); + } + } catch (err) { + console.error('Error loading protected contacts:', err); + } +} + +function isContactProtectedByName(senderName) { + const pubkey = contactsPubkeyMap[senderName]; + return pubkey && protectedContactPubkeys.has(pubkey.toLowerCase()); +} + // Initialize on page load /** * Connect to SocketIO /chat namespace for real-time message updates @@ -486,6 +504,7 @@ document.addEventListener('DOMContentLoaded', async function() { const messagesPromise = loadMessages(); const geoCachePromise = loadContactsGeoCache(); // Non-blocking, Map buttons update when ready const blockedPromise = loadBlockedNames(); // Non-blocking, for real-time filtering + const protectedPromise = loadProtectedPubkeys(); // Non-blocking, for disabling ignore/block on protected // Also start archive list loading in parallel loadArchiveList(); @@ -1187,14 +1206,16 @@ function createMessageElement(msg) { ` : ''} - ${contactsPubkeyMap[msg.sender] ? ` + ${contactsPubkeyMap[msg.sender] && !isContactProtectedByName(msg.sender) ? ` ` : ''} + ${!isContactProtectedByName(msg.sender) ? ` + ` : ''} diff --git a/app/static/js/contacts.js b/app/static/js/contacts.js index ce0f07b..0a31470 100644 --- a/app/static/js/contacts.js +++ b/app/static/js/contacts.js @@ -1082,12 +1082,15 @@ function updateProtectionUI(publicKey, isProtected, buttonEl) { if (lockIcon) lockIcon.remove(); } - // Enable/disable delete button - const deleteBtn = cardEl.querySelector('.btn-outline-danger'); - if (deleteBtn) { - deleteBtn.disabled = isProtected; - deleteBtn.title = isProtected ? 'Cannot delete protected contact' : ''; - } + // Enable/disable delete, ignore, and block buttons based on protection + cardEl.querySelectorAll('button').forEach(btn => { + const icon = btn.querySelector('i'); + if (!icon) return; + if (icon.classList.contains('bi-trash') || icon.classList.contains('bi-eye-slash') || icon.classList.contains('bi-slash-circle')) { + btn.disabled = isProtected; + btn.title = isProtected ? 'Protected contact' : ''; + } + }); } async function toggleContactIgnore(publicKey, ignored) { @@ -2335,12 +2338,20 @@ function createExistingContactCard(contact, index) { ignoreBtn.className = 'btn btn-sm btn-outline-secondary'; ignoreBtn.innerHTML = ' Ignore'; ignoreBtn.onclick = () => toggleContactIgnore(contact.public_key, true); + if (isProtected) { + ignoreBtn.disabled = true; + ignoreBtn.title = 'Cannot ignore protected contact'; + } actionsDiv.appendChild(ignoreBtn); const blockBtn = document.createElement('button'); blockBtn.className = 'btn btn-sm btn-outline-danger'; blockBtn.innerHTML = ' Block'; blockBtn.onclick = () => toggleContactBlock(contact.public_key, true); + if (isProtected) { + blockBtn.disabled = true; + blockBtn.title = 'Cannot block protected contact'; + } actionsDiv.appendChild(blockBtn); }