From 5df9b4b4a2cdfdf45ab3896cf2f35922a3b032be Mon Sep 17 00:00:00 2001 From: MarekWo Date: Fri, 27 Mar 2026 20:29:26 +0100 Subject: [PATCH] fix(ui): refresh Contact Info path display in real-time Path info in Contact Info modal was stale due to 60s server cache and no refresh after path operations. Now: - Invalidate contacts cache after reset_path, change_path, path_update - Emit 'path_changed' socket event on PATH_UPDATE from device - UI listens and re-renders Contact Info when path changes - Reset to FLOOD button immediately refreshes the path display Co-Authored-By: Claude Opus 4.6 --- app/device_manager.py | 19 +++++++++++++++++++ app/routes/api.py | 1 + app/static/js/dm.js | 41 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+) diff --git a/app/device_manager.py b/app/device_manager.py index 9ff3942..ab10e3d 100644 --- a/app/device_manager.py +++ b/app/device_manager.py @@ -740,6 +740,19 @@ class DeviceManager: ) logger.debug(f"Path update for {pubkey[:8]}...") + # Invalidate contacts cache so UI gets fresh path data + try: + from app.routes.api import invalidate_contacts_cache + invalidate_contacts_cache() + except ImportError: + pass + + # Notify UI about path change + if self.socketio: + self.socketio.emit('path_changed', { + 'public_key': pubkey, + }, namespace='/chat') + # Backup: check for pending DM to this contact for ack_code, dm_id in list(self._pending_acks.items()): dm = self.db.get_dm_by_id(dm_id) @@ -1161,6 +1174,12 @@ class DeviceManager: """Change contact path on device with proper hash_size encoding.""" path_hash_mode = hash_size - 1 # 0=1B, 1=2B, 2=3B await self.mc.commands.change_contact_path(contact, path_hex, path_hash_mode=path_hash_mode) + # Invalidate contacts cache so UI gets fresh path data + try: + from app.routes.api import invalidate_contacts_cache + invalidate_contacts_cache() + except ImportError: + pass async def _restore_primary_path(self, contact, contact_pubkey: str): """Restore the primary configured path on the device after retry exhaustion.""" diff --git a/app/routes/api.py b/app/routes/api.py index 6c445dd..6472899 100644 --- a/app/routes/api.py +++ b/app/routes/api.py @@ -2322,6 +2322,7 @@ def reset_contact_to_flood(pubkey): dev_result = dm.reset_path(pubkey) logger.info(f"reset_path({pubkey[:12]}...) result: {dev_result}") if dev_result.get('success'): + invalidate_contacts_cache() return jsonify({'success': True}), 200 return jsonify({'success': False, 'error': dev_result.get('error', 'Device reset failed')}), 500 except Exception as e: diff --git a/app/static/js/dm.js b/app/static/js/dm.js index a8d5438..61ee759 100644 --- a/app/static/js/dm.js +++ b/app/static/js/dm.js @@ -120,6 +120,18 @@ function connectChatSocket() { chatSocket.on('device_status', (data) => { updateStatus(data.connected ? 'connected' : 'disconnected'); }); + + // Real-time path change — refresh Contact Info if open for this contact + chatSocket.on('path_changed', async (data) => { + const modalEl = document.getElementById('dmContactInfoModal'); + if (!modalEl || !modalEl.classList.contains('show')) return; + const currentPubkey = getCurrentContactPubkey(); + if (!currentPubkey) return; + const changedKey = (data.public_key || '').toLowerCase(); + if (changedKey && changedKey.startsWith(currentPubkey.toLowerCase())) { + await refreshContactInfoPath(); + } + }); } // Initialize on page load @@ -975,6 +987,34 @@ function populateContactInfoModal() { } } +/** + * Refresh contact data from device and re-render Contact Info modal if open. + * Uses ?refresh=true to bypass server-side cache. + */ +async function refreshContactInfoPath() { + try { + const response = await fetch('/api/contacts/detailed?refresh=true'); + const data = await response.json(); + if (data.success) { + contactsList = (data.contacts || []).sort((a, b) => + (a.name || '').localeCompare(b.name || '')); + contactsMap = {}; + contactsList.forEach(c => { + if (c.public_key) contactsMap[c.public_key] = c; + }); + } + } catch (e) { + console.error('[DM] refreshContactInfoPath fetch error:', e); + return; + } + // Re-populate modal if still open + const modalEl = document.getElementById('dmContactInfoModal'); + if (modalEl && modalEl.classList.contains('show')) { + populateContactInfoModal(); + loadPathSection(); + } +} + /** * Load messages for current conversation */ @@ -2114,6 +2154,7 @@ function setupPathFormHandlers(pubkey) { const data = await response.json(); if (data.success) { showNotification('Device path reset to FLOOD', 'info'); + await refreshContactInfoPath(); } else { showNotification(data.error || 'Reset failed', 'danger'); }