summaryrefslogtreecommitdiffhomepage
path: root/android/app/src
diff options
context:
space:
mode:
Diffstat (limited to 'android/app/src')
-rw-r--r--android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/DnsDialogTest.kt13
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/DnsDialog.kt54
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/DnsDialogViewModel.kt102
3 files changed, 61 insertions, 108 deletions
diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/DnsDialogTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/DnsDialogTest.kt
index 5a2d1e7e3b..0882c1dd42 100644
--- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/DnsDialogTest.kt
+++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/DnsDialogTest.kt
@@ -8,6 +8,7 @@ import androidx.compose.ui.test.onNodeWithText
import net.mullvad.mullvadvpn.compose.createEdgeToEdgeComposeExtension
import net.mullvad.mullvadvpn.compose.setContentWithTheme
import net.mullvad.mullvadvpn.viewmodel.DnsDialogViewState
+import net.mullvad.mullvadvpn.viewmodel.ValidationError
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.RegisterExtension
@@ -19,8 +20,8 @@ class DnsDialogTest {
private val defaultState =
DnsDialogViewState(
- ipAddress = "",
- validationResult = DnsDialogViewState.ValidationResult.Success,
+ input = "",
+ validationError = null,
isLocal = false,
isAllowLanEnabled = false,
index = null
@@ -93,8 +94,8 @@ class DnsDialogTest {
setContentWithTheme {
testDnsDialog(
defaultState.copy(
- ipAddress = invalidIpAddress,
- validationResult = DnsDialogViewState.ValidationResult.InvalidAddress,
+ input = invalidIpAddress,
+ validationError = ValidationError.InvalidAddress,
)
)
}
@@ -110,8 +111,8 @@ class DnsDialogTest {
setContentWithTheme {
testDnsDialog(
defaultState.copy(
- ipAddress = "192.168.0.1",
- validationResult = DnsDialogViewState.ValidationResult.DuplicateAddress,
+ input = "192.168.0.1",
+ validationError = ValidationError.DuplicateAddress,
)
)
}
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/DnsDialog.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/DnsDialog.kt
index 163d19f4b5..30f1c71486 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/DnsDialog.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/DnsDialog.kt
@@ -28,67 +28,26 @@ import net.mullvad.mullvadvpn.lib.theme.Dimens
import net.mullvad.mullvadvpn.viewmodel.DnsDialogSideEffect
import net.mullvad.mullvadvpn.viewmodel.DnsDialogViewModel
import net.mullvad.mullvadvpn.viewmodel.DnsDialogViewState
+import net.mullvad.mullvadvpn.viewmodel.ValidationError
import org.koin.androidx.compose.koinViewModel
import org.koin.core.parameter.parametersOf
@Preview
@Composable
private fun PreviewDnsDialogNew() {
- AppTheme {
- DnsDialog(
- DnsDialogViewState(
- "1.1.1.1",
- DnsDialogViewState.ValidationResult.Success,
- false,
- false,
- null
- ),
- {},
- {},
- {},
- {}
- )
- }
+ AppTheme { DnsDialog(DnsDialogViewState("1.1.1.1", null, false, false, null), {}, {}, {}, {}) }
}
@Preview
@Composable
private fun PreviewDnsDialogEdit() {
- AppTheme {
- DnsDialog(
- DnsDialogViewState(
- "1.1.1.1",
- DnsDialogViewState.ValidationResult.Success,
- false,
- false,
- 0
- ),
- {},
- {},
- {},
- {}
- )
- }
+ AppTheme { DnsDialog(DnsDialogViewState("1.1.1.1", null, false, false, 0), {}, {}, {}, {}) }
}
@Preview
@Composable
private fun PreviewDnsDialogEditAllowLanDisabled() {
- AppTheme {
- DnsDialog(
- DnsDialogViewState(
- "192.168.1.1",
- DnsDialogViewState.ValidationResult.Success,
- true,
- false,
- 0
- ),
- {},
- {},
- {},
- {}
- )
- }
+ AppTheme { DnsDialog(DnsDialogViewState("192.168.1.1", null, true, false, 0), {}, {}, {}, {}) }
}
@Destination(style = DestinationStyle.Dialog::class)
@@ -143,7 +102,7 @@ fun DnsDialog(
text = {
Column {
DnsTextField(
- value = state.ipAddress,
+ value = state.input,
isValidValue = state.isValid(),
onValueChanged = { newDnsValue -> onDnsInputChange(newDnsValue) },
onSubmit = onSaveDnsClick,
@@ -154,8 +113,7 @@ fun DnsDialog(
val errorMessage =
when {
- state.validationResult is
- DnsDialogViewState.ValidationResult.DuplicateAddress -> {
+ state.validationError is ValidationError.DuplicateAddress -> {
stringResource(R.string.duplicate_address_warning)
}
state.isLocal && !state.isAllowLanEnabled -> {
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/DnsDialogViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/DnsDialogViewModel.kt
index 4b3a6d5d6b..5df10ef0b1 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/DnsDialogViewModel.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/DnsDialogViewModel.kt
@@ -2,6 +2,9 @@ package net.mullvad.mullvadvpn.viewmodel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
+import arrow.core.Either
+import arrow.core.raise.either
+import arrow.core.raise.ensure
import java.net.InetAddress
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
@@ -11,7 +14,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.map
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
@@ -25,88 +27,77 @@ sealed interface DnsDialogSideEffect {
data object Error : DnsDialogSideEffect
}
-data class DnsDialogViewModelState(
- val customDnsList: List<InetAddress>,
- val isAllowLanEnabled: Boolean
-) {
- companion object {
- fun default() = DnsDialogViewModelState(emptyList(), false)
- }
-}
-
data class DnsDialogViewState(
- val ipAddress: String,
- val validationResult: ValidationResult = ValidationResult.Success,
+ val input: String,
+ val validationError: ValidationError?,
val isLocal: Boolean,
val isAllowLanEnabled: Boolean,
val index: Int?,
) {
val isNewEntry = index == null
- fun isValid() = (validationResult is ValidationResult.Success)
-
- sealed class ValidationResult {
- data object Success : ValidationResult()
+ fun isValid() = validationError == null
+}
- data object InvalidAddress : ValidationResult()
+sealed class ValidationError {
+ data object InvalidAddress : ValidationError()
- data object DuplicateAddress : ValidationResult()
- }
+ data object DuplicateAddress : ValidationError()
}
class DnsDialogViewModel(
private val repository: SettingsRepository,
private val inetAddressValidator: InetAddressValidator,
- private val index: Int? = null,
+ index: Int? = null,
initialValue: String?,
private val dispatcher: CoroutineDispatcher = Dispatchers.IO,
) : ViewModel() {
+ private val currentIndex = MutableStateFlow(index)
private val _ipAddressInput = MutableStateFlow(initialValue ?: EMPTY_STRING)
- private val vmState =
- repository.settingsUpdates
- .filterNotNull()
- .map {
- val customDnsList = it.addresses()
- val isAllowLanEnabled = it.allowLan
- DnsDialogViewModelState(customDnsList, isAllowLanEnabled = isAllowLanEnabled)
- }
- .stateIn(viewModelScope, SharingStarted.Lazily, DnsDialogViewModelState.default())
-
val uiState: StateFlow<DnsDialogViewState> =
- combine(_ipAddressInput, vmState, ::createViewState)
+ combine(_ipAddressInput, currentIndex, repository.settingsUpdates.filterNotNull()) {
+ input,
+ currentIndex,
+ settings ->
+ createViewState(settings.addresses(), currentIndex, settings.allowLan, input)
+ }
.stateIn(
viewModelScope,
SharingStarted.Lazily,
- createViewState(_ipAddressInput.value, vmState.value)
+ createViewState(emptyList(), null, false, _ipAddressInput.value)
)
private val _uiSideEffect = Channel<DnsDialogSideEffect>()
val uiSideEffect = _uiSideEffect.receiveAsFlow()
- private fun createViewState(ipAddress: String, vmState: DnsDialogViewModelState) =
+ private fun createViewState(
+ customDnsList: List<InetAddress>,
+ currentIndex: Int?,
+ isAllowLanEnabled: Boolean,
+ input: String
+ ): DnsDialogViewState =
DnsDialogViewState(
- ipAddress,
- ipAddress.validateDnsEntry(index, vmState.customDnsList),
- ipAddress.isLocalAddress(),
- isAllowLanEnabled = vmState.isAllowLanEnabled,
- index
+ input,
+ input.validateDnsEntry(currentIndex, customDnsList).leftOrNull(),
+ input.isLocalAddress(),
+ isAllowLanEnabled = isAllowLanEnabled,
+ currentIndex
)
private fun String.validateDnsEntry(
index: Int?,
dnsList: List<InetAddress>
- ): DnsDialogViewState.ValidationResult =
- when {
- this.isBlank() || !this.isValidIp() -> {
- DnsDialogViewState.ValidationResult.InvalidAddress
- }
- InetAddress.getByName(this).isDuplicateDnsEntry(index, dnsList) -> {
- DnsDialogViewState.ValidationResult.DuplicateAddress
- }
- else -> DnsDialogViewState.ValidationResult.Success
+ ): Either<ValidationError, InetAddress> = either {
+ ensure(isNotBlank()) { ValidationError.InvalidAddress }
+ ensure(isValidIp()) { ValidationError.InvalidAddress }
+ val inetAddress = InetAddress.getByName(this@validateDnsEntry)
+ ensure(!inetAddress.isDuplicateDnsEntry(index, dnsList)) {
+ ValidationError.DuplicateAddress
}
+ inetAddress
+ }
fun onDnsInputChange(ipAddress: String) {
_ipAddressInput.value = ipAddress
@@ -116,17 +107,20 @@ class DnsDialogViewModel(
viewModelScope.launch(dispatcher) {
if (!uiState.value.isValid()) return@launch
- val address = InetAddress.getByName(uiState.value.ipAddress)
+ val address = InetAddress.getByName(uiState.value.input)
- if (index != null) {
+ val index = uiState.value.index
+ val result =
+ if (index != null) {
repository.setCustomDns(index = index, address = address)
} else {
- repository.addCustomDns(address = address)
+ repository.addCustomDns(address = address).onRight { currentIndex.value = it }
}
- .fold(
- { _uiSideEffect.send(DnsDialogSideEffect.Error) },
- { _uiSideEffect.send(DnsDialogSideEffect.Complete) }
- )
+
+ result.fold(
+ { _uiSideEffect.send(DnsDialogSideEffect.Error) },
+ { _uiSideEffect.send(DnsDialogSideEffect.Complete) }
+ )
}
fun onRemoveDnsClick(index: Int) =