Skip to content

Commit

Permalink
Support new location api
Browse files Browse the repository at this point in the history
  • Loading branch information
Rawa committed Dec 13, 2023
1 parent 674c839 commit a4673cd
Show file tree
Hide file tree
Showing 27 changed files with 62 additions and 376 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import net.mullvad.talpid.tunnel.ErrorStateCause
private fun PreviewConnectionStatusText() {
AppTheme {
SpacedColumn {
ConnectionStatusText(TunnelState.Disconnected)
ConnectionStatusText(TunnelState.Disconnected())
ConnectionStatusText(TunnelState.Connecting(null, null))
ConnectionStatusText(
state = TunnelState.Error(ErrorState(ErrorStateCause.Ipv6Unavailable, true))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableLongStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
Expand Down Expand Up @@ -77,7 +78,6 @@ fun ConnectScreen(
onConnectClick: () -> Unit = {},
onCancelClick: () -> Unit = {},
onSwitchLocationClick: () -> Unit = {},
onToggleTunnelInfo: () -> Unit = {},
onUpdateVersionClick: () -> Unit = {},
onManageAccountClick: () -> Unit = {},
onOpenOutOfTimeScreen: () -> Unit = {},
Expand Down Expand Up @@ -202,12 +202,13 @@ fun ConnectScreen(
color = MaterialTheme.colorScheme.onPrimary,
modifier = Modifier.padding(horizontal = Dimens.sideMargin)
)
var expanded by remember { mutableStateOf(false) }
LocationInfo(
onToggleTunnelInfo = onToggleTunnelInfo,
onToggleTunnelInfo = { expanded = !expanded },
isVisible =
uiState.tunnelRealState != TunnelState.Disconnected &&
uiState.tunnelRealState !is TunnelState.Disconnected &&
uiState.location?.hostname != null,
isExpanded = uiState.isTunnelInfoExpanded,
isExpanded = expanded,
location = uiState.location,
inAddress = uiState.inAddress,
outAddress = uiState.outAddress,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ private fun PreviewOutOfTimeScreenDisconnected() {
AppTheme {
OutOfTimeScreen(
showSitePayment = true,
uiState = OutOfTimeUiState(tunnelState = TunnelState.Disconnected, "Heroic Frog"),
uiState = OutOfTimeUiState(tunnelState = TunnelState.Disconnected(), "Heroic Frog"),
uiSideEffect = MutableSharedFlow<OutOfTimeViewModel.UiSideEffect>().asSharedFlow()
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ data class ConnectUiState(
val outAddress: String,
val showLocation: Boolean,
val inAppNotification: InAppNotification?,
val isTunnelInfoExpanded: Boolean,
val deviceName: String?,
val daysLeftUntilExpiry: Int?
) {
Expand All @@ -24,12 +23,11 @@ data class ConnectUiState(
ConnectUiState(
location = null,
relayLocation = null,
tunnelUiState = TunnelState.Disconnected,
tunnelRealState = TunnelState.Disconnected,
tunnelUiState = TunnelState.Disconnected(),
tunnelRealState = TunnelState.Disconnected(),
inAddress = null,
outAddress = "",
showLocation = false,
isTunnelInfoExpanded = false,
inAppNotification = null,
deviceName = null,
daysLeftUntilExpiry = null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import net.mullvad.mullvadvpn.compose.dialog.payment.PaymentDialogData
import net.mullvad.mullvadvpn.model.TunnelState

data class OutOfTimeUiState(
val tunnelState: TunnelState = TunnelState.Disconnected,
val tunnelState: TunnelState = TunnelState.Disconnected(),
val deviceName: String = "",
val billingPaymentState: PaymentState? = null,
val paymentDialogData: PaymentDialogData? = null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import net.mullvad.mullvadvpn.compose.dialog.payment.PaymentDialogData
import net.mullvad.mullvadvpn.model.TunnelState

data class WelcomeUiState(
val tunnelState: TunnelState = TunnelState.Disconnected,
val tunnelState: TunnelState = TunnelState.Disconnected(),
val accountNumber: String? = null,
val deviceName: String? = null,
val billingPaymentState: PaymentState? = null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ class ConnectFragment : BaseFragment() {
onConnectClick = connectViewModel::onConnectClick,
onCancelClick = connectViewModel::onCancelClick,
onSwitchLocationClick = ::openSwitchLocationScreen,
onToggleTunnelInfo = connectViewModel::toggleTunnelInfoExpansion,
onUpdateVersionClick = { openDownloadUrl() },
onManageAccountClick = connectViewModel::onManageAccountClick,
onOpenOutOfTimeScreen = ::openOutOfTimeScreen,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ const val ANTICIPATED_STATE_TIMEOUT_MS = 1500L
class ConnectionProxy(private val connection: Messenger, eventDispatcher: EventDispatcher) {
private var resetAnticipatedStateJob: Job? = null

val onStateChange = EventNotifier<TunnelState>(TunnelState.Disconnected)
val onUiStateChange = EventNotifier<TunnelState>(TunnelState.Disconnected)
val onStateChange = EventNotifier<TunnelState>(TunnelState.Disconnected())
val onUiStateChange = EventNotifier<TunnelState>(TunnelState.Disconnected())

var state by onStateChange.notifiable()
private set
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ class ServiceConnectionContainer(
val authTokenCache = AuthTokenCache(connection, dispatcher)
val connectionProxy = ConnectionProxy(connection, dispatcher)
val deviceDataSource = ServiceConnectionDeviceDataSource(connection, dispatcher)
val locationInfoCache = LocationInfoCache(dispatcher)
val settingsListener = SettingsListener(connection, dispatcher)

val splitTunneling = SplitTunneling(connection, dispatcher)
Expand Down Expand Up @@ -62,7 +61,6 @@ class ServiceConnectionContainer(

authTokenCache.onDestroy()
connectionProxy.onDestroy()
locationInfoCache.onDestroy()
settingsListener.onDestroy()
voucherRedeemer.onDestroy()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class TunnelStateNotificationUseCase(
}
is TunnelState.Error -> InAppNotification.TunnelStateError(tunnelUiState.errorState)
is TunnelState.Connected,
TunnelState.Disconnected -> null
is TunnelState.Disconnected -> null
}

private fun ConnectionProxy.tunnelUiStateFlow(): Flow<TunnelState> =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,30 @@ package net.mullvad.mullvadvpn.viewmodel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import net.mullvad.mullvadvpn.compose.state.ConnectUiState
import net.mullvad.mullvadvpn.model.GeoIpLocation
import net.mullvad.mullvadvpn.model.TunnelState
import net.mullvad.mullvadvpn.repository.AccountRepository
import net.mullvad.mullvadvpn.repository.DeviceRepository
import net.mullvad.mullvadvpn.repository.InAppNotificationController
import net.mullvad.mullvadvpn.ui.serviceconnection.ConnectionProxy
import net.mullvad.mullvadvpn.ui.serviceconnection.LocationInfoCache
import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionContainer
import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionManager
import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionState
Expand Down Expand Up @@ -67,51 +67,48 @@ class ConnectViewModel(
}
.shareIn(viewModelScope, SharingStarted.WhileSubscribed())

private val _isTunnelInfoExpanded = MutableStateFlow(false)

val uiState: StateFlow<ConnectUiState> =
_shared
.flatMapLatest { serviceConnection ->
combine(
serviceConnection.locationInfoCache.locationCallbackFlow(),
relayListUseCase.selectedRelayItem(),
inAppNotificationController.notifications,
serviceConnection.connectionProxy.tunnelUiStateFlow(),
serviceConnection.connectionProxy.tunnelRealStateFlow(),
serviceConnection.connectionProxy.lastKnownLocation(),
accountRepository.accountExpiryState,
_isTunnelInfoExpanded,
deviceRepository.deviceState.map { it.deviceName() }
) {
location,
relayLocation,
notifications,
tunnelUiState,
tunnelRealState,
lastKnownLocation,
accountExpiry,
isTunnelInfoExpanded,
deviceName ->
if (tunnelRealState.isTunnelErrorStateDueToExpiredAccount()) {
_uiSideEffect.tryEmit(UiSideEffect.OpenOutOfTimeView)
}
ConnectUiState(
location =
when (tunnelRealState) {
is TunnelState.Connected -> tunnelRealState.location
is TunnelState.Disconnected -> tunnelRealState.location()
?: lastKnownLocation
is TunnelState.Connecting -> tunnelRealState.location
else -> null
}
?: location,
is TunnelState.Connected -> tunnelRealState.location
is TunnelState.Disconnecting -> lastKnownLocation
is TunnelState.Error -> lastKnownLocation
},
relayLocation = relayLocation,
tunnelUiState = tunnelUiState,
tunnelRealState = tunnelRealState,
isTunnelInfoExpanded = isTunnelInfoExpanded,
inAddress =
when (tunnelRealState) {
is TunnelState.Connected -> tunnelRealState.endpoint.toInAddress()
is TunnelState.Connecting -> tunnelRealState.endpoint?.toInAddress()
else -> null
},
outAddress = location?.toOutAddress() ?: "",
outAddress = tunnelRealState.location()?.toOutAddress() ?: "",
showLocation =
when (tunnelUiState) {
is TunnelState.Disconnected -> true
Expand Down Expand Up @@ -144,27 +141,25 @@ class ConnectViewModel(
}
}

private fun LocationInfoCache.locationCallbackFlow() = callbackFlow {
onNewLocation = { this.trySend(it) }
awaitClose { onNewLocation = null }
}

private fun ConnectionProxy.tunnelUiStateFlow(): Flow<TunnelState> =
callbackFlowFromNotifier(this.onUiStateChange)

private fun ConnectionProxy.tunnelRealStateFlow(): Flow<TunnelState> =
callbackFlowFromNotifier(this.onStateChange)

private fun ConnectionProxy.lastKnownLocation(): Flow<GeoIpLocation?> =
tunnelRealStateFlow()
.filterIsInstance<TunnelState.Disconnected>()
.filter { it.location == null }
.map { it.location }
.onStart { emit(null) }

private fun TunnelState.isTunnelErrorStateDueToExpiredAccount(): Boolean {
return ((this as? TunnelState.Error)?.errorState?.cause as? ErrorStateCause.AuthFailed)
?.isCausedByExpiredAccount()
?: false
}

fun toggleTunnelInfoExpansion() {
_isTunnelInfoExpanded.value = _isTunnelInfoExpanded.value.not()
}

fun onDisconnectClick() {
serviceConnectionManager.connectionProxy()?.disconnect()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class TunnelStateNotificationUseCaseTest {
MutableStateFlow<ServiceConnectionState>(ServiceConnectionState.Disconnected)
private lateinit var tunnelStateNotificationUseCase: TunnelStateNotificationUseCase

private val eventNotifierTunnelUiState = EventNotifier<TunnelState>(TunnelState.Disconnected)
private val eventNotifierTunnelUiState = EventNotifier<TunnelState>(TunnelState.Disconnected())

@Before
fun setup() {
Expand Down
Loading

0 comments on commit a4673cd

Please sign in to comment.