summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDavid Göransson <david.goransson@mullvad.net>2024-07-04 12:07:28 +0200
committerDavid Göransson <david.goransson@mullvad.net>2024-07-04 12:07:28 +0200
commit8263dc7ad41f07e2cfdeab9d24ec82c638ca6c0f (patch)
treed7af39cfc3befda56bb059fe3aea82a33f299489
parent73bba2cfb78b789241db09ff5e39f638ecfad5ac (diff)
parent76f57d2a131aa998fadaa7b9a80f7435bddec8b9 (diff)
downloadmullvadvpn-8263dc7ad41f07e2cfdeab9d24ec82c638ca6c0f.tar.xz
mullvadvpn-8263dc7ad41f07e2cfdeab9d24ec82c638ca6c0f.zip
Merge branch 'investigateclean-up-arguments-for-destinations-droid-731'
-rw-r--r--android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/DeleteCustomListConfirmationDialogTest.kt9
-rw-r--r--android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/EditCustomListNameDialogTest.kt4
-rw-r--r--android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/EditCustomListScreenTest.kt4
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/CreateCustomListDialog.kt12
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/DeleteApiAccessMethodConfirmationDialog.kt14
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/DeleteCustomListConfirmationDialog.kt28
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/DnsDialog.kt13
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/EditCustomListNameDialog.kt39
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/MtuDialog.kt11
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/SaveApiAccessMethodDialog.kt20
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ApiAccessMethodDetailsScreen.kt14
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/CustomListLocationsScreen.kt18
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/CustomListsScreen.kt2
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceListScreen.kt24
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/EditApiAccessMethodScreen.kt12
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/EditCustomListScreen.kt36
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/LoginScreen.kt2
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/DeleteCustomListUiState.kt3
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/EditCustomListNameUiState.kt4
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt46
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ApiAccessMethodDetailsViewModel.kt9
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/CreateCustomListDialogViewModel.kt7
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/CustomListLocationsViewModel.kt24
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/DeleteApiAccessMethodConfirmationViewModel.kt9
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/DeleteCustomListConfirmationViewModel.kt15
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/DeviceListViewModel.kt22
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/DnsDialogViewModel.kt10
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/EditApiAccessMethodViewModel.kt8
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/EditCustomListNameDialogViewModel.kt25
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/EditCustomListViewModel.kt9
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/MtuDialogViewModel.kt10
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SaveApiAccessMethodViewModel.kt13
-rw-r--r--android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ApiAccessMethodDetailsViewModelTest.kt8
-rw-r--r--android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/CreateCustomListDialogViewModelTest.kt10
-rw-r--r--android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/CustomListLocationsViewModelTest.kt12
-rw-r--r--android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/DeleteApiAccessMethodConfirmationViewModelTest.kt8
-rw-r--r--android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/DeleteCustomListConfirmationViewModelTest.kt12
-rw-r--r--android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/EditCustomListNameDialogViewModelTest.kt15
-rw-r--r--android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/EditCustomListViewModelTest.kt30
-rw-r--r--android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/SaveApiAccessMethodViewModelTest.kt14
-rw-r--r--android/config/baseline.xml4
-rw-r--r--android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/RelayItemId.kt3
42 files changed, 337 insertions, 255 deletions
diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/DeleteCustomListConfirmationDialogTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/DeleteCustomListConfirmationDialogTest.kt
index ee347c246a..184cd7defe 100644
--- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/DeleteCustomListConfirmationDialogTest.kt
+++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/DeleteCustomListConfirmationDialogTest.kt
@@ -32,8 +32,7 @@ class DeleteCustomListConfirmationDialogTest {
val name = CustomListName.fromString("List should be deleted")
setContentWithTheme {
DeleteCustomListConfirmationDialog(
- name = name,
- state = DeleteCustomListUiState(null)
+ state = DeleteCustomListUiState(name = name, deleteError = null),
)
}
@@ -49,8 +48,7 @@ class DeleteCustomListConfirmationDialogTest {
val mockedOnDelete: () -> Unit = mockk(relaxed = true)
setContentWithTheme {
DeleteCustomListConfirmationDialog(
- name = name,
- state = DeleteCustomListUiState(null),
+ state = DeleteCustomListUiState(name = name, deleteError = null),
onDelete = mockedOnDelete
)
}
@@ -70,8 +68,7 @@ class DeleteCustomListConfirmationDialogTest {
val mockedOnBack: () -> Unit = mockk(relaxed = true)
setContentWithTheme {
DeleteCustomListConfirmationDialog(
- name = name,
- state = DeleteCustomListUiState(null),
+ state = DeleteCustomListUiState(name = name, deleteError = null),
onBack = mockedOnBack
)
}
diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/EditCustomListNameDialogTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/EditCustomListNameDialogTest.kt
index 3128bbc508..6bca03db69 100644
--- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/EditCustomListNameDialogTest.kt
+++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/EditCustomListNameDialogTest.kt
@@ -126,7 +126,7 @@ class EditCustomListNameDialogTest {
fun whenInputIsChangedShouldCallOnInputChanged() =
composeExtension.use {
// Arrange
- val mockedOnInputChanged: () -> Unit = mockk(relaxed = true)
+ val mockedOnInputChanged: (String) -> Unit = mockk(relaxed = true)
val inputText = "NEW NAME"
val state = EditCustomListNameUiState()
setContentWithTheme {
@@ -137,7 +137,7 @@ class EditCustomListNameDialogTest {
onNodeWithTag(EDIT_CUSTOM_LIST_DIALOG_INPUT_TEST_TAG).performTextInput(inputText)
// Assert
- verify { mockedOnInputChanged.invoke() }
+ verify { mockedOnInputChanged.invoke(inputText) }
}
companion object {
diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/EditCustomListScreenTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/EditCustomListScreenTest.kt
index 5e57309777..2bc53013ca 100644
--- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/EditCustomListScreenTest.kt
+++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/EditCustomListScreenTest.kt
@@ -93,7 +93,7 @@ class EditCustomListScreenTest {
fun whenClickingOnDeleteDropdownShouldCallOnDeleteList() =
composeExtension.use {
// Arrange
- val mockedOnDelete: (CustomListName) -> Unit = mockk(relaxed = true)
+ val mockedOnDelete: (CustomListId, CustomListName) -> Unit = mockk(relaxed = true)
val customList = DUMMY_CUSTOM_LISTS[0]
setContentWithTheme {
EditCustomListScreen(
@@ -112,7 +112,7 @@ class EditCustomListScreenTest {
onNodeWithTag(DELETE_DROPDOWN_MENU_ITEM_TEST_TAG).performClick()
// Assert
- verify { mockedOnDelete(customList.name) }
+ verify { mockedOnDelete(customList.id, customList.name) }
}
@Test
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/CreateCustomListDialog.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/CreateCustomListDialog.kt
index 9e9cb5b78d..db7f664e37 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/CreateCustomListDialog.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/CreateCustomListDialog.kt
@@ -34,7 +34,6 @@ import net.mullvad.mullvadvpn.usecase.customlists.CreateWithLocationsError
import net.mullvad.mullvadvpn.viewmodel.CreateCustomListDialogSideEffect
import net.mullvad.mullvadvpn.viewmodel.CreateCustomListDialogViewModel
import org.koin.androidx.compose.koinViewModel
-import org.koin.core.parameter.parametersOf
@Preview
@Composable
@@ -55,15 +54,18 @@ private fun PreviewCreateCustomListDialogError() {
}
}
+data class CreateCustomListNavArgs(val locationCode: GeoLocationId?)
+
@Composable
-@Destination<RootGraph>(style = DestinationStyle.Dialog::class)
+@Destination<RootGraph>(
+ style = DestinationStyle.Dialog::class,
+ navArgs = CreateCustomListNavArgs::class
+)
fun CreateCustomList(
navigator: DestinationsNavigator,
backNavigator: ResultBackNavigator<Created>,
- locationCode: GeoLocationId? = null
) {
- val vm: CreateCustomListDialogViewModel =
- koinViewModel(parameters = { parametersOf(locationCode) })
+ val vm: CreateCustomListDialogViewModel = koinViewModel()
LaunchedEffect(key1 = Unit) {
vm.uiSideEffect.collect { sideEffect ->
when (sideEffect) {
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/DeleteApiAccessMethodConfirmationDialog.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/DeleteApiAccessMethodConfirmationDialog.kt
index 021a78e5eb..6472f7d1b2 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/DeleteApiAccessMethodConfirmationDialog.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/DeleteApiAccessMethodConfirmationDialog.kt
@@ -16,7 +16,6 @@ import net.mullvad.mullvadvpn.lib.theme.AppTheme
import net.mullvad.mullvadvpn.viewmodel.DeleteApiAccessMethodConfirmationSideEffect
import net.mullvad.mullvadvpn.viewmodel.DeleteApiAccessMethodConfirmationViewModel
import org.koin.androidx.compose.koinViewModel
-import org.koin.core.parameter.parametersOf
@Preview
@Composable
@@ -24,16 +23,17 @@ private fun PreviewDeleteApiAccessMethodConfirmationDialog() {
AppTheme { DeleteApiAccessMethodConfirmationDialog(state = DeleteApiAccessMethodUiState(null)) }
}
+data class DeleteApiAccessMethodNavArgs(val apiAccessMethodId: ApiAccessMethodId)
+
@Composable
-@Destination<RootGraph>(style = DestinationStyle.Dialog::class)
+@Destination<RootGraph>(
+ style = DestinationStyle.Dialog::class,
+ navArgs = DeleteApiAccessMethodNavArgs::class
+)
fun DeleteApiAccessMethodConfirmation(
navigator: ResultBackNavigator<Boolean>,
- apiAccessMethodId: ApiAccessMethodId
) {
- val viewModel =
- koinViewModel<DeleteApiAccessMethodConfirmationViewModel>(
- parameters = { parametersOf(apiAccessMethodId) }
- )
+ val viewModel = koinViewModel<DeleteApiAccessMethodConfirmationViewModel>()
val state = viewModel.uiState.collectAsStateWithLifecycle()
LaunchedEffectCollect(viewModel.uiSideEffect) {
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/DeleteCustomListConfirmationDialog.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/DeleteCustomListConfirmationDialog.kt
index 22598d3d2d..0f26bcbe48 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/DeleteCustomListConfirmationDialog.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/DeleteCustomListConfirmationDialog.kt
@@ -1,6 +1,7 @@
package net.mullvad.mullvadvpn.compose.dialog
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.lifecycle.compose.collectAsStateWithLifecycle
@@ -19,29 +20,29 @@ import net.mullvad.mullvadvpn.lib.theme.AppTheme
import net.mullvad.mullvadvpn.viewmodel.DeleteCustomListConfirmationSideEffect
import net.mullvad.mullvadvpn.viewmodel.DeleteCustomListConfirmationViewModel
import org.koin.androidx.compose.koinViewModel
-import org.koin.core.parameter.parametersOf
@Preview
@Composable
private fun PreviewRemoveDeviceConfirmationDialog() {
AppTheme {
DeleteCustomListConfirmationDialog(
- state = DeleteCustomListUiState(null),
- name = CustomListName.fromString("My Custom List")
+ state = DeleteCustomListUiState(CustomListName.fromString("My Custom List"), null)
)
}
}
+data class DeleteCustomListNavArgs(val customListId: CustomListId, val name: CustomListName)
+
@Composable
-@Destination<RootGraph>(style = DestinationStyle.Dialog::class)
+@Destination<RootGraph>(
+ style = DestinationStyle.Dialog::class,
+ navArgs = DeleteCustomListNavArgs::class
+)
fun DeleteCustomList(
navigator: ResultBackNavigator<Deleted>,
- customListId: CustomListId,
- name: CustomListName
) {
- val viewModel: DeleteCustomListConfirmationViewModel =
- koinViewModel(parameters = { parametersOf(customListId) })
- val state = viewModel.uiState.collectAsStateWithLifecycle()
+ val viewModel: DeleteCustomListConfirmationViewModel = koinViewModel()
+ val state by viewModel.uiState.collectAsStateWithLifecycle()
LaunchedEffectCollect(viewModel.uiSideEffect) {
when (it) {
@@ -51,8 +52,7 @@ fun DeleteCustomList(
}
DeleteCustomListConfirmationDialog(
- state = state.value,
- name = name,
+ state = state,
onDelete = viewModel::deleteCustomList,
onBack = dropUnlessResumed { navigator.navigateBack() }
)
@@ -61,7 +61,6 @@ fun DeleteCustomList(
@Composable
fun DeleteCustomListConfirmationDialog(
state: DeleteCustomListUiState,
- name: CustomListName,
onDelete: () -> Unit = {},
onBack: () -> Unit = {}
) {
@@ -69,7 +68,10 @@ fun DeleteCustomListConfirmationDialog(
onDelete = onDelete,
onBack = onBack,
message =
- stringResource(id = R.string.delete_custom_list_confirmation_description, name.value),
+ stringResource(
+ id = R.string.delete_custom_list_confirmation_description,
+ state.name.value
+ ),
errorMessage =
if (state.deleteError != null) {
stringResource(id = R.string.error_occurred)
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/DnsDialog.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/DnsDialog.kt
index 8db8caffe5..518539dd64 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/DnsDialog.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/DnsDialog.kt
@@ -32,7 +32,6 @@ import net.mullvad.mullvadvpn.viewmodel.DnsDialogViewModel
import net.mullvad.mullvadvpn.viewmodel.DnsDialogViewState
import net.mullvad.mullvadvpn.viewmodel.ValidationError
import org.koin.androidx.compose.koinViewModel
-import org.koin.core.parameter.parametersOf
@Preview
@Composable
@@ -52,15 +51,17 @@ private fun PreviewDnsDialogEditAllowLanDisabled() {
AppTheme { DnsDialog(DnsDialogViewState("192.168.1.1", null, true, false, 0), {}, {}, {}, {}) }
}
-@Destination<RootGraph>(style = DestinationStyle.Dialog::class)
+data class DnsDialogNavArgs(
+ val index: Int? = null,
+ val initialValue: String? = null,
+)
+
+@Destination<RootGraph>(style = DestinationStyle.Dialog::class, navArgs = DnsDialogNavArgs::class)
@Composable
fun DnsDialog(
resultNavigator: ResultBackNavigator<DnsDialogResult>,
- index: Int?,
- initialValue: String?,
) {
- val viewModel =
- koinViewModel<DnsDialogViewModel>(parameters = { parametersOf(initialValue, index) })
+ val viewModel = koinViewModel<DnsDialogViewModel>()
LaunchedEffectCollect(viewModel.uiSideEffect) {
when (it) {
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/EditCustomListNameDialog.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/EditCustomListNameDialog.kt
index 42fe1cbf82..3a6d234fc6 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/EditCustomListNameDialog.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/EditCustomListNameDialog.kt
@@ -4,10 +4,7 @@ import androidx.compose.material3.AlertDialog
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.stringResource
@@ -35,7 +32,6 @@ import net.mullvad.mullvadvpn.usecase.customlists.RenameError
import net.mullvad.mullvadvpn.viewmodel.EditCustomListNameDialogSideEffect
import net.mullvad.mullvadvpn.viewmodel.EditCustomListNameDialogViewModel
import org.koin.androidx.compose.koinViewModel
-import org.koin.core.parameter.parametersOf
@Preview
@Composable
@@ -43,15 +39,20 @@ private fun PreviewEditCustomListNameDialog() {
AppTheme { EditCustomListNameDialog(EditCustomListNameUiState()) }
}
+data class EditCustomListNameNavArgs(
+ val customListId: CustomListId,
+ val initialName: CustomListName
+)
+
@Composable
-@Destination<RootGraph>(style = DestinationStyle.Dialog::class)
+@Destination<RootGraph>(
+ style = DestinationStyle.Dialog::class,
+ navArgs = EditCustomListNameNavArgs::class
+)
fun EditCustomListName(
backNavigator: ResultBackNavigator<Renamed>,
- customListId: CustomListId,
- initialName: CustomListName
) {
- val vm: EditCustomListNameDialogViewModel =
- koinViewModel(parameters = { parametersOf(customListId, initialName) })
+ val vm: EditCustomListNameDialogViewModel = koinViewModel()
LaunchedEffectCollect(vm.uiSideEffect) { sideEffect ->
when (sideEffect) {
is EditCustomListNameDialogSideEffect.ReturnWithResult -> {
@@ -64,7 +65,7 @@ fun EditCustomListName(
EditCustomListNameDialog(
state = state,
updateName = vm::updateCustomListName,
- onInputChanged = vm::clearError,
+ onInputChanged = vm::onNameChanged,
onDismiss = dropUnlessResumed { backNavigator.navigateBack() }
)
}
@@ -73,12 +74,9 @@ fun EditCustomListName(
fun EditCustomListNameDialog(
state: EditCustomListNameUiState,
updateName: (String) -> Unit = {},
- onInputChanged: () -> Unit = {},
+ onInputChanged: (String) -> Unit = {},
onDismiss: () -> Unit = {}
) {
- val name = remember { mutableStateOf(state.name) }
- val isValidName by remember { derivedStateOf { name.value.isNotBlank() } }
-
AlertDialog(
title = {
Text(
@@ -87,14 +85,11 @@ fun EditCustomListNameDialog(
},
text = {
CustomListNameTextField(
- name = name.value,
- isValidName = isValidName,
+ name = state.name,
+ isValidName = state.isValidName,
error = state.error?.errorString(),
onSubmit = updateName,
- onValueChanged = {
- name.value = it
- onInputChanged()
- },
+ onValueChanged = onInputChanged,
modifier = Modifier.testTag(EDIT_CUSTOM_LIST_DIALOG_INPUT_TEST_TAG)
)
},
@@ -104,8 +99,8 @@ fun EditCustomListNameDialog(
confirmButton = {
PrimaryButton(
text = stringResource(id = R.string.save),
- onClick = { updateName(name.value) },
- isEnabled = isValidName
+ onClick = { updateName(state.name) },
+ isEnabled = state.isValidName
)
},
dismissButton = {
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/MtuDialog.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/MtuDialog.kt
index e3d44c0fd5..f90182640d 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/MtuDialog.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/MtuDialog.kt
@@ -34,18 +34,19 @@ import net.mullvad.mullvadvpn.viewmodel.MtuDialogSideEffect
import net.mullvad.mullvadvpn.viewmodel.MtuDialogUiState
import net.mullvad.mullvadvpn.viewmodel.MtuDialogViewModel
import org.koin.androidx.compose.koinViewModel
-import org.koin.core.parameter.parametersOf
@Preview
@Composable
private fun PreviewMtuDialog() {
- AppTheme { MtuDialog(mtuInitial = Mtu(1234), EmptyResultBackNavigator()) }
+ AppTheme { MtuDialog(EmptyResultBackNavigator()) }
}
-@Destination<RootGraph>(style = DestinationStyle.Dialog::class)
+data class MtuNavArgs(val initialMtu: Mtu? = null)
+
+@Destination<RootGraph>(style = DestinationStyle.Dialog::class, navArgs = MtuNavArgs::class)
@Composable
-fun MtuDialog(mtuInitial: Mtu?, navigator: ResultBackNavigator<Boolean>) {
- val viewModel = koinViewModel<MtuDialogViewModel>(parameters = { parametersOf(mtuInitial) })
+fun MtuDialog(navigator: ResultBackNavigator<Boolean>) {
+ val viewModel = koinViewModel<MtuDialogViewModel>()
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
LaunchedEffectCollect(viewModel.uiSideEffect) {
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/SaveApiAccessMethodDialog.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/SaveApiAccessMethodDialog.kt
index 19f02d4802..be13b62dec 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/SaveApiAccessMethodDialog.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/SaveApiAccessMethodDialog.kt
@@ -35,7 +35,6 @@ import net.mullvad.mullvadvpn.lib.theme.AppTheme
import net.mullvad.mullvadvpn.viewmodel.SaveApiAccessMethodSideEffect
import net.mullvad.mullvadvpn.viewmodel.SaveApiAccessMethodViewModel
import org.koin.androidx.compose.koinViewModel
-import org.koin.core.parameter.parametersOf
@Preview
@Composable
@@ -46,18 +45,21 @@ private fun PreviewSaveApiAccessMethodDialog(
AppTheme { SaveApiAccessMethodDialog(state = state) }
}
-@Destination<RootGraph>(style = DestinationStyle.Dialog::class)
+data class SaveApiAccessMethodNavArgs(
+ val id: ApiAccessMethodId?,
+ val name: ApiAccessMethodName,
+ val customProxy: ApiAccessMethod.CustomProxy
+)
+
+@Destination<RootGraph>(
+ style = DestinationStyle.Dialog::class,
+ navArgs = SaveApiAccessMethodNavArgs::class
+)
@Composable
fun SaveApiAccessMethod(
backNavigator: ResultBackNavigator<Boolean>,
- id: ApiAccessMethodId?,
- name: ApiAccessMethodName,
- customProxy: ApiAccessMethod.CustomProxy
) {
- val viewModel =
- koinViewModel<SaveApiAccessMethodViewModel>(
- parameters = { parametersOf(id, name, customProxy) }
- )
+ val viewModel = koinViewModel<SaveApiAccessMethodViewModel>()
LaunchedEffectCollect(sideEffect = viewModel.uiSideEffect) {
when (it) {
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ApiAccessMethodDetailsScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ApiAccessMethodDetailsScreen.kt
index d5aa7dfa0e..f876a0790f 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ApiAccessMethodDetailsScreen.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ApiAccessMethodDetailsScreen.kt
@@ -65,7 +65,6 @@ import net.mullvad.mullvadvpn.lib.theme.color.menuItemColors
import net.mullvad.mullvadvpn.viewmodel.ApiAccessMethodDetailsSideEffect
import net.mullvad.mullvadvpn.viewmodel.ApiAccessMethodDetailsViewModel
import org.koin.androidx.compose.koinViewModel
-import org.koin.core.parameter.parametersOf
@Preview
@Composable
@@ -76,18 +75,19 @@ private fun PreviewApiAccessMethodDetailsScreen(
AppTheme { ApiAccessMethodDetailsScreen(state = state) }
}
-@Destination<RootGraph>(style = SlideInFromRightTransition::class)
+data class ApiAccessMethodDetailsNavArgs(val accessMethodId: ApiAccessMethodId)
+
+@Destination<RootGraph>(
+ style = SlideInFromRightTransition::class,
+ navArgs = ApiAccessMethodDetailsNavArgs::class
+)
@Composable
fun ApiAccessMethodDetails(
navigator: DestinationsNavigator,
- accessMethodId: ApiAccessMethodId,
confirmDeleteListResultRecipient:
ResultRecipient<DeleteApiAccessMethodConfirmationDestination, Boolean>
) {
- val viewModel =
- koinViewModel<ApiAccessMethodDetailsViewModel>(
- parameters = { parametersOf(accessMethodId) }
- )
+ val viewModel = koinViewModel<ApiAccessMethodDetailsViewModel>()
val snackbarHostState = remember { SnackbarHostState() }
val context = LocalContext.current
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/CustomListLocationsScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/CustomListLocationsScreen.kt
index 4d60c06b0a..9350b8776e 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/CustomListLocationsScreen.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/CustomListLocationsScreen.kt
@@ -60,7 +60,6 @@ import net.mullvad.mullvadvpn.lib.theme.color.AlphaScrollbar
import net.mullvad.mullvadvpn.viewmodel.CustomListLocationsSideEffect
import net.mullvad.mullvadvpn.viewmodel.CustomListLocationsViewModel
import org.koin.androidx.compose.koinViewModel
-import org.koin.core.parameter.parametersOf
@Composable
@Preview
@@ -68,19 +67,22 @@ private fun PreviewCustomListLocationScreen() {
AppTheme { CustomListLocationsScreen(state = CustomListLocationsUiState.Content.Data()) }
}
+data class CustomListLocationsNavArgs(
+ val customListId: CustomListId,
+ val newList: Boolean,
+)
+
@Composable
-@Destination<RootGraph>(style = SlideInFromRightTransition::class)
+@Destination<RootGraph>(
+ style = SlideInFromRightTransition::class,
+ navArgs = CustomListLocationsNavArgs::class
+)
fun CustomListLocations(
navigator: DestinationsNavigator,
backNavigator: ResultBackNavigator<LocationsChanged>,
discardChangesResultRecipient: ResultRecipient<DiscardChangesDialogDestination, Boolean>,
- customListId: CustomListId,
- newList: Boolean,
) {
- val customListsViewModel =
- koinViewModel<CustomListLocationsViewModel>(
- parameters = { parametersOf(customListId, newList) }
- )
+ val customListsViewModel = koinViewModel<CustomListLocationsViewModel>()
discardChangesResultRecipient.onNavResult {
when (it) {
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/CustomListsScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/CustomListsScreen.kt
index 8cdef9bd76..5b9f0d4cc1 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/CustomListsScreen.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/CustomListsScreen.kt
@@ -98,7 +98,7 @@ fun CustomLists(
addCustomList =
dropUnlessResumed {
navigator.navigate(
- CreateCustomListDestination(),
+ CreateCustomListDestination(null),
)
},
openCustomList =
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceListScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceListScreen.kt
index 1ea7d9e80c..74d4714d6d 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceListScreen.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceListScreen.kt
@@ -70,7 +70,6 @@ import net.mullvad.mullvadvpn.util.formatDate
import net.mullvad.mullvadvpn.viewmodel.DeviceListSideEffect
import net.mullvad.mullvadvpn.viewmodel.DeviceListViewModel
import org.koin.androidx.compose.koinViewModel
-import org.koin.core.parameter.parametersOf
@Composable
@Preview
@@ -103,18 +102,16 @@ private fun PreviewDeviceListError() {
}
}
-@Destination<RootGraph>(style = DefaultTransition::class)
+data class DeviceListNavArgs(val accountNumber: AccountNumber)
+
+@Destination<RootGraph>(style = DefaultTransition::class, navArgs = DeviceListNavArgs::class)
@Composable
fun DeviceList(
navigator: DestinationsNavigator,
- accountNumber: String,
confirmRemoveResultRecipient:
ResultRecipient<RemoveDeviceConfirmationDialogDestination, DeviceId>
) {
- val viewModel =
- koinViewModel<DeviceListViewModel>(
- parameters = { parametersOf(AccountNumber(accountNumber)) }
- )
+ val viewModel = koinViewModel<DeviceListViewModel>()
val state by viewModel.uiState.collectAsStateWithLifecycle()
confirmRemoveResultRecipient.onNavResult {
@@ -142,6 +139,11 @@ fun DeviceList(
)
}
}
+ is DeviceListSideEffect.NavigateToLogin ->
+ navigator.navigate(LoginDestination(sideEffect.accountNumber.value)) {
+ launchSingleTop = true
+ popUpTo(LoginDestination) { inclusive = true }
+ }
}
}
@@ -149,13 +151,7 @@ fun DeviceList(
state = state,
snackbarHostState = snackbarHostState,
onBackClick = dropUnlessResumed { navigator.navigateUp() },
- onContinueWithLogin =
- dropUnlessResumed {
- navigator.navigate(LoginDestination(accountNumber)) {
- launchSingleTop = true
- popUpTo(LoginDestination) { inclusive = true }
- }
- },
+ onContinueWithLogin = viewModel::continueToLogin,
onSettingsClicked = dropUnlessResumed { navigator.navigate(SettingsDestination) },
onTryAgainClicked = viewModel::fetchDevices,
navigateToRemoveDeviceConfirmationDialog =
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/EditApiAccessMethodScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/EditApiAccessMethodScreen.kt
index 3d2926b90c..84162e501e 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/EditApiAccessMethodScreen.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/EditApiAccessMethodScreen.kt
@@ -72,7 +72,6 @@ import net.mullvad.mullvadvpn.lib.theme.color.AlphaVisible
import net.mullvad.mullvadvpn.viewmodel.EditApiAccessMethodViewModel
import net.mullvad.mullvadvpn.viewmodel.EditApiAccessSideEffect
import org.koin.androidx.compose.koinViewModel
-import org.koin.core.parameter.parametersOf
@Preview
@Composable
@@ -83,17 +82,20 @@ private fun PreviewEditApiAccessMethodScreen(
AppTheme { EditApiAccessMethodScreen(state = state) }
}
-@Destination<RootGraph>(style = SlideInFromRightTransition::class)
+data class EditApiAccessMethodNavArgs(val accessMethodId: ApiAccessMethodId?)
+
+@Destination<RootGraph>(
+ style = SlideInFromRightTransition::class,
+ navArgs = EditApiAccessMethodNavArgs::class
+)
@Composable
fun EditApiAccessMethod(
navigator: DestinationsNavigator,
backNavigator: ResultBackNavigator<Boolean>,
saveApiAccessMethodResultRecipient: ResultRecipient<SaveApiAccessMethodDestination, Boolean>,
discardChangesResultRecipient: ResultRecipient<DiscardChangesDialogDestination, Boolean>,
- accessMethodId: ApiAccessMethodId?
) {
- val viewModel =
- koinViewModel<EditApiAccessMethodViewModel>(parameters = { parametersOf(accessMethodId) })
+ val viewModel = koinViewModel<EditApiAccessMethodViewModel>()
val snackbarHostState = remember { SnackbarHostState() }
val context = LocalContext.current
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/EditCustomListScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/EditCustomListScreen.kt
index 341c8e9f16..c3f7662ea2 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/EditCustomListScreen.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/EditCustomListScreen.kt
@@ -52,7 +52,6 @@ import net.mullvad.mullvadvpn.lib.theme.Dimens
import net.mullvad.mullvadvpn.lib.theme.color.menuItemColors
import net.mullvad.mullvadvpn.viewmodel.EditCustomListViewModel
import org.koin.androidx.compose.koinViewModel
-import org.koin.core.parameter.parametersOf
@Preview
@Composable
@@ -78,16 +77,19 @@ private fun PreviewEditCustomListScreen() {
}
}
+data class EditCustomListNavArgs(val customListId: CustomListId)
+
@Composable
-@Destination<RootGraph>(style = SlideInFromRightTransition::class)
+@Destination<RootGraph>(
+ style = SlideInFromRightTransition::class,
+ navArgs = EditCustomListNavArgs::class
+)
fun EditCustomList(
navigator: DestinationsNavigator,
backNavigator: ResultBackNavigator<Deleted>,
- customListId: CustomListId,
confirmDeleteListResultRecipient: ResultRecipient<DeleteCustomListDestination, Deleted>
) {
- val viewModel =
- koinViewModel<EditCustomListViewModel>(parameters = { parametersOf(customListId) })
+ val viewModel = koinViewModel<EditCustomListViewModel>()
confirmDeleteListResultRecipient.onNavResult {
when (it) {
@@ -103,9 +105,9 @@ fun EditCustomList(
EditCustomListScreen(
state = state,
onDeleteList =
- dropUnlessResumed { name ->
+ dropUnlessResumed { id, name ->
navigator.navigate(
- DeleteCustomListDestination(customListId = customListId, name = name),
+ DeleteCustomListDestination(customListId = id, name = name),
)
},
onNameClicked =
@@ -127,21 +129,25 @@ fun EditCustomList(
@Composable
fun EditCustomListScreen(
state: EditCustomListState,
- onDeleteList: (name: CustomListName) -> Unit = {},
+ onDeleteList: (id: CustomListId, name: CustomListName) -> Unit = { _, _ -> },
onNameClicked: (id: CustomListId, name: CustomListName) -> Unit = { _, _ -> },
onLocationsClicked: (CustomListId) -> Unit = {},
onBackClick: () -> Unit = {}
) {
- val title =
- when (state) {
- EditCustomListState.Loading,
- EditCustomListState.NotFound -> null
- is EditCustomListState.Content -> state.name
- }
ScaffoldWithMediumTopBar(
appBarTitle = stringResource(id = R.string.edit_list),
navigationIcon = { NavigateBackIconButton(onNavigateBack = onBackClick) },
- actions = { Actions(enabled = title != null, onDeleteList = { onDeleteList(title!!) }) },
+ actions = {
+ val content = state as? EditCustomListState.Content
+ Actions(
+ enabled = content?.name != null,
+ onDeleteList = {
+ if (content is EditCustomListState.Content) {
+ onDeleteList(content.id, content.name)
+ }
+ }
+ )
+ },
) { modifier: Modifier ->
SpacedColumn(modifier = modifier, alignment = Alignment.Top) {
when (state) {
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/LoginScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/LoginScreen.kt
index 3cb51614c2..88d5ef57f2 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/LoginScreen.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/LoginScreen.kt
@@ -141,7 +141,7 @@ fun Login(
popUpTo(NavGraphs.root) { inclusive = true }
}
is LoginUiSideEffect.TooManyDevices ->
- navigator.navigate(DeviceListDestination(it.accountNumber.value)) {
+ navigator.navigate(DeviceListDestination(it.accountNumber)) {
launchSingleTop = true
}
LoginUiSideEffect.NavigateToOutOfTime ->
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/DeleteCustomListUiState.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/DeleteCustomListUiState.kt
index 000fc13f4a..75b4cd6aaf 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/DeleteCustomListUiState.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/DeleteCustomListUiState.kt
@@ -1,5 +1,6 @@
package net.mullvad.mullvadvpn.compose.state
+import net.mullvad.mullvadvpn.lib.model.CustomListName
import net.mullvad.mullvadvpn.usecase.customlists.DeleteWithUndoError
-data class DeleteCustomListUiState(val deleteError: DeleteWithUndoError?)
+data class DeleteCustomListUiState(val name: CustomListName, val deleteError: DeleteWithUndoError?)
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/EditCustomListNameUiState.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/EditCustomListNameUiState.kt
index 9e6bcdecf8..06e9fc709b 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/EditCustomListNameUiState.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/EditCustomListNameUiState.kt
@@ -2,4 +2,6 @@ package net.mullvad.mullvadvpn.compose.state
import net.mullvad.mullvadvpn.usecase.customlists.RenameError
-data class EditCustomListNameUiState(val name: String = "", val error: RenameError? = null)
+data class EditCustomListNameUiState(val name: String = "", val error: RenameError? = null) {
+ val isValidName = name.isNotBlank()
+}
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt
index d752460ddb..ec940364dc 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt
@@ -9,10 +9,6 @@ import net.mullvad.mullvadvpn.BuildConfig
import net.mullvad.mullvadvpn.applist.ApplicationsProvider
import net.mullvad.mullvadvpn.constant.IS_PLAY_BUILD
import net.mullvad.mullvadvpn.dataproxy.MullvadProblemReport
-import net.mullvad.mullvadvpn.lib.model.ApiAccessMethod
-import net.mullvad.mullvadvpn.lib.model.ApiAccessMethodId
-import net.mullvad.mullvadvpn.lib.model.ApiAccessMethodName
-import net.mullvad.mullvadvpn.lib.model.GeoLocationId
import net.mullvad.mullvadvpn.lib.payment.PaymentProvider
import net.mullvad.mullvadvpn.lib.shared.VoucherRepository
import net.mullvad.mullvadvpn.repository.ApiAccessRepository
@@ -178,12 +174,10 @@ val uiModule = module {
IS_PLAY_BUILD
)
}
- viewModel { parameters -> DeviceListViewModel(get(), parameters.get()) }
+ viewModel { DeviceListViewModel(get(), get()) }
viewModel { DeviceRevokedViewModel(get(), get()) }
- viewModel { parameters -> MtuDialogViewModel(get(), parameters.getOrNull()) }
- viewModel { parameters ->
- DnsDialogViewModel(get(), get(), parameters.getOrNull(), parameters.getOrNull())
- }
+ viewModel { MtuDialogViewModel(get(), get()) }
+ viewModel { DnsDialogViewModel(get(), get(), get()) }
viewModel { LoginViewModel(get(), get(), get()) }
viewModel { PrivacyDisclaimerViewModel(get(), IS_PLAY_BUILD) }
viewModel { SelectLocationViewModel(get(), get(), get(), get(), get(), get()) }
@@ -197,36 +191,20 @@ val uiModule = module {
viewModel { OutOfTimeViewModel(get(), get(), get(), get(), get(), isPlayBuild = IS_PLAY_BUILD) }
viewModel { PaymentViewModel(get()) }
viewModel { FilterViewModel(get(), get()) }
- viewModel { (location: GeoLocationId?) -> CreateCustomListDialogViewModel(location, get()) }
- viewModel { parameters ->
- CustomListLocationsViewModel(parameters.get(), parameters.get(), get(), get(), get())
- }
- viewModel { parameters -> EditCustomListViewModel(parameters.get(), get()) }
- viewModel { parameters ->
- EditCustomListNameDialogViewModel(parameters.get(), parameters.get(), get())
- }
+ viewModel { CreateCustomListDialogViewModel(get(), get()) }
+ viewModel { CustomListLocationsViewModel(get(), get(), get(), get()) }
+ viewModel { EditCustomListViewModel(get(), get()) }
+ viewModel { EditCustomListNameDialogViewModel(get(), get()) }
viewModel { CustomListsViewModel(get(), get()) }
- viewModel { parameters -> DeleteCustomListConfirmationViewModel(parameters.get(), get()) }
+ viewModel { DeleteCustomListConfirmationViewModel(get(), get()) }
viewModel { ServerIpOverridesViewModel(get(), get()) }
viewModel { ResetServerIpOverridesConfirmationViewModel(get()) }
viewModel { VpnPermissionViewModel(get(), get()) }
viewModel { ApiAccessListViewModel(get()) }
- viewModel { (accessMethodId: ApiAccessMethodId?) ->
- EditApiAccessMethodViewModel(accessMethodId, get(), get())
- }
- viewModel {
- (
- id: ApiAccessMethodId?,
- name: ApiAccessMethodName,
- customProxy: ApiAccessMethod.CustomProxy) ->
- SaveApiAccessMethodViewModel(id, name, customProxy, get())
- }
- viewModel { (accessMethodId: ApiAccessMethodId) ->
- ApiAccessMethodDetailsViewModel(accessMethodId, get())
- }
- viewModel { (accessMethodId: ApiAccessMethodId) ->
- DeleteApiAccessMethodConfirmationViewModel(accessMethodId, get())
- }
+ viewModel { EditApiAccessMethodViewModel(get(), get(), get()) }
+ viewModel { SaveApiAccessMethodViewModel(get(), get()) }
+ viewModel { ApiAccessMethodDetailsViewModel(get(), get()) }
+ viewModel { DeleteApiAccessMethodConfirmationViewModel(get(), get()) }
// This view model must be single so we correctly attach lifecycle and share it with activity
single { NoDaemonViewModel(get()) }
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ApiAccessMethodDetailsViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ApiAccessMethodDetailsViewModel.kt
index fc52cb7eee..f133cf2172 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ApiAccessMethodDetailsViewModel.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ApiAccessMethodDetailsViewModel.kt
@@ -1,9 +1,11 @@
package net.mullvad.mullvadvpn.viewmodel
+import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import arrow.core.Either
import arrow.core.raise.either
+import com.ramcosta.composedestinations.generated.destinations.ApiAccessMethodDetailsDestination
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel
import kotlinx.coroutines.channels.Channel
@@ -22,11 +24,14 @@ import net.mullvad.mullvadvpn.repository.ApiAccessRepository
import net.mullvad.mullvadvpn.util.delayAtLeast
class ApiAccessMethodDetailsViewModel(
- private val apiAccessMethodId: ApiAccessMethodId,
- private val apiAccessRepository: ApiAccessRepository
+ private val apiAccessRepository: ApiAccessRepository,
+ savedStateHandle: SavedStateHandle
) : ViewModel() {
private var testingJob: Job? = null
+ private val apiAccessMethodId: ApiAccessMethodId =
+ ApiAccessMethodDetailsDestination.argsFrom(savedStateHandle).accessMethodId
+
private val _uiSideEffect = Channel<ApiAccessMethodDetailsSideEffect>(Channel.BUFFERED)
val uiSideEffect = _uiSideEffect.receiveAsFlow()
private val isTestingApiAccessMethodState = MutableStateFlow(false)
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/CreateCustomListDialogViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/CreateCustomListDialogViewModel.kt
index ef45302975..e367386bf2 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/CreateCustomListDialogViewModel.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/CreateCustomListDialogViewModel.kt
@@ -1,7 +1,9 @@
package net.mullvad.mullvadvpn.viewmodel
+import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
+import com.ramcosta.composedestinations.generated.destinations.CreateCustomListDestination
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.MutableStateFlow
@@ -20,10 +22,13 @@ import net.mullvad.mullvadvpn.usecase.customlists.CreateWithLocationsError
import net.mullvad.mullvadvpn.usecase.customlists.CustomListActionUseCase
class CreateCustomListDialogViewModel(
- private val locationCode: GeoLocationId?,
private val customListActionUseCase: CustomListActionUseCase,
+ savedStateHandle: SavedStateHandle
) : ViewModel() {
+ private val locationCode: GeoLocationId? =
+ CreateCustomListDestination.argsFrom(savedStateHandle).locationCode
+
private val _uiSideEffect =
Channel<CreateCustomListDialogSideEffect>(1, BufferOverflow.DROP_OLDEST)
val uiSideEffect = _uiSideEffect.receiveAsFlow()
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/CustomListLocationsViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/CustomListLocationsViewModel.kt
index f25384c120..6d738a6417 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/CustomListLocationsViewModel.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/CustomListLocationsViewModel.kt
@@ -1,7 +1,9 @@
package net.mullvad.mullvadvpn.viewmodel
+import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
+import com.ramcosta.composedestinations.generated.destinations.CustomListLocationsDestination
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharedFlow
@@ -14,7 +16,6 @@ import kotlinx.coroutines.launch
import net.mullvad.mullvadvpn.compose.communication.CustomListAction
import net.mullvad.mullvadvpn.compose.communication.LocationsChanged
import net.mullvad.mullvadvpn.compose.state.CustomListLocationsUiState
-import net.mullvad.mullvadvpn.lib.model.CustomListId
import net.mullvad.mullvadvpn.lib.model.RelayItem
import net.mullvad.mullvadvpn.relaylist.descendants
import net.mullvad.mullvadvpn.relaylist.filterOnSearchTerm
@@ -24,12 +25,13 @@ import net.mullvad.mullvadvpn.usecase.customlists.CustomListActionUseCase
import net.mullvad.mullvadvpn.usecase.customlists.CustomListRelayItemsUseCase
class CustomListLocationsViewModel(
- private val customListId: CustomListId,
- private val newList: Boolean,
relayListRepository: RelayListRepository,
private val customListRelayItemsUseCase: CustomListRelayItemsUseCase,
- private val customListActionUseCase: CustomListActionUseCase
+ private val customListActionUseCase: CustomListActionUseCase,
+ savedStateHandle: SavedStateHandle
) : ViewModel() {
+ private val navArgs =
+ CustomListLocationsDestination.argsFrom(savedStateHandle = savedStateHandle)
private val _uiSideEffect =
MutableSharedFlow<CustomListLocationsSideEffect>(replay = 1, extraBufferCapacity = 1)
val uiSideEffect: SharedFlow<CustomListLocationsSideEffect> = _uiSideEffect
@@ -47,15 +49,15 @@ class CustomListLocationsViewModel(
when {
selectedLocations == null ->
- CustomListLocationsUiState.Loading(newList = newList)
+ CustomListLocationsUiState.Loading(newList = navArgs.newList)
filteredRelayCountries.isEmpty() ->
CustomListLocationsUiState.Content.Empty(
- newList = newList,
+ newList = navArgs.newList,
searchTerm = searchTerm
)
else ->
CustomListLocationsUiState.Content.Data(
- newList = newList,
+ newList = navArgs.newList,
searchTerm = searchTerm,
availableLocations = filteredRelayCountries,
selectedLocations = selectedLocations,
@@ -69,7 +71,7 @@ class CustomListLocationsViewModel(
.stateIn(
viewModelScope,
SharingStarted.WhileSubscribed(),
- CustomListLocationsUiState.Loading(newList = newList)
+ CustomListLocationsUiState.Loading(newList = navArgs.newList)
)
init {
@@ -81,7 +83,7 @@ class CustomListLocationsViewModel(
_selectedLocations.value?.let { selectedLocations ->
customListActionUseCase(
CustomListAction.UpdateLocations(
- customListId,
+ navArgs.customListId,
selectedLocations.calculateLocationsToSave().map { it.id }
)
)
@@ -91,7 +93,7 @@ class CustomListLocationsViewModel(
_uiSideEffect.tryEmit(
// This is so that we don't show a snackbar after returning to the
// select location screen
- if (newList) {
+ if (navArgs.newList) {
CustomListLocationsSideEffect.CloseScreen
} else {
CustomListLocationsSideEffect.ReturnWithResult(it)
@@ -190,7 +192,7 @@ class CustomListLocationsViewModel(
private suspend fun fetchInitialSelectedLocations() {
val selectedLocations =
- customListRelayItemsUseCase(customListId).first().withDescendants().toSet()
+ customListRelayItemsUseCase(navArgs.customListId).first().withDescendants().toSet()
_initialLocations.value = selectedLocations
_selectedLocations.value = selectedLocations
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/DeleteApiAccessMethodConfirmationViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/DeleteApiAccessMethodConfirmationViewModel.kt
index 651081244f..bac2e3ad95 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/DeleteApiAccessMethodConfirmationViewModel.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/DeleteApiAccessMethodConfirmationViewModel.kt
@@ -1,7 +1,9 @@
package net.mullvad.mullvadvpn.viewmodel
+import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
+import com.ramcosta.composedestinations.generated.destinations.DeleteApiAccessMethodConfirmationDestination
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
@@ -15,9 +17,12 @@ import net.mullvad.mullvadvpn.lib.model.RemoveApiAccessMethodError
import net.mullvad.mullvadvpn.repository.ApiAccessRepository
class DeleteApiAccessMethodConfirmationViewModel(
- private val apiAccessMethodId: ApiAccessMethodId,
- private val apiAccessRepository: ApiAccessRepository
+ private val apiAccessRepository: ApiAccessRepository,
+ savedStateHandle: SavedStateHandle
) : ViewModel() {
+ private val apiAccessMethodId: ApiAccessMethodId =
+ DeleteApiAccessMethodConfirmationDestination.argsFrom(savedStateHandle).apiAccessMethodId
+
private val _uiSideEffect =
Channel<DeleteApiAccessMethodConfirmationSideEffect>(Channel.BUFFERED)
val uiSideEffect = _uiSideEffect.receiveAsFlow()
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/DeleteCustomListConfirmationViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/DeleteCustomListConfirmationViewModel.kt
index 8bea330f83..c492bd368a 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/DeleteCustomListConfirmationViewModel.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/DeleteCustomListConfirmationViewModel.kt
@@ -1,7 +1,9 @@
package net.mullvad.mullvadvpn.viewmodel
+import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
+import com.ramcosta.composedestinations.generated.destinations.DeleteCustomListDestination
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
@@ -13,13 +15,18 @@ import net.mullvad.mullvadvpn.compose.communication.CustomListAction
import net.mullvad.mullvadvpn.compose.communication.Deleted
import net.mullvad.mullvadvpn.compose.state.DeleteCustomListUiState
import net.mullvad.mullvadvpn.lib.model.CustomListId
+import net.mullvad.mullvadvpn.lib.model.CustomListName
import net.mullvad.mullvadvpn.usecase.customlists.CustomListActionUseCase
import net.mullvad.mullvadvpn.usecase.customlists.DeleteWithUndoError
class DeleteCustomListConfirmationViewModel(
- private val customListId: CustomListId,
- private val customListActionUseCase: CustomListActionUseCase
+ private val customListActionUseCase: CustomListActionUseCase,
+ savedStateHandle: SavedStateHandle
) : ViewModel() {
+ private val navArgs = DeleteCustomListDestination.argsFrom(savedStateHandle)
+ private val name: CustomListName = navArgs.name
+ private val customListId: CustomListId = navArgs.customListId
+
private val _uiSideEffect = Channel<DeleteCustomListConfirmationSideEffect>(Channel.BUFFERED)
val uiSideEffect = _uiSideEffect.receiveAsFlow()
@@ -27,11 +34,11 @@ class DeleteCustomListConfirmationViewModel(
val uiState =
_error
- .map { DeleteCustomListUiState(it) }
+ .map { DeleteCustomListUiState(name, it) }
.stateIn(
viewModelScope,
SharingStarted.WhileSubscribed(),
- DeleteCustomListUiState(null)
+ DeleteCustomListUiState(name, null)
)
fun deleteCustomList() {
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 b1c5736988..11e4071c9e 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
@@ -1,7 +1,9 @@
package net.mullvad.mullvadvpn.viewmodel
+import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
+import com.ramcosta.composedestinations.generated.destinations.DeviceListDestination
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.Channel
@@ -25,9 +27,12 @@ import net.mullvad.mullvadvpn.lib.shared.DeviceRepository
class DeviceListViewModel(
private val deviceRepository: DeviceRepository,
- private val token: AccountNumber,
+ savedStateHandle: SavedStateHandle,
private val dispatcher: CoroutineDispatcher = Dispatchers.Default,
) : ViewModel() {
+ private val accountNumber: AccountNumber =
+ DeviceListDestination.argsFrom(savedStateHandle).accountNumber
+
private val loadingDevices = MutableStateFlow<Set<DeviceId>>(emptySet())
private val deviceList = MutableStateFlow<List<Device>>(emptyList())
private val loading = MutableStateFlow(true)
@@ -59,7 +64,9 @@ class DeviceListViewModel(
viewModelScope.launch {
error.value = null
loading.value = true
- deviceRepository.deviceList(token).fold({ error.value = it }, { deviceList.value = it })
+ deviceRepository
+ .deviceList(accountNumber)
+ .fold({ error.value = it }, { deviceList.value = it })
loading.value = false
}
@@ -67,12 +74,12 @@ class DeviceListViewModel(
viewModelScope.launch(dispatcher) {
setLoadingState(deviceIdToRemove, true)
deviceRepository
- .removeDevice(token, deviceIdToRemove)
+ .removeDevice(accountNumber, deviceIdToRemove)
.fold(
{
_uiSideEffect.send(DeviceListSideEffect.FailedToRemoveDevice)
setLoadingState(deviceIdToRemove, false)
- deviceRepository.deviceList(token).onRight { deviceList.value = it }
+ deviceRepository.deviceList(accountNumber).onRight { deviceList.value = it }
},
{ removeDeviceFromState(deviceIdToRemove) }
)
@@ -82,6 +89,11 @@ class DeviceListViewModel(
loadingDevices.update { if (isLoading) it + deviceId else it - deviceId }
}
+ fun continueToLogin() =
+ viewModelScope.launch {
+ _uiSideEffect.send(DeviceListSideEffect.NavigateToLogin(accountNumber = accountNumber))
+ }
+
private fun removeDeviceFromState(deviceId: DeviceId) {
deviceList.update { devices -> devices.filter { item -> item.id != deviceId } }
loadingDevices.update { it - deviceId }
@@ -90,4 +102,6 @@ class DeviceListViewModel(
sealed interface DeviceListSideEffect {
data object FailedToRemoveDevice : DeviceListSideEffect
+
+ data class NavigateToLogin(val accountNumber: AccountNumber) : DeviceListSideEffect
}
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/DnsDialogViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/DnsDialogViewModel.kt
index 5df10ef0b1..14d154498d 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/DnsDialogViewModel.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/DnsDialogViewModel.kt
@@ -1,10 +1,12 @@
package net.mullvad.mullvadvpn.viewmodel
+import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import arrow.core.Either
import arrow.core.raise.either
import arrow.core.raise.ensure
+import com.ramcosta.composedestinations.generated.destinations.DnsDialogDestination
import java.net.InetAddress
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
@@ -48,13 +50,13 @@ sealed class ValidationError {
class DnsDialogViewModel(
private val repository: SettingsRepository,
private val inetAddressValidator: InetAddressValidator,
- index: Int? = null,
- initialValue: String?,
+ savedStateHandle: SavedStateHandle,
private val dispatcher: CoroutineDispatcher = Dispatchers.IO,
) : ViewModel() {
+ private val navArgs = DnsDialogDestination.argsFrom(savedStateHandle)
- private val currentIndex = MutableStateFlow(index)
- private val _ipAddressInput = MutableStateFlow(initialValue ?: EMPTY_STRING)
+ private val currentIndex = MutableStateFlow(navArgs.index)
+ private val _ipAddressInput = MutableStateFlow(navArgs.initialValue ?: EMPTY_STRING)
val uiState: StateFlow<DnsDialogViewState> =
combine(_ipAddressInput, currentIndex, repository.settingsUpdates.filterNotNull()) {
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/EditApiAccessMethodViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/EditApiAccessMethodViewModel.kt
index 043c503792..d677b27f7a 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/EditApiAccessMethodViewModel.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/EditApiAccessMethodViewModel.kt
@@ -1,5 +1,6 @@
package net.mullvad.mullvadvpn.viewmodel
+import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import arrow.core.Either
@@ -9,6 +10,7 @@ import arrow.core.getOrElse
import arrow.core.nel
import arrow.core.raise.either
import arrow.core.raise.ensure
+import com.ramcosta.composedestinations.generated.destinations.EditApiAccessMethodDestination
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel
import kotlinx.coroutines.channels.Channel
@@ -37,11 +39,13 @@ import net.mullvad.mullvadvpn.util.delayAtLeast
import org.apache.commons.validator.routines.InetAddressValidator
class EditApiAccessMethodViewModel(
- private val apiAccessMethodId: ApiAccessMethodId?,
private val apiAccessRepository: ApiAccessRepository,
- private val inetAddressValidator: InetAddressValidator
+ private val inetAddressValidator: InetAddressValidator,
+ savedStateHandle: SavedStateHandle
) : ViewModel() {
private var testingJob: Job? = null
+ private val apiAccessMethodId =
+ EditApiAccessMethodDestination.argsFrom(savedStateHandle).accessMethodId
private val _uiSideEffect = Channel<EditApiAccessSideEffect>(Channel.BUFFERED)
val uiSideEffect = _uiSideEffect.receiveAsFlow()
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/EditCustomListNameDialogViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/EditCustomListNameDialogViewModel.kt
index 2536f8edd7..0b71a5053e 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/EditCustomListNameDialogViewModel.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/EditCustomListNameDialogViewModel.kt
@@ -1,29 +1,32 @@
package net.mullvad.mullvadvpn.viewmodel
+import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
+import com.ramcosta.composedestinations.generated.destinations.EditCustomListNameDestination
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import net.mullvad.mullvadvpn.compose.communication.CustomListAction
import net.mullvad.mullvadvpn.compose.communication.Renamed
import net.mullvad.mullvadvpn.compose.state.EditCustomListNameUiState
-import net.mullvad.mullvadvpn.lib.model.CustomListId
import net.mullvad.mullvadvpn.lib.model.CustomListName
import net.mullvad.mullvadvpn.usecase.customlists.CustomListActionUseCase
import net.mullvad.mullvadvpn.usecase.customlists.RenameError
class EditCustomListNameDialogViewModel(
- private val customListId: CustomListId,
- private val initialName: CustomListName,
- private val customListActionUseCase: CustomListActionUseCase
+ private val customListActionUseCase: CustomListActionUseCase,
+ savedStateHandle: SavedStateHandle
) : ViewModel() {
+ private val navArgs = EditCustomListNameDestination.argsFrom(savedStateHandle)
+ private val inputName = MutableStateFlow(navArgs.initialName.value)
+
private val _uiSideEffect =
Channel<EditCustomListNameDialogSideEffect>(1, BufferOverflow.DROP_OLDEST)
val uiSideEffect = _uiSideEffect.receiveAsFlow()
@@ -31,20 +34,19 @@ class EditCustomListNameDialogViewModel(
private val _error = MutableStateFlow<RenameError?>(null)
val uiState =
- _error
- .map { EditCustomListNameUiState(name = initialName.value, error = it) }
+ combine(inputName, _error) { name, error -> EditCustomListNameUiState(name = name, error) }
.stateIn(
viewModelScope,
SharingStarted.WhileSubscribed(),
- EditCustomListNameUiState(name = initialName.value)
+ EditCustomListNameUiState(name = navArgs.initialName.value)
)
fun updateCustomListName(name: String) {
viewModelScope.launch {
customListActionUseCase(
CustomListAction.Rename(
- id = customListId,
- name = initialName,
+ id = navArgs.customListId,
+ name = navArgs.initialName,
newName = CustomListName.fromString(name)
)
)
@@ -55,7 +57,8 @@ class EditCustomListNameDialogViewModel(
}
}
- fun clearError() {
+ fun onNameChanged(name: String) {
+ inputName.value = name
viewModelScope.launch { _error.emit(null) }
}
}
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/EditCustomListViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/EditCustomListViewModel.kt
index adfacceb4e..14295533d7 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/EditCustomListViewModel.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/EditCustomListViewModel.kt
@@ -1,7 +1,9 @@
package net.mullvad.mullvadvpn.viewmodel
+import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
+import com.ramcosta.composedestinations.generated.destinations.EditCustomListDestination
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
@@ -10,9 +12,12 @@ import net.mullvad.mullvadvpn.lib.model.CustomListId
import net.mullvad.mullvadvpn.repository.CustomListsRepository
class EditCustomListViewModel(
- private val customListId: CustomListId,
- customListsRepository: CustomListsRepository
+ customListsRepository: CustomListsRepository,
+ savedStateHandle: SavedStateHandle
) : ViewModel() {
+ private val customListId: CustomListId =
+ EditCustomListDestination.argsFrom(savedStateHandle).customListId
+
val uiState =
customListsRepository.customLists
.map { customLists ->
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/MtuDialogViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/MtuDialogViewModel.kt
index 9d1a17207c..fa29558f13 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/MtuDialogViewModel.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/MtuDialogViewModel.kt
@@ -1,7 +1,9 @@
package net.mullvad.mullvadvpn.viewmodel
+import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
+import com.ramcosta.composedestinations.generated.destinations.MtuDialogDestination
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.Channel
@@ -17,12 +19,14 @@ import net.mullvad.mullvadvpn.repository.SettingsRepository
class MtuDialogViewModel(
private val repository: SettingsRepository,
- private val initialMtu: Mtu?,
+ savedStateHandle: SavedStateHandle,
private val dispatcher: CoroutineDispatcher = Dispatchers.IO
) : ViewModel() {
+ private val navArgs = MtuDialogDestination.argsFrom(savedStateHandle)
- private val _mtuInput = MutableStateFlow(initialMtu?.value?.toString() ?: "")
+ private val _mtuInput = MutableStateFlow(navArgs.initialMtu?.value?.toString() ?: "")
private val _isValidMtu = MutableStateFlow(true)
+
val uiState: StateFlow<MtuDialogUiState> =
combine(_mtuInput, _isValidMtu, ::createState)
.stateIn(
@@ -38,7 +42,7 @@ class MtuDialogViewModel(
MtuDialogUiState(
mtuInput = mtuInput,
isValidInput = isValidMtuInput,
- showResetToDefault = initialMtu != null
+ showResetToDefault = navArgs.initialMtu != null
)
fun onInputChanged(value: String) {
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SaveApiAccessMethodViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SaveApiAccessMethodViewModel.kt
index be937e9416..e3bb3f783a 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SaveApiAccessMethodViewModel.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SaveApiAccessMethodViewModel.kt
@@ -1,7 +1,9 @@
package net.mullvad.mullvadvpn.viewmodel
+import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
+import com.ramcosta.composedestinations.generated.destinations.SaveApiAccessMethodDestination
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@@ -17,11 +19,14 @@ import net.mullvad.mullvadvpn.lib.model.NewAccessMethodSetting
import net.mullvad.mullvadvpn.repository.ApiAccessRepository
class SaveApiAccessMethodViewModel(
- private val apiAccessMethodId: ApiAccessMethodId?,
- private val apiAccessMethodName: ApiAccessMethodName,
- private val customProxy: ApiAccessMethod.CustomProxy,
- private val apiAccessRepository: ApiAccessRepository
+ private val apiAccessRepository: ApiAccessRepository,
+ savedStateHandle: SavedStateHandle
) : ViewModel() {
+ private val navArgs = SaveApiAccessMethodDestination.argsFrom(savedStateHandle)
+ private val apiAccessMethodId: ApiAccessMethodId? = navArgs.id
+ private val apiAccessMethodName: ApiAccessMethodName = navArgs.name
+ private val customProxy: ApiAccessMethod.CustomProxy = navArgs.customProxy
+
private val _uiSideEffect = Channel<SaveApiAccessMethodSideEffect>()
val uiSideEffect = _uiSideEffect.receiveAsFlow()
private val _uiState = MutableStateFlow(SaveApiAccessMethodUiState())
diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ApiAccessMethodDetailsViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ApiAccessMethodDetailsViewModelTest.kt
index 63ce73c1a3..bd304573d3 100644
--- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ApiAccessMethodDetailsViewModelTest.kt
+++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ApiAccessMethodDetailsViewModelTest.kt
@@ -4,6 +4,7 @@ import app.cash.turbine.test
import arrow.core.Either
import arrow.core.left
import arrow.core.right
+import com.ramcosta.composedestinations.generated.navargs.toSavedStateHandle
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.every
@@ -14,6 +15,7 @@ import kotlin.test.assertIs
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.time.delay
+import net.mullvad.mullvadvpn.compose.screen.ApiAccessMethodDetailsNavArgs
import net.mullvad.mullvadvpn.compose.state.ApiAccessMethodDetailsUiState
import net.mullvad.mullvadvpn.data.UUID
import net.mullvad.mullvadvpn.lib.common.test.TestCoroutineRule
@@ -49,8 +51,10 @@ class ApiAccessMethodDetailsViewModelTest {
apiAccessMethodDetailsViewModel =
ApiAccessMethodDetailsViewModel(
- apiAccessMethodId = apiAccessMethodId,
- apiAccessRepository = mockApiAccessRepository
+ apiAccessRepository = mockApiAccessRepository,
+ savedStateHandle =
+ ApiAccessMethodDetailsNavArgs(accessMethodId = apiAccessMethodId)
+ .toSavedStateHandle()
)
}
diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/CreateCustomListDialogViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/CreateCustomListDialogViewModelTest.kt
index efdf08447d..2ab6e6267b 100644
--- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/CreateCustomListDialogViewModelTest.kt
+++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/CreateCustomListDialogViewModelTest.kt
@@ -3,6 +3,7 @@ package net.mullvad.mullvadvpn.viewmodel
import app.cash.turbine.test
import arrow.core.left
import arrow.core.right
+import com.ramcosta.composedestinations.generated.navargs.toSavedStateHandle
import io.mockk.coEvery
import io.mockk.every
import io.mockk.mockk
@@ -10,6 +11,7 @@ import kotlin.test.assertIs
import kotlinx.coroutines.test.runTest
import net.mullvad.mullvadvpn.compose.communication.Created
import net.mullvad.mullvadvpn.compose.communication.CustomListAction
+import net.mullvad.mullvadvpn.compose.dialog.CreateCustomListNavArgs
import net.mullvad.mullvadvpn.lib.common.test.TestCoroutineRule
import net.mullvad.mullvadvpn.lib.model.CustomListAlreadyExists
import net.mullvad.mullvadvpn.lib.model.CustomListId
@@ -113,7 +115,11 @@ class CreateCustomListDialogViewModelTest {
private fun createViewModelWithLocationCode(locationCode: GeoLocationId) =
CreateCustomListDialogViewModel(
- locationCode = locationCode,
- customListActionUseCase = mockCustomListActionUseCase
+ customListActionUseCase = mockCustomListActionUseCase,
+ savedStateHandle =
+ CreateCustomListNavArgs(
+ locationCode = locationCode,
+ )
+ .toSavedStateHandle()
)
}
diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/CustomListLocationsViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/CustomListLocationsViewModelTest.kt
index 20130018d8..4879031ce7 100644
--- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/CustomListLocationsViewModelTest.kt
+++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/CustomListLocationsViewModelTest.kt
@@ -2,6 +2,7 @@ package net.mullvad.mullvadvpn.viewmodel
import app.cash.turbine.test
import arrow.core.right
+import com.ramcosta.composedestinations.generated.navargs.toSavedStateHandle
import io.mockk.coEvery
import io.mockk.every
import io.mockk.mockk
@@ -10,6 +11,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.runTest
import net.mullvad.mullvadvpn.compose.communication.CustomListAction
import net.mullvad.mullvadvpn.compose.communication.LocationsChanged
+import net.mullvad.mullvadvpn.compose.screen.CustomListLocationsNavArgs
import net.mullvad.mullvadvpn.compose.state.CustomListLocationsUiState
import net.mullvad.mullvadvpn.lib.common.test.TestCoroutineRule
import net.mullvad.mullvadvpn.lib.model.CustomList
@@ -221,11 +223,15 @@ class CustomListLocationsViewModelTest {
newList: Boolean
): CustomListLocationsViewModel {
return CustomListLocationsViewModel(
- customListId = customListId,
- newList = newList,
relayListRepository = mockRelayListRepository,
customListRelayItemsUseCase = mockCustomListRelayItemsUseCase,
- customListActionUseCase = mockCustomListUseCase
+ customListActionUseCase = mockCustomListUseCase,
+ savedStateHandle =
+ CustomListLocationsNavArgs(
+ customListId = customListId,
+ newList = newList,
+ )
+ .toSavedStateHandle()
)
}
diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/DeleteApiAccessMethodConfirmationViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/DeleteApiAccessMethodConfirmationViewModelTest.kt
index 18f6a64647..eea3c01b2d 100644
--- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/DeleteApiAccessMethodConfirmationViewModelTest.kt
+++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/DeleteApiAccessMethodConfirmationViewModelTest.kt
@@ -3,9 +3,11 @@ package net.mullvad.mullvadvpn.viewmodel
import app.cash.turbine.test
import arrow.core.left
import arrow.core.right
+import com.ramcosta.composedestinations.generated.navargs.toSavedStateHandle
import io.mockk.coEvery
import io.mockk.mockk
import kotlinx.coroutines.test.runTest
+import net.mullvad.mullvadvpn.compose.dialog.DeleteApiAccessMethodNavArgs
import net.mullvad.mullvadvpn.data.UUID
import net.mullvad.mullvadvpn.lib.common.test.TestCoroutineRule
import net.mullvad.mullvadvpn.lib.model.ApiAccessMethodId
@@ -29,8 +31,10 @@ class DeleteApiAccessMethodConfirmationViewModelTest {
deleteApiAccessMethodConfirmationViewModel =
DeleteApiAccessMethodConfirmationViewModel(
- apiAccessMethodId = apiAccessMethodId,
- apiAccessRepository = mockApiAccessRepository
+ apiAccessRepository = mockApiAccessRepository,
+ savedStateHandle =
+ DeleteApiAccessMethodNavArgs(apiAccessMethodId = apiAccessMethodId)
+ .toSavedStateHandle()
)
}
diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/DeleteCustomListConfirmationViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/DeleteCustomListConfirmationViewModelTest.kt
index 66307a499d..2656d1a4ff 100644
--- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/DeleteCustomListConfirmationViewModelTest.kt
+++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/DeleteCustomListConfirmationViewModelTest.kt
@@ -2,14 +2,17 @@ package net.mullvad.mullvadvpn.viewmodel
import app.cash.turbine.test
import arrow.core.right
+import com.ramcosta.composedestinations.generated.navargs.toSavedStateHandle
import io.mockk.coEvery
import io.mockk.mockk
import kotlin.test.assertIs
import kotlinx.coroutines.test.runTest
import net.mullvad.mullvadvpn.compose.communication.CustomListAction
import net.mullvad.mullvadvpn.compose.communication.Deleted
+import net.mullvad.mullvadvpn.compose.dialog.DeleteCustomListNavArgs
import net.mullvad.mullvadvpn.lib.common.test.TestCoroutineRule
import net.mullvad.mullvadvpn.lib.model.CustomListId
+import net.mullvad.mullvadvpn.lib.model.CustomListName
import net.mullvad.mullvadvpn.usecase.customlists.CustomListActionUseCase
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
@@ -38,7 +41,12 @@ class DeleteCustomListConfirmationViewModelTest {
private fun createViewModel() =
DeleteCustomListConfirmationViewModel(
- customListId = CustomListId("1"),
- customListActionUseCase = mockCustomListActionUseCase
+ customListActionUseCase = mockCustomListActionUseCase,
+ savedStateHandle =
+ DeleteCustomListNavArgs(
+ customListId = CustomListId("1"),
+ name = CustomListName.fromString("asdf")
+ )
+ .toSavedStateHandle()
)
}
diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/EditCustomListNameDialogViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/EditCustomListNameDialogViewModelTest.kt
index 130ca21ece..7b4dcc0b83 100644
--- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/EditCustomListNameDialogViewModelTest.kt
+++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/EditCustomListNameDialogViewModelTest.kt
@@ -3,12 +3,14 @@ package net.mullvad.mullvadvpn.viewmodel
import app.cash.turbine.test
import arrow.core.left
import arrow.core.right
+import com.ramcosta.composedestinations.generated.navargs.toSavedStateHandle
import io.mockk.coEvery
import io.mockk.mockk
import kotlin.test.assertIs
import kotlinx.coroutines.test.runTest
import net.mullvad.mullvadvpn.compose.communication.CustomListAction
import net.mullvad.mullvadvpn.compose.communication.Renamed
+import net.mullvad.mullvadvpn.compose.dialog.EditCustomListNameNavArgs
import net.mullvad.mullvadvpn.lib.common.test.TestCoroutineRule
import net.mullvad.mullvadvpn.lib.model.CustomListId
import net.mullvad.mullvadvpn.lib.model.CustomListName
@@ -16,7 +18,6 @@ import net.mullvad.mullvadvpn.lib.model.NameAlreadyExists
import net.mullvad.mullvadvpn.usecase.customlists.CustomListActionUseCase
import net.mullvad.mullvadvpn.usecase.customlists.RenameError
import org.junit.jupiter.api.Assertions.assertEquals
-import org.junit.jupiter.api.Assertions.assertNull
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
@@ -77,15 +78,17 @@ class EditCustomListNameDialogViewModelTest {
awaitItem() // Default state
viewModel.updateCustomListName(customListName)
assertEquals(expectedError, awaitItem().error) // Showing error
- viewModel.clearError()
- assertNull(awaitItem().error)
}
}
private fun createViewModel(customListId: CustomListId, initialName: String) =
EditCustomListNameDialogViewModel(
- customListId = customListId,
- initialName = CustomListName.fromString(initialName),
- customListActionUseCase = mockCustomListActionUseCase
+ customListActionUseCase = mockCustomListActionUseCase,
+ savedStateHandle =
+ EditCustomListNameNavArgs(
+ customListId = customListId,
+ initialName = CustomListName.fromString(initialName),
+ )
+ .toSavedStateHandle()
)
}
diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/EditCustomListViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/EditCustomListViewModelTest.kt
index c3f233846a..16d1488bc4 100644
--- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/EditCustomListViewModelTest.kt
+++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/EditCustomListViewModelTest.kt
@@ -1,11 +1,13 @@
package net.mullvad.mullvadvpn.viewmodel
import app.cash.turbine.test
+import com.ramcosta.composedestinations.generated.navargs.toSavedStateHandle
import io.mockk.every
import io.mockk.mockk
import kotlin.test.assertIs
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.runTest
+import net.mullvad.mullvadvpn.compose.dialog.EditCustomListNameNavArgs
import net.mullvad.mullvadvpn.compose.state.EditCustomListState
import net.mullvad.mullvadvpn.lib.common.test.TestCoroutineRule
import net.mullvad.mullvadvpn.lib.model.CustomList
@@ -24,14 +26,10 @@ class EditCustomListViewModelTest {
fun `given a custom list id that does not exists should return not found ui state`() = runTest {
// Arrange
val customListId = CustomListId("2")
- val customList =
- CustomList(
- id = CustomListId("1"),
- name = CustomListName.fromString("test"),
- locations = emptyList()
- )
+ val name = CustomListName.fromString("test")
+ val customList = CustomList(id = CustomListId("1"), name = name, locations = emptyList())
every { mockCustomListsRepository.customLists } returns MutableStateFlow(listOf(customList))
- val viewModel = createViewModel(customListId)
+ val viewModel = createViewModel(customListId, name)
// Act, Assert
viewModel.uiState.test {
@@ -44,14 +42,10 @@ class EditCustomListViewModelTest {
fun `given a custom list id that exists should return content ui state`() = runTest {
// Arrange
val customListId = CustomListId("1")
- val customList =
- CustomList(
- id = customListId,
- name = CustomListName.fromString("test"),
- locations = emptyList()
- )
+ val name = CustomListName.fromString("test")
+ val customList = CustomList(id = customListId, name = name, locations = emptyList())
every { mockCustomListsRepository.customLists } returns MutableStateFlow(listOf(customList))
- val viewModel = createViewModel(customListId)
+ val viewModel = createViewModel(customListId, name)
// Act, Assert
viewModel.uiState.test {
@@ -63,9 +57,11 @@ class EditCustomListViewModelTest {
}
}
- private fun createViewModel(customListId: CustomListId) =
+ private fun createViewModel(customListId: CustomListId, initialName: CustomListName) =
EditCustomListViewModel(
- customListId = customListId,
- customListsRepository = mockCustomListsRepository
+ customListsRepository = mockCustomListsRepository,
+ savedStateHandle =
+ EditCustomListNameNavArgs(customListId = customListId, initialName = initialName)
+ .toSavedStateHandle()
)
}
diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/SaveApiAccessMethodViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/SaveApiAccessMethodViewModelTest.kt
index 0828b3ed08..3455bba423 100644
--- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/SaveApiAccessMethodViewModelTest.kt
+++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/SaveApiAccessMethodViewModelTest.kt
@@ -3,10 +3,12 @@ package net.mullvad.mullvadvpn.viewmodel
import app.cash.turbine.test
import arrow.core.left
import arrow.core.right
+import com.ramcosta.composedestinations.generated.navargs.toSavedStateHandle
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.mockk
import kotlinx.coroutines.test.runTest
+import net.mullvad.mullvadvpn.compose.dialog.SaveApiAccessMethodNavArgs
import net.mullvad.mullvadvpn.compose.state.SaveApiAccessMethodUiState
import net.mullvad.mullvadvpn.compose.state.TestApiAccessMethodState
import net.mullvad.mullvadvpn.data.UUID
@@ -212,10 +214,14 @@ class SaveApiAccessMethodViewModelTest {
) {
saveApiAccessMethodViewModel =
SaveApiAccessMethodViewModel(
- apiAccessMethodId = apiAccessMethodId,
- apiAccessMethodName = apiAccessMethodName,
- customProxy = customProxy,
- apiAccessRepository = mockApiAccessRepository
+ apiAccessRepository = mockApiAccessRepository,
+ savedStateHandle =
+ SaveApiAccessMethodNavArgs(
+ id = apiAccessMethodId,
+ name = apiAccessMethodName,
+ customProxy = customProxy,
+ )
+ .toSavedStateHandle()
)
}
}
diff --git a/android/config/baseline.xml b/android/config/baseline.xml
index 9f7bc424b7..36bd121a12 100644
--- a/android/config/baseline.xml
+++ b/android/config/baseline.xml
@@ -6,9 +6,9 @@
<ID>EmptyFunctionBlock:AccountTestRule.kt$AccountTestRule${}</ID>
<ID>EmptyKtFile:build.gradle.kts$.build.gradle.kts</ID>
<ID>LargeClass:ConnectScreenTest.kt$ConnectScreenTest</ID>
- <ID>LongMethod:ApiAccessMethodDetailsScreen.kt$@Destination&lt;RootGraph&gt;(style = SlideInFromRightTransition::class) @Composable fun ApiAccessMethodDetails( navigator: DestinationsNavigator, accessMethodId: ApiAccessMethodId, confirmDeleteListResultRecipient: ResultRecipient&lt;DeleteApiAccessMethodConfirmationDestination, Boolean&gt; )</ID>
+ <ID>LongMethod:ApiAccessMethodDetailsScreen.kt$@Destination&lt;RootGraph&gt;( style = SlideInFromRightTransition::class, navArgs = ApiAccessMethodDetailsNavArgs::class ) @Composable fun ApiAccessMethodDetails( navigator: DestinationsNavigator, confirmDeleteListResultRecipient: ResultRecipient&lt;DeleteApiAccessMethodConfirmationDestination, Boolean&gt; )</ID>
<ID>LongMethod:ConnectionButton.kt$@OptIn(ExperimentalMaterial3Api::class) @Composable private fun ConnectionButton( text: String, mainClick: () -&gt; Unit, reconnectClick: () -&gt; Unit, isReconnectButtonEnabled: Boolean, containerColor: Color, contentColor: Color, modifier: Modifier = Modifier, reconnectButtonTestTag: String = "" )</ID>
- <ID>LongMethod:EditApiAccessMethodScreen.kt$@Destination&lt;RootGraph&gt;(style = SlideInFromRightTransition::class) @Composable fun EditApiAccessMethod( navigator: DestinationsNavigator, backNavigator: ResultBackNavigator&lt;Boolean&gt;, saveApiAccessMethodResultRecipient: ResultRecipient&lt;SaveApiAccessMethodDestination, Boolean&gt;, discardChangesResultRecipient: ResultRecipient&lt;DiscardChangesDialogDestination, Boolean&gt;, accessMethodId: ApiAccessMethodId? )</ID>
+ <ID>LongMethod:EditApiAccessMethodScreen.kt$@Destination&lt;RootGraph&gt;( style = SlideInFromRightTransition::class, navArgs = EditApiAccessMethodNavArgs::class ) @Composable fun EditApiAccessMethod( navigator: DestinationsNavigator, backNavigator: ResultBackNavigator&lt;Boolean&gt;, saveApiAccessMethodResultRecipient: ResultRecipient&lt;SaveApiAccessMethodDestination, Boolean&gt;, discardChangesResultRecipient: ResultRecipient&lt;DiscardChangesDialogDestination, Boolean&gt;, )</ID>
<ID>LongMethod:NotificationBanner.kt$@Composable private fun Notification(notificationBannerData: NotificationData)</ID>
<ID>MagicNumber:Chevron.kt$100</ID>
<ID>MagicNumber:Chevron.kt$270f</ID>
diff --git a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/RelayItemId.kt b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/RelayItemId.kt
index da59481269..c560fab49c 100644
--- a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/RelayItemId.kt
+++ b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/RelayItemId.kt
@@ -17,7 +17,8 @@ value class CustomListId(val value: String) : RelayItemId, Parcelable {
}
@optics
-sealed interface GeoLocationId : RelayItemId {
+@Parcelize
+sealed interface GeoLocationId : RelayItemId, Parcelable {
@optics
@Parcelize
data class Country(val countryCode: String) : GeoLocationId {