diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/list/NodeListScreen.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/list/NodeListScreen.kt index 590e6ea43..42dc686df 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/list/NodeListScreen.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/list/NodeListScreen.kt @@ -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, diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/list/NodeListViewModel.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/list/NodeListViewModel.kt index 0a818aac0..6e29fcbea 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/list/NodeListViewModel.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/list/NodeListViewModel.kt @@ -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 = nodeRepository.ourNodeInfo - - val onlineNodeCount = nodeRepository.onlineNodeCount.stateInWhileSubscribed(initialValue = 0) - - val totalNodeCount = nodeRepository.totalNodeCount.stateInWhileSubscribed(initialValue = 0) - - val connectionState = serviceRepository.connectionState - private val _sharedContactRequested: MutableStateFlow = MutableStateFlow(null) val sharedContactRequested = _sharedContactRequested.asStateFlow() @@ -74,6 +68,10 @@ constructor( val uiState: StateFlow 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> 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 = emptyList(), val distanceUnits: Int = 0, val tempInFahrenheit: Boolean = false, )