mirror of
https://github.com/d4rken-org/capod.git
synced 2026-09-14 18:26:11 -04:00
fix(ui): Keep the device detail sheet reachable while scrolled
The detail sheet was composed inside the device-info lazy item, so tapping the battery runtime warning banner after that item had scrolled out of composition did nothing visible while still setting the visibility flag, which made the sheet pop up unprompted on scrolling back up. Build the detail items and compose the sheet at screen scope instead; the card now only reports the tap. Fixes review finding F1
This commit is contained in:
@@ -47,6 +47,7 @@ import eu.darken.capod.main.ui.devicesettings.cards.BatteryCard
|
||||
import eu.darken.capod.main.ui.devicesettings.cards.BatteryHealthTexts
|
||||
import eu.darken.capod.main.ui.devicesettings.cards.BatteryRuntimeWarningBanner
|
||||
import eu.darken.capod.main.ui.devicesettings.cards.ControlsCard
|
||||
import eu.darken.capod.main.ui.devicesettings.cards.DeviceInfoBottomSheet
|
||||
import eu.darken.capod.main.ui.devicesettings.cards.DeviceInfoCard
|
||||
import eu.darken.capod.main.ui.devicesettings.cards.NoiseControlCard
|
||||
import eu.darken.capod.main.ui.devicesettings.cards.NotConnectedCard
|
||||
@@ -242,9 +243,65 @@ fun DeviceSettingsScreen(
|
||||
val enabled = device?.isAapReady == true
|
||||
val isPro = state.isPro
|
||||
// Hoisted out of DeviceInfoCard: the runtime warning banner opens the same detail sheet as the
|
||||
// card's info icon.
|
||||
// card's info icon, and both entry points can be scrolled out of composition independently.
|
||||
var showDeviceDetails by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
val context = LocalContext.current
|
||||
val stateDetection = device?.ble as? HasStateDetection
|
||||
val seenFirst = device?.seenFirstAt
|
||||
val seenLast = device?.seenLastAt
|
||||
val firstSeen = if (device != null && seenFirst != null && seenLast != null &&
|
||||
Duration.between(seenFirst, seenLast).toMinutes() >= 1
|
||||
) {
|
||||
device.firstSeenFormatted(state.now)
|
||||
} else null
|
||||
val locale = LocalConfiguration.current.locales[0]
|
||||
val zoneId = ZoneId.systemDefault()
|
||||
val dateFormatter = remember(locale, zoneId) {
|
||||
DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM)
|
||||
.withLocale(locale)
|
||||
.withZone(zoneId)
|
||||
}
|
||||
val detailLabels = rememberDeviceInfoDetailLabels()
|
||||
val batteryHealthTexts = when {
|
||||
state.batteryHealth != null -> BatteryHealthTexts(
|
||||
left = state.batteryHealth.left?.let {
|
||||
stringResource(R.string.device_settings_info_battery_health_value, it.percent)
|
||||
},
|
||||
right = state.batteryHealth.right?.let {
|
||||
stringResource(R.string.device_settings_info_battery_health_value, it.percent)
|
||||
},
|
||||
headset = state.batteryHealth.headset?.let {
|
||||
stringResource(R.string.device_settings_info_battery_health_value, it.percent)
|
||||
},
|
||||
)
|
||||
state.batteryHealthPending -> BatteryHealthTexts(
|
||||
pending = stringResource(R.string.device_settings_info_battery_health_pending),
|
||||
)
|
||||
else -> null
|
||||
}
|
||||
val detailItems = if (device != null) {
|
||||
buildDeviceInfoDetailItems(
|
||||
info = device.deviceInfo,
|
||||
labels = detailLabels,
|
||||
formatDate = { instant -> dateFormatter.format(instant) },
|
||||
batteryHealth = batteryHealthTexts,
|
||||
)
|
||||
} else {
|
||||
emptyList()
|
||||
}
|
||||
|
||||
LaunchedEffect(detailItems) {
|
||||
if (detailItems.isEmpty()) showDeviceDetails = false
|
||||
}
|
||||
|
||||
if (showDeviceDetails && detailItems.isNotEmpty()) {
|
||||
DeviceInfoBottomSheet(
|
||||
items = detailItems,
|
||||
onDismiss = { showDeviceDetails = false },
|
||||
)
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
@@ -286,45 +343,6 @@ fun DeviceSettingsScreen(
|
||||
// Device Info
|
||||
if (device != null) {
|
||||
item("device_info") {
|
||||
val context = LocalContext.current
|
||||
val stateDetection = device.ble as? HasStateDetection
|
||||
val seenFirst = device.seenFirstAt
|
||||
val seenLast = device.seenLastAt
|
||||
val firstSeen = if (seenFirst != null && seenLast != null && Duration.between(seenFirst, seenLast)
|
||||
.toMinutes() >= 1
|
||||
) {
|
||||
device.firstSeenFormatted(state.now)
|
||||
} else null
|
||||
val info = device.deviceInfo
|
||||
val locale = LocalConfiguration.current.locales[0]
|
||||
val zoneId = ZoneId.systemDefault()
|
||||
val dateFormatter = remember(locale, zoneId) {
|
||||
DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM)
|
||||
.withLocale(locale)
|
||||
.withZone(zoneId)
|
||||
}
|
||||
val detailItems = buildDeviceInfoDetailItems(
|
||||
info = info,
|
||||
labels = rememberDeviceInfoDetailLabels(),
|
||||
formatDate = { instant -> dateFormatter.format(instant) },
|
||||
batteryHealth = when {
|
||||
state.batteryHealth != null -> BatteryHealthTexts(
|
||||
left = state.batteryHealth.left?.let {
|
||||
stringResource(R.string.device_settings_info_battery_health_value, it.percent)
|
||||
},
|
||||
right = state.batteryHealth.right?.let {
|
||||
stringResource(R.string.device_settings_info_battery_health_value, it.percent)
|
||||
},
|
||||
headset = state.batteryHealth.headset?.let {
|
||||
stringResource(R.string.device_settings_info_battery_health_value, it.percent)
|
||||
},
|
||||
)
|
||||
state.batteryHealthPending -> BatteryHealthTexts(
|
||||
pending = stringResource(R.string.device_settings_info_battery_health_pending),
|
||||
)
|
||||
else -> null
|
||||
},
|
||||
)
|
||||
DeviceInfoCard(
|
||||
deviceInfo = device.deviceInfo,
|
||||
modelLabel = buildModelLabel(device),
|
||||
@@ -335,8 +353,7 @@ fun DeviceSettingsScreen(
|
||||
detailItems = detailItems,
|
||||
canRename = device.isAapReady,
|
||||
onRename = onDeviceNameChange,
|
||||
showDetails = showDeviceDetails,
|
||||
onShowDetailsChange = { showDeviceDetails = it },
|
||||
onShowDetails = { showDeviceDetails = true },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.twotone.Edit
|
||||
import androidx.compose.material.icons.twotone.Info
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.ElevatedCard
|
||||
import androidx.compose.material3.Icon
|
||||
@@ -53,15 +52,10 @@ internal fun DeviceInfoCard(
|
||||
detailItems: List<DeviceDetailItem> = emptyList(),
|
||||
canRename: Boolean = false,
|
||||
onRename: (String) -> Unit = {},
|
||||
showDetails: Boolean = false,
|
||||
onShowDetailsChange: (Boolean) -> Unit = {},
|
||||
onShowDetails: () -> Unit = {},
|
||||
) {
|
||||
var showRenameDialog by remember { mutableStateOf(false) }
|
||||
|
||||
LaunchedEffect(detailItems) {
|
||||
if (detailItems.isEmpty()) onShowDetailsChange(false)
|
||||
}
|
||||
|
||||
if (showRenameDialog && deviceInfo != null) {
|
||||
RenameDialog(
|
||||
currentName = deviceInfo.name,
|
||||
@@ -73,13 +67,6 @@ internal fun DeviceInfoCard(
|
||||
)
|
||||
}
|
||||
|
||||
if (showDetails && detailItems.isNotEmpty()) {
|
||||
DeviceInfoBottomSheet(
|
||||
items = detailItems,
|
||||
onDismiss = { onShowDetailsChange(false) },
|
||||
)
|
||||
}
|
||||
|
||||
ElevatedCard(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
@@ -104,7 +91,7 @@ internal fun DeviceInfoCard(
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
if (showInfoIconInModelRow) {
|
||||
IconButton(onClick = { onShowDetailsChange(true) }) {
|
||||
IconButton(onClick = onShowDetails) {
|
||||
Icon(
|
||||
imageVector = Icons.TwoTone.Info,
|
||||
contentDescription = stringResource(R.string.device_settings_info_details_action),
|
||||
@@ -145,7 +132,7 @@ internal fun DeviceInfoCard(
|
||||
} else null,
|
||||
)
|
||||
if (showInfoIconInNameRow) {
|
||||
IconButton(onClick = { onShowDetailsChange(true) }) {
|
||||
IconButton(onClick = onShowDetails) {
|
||||
Icon(
|
||||
imageVector = Icons.TwoTone.Info,
|
||||
contentDescription = stringResource(R.string.device_settings_info_details_action),
|
||||
@@ -160,7 +147,7 @@ internal fun DeviceInfoCard(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.End,
|
||||
) {
|
||||
IconButton(onClick = { onShowDetailsChange(true) }) {
|
||||
IconButton(onClick = onShowDetails) {
|
||||
Icon(
|
||||
imageVector = Icons.TwoTone.Info,
|
||||
contentDescription = stringResource(R.string.device_settings_info_details_action),
|
||||
|
||||
Reference in New Issue
Block a user