mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
Remove node card expansion (#3243)
This commit is contained in:
parent
9ed0f5e0a7
commit
a878373d14
5 changed files with 22 additions and 138 deletions
|
|
@ -91,7 +91,9 @@ fun NodeScreen(nodesViewModel: NodesViewModel = hiltViewModel(), navigateToNodeD
|
|||
val currentTimeMillis = rememberTimeTickWithLifecycle()
|
||||
val connectionState by nodesViewModel.connectionState.collectAsStateWithLifecycle()
|
||||
|
||||
val isScrollInProgress by remember { derivedStateOf { listState.isScrollInProgress } }
|
||||
val isScrollInProgress by remember {
|
||||
derivedStateOf { listState.isScrollInProgress && (listState.canScrollForward || listState.canScrollBackward) }
|
||||
}
|
||||
Scaffold(
|
||||
topBar = {
|
||||
MainAppBar(
|
||||
|
|
@ -144,8 +146,6 @@ fun NodeScreen(nodesViewModel: NodesViewModel = hiltViewModel(), navigateToNodeD
|
|||
onToggleOnlyOnline = nodesViewModel::toggleOnlyOnline,
|
||||
onlyDirect = state.filter.onlyDirect,
|
||||
onToggleOnlyDirect = nodesViewModel::toggleOnlyDirect,
|
||||
showDetails = state.showDetails,
|
||||
onToggleShowDetails = nodesViewModel::toggleShowDetails,
|
||||
showIgnored = state.filter.showIgnored,
|
||||
onToggleShowIgnored = nodesViewModel::toggleShowIgnored,
|
||||
ignoredNodeCount = ignoredNodeCount,
|
||||
|
|
@ -172,8 +172,14 @@ fun NodeScreen(nodesViewModel: NodesViewModel = hiltViewModel(), navigateToNodeD
|
|||
onConfirmRemove = { nodesViewModel.removeNode(it.num) },
|
||||
)
|
||||
|
||||
Box {
|
||||
Box(modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp)) {
|
||||
var showContextMenu by remember { mutableStateOf(false) }
|
||||
val longClick =
|
||||
if (node.num != ourNode?.num) {
|
||||
{ showContextMenu = true }
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
NodeItem(
|
||||
modifier = Modifier.animateItem(),
|
||||
|
|
@ -181,9 +187,8 @@ fun NodeScreen(nodesViewModel: NodesViewModel = hiltViewModel(), navigateToNodeD
|
|||
thatNode = node,
|
||||
distanceUnits = state.distanceUnits,
|
||||
tempInFahrenheit = state.tempInFahrenheit,
|
||||
onClickChip = { navigateToNodeDetails(it.num) },
|
||||
onLongClick = { showContextMenu = true },
|
||||
expanded = state.showDetails,
|
||||
onClick = { navigateToNodeDetails(node.num) },
|
||||
onLongClick = longClick,
|
||||
currentTimeMillis = currentTimeMillis,
|
||||
isConnected = connectionState.isConnected(),
|
||||
)
|
||||
|
|
@ -213,7 +218,7 @@ private fun ContextMenu(
|
|||
onClickRemove: (Node) -> Unit,
|
||||
onDismiss: () -> Unit,
|
||||
) {
|
||||
DropdownMenu(expanded = expanded, onDismissRequest = onDismiss, offset = DpOffset(16.dp, 0.dp)) {
|
||||
DropdownMenu(expanded = expanded, onDismissRequest = onDismiss, offset = DpOffset(x = 0.dp, y = 8.dp)) {
|
||||
val isFavorite = node.isFavorite
|
||||
val isIgnored = node.isIgnored
|
||||
|
||||
|
|
|
|||
|
|
@ -34,14 +34,12 @@ import kotlinx.coroutines.flow.combine
|
|||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
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.prefs.ui.UiPrefs
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
|
|
@ -52,7 +50,6 @@ constructor(
|
|||
private val nodeRepository: NodeRepository,
|
||||
radioConfigRepository: RadioConfigRepository,
|
||||
private val serviceRepository: ServiceRepository,
|
||||
private val uiPrefs: UiPrefs,
|
||||
private val uiPreferencesDataSource: UiPreferencesDataSource,
|
||||
) : ViewModel() {
|
||||
|
||||
|
|
@ -97,21 +94,13 @@ constructor(
|
|||
NodeFilterState(filterText, includeUnknown, onlyOnline, onlyDirect, showIgnored)
|
||||
}
|
||||
|
||||
private val showDetails = MutableStateFlow(uiPrefs.showDetails)
|
||||
|
||||
val nodesUiState: StateFlow<NodesUiState> =
|
||||
combine(nodeSortOption, nodeFilter, showDetails, radioConfigRepository.deviceProfileFlow) {
|
||||
sort,
|
||||
nodeFilter,
|
||||
showDetails,
|
||||
profile,
|
||||
->
|
||||
combine(nodeSortOption, nodeFilter, radioConfigRepository.deviceProfileFlow) { sort, nodeFilter, profile ->
|
||||
NodesUiState(
|
||||
sort = sort,
|
||||
filter = nodeFilter,
|
||||
distanceUnits = profile.config.display.units.number,
|
||||
tempInFahrenheit = profile.moduleConfig.telemetry.environmentDisplayFahrenheit,
|
||||
showDetails = showDetails,
|
||||
)
|
||||
}
|
||||
.stateIn(
|
||||
|
|
@ -172,8 +161,6 @@ constructor(
|
|||
uiPreferencesDataSource.setNodeSort(sort.ordinal)
|
||||
}
|
||||
|
||||
fun toggleShowDetails() = toggle(showDetails) { uiPrefs.showDetails = it }
|
||||
|
||||
fun addSharedContact(sharedContact: AdminProtos.SharedContact) =
|
||||
viewModelScope.launch { serviceRepository.onServiceAction(ServiceAction.AddSharedContact(sharedContact)) }
|
||||
|
||||
|
|
@ -181,13 +168,6 @@ constructor(
|
|||
_sharedContactRequested.value = sharedContact
|
||||
}
|
||||
|
||||
private fun toggle(state: MutableStateFlow<Boolean>, onChanged: (newValue: Boolean) -> Unit) {
|
||||
(!state.value).let { toggled ->
|
||||
state.update { toggled }
|
||||
onChanged(toggled)
|
||||
}
|
||||
}
|
||||
|
||||
fun favoriteNode(node: Node) = viewModelScope.launch {
|
||||
try {
|
||||
serviceRepository.onServiceAction(ServiceAction.Favorite(node))
|
||||
|
|
@ -221,7 +201,6 @@ data class NodesUiState(
|
|||
val filter: NodeFilterState = NodeFilterState(),
|
||||
val distanceUnits: Int = 0,
|
||||
val tempInFahrenheit: Boolean = false,
|
||||
val showDetails: Boolean = false,
|
||||
)
|
||||
|
||||
data class NodeFilterState(
|
||||
|
|
|
|||
|
|
@ -75,8 +75,6 @@ fun NodeFilterTextField(
|
|||
onToggleOnlyOnline: () -> Unit,
|
||||
onlyDirect: Boolean,
|
||||
onToggleOnlyDirect: () -> Unit,
|
||||
showDetails: Boolean,
|
||||
onToggleShowDetails: () -> Unit,
|
||||
showIgnored: Boolean,
|
||||
onToggleShowIgnored: () -> Unit,
|
||||
ignoredNodeCount: Int,
|
||||
|
|
@ -97,8 +95,6 @@ fun NodeFilterTextField(
|
|||
onToggleOnlyOnline = onToggleOnlyOnline,
|
||||
onlyDirect = onlyDirect,
|
||||
onToggleOnlyDirect = onToggleOnlyDirect,
|
||||
showDetails = showDetails,
|
||||
onToggleShowDetails = onToggleShowDetails,
|
||||
showIgnored = showIgnored,
|
||||
onToggleShowIgnored = onToggleShowIgnored,
|
||||
ignoredNodeCount = ignoredNodeCount,
|
||||
|
|
@ -258,25 +254,6 @@ private fun NodeSortButton(
|
|||
},
|
||||
)
|
||||
HorizontalDivider()
|
||||
DropdownMenuItem(
|
||||
onClick = {
|
||||
toggles.onToggleShowDetails()
|
||||
expanded = false
|
||||
},
|
||||
text = {
|
||||
Row {
|
||||
AnimatedVisibility(visible = toggles.showDetails) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Done,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.padding(end = 4.dp),
|
||||
)
|
||||
}
|
||||
Text(text = stringResource(id = R.string.node_filter_show_details))
|
||||
}
|
||||
},
|
||||
)
|
||||
HorizontalDivider()
|
||||
DropdownMenuItem(
|
||||
onClick = {
|
||||
toggles.onToggleShowIgnored()
|
||||
|
|
@ -321,8 +298,6 @@ private fun NodeFilterTextFieldPreview() {
|
|||
onToggleOnlyOnline = {},
|
||||
onlyDirect = false,
|
||||
onToggleOnlyDirect = {},
|
||||
showDetails = false,
|
||||
onToggleShowDetails = {},
|
||||
showIgnored = false,
|
||||
onToggleShowIgnored = {},
|
||||
ignoredNodeCount = 0,
|
||||
|
|
@ -337,8 +312,6 @@ data class NodeFilterToggles(
|
|||
val onToggleOnlyOnline: () -> Unit,
|
||||
val onlyDirect: Boolean,
|
||||
val onToggleOnlyDirect: () -> Unit,
|
||||
val showDetails: Boolean,
|
||||
val onToggleShowDetails: () -> Unit,
|
||||
val showIgnored: Boolean,
|
||||
val onToggleShowIgnored: () -> Unit,
|
||||
val ignoredNodeCount: Int,
|
||||
|
|
|
|||
|
|
@ -31,27 +31,22 @@ import androidx.compose.foundation.layout.size
|
|||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.LocalTextStyle
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.contentColorFor
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontStyle
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextDecoration
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.geeksville.mesh.ConfigProtos.Config.DeviceConfig
|
||||
import com.geeksville.mesh.ConfigProtos.Config.DisplayConfig
|
||||
import com.geeksville.mesh.MeshProtos
|
||||
import com.geeksville.mesh.ui.common.components.SignalInfo
|
||||
import com.geeksville.mesh.ui.common.preview.NodePreviewParameterProvider
|
||||
import org.meshtastic.core.database.model.Node
|
||||
|
|
@ -69,9 +64,8 @@ fun NodeItem(
|
|||
distanceUnits: Int,
|
||||
tempInFahrenheit: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
onClickChip: (Node) -> Unit = {},
|
||||
onLongClick: () -> Unit = {},
|
||||
expanded: Boolean = false,
|
||||
onClick: () -> Unit = {},
|
||||
onLongClick: (() -> Unit)? = null,
|
||||
currentTimeMillis: Long,
|
||||
isConnected: Boolean = false,
|
||||
) {
|
||||
|
|
@ -83,18 +77,6 @@ fun NodeItem(
|
|||
val distance =
|
||||
remember(thisNode, thatNode) { thisNode?.distance(thatNode)?.takeIf { it > 0 }?.toDistanceString(system) }
|
||||
|
||||
val hwInfoString =
|
||||
when (val hwModel = thatNode.user.hwModel) {
|
||||
MeshProtos.HardwareModel.UNSET -> MeshProtos.HardwareModel.UNSET.name
|
||||
else -> hwModel.name.replace('_', '-').replace('p', '.').lowercase()
|
||||
}
|
||||
val roleName =
|
||||
if (thatNode.isUnknownUser) {
|
||||
DeviceConfig.Role.UNRECOGNIZED.name
|
||||
} else {
|
||||
thatNode.user.role.name
|
||||
}
|
||||
|
||||
val style =
|
||||
if (thatNode.isUnknownUser) {
|
||||
LocalTextStyle.current.copy(fontStyle = FontStyle.Italic)
|
||||
|
|
@ -114,7 +96,6 @@ fun NodeItem(
|
|||
.copy(containerColor = containerColor, contentColor = contentColorFor(containerColor))
|
||||
} ?: (CardDefaults.cardColors())
|
||||
|
||||
val (detailsShown, showDetails) = remember { mutableStateOf(expanded) }
|
||||
val unmessageable =
|
||||
remember(thatNode) {
|
||||
when {
|
||||
|
|
@ -123,18 +104,13 @@ fun NodeItem(
|
|||
}
|
||||
}
|
||||
|
||||
Card(
|
||||
modifier =
|
||||
modifier
|
||||
.combinedClickable(onClick = { showDetails(!detailsShown) }, onLongClick = onLongClick)
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 8.dp, vertical = 4.dp)
|
||||
.defaultMinSize(minHeight = 80.dp),
|
||||
colors = cardColors,
|
||||
) {
|
||||
Column(modifier = Modifier.fillMaxWidth().padding(8.dp)) {
|
||||
Card(modifier = modifier.fillMaxWidth().defaultMinSize(minHeight = 80.dp), colors = cardColors) {
|
||||
Column(
|
||||
modifier =
|
||||
Modifier.combinedClickable(onClick = onClick, onLongClick = onLongClick).fillMaxWidth().padding(8.dp),
|
||||
) {
|
||||
Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
|
||||
NodeChip(node = thatNode, onClick = onClickChip)
|
||||
NodeChip(node = thatNode)
|
||||
|
||||
NodeKeyStatusIcon(
|
||||
hasPKC = thatNode.hasPKC,
|
||||
|
|
@ -190,51 +166,6 @@ fun NodeItem(
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (detailsShown || expanded) {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
HorizontalDivider()
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
|
||||
thatNode.validPosition?.let {
|
||||
LinkedCoordinates(
|
||||
latitude = thatNode.latitude,
|
||||
longitude = thatNode.longitude,
|
||||
nodeName = longName,
|
||||
)
|
||||
}
|
||||
thatNode.validPosition?.let { position ->
|
||||
ElevationInfo(
|
||||
altitude = position.altitude,
|
||||
system = system,
|
||||
suffix = stringResource(id = R.string.elevation_suffix),
|
||||
)
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
Row(modifier = Modifier.fillMaxWidth()) {
|
||||
Text(
|
||||
modifier = Modifier.weight(1f),
|
||||
text = hwInfoString,
|
||||
fontSize = MaterialTheme.typography.labelLarge.fontSize,
|
||||
style = style,
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.weight(1f),
|
||||
text = roleName,
|
||||
textAlign = TextAlign.Center,
|
||||
fontSize = MaterialTheme.typography.labelLarge.fontSize,
|
||||
style = style,
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.weight(1f),
|
||||
text = thatNode.user.id.ifEmpty { "???" },
|
||||
textAlign = TextAlign.End,
|
||||
fontSize = MaterialTheme.typography.labelLarge.fontSize,
|
||||
style = style,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -261,7 +192,6 @@ fun NodeInfoPreview(@PreviewParameter(NodePreviewParameterProvider::class) thatN
|
|||
thatNode = thatNode,
|
||||
distanceUnits = 1,
|
||||
tempInFahrenheit = true,
|
||||
expanded = false,
|
||||
currentTimeMillis = System.currentTimeMillis(),
|
||||
)
|
||||
Text(text = "Details Shown", color = MaterialTheme.colorScheme.onBackground)
|
||||
|
|
@ -270,7 +200,6 @@ fun NodeInfoPreview(@PreviewParameter(NodePreviewParameterProvider::class) thatN
|
|||
thatNode = thatNode,
|
||||
distanceUnits = 1,
|
||||
tempInFahrenheit = true,
|
||||
expanded = true,
|
||||
currentTimeMillis = System.currentTimeMillis(),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue