Skip to content

Commit

Permalink
Add daita confirmation dialog and address feedback
Browse files Browse the repository at this point in the history
  • Loading branch information
albin-mullvad committed Aug 28, 2024
1 parent abe1286 commit b5e5006
Show file tree
Hide file tree
Showing 13 changed files with 160 additions and 56 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package net.mullvad.mullvadvpn.compose.dialog

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.lifecycle.compose.dropUnlessResumed
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.annotation.RootGraph
import com.ramcosta.composedestinations.result.EmptyResultBackNavigator
import com.ramcosta.composedestinations.result.ResultBackNavigator
import com.ramcosta.composedestinations.spec.DestinationStyle
import net.mullvad.mullvadvpn.R
import net.mullvad.mullvadvpn.compose.button.PrimaryButton
import net.mullvad.mullvadvpn.compose.component.drawVerticalScrollbar
import net.mullvad.mullvadvpn.lib.theme.AppTheme
import net.mullvad.mullvadvpn.lib.theme.Dimens
import net.mullvad.mullvadvpn.lib.theme.color.AlphaScrollbar

@Preview
@Composable
private fun PreviewDaitaConfirmationDialog() {
AppTheme { DaitaConfirmationDialog(EmptyResultBackNavigator()) }
}

@Destination<RootGraph>(style = DestinationStyle.Dialog::class)
@Composable
fun DaitaConfirmationDialog(navigator: ResultBackNavigator<Boolean>) {
AlertDialog(
onDismissRequest = dropUnlessResumed { navigator.navigateBack(false) },
icon = {
Icon(
modifier = Modifier.fillMaxWidth().height(Dimens.dialogIconHeight),
painter = painterResource(id = R.drawable.icon_alert),
contentDescription = "",
tint = MaterialTheme.colorScheme.onSurface
)
},
text = {
val scrollState = rememberScrollState()
Column(
Modifier.drawVerticalScrollbar(
scrollState,
MaterialTheme.colorScheme.onPrimary.copy(alpha = AlphaScrollbar)
)
.verticalScroll(scrollState),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(
text = stringResource(id = R.string.daita_relay_subset_warning),
color = MaterialTheme.colorScheme.onSurface,
style = MaterialTheme.typography.bodySmall,
modifier = Modifier.fillMaxWidth(),
)

Spacer(modifier = Modifier.height(Dimens.verticalSpace))

Text(
text =
stringResource(
id = R.string.daita_warning,
stringResource(
id = R.string.daita,
),
),
color = MaterialTheme.colorScheme.onSurface,
style = MaterialTheme.typography.bodySmall,
modifier = Modifier.fillMaxWidth(),
)
}
},
confirmButton = {
Column(verticalArrangement = Arrangement.spacedBy(Dimens.buttonSpacing)) {
PrimaryButton(
modifier = Modifier.fillMaxWidth(),
text = stringResource(R.string.enable_anyway),
onClick = { navigator.navigateBack(true) },
)

PrimaryButton(
modifier = Modifier.fillMaxWidth(),
text = stringResource(R.string.back),
onClick = dropUnlessResumed { navigator.navigateBack(false) },
)
}
},
containerColor = MaterialTheme.colorScheme.surface,
titleContentColor = MaterialTheme.colorScheme.onSurface,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import com.ramcosta.composedestinations.annotation.RootGraph
import com.ramcosta.composedestinations.generated.destinations.AutoConnectAndLockdownModeDestination
import com.ramcosta.composedestinations.generated.destinations.ContentBlockersInfoDestination
import com.ramcosta.composedestinations.generated.destinations.CustomDnsInfoDestination
import com.ramcosta.composedestinations.generated.destinations.DaitaConfirmationDialogDestination
import com.ramcosta.composedestinations.generated.destinations.DaitaInfoDestination
import com.ramcosta.composedestinations.generated.destinations.DnsDestination
import com.ramcosta.composedestinations.generated.destinations.LocalNetworkSharingInfoDestination
Expand Down Expand Up @@ -148,6 +149,7 @@ fun VpnSettings(
dnsDialogResult: ResultRecipient<DnsDestination, DnsDialogResult>,
customWgPortResult: ResultRecipient<WireguardCustomPortDestination, Port?>,
mtuDialogResult: ResultRecipient<MtuDestination, Boolean>,
daitaConfirmationDialogResult: ResultRecipient<DaitaConfirmationDialogDestination, Boolean>,
) {
val vm = koinViewModel<VpnSettingsViewModel>()
val state by vm.uiState.collectAsStateWithLifecycle()
Expand Down Expand Up @@ -177,6 +179,12 @@ fun VpnSettings(
}
}

daitaConfirmationDialogResult.OnNavResultValue { doEnableDaita ->
if (doEnableDaita) {
vm.onToggleDaita(true)
}
}

val snackbarHostState = remember { SnackbarHostState() }
val context = LocalContext.current
LaunchedEffectCollect(vm.uiSideEffect) {
Expand Down Expand Up @@ -226,14 +234,16 @@ fun VpnSettings(
navigateToLocalNetworkSharingInfo =
dropUnlessResumed { navigator.navigate(LocalNetworkSharingInfoDestination) },
navigateToDaitaInfo = dropUnlessResumed { navigator.navigate(DaitaInfoDestination) },
navigateToDaitaConfirmation =
dropUnlessResumed { navigator.navigate(DaitaConfirmationDialogDestination) },
navigateToServerIpOverrides =
dropUnlessResumed { navigator.navigate(ServerIpOverridesDestination) },
onToggleBlockTrackers = vm::onToggleBlockTrackers,
onToggleBlockAds = vm::onToggleBlockAds,
onToggleBlockMalware = vm::onToggleBlockMalware,
onToggleAutoConnect = vm::onToggleAutoConnect,
onToggleLocalNetworkSharing = vm::onToggleLocalNetworkSharing,
onToggleDaita = vm::onToggleDaita,
onDisableDaita = { vm.onToggleDaita(false) },
onToggleBlockAdultContent = vm::onToggleBlockAdultContent,
onToggleBlockGambling = vm::onToggleBlockGambling,
onToggleBlockSocialMedia = vm::onToggleBlockSocialMedia,
Expand Down Expand Up @@ -277,14 +287,15 @@ fun VpnSettingsScreen(
navigateToWireguardPortInfo: (availablePortRanges: List<PortRange>) -> Unit = {},
navigateToLocalNetworkSharingInfo: () -> Unit = {},
navigateToDaitaInfo: () -> Unit = {},
navigateToDaitaConfirmation: () -> Unit = {},
navigateToWireguardPortDialog: () -> Unit = {},
navigateToServerIpOverrides: () -> Unit = {},
onToggleBlockTrackers: (Boolean) -> Unit = {},
onToggleBlockAds: (Boolean) -> Unit = {},
onToggleBlockMalware: (Boolean) -> Unit = {},
onToggleAutoConnect: (Boolean) -> Unit = {},
onToggleLocalNetworkSharing: (Boolean) -> Unit = {},
onToggleDaita: (Boolean) -> Unit = {},
onDisableDaita: () -> Unit = {},
onToggleBlockAdultContent: (Boolean) -> Unit = {},
onToggleBlockGambling: (Boolean) -> Unit = {},
onToggleBlockSocialMedia: (Boolean) -> Unit = {},
Expand Down Expand Up @@ -505,11 +516,16 @@ fun VpnSettingsScreen(
item {
Spacer(modifier = Modifier.height(Dimens.cellLabelVerticalPadding))
HeaderSwitchComposeCell(
title = "DAITA",
title = stringResource(id = R.string.daita),
isToggled = state.isDaitaEnabled,
isEnabled = true,
onCellClicked = { newValue -> onToggleDaita(newValue) },
onInfoClicked = navigateToDaitaInfo
onCellClicked = { newValueIsEnable ->
if (newValueIsEnable) {
navigateToDaitaConfirmation()
} else {
onDisableDaita()
}
},
onInfoClicked = navigateToDaitaInfo,
)
Spacer(modifier = Modifier.height(Dimens.cellLabelVerticalPadding))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,42 +53,41 @@ private fun RelayItem.Location.hasProvider(providersConstraint: Constraint<Provi
true
}

fun RelayItem.CustomList.applyFilters(
fun RelayItem.CustomList.filter(
ownership: Constraint<Ownership>,
providers: Constraint<Providers>,
isDaitaEnabled: Boolean
): RelayItem.CustomList {
val newLocations =
locations.mapNotNull {
when (it) {
is RelayItem.Location.Country ->
it.applyFilters(ownership, providers, isDaitaEnabled)
is RelayItem.Location.City -> it.applyFilters(ownership, providers, isDaitaEnabled)
is RelayItem.Location.Relay -> it.applyFilters(ownership, providers, isDaitaEnabled)
is RelayItem.Location.Country -> it.filter(ownership, providers, isDaitaEnabled)
is RelayItem.Location.City -> it.filter(ownership, providers, isDaitaEnabled)
is RelayItem.Location.Relay -> it.filter(ownership, providers, isDaitaEnabled)
}
}
return copy(locations = newLocations)
}

fun RelayItem.Location.Country.applyFilters(
fun RelayItem.Location.Country.filter(
ownership: Constraint<Ownership>,
providers: Constraint<Providers>,
isDaitaEnabled: Boolean
): RelayItem.Location.Country? {
val cities = cities.mapNotNull { it.applyFilters(ownership, providers, isDaitaEnabled) }
val cities = cities.mapNotNull { it.filter(ownership, providers, isDaitaEnabled) }
return if (cities.isNotEmpty()) {
this.copy(cities = cities)
} else {
null
}
}

private fun RelayItem.Location.City.applyFilters(
private fun RelayItem.Location.City.filter(
ownership: Constraint<Ownership>,
providers: Constraint<Providers>,
isDaitaEnabled: Boolean
): RelayItem.Location.City? {
val relays = relays.mapNotNull { it.applyFilters(ownership, providers, isDaitaEnabled) }
val relays = relays.mapNotNull { it.filter(ownership, providers, isDaitaEnabled) }
return if (relays.isNotEmpty()) {
this.copy(relays = relays)
} else {
Expand All @@ -100,7 +99,7 @@ private fun RelayItem.Location.Relay.hasMatchingDaitaSetting(isDaitaEnabled: Boo
return if (isDaitaEnabled) daita else true
}

private fun RelayItem.Location.Relay.applyFilters(
private fun RelayItem.Location.Relay.filter(
ownership: Constraint<Ownership>,
providers: Constraint<Providers>,
isDaitaEnabled: Boolean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import kotlinx.coroutines.flow.stateIn
import net.mullvad.mullvadvpn.lib.daemon.grpc.ManagementService
import net.mullvad.mullvadvpn.lib.model.Constraint
import net.mullvad.mullvadvpn.lib.model.CustomDnsOptions
import net.mullvad.mullvadvpn.lib.model.DaitaSettings
import net.mullvad.mullvadvpn.lib.model.DefaultDnsOptions
import net.mullvad.mullvadvpn.lib.model.DnsOptions
import net.mullvad.mullvadvpn.lib.model.DnsState
Expand Down Expand Up @@ -72,6 +71,5 @@ class SettingsRepository(
suspend fun setLocalNetworkSharing(isEnabled: Boolean) =
managementService.setAllowLan(isEnabled)

suspend fun setDaitaSettings(daitaSettings: DaitaSettings) =
managementService.setDaitaSettings(daitaSettings)
suspend fun setDaitaEnabled(enabled: Boolean) = managementService.setDaitaEnabled(enabled)
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import net.mullvad.mullvadvpn.lib.model.Constraint
import net.mullvad.mullvadvpn.lib.model.Ownership
import net.mullvad.mullvadvpn.lib.model.Providers
import net.mullvad.mullvadvpn.lib.model.RelayItem
import net.mullvad.mullvadvpn.relaylist.applyFilters
import net.mullvad.mullvadvpn.relaylist.filter
import net.mullvad.mullvadvpn.repository.RelayListFilterRepository
import net.mullvad.mullvadvpn.repository.RelayListRepository
import net.mullvad.mullvadvpn.repository.SettingsRepository
Expand All @@ -22,16 +22,16 @@ class FilteredRelayListUseCase(
relayListFilterRepository.selectedProviders,
settingsRepository.settingsUpdates
) { relayList, selectedOwnership, selectedProviders, settings ->
relayList.applyFilters(
relayList.filter(
selectedOwnership,
selectedProviders,
isDaitaEnabled = settings?.tunnelOptions?.wireguard?.daita ?: false
isDaitaEnabled = settings?.isDaitaEnabled() ?: false
)
}

private fun List<RelayItem.Location.Country>.applyFilters(
private fun List<RelayItem.Location.Country>.filter(
ownership: Constraint<Ownership>,
providers: Constraint<Providers>,
isDaitaEnabled: Boolean
) = mapNotNull { it.applyFilters(ownership, providers, isDaitaEnabled) }
) = mapNotNull { it.filter(ownership, providers, isDaitaEnabled) }
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import net.mullvad.mullvadvpn.lib.model.Constraint
import net.mullvad.mullvadvpn.lib.model.Ownership
import net.mullvad.mullvadvpn.lib.model.Providers
import net.mullvad.mullvadvpn.lib.model.RelayItem
import net.mullvad.mullvadvpn.relaylist.applyFilters
import net.mullvad.mullvadvpn.relaylist.filter
import net.mullvad.mullvadvpn.repository.RelayListFilterRepository
import net.mullvad.mullvadvpn.repository.SettingsRepository

Expand All @@ -26,13 +26,13 @@ class FilterCustomListsRelayItemUseCase(
customLists.filterOnOwnershipAndProvider(
selectedOwnership,
selectedProviders,
isDaitaEnabled = settings?.tunnelOptions?.wireguard?.daita ?: false
isDaitaEnabled = settings?.isDaitaEnabled() ?: false
)
}

private fun List<RelayItem.CustomList>.filterOnOwnershipAndProvider(
ownership: Constraint<Ownership>,
providers: Constraint<Providers>,
isDaitaEnabled: Boolean
) = mapNotNull { it.applyFilters(ownership, providers, isDaitaEnabled = isDaitaEnabled) }
) = mapNotNull { it.filter(ownership, providers, isDaitaEnabled = isDaitaEnabled) }
}
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ class SelectLocationViewModel(
if (providerCountFilter != null) {
add(FilterChip.Provider(providerCountFilter))
}
if (settings?.tunnelOptions?.wireguard?.daita == true) {
if (settings?.isDaitaEnabled() == true) {
add(FilterChip.Daita)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import net.mullvad.mullvadvpn.compose.state.VpnSettingsUiState
import net.mullvad.mullvadvpn.lib.model.Constraint
import net.mullvad.mullvadvpn.lib.model.DaitaSettings
import net.mullvad.mullvadvpn.lib.model.DefaultDnsOptions
import net.mullvad.mullvadvpn.lib.model.DnsState
import net.mullvad.mullvadvpn.lib.model.Port
Expand Down Expand Up @@ -65,7 +64,7 @@ class VpnSettingsViewModel(
mtuValue = settings?.tunnelOptions?.wireguard?.mtu,
isAutoConnectEnabled = settings?.autoConnect ?: false,
isLocalNetworkSharingEnabled = settings?.allowLan ?: false,
isDaitaEnabled = settings?.tunnelOptions?.wireguard?.daita ?: false,
isDaitaEnabled = settings?.isDaitaEnabled() ?: false,
isCustomDnsEnabled = settings?.isCustomDnsEnabled() ?: false,
customDnsList = settings?.addresses()?.asStringAddressList() ?: listOf(),
contentBlockersOptions =
Expand Down Expand Up @@ -126,11 +125,11 @@ class VpnSettingsViewModel(
}
}

fun onToggleDaita(isEnabled: Boolean) {
fun onToggleDaita(enable: Boolean) {
viewModelScope.launch(dispatcher) {
repository
.setDaitaSettings(if (isEnabled) DaitaSettings.On else DaitaSettings.Off)
.onLeft { _uiSideEffect.send(VpnSettingsSideEffect.ShowToast.GenericError) }
repository.setDaitaEnabled(enable).onLeft {
_uiSideEffect.send(VpnSettingsSideEffect.ShowToast.GenericError)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ import net.mullvad.mullvadvpn.lib.model.CustomList as ModelCustomList
import net.mullvad.mullvadvpn.lib.model.CustomListAlreadyExists
import net.mullvad.mullvadvpn.lib.model.CustomListId
import net.mullvad.mullvadvpn.lib.model.CustomListName
import net.mullvad.mullvadvpn.lib.model.DaitaSettings
import net.mullvad.mullvadvpn.lib.model.DeleteCustomListError
import net.mullvad.mullvadvpn.lib.model.DeleteDeviceError
import net.mullvad.mullvadvpn.lib.model.Device
Expand Down Expand Up @@ -503,10 +502,12 @@ class ManagementService(
.mapLeft(SetAllowLanError::Unknown)
.mapEmpty()

suspend fun setDaitaSettings(
daitaSettings: DaitaSettings
): Either<SetDaitaSettingsError, Unit> =
Either.catch { grpc.setDaitaSettings(daitaSettings.toDomain()) }
suspend fun setDaitaEnabled(enabled: Boolean): Either<SetDaitaSettingsError, Unit> =
Either.catch {
val daitaSettings =
ManagementInterface.DaitaSettings.newBuilder().setEnabled(enabled).build()
grpc.setDaitaSettings(daitaSettings)
}
.mapLeft(SetDaitaSettingsError::Unknown)
.mapEmpty()

Expand Down
Loading

0 comments on commit b5e5006

Please sign in to comment.