Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve DnsDialogViewModel nav arguments #7053

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,7 @@ class VpnSettingsScreenTest {
fun testClickAddDns() =
composeExtension.use {
// Arrange
val mockedClickHandler: (Int?, String?) -> Unit = mockk(relaxed = true)
val mockedClickHandler: (Int?) -> Unit = mockk(relaxed = true)
setContentWithTheme {
VpnSettingsScreen(
state = VpnSettingsUiState.createDefault(isCustomDnsEnabled = true),
Expand All @@ -390,7 +390,7 @@ class VpnSettingsScreenTest {
onNodeWithText("Add a server").performClick()

// Assert
verify { mockedClickHandler.invoke(null, null) }
verify { mockedClickHandler.invoke(null) }
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ private fun PreviewDnsDialogEditAllowLanDisabled() {
AppTheme { DnsDialog(DnsDialogViewState("192.168.1.1", null, true, false, 0), {}, {}, {}, {}) }
}

data class DnsDialogNavArgs(val index: Int? = null, val initialValue: String? = null)
data class DnsDialogNavArgs(val index: Int? = null)

@Destination<RootGraph>(style = DestinationStyle.Dialog::class, navArgs = DnsDialogNavArgs::class)
@Composable
Expand All @@ -57,7 +57,8 @@ fun Dns(resultNavigator: ResultBackNavigator<DnsDialogResult>) {
resultNavigator.navigateBack(result = DnsDialogResult.Error)
}
}
val state by viewModel.uiState.collectAsStateWithLifecycle()
val stateLc by viewModel.uiState.collectAsStateWithLifecycle()
val state = stateLc.content() ?: return

DnsDialog(
state,
Expand All @@ -69,6 +70,8 @@ fun Dns(resultNavigator: ResultBackNavigator<DnsDialogResult>) {
)
}

// 4490997557103082

@Composable
fun DnsDialog(
state: DnsDialogViewState,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ private fun PreviewVpnSettings(
onToggleBlockGambling = {},
onToggleBlockSocialMedia = {},
navigateToMtuDialog = {},
navigateToDns = { _, _ -> },
navigateToDns = { _ -> },
onToggleDnsClick = {},
onBackClick = {},
onSelectObfuscationMode = {},
Expand Down Expand Up @@ -184,7 +184,7 @@ fun VpnSettings(
is VpnSettingsSideEffect.ShowToast ->
launch { snackbarHostState.showSnackbarImmediately(message = it.message(context)) }
VpnSettingsSideEffect.NavigateToDnsDialog ->
navigator.navigate(DnsDestination(null, null)) { launchSingleTop = true }
navigator.navigate(DnsDestination(null)) { launchSingleTop = true }
}
}

Expand Down Expand Up @@ -239,9 +239,7 @@ fun VpnSettings(
navigateToMtuDialog =
dropUnlessResumed { mtu: Mtu? -> navigator.navigate(MtuDestination(mtu)) },
navigateToDns =
dropUnlessResumed { index: Int?, address: String? ->
navigator.navigate(DnsDestination(index, address))
},
dropUnlessResumed { index: Int? -> navigator.navigate(DnsDestination(index)) },
navigateToWireguardPortDialog =
dropUnlessResumed {
navigator.navigate(
Expand Down Expand Up @@ -293,7 +291,7 @@ fun VpnSettingsScreen(
onToggleBlockGambling: (Boolean) -> Unit = {},
onToggleBlockSocialMedia: (Boolean) -> Unit = {},
navigateToMtuDialog: (mtu: Mtu?) -> Unit = {},
navigateToDns: (index: Int?, address: String?) -> Unit = { _, _ -> },
navigateToDns: (index: Int?) -> Unit = { _ -> },
onToggleDnsClick: (Boolean) -> Unit = {},
onBackClick: () -> Unit = {},
onSelectObfuscationMode: (obfuscationMode: ObfuscationMode) -> Unit = {},
Expand Down Expand Up @@ -468,14 +466,14 @@ fun VpnSettingsScreen(
address = item.address,
isUnreachableLocalDnsWarningVisible =
item.isLocal && !state.isLocalNetworkSharingEnabled,
onClick = { navigateToDns(index, item.address) },
onClick = { navigateToDns(index) },
modifier = Modifier.animateItem(),
)
}

itemWithDivider {
BaseCell(
onCellClicked = { navigateToDns(null, null) },
onCellClicked = { navigateToDns(null) },
headlineContent = {
Text(
text = stringResource(id = R.string.add_a_server),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
Expand All @@ -30,6 +29,33 @@ sealed interface DnsDialogSideEffect {
data object Error : DnsDialogSideEffect
}

sealed interface Lce<out T, out E> {
data object Loading : Lce<Nothing, Nothing>

data class Content<T>(val value: T) : Lce<T, Nothing>

data class Error<E>(val error: E) : Lce<E, Nothing>
}

fun <T, E> T.toLce(): Lce<T, E> = Lce.Content(this)

sealed interface Lc<out T> {
data object Loading : Lc<Nothing>

data object Empty : Lc<Nothing>

data class Content<T>(val value: T) : Lc<T>

fun content(): T? =
when (this) {
is Loading,
Empty -> null
is Content -> value
}
}

fun <T> T.toLc(): Lc<T> = Lc.Content(this)

data class DnsDialogViewState(
val input: String,
val validationError: ValidationError?,
Expand All @@ -56,43 +82,35 @@ class DnsDialogViewModel(
) : ViewModel() {
private val navArgs = DnsDestination.argsFrom(savedStateHandle)

private val settings = MutableStateFlow<Settings?>(null)
private val currentIndex = MutableStateFlow(navArgs.index)
private val _ipAddressInput = MutableStateFlow(navArgs.initialValue ?: EMPTY_STRING)
private val ipAddressInput = MutableStateFlow(EMPTY_STRING)

val uiState: StateFlow<DnsDialogViewState> =
combine(_ipAddressInput, currentIndex, settings.filterNotNull()) {
input,
val uiState: StateFlow<Lc<DnsDialogViewState>> =
combine(currentIndex, ipAddressInput, repository.settingsUpdates.filterNotNull()) {
currentIndex,
input,
settings ->
createViewState(settings.addresses(), currentIndex, settings.allowLan, input)
}
.stateIn(
viewModelScope,
SharingStarted.Lazily,
createViewState(emptyList(), null, false, _ipAddressInput.value),
)
.stateIn(viewModelScope, SharingStarted.Lazily, Lc.Loading)

private val _uiSideEffect = Channel<DnsDialogSideEffect>()
val uiSideEffect = _uiSideEffect.receiveAsFlow()

init {
viewModelScope.launch { settings.emit(repository.settingsUpdates.filterNotNull().first()) }
}

private fun createViewState(
customDnsList: List<InetAddress>,
currentIndex: Int?,
isAllowLanEnabled: Boolean,
input: String,
): DnsDialogViewState =
): Lc<DnsDialogViewState> =
DnsDialogViewState(
input,
input.validateDnsEntry(currentIndex, customDnsList).leftOrNull(),
input.isLocalAddress(),
isAllowLanEnabled = isAllowLanEnabled,
currentIndex,
)
input,
input.validateDnsEntry(currentIndex, customDnsList).leftOrNull(),
input.isLocalAddress(),
isAllowLanEnabled = isAllowLanEnabled,
currentIndex,
)
.toLc()

private fun String.validateDnsEntry(
index: Int?,
Expand All @@ -108,16 +126,17 @@ class DnsDialogViewModel(
}

fun onDnsInputChange(ipAddress: String) {
_ipAddressInput.value = ipAddress
ipAddressInput.value = ipAddress
}

fun onSaveDnsClick() =
viewModelScope.launch(dispatcher) {
if (!uiState.value.isValid()) return@launch
val state = uiState.value.content()
if (state == null || !state.isValid()) return@launch

val address = InetAddress.getByName(uiState.value.input)
val address = InetAddress.getByName(state.input)

val index = uiState.value.index
val index = state.index
val result =
if (index != null) {
repository.setCustomDns(index = index, address = address)
Expand Down
Loading