Decouple ScannedQrCodeDialog from UiViewModel (#3300)

This commit is contained in:
Phil Oliver
2025-10-03 06:12:40 -04:00
committed by GitHub
parent f854cafe6f
commit 4a8cd6fb41
6 changed files with 105 additions and 21 deletions

View File

@@ -347,23 +347,6 @@ constructor(
}
}
fun setChannel(channel: ChannelProtos.Channel) {
try {
meshService?.setChannel(channel.toByteArray())
} catch (ex: RemoteException) {
Timber.e(ex, "Set channel error")
}
}
/** Set the radio config (also updates our saved copy in preferences). */
fun setChannels(channelSet: AppOnlyProtos.ChannelSet) = viewModelScope.launch {
getChannelList(channelSet.settingsList, channels.value.settingsList).forEach(::setChannel)
radioConfigRepository.replaceAllSettings(channelSet.settingsList)
val newConfig = config { lora = channelSet.loraConfig }
if (config.lora != newConfig.lora) setConfig(newConfig)
}
fun addQuickChatAction(action: QuickChatAction) =
viewModelScope.launch(Dispatchers.IO) { quickChatActionRepository.upsert(action) }

View File

@@ -150,7 +150,9 @@ fun MainScreen(uIViewModel: UIViewModel = hiltViewModel(), scanModel: BTScanMode
}
if (connectionState == ConnectionState.CONNECTED) {
requestChannelSet?.let { newChannelSet -> ScannedQrCodeDialog(uIViewModel, newChannelSet) }
requestChannelSet?.let { newChannelSet ->
ScannedQrCodeDialog(newChannelSet, onDismiss = { uIViewModel.clearRequestChannelUrl() })
}
}
analytics.addNavigationTrackingEffect(navController = navController)

View File

@@ -49,24 +49,28 @@ import androidx.compose.ui.tooling.preview.PreviewScreenSizes
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.geeksville.mesh.AppOnlyProtos.ChannelSet
import com.geeksville.mesh.ConfigProtos.Config.LoRaConfig.ModemPreset
import com.geeksville.mesh.channelSet
import com.geeksville.mesh.copy
import com.geeksville.mesh.model.UIViewModel
import com.geeksville.mesh.ui.settings.radio.components.ChannelSelection
import org.meshtastic.core.model.Channel
import org.meshtastic.core.strings.R
@Composable
fun ScannedQrCodeDialog(viewModel: UIViewModel, incoming: ChannelSet) {
fun ScannedQrCodeDialog(
incoming: ChannelSet,
onDismiss: () -> Unit,
viewModel: ScannedQrCodeViewModel = hiltViewModel(),
) {
val channels by viewModel.channels.collectAsStateWithLifecycle()
ScannedQrCodeDialog(
channels = channels,
incoming = incoming,
onDismiss = viewModel::clearRequestChannelUrl,
onDismiss = onDismiss,
onConfirm = viewModel::setChannels,
)
}

View File

@@ -0,0 +1,86 @@
/*
* 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 com.geeksville.mesh.ui.common.components
import android.os.RemoteException
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.geeksville.mesh.AppOnlyProtos
import com.geeksville.mesh.ChannelProtos
import com.geeksville.mesh.ConfigProtos.Config
import com.geeksville.mesh.LocalOnlyProtos.LocalConfig
import com.geeksville.mesh.channelSet
import com.geeksville.mesh.config
import com.geeksville.mesh.model.getChannelList
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import org.meshtastic.core.data.repository.RadioConfigRepository
import org.meshtastic.core.service.ServiceRepository
import timber.log.Timber
import javax.inject.Inject
@HiltViewModel
class ScannedQrCodeViewModel
@Inject
constructor(
private val radioConfigRepository: RadioConfigRepository,
private val serviceRepository: ServiceRepository,
) : ViewModel() {
val channels =
radioConfigRepository.channelSetFlow.stateIn(
viewModelScope,
SharingStarted.WhileSubscribed(5_000L),
channelSet {},
)
private val localConfig =
radioConfigRepository.localConfigFlow.stateIn(
viewModelScope,
SharingStarted.WhileSubscribed(5_000L),
LocalConfig.getDefaultInstance(),
)
/** Set the radio config (also updates our saved copy in preferences). */
fun setChannels(channelSet: AppOnlyProtos.ChannelSet) = viewModelScope.launch {
getChannelList(channelSet.settingsList, channels.value.settingsList).forEach(::setChannel)
radioConfigRepository.replaceAllSettings(channelSet.settingsList)
val newConfig = config { lora = channelSet.loraConfig }
if (localConfig.value.lora != newConfig.lora) setConfig(newConfig)
}
private fun setChannel(channel: ChannelProtos.Channel) {
try {
serviceRepository.meshService?.setChannel(channel.toByteArray())
} catch (ex: RemoteException) {
Timber.e(ex, "Set channel error")
}
}
// Set the radio config (also updates our saved copy in preferences)
private fun setConfig(config: Config) {
try {
serviceRepository.meshService?.setConfig(config.toByteArray())
} catch (ex: RemoteException) {
Timber.e(ex, "Set config error")
}
}
}

View File

@@ -96,6 +96,7 @@ import com.geeksville.mesh.channelSet
import com.geeksville.mesh.copy
import com.geeksville.mesh.navigation.ConfigRoute
import com.geeksville.mesh.navigation.getNavRouteFrom
import com.geeksville.mesh.ui.common.components.ScannedQrCodeDialog
import com.geeksville.mesh.ui.settings.radio.RadioConfigViewModel
import com.geeksville.mesh.ui.settings.radio.components.ChannelSelection
import com.geeksville.mesh.ui.settings.radio.components.PacketResponseStateDialog
@@ -144,6 +145,8 @@ fun ChannelScreen(
var shouldAddChannelsState by remember { mutableStateOf(true) }
val requestChannelSet by viewModel.requestChannelSet.collectAsStateWithLifecycle()
/* Animate waiting for the configurations */
var isWaiting by remember { mutableStateOf(false) }
if (isWaiting) {
@@ -269,6 +272,8 @@ fun ChannelScreen(
)
}
requestChannelSet?.let { ScannedQrCodeDialog(it, onDismiss = { viewModel.clearRequestChannelUrl() }) }
val listState = rememberLazyListState()
LazyColumn(state = listState, contentPadding = PaddingValues(horizontal = 24.dp, vertical = 16.dp)) {
item {

View File

@@ -91,6 +91,10 @@ constructor(
onError()
}
fun clearRequestChannelUrl() {
_requestChannelSet.value = null
}
/** Set the radio config (also updates our saved copy in preferences). */
fun setChannels(channelSet: AppOnlyProtos.ChannelSet) = viewModelScope.launch {
getChannelList(channelSet.settingsList, channels.value.settingsList).forEach(::setChannel)