diff --git a/app/device_manager.py b/app/device_manager.py index 9d8f767..d2f9649 100644 --- a/app/device_manager.py +++ b/app/device_manager.py @@ -207,13 +207,18 @@ class DeviceManager: count = 0 for pubkey, contact in self.mc.contacts.items(): + # last_advert from meshcore is Unix timestamp (int) or None + last_adv = contact.get('last_advert') + last_advert_val = str(int(last_adv)) if last_adv and isinstance(last_adv, (int, float)) and last_adv > 0 else None + self.db.upsert_contact( public_key=pubkey, name=contact.get('adv_name', ''), - type=contact.get('adv_type', 0), + type=contact.get('type', 0), flags=contact.get('flags', 0), out_path=contact.get('out_path', ''), out_path_len=contact.get('out_path_len', 0), + last_advert=last_advert_val, adv_lat=contact.get('adv_lat'), adv_lon=contact.get('adv_lon'), source='device', @@ -420,7 +425,7 @@ class DeviceManager: type=data.get('adv_type', 0), adv_lat=data.get('adv_lat'), adv_lon=data.get('adv_lon'), - last_advert=time.strftime('%Y-%m-%dT%H:%M:%S'), + last_advert=str(int(time.time())), source='advert', ) diff --git a/app/meshcore/cli.py b/app/meshcore/cli.py index c377bbe..6bde8c7 100644 --- a/app/meshcore/cli.py +++ b/app/meshcore/cli.py @@ -125,6 +125,27 @@ def get_all_contacts_detailed() -> Tuple[bool, List[Dict], int, str]: return False, [], 0, str(e) +def _parse_last_advert(value) -> int: + """Convert last_advert from DB (Unix timestamp string or ISO string) to Unix int.""" + if not value: + return 0 + # Try as Unix timestamp first + try: + ts = int(value) + if ts > 0: + return ts + except (ValueError, TypeError): + pass + # Try as ISO datetime string + try: + from datetime import datetime + dt = datetime.fromisoformat(str(value)) + return int(dt.timestamp()) + except (ValueError, TypeError): + pass + return 0 + + def get_contacts_with_last_seen() -> Tuple[bool, Dict[str, Dict], str]: """Get contacts with last_advert timestamps from DB.""" try: @@ -140,7 +161,7 @@ def get_contacts_with_last_seen() -> Tuple[bool, Dict[str, Dict], str]: 'out_path_len': c.get('out_path_len', -1), 'out_path': c.get('out_path', ''), 'adv_name': c.get('name', ''), - 'last_advert': c.get('last_advert', ''), + 'last_advert': _parse_last_advert(c.get('last_advert')), 'adv_lat': c.get('adv_lat', 0.0), 'adv_lon': c.get('adv_lon', 0.0), 'lastmod': c.get('lastmod', ''), @@ -165,7 +186,7 @@ def get_contacts_json() -> Tuple[bool, Dict[str, Dict], str]: 'flags': c.get('flags', 0), 'out_path_len': c.get('out_path_len', -1), 'out_path': c.get('out_path', ''), - 'last_advert': c.get('last_advert', ''), + 'last_advert': _parse_last_advert(c.get('last_advert')), 'adv_lat': c.get('adv_lat'), 'adv_lon': c.get('adv_lon'), 'lastmod': c.get('lastmod', ''), diff --git a/app/static/js/dm.js b/app/static/js/dm.js index 6ddf14e..e500049 100644 --- a/app/static/js/dm.js +++ b/app/static/js/dm.js @@ -223,7 +223,12 @@ function populateConversationSelector() { const lastSeen = dmLastSeenTimestamps[conv.conversation_id] || 0; const isUnread = conv.last_message_timestamp > lastSeen; - let label = conv.display_name; + // If display_name is a full pubkey, show short prefix + let displayName = conv.display_name; + if (/^[0-9a-f]{64}$/i.test(displayName)) { + displayName = displayName.substring(0, 8) + '...'; + } + let label = displayName; if (isUnread) { label = `* ${label}`; } @@ -286,8 +291,13 @@ async function selectConversation(conversationId) { // Find the conversation to get recipient name const conv = dmConversations.find(c => c.conversation_id === conversationId); - if (conv) { - currentRecipient = conv.display_name; + if (conv && conv.display_name) { + // If display_name is a full pubkey (64 hex chars), show short prefix + if (/^[0-9a-f]{64}$/i.test(conv.display_name)) { + currentRecipient = conv.display_name.substring(0, 8) + '...'; + } else { + currentRecipient = conv.display_name; + } } else { // Extract name from conversation_id if (conversationId.startsWith('name_')) {