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 <noreply@anthropic.com>
This commit is contained in:
MarekWo
2026-03-20 09:15:44 +01:00
parent 3337e3fdff
commit 670715f57f
2 changed files with 39 additions and 7 deletions
+22 -1
View File
@@ -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) {
<i class="bi bi-clipboard-data"></i>
</button>
` : ''}
${contactsPubkeyMap[msg.sender] ? `
${contactsPubkeyMap[msg.sender] && !isContactProtectedByName(msg.sender) ? `
<button class="btn btn-outline-secondary btn-msg-action" onclick="ignoreContactFromChat('${contactsPubkeyMap[msg.sender]}')" title="Ignore ${escapeHtml(msg.sender)}">
<i class="bi bi-eye-slash"></i>
</button>
` : ''}
${!isContactProtectedByName(msg.sender) ? `
<button class="btn btn-outline-danger btn-msg-action" onclick="blockContactFromChat('${escapeHtml(msg.sender)}')" title="Block ${escapeHtml(msg.sender)}">
<i class="bi bi-slash-circle"></i>
</button>
` : ''}
</div>
</div>
</div>
+17 -6
View File
@@ -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 = '<i class="bi bi-eye-slash"></i> <span class="btn-label">Ignore</span>';
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 = '<i class="bi bi-slash-circle"></i> <span class="btn-label">Block</span>';
blockBtn.onclick = () => toggleContactBlock(contact.public_key, true);
if (isProtected) {
blockBtn.disabled = true;
blockBtn.title = 'Cannot block protected contact';
}
actionsDiv.appendChild(blockBtn);
}