Move more properties into UI state

This commit is contained in:
Phil Oliver 2025-11-18 02:03:42 -05:00
parent 8152a99f47
commit 6fda0da91a
2 changed files with 34 additions and 39 deletions

View file

@ -96,10 +96,6 @@ fun NodeListScreen(
) {
val state by viewModel.uiState.collectAsStateWithLifecycle()
val nodes by viewModel.nodeList.collectAsStateWithLifecycle()
val ourNode by viewModel.ourNodeInfo.collectAsStateWithLifecycle()
val onlineNodeCount by viewModel.onlineNodeCount.collectAsStateWithLifecycle(0)
val totalNodeCount by viewModel.totalNodeCount.collectAsStateWithLifecycle(0)
val unfilteredNodes by viewModel.unfilteredNodeList.collectAsStateWithLifecycle()
val ignoredNodeCount = unfilteredNodes.count { it.isIgnored }
@ -115,7 +111,6 @@ fun NodeListScreen(
}
val currentTimeMillis = rememberTimeTickWithLifecycle()
val connectionState by viewModel.connectionState.collectAsStateWithLifecycle()
val isScrollInProgress by remember {
derivedStateOf { listState.isScrollInProgress && (listState.canScrollForward || listState.canScrollBackward) }
@ -124,8 +119,8 @@ fun NodeListScreen(
topBar = {
MainAppBar(
title = stringResource(Res.string.nodes),
subtitle = stringResource(Res.string.node_count_template, onlineNodeCount, nodes.size, totalNodeCount),
ourNode = ourNode,
subtitle = stringResource(Res.string.node_count_template, state.onlineNodeCount, state.totalNodeCount),
ourNode = state.ourNodeInfo,
showNodeChip = false,
canNavigateUp = false,
onNavigateUp = {},
@ -134,7 +129,7 @@ fun NodeListScreen(
)
},
floatingActionButton = {
val firmwareVersion = DeviceVersion(ourNode?.metadata?.firmwareVersion ?: "0.0.0")
val firmwareVersion = DeviceVersion(state.ourNodeInfo?.metadata?.firmwareVersion ?: "0.0.0")
val shareCapable = firmwareVersion.supportsQrCodeSharing()
val sharedContact: AdminProtos.SharedContact? by
viewModel.sharedContactRequested.collectAsStateWithLifecycle(null)
@ -142,7 +137,8 @@ fun NodeListScreen(
sharedContact = sharedContact,
modifier =
Modifier.animateFloatingActionButton(
visible = !isScrollInProgress && connectionState == ConnectionState.Connected && shareCapable,
visible =
!isScrollInProgress && state.connectionState == ConnectionState.Connected && shareCapable,
alignment = Alignment.BottomEnd,
),
onSharedContactRequested = { contact -> viewModel.setSharedContactRequested(contact) },
@ -180,7 +176,7 @@ fun NodeListScreen(
)
}
items(nodes, key = { it.num }) { node ->
items(state.nodes, key = { it.num }) { node ->
var displayFavoriteDialog by remember { mutableStateOf(false) }
var displayIgnoreDialog by remember { mutableStateOf(false) }
var displayRemoveDialog by remember { mutableStateOf(false) }
@ -204,7 +200,7 @@ fun NodeListScreen(
Box(modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp)) {
val longClick =
if (node.num != ourNode?.num) {
if (node.num != state.ourNodeInfo?.num) {
{ expanded = true }
} else {
null
@ -214,17 +210,17 @@ fun NodeListScreen(
NodeItem(
modifier = Modifier.animateItem(),
thisNode = ourNode,
thisNode = state.ourNodeInfo,
thatNode = node,
distanceUnits = state.distanceUnits,
tempInFahrenheit = state.tempInFahrenheit,
onClick = { navigateToNodeDetails(node.num) },
onLongClick = longClick,
currentTimeMillis = currentTimeMillis,
connectionState = connectionState,
connectionState = state.connectionState,
isActive = isActive,
)
val isThisNode = remember(node) { ourNode?.num == node.num }
val isThisNode = remember(node) { state.ourNodeInfo?.num == node.num }
if (!isThisNode) {
ContextMenu(
expanded = expanded,

View file

@ -37,6 +37,8 @@ import org.meshtastic.core.data.repository.NodeRepository
import org.meshtastic.core.data.repository.RadioConfigRepository
import org.meshtastic.core.database.model.Node
import org.meshtastic.core.database.model.NodeSortOption
import org.meshtastic.core.datastore.UiPreferencesDataSource
import org.meshtastic.core.service.ConnectionState
import org.meshtastic.core.service.ServiceRepository
import org.meshtastic.core.ui.viewmodel.stateInWhileSubscribed
import org.meshtastic.feature.node.model.isEffectivelyUnmessageable
@ -57,14 +59,6 @@ constructor(
val nodeFilterPreferences: NodeFilterPreferences,
) : ViewModel() {
val ourNodeInfo: StateFlow<Node?> = nodeRepository.ourNodeInfo
val onlineNodeCount = nodeRepository.onlineNodeCount.stateInWhileSubscribed(initialValue = 0)
val totalNodeCount = nodeRepository.totalNodeCount.stateInWhileSubscribed(initialValue = 0)
val connectionState = serviceRepository.connectionState
private val _sharedContactRequested: MutableStateFlow<AdminProtos.SharedContact?> = MutableStateFlow(null)
val sharedContactRequested = _sharedContactRequested.asStateFlow()
@ -74,6 +68,10 @@ constructor(
val uiState: StateFlow<NodesUiState> by
lazy(LazyThreadSafetyMode.NONE) {
moleculeScope.launchMolecule(mode = RecompositionMode.ContextClock) {
val ourNodeInfo by nodeRepository.ourNodeInfo.collectAsState()
val onlineNodeCount by nodeRepository.onlineNodeCount.collectAsState(0)
val totalNodeCount by nodeRepository.totalNodeCount.collectAsState(0)
val connectionState by serviceRepository.connectionState.collectAsState()
val filterText by filterText
val includeUnknown by nodeFilterPreferences.includeUnknown.collectAsState()
val excludeInfrastructure by nodeFilterPreferences.excludeInfrastructure.collectAsState()
@ -81,7 +79,7 @@ constructor(
val onlyDirect by nodeFilterPreferences.onlyDirect.collectAsState()
val showIgnored by nodeFilterPreferences.showIgnored.collectAsState()
val filterState =
val filter =
NodeFilterState(
filterText = filterText,
includeUnknown = includeUnknown,
@ -94,21 +92,6 @@ constructor(
nodeFilterPreferences.nodeSortOption
.collectAsState(NodeSortOption.VIA_FAVORITE)
val profile by radioConfigRepository.deviceProfileFlow.collectAsState(deviceProfile {})
NodesUiState(
sort = sort,
filter = filterState,
distanceUnits = profile.config.display.units.number,
tempInFahrenheit = profile.moduleConfig.telemetry.environmentDisplayFahrenheit,
)
}
}
val nodeList: StateFlow<List<Node>> by
lazy(LazyThreadSafetyMode.NONE) {
moleculeScope.launchMolecule(mode = RecompositionMode.ContextClock) {
val uiState by uiState.collectAsState()
val sort = uiState.sort
val filter = uiState.filter
val nodeList by
nodeRepository
@ -139,7 +122,18 @@ constructor(
}
}
.collectAsState(emptyList())
nodeList
NodesUiState(
ourNodeInfo = ourNodeInfo,
onlineNodeCount = onlineNodeCount,
totalNodeCount = totalNodeCount,
connectionState = connectionState,
sort = sort,
filter = filter,
nodes = nodeList,
distanceUnits = profile.config.display.units.number,
tempInFahrenheit = profile.moduleConfig.telemetry.environmentDisplayFahrenheit,
)
}
}
@ -170,8 +164,13 @@ constructor(
}
data class NodesUiState(
val ourNodeInfo: Node? = null,
val onlineNodeCount: Int = 0,
val totalNodeCount: Int = 0,
val connectionState: ConnectionState = ConnectionState.Connected,
val sort: NodeSortOption = NodeSortOption.LAST_HEARD,
val filter: NodeFilterState = NodeFilterState(),
val nodes: List<Node> = emptyList(),
val distanceUnits: Int = 0,
val tempInFahrenheit: Boolean = false,
)