mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
refactor: migrate core UI and features to KMP, adopt Navigation 3 (#4750)
Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
This commit is contained in:
parent
b1070321fe
commit
d076361c55
245 changed files with 3106 additions and 1748 deletions
|
|
@ -36,7 +36,6 @@ import androidx.compose.material3.Icon
|
|||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
|
|
@ -47,7 +46,6 @@ import androidx.compose.ui.Alignment
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
|
|
@ -57,9 +55,7 @@ import org.meshtastic.core.resources.debug_logs_export
|
|||
import org.meshtastic.core.resources.debug_search_clear
|
||||
import org.meshtastic.core.resources.debug_search_next
|
||||
import org.meshtastic.core.resources.debug_search_prev
|
||||
import org.meshtastic.core.ui.theme.AppTheme
|
||||
import org.meshtastic.feature.settings.debugging.DebugViewModel.UiMeshLog
|
||||
import org.meshtastic.feature.settings.debugging.LogSearchManager.SearchMatch
|
||||
import org.meshtastic.feature.settings.debugging.LogSearchManager.SearchState
|
||||
|
||||
@Composable
|
||||
|
|
@ -234,71 +230,3 @@ fun DebugSearchStateWithViewModel(
|
|||
onExportLogs = onExportLogs,
|
||||
)
|
||||
}
|
||||
|
||||
@PreviewLightDark
|
||||
@Composable
|
||||
private fun DebugSearchBarEmptyPreview() {
|
||||
AppTheme {
|
||||
Surface {
|
||||
Row(modifier = Modifier.fillMaxWidth().padding(16.dp), verticalAlignment = Alignment.CenterVertically) {
|
||||
DebugSearchBar(
|
||||
searchState = SearchState(),
|
||||
onSearchTextChange = {},
|
||||
onNextMatch = {},
|
||||
onPreviousMatch = {},
|
||||
onClearSearch = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@PreviewLightDark
|
||||
@Composable
|
||||
@Suppress("detekt:MagicNumber") // fake data
|
||||
private fun DebugSearchBarWithTextPreview() {
|
||||
AppTheme {
|
||||
Surface {
|
||||
Row(modifier = Modifier.fillMaxWidth().padding(16.dp), verticalAlignment = Alignment.CenterVertically) {
|
||||
DebugSearchBar(
|
||||
searchState =
|
||||
SearchState(
|
||||
searchText = "test message",
|
||||
currentMatchIndex = 2,
|
||||
allMatches = List(5) { SearchMatch(it, 0, 10, "message") },
|
||||
hasMatches = true,
|
||||
),
|
||||
onSearchTextChange = {},
|
||||
onNextMatch = {},
|
||||
onPreviousMatch = {},
|
||||
onClearSearch = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@PreviewLightDark
|
||||
@Composable
|
||||
@Suppress("detekt:MagicNumber") // fake data
|
||||
private fun DebugSearchBarWithMatchesPreview() {
|
||||
AppTheme {
|
||||
Surface {
|
||||
Row(modifier = Modifier.fillMaxWidth().padding(16.dp), verticalAlignment = Alignment.CenterVertically) {
|
||||
DebugSearchBar(
|
||||
searchState =
|
||||
SearchState(
|
||||
searchText = "error",
|
||||
currentMatchIndex = 0,
|
||||
allMatches = List(3) { SearchMatch(it, 0, 5, "message") },
|
||||
hasMatches = true,
|
||||
),
|
||||
onSearchTextChange = {},
|
||||
onNextMatch = {},
|
||||
onPreviousMatch = {},
|
||||
onClearSearch = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -18,7 +18,6 @@ package org.meshtastic.feature.settings.radio
|
|||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.FlowRow
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
|
|
@ -154,7 +153,7 @@ private fun NodesDeletionPreview(nodesToDelete: List<Node>) {
|
|||
stringResource(Res.string.nodes_queued_for_deletion, nodesToDelete.size),
|
||||
modifier = Modifier.padding(bottom = 16.dp),
|
||||
)
|
||||
FlowRow(
|
||||
androidx.compose.foundation.layout.FlowRow(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
verticalArrangement = Arrangement.Center,
|
||||
|
|
@ -38,7 +38,6 @@ import androidx.compose.material3.Text
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import org.jetbrains.compose.resources.StringResource
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
|
|
@ -63,8 +62,6 @@ import org.meshtastic.core.resources.radio_configuration
|
|||
import org.meshtastic.core.resources.reboot
|
||||
import org.meshtastic.core.resources.shutdown
|
||||
import org.meshtastic.core.ui.component.ListItem
|
||||
import org.meshtastic.core.ui.theme.AppTheme
|
||||
import org.meshtastic.core.ui.theme.StatusColors.StatusRed
|
||||
import org.meshtastic.feature.settings.component.ExpressiveSection
|
||||
import org.meshtastic.feature.settings.navigation.ConfigRoute
|
||||
|
||||
|
|
@ -221,31 +218,11 @@ enum class AdminRoute(val icon: ImageVector, val title: StringResource) {
|
|||
NODEDB_RESET(Icons.Rounded.Storage, Res.string.nodedb_reset),
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun RadioSettingsScreenPreview() = AppTheme {
|
||||
RadioConfigItemList(
|
||||
state = RadioConfigState(isLocal = true, connected = true),
|
||||
isManaged = false,
|
||||
onNavigate = { _ -> },
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ManagedMessage() {
|
||||
Text(
|
||||
text = stringResource(Res.string.message_device_managed),
|
||||
modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp),
|
||||
color = MaterialTheme.colorScheme.StatusRed,
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun RadioSettingsScreenManagedPreview() = AppTheme {
|
||||
RadioConfigItemList(
|
||||
state = RadioConfigState(isLocal = true, connected = true),
|
||||
isManaged = true,
|
||||
onNavigate = { _ -> },
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ package org.meshtastic.feature.settings.radio
|
|||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.navigation.toRoute
|
||||
import co.touchlab.kermit.Logger
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
|
@ -45,7 +44,6 @@ import org.meshtastic.core.model.ConnectionState
|
|||
import org.meshtastic.core.model.MyNodeInfo
|
||||
import org.meshtastic.core.model.Node
|
||||
import org.meshtastic.core.model.Position
|
||||
import org.meshtastic.core.navigation.SettingsRoutes
|
||||
import org.meshtastic.core.repository.AnalyticsPrefs
|
||||
import org.meshtastic.core.repository.HomoglyphPrefs
|
||||
import org.meshtastic.core.repository.LocationRepository
|
||||
|
|
@ -126,9 +124,7 @@ open class RadioConfigViewModel(
|
|||
toggleHomoglyphEncodingUseCase()
|
||||
}
|
||||
|
||||
private val destNum =
|
||||
savedStateHandle.get<Int>("destNum")
|
||||
?: runCatching { savedStateHandle.toRoute<SettingsRoutes.Settings>().destNum }.getOrNull()
|
||||
private val destNum = savedStateHandle.get<Int>("destNum")
|
||||
|
||||
private val _destNode = MutableStateFlow<Node?>(null)
|
||||
val destNode: StateFlow<Node?>
|
||||
|
|
|
|||
|
|
@ -47,7 +47,6 @@ import androidx.compose.ui.Alignment
|
|||
import androidx.compose.ui.Modifier
|
||||
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
|
||||
|
|
@ -57,7 +56,6 @@ import org.meshtastic.core.model.Channel
|
|||
import org.meshtastic.core.resources.Res
|
||||
import org.meshtastic.core.resources.add
|
||||
import org.meshtastic.core.resources.cancel
|
||||
import org.meshtastic.core.resources.channel_name
|
||||
import org.meshtastic.core.resources.channels
|
||||
import org.meshtastic.core.resources.press_and_drag
|
||||
import org.meshtastic.core.resources.send
|
||||
|
|
@ -301,21 +299,3 @@ private fun determineLocationSharingChannel(capabilities: Capabilities, settings
|
|||
}
|
||||
return output
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun ChannelConfigScreenPreview() {
|
||||
ChannelConfigScreen(
|
||||
title = "Channels",
|
||||
onBack = {},
|
||||
settingsList =
|
||||
listOf(
|
||||
ChannelSettings(psk = Channel.default.settings.psk, name = Channel.default.name),
|
||||
ChannelSettings(name = stringResource(Res.string.channel_name)),
|
||||
),
|
||||
loraConfig = Channel.default.loraConfig,
|
||||
firmwareVersion = "1.3.2",
|
||||
enabled = true,
|
||||
onPositiveClicked = {},
|
||||
)
|
||||
}
|
||||
|
|
@ -26,14 +26,12 @@ import androidx.compose.material3.Icon
|
|||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
import org.meshtastic.core.resources.Res
|
||||
import org.meshtastic.core.resources.delete
|
||||
import org.meshtastic.core.ui.component.ChannelItem
|
||||
import org.meshtastic.core.ui.component.SecurityIcon
|
||||
import org.meshtastic.core.ui.theme.AppTheme
|
||||
import org.meshtastic.proto.ChannelSettings
|
||||
import org.meshtastic.proto.Config
|
||||
|
||||
|
|
@ -79,20 +77,3 @@ internal fun ChannelCard(
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun ChannelCardPreview() {
|
||||
AppTheme {
|
||||
ChannelCard(
|
||||
index = 0,
|
||||
title = "Medium Fast",
|
||||
enabled = true,
|
||||
channelSettings = ChannelSettings(uplink_enabled = true, downlink_enabled = true),
|
||||
loraConfig = Config.LoRaConfig(),
|
||||
onEditClick = {},
|
||||
onDeleteClick = {},
|
||||
sharesLocation = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -24,7 +24,6 @@ import androidx.compose.material3.Text
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.sp
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
import org.meshtastic.core.resources.Res
|
||||
|
|
@ -32,7 +31,6 @@ import org.meshtastic.core.resources.channels
|
|||
import org.meshtastic.core.resources.freq
|
||||
import org.meshtastic.core.resources.slot
|
||||
import org.meshtastic.core.ui.component.PreferenceCategory
|
||||
import org.meshtastic.core.ui.theme.AppTheme
|
||||
|
||||
@Composable
|
||||
internal fun ChannelConfigHeader(frequency: Float, slot: Int) {
|
||||
|
|
@ -48,9 +46,3 @@ internal fun ChannelConfigHeader(frequency: Float, slot: Int) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun ChannelConfigHeaderPreview() {
|
||||
AppTheme { ChannelConfigHeader(frequency = 913.125f, slot = 45) }
|
||||
}
|
||||
|
|
@ -37,7 +37,6 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import org.jetbrains.compose.resources.StringResource
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
|
|
@ -169,9 +168,3 @@ private fun IconDefinitions() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun PreviewChannelLegendDialog() {
|
||||
ChannelLegendDialog(capabilities = Capabilities("2.6.10")) {}
|
||||
}
|
||||
|
|
@ -30,7 +30,6 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
import org.meshtastic.core.model.Channel
|
||||
|
|
@ -142,13 +141,3 @@ fun EditChannelDialog(
|
|||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun EditChannelDialogPreview() {
|
||||
EditChannelDialog(
|
||||
channelSettings = ChannelSettings(psk = Channel.default.settings.psk, name = Channel.default.name),
|
||||
onAddClick = {},
|
||||
onDismissRequest = {},
|
||||
)
|
||||
}
|
||||
|
|
@ -24,7 +24,6 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.runtime.mutableStateMapOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import org.jetbrains.compose.resources.StringResource
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
|
|
@ -130,14 +129,3 @@ fun EditDeviceProfileDialog(
|
|||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun EditDeviceProfileDialogPreview() {
|
||||
EditDeviceProfileDialog(
|
||||
title = "Export configuration",
|
||||
deviceProfile = DeviceProfile(),
|
||||
onConfirm = {},
|
||||
onDismiss = {},
|
||||
)
|
||||
}
|
||||
|
|
@ -34,7 +34,6 @@ import androidx.compose.runtime.saveable.rememberSaveable
|
|||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
import org.meshtastic.core.model.util.DistanceUnit
|
||||
|
|
@ -143,7 +142,6 @@ fun MapReportingPreference(
|
|||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
fun MapReportingPreview() {
|
||||
MapReportingPreference(
|
||||
|
|
@ -16,7 +16,6 @@
|
|||
*/
|
||||
package org.meshtastic.feature.settings.radio.component
|
||||
|
||||
import androidx.activity.compose.LocalOnBackPressedDispatcherOwner
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
|
|
@ -37,7 +36,6 @@ import androidx.compose.runtime.getValue
|
|||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import kotlinx.coroutines.delay
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
|
|
@ -54,13 +52,17 @@ private const val AUTO_DISMISS_DELAY_MS = 1500L
|
|||
|
||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||
@Composable
|
||||
fun <T> PacketResponseStateDialog(state: ResponseState<T>, onDismiss: () -> Unit = {}, onComplete: () -> Unit = {}) {
|
||||
val backDispatcher = LocalOnBackPressedDispatcherOwner.current?.onBackPressedDispatcher
|
||||
fun <T> PacketResponseStateDialog(
|
||||
state: ResponseState<T>,
|
||||
onDismiss: () -> Unit = {},
|
||||
onComplete: () -> Unit = {},
|
||||
onBack: () -> Unit = {},
|
||||
) {
|
||||
LaunchedEffect(state) {
|
||||
if (state is ResponseState.Success) {
|
||||
delay(AUTO_DISMISS_DELAY_MS)
|
||||
onDismiss()
|
||||
backDispatcher?.onBackPressed()
|
||||
onBack()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -93,7 +95,7 @@ fun <T> PacketResponseStateDialog(state: ResponseState<T>, onDismiss: () -> Unit
|
|||
if (state !is ResponseState.Loading) {
|
||||
{
|
||||
onDismiss()
|
||||
backDispatcher?.onBackPressed()
|
||||
onBack()
|
||||
}
|
||||
} else {
|
||||
null
|
||||
|
|
@ -176,23 +178,3 @@ private fun ErrorContent(state: ResponseState.Error) {
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun PacketResponseStateDialogLoadingPreview() {
|
||||
PacketResponseStateDialog(state = ResponseState.Loading(total = 17, completed = 5))
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun PacketResponseStateDialogSuccessPreview() {
|
||||
PacketResponseStateDialog(state = ResponseState.Success(Unit))
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun PacketResponseStateDialogErrorPreview() {
|
||||
PacketResponseStateDialog(
|
||||
state = ResponseState.Error(org.meshtastic.core.resources.UiText.DynamicString("Failed to send packet")),
|
||||
)
|
||||
}
|
||||
|
|
@ -30,7 +30,6 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
import org.meshtastic.core.model.Node
|
||||
|
|
@ -40,8 +39,6 @@ import org.meshtastic.core.resources.send
|
|||
import org.meshtastic.core.resources.shutdown_node_name
|
||||
import org.meshtastic.core.resources.shutdown_warning
|
||||
import org.meshtastic.core.ui.component.MeshtasticDialog
|
||||
import org.meshtastic.core.ui.theme.AppTheme
|
||||
import org.meshtastic.proto.User
|
||||
|
||||
@Composable
|
||||
fun ShutdownConfirmationDialog(
|
||||
|
|
@ -91,11 +88,3 @@ private fun ShutdownDialogContent(nodeLongName: String, isShutdown: Boolean) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun ShutdownConfirmationDialogPreview() {
|
||||
val mockNode = Node(num = 123, user = User(long_name = "Rooftop Router Node", short_name = "ROOF"))
|
||||
|
||||
AppTheme { ShutdownConfirmationDialog(title = "Shutdown?", node = mockNode, onDismiss = {}, onConfirm = {}) }
|
||||
}
|
||||
|
|
@ -20,13 +20,11 @@ import androidx.compose.material.icons.Icons
|
|||
import androidx.compose.material.icons.rounded.Warning
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
import org.meshtastic.core.resources.Res
|
||||
import org.meshtastic.core.resources.cancel
|
||||
import org.meshtastic.core.resources.send
|
||||
import org.meshtastic.core.ui.component.MeshtasticDialog
|
||||
import org.meshtastic.core.ui.theme.AppTheme
|
||||
|
||||
@Composable
|
||||
fun WarningDialog(
|
||||
|
|
@ -49,9 +47,3 @@ fun WarningDialog(
|
|||
dismissText = stringResource(Res.string.cancel),
|
||||
)
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun WarningDialogPreview() {
|
||||
AppTheme { WarningDialog(title = "Factory Reset?", onDismiss = {}, onConfirm = {}) }
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue