From b10b2ba2c5d99d70befe1c7db8e5dee444ed7e0a Mon Sep 17 00:00:00 2001 From: Albin Date: Fri, 29 Sep 2023 17:26:47 +0200 Subject: [PATCH 1/8] Add konsist check for vm property names --- .../mullvadvpn/test/arch/ViewModelTests.kt | 30 +++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/ViewModelTests.kt b/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/ViewModelTests.kt index 35ffe52c312d..e5a2ae8e0bf3 100644 --- a/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/ViewModelTests.kt +++ b/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/ViewModelTests.kt @@ -2,6 +2,8 @@ package net.mullvad.mullvadvpn.test.arch import androidx.lifecycle.ViewModel import com.lemonappdev.konsist.api.Konsist +import com.lemonappdev.konsist.api.ext.list.modifierprovider.withPublicOrDefaultModifier +import com.lemonappdev.konsist.api.ext.list.properties import com.lemonappdev.konsist.api.ext.list.withAllParentsOf import com.lemonappdev.konsist.api.verify.assert import org.junit.Test @@ -9,8 +11,32 @@ import org.junit.Test class ViewModelTests { @Test fun ensureViewModelsHaveViewModelSuffix() { - Konsist.scopeFromProject().classes().withAllParentsOf(ViewModel::class).assert { - it.name.endsWith("ViewModel") + allViewModels().assert { it.name.endsWith("ViewModel") } + } + + // The purpose of this check is to both keep the naming consistent and also to avoid exposing + // properties that shouldn't be exposed. + @Test + fun ensurePublicPropertiesUsePermittedNames() { + allViewModels().properties().withPublicOrDefaultModifier().assert { property -> + permittedPublicPropertyNames.contains(property.name) } } + + private fun allViewModels() = + Konsist.scopeFromProject().classes().withAllParentsOf(ViewModel::class) + + companion object { + // TODO: The goal is to reduce this list to only "uiState" and "uiAction". + private val permittedPublicPropertyNames = + listOf( + "uiState", + "viewActions", + "toastMessages", + "uiCloseAction", + "enterTransitionEndAction", + "accountToken", + "changelogDialogUiState" + ) + } } From 0016716c66231297619cb3cea9ed59745ded82b7 Mon Sep 17 00:00:00 2001 From: Albin Date: Fri, 29 Sep 2023 17:34:28 +0200 Subject: [PATCH 2/8] Align changelog ui state property name --- .../mullvadvpn/compose/screen/ChangelogDialogTest.kt | 2 +- .../kotlin/net/mullvad/mullvadvpn/ui/MainActivity.kt | 2 +- .../mullvad/mullvadvpn/viewmodel/ChangelogViewModel.kt | 9 ++++----- .../mullvadvpn/viewmodel/ChangelogViewModelTest.kt | 8 +++----- .../net/mullvad/mullvadvpn/test/arch/ViewModelTests.kt | 3 +-- 5 files changed, 10 insertions(+), 14 deletions(-) diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ChangelogDialogTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ChangelogDialogTest.kt index 791eb1f0c5cc..778799c5899f 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ChangelogDialogTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ChangelogDialogTest.kt @@ -31,7 +31,7 @@ class ChangelogDialogTest { @Test fun testShowChangeLogWhenNeeded() { // Arrange - every { mockedViewModel.changelogDialogUiState } returns + every { mockedViewModel.uiState } returns MutableStateFlow(ChangelogDialogUiState.Show(listOf(CHANGELOG_ITEM))) every { mockedViewModel.dismissChangelogDialog() } just Runs diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/MainActivity.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/MainActivity.kt index e8343aba8a6e..fa88696cd9d8 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/MainActivity.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/MainActivity.kt @@ -253,7 +253,7 @@ open class MainActivity : FragmentActivity() { findViewById(R.id.compose_view).apply { setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnDetachedFromWindow) setContent { - val state = changelogViewModel.changelogDialogUiState.collectAsState().value + val state = changelogViewModel.uiState.collectAsState().value if (state is ChangelogDialogUiState.Show) { AppTheme { ChangelogDialog( diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ChangelogViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ChangelogViewModel.kt index 1c541944d2df..f6549cded6a9 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ChangelogViewModel.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ChangelogViewModel.kt @@ -10,15 +10,14 @@ class ChangelogViewModel( private val buildVersionCode: Int, private val alwaysShowChangelog: Boolean ) : ViewModel() { - private val _changelogDialogUiState = - MutableStateFlow(ChangelogDialogUiState.Hide) - val changelogDialogUiState = _changelogDialogUiState.asStateFlow() + private val _uiState = MutableStateFlow(ChangelogDialogUiState.Hide) + val uiState = _uiState.asStateFlow() fun refreshChangelogDialogUiState() { val shouldShowChangelogDialog = alwaysShowChangelog || changelogRepository.getVersionCodeOfMostRecentChangelogShowed() < buildVersionCode - _changelogDialogUiState.value = + _uiState.value = if (shouldShowChangelogDialog) { val changelogList = changelogRepository.getLastVersionChanges() if (changelogList.isNotEmpty()) { @@ -33,7 +32,7 @@ class ChangelogViewModel( fun dismissChangelogDialog() { changelogRepository.setVersionCodeOfMostRecentChangelogShowed(buildVersionCode) - _changelogDialogUiState.value = ChangelogDialogUiState.Hide + _uiState.value = ChangelogDialogUiState.Hide } } diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ChangelogViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ChangelogViewModelTest.kt index 3547b9206561..7c74e96a75c1 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ChangelogViewModelTest.kt +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ChangelogViewModelTest.kt @@ -40,14 +40,12 @@ class ChangelogViewModelTest { @Test fun testInitialState() = runBlockingTest { // Arrange, Act, Assert - viewModel.changelogDialogUiState.test { - Assert.assertEquals(ChangelogDialogUiState.Hide, awaitItem()) - } + viewModel.uiState.test { Assert.assertEquals(ChangelogDialogUiState.Hide, awaitItem()) } } @Test fun testShowAndDismissChangelogDialog() = runBlockingTest { - viewModel.changelogDialogUiState.test { + viewModel.uiState.test { // Arrange val fakeList = listOf("test") every { mockedChangelogRepository.getVersionCodeOfMostRecentChangelogShowed() } returns @@ -70,7 +68,7 @@ class ChangelogViewModelTest { @Test fun testShowCaseChangelogWithEmptyListDialog() = runBlockingTest { - viewModel.changelogDialogUiState.test { + viewModel.uiState.test { // Arrange val fakeEmptyList = emptyList() every { mockedChangelogRepository.getVersionCodeOfMostRecentChangelogShowed() } returns diff --git a/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/ViewModelTests.kt b/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/ViewModelTests.kt index e5a2ae8e0bf3..ae1cb0ee81fc 100644 --- a/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/ViewModelTests.kt +++ b/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/ViewModelTests.kt @@ -35,8 +35,7 @@ class ViewModelTests { "toastMessages", "uiCloseAction", "enterTransitionEndAction", - "accountToken", - "changelogDialogUiState" + "accountToken" ) } } From d87fc8a8556a886611bf5e49c9f137fca42d899b Mon Sep 17 00:00:00 2001 From: Albin Date: Fri, 29 Sep 2023 17:33:02 +0200 Subject: [PATCH 3/8] Rename viewActions to uiSideEffect --- .../compose/screen/AccountScreenTest.kt | 8 +-- .../compose/screen/ConnectScreenTest.kt | 52 +++++++++---------- .../compose/screen/OutOfTimeScreenTest.kt | 14 ++--- .../compose/screen/WelcomeScreenTest.kt | 15 +++--- .../compose/screen/AccountScreen.kt | 12 +++-- .../compose/screen/ConnectScreen.kt | 14 ++--- .../compose/screen/OutOfTimeScreen.kt | 18 +++---- .../compose/screen/WelcomeScreen.kt | 14 ++--- .../mullvadvpn/ui/fragment/AccountFragment.kt | 2 +- .../mullvadvpn/ui/fragment/ConnectFragment.kt | 2 +- .../mullvadvpn/ui/fragment/LoginFragment.kt | 10 ++-- .../ui/fragment/OutOfTimeFragment.kt | 2 +- .../mullvadvpn/ui/fragment/WelcomeFragment.kt | 2 +- .../mullvadvpn/viewmodel/AccountViewModel.kt | 12 ++--- .../mullvadvpn/viewmodel/ConnectViewModel.kt | 16 +++--- .../mullvadvpn/viewmodel/LoginViewModel.kt | 20 +++---- .../viewmodel/OutOfTimeViewModel.kt | 16 +++--- .../mullvadvpn/viewmodel/WelcomeViewModel.kt | 16 +++--- .../viewmodel/ConnectViewModelTest.kt | 10 ++-- .../viewmodel/LoginViewModelTest.kt | 12 ++--- .../viewmodel/OutOfTimeViewModelTest.kt | 8 +-- .../viewmodel/WelcomeViewModelTest.kt | 8 +-- .../mullvadvpn/test/arch/ViewModelTests.kt | 4 +- 23 files changed, 145 insertions(+), 142 deletions(-) diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/AccountScreenTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/AccountScreenTest.kt index c4d2fab62eda..41bf44936926 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/AccountScreenTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/AccountScreenTest.kt @@ -35,7 +35,7 @@ class AccountScreenTest { accountNumber = DUMMY_ACCOUNT_NUMBER, accountExpiry = null ), - viewActions = MutableSharedFlow().asSharedFlow(), + uiSideEffect = MutableSharedFlow().asSharedFlow(), enterTransitionEndAction = MutableSharedFlow().asSharedFlow() ) } @@ -60,7 +60,7 @@ class AccountScreenTest { accountNumber = DUMMY_ACCOUNT_NUMBER, accountExpiry = null ), - viewActions = MutableSharedFlow().asSharedFlow(), + uiSideEffect = MutableSharedFlow().asSharedFlow(), enterTransitionEndAction = MutableSharedFlow().asSharedFlow(), onManageAccountClick = mockedClickHandler ) @@ -86,7 +86,7 @@ class AccountScreenTest { accountNumber = DUMMY_ACCOUNT_NUMBER, accountExpiry = null ), - viewActions = MutableSharedFlow().asSharedFlow(), + uiSideEffect = MutableSharedFlow().asSharedFlow(), enterTransitionEndAction = MutableSharedFlow().asSharedFlow(), onRedeemVoucherClick = mockedClickHandler ) @@ -112,7 +112,7 @@ class AccountScreenTest { accountNumber = DUMMY_ACCOUNT_NUMBER, accountExpiry = null ), - viewActions = MutableSharedFlow().asSharedFlow(), + uiSideEffect = MutableSharedFlow().asSharedFlow(), enterTransitionEndAction = MutableSharedFlow().asSharedFlow(), onLogoutClick = mockedClickHandler ) diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreenTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreenTest.kt index 40f9b278b82a..02a148b22d85 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreenTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreenTest.kt @@ -56,7 +56,7 @@ class ConnectScreenTest { composeTestRule.setContent { ConnectScreen( uiState = ConnectUiState.INITIAL, - viewActions = MutableSharedFlow().asSharedFlow() + uiSideEffect = MutableSharedFlow().asSharedFlow() ) } @@ -86,7 +86,7 @@ class ConnectScreenTest { connectNotificationState = ConnectNotificationState.ShowTunnelStateNotificationBlocked ), - viewActions = MutableSharedFlow().asSharedFlow() + uiSideEffect = MutableSharedFlow().asSharedFlow() ) } @@ -121,7 +121,7 @@ class ConnectScreenTest { connectNotificationState = ConnectNotificationState.ShowTunnelStateNotificationBlocked ), - viewActions = MutableSharedFlow().asSharedFlow() + uiSideEffect = MutableSharedFlow().asSharedFlow() ) } @@ -153,7 +153,7 @@ class ConnectScreenTest { isTunnelInfoExpanded = false, connectNotificationState = ConnectNotificationState.HideNotification ), - viewActions = MutableSharedFlow().asSharedFlow() + uiSideEffect = MutableSharedFlow().asSharedFlow() ) } @@ -184,7 +184,7 @@ class ConnectScreenTest { isTunnelInfoExpanded = false, connectNotificationState = ConnectNotificationState.HideNotification ), - viewActions = MutableSharedFlow().asSharedFlow() + uiSideEffect = MutableSharedFlow().asSharedFlow() ) } @@ -216,7 +216,7 @@ class ConnectScreenTest { isTunnelInfoExpanded = false, connectNotificationState = ConnectNotificationState.HideNotification ), - viewActions = MutableSharedFlow().asSharedFlow() + uiSideEffect = MutableSharedFlow().asSharedFlow() ) } @@ -248,7 +248,7 @@ class ConnectScreenTest { isTunnelInfoExpanded = false, connectNotificationState = ConnectNotificationState.HideNotification ), - viewActions = MutableSharedFlow().asSharedFlow() + uiSideEffect = MutableSharedFlow().asSharedFlow() ) } @@ -285,7 +285,7 @@ class ConnectScreenTest { ErrorState(ErrorStateCause.StartTunnelError, true) ) ), - viewActions = MutableSharedFlow().asSharedFlow() + uiSideEffect = MutableSharedFlow().asSharedFlow() ) } @@ -323,7 +323,7 @@ class ConnectScreenTest { ErrorState(ErrorStateCause.StartTunnelError, false) ) ), - viewActions = MutableSharedFlow().asSharedFlow() + uiSideEffect = MutableSharedFlow().asSharedFlow() ) } @@ -356,7 +356,7 @@ class ConnectScreenTest { connectNotificationState = ConnectNotificationState.ShowTunnelStateNotificationBlocked ), - viewActions = MutableSharedFlow().asSharedFlow() + uiSideEffect = MutableSharedFlow().asSharedFlow() ) } @@ -391,7 +391,7 @@ class ConnectScreenTest { connectNotificationState = ConnectNotificationState.ShowTunnelStateNotificationBlocked ), - viewActions = MutableSharedFlow().asSharedFlow() + uiSideEffect = MutableSharedFlow().asSharedFlow() ) } @@ -425,7 +425,7 @@ class ConnectScreenTest { isTunnelInfoExpanded = false, connectNotificationState = ConnectNotificationState.HideNotification ), - viewActions = MutableSharedFlow().asSharedFlow(), + uiSideEffect = MutableSharedFlow().asSharedFlow(), onSwitchLocationClick = mockedClickHandler ) } @@ -456,7 +456,7 @@ class ConnectScreenTest { isTunnelInfoExpanded = false, connectNotificationState = ConnectNotificationState.HideNotification ), - viewActions = MutableSharedFlow().asSharedFlow(), + uiSideEffect = MutableSharedFlow().asSharedFlow(), onDisconnectClick = mockedClickHandler ) } @@ -487,7 +487,7 @@ class ConnectScreenTest { isTunnelInfoExpanded = false, connectNotificationState = ConnectNotificationState.HideNotification ), - viewActions = MutableSharedFlow().asSharedFlow(), + uiSideEffect = MutableSharedFlow().asSharedFlow(), onReconnectClick = mockedClickHandler ) } @@ -517,7 +517,7 @@ class ConnectScreenTest { isTunnelInfoExpanded = false, connectNotificationState = ConnectNotificationState.HideNotification ), - viewActions = MutableSharedFlow().asSharedFlow(), + uiSideEffect = MutableSharedFlow().asSharedFlow(), onConnectClick = mockedClickHandler ) } @@ -547,7 +547,7 @@ class ConnectScreenTest { isTunnelInfoExpanded = false, connectNotificationState = ConnectNotificationState.HideNotification ), - viewActions = MutableSharedFlow().asSharedFlow(), + uiSideEffect = MutableSharedFlow().asSharedFlow(), onCancelClick = mockedClickHandler ) } @@ -578,7 +578,7 @@ class ConnectScreenTest { isTunnelInfoExpanded = false, connectNotificationState = ConnectNotificationState.HideNotification ), - viewActions = MutableSharedFlow().asSharedFlow(), + uiSideEffect = MutableSharedFlow().asSharedFlow(), onToggleTunnelInfo = mockedClickHandler ) } @@ -616,7 +616,7 @@ class ConnectScreenTest { isTunnelInfoExpanded = true, connectNotificationState = ConnectNotificationState.HideNotification ), - viewActions = MutableSharedFlow().asSharedFlow() + uiSideEffect = MutableSharedFlow().asSharedFlow() ) } @@ -654,7 +654,7 @@ class ConnectScreenTest { connectNotificationState = ConnectNotificationState.ShowVersionInfoNotification(versionInfo) ), - viewActions = MutableSharedFlow().asSharedFlow() + uiSideEffect = MutableSharedFlow().asSharedFlow() ) } @@ -690,7 +690,7 @@ class ConnectScreenTest { connectNotificationState = ConnectNotificationState.ShowVersionInfoNotification(versionInfo) ), - viewActions = MutableSharedFlow().asSharedFlow() + uiSideEffect = MutableSharedFlow().asSharedFlow() ) } @@ -723,7 +723,7 @@ class ConnectScreenTest { connectNotificationState = ConnectNotificationState.ShowAccountExpiryNotification(expiryDate) ), - viewActions = MutableSharedFlow().asSharedFlow() + uiSideEffect = MutableSharedFlow().asSharedFlow() ) } @@ -761,7 +761,7 @@ class ConnectScreenTest { connectNotificationState = ConnectNotificationState.ShowVersionInfoNotification(versionInfo) ), - viewActions = MutableSharedFlow().asSharedFlow() + uiSideEffect = MutableSharedFlow().asSharedFlow() ) } @@ -793,7 +793,7 @@ class ConnectScreenTest { connectNotificationState = ConnectNotificationState.ShowAccountExpiryNotification(expiryDate) ), - viewActions = MutableSharedFlow().asSharedFlow() + uiSideEffect = MutableSharedFlow().asSharedFlow() ) } @@ -810,9 +810,9 @@ class ConnectScreenTest { composeTestRule.setContent { ConnectScreen( uiState = ConnectUiState.INITIAL, - viewActions = + uiSideEffect = MutableStateFlow( - ConnectViewModel.ViewAction.OpenAccountManagementPageInBrowser("222") + ConnectViewModel.UiSideEffect.OpenAccountManagementPageInBrowser("222") ) ) } @@ -828,7 +828,7 @@ class ConnectScreenTest { composeTestRule.setContent { ConnectScreen( uiState = ConnectUiState.INITIAL, - viewActions = MutableStateFlow(ConnectViewModel.ViewAction.OpenOutOfTimeView), + uiSideEffect = MutableStateFlow(ConnectViewModel.UiSideEffect.OpenOutOfTimeView), onOpenOutOfTimeScreen = mockedOpenScreenHandler ) } diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/OutOfTimeScreenTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/OutOfTimeScreenTest.kt index a97e587c8c92..a177aa8ac199 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/OutOfTimeScreenTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/OutOfTimeScreenTest.kt @@ -30,7 +30,7 @@ class OutOfTimeScreenTest { OutOfTimeScreen( showSitePayment = false, uiState = OutOfTimeUiState(), - viewActions = MutableSharedFlow(), + uiSideEffect = MutableSharedFlow(), onSitePaymentClick = {}, onRedeemVoucherClick = {}, onSettingsClick = {}, @@ -58,8 +58,8 @@ class OutOfTimeScreenTest { OutOfTimeScreen( showSitePayment = true, uiState = OutOfTimeUiState(), - viewActions = - MutableStateFlow(OutOfTimeViewModel.ViewAction.OpenAccountView("222")), + uiSideEffect = + MutableStateFlow(OutOfTimeViewModel.UiSideEffect.OpenAccountView("222")), onSitePaymentClick = {}, onRedeemVoucherClick = {}, onSettingsClick = {}, @@ -81,7 +81,7 @@ class OutOfTimeScreenTest { OutOfTimeScreen( showSitePayment = true, uiState = OutOfTimeUiState(), - viewActions = MutableStateFlow(OutOfTimeViewModel.ViewAction.OpenConnectScreen), + uiSideEffect = MutableStateFlow(OutOfTimeViewModel.UiSideEffect.OpenConnectScreen), onSitePaymentClick = {}, onRedeemVoucherClick = {}, onSettingsClick = {}, @@ -103,7 +103,7 @@ class OutOfTimeScreenTest { OutOfTimeScreen( showSitePayment = true, uiState = OutOfTimeUiState(), - viewActions = MutableSharedFlow(), + uiSideEffect = MutableSharedFlow(), onSitePaymentClick = mockClickListener, onRedeemVoucherClick = {}, onSettingsClick = {}, @@ -128,7 +128,7 @@ class OutOfTimeScreenTest { OutOfTimeScreen( showSitePayment = true, uiState = OutOfTimeUiState(), - viewActions = MutableSharedFlow(), + uiSideEffect = MutableSharedFlow(), onSitePaymentClick = {}, onRedeemVoucherClick = mockClickListener, onSettingsClick = {}, @@ -153,7 +153,7 @@ class OutOfTimeScreenTest { OutOfTimeScreen( showSitePayment = true, uiState = OutOfTimeUiState(tunnelState = TunnelState.Connecting(null, null)), - viewActions = MutableSharedFlow(), + uiSideEffect = MutableSharedFlow(), onSitePaymentClick = {}, onRedeemVoucherClick = {}, onSettingsClick = {}, diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/WelcomeScreenTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/WelcomeScreenTest.kt index 051b16b6b1ea..4efa98225b3b 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/WelcomeScreenTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/WelcomeScreenTest.kt @@ -29,7 +29,7 @@ class WelcomeScreenTest { WelcomeScreen( showSitePayment = true, uiState = WelcomeUiState(), - viewActions = MutableSharedFlow(), + uiSideEffect = MutableSharedFlow(), onSitePaymentClick = {}, onRedeemVoucherClick = {}, onSettingsClick = {}, @@ -52,7 +52,7 @@ class WelcomeScreenTest { WelcomeScreen( showSitePayment = false, uiState = WelcomeUiState(), - viewActions = MutableSharedFlow(), + uiSideEffect = MutableSharedFlow(), onSitePaymentClick = {}, onRedeemVoucherClick = {}, onSettingsClick = {}, @@ -81,7 +81,7 @@ class WelcomeScreenTest { WelcomeScreen( showSitePayment = true, uiState = WelcomeUiState(accountNumber = rawAccountNumber), - viewActions = MutableSharedFlow(), + uiSideEffect = MutableSharedFlow(), onSitePaymentClick = {}, onRedeemVoucherClick = {}, onSettingsClick = {}, @@ -101,7 +101,8 @@ class WelcomeScreenTest { WelcomeScreen( showSitePayment = true, uiState = WelcomeUiState(), - viewActions = MutableStateFlow(WelcomeViewModel.ViewAction.OpenAccountView("222")), + uiSideEffect = + MutableStateFlow(WelcomeViewModel.UiSideEffect.OpenAccountView("222")), onSitePaymentClick = {}, onRedeemVoucherClick = {}, onSettingsClick = {}, @@ -122,7 +123,7 @@ class WelcomeScreenTest { WelcomeScreen( showSitePayment = true, uiState = WelcomeUiState(), - viewActions = MutableStateFlow(WelcomeViewModel.ViewAction.OpenConnectScreen), + uiSideEffect = MutableStateFlow(WelcomeViewModel.UiSideEffect.OpenConnectScreen), onSitePaymentClick = {}, onRedeemVoucherClick = {}, onSettingsClick = {}, @@ -143,7 +144,7 @@ class WelcomeScreenTest { WelcomeScreen( showSitePayment = true, uiState = WelcomeUiState(), - viewActions = MutableStateFlow(WelcomeViewModel.ViewAction.OpenConnectScreen), + uiSideEffect = MutableStateFlow(WelcomeViewModel.UiSideEffect.OpenConnectScreen), onSitePaymentClick = mockClickListener, onRedeemVoucherClick = {}, onSettingsClick = {}, @@ -167,7 +168,7 @@ class WelcomeScreenTest { WelcomeScreen( showSitePayment = true, uiState = WelcomeUiState(), - viewActions = MutableStateFlow(WelcomeViewModel.ViewAction.OpenConnectScreen), + uiSideEffect = MutableStateFlow(WelcomeViewModel.UiSideEffect.OpenConnectScreen), onSitePaymentClick = {}, onRedeemVoucherClick = mockClickListener, onSettingsClick = {}, diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/AccountScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/AccountScreen.kt index b3bc9e7f3beb..6241b3bf04a0 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/AccountScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/AccountScreen.kt @@ -62,7 +62,7 @@ private fun PreviewAccountScreen() { accountNumber = "1234123412341234", accountExpiry = null ), - viewActions = MutableSharedFlow().asSharedFlow(), + uiSideEffect = MutableSharedFlow().asSharedFlow(), enterTransitionEndAction = MutableSharedFlow() ) } @@ -71,7 +71,7 @@ private fun PreviewAccountScreen() { @Composable fun AccountScreen( uiState: AccountUiState, - viewActions: SharedFlow, + uiSideEffect: SharedFlow, enterTransitionEndAction: SharedFlow, onRedeemVoucherClick: () -> Unit = {}, onManageAccountClick: () -> Unit = {}, @@ -116,9 +116,11 @@ fun AccountScreen( }, ) { LaunchedEffect(Unit) { - viewActions.collect { viewAction -> - if (viewAction is AccountViewModel.ViewAction.OpenAccountManagementPageInBrowser) { - context.openAccountPageInBrowser(viewAction.token) + uiSideEffect.collect { uiSideEffect -> + if ( + uiSideEffect is AccountViewModel.UiSideEffect.OpenAccountManagementPageInBrowser + ) { + context.openAccountPageInBrowser(uiSideEffect.token) } } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreen.kt index d250a5467eaa..8ca98c410a9d 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreen.kt @@ -60,7 +60,7 @@ fun PreviewConnectScreen() { AppTheme { ConnectScreen( uiState = state, - viewActions = MutableSharedFlow().asSharedFlow() + uiSideEffect = MutableSharedFlow().asSharedFlow() ) } } @@ -68,7 +68,7 @@ fun PreviewConnectScreen() { @Composable fun ConnectScreen( uiState: ConnectUiState, - viewActions: SharedFlow, + uiSideEffect: SharedFlow, onDisconnectClick: () -> Unit = {}, onReconnectClick: () -> Unit = {}, onConnectClick: () -> Unit = {}, @@ -83,12 +83,12 @@ fun ConnectScreen( ) { val context = LocalContext.current LaunchedEffect(key1 = Unit) { - viewActions.collect { viewAction -> - when (viewAction) { - is ConnectViewModel.ViewAction.OpenAccountManagementPageInBrowser -> { - context.openAccountPageInBrowser(viewAction.token) + uiSideEffect.collect { uiSideEffect -> + when (uiSideEffect) { + is ConnectViewModel.UiSideEffect.OpenAccountManagementPageInBrowser -> { + context.openAccountPageInBrowser(uiSideEffect.token) } - is ConnectViewModel.ViewAction.OpenOutOfTimeView -> { + is ConnectViewModel.UiSideEffect.OpenOutOfTimeView -> { onOpenOutOfTimeScreen() } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/OutOfTimeScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/OutOfTimeScreen.kt index a9ab126dae42..49de23228c45 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/OutOfTimeScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/OutOfTimeScreen.kt @@ -48,7 +48,7 @@ private fun PreviewOutOfTimeScreenDisconnected() { OutOfTimeScreen( showSitePayment = true, uiState = OutOfTimeUiState(tunnelState = TunnelState.Disconnected), - viewActions = MutableSharedFlow().asSharedFlow() + uiSideEffect = MutableSharedFlow().asSharedFlow() ) } } @@ -60,7 +60,7 @@ private fun PreviewOutOfTimeScreenConnecting() { OutOfTimeScreen( showSitePayment = true, uiState = OutOfTimeUiState(tunnelState = TunnelState.Connecting(null, null)), - viewActions = MutableSharedFlow().asSharedFlow() + uiSideEffect = MutableSharedFlow().asSharedFlow() ) } } @@ -78,7 +78,7 @@ private fun PreviewOutOfTimeScreenError() { ErrorState(cause = ErrorStateCause.IsOffline, isBlocking = true) ) ), - viewActions = MutableSharedFlow().asSharedFlow() + uiSideEffect = MutableSharedFlow().asSharedFlow() ) } } @@ -87,7 +87,7 @@ private fun PreviewOutOfTimeScreenError() { fun OutOfTimeScreen( showSitePayment: Boolean, uiState: OutOfTimeUiState, - viewActions: SharedFlow, + uiSideEffect: SharedFlow, onDisconnectClick: () -> Unit = {}, onSitePaymentClick: () -> Unit = {}, onRedeemVoucherClick: () -> Unit = {}, @@ -97,11 +97,11 @@ fun OutOfTimeScreen( ) { val openAccountPage = LocalUriHandler.current.createOpenAccountPageHook() LaunchedEffect(key1 = Unit) { - viewActions.collect { viewAction -> - when (viewAction) { - is OutOfTimeViewModel.ViewAction.OpenAccountView -> - openAccountPage(viewAction.token) - OutOfTimeViewModel.ViewAction.OpenConnectScreen -> openConnectScreen() + uiSideEffect.collect { uiSideEffect -> + when (uiSideEffect) { + is OutOfTimeViewModel.UiSideEffect.OpenAccountView -> + openAccountPage(uiSideEffect.token) + OutOfTimeViewModel.UiSideEffect.OpenConnectScreen -> openConnectScreen() } } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/WelcomeScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/WelcomeScreen.kt index 140b9824df0a..5cae9e966a1a 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/WelcomeScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/WelcomeScreen.kt @@ -56,7 +56,7 @@ private fun PreviewWelcomeScreen() { WelcomeScreen( showSitePayment = true, uiState = WelcomeUiState(accountNumber = "4444555566667777", deviceName = "Happy Mole"), - viewActions = MutableSharedFlow().asSharedFlow(), + uiSideEffect = MutableSharedFlow().asSharedFlow(), onSitePaymentClick = {}, onRedeemVoucherClick = {}, onSettingsClick = {}, @@ -70,7 +70,7 @@ private fun PreviewWelcomeScreen() { fun WelcomeScreen( showSitePayment: Boolean, uiState: WelcomeUiState, - viewActions: SharedFlow, + uiSideEffect: SharedFlow, onSitePaymentClick: () -> Unit, onRedeemVoucherClick: () -> Unit, onSettingsClick: () -> Unit, @@ -79,11 +79,11 @@ fun WelcomeScreen( ) { val context = LocalContext.current LaunchedEffect(Unit) { - viewActions.collect { viewAction -> - when (viewAction) { - is WelcomeViewModel.ViewAction.OpenAccountView -> - context.openAccountPageInBrowser(viewAction.token) - WelcomeViewModel.ViewAction.OpenConnectScreen -> openConnectScreen() + uiSideEffect.collect { uiSideEffect -> + when (uiSideEffect) { + is WelcomeViewModel.UiSideEffect.OpenAccountView -> + context.openAccountPageInBrowser(uiSideEffect.token) + WelcomeViewModel.UiSideEffect.OpenConnectScreen -> openConnectScreen() } } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/AccountFragment.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/AccountFragment.kt index 666bd19cdbbd..b0784d62a99d 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/AccountFragment.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/AccountFragment.kt @@ -32,7 +32,7 @@ class AccountFragment : BaseFragment(), StatusBarPainter, NavigationBarPainter { val state = vm.uiState.collectAsState().value AccountScreen( uiState = state, - viewActions = vm.viewActions, + uiSideEffect = vm.uiSideEffect, enterTransitionEndAction = vm.enterTransitionEndAction, onRedeemVoucherClick = { openRedeemVoucherFragment() }, onManageAccountClick = vm::onManageAccountClick, diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/ConnectFragment.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/ConnectFragment.kt index b32f162761c2..afb2ec9d9468 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/ConnectFragment.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/ConnectFragment.kt @@ -34,7 +34,7 @@ class ConnectFragment : BaseFragment(), NavigationBarPainter { val state = connectViewModel.uiState.collectAsState().value ConnectScreen( uiState = state, - viewActions = connectViewModel.viewActions, + uiSideEffect = connectViewModel.uiSideEffect, onDisconnectClick = connectViewModel::onDisconnectClick, onReconnectClick = connectViewModel::onReconnectClick, onConnectClick = connectViewModel::onConnectClick, diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/LoginFragment.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/LoginFragment.kt index 466b0eca8586..9fd6e5aceb33 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/LoginFragment.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/LoginFragment.kt @@ -14,7 +14,7 @@ import net.mullvad.mullvadvpn.lib.theme.AppTheme import net.mullvad.mullvadvpn.model.AccountToken import net.mullvad.mullvadvpn.ui.MainActivity import net.mullvad.mullvadvpn.ui.NavigationBarPainter -import net.mullvad.mullvadvpn.viewmodel.LoginViewAction +import net.mullvad.mullvadvpn.viewmodel.LoginUiSideEffect import net.mullvad.mullvadvpn.viewmodel.LoginViewModel import org.koin.androidx.viewmodel.ext.android.viewModel @@ -40,12 +40,12 @@ class LoginFragment : BaseFragment(), NavigationBarPainter { AppTheme { val uiState by vm.uiState.collectAsState() LaunchedEffect(Unit) { - vm.viewActions.collect { + vm.uiSideEffect.collect { when (it) { - LoginViewAction.NavigateToWelcome, - LoginViewAction + LoginUiSideEffect.NavigateToWelcome, + LoginUiSideEffect .NavigateToConnect -> {} // TODO Fix when we redo navigation - is LoginViewAction.TooManyDevices -> { + is LoginUiSideEffect.TooManyDevices -> { navigateToDeviceListFragment(it.accountToken) } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/OutOfTimeFragment.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/OutOfTimeFragment.kt index 8d3bf00010e9..700ffba14576 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/OutOfTimeFragment.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/OutOfTimeFragment.kt @@ -30,7 +30,7 @@ class OutOfTimeFragment : BaseFragment() { OutOfTimeScreen( showSitePayment = IS_PLAY_BUILD.not(), uiState = state, - viewActions = vm.viewActions, + uiSideEffect = vm.uiSideEffect, onSitePaymentClick = vm::onSitePaymentClick, onRedeemVoucherClick = ::openRedeemVoucherFragment, onSettingsClick = ::openSettingsView, diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/WelcomeFragment.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/WelcomeFragment.kt index 6c9fa0c4c75d..403c50ccec12 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/WelcomeFragment.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/WelcomeFragment.kt @@ -30,7 +30,7 @@ class WelcomeFragment : BaseFragment() { WelcomeScreen( showSitePayment = IS_PLAY_BUILD.not(), uiState = state, - viewActions = vm.viewActions, + uiSideEffect = vm.uiSideEffect, onSitePaymentClick = vm::onSitePaymentClick, onRedeemVoucherClick = ::openRedeemVoucherFragment, onSettingsClick = ::openSettingsView, diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/AccountViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/AccountViewModel.kt index b1ce2e0027c9..425a64dd71b4 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/AccountViewModel.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/AccountViewModel.kt @@ -22,9 +22,9 @@ class AccountViewModel( deviceRepository: DeviceRepository ) : ViewModel() { - private val _viewActions = MutableSharedFlow(extraBufferCapacity = 1) + private val _uiSideEffect = MutableSharedFlow(extraBufferCapacity = 1) private val _enterTransitionEndAction = MutableSharedFlow() - val viewActions = _viewActions.asSharedFlow() + val uiSideEffect = _uiSideEffect.asSharedFlow() val uiState = combine(deviceRepository.deviceState, accountRepository.accountExpiryState) { @@ -42,8 +42,8 @@ class AccountViewModel( fun onManageAccountClick() { viewModelScope.launch { - _viewActions.tryEmit( - ViewAction.OpenAccountManagementPageInBrowser( + _uiSideEffect.tryEmit( + UiSideEffect.OpenAccountManagementPageInBrowser( serviceConnectionManager.authTokenCache()?.fetchAuthToken() ?: "" ) ) @@ -58,8 +58,8 @@ class AccountViewModel( viewModelScope.launch { _enterTransitionEndAction.emit(Unit) } } - sealed class ViewAction { - data class OpenAccountManagementPageInBrowser(val token: String) : ViewAction() + sealed class UiSideEffect { + data class OpenAccountManagementPageInBrowser(val token: String) : UiSideEffect() } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModel.kt index bf3414b0e197..01a1c848961f 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModel.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModel.kt @@ -48,8 +48,8 @@ class ConnectViewModel( private val isVersionInfoNotificationEnabled: Boolean, accountRepository: AccountRepository, ) : ViewModel() { - private val _viewActions = MutableSharedFlow(extraBufferCapacity = 1) - val viewActions = _viewActions.asSharedFlow() + private val _uiSideEffect = MutableSharedFlow(extraBufferCapacity = 1) + val uiSideEffect = _uiSideEffect.asSharedFlow() private val _shared: SharedFlow = serviceConnectionManager.connectionState @@ -84,7 +84,7 @@ class ConnectViewModel( accountExpiry, isTunnelInfoExpanded -> if (tunnelRealState.isTunnelErrorStateDueToExpiredAccount()) { - _viewActions.tryEmit(ViewAction.OpenOutOfTimeView) + _uiSideEffect.tryEmit(UiSideEffect.OpenOutOfTimeView) } ConnectUiState( location = @@ -205,18 +205,18 @@ class ConnectViewModel( fun onManageAccountClick() { viewModelScope.launch { - _viewActions.tryEmit( - ViewAction.OpenAccountManagementPageInBrowser( + _uiSideEffect.tryEmit( + UiSideEffect.OpenAccountManagementPageInBrowser( serviceConnectionManager.authTokenCache()?.fetchAuthToken() ?: "" ) ) } } - sealed interface ViewAction { - data class OpenAccountManagementPageInBrowser(val token: String) : ViewAction + sealed interface UiSideEffect { + data class OpenAccountManagementPageInBrowser(val token: String) : UiSideEffect - data object OpenOutOfTimeView : ViewAction + data object OpenOutOfTimeView : UiSideEffect } companion object { diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/LoginViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/LoginViewModel.kt index 194b6462a583..953e59f388d0 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/LoginViewModel.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/LoginViewModel.kt @@ -27,12 +27,12 @@ import net.mullvad.mullvadvpn.repository.DeviceRepository private const val MINIMUM_LOADING_SPINNER_TIME_MILLIS = 500L -sealed interface LoginViewAction { - data object NavigateToWelcome : LoginViewAction +sealed interface LoginUiSideEffect { + data object NavigateToWelcome : LoginUiSideEffect - data object NavigateToConnect : LoginViewAction + data object NavigateToConnect : LoginUiSideEffect - data class TooManyDevices(val accountToken: AccountToken) : LoginViewAction + data class TooManyDevices(val accountToken: AccountToken) : LoginUiSideEffect } class LoginViewModel( @@ -43,8 +43,8 @@ class LoginViewModel( private val _loginState = MutableStateFlow(LoginUiState.INITIAL.loginState) private val _loginInput = MutableStateFlow(LoginUiState.INITIAL.accountNumberInput) - private val _viewActions = MutableSharedFlow(extraBufferCapacity = 1) - val viewActions = _viewActions.asSharedFlow() + private val _uiSideEffect = MutableSharedFlow(extraBufferCapacity = 1) + val uiSideEffect = _uiSideEffect.asSharedFlow() private val _uiState = combine( @@ -83,7 +83,7 @@ class LoginViewModel( LoginResult.Ok -> { launch { delay(1000) - _viewActions.emit(LoginViewAction.NavigateToConnect) + _uiSideEffect.emit(LoginUiSideEffect.NavigateToConnect) } Success } @@ -100,8 +100,8 @@ class LoginViewModel( if (refreshResult.isAvailable()) { // Navigate to device list - _viewActions.emit( - LoginViewAction.TooManyDevices(AccountToken(accountToken)) + _uiSideEffect.emit( + LoginUiSideEffect.TooManyDevices(AccountToken(accountToken)) ) return@launch } else { @@ -123,7 +123,7 @@ class LoginViewModel( private suspend fun AccountCreationResult.mapToUiState(): LoginState? { return if (this is AccountCreationResult.Success) { - _viewActions.emit(LoginViewAction.NavigateToWelcome) + _uiSideEffect.emit(LoginUiSideEffect.NavigateToWelcome) null } else { Idle(LoginError.UnableToCreateAccount) diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/OutOfTimeViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/OutOfTimeViewModel.kt index 00f3850777ba..8a789f62fdd4 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/OutOfTimeViewModel.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/OutOfTimeViewModel.kt @@ -34,8 +34,8 @@ class OutOfTimeViewModel( private val pollAccountExpiry: Boolean = true ) : ViewModel() { - private val _viewActions = MutableSharedFlow(extraBufferCapacity = 1) - val viewActions = _viewActions.asSharedFlow() + private val _uiSideEffect = MutableSharedFlow(extraBufferCapacity = 1) + val uiSideEffect = _uiSideEffect.asSharedFlow() val uiState = serviceConnectionManager.connectionState @@ -59,7 +59,7 @@ class OutOfTimeViewModel( val tomorrow = DateTime.now().plusHours(20) if (expiry.isAfter(tomorrow)) { - _viewActions.tryEmit(ViewAction.OpenConnectScreen) + _uiSideEffect.tryEmit(UiSideEffect.OpenConnectScreen) } } } @@ -77,8 +77,8 @@ class OutOfTimeViewModel( fun onSitePaymentClick() { viewModelScope.launch { - _viewActions.tryEmit( - ViewAction.OpenAccountView( + _uiSideEffect.tryEmit( + UiSideEffect.OpenAccountView( serviceConnectionManager.authTokenCache()?.fetchAuthToken() ?: "" ) ) @@ -89,9 +89,9 @@ class OutOfTimeViewModel( viewModelScope.launch { serviceConnectionManager.connectionProxy()?.disconnect() } } - sealed interface ViewAction { - data class OpenAccountView(val token: String) : ViewAction + sealed interface UiSideEffect { + data class OpenAccountView(val token: String) : UiSideEffect - data object OpenConnectScreen : ViewAction + data object OpenConnectScreen : UiSideEffect } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/WelcomeViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/WelcomeViewModel.kt index eb25ad1268ed..fe2ddcb66a0a 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/WelcomeViewModel.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/WelcomeViewModel.kt @@ -39,8 +39,8 @@ class WelcomeViewModel( private val pollAccountExpiry: Boolean = true ) : ViewModel() { - private val _viewActions = MutableSharedFlow(extraBufferCapacity = 1) - val viewActions = _viewActions.asSharedFlow() + private val _uiSideEffect = MutableSharedFlow(extraBufferCapacity = 1) + val uiSideEffect = _uiSideEffect.asSharedFlow() val uiState = serviceConnectionManager.connectionState @@ -74,7 +74,7 @@ class WelcomeViewModel( val tomorrow = DateTime.now().plusHours(20) if (expiry.isAfter(tomorrow)) { - _viewActions.tryEmit(ViewAction.OpenConnectScreen) + _uiSideEffect.tryEmit(UiSideEffect.OpenConnectScreen) } } } @@ -92,17 +92,17 @@ class WelcomeViewModel( fun onSitePaymentClick() { viewModelScope.launch { - _viewActions.tryEmit( - ViewAction.OpenAccountView( + _uiSideEffect.tryEmit( + UiSideEffect.OpenAccountView( serviceConnectionManager.authTokenCache()?.fetchAuthToken() ?: "" ) ) } } - sealed interface ViewAction { - data class OpenAccountView(val token: String) : ViewAction + sealed interface UiSideEffect { + data class OpenAccountView(val token: String) : UiSideEffect - data object OpenConnectScreen : ViewAction + data object OpenConnectScreen : UiSideEffect } } diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModelTest.kt index a2a39e54c46a..18f8447f4434 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModelTest.kt +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModelTest.kt @@ -375,21 +375,21 @@ class ConnectViewModelTest { coEvery { mockAuthTokenCache.fetchAuthToken() } returns mockToken // Act, Assert - viewModel.viewActions.test { + viewModel.uiSideEffect.test { viewModel.onManageAccountClick() val action = awaitItem() - assertIs(action) + assertIs(action) assertEquals(mockToken, action.token) } } @Test - fun testOutOfTimeViewAction() = + fun testOutOfTimeUiSideEffect() = runTest(testCoroutineRule.testDispatcher) { // Arrange val errorStateCause = ErrorStateCause.AuthFailed("[EXPIRED_ACCOUNT]") val tunnelRealStateTestItem = TunnelState.Error(ErrorState(errorStateCause, true)) - val deferred = async { viewModel.viewActions.first() } + val deferred = async { viewModel.uiSideEffect.first() } // Act viewModel.uiState.test { @@ -403,7 +403,7 @@ class ConnectViewModelTest { } // Assert - assertIs(deferred.await()) + assertIs(deferred.await()) } companion object { diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/LoginViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/LoginViewModelTest.kt index 73bfd1ef38b7..744989a9220c 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/LoginViewModelTest.kt +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/LoginViewModelTest.kt @@ -63,7 +63,7 @@ class LoginViewModelTest { turbineScope { // Arrange val uiStates = loginViewModel.uiState.testIn(backgroundScope) - val sideEffects = loginViewModel.viewActions.testIn(backgroundScope) + val sideEffects = loginViewModel.uiSideEffect.testIn(backgroundScope) coEvery { mockedAccountRepository.createAccount() } returns AccountCreationResult.Success(DUMMY_ACCOUNT_TOKEN) @@ -71,7 +71,7 @@ class LoginViewModelTest { uiStates.skipDefaultItem() loginViewModel.createAccount() assertEquals(Loading.CreatingAccount, uiStates.awaitItem().loginState) - assertEquals(LoginViewAction.NavigateToWelcome, sideEffects.awaitItem()) + assertEquals(LoginUiSideEffect.NavigateToWelcome, sideEffects.awaitItem()) } } @@ -80,7 +80,7 @@ class LoginViewModelTest { turbineScope { // Arrange val uiStates = loginViewModel.uiState.testIn(backgroundScope) - val sideEffects = loginViewModel.viewActions.testIn(backgroundScope) + val sideEffects = loginViewModel.uiSideEffect.testIn(backgroundScope) coEvery { mockedAccountRepository.login(any()) } returns LoginResult.Ok // Act, Assert @@ -88,7 +88,7 @@ class LoginViewModelTest { loginViewModel.login(DUMMY_ACCOUNT_TOKEN) assertEquals(Loading.LoggingIn, uiStates.awaitItem().loginState) assertEquals(Success, uiStates.awaitItem().loginState) - assertEquals(LoginViewAction.NavigateToConnect, sideEffects.awaitItem()) + assertEquals(LoginUiSideEffect.NavigateToConnect, sideEffects.awaitItem()) } } @@ -111,7 +111,7 @@ class LoginViewModelTest { turbineScope { // Arrange val uiStates = loginViewModel.uiState.testIn(backgroundScope) - val sideEffects = loginViewModel.viewActions.testIn(backgroundScope) + val sideEffects = loginViewModel.uiSideEffect.testIn(backgroundScope) coEvery { mockedDeviceRepository.refreshAndAwaitDeviceListWithTimeout( any(), @@ -127,7 +127,7 @@ class LoginViewModelTest { loginViewModel.login(DUMMY_ACCOUNT_TOKEN) assertEquals(Loading.LoggingIn, uiStates.awaitItem().loginState) assertEquals( - LoginViewAction.TooManyDevices(AccountToken(DUMMY_ACCOUNT_TOKEN)), + LoginUiSideEffect.TooManyDevices(AccountToken(DUMMY_ACCOUNT_TOKEN)), sideEffects.awaitItem() ) } diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/OutOfTimeViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/OutOfTimeViewModelTest.kt index b12c0382a5c7..5f81032938f0 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/OutOfTimeViewModelTest.kt +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/OutOfTimeViewModelTest.kt @@ -88,10 +88,10 @@ class OutOfTimeViewModelTest { coEvery { mockAuthTokenCache.fetchAuthToken() } returns mockToken // Act, Assert - viewModel.viewActions.test { + viewModel.uiSideEffect.test { viewModel.onSitePaymentClick() val action = awaitItem() - assertIs(action) + assertIs(action) assertEquals(mockToken, action.token) } } @@ -121,10 +121,10 @@ class OutOfTimeViewModelTest { every { mockExpiryDate.isAfter(any()) } returns true // Act, Assert - viewModel.viewActions.test { + viewModel.uiSideEffect.test { accountExpiryState.value = AccountExpiry.Available(mockExpiryDate) val action = awaitItem() - assertIs(action) + assertIs(action) } } diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/WelcomeViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/WelcomeViewModelTest.kt index 1c61b115057b..52713c0b0d31 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/WelcomeViewModelTest.kt +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/WelcomeViewModelTest.kt @@ -95,10 +95,10 @@ class WelcomeViewModelTest { coEvery { mockAuthTokenCache.fetchAuthToken() } returns mockToken // Act, Assert - viewModel.viewActions.test { + viewModel.uiSideEffect.test { viewModel.onSitePaymentClick() val action = awaitItem() - assertIs(action) + assertIs(action) assertEquals(mockToken, action.token) } } @@ -151,10 +151,10 @@ class WelcomeViewModelTest { every { mockExpiryDate.isAfter(any()) } returns true // Act, Assert - viewModel.viewActions.test { + viewModel.uiSideEffect.test { accountExpiryState.value = AccountExpiry.Available(mockExpiryDate) val action = awaitItem() - assertIs(action) + assertIs(action) } } diff --git a/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/ViewModelTests.kt b/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/ViewModelTests.kt index ae1cb0ee81fc..32fe3d76505d 100644 --- a/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/ViewModelTests.kt +++ b/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/ViewModelTests.kt @@ -27,11 +27,11 @@ class ViewModelTests { Konsist.scopeFromProject().classes().withAllParentsOf(ViewModel::class) companion object { - // TODO: The goal is to reduce this list to only "uiState" and "uiAction". + // TODO: The goal is to reduce this list to only "uiState" and "uiSideEffect". private val permittedPublicPropertyNames = listOf( "uiState", - "viewActions", + "uiSideEffect", "toastMessages", "uiCloseAction", "enterTransitionEndAction", From 4f6c9b3f7950128885acca70c0bd908306d7dcb8 Mon Sep 17 00:00:00 2001 From: Albin Date: Fri, 29 Sep 2023 17:41:30 +0200 Subject: [PATCH 4/8] Add konsist check for vm function return type --- .../net/mullvad/mullvadvpn/test/arch/ViewModelTests.kt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/ViewModelTests.kt b/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/ViewModelTests.kt index 32fe3d76505d..42204793aaa8 100644 --- a/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/ViewModelTests.kt +++ b/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/ViewModelTests.kt @@ -2,10 +2,12 @@ package net.mullvad.mullvadvpn.test.arch import androidx.lifecycle.ViewModel import com.lemonappdev.konsist.api.Konsist +import com.lemonappdev.konsist.api.ext.list.functions import com.lemonappdev.konsist.api.ext.list.modifierprovider.withPublicOrDefaultModifier import com.lemonappdev.konsist.api.ext.list.properties import com.lemonappdev.konsist.api.ext.list.withAllParentsOf import com.lemonappdev.konsist.api.verify.assert +import com.lemonappdev.konsist.api.verify.assertNot import org.junit.Test class ViewModelTests { @@ -23,6 +25,13 @@ class ViewModelTests { } } + @Test + fun ensurePublicFunctionsHaveNoReturnType() { + allViewModels().functions().withPublicOrDefaultModifier().assertNot { function -> + function.hasReturnType + } + } + private fun allViewModels() = Konsist.scopeFromProject().classes().withAllParentsOf(ViewModel::class) From 98604abe835f75ad31bea308b8c4173274b03a85 Mon Sep 17 00:00:00 2001 From: Albin Date: Fri, 29 Sep 2023 18:36:07 +0200 Subject: [PATCH 5/8] Bump konsist to 0.12.2 --- android/buildSrc/src/main/kotlin/Versions.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/buildSrc/src/main/kotlin/Versions.kt b/android/buildSrc/src/main/kotlin/Versions.kt index 553c952748e1..15892d5cd914 100644 --- a/android/buildSrc/src/main/kotlin/Versions.kt +++ b/android/buildSrc/src/main/kotlin/Versions.kt @@ -3,7 +3,7 @@ object Versions { const val jodaTime = "2.12.5" const val junit = "4.13.2" const val jvmTarget = "17" - const val konsist = "0.11.0" + const val konsist = "0.12.2" const val kotlin = "1.9.10" const val kotlinCompilerExtensionVersion = "1.5.3" const val kotlinx = "1.7.3" From 69a082c5a2c77c94fe990c116cc94455d68d517c Mon Sep 17 00:00:00 2001 From: Albin Date: Fri, 29 Sep 2023 18:51:59 +0200 Subject: [PATCH 6/8] Update gradle lockfile --- android/gradle/verification-metadata.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/android/gradle/verification-metadata.xml b/android/gradle/verification-metadata.xml index 6aca8d73fad0..3b71ce2f5a9f 100644 --- a/android/gradle/verification-metadata.xml +++ b/android/gradle/verification-metadata.xml @@ -2222,12 +2222,12 @@ - - - + + + - - + + From cc230f6a6d605b4a5caa4451e88d8f9adaac9837 Mon Sep 17 00:00:00 2001 From: Albin Date: Fri, 29 Sep 2023 19:32:34 +0200 Subject: [PATCH 7/8] Fix deprecated type check call --- .../kotlin/net/mullvad/mullvadvpn/test/arch/ViewModelTests.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/ViewModelTests.kt b/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/ViewModelTests.kt index 42204793aaa8..47736e96111f 100644 --- a/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/ViewModelTests.kt +++ b/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/ViewModelTests.kt @@ -28,7 +28,7 @@ class ViewModelTests { @Test fun ensurePublicFunctionsHaveNoReturnType() { allViewModels().functions().withPublicOrDefaultModifier().assertNot { function -> - function.hasReturnType + function.hasReturnType() } } From 8af8d3c266c032ebb1f7019fa92aec02df9053b8 Mon Sep 17 00:00:00 2001 From: Albin Date: Fri, 29 Sep 2023 19:31:53 +0200 Subject: [PATCH 8/8] Suppress konsist TODOs --- .../mullvadvpn/viewmodel/AccountViewModel.kt | 1 + .../viewmodel/DeviceListViewModel.kt | 3 ++- .../viewmodel/SelectLocationViewModel.kt | 2 ++ .../mullvadvpn/viewmodel/SettingsViewModel.kt | 1 + .../viewmodel/VpnSettingsViewModel.kt | 1 + .../mullvadvpn/test/arch/ViewModelTests.kt | 18 +++--------------- 6 files changed, 10 insertions(+), 16 deletions(-) diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/AccountViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/AccountViewModel.kt index 425a64dd71b4..4099e524807d 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/AccountViewModel.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/AccountViewModel.kt @@ -38,6 +38,7 @@ class AccountViewModel( } .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), AccountUiState.default()) + @Suppress("konsist.ensurePublicPropertiesUsePermittedNames") val enterTransitionEndAction = _enterTransitionEndAction.asSharedFlow() fun onManageAccountClick() { diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/DeviceListViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/DeviceListViewModel.kt index 88337aea00e9..c554bd4daa48 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/DeviceListViewModel.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/DeviceListViewModel.kt @@ -37,9 +37,10 @@ class DeviceListViewModel( private val _loadingDevices = MutableStateFlow>(emptyList()) private val _toastMessages = MutableSharedFlow(extraBufferCapacity = 1) + @Suppress("konsist.ensurePublicPropertiesUsePermittedNames") val toastMessages = _toastMessages.asSharedFlow() - var accountToken: String? = null + @Suppress("konsist.ensurePublicPropertiesUsePermittedNames") var accountToken: String? = null private var cachedDeviceList: List? = null val uiState = diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModel.kt index 4b7bca151d69..2ae361c96440 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModel.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModel.kt @@ -66,7 +66,9 @@ class SelectLocationViewModel(private val serviceConnectionManager: ServiceConne SelectLocationUiState.Loading ) + @Suppress("konsist.ensurePublicPropertiesUsePermittedNames") val uiCloseAction = _closeAction.asSharedFlow() + @Suppress("konsist.ensurePublicPropertiesUsePermittedNames") val enterTransitionEndAction = _enterTransitionEndAction.asSharedFlow() fun selectRelay(relayItem: RelayItem?) { diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SettingsViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SettingsViewModel.kt index eaeaf33933e8..89adbe20befd 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SettingsViewModel.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SettingsViewModel.kt @@ -45,6 +45,7 @@ class SettingsViewModel( SettingsUiState(appVersion = "", isLoggedIn = false, isUpdateAvailable = false) ) + @Suppress("konsist.ensurePublicPropertiesUsePermittedNames") val enterTransitionEndAction = _enterTransitionEndAction.asSharedFlow() fun onTransitionAnimationEnd() { diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModel.kt index 5bd56f150d20..7d3344d9487d 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModel.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModel.kt @@ -51,6 +51,7 @@ class VpnSettingsViewModel( ) : ViewModel() { private val _toastMessages = MutableSharedFlow(extraBufferCapacity = 1) + @Suppress("konsist.ensurePublicPropertiesUsePermittedNames") val toastMessages = _toastMessages.asSharedFlow() private val dialogState = diff --git a/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/ViewModelTests.kt b/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/ViewModelTests.kt index 47736e96111f..8347c799d71f 100644 --- a/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/ViewModelTests.kt +++ b/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/ViewModelTests.kt @@ -20,8 +20,9 @@ class ViewModelTests { // properties that shouldn't be exposed. @Test fun ensurePublicPropertiesUsePermittedNames() { - allViewModels().properties().withPublicOrDefaultModifier().assert { property -> - permittedPublicPropertyNames.contains(property.name) + allViewModels().properties(includeNested = false).withPublicOrDefaultModifier().assert { + property -> + property.name == "uiState" || property.name == "uiSideEffect" } } @@ -34,17 +35,4 @@ class ViewModelTests { private fun allViewModels() = Konsist.scopeFromProject().classes().withAllParentsOf(ViewModel::class) - - companion object { - // TODO: The goal is to reduce this list to only "uiState" and "uiSideEffect". - private val permittedPublicPropertyNames = - listOf( - "uiState", - "uiSideEffect", - "toastMessages", - "uiCloseAction", - "enterTransitionEndAction", - "accountToken" - ) - } }