fix(v2): Fix last_advert timestamps and DM placeholder display

- Sync last_advert from device contacts as Unix timestamp (was missing)
- Convert _on_advertisement to store Unix timestamp (was ISO string)
- Add _parse_last_advert() to handle both ISO and Unix formats in API
- Truncate full pubkey to short prefix in DM placeholder and dropdown

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
MarekWo
2026-03-01 16:23:45 +01:00
parent c20e7c20ad
commit 3e81eeeae7
3 changed files with 43 additions and 7 deletions
+7 -2
View File
@@ -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',
)
+23 -2
View File
@@ -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', ''),
+13 -3
View File
@@ -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_')) {