mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-05-18 11:46:28 -04:00
fix: prompt to confirm discarding unsaved changes in config screens
Fixes #5283 by introducing an UnsavedChangesDialog component and integrating it into RadioConfigScreenList and ChannelConfigScreen. Utilizes PlatformBackHandler to guard against accidental system back gestures.
This commit is contained in:
@@ -182,6 +182,8 @@
|
||||
<string name="accept">Accept</string>
|
||||
<string name="cancel">Cancel</string>
|
||||
<string name="discard_changes">Discard</string>
|
||||
<string name="unsaved_changes_message">You have unsaved changes. Are you sure you want to discard them?</string>
|
||||
<string name="stay">Stay</string>
|
||||
<string name="save_changes">Save</string>
|
||||
<string name="new_channel_rcvd">New Channel URL received</string>
|
||||
<string name="report">Report</string>
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) 2025-2026 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.runtime.Composable
|
||||
import org.meshtastic.core.resources.Res
|
||||
import org.meshtastic.core.resources.cancel
|
||||
import org.meshtastic.core.resources.discard_changes
|
||||
import org.meshtastic.core.resources.stay
|
||||
import org.meshtastic.core.resources.unsaved_changes_message
|
||||
|
||||
@Composable
|
||||
fun UnsavedChangesDialog(onDiscard: () -> Unit, onStay: () -> Unit) {
|
||||
MeshtasticDialog(
|
||||
titleRes = Res.string.cancel,
|
||||
messageRes = Res.string.unsaved_changes_message,
|
||||
confirmTextRes = Res.string.discard_changes,
|
||||
onConfirm = onDiscard,
|
||||
dismissTextRes = Res.string.stay,
|
||||
onDismiss = onStay,
|
||||
)
|
||||
}
|
||||
@@ -137,6 +137,27 @@ private fun ChannelConfigScreen(
|
||||
|
||||
var showEditChannelDialog: Int? by rememberSaveable { mutableStateOf(null) }
|
||||
var showChannelLegendDialog by rememberSaveable { mutableStateOf(false) }
|
||||
var showExitConfirmation by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
val handleBack = {
|
||||
if (isEditing) {
|
||||
showExitConfirmation = true
|
||||
} else {
|
||||
onBack()
|
||||
}
|
||||
}
|
||||
|
||||
org.meshtastic.core.ui.util.PlatformBackHandler(enabled = isEditing) { showExitConfirmation = true }
|
||||
|
||||
if (showExitConfirmation) {
|
||||
org.meshtastic.core.ui.component.UnsavedChangesDialog(
|
||||
onDiscard = {
|
||||
showExitConfirmation = false
|
||||
onBack()
|
||||
},
|
||||
onStay = { showExitConfirmation = false },
|
||||
)
|
||||
}
|
||||
|
||||
if (showEditChannelDialog != null) {
|
||||
val index = showEditChannelDialog ?: return
|
||||
@@ -164,7 +185,7 @@ private fun ChannelConfigScreen(
|
||||
MainAppBar(
|
||||
title = title,
|
||||
canNavigateUp = true,
|
||||
onNavigateUp = onBack,
|
||||
onNavigateUp = handleBack,
|
||||
ourNode = null,
|
||||
showNodeChip = false,
|
||||
actions = {},
|
||||
|
||||
@@ -31,6 +31,10 @@ import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListScope
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.unit.dp
|
||||
@@ -41,6 +45,8 @@ import org.meshtastic.core.resources.discard_changes
|
||||
import org.meshtastic.core.resources.save_changes
|
||||
import org.meshtastic.core.ui.component.MainAppBar
|
||||
import org.meshtastic.core.ui.component.PreferenceFooter
|
||||
import org.meshtastic.core.ui.component.UnsavedChangesDialog
|
||||
import org.meshtastic.core.ui.util.PlatformBackHandler
|
||||
import org.meshtastic.feature.settings.radio.ResponseState
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@@ -60,14 +66,38 @@ fun <T : Message<T, *>> RadioConfigScreenList(
|
||||
content: LazyListScope.() -> Unit,
|
||||
) {
|
||||
val focusManager = LocalFocusManager.current
|
||||
val isEditing = configState.isDirty || additionalDirtyCheck()
|
||||
var showExitConfirmation by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
val handleBack = {
|
||||
if (isEditing) {
|
||||
showExitConfirmation = true
|
||||
} else {
|
||||
onBack()
|
||||
}
|
||||
}
|
||||
|
||||
PlatformBackHandler(enabled = isEditing) { showExitConfirmation = true }
|
||||
|
||||
Box(modifier = modifier) {
|
||||
if (showExitConfirmation) {
|
||||
UnsavedChangesDialog(
|
||||
onDiscard = {
|
||||
showExitConfirmation = false
|
||||
configState.reset()
|
||||
onDiscard()
|
||||
onBack()
|
||||
},
|
||||
onStay = { showExitConfirmation = false },
|
||||
)
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
MainAppBar(
|
||||
title = title,
|
||||
canNavigateUp = true,
|
||||
onNavigateUp = onBack,
|
||||
onNavigateUp = handleBack,
|
||||
ourNode = null,
|
||||
showNodeChip = false,
|
||||
actions = actions,
|
||||
@@ -75,7 +105,7 @@ fun <T : Message<T, *>> RadioConfigScreenList(
|
||||
)
|
||||
},
|
||||
) { innerPadding ->
|
||||
val showFooterButtons = configState.isDirty || additionalDirtyCheck()
|
||||
val showFooterButtons = isEditing
|
||||
|
||||
LazyColumn(
|
||||
modifier = Modifier.padding(innerPadding).fillMaxSize(),
|
||||
|
||||
Reference in New Issue
Block a user