mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-03-27 18:21:58 -04:00
Add "Exclude MQTT" filter to Nodes view. (#4825)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: James Rich <2199651+jamesarich@users.noreply.github.com> Co-authored-by: James Rich <james.a.rich@gmail.com>
This commit is contained in:
@@ -44,6 +44,7 @@ const val KEY_EXCLUDE_INFRASTRUCTURE = "exclude-infrastructure"
|
||||
const val KEY_ONLY_ONLINE = "only-online"
|
||||
const val KEY_ONLY_DIRECT = "only-direct"
|
||||
const val KEY_SHOW_IGNORED = "show-ignored"
|
||||
const val KEY_EXCLUDE_MQTT = "exclude-mqtt"
|
||||
|
||||
@Single
|
||||
@Suppress("TooManyFunctions") // One setter per preference field — inherently grows with preferences.
|
||||
@@ -73,6 +74,7 @@ class UiPreferencesDataSource(@Named("CorePreferencesDataStore") private val dat
|
||||
val onlyOnline: StateFlow<Boolean> = dataStore.prefStateFlow(key = ONLY_ONLINE, default = false)
|
||||
val onlyDirect: StateFlow<Boolean> = dataStore.prefStateFlow(key = ONLY_DIRECT, default = false)
|
||||
val showIgnored: StateFlow<Boolean> = dataStore.prefStateFlow(key = SHOW_IGNORED, default = false)
|
||||
val excludeMqtt: StateFlow<Boolean> = dataStore.prefStateFlow(key = EXCLUDE_MQTT, default = false)
|
||||
|
||||
fun setAppIntroCompleted(completed: Boolean) {
|
||||
dataStore.setPref(key = APP_INTRO_COMPLETED, value = completed)
|
||||
@@ -106,6 +108,10 @@ class UiPreferencesDataSource(@Named("CorePreferencesDataStore") private val dat
|
||||
dataStore.setPref(key = SHOW_IGNORED, value = value)
|
||||
}
|
||||
|
||||
fun setExcludeMqtt(value: Boolean) {
|
||||
dataStore.setPref(key = EXCLUDE_MQTT, value = value)
|
||||
}
|
||||
|
||||
private fun <T : Any> DataStore<Preferences>.prefStateFlow(
|
||||
key: Preferences.Key<T>,
|
||||
default: T,
|
||||
@@ -126,5 +132,6 @@ class UiPreferencesDataSource(@Named("CorePreferencesDataStore") private val dat
|
||||
val ONLY_ONLINE = booleanPreferencesKey(KEY_ONLY_ONLINE)
|
||||
val ONLY_DIRECT = booleanPreferencesKey(KEY_ONLY_DIRECT)
|
||||
val SHOW_IGNORED = booleanPreferencesKey(KEY_SHOW_IGNORED)
|
||||
val EXCLUDE_MQTT = booleanPreferencesKey(KEY_EXCLUDE_MQTT)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
<string name="internal">Internal</string>
|
||||
<string name="node_sort_via_favorite">via Favorite</string>
|
||||
<string name="node_filter_show_ignored">Only show ignored Nodes</string>
|
||||
<string name="node_filter_exclude_mqtt">Exclude MQTT</string>
|
||||
<string name="unrecognized">Unrecognized</string>
|
||||
<string name="message_status_enroute">Waiting to be acknowledged</string>
|
||||
<string name="message_status_queued">Queued for sending</string>
|
||||
|
||||
@@ -150,6 +150,8 @@ fun DesktopAdaptiveNodeListScreen(
|
||||
showIgnored = state.filter.showIgnored,
|
||||
onToggleShowIgnored = { viewModel.nodeFilterPreferences.toggleShowIgnored() },
|
||||
ignoredNodeCount = ignoredNodeCount,
|
||||
excludeMqtt = state.filter.excludeMqtt,
|
||||
onToggleExcludeMqtt = { viewModel.nodeFilterPreferences.toggleExcludeMqtt() },
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -163,6 +163,8 @@ fun NodeListScreen(
|
||||
showIgnored = state.filter.showIgnored,
|
||||
onToggleShowIgnored = { viewModel.nodeFilterPreferences.toggleShowIgnored() },
|
||||
ignoredNodeCount = ignoredNodeCount,
|
||||
excludeMqtt = state.filter.excludeMqtt,
|
||||
onToggleExcludeMqtt = { viewModel.nodeFilterPreferences.toggleExcludeMqtt() },
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -62,6 +62,7 @@ import org.meshtastic.core.model.NodeSortOption
|
||||
import org.meshtastic.core.resources.Res
|
||||
import org.meshtastic.core.resources.desc_node_filter_clear
|
||||
import org.meshtastic.core.resources.node_filter_exclude_infrastructure
|
||||
import org.meshtastic.core.resources.node_filter_exclude_mqtt
|
||||
import org.meshtastic.core.resources.node_filter_ignored
|
||||
import org.meshtastic.core.resources.node_filter_include_unknown
|
||||
import org.meshtastic.core.resources.node_filter_only_direct
|
||||
@@ -91,6 +92,8 @@ fun NodeFilterTextField(
|
||||
showIgnored: Boolean,
|
||||
onToggleShowIgnored: () -> Unit,
|
||||
ignoredNodeCount: Int,
|
||||
excludeMqtt: Boolean,
|
||||
onToggleExcludeMqtt: () -> Unit,
|
||||
) {
|
||||
Column(modifier = modifier.background(MaterialTheme.colorScheme.background)) {
|
||||
Row {
|
||||
@@ -113,6 +116,8 @@ fun NodeFilterTextField(
|
||||
showIgnored = showIgnored,
|
||||
onToggleShowIgnored = onToggleShowIgnored,
|
||||
ignoredNodeCount = ignoredNodeCount,
|
||||
excludeMqtt = excludeMqtt,
|
||||
onToggleExcludeMqtt = onToggleExcludeMqtt,
|
||||
),
|
||||
)
|
||||
}
|
||||
@@ -148,6 +153,8 @@ data class NodeFilterToggles(
|
||||
val showIgnored: Boolean,
|
||||
val onToggleShowIgnored: () -> Unit,
|
||||
val ignoredNodeCount: Int,
|
||||
val excludeMqtt: Boolean,
|
||||
val onToggleExcludeMqtt: () -> Unit,
|
||||
)
|
||||
|
||||
@Composable
|
||||
@@ -268,6 +275,12 @@ private fun NodeSortButton(
|
||||
null
|
||||
},
|
||||
)
|
||||
|
||||
DropdownMenuCheck(
|
||||
text = stringResource(Res.string.node_filter_exclude_mqtt),
|
||||
checked = toggles.excludeMqtt,
|
||||
onClick = toggles.onToggleExcludeMqtt,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -57,5 +57,6 @@ class GetFilteredNodesUseCase constructor(private val nodeRepository: NodeReposi
|
||||
true
|
||||
}
|
||||
}
|
||||
.filter { node -> if (filter.excludeMqtt) !node.viaMqtt else true }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ class NodeFilterPreferences constructor(private val uiPreferencesDataSource: UiP
|
||||
val onlyOnline = uiPreferencesDataSource.onlyOnline
|
||||
val onlyDirect = uiPreferencesDataSource.onlyDirect
|
||||
val showIgnored = uiPreferencesDataSource.showIgnored
|
||||
val excludeMqtt = uiPreferencesDataSource.excludeMqtt
|
||||
|
||||
val nodeSortOption =
|
||||
uiPreferencesDataSource.nodeSort.map { NodeSortOption.entries.getOrElse(it) { NodeSortOption.VIA_FAVORITE } }
|
||||
@@ -55,4 +56,8 @@ class NodeFilterPreferences constructor(private val uiPreferencesDataSource: UiP
|
||||
fun toggleShowIgnored() {
|
||||
uiPreferencesDataSource.setShowIgnored(!showIgnored.value)
|
||||
}
|
||||
|
||||
fun toggleExcludeMqtt() {
|
||||
uiPreferencesDataSource.setExcludeMqtt(!excludeMqtt.value)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,7 +91,11 @@ class NodeListViewModel(
|
||||
}
|
||||
|
||||
private val nodeFilter: Flow<NodeFilterState> =
|
||||
combine(_nodeFilterText, filterToggles) { filterText, filterToggles ->
|
||||
combine(_nodeFilterText, filterToggles, nodeFilterPreferences.excludeMqtt) {
|
||||
filterText,
|
||||
filterToggles,
|
||||
excludeMqtt,
|
||||
->
|
||||
NodeFilterState(
|
||||
filterText = filterText,
|
||||
includeUnknown = filterToggles.includeUnknown,
|
||||
@@ -99,6 +103,7 @@ class NodeListViewModel(
|
||||
onlyOnline = filterToggles.onlyOnline,
|
||||
onlyDirect = filterToggles.onlyDirect,
|
||||
showIgnored = filterToggles.showIgnored,
|
||||
excludeMqtt = excludeMqtt,
|
||||
)
|
||||
}
|
||||
val nodesUiState: StateFlow<NodesUiState> =
|
||||
@@ -183,6 +188,7 @@ data class NodeFilterState(
|
||||
val onlyOnline: Boolean = false,
|
||||
val onlyDirect: Boolean = false,
|
||||
val showIgnored: Boolean = false,
|
||||
val excludeMqtt: Boolean = false,
|
||||
)
|
||||
|
||||
data class NodeFilterToggles(
|
||||
|
||||
@@ -47,9 +47,10 @@ class GetFilteredNodesUseCaseTest {
|
||||
role: Config.DeviceConfig.Role = Config.DeviceConfig.Role.CLIENT,
|
||||
ignored: Boolean = false,
|
||||
name: String = "Node$num",
|
||||
viaMqtt: Boolean = false,
|
||||
): Node {
|
||||
val user = User(id = "!$num", long_name = name, short_name = "N$num", role = role)
|
||||
return Node(num = num, user = user, isIgnored = ignored)
|
||||
return Node(num = num, user = user, isIgnored = ignored, viaMqtt = viaMqtt)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -116,4 +117,37 @@ class GetFilteredNodesUseCaseTest {
|
||||
assertEquals(1, result.size)
|
||||
assertEquals(1, result.first().num)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `invoke filters out MQTT nodes if excludeMqtt is true`() = runTest {
|
||||
// Arrange
|
||||
val loraNode = createNode(1, viaMqtt = false)
|
||||
val mqttNode = createNode(2, viaMqtt = true)
|
||||
val filter = NodeFilterState(excludeMqtt = true)
|
||||
|
||||
every { nodeRepository.getNodes(any(), any(), any(), any(), any()) } returns flowOf(listOf(loraNode, mqttNode))
|
||||
|
||||
// Act
|
||||
val result = useCase(filter, NodeSortOption.LAST_HEARD).first()
|
||||
|
||||
// Assert
|
||||
assertEquals(1, result.size)
|
||||
assertEquals(1, result.first().num)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `invoke keeps MQTT nodes if excludeMqtt is false`() = runTest {
|
||||
// Arrange
|
||||
val loraNode = createNode(1, viaMqtt = false)
|
||||
val mqttNode = createNode(2, viaMqtt = true)
|
||||
val filter = NodeFilterState(excludeMqtt = false)
|
||||
|
||||
every { nodeRepository.getNodes(any(), any(), any(), any(), any()) } returns flowOf(listOf(loraNode, mqttNode))
|
||||
|
||||
// Act
|
||||
val result = useCase(filter, NodeSortOption.LAST_HEARD).first()
|
||||
|
||||
// Assert
|
||||
assertEquals(2, result.size)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user