mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
Add dividers to node details (#3466)
This commit is contained in:
parent
d64825f4f4
commit
73b37c17dc
9 changed files with 122 additions and 6 deletions
|
|
@ -32,15 +32,12 @@ import androidx.compose.runtime.mutableStateOf
|
|||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.unit.dp
|
||||
import org.meshtastic.core.database.entity.FirmwareRelease
|
||||
import org.meshtastic.core.database.model.Node
|
||||
import org.meshtastic.core.strings.R
|
||||
import org.meshtastic.core.ui.component.SharedContactDialog
|
||||
import org.meshtastic.core.ui.component.TitledCard
|
||||
import org.meshtastic.core.ui.component.preview.NodePreviewParameterProvider
|
||||
import org.meshtastic.core.ui.theme.AppTheme
|
||||
import org.meshtastic.feature.node.component.AdministrationSection
|
||||
|
|
@ -116,7 +113,7 @@ fun NodeDetailList(
|
|||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||
) {
|
||||
if (metricsState.deviceHardware != null) {
|
||||
TitledCard(title = stringResource(R.string.device)) { DeviceDetailsSection(metricsState) }
|
||||
DeviceDetailsSection(metricsState)
|
||||
}
|
||||
|
||||
NodeDetailsSection(node)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.meshtastic.core.ui.component
|
||||
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.DividerDefaults
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@Composable
|
||||
fun InsetDivider(
|
||||
modifier: Modifier = Modifier,
|
||||
inset: Dp = 16.dp,
|
||||
thickness: Dp = DividerDefaults.Thickness,
|
||||
color: Color = DividerDefaults.color,
|
||||
) {
|
||||
InsetDivider(modifier = modifier, startInset = inset, endInset = inset, thickness = thickness, color = color)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun InsetDivider(
|
||||
modifier: Modifier = Modifier,
|
||||
startInset: Dp = 0.dp,
|
||||
endInset: Dp = 0.dp,
|
||||
thickness: Dp = DividerDefaults.Thickness,
|
||||
color: Color = DividerDefaults.color,
|
||||
) {
|
||||
HorizontalDivider(
|
||||
modifier = modifier.padding(start = startInset, end = endInset),
|
||||
thickness = thickness,
|
||||
color = color,
|
||||
)
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@
|
|||
<ID>ComposableParamOrder:NodeItem.kt$NodeItem</ID>
|
||||
<ID>ComposableParamOrder:SatelliteCountInfo.kt$SatelliteCountInfo</ID>
|
||||
<ID>ComposableParamOrder:TracerouteButton.kt$TracerouteButton</ID>
|
||||
<ID>LongMethod:NodeDetailsSection.kt$@Composable private fun MainNodeDetails(node: Node)</ID>
|
||||
<ID>ModifierMissing:NodeStatusIcons.kt$NodeStatusIcons</ID>
|
||||
<ID>MultipleEmitters:NodeDetailsSection.kt$MainNodeDetails</ID>
|
||||
<ID>MultipleEmitters:RemoteDeviceActions.kt$RemoteDeviceActions</ID>
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ import androidx.compose.material.icons.filled.ForkLeft
|
|||
import androidx.compose.material.icons.filled.Icecream
|
||||
import androidx.compose.material.icons.filled.Memory
|
||||
import androidx.compose.material.icons.filled.Settings
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
|
|
@ -35,6 +34,7 @@ import org.meshtastic.core.model.DeviceVersion
|
|||
import org.meshtastic.core.navigation.SettingsRoutes
|
||||
import org.meshtastic.core.service.ServiceAction
|
||||
import org.meshtastic.core.strings.R
|
||||
import org.meshtastic.core.ui.component.InsetDivider
|
||||
import org.meshtastic.core.ui.component.ListItem
|
||||
import org.meshtastic.core.ui.component.TitledCard
|
||||
import org.meshtastic.core.ui.theme.StatusColors.StatusGreen
|
||||
|
|
@ -61,6 +61,9 @@ fun AdministrationSection(
|
|||
trailingIcon = null,
|
||||
onClick = { onAction(NodeDetailAction.TriggerServiceAction(ServiceAction.GetDeviceMetadata(node.num))) },
|
||||
)
|
||||
|
||||
InsetDivider()
|
||||
|
||||
ListItem(
|
||||
text = stringResource(id = R.string.remote_admin),
|
||||
leadingIcon = Icons.Default.Settings,
|
||||
|
|
@ -95,6 +98,8 @@ fun AdministrationSection(
|
|||
val deviceVersion = DeviceVersion(firmwareVersion.substringBeforeLast("."))
|
||||
val statusColor = deviceVersion.determineFirmwareStatusColor(latestStable, latestAlpha)
|
||||
|
||||
InsetDivider()
|
||||
|
||||
ListItem(
|
||||
text = stringResource(R.string.installed_firmware_version),
|
||||
leadingIcon = Icons.Default.Memory,
|
||||
|
|
@ -103,7 +108,9 @@ fun AdministrationSection(
|
|||
leadingIconTint = statusColor,
|
||||
trailingIcon = null,
|
||||
)
|
||||
HorizontalDivider()
|
||||
|
||||
InsetDivider()
|
||||
|
||||
ListItem(
|
||||
text = stringResource(R.string.latest_stable_firmware),
|
||||
leadingIcon = Icons.Default.Memory,
|
||||
|
|
@ -113,6 +120,9 @@ fun AdministrationSection(
|
|||
trailingIcon = null,
|
||||
onClick = { onFirmwareSelect(latestStable) },
|
||||
)
|
||||
|
||||
InsetDivider()
|
||||
|
||||
ListItem(
|
||||
text = stringResource(R.string.latest_alpha_firmware),
|
||||
leadingIcon = Icons.Default.Memory,
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ import androidx.compose.ui.graphics.Color
|
|||
import androidx.compose.ui.res.stringResource
|
||||
import org.meshtastic.core.database.model.Node
|
||||
import org.meshtastic.core.strings.R
|
||||
import org.meshtastic.core.ui.component.InsetDivider
|
||||
import org.meshtastic.core.ui.component.ListItem
|
||||
import org.meshtastic.core.ui.component.SwitchListItem
|
||||
import org.meshtastic.core.ui.component.TitledCard
|
||||
|
|
@ -74,8 +75,12 @@ fun DeviceActions(
|
|||
onClick = { onAction(NodeDetailAction.ShareContact) },
|
||||
)
|
||||
if (!isLocal) {
|
||||
InsetDivider()
|
||||
RemoteDeviceActions(node = node, lastTracerouteTime = lastTracerouteTime, onAction = onAction)
|
||||
}
|
||||
|
||||
InsetDivider()
|
||||
|
||||
SwitchListItem(
|
||||
text = stringResource(R.string.favorite),
|
||||
leadingIcon = if (node.isFavorite) Icons.Default.Star else Icons.Default.StarBorder,
|
||||
|
|
@ -83,6 +88,9 @@ fun DeviceActions(
|
|||
checked = node.isFavorite,
|
||||
onClick = { displayFavoriteDialog = true },
|
||||
)
|
||||
|
||||
InsetDivider()
|
||||
|
||||
SwitchListItem(
|
||||
text = stringResource(R.string.ignore),
|
||||
leadingIcon =
|
||||
|
|
@ -90,6 +98,9 @@ fun DeviceActions(
|
|||
checked = node.isIgnored,
|
||||
onClick = { displayIgnoreDialog = true },
|
||||
)
|
||||
|
||||
InsetDivider()
|
||||
|
||||
ListItem(
|
||||
text = stringResource(id = R.string.remove),
|
||||
leadingIcon = Icons.Rounded.Delete,
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ import coil3.compose.AsyncImage
|
|||
import coil3.request.ImageRequest
|
||||
import org.meshtastic.core.model.DeviceHardware
|
||||
import org.meshtastic.core.strings.R
|
||||
import org.meshtastic.core.ui.component.InsetDivider
|
||||
import org.meshtastic.core.ui.component.ListItem
|
||||
import org.meshtastic.core.ui.component.TitledCard
|
||||
import org.meshtastic.core.ui.theme.StatusColors.StatusGreen
|
||||
|
|
@ -58,6 +59,8 @@ fun DeviceDetailsSection(state: MetricsState, modifier: Modifier = Modifier) {
|
|||
val hwModelName = deviceHardware.displayName
|
||||
val isSupported = deviceHardware.activelySupported
|
||||
TitledCard(stringResource(R.string.device), modifier = modifier) {
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
Box(
|
||||
modifier =
|
||||
Modifier.align(Alignment.CenterHorizontally)
|
||||
|
|
@ -71,6 +74,8 @@ fun DeviceDetailsSection(state: MetricsState, modifier: Modifier = Modifier) {
|
|||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
InsetDivider()
|
||||
|
||||
ListItem(
|
||||
text = stringResource(R.string.hardware),
|
||||
leadingIcon = Icons.Default.Router,
|
||||
|
|
@ -78,6 +83,9 @@ fun DeviceDetailsSection(state: MetricsState, modifier: Modifier = Modifier) {
|
|||
copyable = true,
|
||||
trailingIcon = null,
|
||||
)
|
||||
|
||||
InsetDivider()
|
||||
|
||||
ListItem(
|
||||
text =
|
||||
if (isSupported) {
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ import org.meshtastic.core.database.model.Node
|
|||
import org.meshtastic.core.model.util.formatAgo
|
||||
import org.meshtastic.core.model.util.formatUptime
|
||||
import org.meshtastic.core.strings.R
|
||||
import org.meshtastic.core.ui.component.InsetDivider
|
||||
import org.meshtastic.core.ui.component.ListItem
|
||||
import org.meshtastic.core.ui.component.TitledCard
|
||||
import org.meshtastic.feature.node.model.isEffectivelyUnmessageable
|
||||
|
|
@ -87,6 +88,9 @@ private fun MainNodeDetails(node: Node) {
|
|||
copyable = true,
|
||||
trailingIcon = null,
|
||||
)
|
||||
|
||||
InsetDivider()
|
||||
|
||||
ListItem(
|
||||
text = stringResource(R.string.short_name),
|
||||
leadingIcon = Icons.Outlined.Person,
|
||||
|
|
@ -94,6 +98,9 @@ private fun MainNodeDetails(node: Node) {
|
|||
copyable = true,
|
||||
trailingIcon = null,
|
||||
)
|
||||
|
||||
InsetDivider()
|
||||
|
||||
ListItem(
|
||||
text = stringResource(R.string.node_number),
|
||||
leadingIcon = Icons.Default.Numbers,
|
||||
|
|
@ -101,6 +108,9 @@ private fun MainNodeDetails(node: Node) {
|
|||
copyable = true,
|
||||
trailingIcon = null,
|
||||
)
|
||||
|
||||
InsetDivider()
|
||||
|
||||
ListItem(
|
||||
text = stringResource(R.string.user_id),
|
||||
leadingIcon = Icons.Default.Person,
|
||||
|
|
@ -108,13 +118,19 @@ private fun MainNodeDetails(node: Node) {
|
|||
copyable = true,
|
||||
trailingIcon = null,
|
||||
)
|
||||
|
||||
InsetDivider()
|
||||
|
||||
ListItem(
|
||||
text = stringResource(R.string.role),
|
||||
leadingIcon = Icons.Default.Work,
|
||||
supportingText = node.user.role.name,
|
||||
trailingIcon = null,
|
||||
)
|
||||
|
||||
if (node.isEffectivelyUnmessageable) {
|
||||
InsetDivider()
|
||||
|
||||
ListItem(
|
||||
text = stringResource(R.string.unmonitored_or_infrastructure),
|
||||
leadingIcon = Icons.Outlined.NoCell,
|
||||
|
|
@ -122,6 +138,8 @@ private fun MainNodeDetails(node: Node) {
|
|||
)
|
||||
}
|
||||
if (node.deviceMetrics.uptimeSeconds > 0) {
|
||||
InsetDivider()
|
||||
|
||||
ListItem(
|
||||
text = stringResource(R.string.uptime),
|
||||
leadingIcon = Icons.Default.CheckCircle,
|
||||
|
|
@ -129,6 +147,9 @@ private fun MainNodeDetails(node: Node) {
|
|||
trailingIcon = null,
|
||||
)
|
||||
}
|
||||
|
||||
InsetDivider()
|
||||
|
||||
ListItem(
|
||||
text = stringResource(R.string.node_sort_last_heard),
|
||||
leadingIcon = Icons.Default.History,
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import androidx.compose.ui.unit.dp
|
|||
import org.meshtastic.core.database.model.Node
|
||||
import org.meshtastic.core.model.util.toDistanceString
|
||||
import org.meshtastic.core.strings.R
|
||||
import org.meshtastic.core.ui.component.InsetDivider
|
||||
import org.meshtastic.core.ui.component.ListItem
|
||||
import org.meshtastic.core.ui.component.TitledCard
|
||||
import org.meshtastic.feature.node.model.LogsType
|
||||
|
|
@ -60,6 +61,8 @@ fun PositionSection(
|
|||
|
||||
// Distance (if available)
|
||||
if (distance != null && distance.isNotEmpty()) {
|
||||
InsetDivider()
|
||||
|
||||
ListItem(
|
||||
text = stringResource(R.string.node_sort_distance),
|
||||
leadingIcon = Icons.Default.SocialDistance,
|
||||
|
|
@ -69,6 +72,8 @@ fun PositionSection(
|
|||
)
|
||||
}
|
||||
|
||||
InsetDivider()
|
||||
|
||||
// Exchange position action
|
||||
ListItem(
|
||||
text = stringResource(id = R.string.exchange_position),
|
||||
|
|
@ -79,6 +84,8 @@ fun PositionSection(
|
|||
|
||||
// Node Map log
|
||||
if (availableLogs.contains(LogsType.NODE_MAP)) {
|
||||
InsetDivider()
|
||||
|
||||
ListItem(text = stringResource(LogsType.NODE_MAP.titleRes), leadingIcon = LogsType.NODE_MAP.icon) {
|
||||
onAction(NodeDetailAction.Navigate(LogsType.NODE_MAP.route))
|
||||
}
|
||||
|
|
@ -86,6 +93,8 @@ fun PositionSection(
|
|||
|
||||
// Positions Log
|
||||
if (availableLogs.contains(LogsType.POSITIONS)) {
|
||||
InsetDivider()
|
||||
|
||||
ListItem(text = stringResource(LogsType.POSITIONS.titleRes), leadingIcon = LogsType.POSITIONS.icon) {
|
||||
onAction(NodeDetailAction.Navigate(LogsType.POSITIONS.route))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.ui.res.stringResource
|
||||
import org.meshtastic.core.database.model.Node
|
||||
import org.meshtastic.core.strings.R
|
||||
import org.meshtastic.core.ui.component.InsetDivider
|
||||
import org.meshtastic.core.ui.component.ListItem
|
||||
import org.meshtastic.feature.node.model.NodeDetailAction
|
||||
import org.meshtastic.feature.node.model.isEffectivelyUnmessageable
|
||||
|
|
@ -37,13 +38,19 @@ internal fun RemoteDeviceActions(node: Node, lastTracerouteTime: Long?, onAction
|
|||
trailingIcon = null,
|
||||
onClick = { onAction(NodeDetailAction.HandleNodeMenuAction(NodeMenuAction.DirectMessage(node))) },
|
||||
)
|
||||
|
||||
InsetDivider()
|
||||
}
|
||||
|
||||
ListItem(
|
||||
text = stringResource(id = R.string.exchange_userinfo),
|
||||
leadingIcon = Icons.Default.Person,
|
||||
trailingIcon = null,
|
||||
onClick = { onAction(NodeDetailAction.HandleNodeMenuAction(NodeMenuAction.RequestUserInfo(node))) },
|
||||
)
|
||||
|
||||
InsetDivider()
|
||||
|
||||
TracerouteButton(
|
||||
lastTracerouteTime = lastTracerouteTime,
|
||||
onClick = { onAction(NodeDetailAction.HandleNodeMenuAction(NodeMenuAction.TraceRoute(node))) },
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue