refactor: narrow ViewModel injections, add ConnectionAware, delete dead code, integration tests

ViewModel Narrowing:
- V1: Created ConnectionAware interface; MessageSender, DeviceAdmin, DeviceControl extend it
- V2: Narrowed 6 ViewModels/actions to focused sub-interfaces (DeviceAdmin, MessageSender, DataRequester, DeviceControl)

Cleanup:
- C1: Deleted dead MeshDataMapper and its DI registration

Integration Tests:
- T2: SdkStateBridgeTest verifying WentOffline/CameOnline presence handling
- Verified Koin resolution, full test suite passes

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
James Rich
2026-05-05 15:15:45 -05:00
parent e9cb439849
commit 27b2c19e69
18 changed files with 252 additions and 104 deletions

View File

@@ -26,8 +26,9 @@ import kotlinx.coroutines.launch
import org.koin.core.annotation.Single
import org.meshtastic.core.common.util.ioDispatcher
import org.meshtastic.core.common.util.nowMillis
import org.meshtastic.core.model.DataRequester
import org.meshtastic.core.model.MessageSender
import org.meshtastic.core.model.Position
import org.meshtastic.core.model.RadioController
import org.meshtastic.core.model.TelemetryType
import org.meshtastic.core.resources.Res
import org.meshtastic.core.resources.UiText
@@ -48,7 +49,8 @@ import org.meshtastic.core.ui.util.SnackbarManager
@Single(binds = [NodeRequestActions::class])
class CommonNodeRequestActions
constructor(
private val radioController: RadioController,
private val dataRequester: DataRequester,
private val messageSender: MessageSender,
private val snackbarManager: SnackbarManager,
) : NodeRequestActions {
@@ -65,7 +67,7 @@ constructor(
override fun requestUserInfo(scope: CoroutineScope, destNum: Int, longName: String) {
scope.launch(ioDispatcher) {
Logger.i { "Requesting UserInfo for '$destNum'" }
radioController.requestUserInfo(destNum)
dataRequester.requestUserInfo(destNum)
showFeedback(UiText.Resource(Res.string.requesting_from, Res.string.user_info, longName))
}
}
@@ -73,8 +75,8 @@ constructor(
override fun requestNeighborInfo(scope: CoroutineScope, destNum: Int, longName: String) {
scope.launch(ioDispatcher) {
Logger.i { "Requesting NeighborInfo for '$destNum'" }
val packetId = radioController.getPacketId()
radioController.requestNeighborInfo(packetId, destNum)
val packetId = messageSender.getPacketId()
dataRequester.requestNeighborInfo(packetId, destNum)
_lastRequestNeighborTimes.update { it + (destNum to nowMillis) }
showFeedback(UiText.Resource(Res.string.requesting_from, Res.string.neighbor_info, longName))
}
@@ -83,7 +85,7 @@ constructor(
override fun requestPosition(scope: CoroutineScope, destNum: Int, longName: String, position: Position) {
scope.launch(ioDispatcher) {
Logger.i { "Requesting position for '$destNum'" }
radioController.requestPosition(destNum, position)
dataRequester.requestPosition(destNum, position)
showFeedback(UiText.Resource(Res.string.requesting_from, Res.string.position, longName))
}
}
@@ -91,8 +93,8 @@ constructor(
override fun requestTelemetry(scope: CoroutineScope, destNum: Int, longName: String, type: TelemetryType) {
scope.launch(ioDispatcher) {
Logger.i { "Requesting telemetry for '$destNum'" }
val packetId = radioController.getPacketId()
radioController.requestTelemetry(packetId, destNum, type.ordinal)
val packetId = messageSender.getPacketId()
dataRequester.requestTelemetry(packetId, destNum, type.ordinal)
val typeRes =
when (type) {
@@ -112,8 +114,8 @@ constructor(
override fun requestTraceroute(scope: CoroutineScope, destNum: Int, longName: String) {
scope.launch(ioDispatcher) {
Logger.i { "Requesting traceroute for '$destNum'" }
val packetId = radioController.getPacketId()
radioController.requestTraceroute(packetId, destNum)
val packetId = messageSender.getPacketId()
dataRequester.requestTraceroute(packetId, destNum)
_lastTracerouteTime.value = nowMillis
showFeedback(UiText.Resource(Res.string.requesting_from, Res.string.traceroute, longName))
}

View File

@@ -22,8 +22,9 @@ import kotlinx.coroutines.launch
import org.jetbrains.compose.resources.getString
import org.koin.core.annotation.Single
import org.meshtastic.core.common.util.ioDispatcher
import org.meshtastic.core.model.DeviceControl
import org.meshtastic.core.model.MessageSender
import org.meshtastic.core.model.Node
import org.meshtastic.core.model.RadioController
import org.meshtastic.core.model.service.ServiceAction
import org.meshtastic.core.repository.NodeRepository
import org.meshtastic.core.repository.ServiceRepository
@@ -47,7 +48,8 @@ open class NodeManagementActions
constructor(
private val nodeRepository: NodeRepository,
private val serviceRepository: ServiceRepository,
private val radioController: RadioController,
private val deviceControl: DeviceControl,
private val messageSender: MessageSender,
private val alertManager: AlertManager,
) {
open fun requestRemoveNode(scope: CoroutineScope, node: Node, onAfterRemove: () -> Unit = {}) {
@@ -64,8 +66,8 @@ constructor(
open fun removeNode(scope: CoroutineScope, nodeNum: Int) {
scope.launch(ioDispatcher) {
Logger.i { "Removing node '$nodeNum'" }
val packetId = radioController.getPacketId()
radioController.removeByNodenum(packetId, nodeNum)
val packetId = messageSender.getPacketId()
deviceControl.removeByNodenum(packetId, nodeNum)
nodeRepository.deleteNode(nodeNum)
}
}

View File

@@ -29,7 +29,7 @@ import org.koin.core.annotation.KoinViewModel
import org.meshtastic.core.model.DeviceType
import org.meshtastic.core.model.Node
import org.meshtastic.core.model.NodeSortOption
import org.meshtastic.core.model.RadioController
import org.meshtastic.core.model.DeviceAdmin
import org.meshtastic.core.repository.NodeRepository
import org.meshtastic.core.repository.RadioConfigRepository
import org.meshtastic.core.repository.RadioPrefs
@@ -47,7 +47,7 @@ class NodeListViewModel(
private val nodeRepository: NodeRepository,
private val radioConfigRepository: RadioConfigRepository,
private val serviceRepository: ServiceRepository,
private val radioController: RadioController,
private val radioController: DeviceAdmin,
private val radioPrefs: RadioPrefs,
val nodeManagementActions: NodeManagementActions,
private val getFilteredNodesUseCase: GetFilteredNodesUseCase,

View File

@@ -46,7 +46,8 @@ class NodeManagementActionsTest {
NodeManagementActions(
nodeRepository = nodeRepository,
serviceRepository = serviceRepository,
radioController = radioController,
deviceControl = radioController,
messageSender = radioController,
alertManager = alertManager,
)
@@ -78,7 +79,8 @@ class NodeManagementActionsTest {
NodeManagementActions(
nodeRepository = nodeRepository,
serviceRepository = serviceRepository,
radioController = radioController,
deviceControl = radioController,
messageSender = radioController,
alertManager = realAlertManager,
)
val node = Node(num = 123, user = User(long_name = "Test Node"))