Skip to content

Commit c6b1744

Browse files
committed
Revert navigation logic on Welcome to handle bad clocks better
1 parent 5560072 commit c6b1744

File tree

3 files changed

+22
-29
lines changed

3 files changed

+22
-29
lines changed

android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ val uiModule = module {
154154
viewModel { SplashViewModel(get(), get(), get()) }
155155
viewModel { VoucherDialogViewModel(get(), get()) }
156156
viewModel { VpnSettingsViewModel(get(), get(), get(), get(), get()) }
157-
viewModel { WelcomeViewModel(get(), get(), get(), get(), get(), isPlayBuild = IS_PLAY_BUILD) }
157+
viewModel { WelcomeViewModel(get(), get(), get(), get(), isPlayBuild = IS_PLAY_BUILD) }
158158
viewModel { ReportProblemViewModel(get(), get()) }
159159
viewModel { ViewLogsViewModel(get()) }
160160
viewModel { OutOfTimeViewModel(get(), get(), get(), get(), get(), isPlayBuild = IS_PLAY_BUILD) }

android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/WelcomeViewModel.kt

+13-10
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ import kotlinx.coroutines.flow.filter
1414
import kotlinx.coroutines.flow.flatMapLatest
1515
import kotlinx.coroutines.flow.flowOf
1616
import kotlinx.coroutines.flow.map
17+
import kotlinx.coroutines.flow.mapNotNull
1718
import kotlinx.coroutines.flow.merge
19+
import kotlinx.coroutines.flow.onEach
1820
import kotlinx.coroutines.flow.receiveAsFlow
1921
import kotlinx.coroutines.flow.stateIn
2022
import kotlinx.coroutines.launch
@@ -27,7 +29,6 @@ import net.mullvad.mullvadvpn.ui.serviceconnection.ConnectionProxy
2729
import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionManager
2830
import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionState
2931
import net.mullvad.mullvadvpn.ui.serviceconnection.authTokenCache
30-
import net.mullvad.mullvadvpn.usecase.OutOfTimeUseCase
3132
import net.mullvad.mullvadvpn.usecase.PaymentUseCase
3233
import net.mullvad.mullvadvpn.util.UNKNOWN_STATE_DEBOUNCE_DELAY_MILLISECONDS
3334
import net.mullvad.mullvadvpn.util.addDebounceForUnknownState
@@ -40,12 +41,11 @@ class WelcomeViewModel(
4041
private val deviceRepository: DeviceRepository,
4142
private val serviceConnectionManager: ServiceConnectionManager,
4243
private val paymentUseCase: PaymentUseCase,
43-
private val outOfTimeUseCase: OutOfTimeUseCase,
4444
private val pollAccountExpiry: Boolean = true,
4545
private val isPlayBuild: Boolean
4646
) : ViewModel() {
4747
private val _uiSideEffect = Channel<UiSideEffect>()
48-
val uiSideEffect = merge(_uiSideEffect.receiveAsFlow(), notOutOfTimeEffect())
48+
val uiSideEffect = merge(_uiSideEffect.receiveAsFlow(), hasAddedTimeEffect())
4949

5050
val uiState =
5151
serviceConnectionManager.connectionState
@@ -86,13 +86,12 @@ class WelcomeViewModel(
8686
fetchPaymentAvailability()
8787
}
8888

89-
private fun notOutOfTimeEffect() =
90-
outOfTimeUseCase.isOutOfTime
91-
.filter { it == false }
92-
.map {
93-
paymentUseCase.resetPurchaseResult()
94-
UiSideEffect.OpenConnectScreen
95-
}
89+
private fun hasAddedTimeEffect() =
90+
accountRepository.accountExpiryState
91+
.mapNotNull { it.date() }
92+
.filter { it.minusHours(MIN_HOURS_PAST_ACCOUNT_EXPIRY).isAfterNow }
93+
.onEach { paymentUseCase.resetPurchaseResult() }
94+
.map { UiSideEffect.OpenConnectScreen }
9695

9796
private fun ConnectionProxy.tunnelUiStateFlow(): Flow<TunnelState> =
9897
callbackFlowFromNotifier(this.onUiStateChange)
@@ -144,4 +143,8 @@ class WelcomeViewModel(
144143

145144
data object OpenConnectScreen : UiSideEffect
146145
}
146+
147+
companion object {
148+
private const val MIN_HOURS_PAST_ACCOUNT_EXPIRY = 20
149+
}
147150
}

android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/WelcomeViewModelTest.kt

+8-18
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,9 @@ import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionContainer
3232
import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionManager
3333
import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionState
3434
import net.mullvad.mullvadvpn.ui.serviceconnection.authTokenCache
35-
import net.mullvad.mullvadvpn.usecase.OutOfTimeUseCase
3635
import net.mullvad.mullvadvpn.usecase.PaymentUseCase
3736
import net.mullvad.talpid.util.EventNotifier
3837
import org.joda.time.DateTime
39-
import org.joda.time.ReadableInstant
4038
import org.junit.jupiter.api.AfterEach
4139
import org.junit.jupiter.api.BeforeEach
4240
import org.junit.jupiter.api.Test
@@ -51,7 +49,6 @@ class WelcomeViewModelTest {
5149
private val accountExpiryStateFlow = MutableStateFlow<AccountExpiry>(AccountExpiry.Missing)
5250
private val purchaseResultFlow = MutableStateFlow<PurchaseResult?>(null)
5351
private val paymentAvailabilityFlow = MutableStateFlow<PaymentAvailability?>(null)
54-
private val outOfTimeFlow = MutableStateFlow(true)
5552

5653
// Service connections
5754
private val mockServiceConnectionContainer: ServiceConnectionContainer = mockk()
@@ -64,7 +61,6 @@ class WelcomeViewModelTest {
6461
private val mockDeviceRepository: DeviceRepository = mockk()
6562
private val mockServiceConnectionManager: ServiceConnectionManager = mockk()
6663
private val mockPaymentUseCase: PaymentUseCase = mockk(relaxed = true)
67-
private val mockOutOfTimeUseCase: OutOfTimeUseCase = mockk(relaxed = true)
6864

6965
private lateinit var viewModel: WelcomeViewModel
7066

@@ -87,15 +83,12 @@ class WelcomeViewModelTest {
8783

8884
coEvery { mockPaymentUseCase.paymentAvailability } returns paymentAvailabilityFlow
8985

90-
coEvery { mockOutOfTimeUseCase.isOutOfTime } returns outOfTimeFlow
91-
9286
viewModel =
9387
WelcomeViewModel(
9488
accountRepository = mockAccountRepository,
9589
deviceRepository = mockDeviceRepository,
9690
serviceConnectionManager = mockServiceConnectionManager,
9791
paymentUseCase = mockPaymentUseCase,
98-
outOfTimeUseCase = mockOutOfTimeUseCase,
9992
pollAccountExpiry = false,
10093
isPlayBuild = false
10194
)
@@ -164,19 +157,16 @@ class WelcomeViewModelTest {
164157
}
165158

166159
@Test
167-
fun `when OutOfTimeUseCase return false uiSideEffect should emit OpenConnectScreen`() =
168-
runTest {
169-
// Arrange
170-
val mockExpiryDate: DateTime = mockk()
171-
every { mockExpiryDate.isAfter(any<ReadableInstant>()) } returns true
160+
fun `when user has added time then uiSideEffect should emit OpenConnectScreen`() = runTest {
161+
// Arrange
162+
accountExpiryStateFlow.emit(AccountExpiry.Available(DateTime().plusDays(1)))
172163

173-
// Act, Assert
174-
viewModel.uiSideEffect.test {
175-
outOfTimeFlow.value = false
176-
val action = awaitItem()
177-
assertIs<WelcomeViewModel.UiSideEffect.OpenConnectScreen>(action)
178-
}
164+
// Act, Assert
165+
viewModel.uiSideEffect.test {
166+
val action = awaitItem()
167+
assertIs<WelcomeViewModel.UiSideEffect.OpenConnectScreen>(action)
179168
}
169+
}
180170

181171
@Test
182172
fun `when paymentAvailability emits ProductsUnavailable uiState should include state NoPayment`() =

0 commit comments

Comments
 (0)