mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
block creation or sending of duplicate channels. (#3913)
This commit is contained in:
parent
499ed58311
commit
7db7f61386
5 changed files with 108 additions and 9 deletions
|
|
@ -37,27 +37,33 @@ import androidx.compose.material3.Icon
|
|||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.saveable.listSaver
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.toMutableStateList
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import kotlinx.coroutines.launch
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
import org.meshtastic.core.model.Channel
|
||||
import org.meshtastic.core.model.DeviceVersion
|
||||
import org.meshtastic.core.model.util.hasDuplicateKeys
|
||||
import org.meshtastic.core.strings.Res
|
||||
import org.meshtastic.core.strings.add
|
||||
import org.meshtastic.core.strings.cancel
|
||||
import org.meshtastic.core.strings.channel_key_already_in_use
|
||||
import org.meshtastic.core.strings.channel_name
|
||||
import org.meshtastic.core.strings.channels
|
||||
import org.meshtastic.core.strings.press_and_drag
|
||||
|
|
@ -67,6 +73,7 @@ import org.meshtastic.core.ui.component.PreferenceFooter
|
|||
import org.meshtastic.core.ui.component.dragContainer
|
||||
import org.meshtastic.core.ui.component.dragDropItemsIndexed
|
||||
import org.meshtastic.core.ui.component.rememberDragDropState
|
||||
import org.meshtastic.core.ui.util.showToast
|
||||
import org.meshtastic.feature.settings.radio.RadioConfigViewModel
|
||||
import org.meshtastic.feature.settings.radio.channel.component.ChannelCard
|
||||
import org.meshtastic.feature.settings.radio.channel.component.ChannelConfigHeader
|
||||
|
|
@ -75,6 +82,7 @@ import org.meshtastic.feature.settings.radio.channel.component.ChannelLegendDial
|
|||
import org.meshtastic.feature.settings.radio.channel.component.EditChannelDialog
|
||||
import org.meshtastic.feature.settings.radio.channel.component.SECONDARY_CHANNEL_EPOCH
|
||||
import org.meshtastic.feature.settings.radio.component.PacketResponseStateDialog
|
||||
import org.meshtastic.proto.AppOnlyProtos
|
||||
import org.meshtastic.proto.ChannelProtos.ChannelSettings
|
||||
import org.meshtastic.proto.ConfigProtos.Config.LoRaConfig
|
||||
import org.meshtastic.proto.channelSettings
|
||||
|
|
@ -135,21 +143,69 @@ private fun ChannelConfigScreen(
|
|||
settingsList.size != settingsListInput.size ||
|
||||
settingsList.zip(settingsListInput).any { (item1, item2) -> item1 != item2 }
|
||||
|
||||
// Check if the current channel list has duplicate PSKs - recompute when list changes
|
||||
var hasDuplicateKeys by remember { mutableStateOf(false) }
|
||||
LaunchedEffect(settingsListInput.size, settingsListInput.toList(), loraConfig) {
|
||||
val channelSet =
|
||||
AppOnlyProtos.ChannelSet.newBuilder()
|
||||
.apply {
|
||||
addAllSettings(settingsListInput.toList())
|
||||
setLoraConfig(loraConfig)
|
||||
}
|
||||
.build()
|
||||
hasDuplicateKeys = channelSet.hasDuplicateKeys()
|
||||
}
|
||||
|
||||
var showEditChannelDialog: Int? by rememberSaveable { mutableStateOf(null) }
|
||||
var showChannelLegendDialog by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
val context = LocalContext.current
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
|
||||
if (showEditChannelDialog != null) {
|
||||
val index = showEditChannelDialog ?: return
|
||||
EditChannelDialog(
|
||||
channelSettings = with(settingsListInput) { if (size > index) get(index) else channelSettings {} },
|
||||
modemPresetName = modemPresetName,
|
||||
onAddClick = {
|
||||
if (settingsListInput.size > index) {
|
||||
settingsListInput[index] = it
|
||||
onAddClick = { newChannelSettings ->
|
||||
val isEditing = index < settingsListInput.size
|
||||
|
||||
// Build a temporary list to check for duplicates
|
||||
val tempList = settingsListInput.toMutableList()
|
||||
if (isEditing) {
|
||||
tempList[index] = newChannelSettings
|
||||
} else {
|
||||
settingsListInput.add(it)
|
||||
tempList.add(newChannelSettings)
|
||||
}
|
||||
|
||||
// Check for duplicates in the temporary list
|
||||
val tempChannelSet =
|
||||
AppOnlyProtos.ChannelSet.newBuilder()
|
||||
.apply {
|
||||
addAllSettings(tempList)
|
||||
setLoraConfig(loraConfig)
|
||||
}
|
||||
.build()
|
||||
|
||||
val hasDuplicate = tempChannelSet.hasDuplicateKeys()
|
||||
|
||||
if (hasDuplicate) {
|
||||
coroutineScope.launch { context.showToast(Res.string.channel_key_already_in_use) }
|
||||
// If this was a new channel added by FAB (at the end), remove it
|
||||
// FAB adds channel then opens dialog, so index will be lastIndex
|
||||
if (index == settingsListInput.size - 1 && index >= 0) {
|
||||
settingsListInput.removeAt(index)
|
||||
showEditChannelDialog = null
|
||||
}
|
||||
// If editing existing channel, don't update - keep original and dialog open
|
||||
} else {
|
||||
if (isEditing) {
|
||||
settingsListInput[index] = newChannelSettings
|
||||
} else {
|
||||
settingsListInput.add(newChannelSettings)
|
||||
}
|
||||
showEditChannelDialog = null
|
||||
}
|
||||
showEditChannelDialog = null
|
||||
},
|
||||
onDismissRequest = { showEditChannelDialog = null },
|
||||
)
|
||||
|
|
@ -238,7 +294,7 @@ private fun ChannelConfigScreen(
|
|||
item { Spacer(modifier = Modifier.weight(1f)) }
|
||||
item {
|
||||
PreferenceFooter(
|
||||
enabled = enabled && isEditing,
|
||||
enabled = enabled && isEditing && !hasDuplicateKeys,
|
||||
negativeText = stringResource(Res.string.cancel),
|
||||
onNegativeClicked = {
|
||||
focusManager.clearFocus()
|
||||
|
|
@ -248,7 +304,11 @@ private fun ChannelConfigScreen(
|
|||
positiveText = stringResource(Res.string.send),
|
||||
onPositiveClicked = {
|
||||
focusManager.clearFocus()
|
||||
onPositiveClicked(settingsListInput)
|
||||
if (hasDuplicateKeys) {
|
||||
coroutineScope.launch { context.showToast(Res.string.channel_key_already_in_use) }
|
||||
} else {
|
||||
onPositiveClicked(settingsListInput)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue