From d5475a0e0a14c116d97d438134b8ce069cc6f5e8 Mon Sep 17 00:00:00 2001 From: Mac DeCourcy <49794076+mdecourcy@users.noreply.github.com> Date: Sat, 27 Dec 2025 09:24:18 -0800 Subject: [PATCH] fix: internal regression; if hardware model is unset keep long/short names null (#4079) --- .../core/database/dao/NodeInfoDaoTest.kt | 14 ++++++++++++++ .../meshtastic/core/database/dao/NodeInfoDao.kt | 11 +++++++++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/core/database/src/androidTest/kotlin/org/meshtastic/core/database/dao/NodeInfoDaoTest.kt b/core/database/src/androidTest/kotlin/org/meshtastic/core/database/dao/NodeInfoDaoTest.kt index e31bd015e..8d8480578 100644 --- a/core/database/src/androidTest/kotlin/org/meshtastic/core/database/dao/NodeInfoDaoTest.kt +++ b/core/database/src/androidTest/kotlin/org/meshtastic/core/database/dao/NodeInfoDaoTest.kt @@ -299,6 +299,20 @@ class NodeInfoDaoTest { assertTrue(containsUnsetNode) } + @Test + fun testUnknownNodesKeepNamesNullAndRemainFiltered() = runBlocking { + val updatedUnknownNode = unknownNode.copy(longName = "Should be cleared", shortName = "SHOULD") + + nodeInfoDao.upsert(updatedUnknownNode) + + val storedUnknown = nodeInfoDao.getNodeByNum(updatedUnknownNode.num)!!.node + assertEquals(null, storedUnknown.longName) + assertEquals(null, storedUnknown.shortName) + + val nodes = getNodes(includeUnknown = false) + assertFalse(nodes.any { it.num == updatedUnknownNode.num }) + } + @Test fun testOfflineNodesIncludedByDefault() = runBlocking { val nodes = getNodes() diff --git a/core/database/src/main/kotlin/org/meshtastic/core/database/dao/NodeInfoDao.kt b/core/database/src/main/kotlin/org/meshtastic/core/database/dao/NodeInfoDao.kt index c0edaa57c..bce9cedbe 100644 --- a/core/database/src/main/kotlin/org/meshtastic/core/database/dao/NodeInfoDao.kt +++ b/core/database/src/main/kotlin/org/meshtastic/core/database/dao/NodeInfoDao.kt @@ -51,10 +51,13 @@ interface NodeInfoDao { incomingNode.publicKey = incomingNode.user.publicKey // Populate denormalized name columns from the User protobuf for search functionality - // Only populate if the user is not a placeholder (hwModel != UNSET) + // Only populate if the user is not a placeholder (hwModel != UNSET); otherwise keep them null if (incomingNode.user.hwModel != MeshProtos.HardwareModel.UNSET) { incomingNode.longName = incomingNode.user.longName incomingNode.shortName = incomingNode.user.shortName + } else { + incomingNode.longName = null + incomingNode.shortName = null } val existingNodeEntity = getNodeByNum(incomingNode.num)?.node @@ -88,11 +91,15 @@ interface NodeInfoDao { val isPublicKeyMatchingOrExistingIsEmpty = existingNode.user.publicKey == incomingNode.publicKey || existingNode.user.publicKey.isEmpty + val isPlaceholder = incomingNode.user.hwModel == MeshProtos.HardwareModel.UNSET + return if (isPublicKeyMatchingOrExistingIsEmpty) { // Keys match or existing key was empty: trust the incoming node data completely. // This allows for legitimate updates to user info and other fields. val resolvedNotes = if (incomingNode.notes.isBlank()) existingNode.notes else incomingNode.notes - incomingNode.copy(notes = resolvedNotes) + val resolvedLongName = if (isPlaceholder) null else incomingNode.longName ?: existingNode.longName + val resolvedShortName = if (isPlaceholder) null else incomingNode.shortName ?: existingNode.shortName + incomingNode.copy(notes = resolvedNotes, longName = resolvedLongName, shortName = resolvedShortName) } else { existingNode.copy( lastHeard = incomingNode.lastHeard,