diff --git a/app/routes/api.py b/app/routes/api.py
index ee5f222..5f8c9d3 100644
--- a/app/routes/api.py
+++ b/app/routes/api.py
@@ -2751,6 +2751,13 @@ def get_pending_contacts_api():
excluded = ignored_keys | blocked_keys
pending = [c for c in pending if c.get('public_key', '').lower() not in excluded]
+ # Add type_label for frontend display
+ type_labels = {1: 'CLI', 2: 'REP', 3: 'ROOM', 4: 'SENS'}
+ for c in pending:
+ c['type_label'] = type_labels.get(c.get('type', 0), 'CLI')
+ pk = c.get('public_key', '')
+ c['public_key_prefix'] = pk[:12] if len(pk) >= 12 else pk
+
# Filter by types if specified
if types_param:
pending = [contact for contact in pending if contact.get('type') in types_param]
diff --git a/app/static/css/style.css b/app/static/css/style.css
index f7000eb..81a92d8 100644
--- a/app/static/css/style.css
+++ b/app/static/css/style.css
@@ -333,6 +333,18 @@ main {
font-size: 0.875rem;
}
+ /* Contact card buttons: icon-only on mobile */
+ .pending-contact-card .btn-label,
+ .existing-contact-card .btn-label {
+ display: none;
+ }
+
+ /* Allow button wrapping on small screens */
+ .pending-contact-card .d-flex.gap-2,
+ .existing-contact-card .d-flex.gap-2 {
+ flex-wrap: wrap;
+ }
+
/* Modal: Better mobile layout */
.modal-dialog {
margin: 0.5rem;
diff --git a/app/static/js/contacts.js b/app/static/js/contacts.js
index 5c65d94..0ab3dea 100644
--- a/app/static/js/contacts.js
+++ b/app/static/js/contacts.js
@@ -1037,11 +1037,11 @@ function updateProtectionUI(publicKey, isProtected, buttonEl) {
// Update button appearance
buttonEl.disabled = false;
if (isProtected) {
- buttonEl.innerHTML = ' Protected';
+ buttonEl.innerHTML = ' Protected';
buttonEl.classList.remove('btn-outline-warning');
buttonEl.classList.add('btn-warning');
} else {
- buttonEl.innerHTML = ' Protect';
+ buttonEl.innerHTML = ' Protect';
buttonEl.classList.remove('btn-warning');
buttonEl.classList.add('btn-outline-warning');
}
@@ -1337,11 +1337,12 @@ function createContactCard(contact, index) {
infoRow.appendChild(nameDiv);
infoRow.appendChild(typeBadge);
- // Public key row (use prefix for display)
+ // Public key row (clickable to copy)
const keyDiv = document.createElement('div');
- keyDiv.className = 'contact-key';
+ keyDiv.className = 'contact-key clickable-key';
keyDiv.textContent = contact.public_key_prefix || contact.public_key.substring(0, 12);
- keyDiv.title = 'Public Key Prefix';
+ keyDiv.title = 'Click to copy';
+ keyDiv.onclick = () => copyToClipboard(keyDiv.textContent, keyDiv);
// Last advert (optional - show if available)
let lastAdvertDiv = null;
@@ -1359,7 +1360,7 @@ function createContactCard(contact, index) {
// Approve button
const approveBtn = document.createElement('button');
approveBtn.className = 'btn btn-sm btn-success';
- approveBtn.innerHTML = ' Approve';
+ approveBtn.innerHTML = ' Approve';
approveBtn.onclick = () => approveContact(contact, index);
actionsDiv.appendChild(approveBtn);
@@ -1368,23 +1369,15 @@ function createContactCard(contact, index) {
if (contact.adv_lat && contact.adv_lon && (contact.adv_lat !== 0 || contact.adv_lon !== 0)) {
const mapBtn = document.createElement('button');
mapBtn.className = 'btn btn-sm btn-outline-primary';
- mapBtn.innerHTML = ' Map';
+ mapBtn.innerHTML = ' Map';
mapBtn.onclick = () => window.showContactOnMap(contact.name, contact.adv_lat, contact.adv_lon);
actionsDiv.appendChild(mapBtn);
}
- // Copy key button
- const copyBtn = document.createElement('button');
- copyBtn.className = 'btn btn-sm btn-outline-secondary';
- copyBtn.innerHTML = ' Copy Key';
- copyBtn.onclick = () => copyPublicKey(contact.public_key, copyBtn);
-
- actionsDiv.appendChild(copyBtn);
-
// Ignore button
const ignoreBtn = document.createElement('button');
ignoreBtn.className = 'btn btn-sm btn-outline-secondary';
- ignoreBtn.innerHTML = ' Ignore';
+ ignoreBtn.innerHTML = ' Ignore';
ignoreBtn.onclick = () => {
toggleContactIgnore(contact.public_key, true).then(() => loadPendingContacts());
};
@@ -1393,7 +1386,7 @@ function createContactCard(contact, index) {
// Block button
const blockBtn = document.createElement('button');
blockBtn.className = 'btn btn-sm btn-outline-danger';
- blockBtn.innerHTML = ' Block';
+ blockBtn.innerHTML = ' Block';
blockBtn.onclick = () => {
toggleContactBlock(contact.public_key, true).then(() => loadPendingContacts());
};
@@ -2091,7 +2084,7 @@ function createExistingContactCard(contact, index) {
if (contact.adv_lat && contact.adv_lon && (contact.adv_lat !== 0 || contact.adv_lon !== 0)) {
const mapBtn = document.createElement('button');
mapBtn.className = 'btn btn-sm btn-outline-primary';
- mapBtn.innerHTML = ' Map';
+ mapBtn.innerHTML = ' Map';
mapBtn.onclick = () => window.showContactOnMap(contact.name, contact.adv_lat, contact.adv_lon);
actionsDiv.appendChild(mapBtn);
}
@@ -2101,14 +2094,14 @@ function createExistingContactCard(contact, index) {
const protectBtn = document.createElement('button');
protectBtn.className = isProtected ? 'btn btn-sm btn-warning' : 'btn btn-sm btn-outline-warning';
protectBtn.innerHTML = isProtected
- ? ' Protected'
- : ' Protect';
+ ? ' Protected'
+ : ' Protect';
protectBtn.onclick = () => toggleContactProtection(contact.public_key, protectBtn);
actionsDiv.appendChild(protectBtn);
const deleteBtn = document.createElement('button');
deleteBtn.className = 'btn btn-sm btn-outline-danger';
- deleteBtn.innerHTML = ' Delete';
+ deleteBtn.innerHTML = ' Delete';
deleteBtn.onclick = () => showDeleteModal(contact);
deleteBtn.disabled = isProtected;
if (isProtected) {
@@ -2121,25 +2114,25 @@ function createExistingContactCard(contact, index) {
if (contact.is_blocked) {
const unblockBtn = document.createElement('button');
unblockBtn.className = 'btn btn-sm btn-outline-success';
- unblockBtn.innerHTML = ' Unblock';
+ unblockBtn.innerHTML = ' Unblock';
unblockBtn.onclick = () => toggleContactBlock(contact.public_key, false);
actionsDiv.appendChild(unblockBtn);
} else if (contact.is_ignored) {
const unignoreBtn = document.createElement('button');
unignoreBtn.className = 'btn btn-sm btn-outline-success';
- unignoreBtn.innerHTML = ' Unignore';
+ unignoreBtn.innerHTML = ' Unignore';
unignoreBtn.onclick = () => toggleContactIgnore(contact.public_key, false);
actionsDiv.appendChild(unignoreBtn);
} else {
const ignoreBtn = document.createElement('button');
ignoreBtn.className = 'btn btn-sm btn-outline-secondary';
- ignoreBtn.innerHTML = ' Ignore';
+ ignoreBtn.innerHTML = ' Ignore';
ignoreBtn.onclick = () => toggleContactIgnore(contact.public_key, true);
actionsDiv.appendChild(ignoreBtn);
const blockBtn = document.createElement('button');
blockBtn.className = 'btn btn-sm btn-outline-danger';
- blockBtn.innerHTML = ' Block';
+ blockBtn.innerHTML = ' Block';
blockBtn.onclick = () => toggleContactBlock(contact.public_key, true);
actionsDiv.appendChild(blockBtn);
}